diff options
Diffstat (limited to 'drivers/net/wireless/bcmdhd/dhd_linux.c')
-rw-r--r-- | drivers/net/wireless/bcmdhd/dhd_linux.c | 3838 |
1 files changed, 2387 insertions, 1451 deletions
diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index ff8e634..da6b2a2 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -2,7 +2,7 @@ * Broadcom Dongle Host Driver (DHD), Linux-specific network interface * Basically selected code segments from usb-cdc.c and usb-rndis.c * - * Copyright (C) 1999-2012, Broadcom Corporation + * Copyright (C) 1999-2014, Broadcom Corporation * * Unless you and Broadcom execute a separate written software license * agreement governing use of this software, this software is licensed to you @@ -22,7 +22,7 @@ * software in any way with any other Broadcom software provided under a license * other than the GPL, without Broadcom's express prior written consent. * - * $Id: dhd_linux.c 394719 2013-04-03 13:22:12Z $ + * $Id: dhd_linux.c 464559 2014-03-25 08:26:34Z $ */ #include <typedefs.h> @@ -42,6 +42,9 @@ #include <linux/ethtool.h> #include <linux/fcntl.h> #include <linux/fs.h> +#include <linux/ip.h> +#include <net/addrconf.h> +#include <linux/cpufreq.h> #include <asm/uaccess.h> #include <asm/unaligned.h> @@ -52,8 +55,11 @@ #include <bcmdevs.h> #include <proto/ethernet.h> +#include <proto/bcmevent.h> #include <dngl_stats.h> +#include <dhd_linux_wq.h> #include <dhd.h> +#include <dhd_linux.h> #include <dhd_bus.h> #include <dhd_proto.h> #include <dhd_dbg.h> @@ -63,13 +69,23 @@ #ifdef WL_CFG80211 #include <wl_cfg80211.h> #endif - +#ifdef PNO_SUPPORT +#include <dhd_pno.h> +#endif #ifdef WLBTAMP #include <proto/802.11_bta.h> #include <proto/bt_amp_hci.h> #include <dhd_bta.h> #endif +#ifdef AMPDU_VO_ENABLE +#include <proto/802.1d.h> +#endif /* AMPDU_VO_ENABLE */ +#ifdef DHDTCPACK_SUPPRESS +#include <dhd_ip.h> +#endif /* DHDTCPACK_SUPPRESS */ + + #ifdef WLMEDIA_HTSF #include <linux/time.h> #include <htsf.h> @@ -96,6 +112,7 @@ typedef struct histo_ { static histo_t vi_d1, vi_d2, vi_d3, vi_d4; #endif /* WLMEDIA_HTSF */ + #if defined(BLOCK_IPV6_PACKET) && defined(CUSTOMER_HW4) #define HEX_PREF_STR "0x" #define UNI_FILTER_STR "010000000000" @@ -110,6 +127,19 @@ extern bool ap_cfg_running; extern bool ap_fw_loaded; #endif +#ifdef CUSTOMER_HW4 +#ifdef FIX_CPU_MIN_CLOCK +#include <linux/pm_qos.h> +#endif /* FIX_CPU_MIN_CLOCK */ +#endif /* CUSTOMER_HW4 */ + +#ifdef ENABLE_ADAPTIVE_SCHED +#define DEFAULT_CPUFREQ_THRESH 1000000 /* threshold frequency : 1000000 = 1GHz */ +#ifndef CUSTOM_CPUFREQ_THRESH +#define CUSTOM_CPUFREQ_THRESH DEFAULT_CPUFREQ_THRESH +#endif /* CUSTOM_CPUFREQ_THRESH */ +#endif /* ENABLE_ADAPTIVE_SCHED */ + /* enable HOSTIP cache update from the host side when an eth0:N is up */ #define AOE_IP_ALIAS_SUPPORT 1 @@ -124,28 +154,41 @@ extern bool ap_fw_loaded; #include <wl_android.h> + #ifdef ARP_OFFLOAD_SUPPORT void aoe_update_host_ipv4_table(dhd_pub_t *dhd_pub, u32 ipa, bool add, int idx); -static int dhd_device_event(struct notifier_block *this, - unsigned long event, - void *ptr); - -static struct notifier_block dhd_notifier = { - .notifier_call = dhd_device_event +static int dhd_inetaddr_notifier_call(struct notifier_block *this, + unsigned long event, void *ptr); +static struct notifier_block dhd_inetaddr_notifier = { + .notifier_call = dhd_inetaddr_notifier_call }; +/* to make sure we won't register the same notifier twice, otherwise a loop is likely to be + * created in kernel notifier link list (with 'next' pointing to itself) + */ +static bool dhd_inetaddr_notifier_registered = FALSE; #endif /* ARP_OFFLOAD_SUPPORT */ +static int dhd_inet6addr_notifier_call(struct notifier_block *this, + unsigned long event, void *ptr); +static struct notifier_block dhd_inet6addr_notifier = { + .notifier_call = dhd_inet6addr_notifier_call +}; +/* to make sure we won't register the same notifier twice, otherwise a loop is likely to be + * created in kernel notifier link list (with 'next' pointing to itself) + */ +static bool dhd_inet6addr_notifier_registered = FALSE; + #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) #include <linux/suspend.h> volatile bool dhd_mmc_suspend = FALSE; DECLARE_WAIT_QUEUE_HEAD(dhd_dpc_wait); #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */ -#if defined(OOB_INTR_ONLY) +#if defined(OOB_INTR_ONLY) || defined(BCMSPI_ANDROID) extern void dhd_enable_oob_intr(struct dhd_bus *bus, bool enable); -#endif /* defined(OOB_INTR_ONLY) */ -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && (1) -static void dhd_hang_process(struct work_struct *work); +#endif /* defined(OOB_INTR_ONLY) || defined(BCMSPI_ANDROID) */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) +static void dhd_hang_process(void *dhd_info, void *event_data, u8 event); #endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) MODULE_LICENSE("GPL v2"); @@ -163,6 +206,16 @@ MODULE_LICENSE("GPL v2"); #endif #endif /* BCM_FD_AGGR */ +#ifdef PROP_TXSTATUS +extern bool dhd_wlfc_skip_fc(void); +extern void dhd_wlfc_plat_init(void *dhd); +extern void dhd_wlfc_plat_deinit(void *dhd); +#endif /* PROP_TXSTATUS */ +#if defined(CUSTOMER_HW4) && defined(USE_DYNAMIC_F2_BLKSIZE) +extern uint sd_f2_blocksize; +extern int dhdsdio_func_blocksize(dhd_pub_t *dhd, int function_num, int block_size); +#endif /* CUSTOMER_HW4 && USE_DYNAMIC_F2_BLKSIZE */ + #if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 15) const char * print_tainted() @@ -172,10 +225,10 @@ print_tainted() #endif /* LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 15) */ /* Linux wireless extension support */ -#if defined(CONFIG_WIRELESS_EXT) +#if defined(WL_WIRELESS_EXT) #include <wl_iw.h> extern wl_iw_extra_params_t g_wl_iw_params; -#endif /* defined(CONFIG_WIRELESS_EXT) */ +#endif /* defined(WL_WIRELESS_EXT) */ #if defined(CUSTOMER_HW4) && defined(CONFIG_PARTIALSUSPEND_SLP) #include <linux/partialsuspend_slp.h> @@ -196,6 +249,7 @@ extern int dhd_get_suspend_bcn_li_dtim(dhd_pub_t *dhd); #ifdef PKT_FILTER_SUPPORT extern void dhd_pktfilter_offload_set(dhd_pub_t * dhd, char *arg); extern void dhd_pktfilter_offload_enable(dhd_pub_t * dhd, char *arg, int enable, int master_mode); +extern void dhd_pktfilter_offload_delete(dhd_pub_t *dhd, int id); #endif #ifdef CUSTOMER_HW4 @@ -218,20 +272,35 @@ extern int dhd_check_module_mac(dhd_pub_t *dhd, struct ether_addr *mac); #ifdef MIMO_ANT_SETTING extern int dhd_sel_ant_from_file(dhd_pub_t *dhd); #endif -#ifdef GLOBALCONFIG_WLAN_COUNTRY_CODE -int dhd_customer_set_country(dhd_pub_t *dhd); +#ifdef WRITE_WLANINFO +extern uint32 sec_save_wlinfo(char *firm_ver, char *dhd_ver, char *nvram_p); +#endif +#ifdef DHD_OF_SUPPORT +extern void interrupt_set_cpucore(int set); #endif #else #ifdef READ_MACADDR extern int dhd_read_macaddr(struct dhd_info *dhd); +#else +static inline int dhd_read_macaddr(struct dhd_info *dhd) { return 0; } #endif #ifdef WRITE_MACADDR extern int dhd_write_macaddr(struct ether_addr *mac); +#else +static inline int dhd_write_macaddr(struct ether_addr *mac) { return 0; } #endif #endif /* CUSTOMER_HW4 */ + +typedef struct dhd_if_event { + struct list_head list; + wl_event_data_if_t event; + char name[IFNAMSIZ+1]; + uint8 mac[ETHER_ADDR_LEN]; +} dhd_if_event_t; + /* Interface control information */ typedef struct dhd_if { struct dhd_info *info; /* back pointer to dhd_info */ @@ -239,15 +308,14 @@ typedef struct dhd_if { struct net_device *net; struct net_device_stats stats; int idx; /* iface idx in dongle */ - dhd_if_state_t state; /* interface state */ uint subunit; /* subunit */ uint8 mac_addr[ETHER_ADDR_LEN]; /* assigned MAC address */ bool attached; /* Delayed attachment when unset */ bool txflowcontrol; /* Per interface flow control indicator */ char name[IFNAMSIZ+1]; /* linux interface name */ uint8 bssidx; /* bsscfg index for the interface */ + bool set_macaddress; bool set_multicast; - bool event2cfg80211; /* To determine if pass event to cfg80211 */ } dhd_if_t; #ifdef WLMEDIA_HTSF @@ -278,13 +346,22 @@ static uint32 maxdelay = 0, tspktcnt = 0, maxdelaypktno = 0; #endif /* WLMEDIA_HTSF */ +struct ipv6_work_info_t { + uint8 if_idx; + char ipv6_addr[16]; + unsigned long event; +}; + /* Local private structure (extension of pub) */ typedef struct dhd_info { -#if defined(CONFIG_WIRELESS_EXT) +#if defined(WL_WIRELESS_EXT) wl_iw_t iw; /* wireless extensions state (must be first) */ -#endif /* defined(CONFIG_WIRELESS_EXT) */ +#endif /* defined(WL_WIRELESS_EXT) */ dhd_pub_t pub; + void *adapter; /* adapter information, interrupt, fw path etc. */ + char fw_path[PATH_MAX]; /* path to firmware image */ + char nv_path[PATH_MAX]; /* path to nvram vars file */ /* For supporting multiple interfaces */ dhd_if_t *iflist[DHD_MAX_IFS]; @@ -292,44 +369,38 @@ typedef struct dhd_info { struct semaphore proto_sem; #ifdef PROP_TXSTATUS spinlock_t wlfc_spinlock; + #endif /* PROP_TXSTATUS */ #ifdef WLMEDIA_HTSF htsf_t htsf; #endif wait_queue_head_t ioctl_resp_wait; + uint32 default_wd_interval; + struct timer_list timer; bool wd_timer_valid; struct tasklet_struct tasklet; spinlock_t sdlock; spinlock_t txqlock; spinlock_t dhd_lock; -#ifdef DHDTHREAD - /* Thread based operation */ - bool threads_only; - struct semaphore sdsem; + struct semaphore sdsem; tsk_ctl_t thr_dpc_ctl; tsk_ctl_t thr_wdt_ctl; -#ifdef RXFRAME_THREAD + tsk_ctl_t thr_rxf_ctl; spinlock_t rxf_lock; -#endif /* RXFRAME_THREAD */ -#endif /* DHDTHREAD */ - bool dhd_tasklet_create; - tsk_ctl_t thr_sysioc_ctl; -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) - struct work_struct work_hang; -#endif + bool rxthread_enabled; /* Wakelocks */ #if defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) - struct wake_lock *wl_wifi; /* Wifi wakelock */ - struct wake_lock *wl_rxwake; /* Wifi rx wakelock */ - struct wake_lock *wl_ctrlwake; /* Wifi ctrl wakelock */ - struct wake_lock *wl_wdwake; /* Wifi wd wakelock */ + struct wake_lock wl_wifi; /* Wifi wakelock */ + struct wake_lock wl_rxwake; /* Wifi rx wakelock */ + struct wake_lock wl_ctrlwake; /* Wifi ctrl wakelock */ + struct wake_lock wl_wdwake; /* Wifi wd wakelock */ #endif -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) /* net_device interface lock, prevent race conditions among net_dev interface * calls and wifi_on or wifi_off */ @@ -337,14 +408,14 @@ typedef struct dhd_info { struct mutex dhd_suspend_mutex; #endif spinlock_t wakelock_spinlock; - int wakelock_counter; + uint32 wakelock_counter; + bool waive_wakelock; + uint32 wakelock_before_waive; int wakelock_wd_counter; int wakelock_rx_timeout_enable; int wakelock_ctrl_timeout_enable; /* Thread to issue ioctl for multicast */ - unsigned char set_macaddress; - struct ether_addr macvalue; wait_queue_head_t ctrl_wait; atomic_t pend_8021x_cnt; dhd_attach_states_t dhd_state; @@ -366,6 +437,23 @@ typedef struct dhd_info { #ifdef DHDTCPACK_SUPPRESS spinlock_t tcpack_lock; #endif /* DHDTCPACK_SUPPRESS */ +#ifdef CUSTOMER_HW4 +#ifdef FIX_CPU_MIN_CLOCK + bool cpufreq_fix_status; + struct mutex cpufreq_fix; + struct pm_qos_request dhd_cpu_qos; +#ifdef FIX_BUS_MIN_CLOCK + struct pm_qos_request dhd_bus_qos; +#endif /* FIX_BUS_MIN_CLOCK */ +#endif /* FIX_CPU_MIN_CLOCK */ +#endif /* CUSTOMER_HW4 */ + void *dhd_deferred_wq; +#ifdef DEBUG_CPU_FREQ + struct notifier_block freq_trans; + int __percpu *new_freq; +#endif + unsigned int unit; + struct notifier_block pm_notifier; } dhd_info_t; /* Flag to indicate if we should download firmware on driver load */ @@ -377,21 +465,23 @@ uint dhd_download_fw_on_driverload = TRUE; char firmware_path[MOD_PARAM_PATHLEN]; char nvram_path[MOD_PARAM_PATHLEN]; +/* information string to keep firmware, chio, cheip version info visiable from log */ +char info_string[MOD_PARAM_INFOLEN]; +module_param_string(info_string, info_string, MOD_PARAM_INFOLEN, 0444); int op_mode = 0; int disable_proptx = 0; module_param(op_mode, int, 0644); extern int wl_control_wl_start(struct net_device *dev); -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(BCMLXSDMMC) struct semaphore dhd_registration_sem; -struct semaphore dhd_chipup_sem; -int dhd_registration_check = FALSE; - -#define DHD_REGISTRATION_TIMEOUT 12000 /* msec : allowed time to finished dhd registration */ #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ -/* Spawn a thread for system ioctls (set mac, set mcast) */ -uint dhd_sysioc = TRUE; -module_param(dhd_sysioc, uint, 0); +/* deferred handlers */ +static void dhd_ifadd_event_handler(void *handle, void *event_info, u8 event); +static void dhd_ifdel_event_handler(void *handle, void *event_info, u8 event); +static void dhd_set_mac_addr_handler(void *handle, void *event_info, u8 event); +static void dhd_set_mcast_list_handler(void *handle, void *event_info, u8 event); +static void dhd_inet6_work_handler(void *dhd_info, void *event_data, u8 event); /* Error bits */ module_param(dhd_msg_level, int, 0); @@ -400,20 +490,29 @@ module_param(dhd_msg_level, int, 0); /* ARP offload enable */ uint dhd_arp_enable = TRUE; module_param(dhd_arp_enable, uint, 0); -#endif /* ARP_OFFLOAD_SUPPORT */ +/* ARP offload agent mode : Enable ARP Host Auto-Reply and ARP Peer Auto-Reply */ + +#if defined(CUSTOMER_HW4) +uint dhd_arp_mode = ARP_OL_AGENT | ARP_OL_PEER_AUTO_REPLY | ARP_OL_SNOOP; +#else +uint dhd_arp_mode = ARP_OL_AGENT | ARP_OL_PEER_AUTO_REPLY; +#endif + +module_param(dhd_arp_mode, uint, 0); +#endif /* ARP_OFFLOAD_SUPPORT */ /* Disable Prop tx */ module_param(disable_proptx, int, 0644); /* load firmware and/or nvram values from the filesystem */ module_param_string(firmware_path, firmware_path, MOD_PARAM_PATHLEN, 0660); -#ifdef CUSTOMER_HW4 module_param_string(nvram_path, nvram_path, MOD_PARAM_PATHLEN, 0660); -#else -module_param_string(nvram_path, nvram_path, MOD_PARAM_PATHLEN, 0); -#endif /* CUSTOMER_HW4 */ /* Watchdog interval */ + +/* extend watchdog expiration to 2 seconds when DPC is running */ +#define WATCHDOG_EXTEND_INTERVAL (2000) + uint dhd_watchdog_ms = 10; module_param(dhd_watchdog_ms, uint, 0); @@ -423,26 +522,10 @@ uint dhd_console_ms = 0; module_param(dhd_console_ms, uint, 0644); #endif /* defined(DHD_DEBUG) */ -#ifdef REPEAT_READFRAME -uint dhd_doflow = 1; -module_param(dhd_doflow, uint, 0644); - -uint dhd_dpcpoll = 1; -module_param(dhd_dpcpoll, uint, 0644); -#endif /* REPEAT_READFRAME */ uint dhd_slpauto = TRUE; module_param(dhd_slpauto, uint, 0); -/* ARP offload agent mode : Enable ARP Host Auto-Reply and ARP Peer Auto-Reply */ -#if defined(CUSTOMER_HW4) -uint dhd_arp_mode = ARP_OL_AGENT | ARP_OL_PEER_AUTO_REPLY | ARP_OL_SNOOP; -#else -uint dhd_arp_mode = ARP_OL_AGENT | ARP_OL_PEER_AUTO_REPLY; -#endif - -module_param(dhd_arp_mode, uint, 0); - #ifdef PKT_FILTER_SUPPORT /* Global Pkt filter enable control */ uint dhd_pkt_filter_enable = TRUE; @@ -454,14 +537,13 @@ uint dhd_pkt_filter_init = 0; module_param(dhd_pkt_filter_init, uint, 0); /* Pkt filter mode control */ -#ifdef GAN_LITE_NAT_KEEPALIVE_FILTER +#if defined(CUSTOMER_HW4) && defined(GAN_LITE_NAT_KEEPALIVE_FILTER) uint dhd_master_mode = FALSE; #else uint dhd_master_mode = TRUE; -#endif /* GAL_LITE_NAT_KEEPALIVE_FILTER */ +#endif /* CUSTOMER_HW4 && GAN_LITE_NAT_KEEPALIVE_FILTER */ module_param(dhd_master_mode, uint, 0); -#ifdef DHDTHREAD int dhd_watchdog_prio = 0; module_param(dhd_watchdog_prio, int, 0); @@ -469,16 +551,20 @@ module_param(dhd_watchdog_prio, int, 0); int dhd_dpc_prio = CUSTOM_DPC_PRIO_SETTING; module_param(dhd_dpc_prio, int, 0); -#ifdef RXFRAME_THREAD /* RX frame thread priority */ int dhd_rxf_prio = CUSTOM_RXF_PRIO_SETTING; module_param(dhd_rxf_prio, int, 0); -#endif /* RXFRAME_THREAD */ -/* DPC thread priority, -1 to use tasklet */ -extern int dhd_dongle_memsize; -module_param(dhd_dongle_memsize, int, 0); -#endif /* DHDTHREAD */ +#if !defined(BCMDHDUSB) +extern int dhd_dongle_ramsize; +module_param(dhd_dongle_ramsize, int, 0); +#endif /* BCMDHDUSB */ + +/* Keep track of number of instances */ +static int dhd_found = 0; +static int instance_base = 0; /* Starting instance number */ +module_param(instance_base, int, 0644); + /* Control fw roaming */ #ifdef BCMCCX uint dhd_roam_disable = 0; @@ -541,21 +627,15 @@ uint dhd_pktgen_len = 0; module_param(dhd_pktgen_len, uint, 0); #endif /* SDTEST */ -/* Version string to report */ -#ifdef DHD_DEBUG -#ifndef SRCBASE -#define SRCBASE "drivers/net/wireless/bcmdhd" -#endif -#define DHD_COMPILED "\nCompiled in " SRCBASE -#else -#define DHD_COMPILED -#endif /* DHD_DEBUG */ +#if defined(BCMSUP_4WAY_HANDSHAKE) +/* Use in dongle supplicant for 4-way handshake */ +uint dhd_use_idsup = 0; +module_param(dhd_use_idsup, uint, 0); +#endif /* BCMSUP_4WAY_HANDSHAKE */ -static char dhd_version[] = "Dongle Host Driver, version " EPI_VERSION_STR -#ifdef DHD_DEBUG -"\nCompiled in " SRCBASE " on " __DATE__ " at " __TIME__ -#endif -; +extern char dhd_version[]; + +int dhd_net_bus_devreset(struct net_device *dev, uint8 flag); static void dhd_net_if_lock_local(dhd_info_t *dhd); static void dhd_net_if_unlock_local(dhd_info_t *dhd); static void dhd_suspend_lock(dhd_pub_t *dhdp); @@ -582,18 +662,14 @@ bool g_pm_control; void sec_control_pm(dhd_pub_t *dhd, uint *); #endif /* CUSTOMER_HW4 & CONFIG_CONTROL_PM */ -#if defined(CUSTOMER_HW4) && defined(USE_WL_FRAMEBURST) -uint32 sec_control_frameburst(void); -#endif /* CUSTOMER_HW4 & CONFIG_CONTROL_PM */ - - -#if defined(CONFIG_WIRELESS_EXT) +#if defined(WL_WIRELESS_EXT) struct iw_statistics *dhd_get_wireless_stats(struct net_device *dev); -#endif /* defined(CONFIG_WIRELESS_EXT) */ +#endif /* defined(WL_WIRELESS_EXT) */ static void dhd_dpc(ulong data); /* forward decl */ extern int dhd_wait_pend8021x(struct net_device *dev); +void dhd_os_wd_timer_extend(void *bus, bool extend); #ifdef TOE #ifndef BDC @@ -606,38 +682,62 @@ static int dhd_toe_set(dhd_info_t *dhd, int idx, uint32 toe_ol); static int dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata, wl_event_msg_t *event_ptr, void **data_ptr); -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) -static int dhd_sleep_pm_callback(struct notifier_block *nfb, unsigned long action, void *ignored) +#if defined(SUPPORT_P2P_GO_PS) +#ifdef PROP_TXSTATUS +static int dhd_wakelock_waive(dhd_info_t *dhdinfo); +static int dhd_wakelock_restore(dhd_info_t *dhdinfo); +#endif +#endif /* defined(SUPPORT_P2P_GO_PS) */ + +#if defined(CONFIG_PM_SLEEP) +static int dhd_pm_callback(struct notifier_block *nfb, unsigned long action, void *ignored) { int ret = NOTIFY_DONE; + bool suspend = FALSE; + dhd_info_t *dhdinfo = (dhd_info_t*)container_of(nfb, struct dhd_info, pm_notifier); -#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 39)) + BCM_REFERENCE(dhdinfo); switch (action) { - case PM_HIBERNATION_PREPARE: - case PM_SUSPEND_PREPARE: - dhd_mmc_suspend = TRUE; - ret = NOTIFY_OK; - break; - case PM_POST_HIBERNATION: - case PM_POST_SUSPEND: - dhd_mmc_suspend = FALSE; - ret = NOTIFY_OK; - break; + case PM_HIBERNATION_PREPARE: + case PM_SUSPEND_PREPARE: + suspend = TRUE; + break; + case PM_POST_HIBERNATION: + case PM_POST_SUSPEND: + suspend = FALSE; + break; + } + +#if defined(SUPPORT_P2P_GO_PS) +#ifdef PROP_TXSTATUS + if (suspend) { + dhd_wakelock_waive(dhdinfo); + dhd_wlfc_suspend(&dhdinfo->pub); + dhd_wakelock_restore(dhdinfo); + } else { + dhd_wlfc_resume(&dhdinfo->pub); } + +#endif +#endif /* defined(SUPPORT_P2P_GO_PS) */ + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && (LINUX_VERSION_CODE <= \ + KERNEL_VERSION(2, 6, 39)) + dhd_mmc_suspend = suspend; smp_mb(); #endif return ret; } -static struct notifier_block dhd_sleep_pm_notifier = { - .notifier_call = dhd_sleep_pm_callback, - .priority = 10 -}; +/* to make sure we won't register the same notifier twice, otherwise a loop is likely to be + * created in kernel notifier link list (with 'next' pointing to itself) + */ +static bool dhd_pm_notifier_registered = FALSE; + extern int register_pm_notifier(struct notifier_block *nb); extern int unregister_pm_notifier(struct notifier_block *nb); -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */ +#endif /* defined(CONFIG_PM_SLEEP) */ -#if defined(DHDTHREAD) && defined(RXFRAME_THREAD) /* Request scheduling of the bus rx frame */ static void dhd_sched_rxf(dhd_pub_t *dhdp, void *skb); static void dhd_os_rxflock(dhd_pub_t *pub); @@ -658,11 +758,15 @@ static inline int dhd_rxf_enqueue(dhd_pub_t *dhdp, void* skb) sent_idx = dhdp->sent_idx; if (dhdp->skbbuf[store_idx] != NULL) { /* Make sure the previous packets are processed */ - /* Do I need to make this context sleep here? Definitely in Single processor case */ dhd_os_rxfunlock(dhdp); DHD_ERROR(("dhd_rxf_enqueue: pktbuf not consumed %p, store idx %d sent idx %d\n", skb, store_idx, sent_idx)); - msleep(1); + /* removed msleep here, should use wait_event_timeout if we + * want to give rx frame thread a chance to run + */ +#if defined(WAIT_DEQUEUE) + OSL_SLEEP(1); +#endif return BCME_ERROR; } DHD_TRACE(("dhd_rxf_enqueue: Store SKB %p. idx %d -> %d\n", @@ -703,7 +807,68 @@ static inline void* dhd_rxf_dequeue(dhd_pub_t *dhdp) return skb; } -#endif /* defined(DHDTHREAD) && defined(RXFRAME_THREAD) */ + +static int dhd_process_cid_mac(dhd_pub_t *dhdp, bool prepost) +{ + dhd_info_t *dhd = (dhd_info_t *)dhdp->info; + + if (prepost) { /* pre process */ +#ifdef CUSTOMER_HW4 +#ifdef USE_CID_CHECK + dhd_check_module_cid(dhdp); +#endif +#ifdef READ_MACADDR + dhd_read_macaddr(dhd, &dhd->pub.mac); +#endif +#ifdef RDWR_MACADDR + dhd_check_rdwr_macaddr(dhd, &dhd->pub, &dhd->pub.mac); +#endif +#ifdef RDWR_KORICS_MACADDR + dhd_write_rdwr_korics_macaddr(dhd, &dhd->pub.mac); +#endif +#else + dhd_read_macaddr(dhd); +#endif /* CUSTOMER_HW4 */ + } else { /* post process */ +#ifdef CUSTOMER_HW4 +#ifdef GET_MAC_FROM_OTP + dhd_check_module_mac(dhdp, &dhd->pub.mac); +#endif +#ifdef RDWR_MACADDR + dhd_write_rdwr_macaddr(&dhd->pub.mac); +#endif +#ifdef WRITE_MACADDR + dhd_write_macaddr(&dhd->pub.mac); +#endif +#else + dhd_write_macaddr(&dhd->pub.mac); +#endif /* CUSTOMER_HW4 */ + } + + return 0; +} + +#if defined(PKT_FILTER_SUPPORT) && !defined(GAN_LITE_NAT_KEEPALIVE_FILTER) +static bool +_turn_on_arp_filter(dhd_pub_t *dhd, int op_mode) +{ + bool _apply = FALSE; + /* In case of IBSS mode, apply arp pkt filter */ + if (op_mode & DHD_FLAG_IBSS_MODE) { + _apply = TRUE; + goto exit; + } + /* In case of P2P GO or GC, apply pkt filter to pass arp pkt to host */ + if ((dhd->arp_version == 1) && + (op_mode & (DHD_FLAG_P2P_GC_MODE | DHD_FLAG_P2P_GO_MODE))) { + _apply = TRUE; + goto exit; + } + +exit: + return _apply; +} +#endif /* PKT_FILTER_SUPPORT && !GAN_LITE_NAT_KEEPALIVE_FILTER */ void dhd_set_packet_filter(dhd_pub_t *dhd) { @@ -731,15 +896,15 @@ void dhd_enable_packet_filter(int value, dhd_pub_t *dhd) (dhd_support_sta_mode(dhd) && !dhd->dhcp_in_progress))) { for (i = 0; i < dhd->pktfilter_count; i++) { -#ifdef PASS_ARP_PACKET - if (value && (i == dhd->pktfilter_count -1) && - !(dhd->op_mode & (DHD_FLAG_P2P_GC_MODE | DHD_FLAG_P2P_GO_MODE))) { - DHD_TRACE_HW4(("Do not turn on ARP white list pkt filter:" +#ifndef GAN_LITE_NAT_KEEPALIVE_FILTER + if (value && (i == DHD_ARP_FILTER_NUM) && + !_turn_on_arp_filter(dhd, dhd->op_mode)) { + DHD_TRACE(("Do not turn on ARP white list pkt filter:" "val %d, cnt %d, op_mode 0x%x\n", value, i, dhd->op_mode)); continue; } -#endif +#endif /* !GAN_LITE_NAT_KEEPALIVE_FILTER */ dhd_pktfilter_offload_enable(dhd, dhd->pktfilter[i], value, dhd_master_mode); } @@ -761,12 +926,20 @@ static int dhd_set_suspend(int value, dhd_pub_t *dhd) #if defined(CUSTOMER_HW4) && defined(ENABLE_BCN_LI_BCN_WAKEUP) int bcn_li_bcn; #endif /* CUSTOMER_HW4 && ENABLE_BCN_LI_BCN_WAKEUP */ + uint nd_ra_filter = 0; + int ret = 0; #if defined(PASS_ALL_MCAST_PKTS) && defined(CUSTOMER_HW4) struct dhd_info *dhdinfo = dhd->info; uint32 allmulti; uint i; #endif /* PASS_ALL_MCAST_PKTS && CUSTOMER_HW4 */ +#ifdef DYNAMIC_SWOOB_DURATION +#ifndef CUSTOM_INTR_WIDTH +#define CUSTOM_INTR_WIDTH 100 +#endif /* CUSTOM_INTR_WIDTH */ + int intr_width = 0; +#endif /* DYNAMIC_SWOOB_DURATION */ if (!dhd) return -ENODEV; @@ -774,6 +947,12 @@ static int dhd_set_suspend(int value, dhd_pub_t *dhd) __FUNCTION__, value, dhd->in_suspend)); dhd_suspend_lock(dhd); + +#ifdef CUSTOM_SET_CPUCORE + DHD_TRACE(("%s set cpucore(suspend%d)\n", __FUNCTION__, value)); + /* set specific cpucore */ + dhd_set_cpucore(dhd, TRUE); +#endif /* CUSTOM_SET_CPUCORE */ if (dhd->up) { if (value && dhd->in_suspend) { #ifdef PKT_FILTER_SUPPORT @@ -824,12 +1003,38 @@ static int dhd_set_suspend(int value, dhd_pub_t *dhd) 4, iovbuf, sizeof(iovbuf)); dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); #endif /* CUSTOMER_HW4 && ENABLE_BCN_LI_BCN_WAKEUP */ + if (FW_SUPPORTED(dhd, ndoe)) { + /* enable IPv6 RA filter in firmware during suspend */ + nd_ra_filter = 1; + bcm_mkiovar("nd_ra_filter_enable", (char *)&nd_ra_filter, 4, + iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, + sizeof(iovbuf), TRUE, 0)) < 0) + DHD_ERROR(("failed to set nd_ra_filter (%d)\n", + ret)); + } +#ifdef DYNAMIC_SWOOB_DURATION + intr_width = CUSTOM_INTR_WIDTH; + bcm_mkiovar("bus:intr_width", (char *)&intr_width, 4, + iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, + sizeof(iovbuf), TRUE, 0)) < 0) + DHD_ERROR(("failed to set intr_width (%d)\n", ret)); +#endif /* DYNAMIC_SWOOB_DURATION */ } else { #ifdef PKT_FILTER_SUPPORT dhd->early_suspended = 0; #endif /* Kernel resumed */ DHD_ERROR(("%s: Remove extra suspend setting \n", __FUNCTION__)); +#ifdef DYNAMIC_SWOOB_DURATION + intr_width = 0; + bcm_mkiovar("bus:intr_width", (char *)&intr_width, 4, + iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, + sizeof(iovbuf), TRUE, 0)) < 0) + DHD_ERROR(("failed to set intr_width (%d)\n", ret)); +#endif /* DYNAMIC_SWOOB_DURATION */ #ifndef SUPPORT_PM2_ONLY power_mode = PM_FAST; @@ -868,6 +1073,16 @@ static int dhd_set_suspend(int value, dhd_pub_t *dhd) 4, iovbuf, sizeof(iovbuf)); dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); #endif /* CUSTOMER_HW4 && ENABLE_BCN_LI_BCN_WAKEUP */ + if (FW_SUPPORTED(dhd, ndoe)) { + /* disable IPv6 RA filter in firmware during suspend */ + nd_ra_filter = 0; + bcm_mkiovar("nd_ra_filter_enable", (char *)&nd_ra_filter, 4, + iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, + sizeof(iovbuf), TRUE, 0)) < 0) + DHD_ERROR(("failed to set nd_ra_filter (%d)\n", + ret)); + } } } dhd_suspend_unlock(dhd); @@ -949,7 +1164,7 @@ dhd_timeout_expired(dhd_timeout_t *tmo) /* Add the delay that's about to take place */ tmo->elapsed += tmo->increment; - if (tmo->increment < tmo->tick) { + if ((!CAN_SLEEP()) || tmo->increment < tmo->tick) { OSL_DELAY(tmo->increment); tmo->increment *= 2; if (tmo->increment > tmo->tick) @@ -975,7 +1190,7 @@ dhd_net2idx(dhd_info_t *dhd, struct net_device *net) ASSERT(dhd); while (i < DHD_MAX_IFS) { - if (dhd->iflist[i] && (dhd->iflist[i]->net == net)) + if (dhd->iflist[i] && dhd->iflist[i]->net && (dhd->iflist[i]->net == net)) return i; i++; } @@ -1270,7 +1485,7 @@ _dhd_set_multicast_list(dhd_info_t *dhd, int ifidx) } int -_dhd_set_mac_address(dhd_info_t *dhd, int ifidx, struct ether_addr *addr) +_dhd_set_mac_address(dhd_info_t *dhd, int ifidx, uint8 *addr) { char buf[32]; wl_ioctl_t ioc; @@ -1291,7 +1506,8 @@ _dhd_set_mac_address(dhd_info_t *dhd, int ifidx, struct ether_addr *addr) DHD_ERROR(("%s: set cur_etheraddr failed\n", dhd_ifname(&dhd->pub, ifidx))); } else { memcpy(dhd->iflist[ifidx]->net->dev_addr, addr, ETHER_ADDR_LEN); - memcpy(dhd->pub.mac.octet, addr, ETHER_ADDR_LEN); + if (ifidx == 0) + memcpy(dhd->pub.mac.octet, addr, ETHER_ADDR_LEN); } return ret; @@ -1303,231 +1519,198 @@ extern tsk_ctl_t ap_eth_ctl; /* ap netdev heper thread ctl */ #endif static void -dhd_op_if(dhd_if_t *ifp) +dhd_ifadd_event_handler(void *handle, void *event_info, u8 event) { - dhd_info_t *dhd; - int ret = 0, err = 0; -#ifdef SOFTAP - unsigned long flags; -#endif + dhd_info_t *dhd = handle; + dhd_if_event_t *if_event = event_info; + struct net_device *ndev; + int ifidx, bssidx; + int ret; - if (!ifp || !ifp->info || !ifp->idx) + if (event != DHD_WQ_WORK_IF_ADD) { + DHD_ERROR(("%s: unexpected event \n", __FUNCTION__)); return; - ASSERT(ifp && ifp->info && ifp->idx); /* Virtual interfaces only */ - dhd = ifp->info; + } - DHD_TRACE(("%s: idx %d, state %d\n", __FUNCTION__, ifp->idx, ifp->state)); + if (!dhd) { + DHD_ERROR(("%s: dhd info not available \n", __FUNCTION__)); + return; + } -#ifdef WL_CFG80211 - if (wl_cfg80211_is_progress_ifchange()) - return; + if (!if_event) { + DHD_ERROR(("%s: event data is null \n", __FUNCTION__)); + return; + } -#endif - switch (ifp->state) { - case DHD_IF_ADD: - /* - * Delete the existing interface before overwriting it - * in case we missed the WLC_E_IF_DEL event. - */ - if (ifp->net != NULL) { - DHD_ERROR(("%s: ERROR: netdev:%s already exists, try free & unregister \n", - __FUNCTION__, ifp->net->name)); - netif_stop_queue(ifp->net); - unregister_netdev(ifp->net); - free_netdev(ifp->net); - } - /* Allocate etherdev, including space for private structure */ - if (!(ifp->net = alloc_etherdev(sizeof(dhd)))) { - DHD_ERROR(("%s: OOM - alloc_etherdev(%d)\n", __FUNCTION__, sizeof(dhd))); - ret = -ENOMEM; - } - if (ret == 0) { - strncpy(ifp->net->name, ifp->name, IFNAMSIZ); - ifp->net->name[IFNAMSIZ - 1] = '\0'; - memcpy(netdev_priv(ifp->net), &dhd, sizeof(dhd)); -#ifdef WL_CFG80211 - if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211) - if (!wl_cfg80211_notify_ifadd(ifp->net, ifp->idx, ifp->bssidx, - (void*)dhd_net_attach)) { - ifp->state = DHD_IF_NONE; - ifp->event2cfg80211 = TRUE; - return; - } -#endif - if ((err = dhd_net_attach(&dhd->pub, ifp->idx)) != 0) { - DHD_ERROR(("%s: dhd_net_attach failed, err %d\n", - __FUNCTION__, err)); - ret = -EOPNOTSUPP; - } else { -#if defined(SOFTAP) - if (ap_fw_loaded && !(dhd->dhd_state & DHD_ATTACH_STATE_CFG80211)) { - /* semaphore that the soft AP CODE waits on */ - flags = dhd_os_spin_lock(&dhd->pub); + dhd_net_if_lock_local(dhd); + DHD_OS_WAKE_LOCK(&dhd->pub); - /* save ptr to wl0.1 netdev for use in wl_iw.c */ - ap_net_dev = ifp->net; - /* signal to the SOFTAP 'sleeper' thread, wl0.1 is ready */ - up(&ap_eth_ctl.sema); - dhd_os_spin_unlock(&dhd->pub, flags); - } -#endif - DHD_TRACE(("\n ==== pid:%x, net_device for if:%s created ===\n\n", - current->pid, ifp->net->name)); - ifp->state = DHD_IF_NONE; - } - } - break; - case DHD_IF_DEL: - /* Make sure that we don't enter again here if .. */ - /* dhd_op_if is called again from some other context */ - ifp->state = DHD_IF_DELETING; - if (ifp->net != NULL) { - DHD_TRACE(("\n%s: got 'DHD_IF_DEL' state\n", __FUNCTION__)); - netif_stop_queue(ifp->net); -#ifdef WL_CFG80211 - if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211) { - wl_cfg80211_ifdel_ops(ifp->net); - } -#endif - unregister_netdev(ifp->net); - ret = DHD_DEL_IF; /* Make sure the free_netdev() is called */ -#ifdef WL_CFG80211 - if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211) { - wl_cfg80211_notify_ifdel(); - } -#endif - } - break; - case DHD_IF_DELETING: - break; - default: - DHD_ERROR(("%s: bad op %d\n", __FUNCTION__, ifp->state)); - ASSERT(!ifp->state); - break; + ifidx = if_event->event.ifidx; + bssidx = if_event->event.bssidx; + DHD_TRACE(("%s: registering if with ifidx %d\n", __FUNCTION__, ifidx)); + + ndev = dhd_allocate_if(&dhd->pub, ifidx, if_event->name, + if_event->mac, bssidx, TRUE); + if (!ndev) { + DHD_ERROR(("%s: net device alloc failed \n", __FUNCTION__)); + goto done; } - if (ret < 0) { - ifp->set_multicast = FALSE; - if (ifp->net) { - free_netdev(ifp->net); - ifp->net = NULL; - } - dhd->iflist[ifp->idx] = NULL; -#ifdef SOFTAP - flags = dhd_os_spin_lock(&dhd->pub); - if (ifp->net == ap_net_dev) - ap_net_dev = NULL; /* NULL SOFTAP global wl0.1 as well */ - dhd_os_spin_unlock(&dhd->pub, flags); -#endif /* SOFTAP */ - MFREE(dhd->pub.osh, ifp, sizeof(*ifp)); + ret = dhd_register_if(&dhd->pub, ifidx, TRUE); + if (ret != BCME_OK) { + DHD_ERROR(("%s: dhd_register_if failed\n", __FUNCTION__)); + dhd_remove_if(&dhd->pub, ifidx, TRUE); } +done: + MFREE(dhd->pub.osh, if_event, sizeof(dhd_if_event_t)); + + DHD_OS_WAKE_UNLOCK(&dhd->pub); + dhd_net_if_unlock_local(dhd); } +static void +dhd_ifdel_event_handler(void *handle, void *event_info, u8 event) +{ + dhd_info_t *dhd = handle; + int ifidx; + dhd_if_event_t *if_event = event_info; -#ifdef DHDTCPACK_SUPPRESS -uint dhd_use_tcpack_suppress = TRUE; -module_param(dhd_use_tcpack_suppress, uint, FALSE); -extern bool dhd_tcpack_suppress(dhd_pub_t *dhdp, void *pkt); -#endif /* DHDTCPACK_SUPPRESS */ -static int -_dhd_sysioc_thread(void *data) -{ - tsk_ctl_t *tsk = (tsk_ctl_t *)data; - dhd_info_t *dhd = (dhd_info_t *)tsk->parent; + if (event != DHD_WQ_WORK_IF_DEL) { + DHD_ERROR(("%s: unexpected event \n", __FUNCTION__)); + return; + } + if (!dhd) { + DHD_ERROR(("%s: dhd info not available \n", __FUNCTION__)); + return; + } + + if (!if_event) { + DHD_ERROR(("%s: event data is null \n", __FUNCTION__)); + return; + } + + dhd_net_if_lock_local(dhd); + DHD_OS_WAKE_LOCK(&dhd->pub); + + ifidx = if_event->event.ifidx; + DHD_TRACE(("Removing interface with idx %d\n", ifidx)); + + dhd_remove_if(&dhd->pub, ifidx, TRUE); + + MFREE(dhd->pub.osh, if_event, sizeof(dhd_if_event_t)); + + DHD_OS_WAKE_UNLOCK(&dhd->pub); + dhd_net_if_unlock_local(dhd); +} + +static void +dhd_set_mac_addr_handler(void *handle, void *event_info, u8 event) +{ + dhd_info_t *dhd = handle; + dhd_if_t *ifp = event_info; - int i; #ifdef SOFTAP - bool in_ap = FALSE; unsigned long flags; + bool in_ap = FALSE; #endif -#ifndef USE_KTHREAD_API - DAEMONIZE("dhd_sysioc"); + if (event != DHD_WQ_WORK_SET_MAC) { + DHD_ERROR(("%s: unexpected event \n", __FUNCTION__)); + } - complete(&tsk->completed); + if (!dhd) { + DHD_ERROR(("%s: dhd info not available \n", __FUNCTION__)); + return; + } + +#ifdef SOFTAP + flags = dhd_os_spin_lock(&dhd->pub); + in_ap = (ap_net_dev != NULL); + dhd_os_spin_unlock(&dhd->pub, flags); + + if (in_ap) { + DHD_ERROR(("attempt to set MAC for %s in AP Mode, blocked. \n", + ifp->net->name)); + return; + } #endif + dhd_net_if_lock_local(dhd); + DHD_OS_WAKE_LOCK(&dhd->pub); - while (down_interruptible(&tsk->sema) == 0) { -#ifdef MCAST_LIST_ACCUMULATION - bool set_multicast = FALSE; -#endif /* MCAST_LIST_ACCUMULATION */ + if (ifp == NULL || !dhd->pub.up) { + DHD_ERROR(("%s: interface info not available/down \n", __FUNCTION__)); + goto done; + } - SMP_RD_BARRIER_DEPENDS(); - if (tsk->terminated) { - break; - } + DHD_ERROR(("%s: MACID is overwritten\n", __FUNCTION__)); + ifp->set_macaddress = FALSE; + if (_dhd_set_mac_address(dhd, ifp->idx, ifp->mac_addr) == 0) + DHD_INFO(("%s: MACID is overwritten\n", __FUNCTION__)); + else + DHD_ERROR(("%s: _dhd_set_mac_address() failed\n", __FUNCTION__)); - dhd_net_if_lock_local(dhd); - DHD_OS_WAKE_LOCK(&dhd->pub); +done: + DHD_OS_WAKE_UNLOCK(&dhd->pub); + dhd_net_if_unlock_local(dhd); +} + +static void +dhd_set_mcast_list_handler(void *handle, void *event_info, u8 event) +{ + dhd_info_t *dhd = handle; + dhd_if_t *ifp = event_info; + int ifidx; - for (i = 0; i < DHD_MAX_IFS; i++) { - if (dhd->iflist[i]) { - DHD_TRACE(("%s: interface %d\n", __FUNCTION__, i)); #ifdef SOFTAP - flags = dhd_os_spin_lock(&dhd->pub); - in_ap = (ap_net_dev != NULL); - dhd_os_spin_unlock(&dhd->pub, flags); -#endif /* SOFTAP */ - if (dhd->iflist[i] && dhd->iflist[i]->state) - dhd_op_if(dhd->iflist[i]); - - if (dhd->iflist[i] == NULL) { - DHD_TRACE(("\n\n %s: interface %d just been removed," - "!\n\n", __FUNCTION__, i)); - continue; - } + bool in_ap = FALSE; + unsigned long flags; +#endif + + if (event != DHD_WQ_WORK_SET_MCAST_LIST) { + DHD_ERROR(("%s: unexpected event \n", __FUNCTION__)); + return; + } + + if (!dhd) { + DHD_ERROR(("%s: dhd info not available \n", __FUNCTION__)); + return; + } + #ifdef SOFTAP - if (in_ap && dhd->set_macaddress == i+1) { - DHD_TRACE(("attempt to set MAC for %s in AP Mode," - "blocked. \n", dhd->iflist[i]->net->name)); - dhd->set_macaddress = 0; - continue; - } + flags = dhd_os_spin_lock(&dhd->pub); + in_ap = (ap_net_dev != NULL); + dhd_os_spin_unlock(&dhd->pub, flags); - if (in_ap && dhd->iflist[i]->set_multicast) { - DHD_TRACE(("attempt to set MULTICAST list for %s" - "in AP Mode, blocked. \n", dhd->iflist[i]->net->name)); - dhd->iflist[i]->set_multicast = FALSE; - continue; - } -#endif /* SOFTAP */ - if (dhd->pub.up == 0) - continue; - if (dhd->iflist[i]->set_multicast) { - dhd->iflist[i]->set_multicast = FALSE; -#ifdef MCAST_LIST_ACCUMULATION - set_multicast = TRUE; -#else - _dhd_set_multicast_list(dhd, i); -#endif /* MCAST_LIST_ACCUMULATION */ + if (in_ap) { + DHD_ERROR(("set MULTICAST list for %s in AP Mode, blocked. \n", + ifp->net->name)); + ifp->set_multicast = FALSE; + return; + } +#endif + + dhd_net_if_lock_local(dhd); + DHD_OS_WAKE_LOCK(&dhd->pub); + + if (ifp == NULL || !dhd->pub.up) { + DHD_ERROR(("%s: interface info not available/down \n", __FUNCTION__)); + goto done; + } + + ifidx = ifp->idx; - } - if (dhd->set_macaddress == i+1) { - dhd->set_macaddress = 0; - if (_dhd_set_mac_address(dhd, i, &dhd->macvalue) == 0) { - DHD_INFO(( - "%s: MACID is overwritten\n", - __FUNCTION__)); - } else { - DHD_ERROR(( - "%s: _dhd_set_mac_address() failed\n", - __FUNCTION__)); - } - } - } - } #ifdef MCAST_LIST_ACCUMULATION - if (set_multicast) - _dhd_set_multicast_list(dhd, 0); + ifidx = 0; #endif /* MCAST_LIST_ACCUMULATION */ - DHD_OS_WAKE_UNLOCK(&dhd->pub); - dhd_net_if_unlock_local(dhd); - } - DHD_TRACE(("%s: stopped\n", __FUNCTION__)); - complete_and_exit(&tsk->completed, 0); + _dhd_set_multicast_list(dhd, ifidx); + DHD_INFO(("%s: set multicast list for if %d\n", __FUNCTION__, ifidx)); + +done: + DHD_OS_WAKE_UNLOCK(&dhd->pub); + dhd_net_if_unlock_local(dhd); } static int @@ -1538,16 +1721,20 @@ dhd_set_mac_address(struct net_device *dev, void *addr) dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); struct sockaddr *sa = (struct sockaddr *)addr; int ifidx; + dhd_if_t *dhdif; ifidx = dhd_net2idx(dhd, dev); if (ifidx == DHD_BAD_IF) return -1; - ASSERT(dhd->thr_sysioc_ctl.thr_pid >= 0); - memcpy(&dhd->macvalue, sa->sa_data, ETHER_ADDR_LEN); - dhd->set_macaddress = ifidx+1; - up(&dhd->thr_sysioc_ctl.sema); + dhdif = dhd->iflist[ifidx]; + dhd_net_if_lock_local(dhd); + memcpy(dhdif->mac_addr, sa->sa_data, ETHER_ADDR_LEN); + dhdif->set_macaddress = TRUE; + dhd_net_if_unlock_local(dhd); + dhd_deferred_schedule_work((void *)dhdif, DHD_WQ_WORK_SET_MAC, + dhd_set_mac_addr_handler, DHD_WORK_PRIORITY_LOW); return ret; } @@ -1561,9 +1748,9 @@ dhd_set_multicast_list(struct net_device *dev) if (ifidx == DHD_BAD_IF) return; - ASSERT(dhd->thr_sysioc_ctl.thr_pid >= 0); dhd->iflist[ifidx]->set_multicast = TRUE; - up(&dhd->thr_sysioc_ctl.sema); + dhd_deferred_schedule_work((void *)dhd->iflist[ifidx], DHD_WQ_WORK_SET_MCAST_LIST, + dhd_set_mcast_list_handler, DHD_WORK_PRIORITY_LOW); } #ifdef PROP_TXSTATUS @@ -1591,7 +1778,7 @@ uint8 prio2fifo[8] = { 1, 0, 0, 1, 2, 2, 3, 3 }; #define WME_PRIO2AC(prio) wme_fifo2ac[prio2fifo[(prio)]] #endif /* PROP_TXSTATUS */ -int +int BCMFASTPATH dhd_sendpkt(dhd_pub_t *dhdp, int ifidx, void *pktbuf) { int ret = BCME_OK; @@ -1619,6 +1806,12 @@ dhd_sendpkt(dhd_pub_t *dhdp, int ifidx, void *pktbuf) return BCME_ERROR; } +#ifdef DHDTCPACK_SUPPRESS + /* If this packet has replaced another packet and got freed, just return */ + if (dhd_tcpack_suppress(dhdp, pktbuf)) + return ret; +#endif /* DHDTCPACK_SUPPRESS */ + /* Look into the packet and update the packet priority */ #ifndef PKTPRIO_OVERRIDE if (PKTPRIO(pktbuf) == 0) @@ -1626,7 +1819,7 @@ dhd_sendpkt(dhd_pub_t *dhdp, int ifidx, void *pktbuf) pktsetprio(pktbuf, FALSE); #ifdef PROP_TXSTATUS - if (dhdp->wlfc_state) { + if (dhd_wlfc_is_supported(dhdp)) { /* store the interface ID */ DHD_PKTTAG_SETIF(PKTTAG(pktbuf), ifidx); @@ -1648,43 +1841,33 @@ dhd_sendpkt(dhd_pub_t *dhdp, int ifidx, void *pktbuf) #ifdef WLMEDIA_HTSF dhd_htsf_addtxts(dhdp, pktbuf); #endif -#ifdef DHDTCPACK_SUPPRESS - if (dhd_use_tcpack_suppress && dhd_tcpack_suppress(dhdp, pktbuf)) - ret = BCME_OK; - else -#endif /* DHDTCPACK_SUPPRESS */ #ifdef PROP_TXSTATUS { - dhd_os_wlfc_block(dhdp); - if (dhdp->wlfc_state && - ((athost_wl_status_info_t*)dhdp->wlfc_state)->proptxstatus_mode - != WLFC_FCMODE_NONE) { - dhd_wlfc_commit_packets(dhdp->wlfc_state, (f_commitpkt_t)dhd_bus_txdata, - dhdp->bus, pktbuf); - if (((athost_wl_status_info_t*)dhdp->wlfc_state)->toggle_host_if) { - ((athost_wl_status_info_t*)dhdp->wlfc_state)->toggle_host_if = 0; - } - dhd_os_wlfc_unblock(dhdp); - } - else { - dhd_os_wlfc_unblock(dhdp); + if (dhd_wlfc_commit_packets(dhdp, (f_commitpkt_t)dhd_bus_txdata, + dhdp->bus, pktbuf, TRUE) == WLFC_UNSUPPORTED) { /* non-proptxstatus way */ ret = dhd_bus_txdata(dhdp->bus, pktbuf); } } #else +#ifdef BCMPCIE + ret = dhd_bus_txdata(dhdp->bus, pktbuf, (uint8)ifidx); +#else ret = dhd_bus_txdata(dhdp->bus, pktbuf); +#endif /* BCMPCIE */ #endif /* PROP_TXSTATUS */ return ret; } -int +int BCMFASTPATH dhd_start_xmit(struct sk_buff *skb, struct net_device *net) { int ret; + uint datalen; void *pktbuf; - dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net); + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net); + dhd_if_t *ifp = NULL; int ifidx; #ifdef WLMEDIA_HTSF uint8 htsfdlystat_sz = dhd->pub.htsfdlystat_sz; @@ -1726,6 +1909,18 @@ dhd_start_xmit(struct sk_buff *skb, struct net_device *net) #endif } + /* re-align socket buffer if "skb->data" is odd adress */ + if (((unsigned long)(skb->data)) & 0x1) { + unsigned char *data = skb->data; + uint32 length = skb->len; + PKTPUSH(dhd->pub.osh, skb, 1); + memmove(skb->data, data, length); + PKTSETLEN(dhd->pub.osh, skb, length); + } + + ifp = dhd->iflist[ifidx]; + datalen = PKTLEN(dhd->pub.osh, skb); + /* Make sure there's enough room for any header */ if (skb_headroom(skb) < dhd->pub.hdrlen + htsfdlystat_sz) { @@ -1768,12 +1963,15 @@ dhd_start_xmit(struct sk_buff *skb, struct net_device *net) ret = dhd_sendpkt(&dhd->pub, ifidx, pktbuf); - done: - if (ret) - dhd->pub.dstats.tx_dropped++; - else + if (ret) { + ifp->stats.tx_dropped++; + } + else { dhd->pub.tx_packets++; + ifp->stats.tx_packets++; + ifp->stats.tx_bytes += datalen; + } DHD_OS_WAKE_UNLOCK(&dhd->pub); @@ -1850,6 +2048,7 @@ static const char *_get_packet_type_str(uint16 type) } #endif /* DHD_RX_DUMP */ + void dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan) { @@ -1863,56 +2062,52 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan) wl_event_msg_t event; int tout_rx = 0; int tout_ctrl = 0; -#if defined(DHDTHREAD) && defined(RXFRAME_THREAD) void *skbhead = NULL; void *skbprev = NULL; -#endif /* defined(DHDTHREAD) && defined(RXFRAME_THREAD) */ -#ifdef DHD_RX_DUMP -#ifdef DHD_RX_FULL_DUMP - int k; -#endif /* DHD_RX_FULL_DUMP */ +#if defined(DHD_RX_DUMP) || defined(DHD_8021X_DUMP) char *dump_data; uint16 protocol; -#endif /* DHD_RX_DUMP */ +#endif /* DHD_RX_DUMP || DHD_8021X_DUMP */ DHD_TRACE(("%s: Enter\n", __FUNCTION__)); for (i = 0; pktbuf && i < numpkt; i++, pktbuf = pnext) { -#ifdef WLBTAMP struct ether_header *eh; +#ifdef WLBTAMP struct dot11_llc_snap_header *lsh; #endif pnext = PKTNEXT(dhdp->osh, pktbuf); - PKTSETNEXT(wl->sh.osh, pktbuf, NULL); + PKTSETNEXT(dhdp->osh, pktbuf, NULL); ifp = dhd->iflist[ifidx]; if (ifp == NULL) { DHD_ERROR(("%s: ifp is NULL. drop packet\n", __FUNCTION__)); - PKTFREE(dhdp->osh, pktbuf, TRUE); + PKTFREE(dhdp->osh, pktbuf, FALSE); continue; } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) - /* Dropping packets before registering net device to avoid kernel panic */ + eh = (struct ether_header *)PKTDATA(dhdp->osh, pktbuf); + /* Dropping only data packets before registering net device to avoid kernel panic */ #ifndef PROP_TXSTATUS_VSDB - if (!ifp->net || ifp->net->reg_state != NETREG_REGISTERED) { + if ((!ifp->net || ifp->net->reg_state != NETREG_REGISTERED) && + (ntoh16(eh->ether_type) != ETHER_TYPE_BRCM)) { #else - if (!ifp->net || ifp->net->reg_state != NETREG_REGISTERED || !dhd->pub.up) { + if ((!ifp->net || ifp->net->reg_state != NETREG_REGISTERED || !dhd->pub.up) && + (ntoh16(eh->ether_type) != ETHER_TYPE_BRCM)) { #endif /* PROP_TXSTATUS_VSDB */ DHD_ERROR(("%s: net device is NOT registered yet. drop packet\n", __FUNCTION__)); - PKTFREE(dhdp->osh, pktbuf, TRUE); + PKTFREE(dhdp->osh, pktbuf, FALSE); continue; } -#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) */ #ifdef WLBTAMP - eh = (struct ether_header *)PKTDATA(wl->sh.osh, pktbuf); + eh = (struct ether_header *)PKTDATA(dhdp->osh, pktbuf); lsh = (struct dot11_llc_snap_header *)&eh[1]; if ((ntoh16(eh->ether_type) < ETHER_TYPE_MIN) && - (PKTLEN(wl->sh.osh, pktbuf) >= RFC1042_HDR_LEN) && + (PKTLEN(dhdp->osh, pktbuf) >= RFC1042_HDR_LEN) && bcmp(lsh, BT_SIG_SNAP_MPROT, DOT11_LLC_SNAP_HDR_LEN - 2) == 0 && lsh->type == HTON16(BTA_PROT_L2CAP)) { amp_hci_ACL_data_t *ACL_data = (amp_hci_ACL_data_t *) @@ -1922,19 +2117,26 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan) #endif /* WLBTAMP */ #ifdef PROP_TXSTATUS - if (dhdp->wlfc_state && PKTLEN(wl->sh.osh, pktbuf) == 0) { + if (dhd_wlfc_is_header_only_pkt(dhdp, pktbuf)) { /* WLFC may send header only packet when there is an urgent message but no packet to piggy-back on */ - ((athost_wl_status_info_t*)dhdp->wlfc_state)->stats.wlfc_header_only_pkt++; - PKTFREE(dhdp->osh, pktbuf, TRUE); + PKTFREE(dhdp->osh, pktbuf, FALSE); continue; } #endif skb = PKTTONATIVE(dhdp->osh, pktbuf); + ifp = dhd->iflist[ifidx]; + if (ifp == NULL) + ifp = dhd->iflist[0]; + + ASSERT(ifp); + skb->dev = ifp->net; + + /* Get the protocol, maintain skb around eth_type_trans() * The main reason for this hack is for the limitation of * Linux 2.4 where 'eth_type_trans' uses the 'net->hard_header_len' @@ -1947,22 +2149,19 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan) eth = skb->data; len = skb->len; -#ifdef DHD_RX_DUMP +#if defined(DHD_RX_DUMP) || defined(DHD_8021X_DUMP) dump_data = skb->data; protocol = (dump_data[12] << 8) | dump_data[13]; - DHD_ERROR(("RX DUMP - %s\n", _get_packet_type_str(protocol))); -#ifdef DHD_RX_FULL_DUMP - if (protocol != ETHER_TYPE_BRCM) { - for (k = 0; k < skb->len; k++) { - DHD_ERROR(("%02X ", dump_data[k])); - if ((k & 15) == 15) - DHD_ERROR(("\n")); - } - DHD_ERROR(("\n")); + if (protocol == ETHER_TYPE_802_1X) { + DHD_ERROR(("ETHER_TYPE_802_1X: " + "ver %d, type %d, replay %d\n", + dump_data[14], dump_data[15], + dump_data[30])); } -#endif /* DHD_RX_FULL_DUMP */ - +#endif /* DHD_RX_DUMP || DHD_8021X_DUMP */ +#if defined(DHD_RX_DUMP) + DHD_ERROR(("RX DUMP - %s\n", _get_packet_type_str(protocol))); if (protocol != ETHER_TYPE_BRCM) { if (dump_data[0] == 0xFF) { DHD_ERROR(("%s: BROADCAST\n", __FUNCTION__)); @@ -1976,23 +2175,20 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan) DHD_ERROR(("%s: MULTICAST: " MACDBG "\n", __FUNCTION__, MAC2STRDBG(dump_data))); } - - if (protocol == ETHER_TYPE_802_1X) { - DHD_ERROR(("ETHER_TYPE_802_1X: " - "ver %d, type %d, replay %d\n", - dump_data[14], dump_data[15], - dump_data[30])); +#ifdef DHD_RX_FULL_DUMP + { + int k; + for (k = 0; k < skb->len; k++) { + DHD_ERROR(("%02X ", dump_data[k])); + if ((k & 15) == 15) + DHD_ERROR(("\n")); + } + DHD_ERROR(("\n")); } +#endif /* DHD_RX_FULL_DUMP */ } - #endif /* DHD_RX_DUMP */ - ifp = dhd->iflist[ifidx]; - if (ifp == NULL) - ifp = dhd->iflist[0]; - - ASSERT(ifp); - skb->dev = ifp->net; skb->protocol = eth_type_trans(skb, skb->dev); if (skb->pkt_type == PACKET_MULTICAST) { @@ -2009,17 +2205,17 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan) skb_pull(skb, ETH_HLEN); /* Process special event packets and then discard them */ + memset(&event, 0, sizeof(event)); if (ntoh16(skb->protocol) == ETHER_TYPE_BRCM) { dhd_wl_host_event(dhd, &ifidx, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) skb_mac_header(skb), #else skb->mac.raw, -#endif +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22) */ &event, &data); -#if defined(WLBTAMP) || defined(PNO_SUPPORT) wl_event_to_host_order(&event); if (!tout_ctrl) tout_ctrl = DHD_PACKET_TIMEOUT_MS; @@ -2029,63 +2225,68 @@ dhd_rx_frame(dhd_pub_t *dhdp, int ifidx, void *pktbuf, int numpkt, uint8 chan) } #endif /* WLBTAMP */ -#ifdef PNO_SUPPORT +#if defined(PNO_SUPPORT) if (event.event_type == WLC_E_PFN_NET_FOUND) { -#ifdef CUSTOMER_HW4 - tout_ctrl = DHD_PNO_TIMEOUT_MS; -#else - tout_ctrl *= 2; -#endif + /* enforce custom wake lock to garantee that Kernel not suspended */ + tout_ctrl = CUSTOM_PNO_EVENT_LOCK_xTIME * DHD_PACKET_TIMEOUT_MS; } #endif /* PNO_SUPPORT */ -#endif /* defined(WLBTAMP) || defined(PNO_SUPPORT) */ + +#if defined(CUSTOMER_HW4) + if (event.event_type == WLC_E_ESCAN_RESULT) { + PKTFREE(dhdp->osh, pktbuf, TRUE); + continue; + } +#endif } else { tout_rx = DHD_PACKET_TIMEOUT_MS; } ASSERT(ifidx < DHD_MAX_IFS && dhd->iflist[ifidx]); - if (dhd->iflist[ifidx] && !dhd->iflist[ifidx]->state) - ifp = dhd->iflist[ifidx]; + ifp = dhd->iflist[ifidx]; if (ifp->net) ifp->net->last_rx = jiffies; dhdp->dstats.rx_bytes += skb->len; dhdp->rx_packets++; /* Local count */ + ifp->stats.rx_bytes += skb->len; + ifp->stats.rx_packets++; if (in_interrupt()) { netif_rx(skb); } else { - /* If the receive is not processed inside an ISR, - * the softirqd must be woken explicitly to service - * the NET_RX_SOFTIRQ. In 2.6 kernels, this is handled - * by netif_rx_ni(), but in earlier kernels, we need - * to do it manually. - */ -#if defined(DHDTHREAD) && defined(RXFRAME_THREAD) - if (!skbhead) - skbhead = skb; - else - PKTSETNEXT(wl->sh.osh, skbprev, skb); - skbprev = skb; -#else + if (dhd->rxthread_enabled) { + if (!skbhead) + skbhead = skb; + else + PKTSETNEXT(dhdp->osh, skbprev, skb); + skbprev = skb; + } else { + + /* If the receive is not processed inside an ISR, + * the softirqd must be woken explicitly to service + * the NET_RX_SOFTIRQ. In 2.6 kernels, this is handled + * by netif_rx_ni(), but in earlier kernels, we need + * to do it manually. + */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) - netif_rx_ni(skb); + netif_rx_ni(skb); #else - ulong flags; - netif_rx(skb); - local_irq_save(flags); - RAISE_RX_SOFTIRQ(); - local_irq_restore(flags); + ulong flags; + netif_rx(skb); + local_irq_save(flags); + RAISE_RX_SOFTIRQ(); + local_irq_restore(flags); #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) */ -#endif /* defined(DHDTHREAD) && defined(RXFRAME_THREAD) */ + } } } -#if defined(DHDTHREAD) && defined(RXFRAME_THREAD) - if (skbhead) + + if (dhd->rxthread_enabled && skbhead) dhd_sched_rxf(dhdp, skbhead); -#endif + DHD_OS_WAKE_LOCK_RX_TIMEOUT_ENABLE(dhdp, tout_rx); DHD_OS_WAKE_LOCK_CTRL_TIMEOUT_ENABLE(dhdp, tout_ctrl); } @@ -2146,7 +2347,9 @@ dhd_get_stats(struct net_device *net) ifidx = dhd_net2idx(dhd, net); if (ifidx == DHD_BAD_IF) { DHD_ERROR(("%s: BAD_IF\n", __FUNCTION__)); - return NULL; + + memset(&net->stats, 0, sizeof(net->stats)); + return &net->stats; } ifp = dhd->iflist[ifidx]; @@ -2171,7 +2374,6 @@ dhd_get_stats(struct net_device *net) return &ifp->stats; } -#ifdef DHDTHREAD static int dhd_watchdog_thread(void *data) { @@ -2186,12 +2388,6 @@ dhd_watchdog_thread(void *data) dhd_watchdog_prio:(MAX_RT_PRIO-1); setScheduler(current, SCHED_FIFO, ¶m); } -#ifndef USE_KTHREAD_API - DAEMONIZE("dhd_watchdog"); - - /* Run until signal received */ - complete(&tsk->completed); -#endif while (1) if (down_interruptible (&tsk->sema) == 0) { @@ -2208,7 +2404,6 @@ dhd_watchdog_thread(void *data) if (dhd->pub.dongle_reset == FALSE) { DHD_TIMER(("%s:\n", __FUNCTION__)); - /* Call the bus module watchdog */ dhd_bus_watchdog(&dhd->pub); @@ -2232,7 +2427,6 @@ dhd_watchdog_thread(void *data) complete_and_exit(&tsk->completed, 0); } -#endif /* DHDTHREAD */ static void dhd_watchdog(ulong data) { @@ -2243,12 +2437,10 @@ static void dhd_watchdog(ulong data) return; } -#ifdef DHDTHREAD if (dhd->thr_wdt_ctl.thr_pid >= 0) { up(&dhd->thr_wdt_ctl.sema); return; } -#endif /* DHDTHREAD */ dhd_os_sdlock(&dhd->pub); /* Call the bus module watchdog */ @@ -2265,7 +2457,40 @@ static void dhd_watchdog(ulong data) dhd_os_sdunlock(&dhd->pub); } -#ifdef DHDTHREAD +#ifdef ENABLE_ADAPTIVE_SCHED +static void +dhd_sched_policy(int prio) +{ + struct sched_param param; + if (cpufreq_quick_get(0) <= CUSTOM_CPUFREQ_THRESH) { + param.sched_priority = 0; + setScheduler(current, SCHED_NORMAL, ¶m); + } else { + if (get_scheduler_policy(current) != SCHED_FIFO) { + param.sched_priority = (prio < MAX_RT_PRIO)? prio : (MAX_RT_PRIO-1); + setScheduler(current, SCHED_FIFO, ¶m); + } + } +} +#endif /* ENABLE_ADAPTIVE_SCHED */ +#ifdef DEBUG_CPU_FREQ +static int dhd_cpufreq_notifier(struct notifier_block *nb, unsigned long val, void *data) +{ + dhd_info_t *dhd = container_of(nb, struct dhd_info, freq_trans); + struct cpufreq_freqs *freq = data; + if (dhd) { + if (!dhd->new_freq) + goto exit; + if (val == CPUFREQ_POSTCHANGE) { + DHD_ERROR(("cpu freq is changed to %u kHZ on CPU %d\n", + freq->new, freq->cpu)); + *per_cpu_ptr(dhd->new_freq, freq->cpu) = freq->new; + } + } +exit: + return 0; +} +#endif /* DEBUG_CPU_FREQ */ static int dhd_dpc_thread(void *data) { @@ -2281,21 +2506,20 @@ dhd_dpc_thread(void *data) param.sched_priority = (dhd_dpc_prio < MAX_RT_PRIO)?dhd_dpc_prio:(MAX_RT_PRIO-1); setScheduler(current, SCHED_FIFO, ¶m); } -#ifndef USE_KTHREAD_API - DAEMONIZE("dhd_dpc"); - /* DHD_OS_WAKE_LOCK is called in dhd_sched_dpc[dhd_linux.c] down below */ - - /* signal: thread has started */ - complete(&tsk->completed); -#endif #ifdef CUSTOM_DPC_CPUCORE set_cpus_allowed_ptr(current, cpumask_of(CUSTOM_DPC_CPUCORE)); #endif +#ifdef CUSTOM_SET_CPUCORE + dhd->pub.current_dpc = current; +#endif /* CUSTOM_SET_CPUCORE */ /* Run until signal received */ while (1) { - if (down_interruptible(&tsk->sema) == 0) { + if (!binary_sema_down(tsk)) { +#ifdef ENABLE_ADAPTIVE_SCHED + dhd_sched_policy(dhd_dpc_prio); +#endif /* ENABLE_ADAPTIVE_SCHED */ SMP_RD_BARRIER_DEPENDS(); if (tsk->terminated) { break; @@ -2303,12 +2527,27 @@ dhd_dpc_thread(void *data) /* Call bus dpc unless it indicated down (then clean stop) */ if (dhd->pub.busstate != DHD_BUS_DOWN) { - if (dhd_bus_dpc(dhd->pub.bus)) { - up(&tsk->sema); - } - else { - DHD_OS_WAKE_UNLOCK(&dhd->pub); +#if defined(CUSTOMER_HW4) + int resched_cnt = 0; +#endif /* CUSTOMER_HW4 */ + dhd_os_wd_timer_extend(&dhd->pub, TRUE); + while (dhd_bus_dpc(dhd->pub.bus)) { + /* process all data */ +#if defined(CUSTOMER_HW4) + resched_cnt++; + if (resched_cnt > MAX_RESCHED_CNT) { + DHD_ERROR(("%s Calling msleep to" + "let other processes run. \n", + __FUNCTION__)); + dhd->pub.dhd_bug_on = true; + resched_cnt = 0; + OSL_SLEEP(1); + } +#endif /* CUSTOMER_HW4 */ } + dhd_os_wd_timer_extend(&dhd->pub, FALSE); + DHD_OS_WAKE_UNLOCK(&dhd->pub); + } else { if (dhd->pub.up) dhd_bus_stop(dhd->pub.bus, TRUE); @@ -2322,13 +2561,16 @@ dhd_dpc_thread(void *data) complete_and_exit(&tsk->completed, 0); } -#ifdef RXFRAME_THREAD static int dhd_rxf_thread(void *data) { tsk_ctl_t *tsk = (tsk_ctl_t *)data; dhd_info_t *dhd = (dhd_info_t *)tsk->parent; dhd_pub_t *pub = &dhd->pub; +#if defined(WAIT_DEQUEUE) +#define RXF_WATCHDOG_TIME 250 /* BARK_TIME(1000) / */ + ulong watchdogTime = OSL_SYSUPTIME(); /* msec */ +#endif /* This thread doesn't need any user-level access, * so get rid of all our resources @@ -2345,6 +2587,9 @@ dhd_rxf_thread(void *data) /* signal: thread has started */ complete(&tsk->completed); +#ifdef CUSTOM_SET_CPUCORE + dhd->pub.current_rxf = current; +#endif /* CUSTOM_SET_CPUCORE */ /* Run until signal received */ while (1) { @@ -2353,6 +2598,9 @@ dhd_rxf_thread(void *data) #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) ulong flags; #endif +#ifdef ENABLE_ADAPTIVE_SCHED + dhd_sched_policy(dhd_rxf_prio); +#endif /* ENABLE_ADAPTIVE_SCHED */ SMP_RD_BARRIER_DEPENDS(); @@ -2364,7 +2612,6 @@ dhd_rxf_thread(void *data) if (skb == NULL) { continue; } - while (skb) { void *skbnext = PKTNEXT(pub->osh, skb); PKTSETNEXT(pub->osh, skb, NULL); @@ -2380,6 +2627,12 @@ dhd_rxf_thread(void *data) #endif skb = skbnext; } +#if defined(WAIT_DEQUEUE) + if (OSL_SYSUPTIME() - watchdogTime > RXF_WATCHDOG_TIME) { + OSL_SLEEP(1); + watchdogTime = OSL_SYSUPTIME(); + } +#endif DHD_OS_WAKE_UNLOCK(pub); } @@ -2389,8 +2642,6 @@ dhd_rxf_thread(void *data) complete_and_exit(&tsk->completed, 0); } -#endif /* RXFRAME_THREAD */ -#endif /* DHDTHREAD */ static void dhd_dpc(ulong data) @@ -2421,18 +2672,18 @@ dhd_sched_dpc(dhd_pub_t *dhdp) dhd_info_t *dhd = (dhd_info_t *)dhdp->info; DHD_OS_WAKE_LOCK(dhdp); -#ifdef DHDTHREAD if (dhd->thr_dpc_ctl.thr_pid >= 0) { - up(&dhd->thr_dpc_ctl.sema); + /* If the semaphore does not get up, + * wake unlock should be done here + */ + if (!binary_sema_up(&dhd->thr_dpc_ctl)) + DHD_OS_WAKE_UNLOCK(dhdp); return; - } -#endif /* DHDTHREAD */ - - if (dhd->dhd_tasklet_create) + } else { tasklet_schedule(&dhd->tasklet); + } } -#if defined(DHDTHREAD) && defined(RXFRAME_THREAD) static void dhd_sched_rxf(dhd_pub_t *dhdp, void *skb) { @@ -2451,7 +2702,6 @@ dhd_sched_rxf(dhd_pub_t *dhdp, void *skb) } return; } -#endif /* defined(DHDTHREAD) && defined(RXFRAME_THREAD) */ #ifdef TOE /* Retrieve current toe component enables, which are kept as a bitmap in toe_ol iovar */ @@ -2662,14 +2912,21 @@ static bool dhd_check_hang(struct net_device *net, dhd_pub_t *dhdp, int error) { dhd_info_t *dhd; - if (!dhdp) + if (!dhdp) { + DHD_ERROR(("%s: dhdp is NULL\n", __FUNCTION__)); + return FALSE; + } + + if (!dhdp->up) return FALSE; dhd = (dhd_info_t *)dhdp->info; - if (dhd->thr_sysioc_ctl.thr_pid < 0) { +#if !defined(BCMPCIE) + if (dhd->thr_dpc_ctl.thr_pid < 0) { DHD_ERROR(("%s : skipped due to negative pid - unloading?\n", __FUNCTION__)); return FALSE; } +#endif if ((error == -ETIMEDOUT) || (error == -EREMOTEIO) || ((dhdp->busstate == DHD_BUS_DOWN) && (!dhdp->dongle_reset))) { @@ -2681,11 +2938,10 @@ static bool dhd_check_hang(struct net_device *net, dhd_pub_t *dhdp, int error) return FALSE; } -int dhd_ioctl_process(dhd_pub_t *pub, int ifidx, dhd_ioctl_t *ioc) +int dhd_ioctl_process(dhd_pub_t *pub, int ifidx, dhd_ioctl_t *ioc, void *data_buf) { int bcmerror = BCME_OK; int buflen = 0; - void *buf = NULL; struct net_device *net; net = dhd_idx2net(pub, ifidx); @@ -2694,40 +2950,18 @@ int dhd_ioctl_process(dhd_pub_t *pub, int ifidx, dhd_ioctl_t *ioc) goto done; } - /* Copy out any buffer passed */ - if (ioc->buf) { - if (ioc->len == 0) { - DHD_TRACE(("%s: ioc->len=0, returns BCME_BADARG \n", __FUNCTION__)); - bcmerror = BCME_BADARG; - goto done; - } + if (data_buf) buflen = MIN(ioc->len, DHD_IOCTL_MAXLEN); - /* optimization for direct ioctl calls from kernel */ - /* - if (segment_eq(get_fs(), KERNEL_DS)) { - buf = ioc->buf; - } else { - */ - { - if (!(buf = (char*)MALLOC(pub->osh, buflen))) { - bcmerror = BCME_NOMEM; - goto done; - } - if (copy_from_user(buf, ioc->buf, buflen)) { - bcmerror = BCME_BADADDR; - goto done; - } - } - } /* check for local dhd ioctl and handle it */ if (ioc->driver == DHD_IOCTL_MAGIC) { - bcmerror = dhd_ioctl((void *)pub, ioc, buf, buflen); + bcmerror = dhd_ioctl((void *)pub, ioc, data_buf, buflen); if (bcmerror) pub->bcmerror = bcmerror; goto done; } + /* send to dongle (must be up, and wl). */ if (pub->busstate != DHD_BUS_DATA) { bcmerror = BCME_DONGLE_DOWN; @@ -2747,22 +2981,22 @@ int dhd_ioctl_process(dhd_pub_t *pub, int ifidx, dhd_ioctl_t *ioc) * prevent disassoc frame being sent before WPS-DONE frame. */ if (ioc->cmd == WLC_SET_KEY || - (ioc->cmd == WLC_SET_VAR && ioc->buf != NULL && - strncmp("wsec_key", ioc->buf, 9) == 0) || - (ioc->cmd == WLC_SET_VAR && ioc->buf != NULL && - strncmp("bsscfg:wsec_key", ioc->buf, 15) == 0) || + (ioc->cmd == WLC_SET_VAR && data_buf != NULL && + strncmp("wsec_key", data_buf, 9) == 0) || + (ioc->cmd == WLC_SET_VAR && data_buf != NULL && + strncmp("bsscfg:wsec_key", data_buf, 15) == 0) || ioc->cmd == WLC_DISASSOC) dhd_wait_pend8021x(net); #ifdef WLMEDIA_HTSF - if (ioc->buf) { + if (data_buf) { /* short cut wl ioctl calls here */ - if (strcmp("htsf", ioc->buf) == 0) { + if (strcmp("htsf", data_buf) == 0) { dhd_ioctl_htsf_get(dhd, 0); return BCME_OK; } - if (strcmp("htsflate", ioc->buf) == 0) { + if (strcmp("htsflate", data_buf) == 0) { if (ioc->set) { memset(ts, 0, sizeof(tstamp_t)*TSMAX); memset(&maxdelayts, 0, sizeof(tstamp_t)); @@ -2778,7 +3012,7 @@ int dhd_ioctl_process(dhd_pub_t *pub, int ifidx, dhd_ioctl_t *ioc) } return BCME_OK; } - if (strcmp("htsfclear", ioc->buf) == 0) { + if (strcmp("htsfclear", data_buf) == 0) { memset(&vi_d1.bin, 0, sizeof(uint32)*NUMBIN); memset(&vi_d2.bin, 0, sizeof(uint32)*NUMBIN); memset(&vi_d3.bin, 0, sizeof(uint32)*NUMBIN); @@ -2786,16 +3020,16 @@ int dhd_ioctl_process(dhd_pub_t *pub, int ifidx, dhd_ioctl_t *ioc) htsf_seqnum = 0; return BCME_OK; } - if (strcmp("htsfhis", ioc->buf) == 0) { + if (strcmp("htsfhis", data_buf) == 0) { dhd_dump_htsfhisto(&vi_d1, "H to D"); dhd_dump_htsfhisto(&vi_d2, "D to D"); dhd_dump_htsfhisto(&vi_d3, "D to H"); dhd_dump_htsfhisto(&vi_d4, "H to H"); return BCME_OK; } - if (strcmp("tsport", ioc->buf) == 0) { + if (strcmp("tsport", data_buf) == 0) { if (ioc->set) { - memcpy(&tsport, ioc->buf + 7, 4); + memcpy(&tsport, data_buf + 7, 4); } else { DHD_ERROR(("current timestamp port: %d \n", tsport)); } @@ -2805,27 +3039,19 @@ int dhd_ioctl_process(dhd_pub_t *pub, int ifidx, dhd_ioctl_t *ioc) #endif /* WLMEDIA_HTSF */ if ((ioc->cmd == WLC_SET_VAR || ioc->cmd == WLC_GET_VAR) && - ioc->buf != NULL && strncmp("rpc_", ioc->buf, 4) == 0) { + data_buf != NULL && strncmp("rpc_", data_buf, 4) == 0) { #ifdef BCM_FD_AGGR - bcmerror = dhd_fdaggr_ioctl(pub, ifidx, (wl_ioctl_t *)ioc, buf, buflen); + bcmerror = dhd_fdaggr_ioctl(pub, ifidx, (wl_ioctl_t *)ioc, data_buf, buflen); #else bcmerror = BCME_UNSUPPORTED; #endif goto done; } - bcmerror = dhd_wl_ioctl(pub, ifidx, (wl_ioctl_t *)ioc, buf, buflen); + bcmerror = dhd_wl_ioctl(pub, ifidx, (wl_ioctl_t *)ioc, data_buf, buflen); done: dhd_check_hang(net, pub, bcmerror); - if (!bcmerror && buf && ioc->buf) { - if (copy_to_user(ioc->buf, buf, buflen)) - bcmerror = -EFAULT; - } - - if (buf) - MFREE(pub->osh, buf, buflen); - return bcmerror; } @@ -2837,6 +3063,8 @@ dhd_ioctl_entry(struct net_device *net, struct ifreq *ifr, int cmd) int bcmerror = 0; int ifidx; int ret; + void *local_buf = NULL; + u16 buflen = 0; DHD_OS_WAKE_LOCK(&dhd->pub); @@ -2857,7 +3085,7 @@ dhd_ioctl_entry(struct net_device *net, struct ifreq *ifr, int cmd) return -1; } -#if defined(CONFIG_WIRELESS_EXT) +#if defined(WL_WIRELESS_EXT) /* linux wireless extensions */ if ((cmd >= SIOCIWFIRST) && (cmd <= SIOCIWLAST)) { /* may recurse, do NOT lock */ @@ -2865,7 +3093,7 @@ dhd_ioctl_entry(struct net_device *net, struct ifreq *ifr, int cmd) DHD_OS_WAKE_UNLOCK(&dhd->pub); return ret; } -#endif /* defined(CONFIG_WIRELESS_EXT) */ +#endif /* defined(WL_WIRELESS_EXT) */ #if LINUX_VERSION_CODE > KERNEL_VERSION(2, 4, 2) if (cmd == SIOCETHTOOL) { @@ -2907,103 +3135,99 @@ dhd_ioctl_entry(struct net_device *net, struct ifreq *ifr, int cmd) goto done; } - bcmerror = dhd_ioctl_process(&dhd->pub, ifidx, &ioc); + if (ioc.len > 0) { + buflen = MIN(ioc.len, DHD_IOCTL_MAXLEN); + if (!(local_buf = MALLOC(dhd->pub.osh, buflen+1))) { + bcmerror = BCME_NOMEM; + goto done; + } + if (copy_from_user(local_buf, ioc.buf, buflen)) { + bcmerror = BCME_BADADDR; + goto done; + } + *(char *)(local_buf + buflen) = '\0'; + } + + bcmerror = dhd_ioctl_process(&dhd->pub, ifidx, &ioc, local_buf); + + if (!bcmerror && buflen && local_buf && ioc.buf) { + if (copy_to_user(ioc.buf, local_buf, buflen)) + bcmerror = -EFAULT; + } done: + if (local_buf) + MFREE(dhd->pub.osh, local_buf, buflen+1); + DHD_OS_WAKE_UNLOCK(&dhd->pub); return OSL_ERROR(bcmerror); } -#ifdef WL_CFG80211 -static void -dhd_delete_virt_iface(dhd_if_t *ifp) +#if defined(WL_CFG80211) && defined(SUPPORT_DEEP_SLEEP) +/* Flags to indicate if we distingish power off policy when + * user set the memu "Keep Wi-Fi on during sleep" to "Never" + */ +int trigger_deep_sleep = 0; +#endif /* WL_CFG80211 && SUPPORT_DEEP_SLEEP */ + +#ifdef CUSTOMER_HW4 +#ifdef FIX_CPU_MIN_CLOCK +static int dhd_init_cpufreq_fix(dhd_info_t *dhd) { - dhd_info_t *dhd; -#ifdef SOFTAP - unsigned long flags; + if (dhd) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + mutex_init(&dhd->cpufreq_fix); #endif - - if (!ifp || !ifp->info || !ifp->idx) - return; - ASSERT(ifp && ifp->info && ifp->idx); /* Virtual interfaces only */ - dhd = ifp->info; - - DHD_TRACE(("%s: idx %d, state %d\n", __FUNCTION__, ifp->idx, ifp->state)); - - if (wl_cfg80211_is_progress_ifchange()) - return; - - /* Make sure that we don't enter again here if .. */ - /* dhd_op_if is called again from some other context */ - ifp->state = DHD_IF_DELETING; - if (ifp->net != NULL) { - DHD_ERROR(("\n%s: got 'DHD_IF_DEL' state\n", __FUNCTION__)); - - ASSERT_RTNL(); - - netif_stop_queue(ifp->net); - if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211) { - wl_cfg80211_ifdel_ops(ifp->net); - } - -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33)) - if (rtnl_is_locked()) - unregister_netdevice_queue(ifp->net, NULL); - else -#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) */ - unregister_netdev(ifp->net); - - if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211) { - wl_cfg80211_notify_ifdel(); - } - - free_netdev(ifp->net); - ifp->net = NULL; + dhd->cpufreq_fix_status = FALSE; } - - ifp->set_multicast = FALSE; - dhd->iflist[ifp->idx] = NULL; -#ifdef SOFTAP - flags = dhd_os_spin_lock(&dhd->pub); - if (ifp->net == ap_net_dev) - ap_net_dev = NULL; /* NULL SOFTAP global wl0.1 as well */ - dhd_os_spin_unlock(&dhd->pub, flags); -#endif /* SOFTAP */ - MFREE(dhd->pub.osh, ifp, sizeof(*ifp)); + return 0; } -static int -dhd_cleanup_virt_ifaces(dhd_info_t *dhd) +static void dhd_fix_cpu_freq(dhd_info_t *dhd) { - int i = 1; /* Leave ifidx 0 [Primary Interface] */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + mutex_lock(&dhd->cpufreq_fix); +#endif + if (dhd && !dhd->cpufreq_fix_status) { + pm_qos_add_request(&dhd->dhd_cpu_qos, PM_QOS_CPU_FREQ_MIN, 300000); +#ifdef FIX_BUS_MIN_CLOCK + pm_qos_add_request(&dhd->dhd_bus_qos, PM_QOS_BUS_THROUGHPUT, 400000); +#endif /* FIX_BUS_MIN_CLOCK */ + DHD_ERROR(("pm_qos_add_requests called\n")); - DHD_TRACE(("%s: Enter \n", __func__)); + dhd->cpufreq_fix_status = TRUE; + } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + mutex_unlock(&dhd->cpufreq_fix); +#endif +} - for (i = 1; i < DHD_MAX_IFS; i++) { - dhd_net_if_lock_local(dhd); - if (dhd->iflist[i]) { - DHD_TRACE(("Deleting IF: %d \n", i)); - if ((dhd->iflist[i]->state != DHD_IF_DEL) && - (dhd->iflist[i]->state != DHD_IF_DELETING)) { - dhd->iflist[i]->state = DHD_IF_DEL; - dhd->iflist[i]->idx = i; - dhd_delete_virt_iface(dhd->iflist[i]); - } - } - dhd_net_if_unlock_local(dhd); +static void dhd_rollback_cpu_freq(dhd_info_t *dhd) +{ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + mutex_lock(&dhd ->cpufreq_fix); +#endif + if (dhd && dhd->cpufreq_fix_status != TRUE) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + mutex_unlock(&dhd->cpufreq_fix); +#endif + return; } - return 0; -} -#endif /* WL_CFG80211 */ + pm_qos_remove_request(&dhd->dhd_cpu_qos); +#ifdef FIX_BUS_MIN_CLOCK + pm_qos_remove_request(&dhd->dhd_bus_qos); +#endif /* FIX_BUS_MIN_CLOCK */ + DHD_ERROR(("pm_qos_add_requests called\n")); -#if defined(WL_CFG80211) && defined(SUPPORT_DEEP_SLEEP) -/* Flags to indicate if we distingish power off policy when - * user set the memu "Keep Wi-Fi on during sleep" to "Never" - */ -int trigger_deep_sleep = 0; -#endif /* WL_CFG80211 && SUPPORT_DEEP_SLEEP */ + dhd->cpufreq_fix_status = FALSE; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) + mutex_unlock(&dhd->cpufreq_fix); +#endif +} +#endif /* FIX_CPU_MIN_CLOCK */ +#endif /* CUSTOMER_HW4 */ static int dhd_stop(struct net_device *net) @@ -3015,6 +3239,12 @@ dhd_stop(struct net_device *net) if (dhd->pub.up == 0) { goto exit; } + +#if defined(CUSTOMER_HW4) && defined(FIX_CPU_MIN_CLOCK) + if (dhd_get_fw_mode(dhd) == DHD_FLAG_HOSTAP_MODE) + dhd_rollback_cpu_freq(dhd); +#endif /* defined(CUSTOMER_HW4) && defined(FIX_CPU_MIN_CLOCK) */ + ifidx = dhd_net2idx(dhd, net); BCM_REFERENCE(ifidx); @@ -3030,12 +3260,19 @@ dhd_stop(struct net_device *net) * For CFG80211: Clean up all the left over virtual interfaces * when the primary Interface is brought down. [ifconfig wlan0 down] */ - if ((dhd->dhd_state & DHD_ATTACH_STATE_ADD_IF) && - (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211)) { - dhd_cleanup_virt_ifaces(dhd); + if (!dhd_download_fw_on_driverload) { + if ((dhd->dhd_state & DHD_ATTACH_STATE_ADD_IF) && + (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211)) { + int i; + + dhd_net_if_lock_local(dhd); + for (i = 1; i < DHD_MAX_IFS; i++) + dhd_remove_if(&dhd->pub, i, FALSE); + dhd_net_if_unlock_local(dhd); + } } } -#endif +#endif /* WL_CFG80211 */ #ifdef PROP_TXSTATUS dhd_wlfc_cleanup(&dhd->pub, NULL, 0); @@ -3064,10 +3301,40 @@ exit: dhd->pub.rxcnt_timeout = 0; dhd->pub.txcnt_timeout = 0; + dhd->pub.hang_was_sent = 0; + + /* Clear country spec for for built-in type driver */ + if (!dhd_download_fw_on_driverload) { + dhd->pub.dhd_cspec.country_abbrev[0] = 0x00; + dhd->pub.dhd_cspec.rev = 0; + dhd->pub.dhd_cspec.ccode[0] = 0x00; + } + DHD_OS_WAKE_UNLOCK(&dhd->pub); return 0; } +#if defined(WL_CFG80211) && (defined(USE_INITIAL_2G_SCAN) || \ + defined(USE_INITIAL_SHORT_DWELL_TIME)) +extern bool g_first_broadcast_scan; +#endif /* OEM_ANDROID && WL_CFG80211 && (USE_INITIAL_2G_SCAN || USE_INITIAL_SHORT_DWELL_TIME) */ + +#ifdef WL11U +static int dhd_interworking_enable(dhd_pub_t *dhd) +{ + char iovbuf[WLC_IOCTL_SMLEN]; + uint32 enable = true; + int ret = BCME_OK; + + bcm_mkiovar("interworking", (char *)&enable, sizeof(enable), iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) { + DHD_ERROR(("%s: enableing interworking failed, ret=%d\n", __FUNCTION__, ret)); + } + + return ret; +} +#endif /* WL11u */ + static int dhd_open(struct net_device *net) { @@ -3078,52 +3345,35 @@ dhd_open(struct net_device *net) int ifidx; int32 ret = 0; +#ifdef CUSTOMER_HW4 /* WAR : to prevent calling dhd_open abnormally in quick succession after hang event */ if (dhd->pub.hang_was_sent == 1) { DHD_ERROR(("%s: HANG was sent up earlier\n", __FUNCTION__)); - return -1; + /* Force to bring down WLAN interface in case dhd_stop() is not called + * from the upper layer when HANG event is triggered. + */ + if (!dhd_download_fw_on_driverload && dhd->pub.up == 1) { + DHD_ERROR(("%s: WLAN interface is not brought down\n", __FUNCTION__)); + dhd_stop(net); + } else { + return -1; + } } -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 && 1 +#endif /* CUSTOMER_HW4 */ + +#if defined(MULTIPLE_SUPPLICANT) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) if (mutex_is_locked(&_dhd_sdio_mutex_lock_) != 0) { DHD_ERROR(("%s : dhd_open: call dev open before insmod complete!\n", __FUNCTION__)); } mutex_lock(&_dhd_sdio_mutex_lock_); #endif +#endif /* MULTIPLE_SUPPLICANT */ DHD_OS_WAKE_LOCK(&dhd->pub); - /* Update FW path if it was changed */ - if (strlen(firmware_path) != 0) { - if (firmware_path[strlen(firmware_path)-1] == '\n') - firmware_path[strlen(firmware_path)-1] = '\0'; - bzero(fw_path, MOD_PARAM_PATHLEN); - strncpy(fw_path, firmware_path, sizeof(fw_path)-1); - firmware_path[0] = '\0'; - } - -#ifdef CUSTOMER_HW4 - /* Update NVRAM path if it was changed */ - if (!dhd_download_fw_on_driverload && (strlen(nvram_path) != 0)) { - if (nvram_path[strlen(nvram_path)-1] == '\n') - nvram_path[strlen(nvram_path)-1] = '\0'; - bzero(nv_path, MOD_PARAM_PATHLEN); - strncpy(nv_path, nvram_path, sizeof(nv_path)-1); - } -#endif /* CUSTOMER_HW4 */ - -#if defined(CUSTOMER_HW4) && defined(SUPPORT_MULTIPLE_REVISION) - /* dhd_open() can be call several times when loading failed */ - if (strlen(firmware_path) != 0) { - ret = concate_revision(dhd->pub.bus, fw_path, MOD_PARAM_PATHLEN, - nv_path, MOD_PARAM_PATHLEN); - if (ret != 0) { - DHD_ERROR(("%s: fail to concatnate revison \n", __FUNCTION__)); - goto exit; - } - } -#endif /* CUSTOMER_HW4 && SUPPORT_MULTIPLE_REVISION */ - dhd->pub.dongle_trap_occured = 0; dhd->pub.hang_was_sent = 0; + #if !defined(WL_CFG80211) /* * Force start if ifconfig_up gets called before START command @@ -3148,7 +3398,7 @@ dhd_open(struct net_device *net) goto exit; } - if (!dhd->iflist[ifidx] || dhd->iflist[ifidx]->state == DHD_IF_DEL) { + if (!dhd->iflist[ifidx]) { DHD_ERROR(("%s: Error: called when IF already deleted\n", __FUNCTION__)); ret = -1; goto exit; @@ -3157,8 +3407,11 @@ dhd_open(struct net_device *net) if (ifidx == 0) { atomic_set(&dhd->pend_8021x_cnt, 0); #if defined(WL_CFG80211) - DHD_ERROR(("\n%s\n", dhd_version)); if (!dhd_download_fw_on_driverload) { + DHD_ERROR(("\n%s\n", dhd_version)); +#if defined(USE_INITIAL_2G_SCAN) || defined(USE_INITIAL_SHORT_DWELL_TIME) + g_first_broadcast_scan = TRUE; +#endif /* USE_INITIAL_2G_SCAN || USE_INITIAL_SHORT_DWELL_TIME */ ret = wl_android_wifi_on(net); if (ret != 0) { DHD_ERROR(("%s : wl_android_wifi_on failed (%d)\n", @@ -3174,11 +3427,20 @@ dhd_open(struct net_device *net) * "Keep Wi-Fi on during sleep" to "Never" */ if (trigger_deep_sleep) { +#if defined(USE_INITIAL_2G_SCAN) || defined(USE_INITIAL_SHORT_DWELL_TIME) + g_first_broadcast_scan = TRUE; +#endif /* USE_INITIAL_2G_SCAN || USE_INITIAL_SHORT_DWELL_TIME */ dhd_deepsleep(net, 0); trigger_deep_sleep = 0; } } #endif /* SUPPORT_DEEP_SLEEP */ +#if defined(CUSTOMER_HW4) && defined(FIX_CPU_MIN_CLOCK) + if (dhd_get_fw_mode(dhd) == DHD_FLAG_HOSTAP_MODE) { + dhd_init_cpufreq_fix(dhd); + dhd_fix_cpu_freq(dhd); + } +#endif /* defined(CUSTOMER_HW4) && defined(FIX_CPU_MIN_CLOCK) */ #endif if (dhd->pub.busstate != DHD_BUS_DATA) { @@ -3227,9 +3489,11 @@ exit: DHD_OS_WAKE_UNLOCK(&dhd->pub); -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 && 1 +#if defined(MULTIPLE_SUPPLICANT) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) mutex_unlock(&_dhd_sdio_mutex_lock_); #endif +#endif /* MULTIPLE_SUPPLICANT */ return ret; } @@ -3243,14 +3507,15 @@ int dhd_do_driver_init(struct net_device *net) return -EINVAL; } -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 && 1 #ifdef MULTIPLE_SUPPLICANT +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) if (mutex_is_locked(&_dhd_sdio_mutex_lock_) != 0) { DHD_ERROR(("%s : dhdsdio_probe is already running!\n", __FUNCTION__)); return 0; } -#endif #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) */ +#endif /* MULTIPLE_SUPPLICANT */ + /* && defined(OEM_ANDROID) && defined(BCMSDIO) */ dhd = *(dhd_info_t **)netdev_priv(net); @@ -3269,94 +3534,175 @@ int dhd_do_driver_init(struct net_device *net) return 0; } -osl_t * -dhd_osl_attach(void *pdev, uint bustype) +int +dhd_event_ifadd(dhd_info_t *dhdinfo, wl_event_data_if_t *ifevent, char *name, uint8 *mac) { - return osl_attach(pdev, bustype, TRUE); -} -void -dhd_osl_detach(osl_t *osh) -{ - if (MALLOCED(osh)) { - DHD_ERROR(("%s: MEMORY LEAK %d bytes\n", __FUNCTION__, MALLOCED(osh))); - } - osl_detach(osh); -#if 1 && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) - dhd_registration_check = FALSE; - up(&dhd_registration_sem); -#if defined(BCMLXSDMMC) - up(&dhd_chipup_sem); +#ifdef WL_CFG80211 + if (wl_cfg80211_notify_ifadd(ifevent->ifidx, name, mac, ifevent->bssidx) == BCME_OK) + return BCME_OK; #endif -#endif + + /* handle IF event caused by wl commands, SoftAP, WEXT and + * anything else. This has to be done asynchronously otherwise + * DPC will be blocked (and iovars will timeout as DPC has no chance + * to read the response back) + */ + if (ifevent->ifidx > 0) { + dhd_if_event_t *if_event = MALLOC(dhdinfo->pub.osh, sizeof(dhd_if_event_t)); + + memcpy(&if_event->event, ifevent, sizeof(if_event->event)); + memcpy(if_event->mac, mac, ETHER_ADDR_LEN); + strncpy(if_event->name, name, IFNAMSIZ); + if_event->name[IFNAMSIZ - 1] = '\0'; + dhd_deferred_schedule_work((void *)if_event, DHD_WQ_WORK_IF_ADD, + dhd_ifadd_event_handler, DHD_WORK_PRIORITY_LOW); + } + + return BCME_OK; } int -dhd_add_if(dhd_info_t *dhd, int ifidx, void *handle, char *name, - uint8 *mac_addr, uint32 flags, uint8 bssidx) +dhd_event_ifdel(dhd_info_t *dhdinfo, wl_event_data_if_t *ifevent, char *name, uint8 *mac) { - dhd_if_t *ifp; + dhd_if_event_t *if_event; - DHD_TRACE(("%s: idx %d, handle->%p\n", __FUNCTION__, ifidx, handle)); +#ifdef WL_CFG80211 +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) + wl_cfg80211_notify_ifdel(ifevent->ifidx, name, mac, ifevent->bssidx); +#else + if (wl_cfg80211_notify_ifdel(ifevent->ifidx, name, mac, ifevent->bssidx) == BCME_OK) + return BCME_OK; +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) */ +#endif /* WL_CFG80211 */ - ASSERT(dhd && (ifidx < DHD_MAX_IFS)); + /* handle IF event caused by wl commands, SoftAP, WEXT and + * anything else + */ + if_event = MALLOC(dhdinfo->pub.osh, sizeof(dhd_if_event_t)); + memcpy(&if_event->event, ifevent, sizeof(if_event->event)); + memcpy(if_event->mac, mac, ETHER_ADDR_LEN); + strncpy(if_event->name, name, IFNAMSIZ); + if_event->name[IFNAMSIZ - 1] = '\0'; + dhd_deferred_schedule_work((void *)if_event, DHD_WQ_WORK_IF_DEL, + dhd_ifdel_event_handler, DHD_WORK_PRIORITY_LOW); + + return BCME_OK; +} + +/* unregister and free the existing net_device interface (if any) in iflist and + * allocate a new one. the slot is reused. this function does NOT register the + * new interface to linux kernel. dhd_register_if does the job + */ +struct net_device* +dhd_allocate_if(dhd_pub_t *dhdpub, int ifidx, char *name, + uint8 *mac, uint8 bssidx, bool need_rtnl_lock) +{ + dhd_info_t *dhdinfo = (dhd_info_t *)dhdpub->info; + dhd_if_t *ifp; + + ASSERT(dhdinfo && (ifidx < DHD_MAX_IFS)); + ifp = dhdinfo->iflist[ifidx]; - ifp = dhd->iflist[ifidx]; if (ifp != NULL) { if (ifp->net != NULL) { - netif_stop_queue(ifp->net); - unregister_netdev(ifp->net); - free_netdev(ifp->net); + DHD_ERROR(("%s: free existing IF %s\n", __FUNCTION__, ifp->net->name)); + + /* in unregister_netdev case, the interface gets freed by net->destructor + * (which is set to free_netdev) + */ + if (ifp->net->reg_state == NETREG_UNINITIALIZED) { + free_netdev(ifp->net); + } else { + netif_stop_queue(ifp->net); + if (need_rtnl_lock) + unregister_netdev(ifp->net); + else + unregister_netdevice(ifp->net); + } + ifp->net = NULL; } - } else - if ((ifp = MALLOC(dhd->pub.osh, sizeof(dhd_if_t))) == NULL) { - DHD_ERROR(("%s: OOM - dhd_if_t(%d)\n", __FUNCTION__, sizeof(dhd_if_t))); - return -ENOMEM; + } else { + ifp = MALLOC(dhdinfo->pub.osh, sizeof(dhd_if_t)); + if (ifp == NULL) { + DHD_ERROR(("%s: OOM - dhd_if_t(%zu)\n", __FUNCTION__, sizeof(dhd_if_t))); + return NULL; } + } memset(ifp, 0, sizeof(dhd_if_t)); - ifp->event2cfg80211 = FALSE; - ifp->info = dhd; - dhd->iflist[ifidx] = ifp; - strncpy(ifp->name, name, IFNAMSIZ); - ifp->name[IFNAMSIZ] = '\0'; - if (mac_addr != NULL) - memcpy(&ifp->mac_addr, mac_addr, ETHER_ADDR_LEN); - - if (handle == NULL) { - ifp->state = DHD_IF_ADD; - ifp->idx = ifidx; - ifp->bssidx = bssidx; - ASSERT(dhd->thr_sysioc_ctl.thr_pid >= 0); - up(&dhd->thr_sysioc_ctl.sema); - } else - ifp->net = (struct net_device *)handle; + ifp->info = dhdinfo; + ifp->idx = ifidx; + ifp->bssidx = bssidx; + if (mac != NULL) + memcpy(&ifp->mac_addr, mac, ETHER_ADDR_LEN); - if (ifidx == 0) { - ifp->event2cfg80211 = TRUE; + /* Allocate etherdev, including space for private structure */ + ifp->net = alloc_etherdev(sizeof(dhdinfo)); + if (ifp->net == NULL) { + DHD_ERROR(("%s: OOM - alloc_etherdev(%zu)\n", __FUNCTION__, sizeof(dhdinfo))); + goto fail; + } + memcpy(netdev_priv(ifp->net), &dhdinfo, sizeof(dhdinfo)); + if (name && name[0]) { + strncpy(ifp->net->name, name, IFNAMSIZ); + ifp->net->name[IFNAMSIZ - 1] = '\0'; } + ifp->net->destructor = free_netdev; + strncpy(ifp->name, ifp->net->name, IFNAMSIZ); + ifp->name[IFNAMSIZ - 1] = '\0'; + dhdinfo->iflist[ifidx] = ifp; + return ifp->net; - return 0; +fail: + if (ifp != NULL) { + if (ifp->net != NULL) { + free_netdev(ifp->net); + ifp->net = NULL; + } + MFREE(dhdinfo->pub.osh, ifp, sizeof(*ifp)); + ifp = NULL; + } + dhdinfo->iflist[ifidx] = NULL; + return NULL; } -void -dhd_del_if(dhd_info_t *dhd, int ifidx) +/* unregister and free the the net_device interface associated with the indexed + * slot, also free the slot memory and set the slot pointer to NULL + */ +int +dhd_remove_if(dhd_pub_t *dhdpub, int ifidx, bool need_rtnl_lock) { + dhd_info_t *dhdinfo = (dhd_info_t *)dhdpub->info; dhd_if_t *ifp; - DHD_TRACE(("%s: idx %d\n", __FUNCTION__, ifidx)); + ifp = dhdinfo->iflist[ifidx]; + if (ifp != NULL) { + if (ifp->net != NULL) { + DHD_ERROR(("deleting interface '%s' idx %d\n", ifp->net->name, ifp->idx)); + + /* in unregister_netdev case, the interface gets freed by net->destructor + * (which is set to free_netdev) + */ + if (ifp->net->reg_state == NETREG_UNINITIALIZED) { + free_netdev(ifp->net); + } else { + netif_stop_queue(ifp->net); + + if (need_rtnl_lock) + unregister_netdev(ifp->net); + else + unregister_netdevice(ifp->net); + } + ifp->net = NULL; + } + + dhdinfo->iflist[ifidx] = NULL; + MFREE(dhdinfo->pub.osh, ifp, sizeof(*ifp)); - ASSERT(dhd && ifidx && (ifidx < DHD_MAX_IFS)); - ifp = dhd->iflist[ifidx]; - if (!ifp) { - DHD_ERROR(("%s: Null interface\n", __FUNCTION__)); - return; } - ifp->state = DHD_IF_DEL; - ifp->idx = ifidx; - ASSERT(dhd->thr_sysioc_ctl.thr_pid >= 0); - up(&dhd->thr_sysioc_ctl.sema); + return BCME_OK; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) @@ -3387,68 +3733,58 @@ static struct net_device_ops dhd_ops_virt = { }; #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) */ +#ifdef DEBUGGER +extern void debugger_init(void *bus_handle); +#endif + + dhd_pub_t * dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) { dhd_info_t *dhd = NULL; struct net_device *net = NULL; + char if_name[IFNAMSIZ] = {'\0'}; + uint32 bus_type = -1; + uint32 bus_num = -1; + uint32 slot_num = -1; + wifi_adapter_info_t *adapter = NULL; dhd_attach_states_t dhd_state = DHD_ATTACH_STATE_INIT; DHD_TRACE(("%s: Enter\n", __FUNCTION__)); - /* updates firmware nvram path if it was provided as module parameters */ - if (strlen(firmware_path) != 0) { - bzero(fw_path, MOD_PARAM_PATHLEN); - strncpy(fw_path, firmware_path, sizeof(fw_path) - 1); - } - if (strlen(nvram_path) != 0) { - bzero(nv_path, MOD_PARAM_PATHLEN); - strncpy(nv_path, nvram_path, sizeof(nv_path) -1); - } -#if defined(CUSTOMER_HW4) && defined(SUPPORT_MULTIPLE_REVISION) - if (strlen(fw_path) != 0 && - concate_revision(bus, fw_path, MOD_PARAM_PATHLEN, - nv_path, MOD_PARAM_PATHLEN) != 0) { - DHD_ERROR(("%s: fail to concatnate revison \n", __FUNCTION__)); - goto fail; - } -#endif /* CUSTOMER_HW4 && SUPPORT_MULTIPLE_REVISION */ - - /* Allocate etherdev, including space for private structure */ - if (!(net = alloc_etherdev(sizeof(dhd)))) { - DHD_ERROR(("%s: OOM - alloc_etherdev\n", __FUNCTION__)); - goto fail; - } - dhd_state |= DHD_ATTACH_STATE_NET_ALLOC; + /* will implement get_ids for DBUS later */ + dhd_bus_get_ids(bus, &bus_type, &bus_num, &slot_num); + adapter = dhd_wifi_platform_get_adapter(bus_type, bus_num, slot_num); /* Allocate primary dhd_info */ -#if defined(CONFIG_DHD_USE_STATIC_BUF) - dhd = (void *)dhd_os_prealloc(osh, DHD_PREALLOC_DHD_INFO, sizeof(dhd_info_t)); - if (!dhd) { - DHD_INFO(("%s: OOM - Pre-alloc dhd_info\n", __FUNCTION__)); -#endif /* CONFIG_DHD_USE_STATIC_BUF */ - if (!(dhd = MALLOC(osh, sizeof(dhd_info_t)))) { + dhd = wifi_platform_prealloc(adapter, DHD_PREALLOC_DHD_INFO, sizeof(dhd_info_t)); + if (dhd == NULL) { + dhd = MALLOC(osh, sizeof(dhd_info_t)); + if (dhd == NULL) { DHD_ERROR(("%s: OOM - alloc dhd_info\n", __FUNCTION__)); goto fail; } -#if defined(CONFIG_DHD_USE_STATIC_BUF) } -#endif /* CONFIG_DHD_USE_STATIC_BUF */ memset(dhd, 0, sizeof(dhd_info_t)); + dhd_state |= DHD_ATTACH_STATE_DHD_ALLOC; -#ifdef DHDTHREAD + dhd->pub.osh = osh; + dhd->adapter = adapter; + +#ifdef GET_CUSTOM_MAC_ENABLE + wifi_platform_get_mac_addr(dhd->adapter, dhd->pub.mac.octet); +#endif /* GET_CUSTOM_MAC_ENABLE */ dhd->thr_dpc_ctl.thr_pid = DHD_PID_KT_TL_INVALID; dhd->thr_wdt_ctl.thr_pid = DHD_PID_KT_INVALID; -#endif /* DHDTHREAD */ - dhd->dhd_tasklet_create = FALSE; - dhd->thr_sysioc_ctl.thr_pid = DHD_PID_KT_INVALID; - dhd_state |= DHD_ATTACH_STATE_DHD_ALLOC; - /* - * Save the dhd_info into the priv + /* Initialize thread based operation and lock */ + sema_init(&dhd->sdsem, 1); + + /* Some DHD modules (e.g. cfg80211) configures operation mode based on firmware name. + * This is indeed a hack but we have to make it work properly before we have a better + * solution */ - memcpy((void *)netdev_priv(net), &dhd, sizeof(dhd)); - dhd->pub.osh = osh; + dhd_update_fw_nv_path(dhd); /* Link to info module */ dhd->pub.info = dhd; @@ -3460,15 +3796,15 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) if (iface_name[0]) { int len; char ch; - strncpy(net->name, iface_name, IFNAMSIZ); - net->name[IFNAMSIZ - 1] = 0; - len = strlen(net->name); - ch = net->name[len - 1]; + strncpy(if_name, iface_name, IFNAMSIZ); + if_name[IFNAMSIZ - 1] = 0; + len = strlen(if_name); + ch = if_name[len - 1]; if ((ch > '9' || ch < '0') && (len < IFNAMSIZ - 2)) - strcat(net->name, "%d"); + strcat(if_name, "%d"); } - - if (dhd_add_if(dhd, 0, (void *)net, net->name, NULL, 0, 0) == DHD_BAD_IF) + net = dhd_allocate_if(&dhd->pub, 0, if_name, NULL, 0, TRUE); + if (net == NULL) goto fail; dhd_state |= DHD_ATTACH_STATE_ADD_IF; @@ -3482,11 +3818,10 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) #ifdef PROP_TXSTATUS spin_lock_init(&dhd->wlfc_spinlock); -#ifdef PROP_TXSTATUS_VSDB - dhd->pub.wlfc_enabled = FALSE; -#else - dhd->pub.wlfc_enabled = TRUE; -#endif /* PROP_TXSTATUS_VSDB */ + + dhd->pub.skip_fc = dhd_wlfc_skip_fc; + dhd->pub.plat_init = dhd_wlfc_plat_init; + dhd->pub.plat_deinit = dhd_wlfc_plat_deinit; #endif /* PROP_TXSTATUS */ /* Initialize other structure content */ @@ -3497,9 +3832,11 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) spin_lock_init(&dhd->sdlock); spin_lock_init(&dhd->txqlock); spin_lock_init(&dhd->dhd_lock); -#if defined(DHDTHREAD) && defined(RXFRAME_THREAD) spin_lock_init(&dhd->rxf_lock); -#endif /* defined(DHDTHREAD) && defined(RXFRAME_THREAD) */ +#if defined(RXFRAME_THREAD) + dhd->rxthread_enabled = TRUE; +#endif /* defined(RXFRAME_THREAD) */ + #ifdef DHDTCPACK_SUPPRESS spin_lock_init(&dhd->tcpack_lock); #endif /* DHDTCPACK_SUPPRESS */ @@ -3510,21 +3847,14 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) dhd->wakelock_wd_counter = 0; dhd->wakelock_rx_timeout_enable = 0; dhd->wakelock_ctrl_timeout_enable = 0; + dhd->waive_wakelock = FALSE; #ifdef CONFIG_HAS_WAKELOCK - dhd->wl_wifi = MALLOC(osh, sizeof(struct wake_lock)); - dhd->wl_rxwake = MALLOC(osh, sizeof(struct wake_lock)); - dhd->wl_ctrlwake = MALLOC(osh, sizeof(struct wake_lock)); - dhd->wl_wdwake = MALLOC(osh, sizeof(struct wake_lock)); - if (!dhd->wl_wifi || !dhd->wl_rxwake || !dhd->wl_ctrlwake || !dhd->wl_wdwake) { - DHD_ERROR(("%s: mem alloc for wake lock failed\n", __FUNCTION__)); - goto fail; - } - wake_lock_init(dhd->wl_wifi, WAKE_LOCK_SUSPEND, "wlan_wake"); - wake_lock_init(dhd->wl_rxwake, WAKE_LOCK_SUSPEND, "wlan_rx_wake"); - wake_lock_init(dhd->wl_ctrlwake, WAKE_LOCK_SUSPEND, "wlan_ctrl_wake"); - wake_lock_init(dhd->wl_wdwake, WAKE_LOCK_SUSPEND, "wlan_wd_wake"); + wake_lock_init(&dhd->wl_wifi, WAKE_LOCK_SUSPEND, "wlan_wake"); + wake_lock_init(&dhd->wl_rxwake, WAKE_LOCK_SUSPEND, "wlan_rx_wake"); + wake_lock_init(&dhd->wl_ctrlwake, WAKE_LOCK_SUSPEND, "wlan_ctrl_wake"); + wake_lock_init(&dhd->wl_wdwake, WAKE_LOCK_SUSPEND, "wlan_wd_wake"); #endif /* CONFIG_HAS_WAKELOCK */ -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) mutex_init(&dhd->dhd_net_if_mutex); mutex_init(&dhd->dhd_suspend_mutex); #endif @@ -3547,7 +3877,7 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) dhd_monitor_init(&dhd->pub); dhd_state |= DHD_ATTACH_STATE_CFG80211; #endif -#if defined(CONFIG_WIRELESS_EXT) +#if defined(WL_WIRELESS_EXT) /* Attach and link in the iw */ if (!(dhd_state & DHD_ATTACH_STATE_CFG80211)) { if (wl_iw_attach(net, (void *)&dhd->pub) != 0) { @@ -3556,23 +3886,14 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) } dhd_state |= DHD_ATTACH_STATE_WL_ATTACH; } -#endif /* defined(CONFIG_WIRELESS_EXT) */ +#endif /* defined(WL_WIRELESS_EXT) */ /* Set up the watchdog timer */ init_timer(&dhd->timer); dhd->timer.data = (ulong)dhd; dhd->timer.function = dhd_watchdog; - -#ifdef DHDTHREAD - /* Initialize thread based operation and lock */ - sema_init(&dhd->sdsem, 1); - if ((dhd_watchdog_prio >= 0) && (dhd_dpc_prio >= 0)) { - dhd->threads_only = TRUE; - } - else { - dhd->threads_only = FALSE; - } + dhd->default_wd_interval = dhd_watchdog_ms; if (dhd_watchdog_prio >= 0) { /* Initialize watchdog thread */ @@ -3582,6 +3903,10 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) dhd->thr_wdt_ctl.thr_pid = -1; } +#ifdef DEBUGGER + debugger_init((void *) bus); +#endif + /* Set up the bottom half handler */ if (dhd_dpc_prio >= 0) { /* Initialize DPC thread */ @@ -3591,36 +3916,28 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) tasklet_init(&dhd->tasklet, dhd_dpc, (ulong)dhd); dhd->thr_dpc_ctl.thr_pid = -1; } -#ifdef RXFRAME_THREAD - bzero(&dhd->pub.skbbuf[0], sizeof(void *) * MAXSKBPEND); - /* Initialize RXF thread */ - PROC_START(dhd_rxf_thread, dhd, &dhd->thr_rxf_ctl, 0, "dhd_rxf"); -#endif -#else - /* Set up the bottom half handler */ - tasklet_init(&dhd->tasklet, dhd_dpc, (ulong)dhd); - dhd->dhd_tasklet_create = TRUE; -#endif /* DHDTHREAD */ - if (dhd_sysioc) { - PROC_START(_dhd_sysioc_thread, dhd, &dhd->thr_sysioc_ctl, 0, "dhd_sysioc"); - } else { - dhd->thr_sysioc_ctl.thr_pid = -1; + if (dhd->rxthread_enabled) { + bzero(&dhd->pub.skbbuf[0], sizeof(void *) * MAXSKBPEND); + /* Initialize RXF thread */ + PROC_START(dhd_rxf_thread, dhd, &dhd->thr_rxf_ctl, 0, "dhd_rxf"); } - dhd_state |= DHD_ATTACH_STATE_THREADS_CREATED; -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && (1) - INIT_WORK(&dhd->work_hang, dhd_hang_process); -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ + dhd_state |= DHD_ATTACH_STATE_THREADS_CREATED; /* * Save the dhd_info into the priv */ memcpy(netdev_priv(net), &dhd, sizeof(dhd)); -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) - register_pm_notifier(&dhd_sleep_pm_notifier); -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */ +#if defined(CONFIG_PM_SLEEP) + dhd->pm_notifier.notifier_call = dhd_pm_callback; + dhd->pm_notifier.priority = 10; + if (!dhd_pm_notifier_registered) { + dhd_pm_notifier_registered = TRUE; + register_pm_notifier(&dhd->pm_notifier); + } +#endif /* CONFIG_PM_SLEEP */ #if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) dhd->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 20; @@ -3632,22 +3949,36 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) #ifdef ARP_OFFLOAD_SUPPORT dhd->pend_ipaddr = 0; - register_inetaddr_notifier(&dhd_notifier); + if (!dhd_inetaddr_notifier_registered) { + dhd_inetaddr_notifier_registered = TRUE; + register_inetaddr_notifier(&dhd_inetaddr_notifier); + } #endif /* ARP_OFFLOAD_SUPPORT */ - + if (!dhd_inet6addr_notifier_registered) { + dhd_inet6addr_notifier_registered = TRUE; + register_inet6addr_notifier(&dhd_inet6addr_notifier); + } + dhd->dhd_deferred_wq = dhd_deferred_work_init((void *)dhd); +#ifdef DEBUG_CPU_FREQ + dhd->new_freq = alloc_percpu(int); + dhd->freq_trans.notifier_call = dhd_cpufreq_notifier; + cpufreq_register_notifier(&dhd->freq_trans, CPUFREQ_TRANSITION_NOTIFIER); +#endif #ifdef DHDTCPACK_SUPPRESS + dhd->pub.tcpack_sup_enabled = TRUE; dhd->pub.tcp_ack_info_cnt = 0; bzero(dhd->pub.tcp_ack_info_tbl, sizeof(struct tcp_ack_info)*MAXTCPSTREAMS); #endif /* DHDTCPACK_SUPPRESS */ dhd_state |= DHD_ATTACH_STATE_DONE; dhd->dhd_state = dhd_state; + + dhd->unit = dhd_found + instance_base; + dhd_found++; return &dhd->pub; fail: - if (dhd_state < DHD_ATTACH_STATE_DHD_ALLOC) { - if (net) free_netdev(net); - } else { + if (dhd_state >= DHD_ATTACH_STATE_DHD_ALLOC) { DHD_TRACE(("%s: Calling dhd_detach dhd_state 0x%x &dhd->pub %p\n", __FUNCTION__, dhd_state, &dhd->pub)); dhd->dhd_state = dhd_state; @@ -3658,6 +3989,144 @@ fail: return NULL; } +int dhd_get_fw_mode(dhd_info_t *dhdinfo) +{ + if (strstr(dhdinfo->fw_path, "_apsta") != NULL) + return DHD_FLAG_HOSTAP_MODE; + if (strstr(dhdinfo->fw_path, "_p2p") != NULL) + return DHD_FLAG_P2P_MODE; + if (strstr(dhdinfo->fw_path, "_ibss") != NULL) + return DHD_FLAG_IBSS_MODE; + if (strstr(dhdinfo->fw_path, "_mfg") != NULL) + return DHD_FLAG_MFG_MODE; + + return DHD_FLAG_STA_MODE; +} + +bool dhd_update_fw_nv_path(dhd_info_t *dhdinfo) +{ + int fw_len; + int nv_len; + const char *fw = NULL; + const char *nv = NULL; + wifi_adapter_info_t *adapter = dhdinfo->adapter; + + + /* Update firmware and nvram path. The path may be from adapter info or module parameter + * The path from adapter info is used for initialization only (as it won't change). + * + * The firmware_path/nvram_path module parameter may be changed by the system at run + * time. When it changes we need to copy it to dhdinfo->fw_path. Also Android private + * command may change dhdinfo->fw_path. As such we need to clear the path info in + * module parameter after it is copied. We won't update the path until the module parameter + * is changed again (first character is not '\0') + */ + + /* set default firmware and nvram path for built-in type driver */ + if (!dhd_download_fw_on_driverload) { +#ifdef CONFIG_BCMDHD_FW_PATH + fw = CONFIG_BCMDHD_FW_PATH; +#endif /* CONFIG_BCMDHD_FW_PATH */ +#ifdef CONFIG_BCMDHD_NVRAM_PATH + nv = CONFIG_BCMDHD_NVRAM_PATH; +#endif /* CONFIG_BCMDHD_NVRAM_PATH */ + } + + /* check if we need to initialize the path */ + if (dhdinfo->fw_path[0] == '\0') { + if (adapter && adapter->fw_path && adapter->fw_path[0] != '\0') + fw = adapter->fw_path; + + } + if (dhdinfo->nv_path[0] == '\0') { + if (adapter && adapter->nv_path && adapter->nv_path[0] != '\0') + nv = adapter->nv_path; + } + + /* Use module parameter if it is valid, EVEN IF the path has not been initialized + * + * TODO: need a solution for multi-chip, can't use the same firmware for all chips + */ + if (firmware_path[0] != '\0') + fw = firmware_path; + if (nvram_path[0] != '\0') + nv = nvram_path; + + if (fw && fw[0] != '\0') { + fw_len = strlen(fw); + if (fw_len >= sizeof(dhdinfo->fw_path)) { + DHD_ERROR(("fw path len exceeds max len of dhdinfo->fw_path\n")); + return FALSE; + } + strncpy(dhdinfo->fw_path, fw, sizeof(dhdinfo->fw_path)); + if (dhdinfo->fw_path[fw_len-1] == '\n') + dhdinfo->fw_path[fw_len-1] = '\0'; + } + if (nv && nv[0] != '\0') { + nv_len = strlen(nv); + if (nv_len >= sizeof(dhdinfo->nv_path)) { + DHD_ERROR(("nvram path len exceeds max len of dhdinfo->nv_path\n")); + return FALSE; + } + strncpy(dhdinfo->nv_path, nv, sizeof(dhdinfo->nv_path)); + if (dhdinfo->nv_path[nv_len-1] == '\n') + dhdinfo->nv_path[nv_len-1] = '\0'; + } + + /* clear the path in module parameter */ + firmware_path[0] = '\0'; + nvram_path[0] = '\0'; + + if (dhdinfo->fw_path[0] == '\0') { + DHD_ERROR(("firmware path not found\n")); + return FALSE; + } + if (dhdinfo->nv_path[0] == '\0') { + DHD_ERROR(("nvram path not found\n")); + return FALSE; + } + + return TRUE; +} + +#ifdef CUSTOMER_HW4 +bool dhd_validate_chipid(dhd_pub_t *dhdp) +{ + uint chipid = dhd_bus_chip_id(dhdp); + uint config_chipid; + +#ifdef BCM4354_CHIP + config_chipid = BCM4354_CHIP_ID; +#elif defined(BCM43349_CHIP) + config_chipid = BCM43349_CHIP_ID; +#elif defined(BCM4339_CHIP) + config_chipid = BCM4339_CHIP_ID; +#elif defined(BCM4335_CHIP) + config_chipid = BCM4335_CHIP_ID; +#elif defined(BCM43241_CHIP) + config_chipid = BCM4324_CHIP_ID; +#elif defined(BCM4334_CHIP) + config_chipid = BCM4334_CHIP_ID; +#elif defined(BCM4330_CHIP) + config_chipid = BCM4330_CHIP_ID; +#else + DHD_ERROR(("%s: Unknown chip id, if you use new chipset," + " please add CONFIG_BCMXXXX into the Kernel and" + " BCMXXXX_CHIP definition into the DHD driver\n", + __FUNCTION__)); + config_chipid = 0; +#endif /* BCM4354_CHIP */ + +#if defined(BCM4354_CHIP) && defined(SUPPORT_MULTIPLE_REVISION) + if (chipid == BCM4350_CHIP_ID && config_chipid == BCM4354_CHIP_ID) + return TRUE; + else +#endif /* SUPPORT_MULTIPLE_REVISION */ + + return config_chipid ? (config_chipid == chipid) : FALSE; +} +#endif /* CUSTOMER_HW4 */ + int dhd_bus_start(dhd_pub_t *dhdp) { @@ -3669,54 +4138,23 @@ dhd_bus_start(dhd_pub_t *dhdp) DHD_TRACE(("Enter %s:\n", __FUNCTION__)); -#ifdef DHDTHREAD - if (dhd->threads_only) - dhd_os_sdlock(dhdp); -#endif /* DHDTHREAD */ - - /* try to download image and nvram to the dongle */ - if ((dhd->pub.busstate == DHD_BUS_DOWN) && - (fw_path[0] != '\0') && - (nv_path[0] != '\0')) { -#ifdef SHOW_NVRAM_TYPE - { /* Show nvram type in the kernel log */ - int i; - for (i = 0; nv_path[i] != '\0'; ++i) { - if (nv_path[i] == '.') { - ++i; - break; - } - } - DHD_ERROR(("%s: nvram_type = [%s]\n", __FUNCTION__, &nv_path[i])); - } -#endif /* SHOW_NVRAM_TYPE */ - /* wake lock moved to dhdsdio_download_firmware */ - if (!(dhd_bus_download_firmware(dhd->pub.bus, dhd->pub.osh, - fw_path, nv_path))) { -#ifdef CUSTOMER_HW4 - DHD_ERROR(("%s: dhdsdio_probe_download failed. " - "firmware or nvram wrong\n", - __FUNCTION__)); -#else - DHD_ERROR(("%s: dhdsdio_probe_download failed. firmware = %s nvram = %s\n", - __FUNCTION__, fw_path, nv_path)); -#endif /* CUSTOMER_HW4 */ -#ifdef DHDTHREAD - if (dhd->threads_only) - dhd_os_sdunlock(dhdp); -#endif /* DHDTHREAD */ - return -1; + if (dhd->pub.busstate == DHD_BUS_DOWN && dhd_update_fw_nv_path(dhd)) { + DHD_INFO(("%s download fw %s, nv %s\n", __FUNCTION__, dhd->fw_path, dhd->nv_path)); + ret = dhd_bus_download_firmware(dhd->pub.bus, dhd->pub.osh, + dhd->fw_path, dhd->nv_path); + if (ret < 0) { + DHD_ERROR(("%s: failed to download firmware %s\n", + __FUNCTION__, dhd->fw_path)); + return ret; } } if (dhd->pub.busstate != DHD_BUS_LOAD) { -#ifdef DHDTHREAD - if (dhd->threads_only) - dhd_os_sdunlock(dhdp); -#endif /* DHDTHREAD */ return -ENETDOWN; } + dhd_os_sdlock(dhdp); + /* Start the watchdog timer */ dhd->pub.tickcnt = 0; dhd_os_wd_timer(&dhd->pub, dhd_watchdog_ms); @@ -3725,15 +4163,12 @@ dhd_bus_start(dhd_pub_t *dhdp) if ((ret = dhd_bus_init(&dhd->pub, FALSE)) != 0) { DHD_ERROR(("%s, dhd_bus_init failed %d\n", __FUNCTION__, ret)); -#ifdef DHDTHREAD - if (dhd->threads_only) - dhd_os_sdunlock(dhdp); -#endif /* DHDTHREAD */ + dhd_os_sdunlock(dhdp); return ret; } #if defined(OOB_INTR_ONLY) || defined(BCMSPI_ANDROID) /* Host registration for OOB interrupt */ - if (bcmsdh_register_oob_intr(dhdp)) { + if (dhd_bus_oob_intr_register(dhdp)) { /* deactivate timer and wait for the handler to finish */ flags = dhd_os_spin_lock(&dhd->pub); @@ -3742,18 +4177,13 @@ dhd_bus_start(dhd_pub_t *dhdp) del_timer_sync(&dhd->timer); DHD_ERROR(("%s Host failed to register for OOB\n", __FUNCTION__)); -#ifdef DHDTHREAD - if (dhd->threads_only) - dhd_os_sdunlock(dhdp); -#endif /* DHDTHREAD */ + dhd_os_sdunlock(dhdp); DHD_OS_WD_WAKE_UNLOCK(&dhd->pub); return -ENODEV; } -#ifndef BCMSPI_ANDROID /* Enable oob at firmware */ dhd_enable_oob_intr(dhd->pub.bus, TRUE); -#endif /* !BCMSPI_ANDROID */ #endif /* defined(OOB_INTR_ONLY) || defined(BCMSPI_ANDROID) */ /* If bus is not ready, can't come up */ @@ -3763,63 +4193,20 @@ dhd_bus_start(dhd_pub_t *dhdp) dhd_os_spin_unlock(&dhd->pub, flags); del_timer_sync(&dhd->timer); DHD_ERROR(("%s failed bus is not ready\n", __FUNCTION__)); -#ifdef DHDTHREAD - if (dhd->threads_only) - dhd_os_sdunlock(dhdp); -#endif /* DHDTHREAD */ + dhd_os_sdunlock(dhdp); DHD_OS_WD_WAKE_UNLOCK(&dhd->pub); return -ENODEV; } -#ifdef DHDTHREAD - if (dhd->threads_only) - dhd_os_sdunlock(dhdp); -#endif /* DHDTHREAD */ + dhd_os_sdunlock(dhdp); -#ifdef BCMSDIOH_TXGLOM - if ((dhd->pub.busstate == DHD_BUS_DATA) && bcmsdh_glom_enabled()) { - dhd_txglom_enable(dhdp, TRUE); - } -#endif - -#ifdef CUSTOMER_HW4 -#ifdef USE_CID_CHECK - dhd_check_module_cid(dhdp); -#endif -#ifdef READ_MACADDR - dhd_read_macaddr(dhd, &dhd->pub.mac); -#endif -#ifdef RDWR_MACADDR - dhd_check_rdwr_macaddr(dhd, &dhd->pub, &dhd->pub.mac); -#endif -#ifdef RDWR_KORICS_MACADDR - dhd_write_rdwr_korics_macaddr(dhd, &dhd->pub.mac); -#endif -#else -#ifdef READ_MACADDR - dhd_read_macaddr(dhd); -#endif -#endif /* CUSTOMER_HW4 */ + dhd_process_cid_mac(dhdp, TRUE); /* Bus is ready, do any protocol initialization */ if ((ret = dhd_prot_init(&dhd->pub)) < 0) return ret; -#ifdef CUSTOMER_HW4 -#ifdef GET_MAC_FROM_OTP - dhd_check_module_mac(dhdp, &dhd->pub.mac); -#endif -#ifdef RDWR_MACADDR - dhd_write_rdwr_macaddr(&dhd->pub.mac); -#endif -#ifdef WRITE_MACADDR - dhd_write_macaddr(&dhd->pub.mac); -#endif -#else -#ifdef WRITE_MACADDR - dhd_write_macaddr(dhd->pub.mac.octet); -#endif -#endif /* CUSTOMER_HW4 */ + dhd_process_cid_mac(dhdp, FALSE); #ifdef ARP_OFFLOAD_SUPPORT if (dhd->pend_ipaddr) { @@ -3832,6 +4219,75 @@ dhd_bus_start(dhd_pub_t *dhdp) return 0; } +#ifdef WLTDLS +int _dhd_tdls_enable(dhd_pub_t *dhd, bool tdls_on, bool auto_on, struct ether_addr *mac) +{ + char iovbuf[WLC_IOCTL_SMLEN]; + uint32 tdls = tdls_on; + int ret = 0; + uint32 tdls_auto_op = 0; + uint32 tdls_idle_time = CUSTOM_TDLS_IDLE_MODE_SETTING; + int32 tdls_rssi_high = CUSTOM_TDLS_RSSI_THRESHOLD_HIGH; + int32 tdls_rssi_low = CUSTOM_TDLS_RSSI_THRESHOLD_LOW; + BCM_REFERENCE(mac); + if (!FW_SUPPORTED(dhd, tdls)) + return BCME_ERROR; + + if (dhd->tdls_enable == tdls_on) + goto auto_mode; + bcm_mkiovar("tdls_enable", (char *)&tdls, sizeof(tdls), iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) { + DHD_ERROR(("%s: tdls %d failed %d\n", __FUNCTION__, tdls, ret)); + goto exit; + } + dhd->tdls_enable = tdls_on; +auto_mode: + + tdls_auto_op = auto_on; + bcm_mkiovar("tdls_auto_op", (char *)&tdls_auto_op, sizeof(tdls_auto_op), + iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, + sizeof(iovbuf), TRUE, 0)) < 0) { + DHD_ERROR(("%s: tdls_auto_op failed %d\n", __FUNCTION__, ret)); + goto exit; + } + + if (tdls_auto_op) { + bcm_mkiovar("tdls_idle_time", (char *)&tdls_idle_time, + sizeof(tdls_idle_time), iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, + sizeof(iovbuf), TRUE, 0)) < 0) { + DHD_ERROR(("%s: tdls_idle_time failed %d\n", __FUNCTION__, ret)); + goto exit; + } + bcm_mkiovar("tdls_rssi_high", (char *)&tdls_rssi_high, 4, iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, + sizeof(iovbuf), TRUE, 0)) < 0) { + DHD_ERROR(("%s: tdls_rssi_high failed %d\n", __FUNCTION__, ret)); + goto exit; + } + bcm_mkiovar("tdls_rssi_low", (char *)&tdls_rssi_low, 4, iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, + sizeof(iovbuf), TRUE, 0)) < 0) { + DHD_ERROR(("%s: tdls_rssi_low failed %d\n", __FUNCTION__, ret)); + goto exit; + } + } + +exit: + return ret; +} +int dhd_tdls_enable(struct net_device *dev, bool tdls_on, bool auto_on, struct ether_addr *mac) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + int ret = 0; + if (dhd) + ret = _dhd_tdls_enable(&dhd->pub, tdls_on, auto_on, mac); + else + ret = BCME_ERROR; + return ret; +} +#endif bool dhd_is_concurrent_mode(dhd_pub_t *dhd) { @@ -3858,23 +4314,15 @@ dhd_get_concurrent_capabilites(dhd_pub_t *dhd) int32 ret = 0; char buf[WLC_IOCTL_SMLEN]; bool mchan_supported = FALSE; - /* if dhd->op_mode is already set for HOSTAP, - * that means we only will use the mode as it is - */ - if (dhd->op_mode & DHD_FLAG_HOSTAP_MODE) - return 0; - memset(buf, 0, sizeof(buf)); - bcm_mkiovar("cap", 0, 0, buf, sizeof(buf)); - if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, buf, sizeof(buf), - FALSE, 0)) < 0) { - DHD_ERROR(("%s: Get Capability failed (error=%d)\n", - __FUNCTION__, ret)); + /* if dhd->op_mode is already set for HOSTAP and Manufacturing + * test mode, that means we only will use the mode as it is + */ + if (dhd->op_mode & (DHD_FLAG_HOSTAP_MODE | DHD_FLAG_MFG_MODE)) return 0; - } - if (strstr(buf, "vsdb")) { + if (FW_SUPPORTED(dhd, vsdb)) { mchan_supported = TRUE; } - if (strstr(buf, "p2p") == NULL) { + if (!FW_SUPPORTED(dhd, p2p)) { DHD_TRACE(("Chip does not support p2p\n")); return 0; } @@ -3895,20 +4343,23 @@ dhd_get_concurrent_capabilites(dhd_pub_t *dhd) ret = DHD_FLAG_CONCURR_SINGLE_CHAN_MODE; if (mchan_supported) ret |= DHD_FLAG_CONCURR_MULTI_CHAN_MODE; -#if defined(WL_ENABLE_P2P_IF) || defined(CUSTOMER_HW4) +#if defined(WL_ENABLE_P2P_IF) || defined(CUSTOMER_HW4) || \ + defined(WL_CFG80211_P2P_DEV_IF) /* For customer_hw4, although ICS, * we still support concurrent mode */ return ret; #else return 0; -#endif +#endif /* WL_ENABLE_P2P_IF || CUSTOMER_HW4 || WL_CFG80211_P2P_DEV_IF */ } } } return 0; } #endif + + int dhd_preinit_ioctls(dhd_pub_t *dhd) { @@ -3919,15 +4370,32 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #if defined(BCMSUP_4WAY_HANDSHAKE) && defined(WLAN_AKM_SUITE_FT_8021X) uint32 sup_wpa = 0; #endif -#ifdef CUSTOM_AMPDU_BA_WSIZE - uint32 ampdu_ba_wsize = CUSTOM_AMPDU_BA_WSIZE; -#endif /* CUSTOM_AMPDU_BA_WSIZE */ +#if defined(CUSTOM_AMPDU_BA_WSIZE) || (defined(WLAIBSS) && \ + defined(CUSTOM_IBSS_AMPDU_BA_WSIZE)) + uint32 ampdu_ba_wsize = 0; +#endif /* CUSTOM_AMPDU_BA_WSIZE ||(WLAIBSS && CUSTOM_IBSS_AMPDU_BA_WSIZE) */ +#if defined(CUSTOM_AMPDU_MPDU) + uint32 ampdu_mpdu = 0; +#endif + +#ifdef PROP_TXSTATUS + int wlfc_enable = TRUE; +#ifndef DISABLE_11N + uint32 hostreorder = 1; + int ret2 = 0; +#endif /* DISABLE_11N */ +#endif /* PROP_TXSTATUS */ + +#ifdef DHD_ENABLE_LPC uint32 lpc = 1; +#endif /* DHD_ENABLE_LPC */ uint power_mode = PM_FAST; uint32 dongle_align = DHD_SDALIGN; uint32 glom = CUSTOM_GLOM_SETTING; -#if (defined(CUSTOMER_HW4) || defined(BOARD_PANDA)) && (defined(VSDB) || \ - defined(ROAM_ENABLE)) +#if defined(CUSTOMER_HW2) && defined(USE_WL_CREDALL) + uint32 credall = 1; +#endif +#if defined(CUSTOMER_HW4) && (defined(VSDB) || defined(ROAM_ENABLE)) uint bcn_timeout = 8; #else uint bcn_timeout = 4; @@ -3939,16 +4407,12 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #if defined(ARP_OFFLOAD_SUPPORT) int arpoe = 1; #endif -#if defined(CUSTOMER_HW4) - int scan_assoc_time = 80; -#else int scan_assoc_time = DHD_SCAN_ASSOC_ACTIVE_TIME; -#endif int scan_unassoc_time = DHD_SCAN_UNASSOC_ACTIVE_TIME; int scan_passive_time = DHD_SCAN_PASSIVE_TIME; char buf[WLC_IOCTL_SMLEN]; char *ptr; - uint32 listen_interval = LISTEN_INTERVAL; /* Default Listen Interval in Beacons */ + uint32 listen_interval = CUSTOM_LISTEN_INTERVAL; /* Default Listen Interval in Beacons */ #ifdef ROAM_ENABLE uint roamvar = 0; int roam_trigger[2] = {CUSTOM_ROAM_TRIGGER_SETTING, WLC_BAND_ALL}; @@ -3975,6 +4439,9 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) uint32 mpc = 0; /* Turn MPC off for AP/APSTA mode */ struct ether_addr p2p_ea; #endif +#ifdef BCMCCX + uint32 ccx = 1; +#endif #if defined(AP) || defined(WLP2P) uint32 apsta = 1; /* Enable APSTA mode */ @@ -3985,30 +4452,17 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #ifdef OKC_SUPPORT uint32 okc = 1; #endif -#ifdef WLTDLS - uint32 tdls = 1; -#ifdef CUSTOMER_HW4 - uint32 tdls_idle_time = 10000; /* 10sec */ - uint32 tdls_auto_op = 1; - int32 tdls_rssi_high = -80; - int32 tdls_rssi_low = -85; -#endif -#endif /* WLTDLS */ #ifdef DISABLE_11N uint32 nmode = 0; #endif /* DISABLE_11N */ -#ifdef DISABLE_11AC - uint32 vhtmode = 0; -#endif /* DISABLE_11AC */ - -#if defined(VSDB) && defined(CUSTOMER_HW4) - int interference_mode = 3; -#endif #ifdef USE_WL_TXBF uint32 txbf = 1; #endif /* USE_WL_TXBF */ +#ifdef AMPDU_VO_ENABLE + struct ampdu_tid_control tid; +#endif #ifdef USE_WL_FRAMEBURST uint32 frameburst = 1; #endif /* USE_WL_FRAMEBURST */ @@ -4016,21 +4470,43 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) uint32 ack_ratio = 250; uint32 ack_ratio_depth = 64; #endif /* DHD_SET_FW_HIGHSPEED */ -#ifdef PROP_TXSTATUS -#ifdef PROP_TXSTATUS_VSDB - /* In case the host does not support proptxstatus, hostreorder in dongle should be off */ - uint32 hostreorder = 0; - dhd->wlfc_enabled = FALSE; - /* enable WLFC only if the firmware is VSDB */ -#else - dhd->wlfc_enabled = TRUE; -#endif /* PROP_TXSTATUS_VSDB */ -#endif /* PROP_TXSTATUS */ +#ifdef SUPPORT_2G_VHT + uint32 vht_features = 0x3; /* 2G enable | rates all */ +#endif /* SUPPORT_2G_VHT */ +#ifdef CUSTOM_PSPRETEND_THR + uint32 pspretend_thr = CUSTOM_PSPRETEND_THR; +#endif +#ifdef PKT_FILTER_SUPPORT + dhd_pkt_filter_enable = TRUE; +#endif /* PKT_FILTER_SUPPORT */ +#ifdef WLTDLS + dhd->tdls_enable = FALSE; +#endif /* WLTDLS */ dhd->suspend_bcn_li_dtim = CUSTOM_SUSPEND_BCN_LI_DTIM; DHD_TRACE(("Enter %s\n", __FUNCTION__)); dhd->op_mode = 0; +#ifdef CUSTOMER_HW4 + if (!dhd_validate_chipid(dhd)) { + DHD_ERROR(("%s: CONFIG_BCMXXX and CHIP ID(%x) is mismatched\n", + __FUNCTION__, dhd_bus_chip_id(dhd))); +#ifndef SUPPORT_MULTIPLE_CHIPS + return BCME_BADARG; +#endif /* !SUPPORT_MULTIPLE_CHIPS */ + } +#endif /* CUSTOMER_HW4 */ + if ((!op_mode && dhd_get_fw_mode(dhd->info) == DHD_FLAG_MFG_MODE) || + (op_mode == DHD_FLAG_MFG_MODE)) { + /* Check and adjust IOCTL response timeout for Manufactring firmware */ + dhd_os_set_ioctl_resp_timeout(MFG_IOCTL_RESP_TIMEOUT); + DHD_ERROR(("%s : Set IOCTL response time for Manufactring Firmware\n", + __FUNCTION__)); + } + else { + dhd_os_set_ioctl_resp_timeout(IOCTL_RESP_TIMEOUT); + DHD_INFO(("%s : Set IOCTL response time.\n", __FUNCTION__)); + } #ifdef GET_CUSTOM_MAC_ENABLE - ret = dhd_custom_get_mac_address(ea_addr.octet); + ret = wifi_platform_get_mac_addr(dhd->info->adapter, ea_addr.octet); if (!ret) { memset(buf, 0, sizeof(buf)); bcm_mkiovar("cur_etheraddr", (void *)&ea_addr, ETHER_ADDR_LEN, buf, sizeof(buf)); @@ -4056,10 +4532,16 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #ifdef GET_CUSTOM_MAC_ENABLE } #endif /* GET_CUSTOM_MAC_ENABLE */ - - DHD_TRACE(("Firmware = %s\n", fw_path)); - - if ((!op_mode && strstr(fw_path, "_apsta") != NULL) || + /* get a capabilities from firmware */ + memset(dhd->fw_capabilities, 0, sizeof(dhd->fw_capabilities)); + bcm_mkiovar("cap", 0, 0, dhd->fw_capabilities, sizeof(dhd->fw_capabilities)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, dhd->fw_capabilities, + sizeof(dhd->fw_capabilities), FALSE, 0)) < 0) { + DHD_ERROR(("%s: Get Capability failed (error=%d)\n", + __FUNCTION__, ret)); + return 0; + } + if ((!op_mode && dhd_get_fw_mode(dhd->info) == DHD_FLAG_HOSTAP_MODE) || (op_mode == DHD_FLAG_HOSTAP_MODE)) { #ifdef SET_RANDOM_MAC_SOFTAP uint rand_mac; @@ -4072,8 +4554,8 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) dhd_pkt_filter_enable = FALSE; #endif #ifdef SET_RANDOM_MAC_SOFTAP - srandom32((uint)jiffies); - rand_mac = random32(); + SRANDOM32((uint)jiffies); + rand_mac = RANDOM32(); iovbuf[0] = 0x02; /* locally administered bit */ iovbuf[1] = 0x1A; iovbuf[2] = 0x11; @@ -4096,10 +4578,24 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) DHD_ERROR(("%s mpc for HostAPD failed %d\n", __FUNCTION__, ret)); } #endif - } - else { +#if defined(CUSTOMER_HW4) && defined(USE_DYNAMIC_F2_BLKSIZE) + dhdsdio_func_blocksize(dhd, 2, DYNAMIC_F2_BLKSIZE_FOR_NONLEGACY); +#endif /* CUSTOMER_HW4 && USE_DYNAMIC_F2_BLKSIZE */ + } else if ((!op_mode && dhd_get_fw_mode(dhd->info) == DHD_FLAG_MFG_MODE) || + (op_mode == DHD_FLAG_MFG_MODE)) { +#if defined(ARP_OFFLOAD_SUPPORT) + arpoe = 0; +#endif /* ARP_OFFLOAD_SUPPORT */ +#ifdef PKT_FILTER_SUPPORT + dhd_pkt_filter_enable = FALSE; +#endif /* PKT_FILTER_SUPPORT */ + dhd->op_mode = DHD_FLAG_MFG_MODE; +#if defined(CUSTOMER_HW4) && defined(USE_DYNAMIC_F2_BLKSIZE) + dhdsdio_func_blocksize(dhd, 2, DYNAMIC_F2_BLKSIZE_FOR_NONLEGACY); +#endif /* CUSTOMER_HW4 && USE_DYNAMIC_F2_BLKSIZE */ + } else { uint32 concurrent_mode = 0; - if ((!op_mode && strstr(fw_path, "_p2p") != NULL) || + if ((!op_mode && dhd_get_fw_mode(dhd->info) == DHD_FLAG_P2P_MODE) || (op_mode == DHD_FLAG_P2P_MODE)) { #if defined(ARP_OFFLOAD_SUPPORT) arpoe = 0; @@ -4108,11 +4604,14 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) dhd_pkt_filter_enable = FALSE; #endif dhd->op_mode = DHD_FLAG_P2P_MODE; - } - else + } else if ((!op_mode && dhd_get_fw_mode(dhd->info) == DHD_FLAG_IBSS_MODE) || + (op_mode == DHD_FLAG_IBSS_MODE)) { + dhd->op_mode = DHD_FLAG_IBSS_MODE; + } else dhd->op_mode = DHD_FLAG_STA_MODE; #if !defined(AP) && defined(WLP2P) - if ((concurrent_mode = dhd_get_concurrent_capabilites(dhd))) { + if (dhd->op_mode != DHD_FLAG_IBSS_MODE && + (concurrent_mode = dhd_get_concurrent_capabilites(dhd))) { #if defined(ARP_OFFLOAD_SUPPORT) arpoe = 1; #endif @@ -4143,10 +4642,8 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #endif } - DHD_ERROR(("Firmware up: op_mode=0x%04x, " - "Broadcom Dongle Host Driver mac="MACDBG"\n", - dhd->op_mode, - MAC2STRDBG(dhd->mac.octet))); + DHD_ERROR(("Firmware up: op_mode=0x%04x, MAC="MACDBG"\n", + dhd->op_mode, MAC2STRDBG(dhd->mac.octet))); /* Set Country code */ if (dhd->dhd_cspec.ccode[0] != 0) { bcm_mkiovar("country", (char *)&dhd->dhd_cspec, @@ -4154,10 +4651,7 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) DHD_ERROR(("%s: country code setting failed\n", __FUNCTION__)); } -#ifdef DISABLE_11AC - bcm_mkiovar("vhtmode", (char *)&vhtmode, 4, iovbuf, sizeof(iovbuf)); - dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); -#endif + /* Set Listen Interval */ bcm_mkiovar("assoc_listen", (char *)&listen_interval, 4, iovbuf, sizeof(iovbuf)); @@ -4165,6 +4659,9 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) DHD_ERROR(("%s assoc_listen failed %d\n", __FUNCTION__, ret)); #if defined(ROAM_ENABLE) || defined(DISABLE_BUILTIN_ROAM) +#if defined(CUSTOMER_HW4) && defined(USE_WFA_CERT_CONF) + roamvar = sec_get_param(dhd, SET_PARAM_ROAMOFF); +#endif /* CUSTOMER_HW4 && USE_WFA_CERT_CONF */ /* Disable built-in roaming to allowed ext supplicant to take care of roaming */ bcm_mkiovar("roam_off", (char *)&roamvar, 4, iovbuf, sizeof(iovbuf)); dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); @@ -4199,30 +4696,40 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) bcm_mkiovar("okc_enable", (char *)&okc, 4, iovbuf, sizeof(iovbuf)); dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); #endif -#ifdef WLTDLS - bcm_mkiovar("tdls_enable", (char *)&tdls, 4, iovbuf, sizeof(iovbuf)); +#ifdef BCMCCX + bcm_mkiovar("ccx_enable", (char *)&ccx, 4, iovbuf, sizeof(iovbuf)); dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); +#endif /* BCMCCX */ +#ifdef WLTDLS #ifdef CUSTOMER_HW4 - bcm_mkiovar("tdls_auto_op", (char *)&tdls_auto_op, 4, iovbuf, sizeof(iovbuf)); - dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); - - bcm_mkiovar("tdls_idle_time", (char *)&tdls_idle_time, 4, iovbuf, sizeof(iovbuf)); - dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); - - bcm_mkiovar("tdls_rssi_high", (char *)&tdls_rssi_high, 4, iovbuf, sizeof(iovbuf)); - dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); - - bcm_mkiovar("tdls_rssi_low", (char *)&tdls_rssi_low, 4, iovbuf, sizeof(iovbuf)); - dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); -#endif + /* by default TDLS on and auto mode on */ + _dhd_tdls_enable(dhd, true, true, NULL); +#else + /* by default TDLS on and auto mode off */ + _dhd_tdls_enable(dhd, true, false, NULL); +#endif /* CUSTOMER_HW4 */ #endif /* WLTDLS */ +#ifdef DHD_ENABLE_LPC /* Set lpc 1 */ bcm_mkiovar("lpc", (char *)&lpc, 4, iovbuf, sizeof(iovbuf)); if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) { DHD_ERROR(("%s Set lpc failed %d\n", __FUNCTION__, ret)); +#ifdef CUSTOMER_HW4 + if (ret == BCME_NOTDOWN) { + uint wl_down = 1; + ret = dhd_wl_ioctl_cmd(dhd, WLC_DOWN, + (char *)&wl_down, sizeof(wl_down), TRUE, 0); + DHD_ERROR(("%s lpc fail WL_DOWN : %d, lpc = %d\n", __FUNCTION__, ret, lpc)); + + bcm_mkiovar("lpc", (char *)&lpc, 4, iovbuf, sizeof(iovbuf)); + ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + DHD_ERROR(("%s Set lpc ret --> %d\n", __FUNCTION__, ret)); + } +#endif /* CUSTOMER_HW4 */ } +#endif /* DHD_ENABLE_LPC */ #if defined(CUSTOMER_HW4) && defined(CONFIG_CONTROL_PM) sec_control_pm(dhd, &power_mode); @@ -4235,6 +4742,15 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) bcm_mkiovar("bus:txglomalign", (char *)&dongle_align, 4, iovbuf, sizeof(iovbuf)); dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); +#if defined(CUSTOMER_HW2) && defined(USE_WL_CREDALL) + /* enable credall to reduce the chance of no bus credit happened. */ + bcm_mkiovar("bus:credall", (char *)&credall, 4, iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); +#endif + +#if defined(CUSTOMER_HW4) && defined(USE_WFA_CERT_CONF) + glom = sec_get_param(dhd, SET_PARAM_BUS_TXGLOM_MODE); +#endif /* CUSTOMER_HW4 && USE_WFA_CERT_CONF */ if (glom != DEFAULT_GLOM_VALUE) { DHD_INFO(("%s set glom=0x%X\n", __FUNCTION__, glom)); bcm_mkiovar("bus:txglom", (char *)&glom, 4, iovbuf, sizeof(iovbuf)); @@ -4273,7 +4789,8 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #if defined(SOFTAP) if (ap_fw_loaded == FALSE) #endif - if (!(dhd->op_mode & DHD_FLAG_HOSTAP_MODE)) { + if (!(dhd->op_mode & + (DHD_FLAG_HOSTAP_MODE | DHD_FLAG_MFG_MODE))) { if ((res = dhd_keep_alive_onoff(dhd)) < 0) DHD_ERROR(("%s set keeplive failed %d\n", __FUNCTION__, res)); @@ -4287,11 +4804,10 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) DHD_ERROR(("%s Set txbf failed %d\n", __FUNCTION__, ret)); } #endif /* USE_WL_TXBF */ - #ifdef USE_WL_FRAMEBURST -#if defined(CUSTOMER_HW4) - frameburst = sec_control_frameburst(); -#endif /* CUSTOMER_HW4 */ +#if defined(CUSTOMER_HW4) && defined(USE_WFA_CERT_CONF) + frameburst = sec_get_param(dhd, SET_PARAM_FRAMEBURST); +#endif /* CUSTOMER_HW4 && USE_WFA_CERT_CONF */ /* Set frameburst to value */ if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_FAKEFRAG, (char *)&frameburst, sizeof(frameburst), TRUE, 0)) < 0) { @@ -4313,27 +4829,86 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) DHD_ERROR(("%s Set ack_ratio_depth failed %d\n", __FUNCTION__, ret)); } #endif /* DHD_SET_FW_HIGHSPEED */ +#if defined(CUSTOM_AMPDU_BA_WSIZE) || (defined(WLAIBSS) && \ + defined(CUSTOM_IBSS_AMPDU_BA_WSIZE)) + /* Set ampdu ba wsize to 64 or 16 */ #ifdef CUSTOM_AMPDU_BA_WSIZE - /* Set ampdu ba wsize to 64 */ - bcm_mkiovar("ampdu_ba_wsize", (char *)&du_ba_wsize, 4, iovbuf, sizeof(iovbuf)); - if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, - sizeof(iovbuf), TRUE, 0)) < 0) { - DHD_ERROR(("%s Set ampdu_ba_wsize to %d failed %d\n", - __FUNCTION__, CUSTOM_AMPDU_BA_WSIZE, ret)); + ampdu_ba_wsize = CUSTOM_AMPDU_BA_WSIZE; +#endif +#if defined(WLAIBSS) && defined(CUSTOM_IBSS_AMPDU_BA_WSIZE) + if (dhd->op_mode == DHD_FLAG_IBSS_MODE) + ampdu_ba_wsize = CUSTOM_IBSS_AMPDU_BA_WSIZE; +#endif /* WLAIBSS && CUSTOM_IBSS_AMPDU_BA_WSIZE */ + if (ampdu_ba_wsize != 0) { + bcm_mkiovar("ampdu_ba_wsize", (char *)&du_ba_wsize, 4, iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, + sizeof(iovbuf), TRUE, 0)) < 0) { + DHD_ERROR(("%s Set ampdu_ba_wsize to %d failed %d\n", + __FUNCTION__, ampdu_ba_wsize, ret)); + } } -#endif /* CUSTOM_AMPDU_BA_WSIZE */ +#endif /* CUSTOM_AMPDU_BA_WSIZE || (WLAIBSS && CUSTOM_IBSS_AMPDU_BA_WSIZE) */ +#if defined(CUSTOM_AMPDU_MPDU) + ampdu_mpdu = CUSTOM_AMPDU_MPDU; + if (ampdu_mpdu != 0 && (ampdu_mpdu <= ampdu_ba_wsize)) { + bcm_mkiovar("ampdu_mpdu", (char *)&du_mpdu, 4, iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, + sizeof(iovbuf), TRUE, 0)) < 0) { + DHD_ERROR(("%s Set ampdu_mpdu to %d failed %d\n", + __FUNCTION__, CUSTOM_AMPDU_MPDU, ret)); + } + } +#endif /* CUSTOM_AMPDU_MPDU */ + #if defined(BCMSUP_4WAY_HANDSHAKE) && defined(WLAN_AKM_SUITE_FT_8021X) - /* Read 4-way handshake requirements. */ - bcm_mkiovar("sup_wpa", (char *)&sup_wpa, 4, - iovbuf, sizeof(iovbuf)); - ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, iovbuf, sizeof(iovbuf), FALSE, 0); - if (ret >= 0) - dhd->fw_4way_handshake = TRUE; - DHD_TRACE(("4-way handshake mode is: %d\n", dhd->fw_4way_handshake)); + /* Read 4-way handshake requirements */ + if (dhd_use_idsup == 1) { + bcm_mkiovar("sup_wpa", (char *)&sup_wpa, 4, iovbuf, sizeof(iovbuf)); + ret = dhd_wl_ioctl_cmd(dhd, WLC_GET_VAR, iovbuf, sizeof(iovbuf), FALSE, 0); + /* sup_wpa iovar returns NOTREADY status on some platforms using modularized + * in-dongle supplicant. + */ + if (ret >= 0 || ret == BCME_NOTREADY) + dhd->fw_4way_handshake = TRUE; + DHD_TRACE(("4-way handshake mode is: %d\n", dhd->fw_4way_handshake)); + } #endif /* BCMSUP_4WAY_HANDSHAKE && WLAN_AKM_SUITE_FT_8021X */ +#ifdef SUPPORT_2G_VHT + bcm_mkiovar("vht_features", (char *)&vht_features, 4, iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) { + DHD_ERROR(("%s vht_features set failed %d\n", __FUNCTION__, ret)); +#ifdef CUSTOMER_HW4 + if (ret == BCME_NOTDOWN) { + uint wl_down = 1; + ret = dhd_wl_ioctl_cmd(dhd, WLC_DOWN, + (char *)&wl_down, sizeof(wl_down), TRUE, 0); + DHD_ERROR(("%s vht_features fail WL_DOWN : %d, vht_features = 0x%x\n", + __FUNCTION__, ret, vht_features)); + + bcm_mkiovar("vht_features", (char *)&vht_features, 4, + iovbuf, sizeof(iovbuf)); + ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + DHD_ERROR(("%s vht_features set. ret --> %d\n", __FUNCTION__, ret)); + } +#endif /* CUSTOMER_HW4 */ + } +#endif /* SUPPORT_2G_VHT */ +#ifdef CUSTOM_PSPRETEND_THR + /* Turn off MPC in AP mode */ + bcm_mkiovar("pspretend_threshold", (char *)&pspretend_thr, 4, + iovbuf, sizeof(iovbuf)); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, + sizeof(iovbuf), TRUE, 0)) < 0) { + DHD_ERROR(("%s pspretend_threshold for HostAPD failed %d\n", + __FUNCTION__, ret)); + } +#endif bcm_mkiovar("buf_key_b4_m4", (char *)&buf_key_b4_m4, 4, iovbuf, sizeof(iovbuf)); - dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, + sizeof(iovbuf), TRUE, 0)) < 0) { + DHD_ERROR(("%s buf_key_b4_m4 set failed %d\n", __FUNCTION__, ret)); + } /* Read event_msgs mask */ bcm_mkiovar("event_msgs", eventmask, WL_EVENTING_MASK_LEN, iovbuf, sizeof(iovbuf)); @@ -4355,6 +4930,7 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) setbit(eventmask, WLC_E_DISASSOC_IND); setbit(eventmask, WLC_E_DISASSOC); setbit(eventmask, WLC_E_JOIN); + setbit(eventmask, WLC_E_START); setbit(eventmask, WLC_E_ASSOC_IND); setbit(eventmask, WLC_E_PSK_SUP); setbit(eventmask, WLC_E_LINK); @@ -4373,9 +4949,13 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #endif /* WLMEDIA_HTSF */ #ifdef PNO_SUPPORT setbit(eventmask, WLC_E_PFN_NET_FOUND); + setbit(eventmask, WLC_E_PFN_BEST_BATCHING); + setbit(eventmask, WLC_E_PFN_BSSID_NET_FOUND); + setbit(eventmask, WLC_E_PFN_BSSID_NET_LOST); #endif /* PNO_SUPPORT */ /* enable dongle roaming event */ setbit(eventmask, WLC_E_ROAM); + setbit(eventmask, WLC_E_BSSID); #ifdef BCMCCX setbit(eventmask, WLC_E_ADDTS_IND); setbit(eventmask, WLC_E_DELTS_IND); @@ -4387,8 +4967,6 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) setbit(eventmask, WLC_E_ESCAN_RESULT); if (dhd->op_mode & DHD_FLAG_P2P_MODE) { setbit(eventmask, WLC_E_ACTION_FRAME_RX); - setbit(eventmask, WLC_E_ACTION_FRAME_COMPLETE); - setbit(eventmask, WLC_E_ACTION_FRAME_OFF_CHAN_COMPLETE); setbit(eventmask, WLC_E_P2P_DISC_LISTEN_COMPLETE); } #if defined(CUSTOMER_HW4) && defined(WES_SUPPORT) @@ -4397,6 +4975,9 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) } #endif /* WES_SUPPORT */ #endif /* WL_CFG80211 */ +#ifdef WLAIBSS + setbit(eventmask, WLC_E_AIBSS_TXFAIL); +#endif /* WLAIBSS */ /* Write updated Event mask */ bcm_mkiovar("event_msgs", eventmask, WL_EVENTING_MASK_LEN, iovbuf, sizeof(iovbuf)); @@ -4430,35 +5011,41 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) #ifdef PKT_FILTER_SUPPORT /* Setup default defintions for pktfilter , enable in suspend */ - dhd->pktfilter_count = 5; - dhd->pktfilter[1] = NULL; - dhd->pktfilter[2] = NULL; - dhd->pktfilter[3] = NULL; + dhd->pktfilter_count = 6; + /* Setup filter to allow only unicast */ + dhd->pktfilter[DHD_UNICAST_FILTER_NUM] = "100 0 0 0 0x01 0x00"; + dhd->pktfilter[DHD_BROADCAST_FILTER_NUM] = NULL; + dhd->pktfilter[DHD_MULTICAST4_FILTER_NUM] = NULL; + dhd->pktfilter[DHD_MULTICAST6_FILTER_NUM] = NULL; /* Add filter to pass multicastDNS packet and NOT filter out as Broadcast */ - dhd->pktfilter[4] = "104 0 0 0 0xFFFFFFFFFFFF 0x01005E0000FB"; + dhd->pktfilter[DHD_MDNS_FILTER_NUM] = "104 0 0 0 0xFFFFFFFFFFFF 0x01005E0000FB"; + /* apply APP pktfilter */ + dhd->pktfilter[DHD_ARP_FILTER_NUM] = "105 0 0 12 0xFFFF 0x0806"; -#if defined(CUSTOMER_HW4) && defined(GAN_LITE_NAT_KEEPALIVE_FILTER) +#ifdef CUSTOMER_HW4 +#ifdef GAN_LITE_NAT_KEEPALIVE_FILTER dhd->pktfilter_count = 4; /* Setup filter to block broadcast and NAT Keepalive packets */ - dhd->pktfilter[0] = "100 0 0 0 0xffffff 0xffffff"; /* discard all broadcast packets */ - dhd->pktfilter[1] = "102 0 0 36 0xffffffff 0x11940009"; /* discard NAT Keepalive packets */ - dhd->pktfilter[2] = "104 0 0 38 0xffffffff 0x11940009"; /* discard NAT Keepalive packets */ - dhd->pktfilter[3] = NULL; + /* discard all broadcast packets */ + dhd->pktfilter[DHD_UNICAST_FILTER_NUM] = "100 0 0 0 0xffffff 0xffffff"; + /* discard NAT Keepalive packets */ + dhd->pktfilter[DHD_BROADCAST_FILTER_NUM] = "102 0 0 36 0xffffffff 0x11940009"; + /* discard NAT Keepalive packets */ + dhd->pktfilter[DHD_MULTICAST4_FILTER_NUM] = "104 0 0 38 0xffffffff 0x11940009"; + dhd->pktfilter[DHD_MULTICAST6_FILTER_NUM] = NULL; #else - /* Setup filter to allow only unicast */ -#if defined(BLOCK_IPV6_PACKET) && defined(CUSTOMER_HW4) - dhd->pktfilter[0] = "100 0 0 0 " +#ifdef BLOCK_IPV6_PACKET + /* Setup filter to allow only IPv4 unicast frames */ + dhd->pktfilter[DHD_UNICAST_FILTER_NUM] = "100 0 0 0 " HEX_PREF_STR UNI_FILTER_STR ZERO_ADDR_STR ETHER_TYPE_STR IPV6_FILTER_STR " " HEX_PREF_STR ZERO_ADDR_STR ZERO_ADDR_STR ETHER_TYPE_STR ZERO_TYPE_STR; -#else - dhd->pktfilter[0] = "100 0 0 0 0x01 0x00"; -#endif /* BLOCK_IPV6_PACKET && CUSTOMER_HW4 */ -#if defined(PASS_IPV4_SUSPEND) && defined(CUSTOMER_HW4) - dhd->pktfilter_count = 5; - dhd->pktfilter[4] = "104 0 0 0 0xFFFFFF 0x01005E"; -#endif /* PASS_IPV4_SUSPEND && CUSTOMER_HW4 */ -#endif /* GAN_LITE_NAT_KEEPALIVE_FILTER && CUSTOMER_HW4 */ +#endif /* BLOCK_IPV6_PACKET */ +#ifdef PASS_IPV4_SUSPEND + dhd->pktfilter[DHD_MDNS_FILTER_NUM] = "104 0 0 0 0xFFFFFF 0x01005E"; +#endif /* PASS_IPV4_SUSPEND */ +#endif /* GAN_LITE_NAT_KEEPALIVE_FILTER */ +#endif /* CUSTOMER_HW4 */ #if defined(SOFTAP) if (ap_fw_loaded) { @@ -4471,22 +5058,24 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) bcm_mkiovar("nmode", (char *)&nmode, 4, iovbuf, sizeof(iovbuf)); if ((ret = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) DHD_ERROR(("%s wl nmode 0 failed %d\n", __FUNCTION__, ret)); -#else -#if defined(PROP_TXSTATUS) && defined(PROP_TXSTATUS_VSDB) - bcm_mkiovar("ampdu_hostreorder", (char *)&hostreorder, 4, buf, sizeof(buf)); - dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, buf, sizeof(buf), TRUE, 0); -#endif #endif /* DISABLE_11N */ -#if defined(VSDB) && defined(CUSTOMER_HW4) - dhd_wl_ioctl_cmd(dhd, WLC_SET_INTERFERENCE_MODE, - (int *)&interference_mode, sizeof(int), TRUE, 0); -#endif - #if defined(CUSTOMER_HW4) && defined(ENABLE_BCN_LI_BCN_WAKEUP) bcm_mkiovar("bcn_li_bcn", (char *)&bcn_li_bcn, 4, iovbuf, sizeof(iovbuf)); dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); #endif /* CUSTOMER_HW4 && ENABLE_BCN_LI_BCN_WAKEUP */ +#ifdef AMPDU_VO_ENABLE + tid.tid = PRIO_8021D_VO; /* Enable TID(6) for voice */ + tid.enable = TRUE; + bcm_mkiovar("ampdu_tid", (char *)&tid, sizeof(tid), iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + + tid.tid = PRIO_8021D_NC; /* Enable TID(7) for voice */ + tid.enable = TRUE; + bcm_mkiovar("ampdu_tid", (char *)&tid, sizeof(tid), iovbuf, sizeof(iovbuf)); + dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); +#endif + /* query for 'ver' to get version info from firmware */ memset(buf, 0, sizeof(buf)); ptr = buf; @@ -4497,14 +5086,67 @@ dhd_preinit_ioctls(dhd_pub_t *dhd) bcmstrtok(&ptr, "\n", 0); /* Print fw version info */ DHD_ERROR(("Firmware version = %s\n", buf)); + dhd_set_version_info(dhd, buf); +#if defined(CUSTOMER_HW4) && defined(WRITE_WLANINFO) + sec_save_wlinfo(buf, EPI_VERSION_STR, dhd->info->nv_path); +#endif /* CUSTOMER_HW4 && WRITE_WLANINFO */ + } - /* Check and adjust IOCTL response timeout for Manufactring firmware */ - if (strstr(buf, MANUFACTRING_FW) != NULL) { - dhd_os_set_ioctl_resp_timeout(20000); - DHD_ERROR(("%s : adjust IOCTL response time for Manufactring Firmware\n", - __FUNCTION__)); + dhd_txglom_enable(dhd, TRUE); + +#ifdef PROP_TXSTATUS + if (disable_proptx || +#ifdef PROP_TXSTATUS_VSDB + /* enable WLFC only if the firmware is VSDB when it is in STA mode */ + (dhd->op_mode != DHD_FLAG_HOSTAP_MODE && + dhd->op_mode != DHD_FLAG_IBSS_MODE) || +#endif /* PROP_TXSTATUS_VSDB */ + FALSE) { + wlfc_enable = FALSE; + } + +#ifndef DISABLE_11N + bcm_mkiovar("ampdu_hostreorder", (char *)&hostreorder, 4, iovbuf, sizeof(iovbuf)); + if ((ret2 = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0)) < 0) { + DHD_ERROR(("%s wl ampdu_hostreorder failed %d\n", __FUNCTION__, ret2)); + if (ret2 != BCME_UNSUPPORTED) + ret = ret2; +#ifdef CUSTOMER_HW4 + if (ret == BCME_NOTDOWN) { + uint wl_down = 1; + ret2 = dhd_wl_ioctl_cmd(dhd, WLC_DOWN, (char *)&wl_down, + sizeof(wl_down), TRUE, 0); + DHD_ERROR(("%s ampdu_hostreorder fail WL_DOWN : %d, hostreorder :%d\n", + __FUNCTION__, ret2, hostreorder)); + + bcm_mkiovar("ampdu_hostreorder", (char *)&hostreorder, 4, + iovbuf, sizeof(iovbuf)); + ret2 = dhd_wl_ioctl_cmd(dhd, WLC_SET_VAR, iovbuf, sizeof(iovbuf), TRUE, 0); + DHD_ERROR(("%s wl ampdu_hostreorder. ret --> %d\n", __FUNCTION__, ret2)); + if (ret2 != BCME_UNSUPPORTED) + ret = ret2; } +#endif /* CUSTOMER_HW4 */ + if (ret2 != BCME_OK) + hostreorder = 0; } +#endif /* DISABLE_11N */ + if (wlfc_enable) + dhd_wlfc_init(dhd); +#ifndef DISABLE_11N + else if (hostreorder) + dhd_wlfc_hostreorder_init(dhd); +#endif /* DISABLE_11N */ + +#endif /* PROP_TXSTATUS */ +#ifdef PNO_SUPPORT + if (!dhd->pno_state) { + dhd_pno_init(dhd); + } +#endif +#ifdef WL11U + dhd_interworking_enable(dhd); +#endif /* WL11U */ done: return ret; @@ -4526,7 +5168,7 @@ dhd_iovar(dhd_pub_t *pub, int ifidx, char *name, char *cmd_buf, uint cmd_len, in ioc.cmd = set? WLC_SET_VAR : WLC_GET_VAR; ioc.buf = buf; ioc.len = len; - ioc.set = TRUE; + ioc.set = set; ret = dhd_wl_ioctl(pub, ifidx, &ioc, ioc.buf, ioc.len); if (!set && ret >= 0) @@ -4618,7 +5260,7 @@ aoe_update_host_ipv4_table(dhd_pub_t *dhd_pub, u32 ipa, bool add, int idx) * whenever there is an event related to an IP address. * ptr : kernel provided pointer to IP address that has changed */ -static int dhd_device_event(struct notifier_block *this, +static int dhd_inetaddr_notifier_call(struct notifier_block *this, unsigned long event, void *ptr) { @@ -4637,9 +5279,9 @@ static int dhd_device_event(struct notifier_block *this, /* Filter notifications meant for non Broadcom devices */ if ((ifa->ifa_dev->dev->netdev_ops != &dhd_ops_pri) && (ifa->ifa_dev->dev->netdev_ops != &dhd_ops_virt)) { -#ifdef WLP2P +#if defined(WL_ENABLE_P2P_IF) if (!wl_cfgp2p_is_ifops(ifa->ifa_dev->dev->netdev_ops)) -#endif +#endif /* WL_ENABLE_P2P_IF */ return NOTIFY_DONE; } #endif /* LINUX_VERSION_CODE */ @@ -4712,8 +5354,125 @@ static int dhd_device_event(struct notifier_block *this, } #endif /* ARP_OFFLOAD_SUPPORT */ +/* Neighbor Discovery Offload: defered handler */ +static void +dhd_inet6_work_handler(void *dhd_info, void *event_data, u8 event) +{ + struct ipv6_work_info_t *ndo_work = (struct ipv6_work_info_t *)event_data; + dhd_pub_t *pub = &((dhd_info_t *)dhd_info)->pub; + int ret; + + if (event != DHD_WQ_WORK_IPV6_NDO) { + DHD_ERROR(("%s: unexpected event \n", __FUNCTION__)); + return; + } + + if (!ndo_work) { + DHD_ERROR(("%s: ipv6 work info is not initialized \n", __FUNCTION__)); + return; + } + + if (!pub) { + DHD_ERROR(("%s: dhd pub is not initialized \n", __FUNCTION__)); + return; + } + + if (ndo_work->if_idx) { + DHD_ERROR(("%s: idx %d \n", __FUNCTION__, ndo_work->if_idx)); + return; + } + + switch (ndo_work->event) { + case NETDEV_UP: + DHD_TRACE(("%s: Enable NDO and add ipv6 into table \n ", __FUNCTION__)); + ret = dhd_ndo_enable(pub, TRUE); + if (ret < 0) { + DHD_ERROR(("%s: Enabling NDO Failed %d\n", __FUNCTION__, ret)); + } + + ret = dhd_ndo_add_ip(pub, &ndo_work->ipv6_addr[0], ndo_work->if_idx); + if (ret < 0) { + DHD_ERROR(("%s: Adding host ip for NDO failed %d\n", + __FUNCTION__, ret)); + } + break; + case NETDEV_DOWN: + DHD_TRACE(("%s: clear ipv6 table \n", __FUNCTION__)); + ret = dhd_ndo_remove_ip(pub, ndo_work->if_idx); + if (ret < 0) { + DHD_ERROR(("%s: Removing host ip for NDO failed %d\n", + __FUNCTION__, ret)); + goto done; + } + + ret = dhd_ndo_enable(pub, FALSE); + if (ret < 0) { + DHD_ERROR(("%s: disabling NDO Failed %d\n", __FUNCTION__, ret)); + goto done; + } + break; + default: + DHD_ERROR(("%s: unknown notifier event \n", __FUNCTION__)); + break; + } +done: + /* free ndo_work. alloced while scheduling the work */ + kfree(ndo_work); + + return; +} + +/* + * Neighbor Discovery Offload: Called when an interface + * is assigned with ipv6 address. + * Handles only primary interface + */ +static int dhd_inet6addr_notifier_call(struct notifier_block *this, + unsigned long event, + void *ptr) +{ + dhd_info_t *dhd; + dhd_pub_t *dhd_pub; + struct inet6_ifaddr *inet6_ifa = ptr; + struct in6_addr *ipv6_addr = &inet6_ifa->addr; + struct ipv6_work_info_t *ndo_info; + int idx = 0; /* REVISIT */ + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)) + /* Filter notifications meant for non Broadcom devices */ + if (inet6_ifa->idev->dev->netdev_ops != &dhd_ops_pri) { + return NOTIFY_DONE; + } +#endif /* LINUX_VERSION_CODE */ + + dhd = *(dhd_info_t **)netdev_priv(inet6_ifa->idev->dev); + if (!dhd) + return NOTIFY_DONE; + + if (dhd->iflist[idx] && dhd->iflist[idx]->net != inet6_ifa->idev->dev) + return NOTIFY_DONE; + dhd_pub = &dhd->pub; + if (!FW_SUPPORTED(dhd_pub, ndoe)) + return NOTIFY_DONE; + + ndo_info = (struct ipv6_work_info_t *)kzalloc(sizeof(struct ipv6_work_info_t), GFP_ATOMIC); + if (!ndo_info) { + DHD_ERROR(("%s: ipv6 work alloc failed\n", __FUNCTION__)); + return NOTIFY_DONE; + } + + ndo_info->event = event; + ndo_info->if_idx = idx; + memcpy(&ndo_info->ipv6_addr[0], ipv6_addr, IPV6_ADDR_LEN); + + /* defer the work to thread as it may block kernel */ + dhd_deferred_schedule_work((void *)ndo_info, DHD_WQ_WORK_IPV6_NDO, + dhd_inet6_work_handler, DHD_WORK_PRIORITY_LOW); + return NOTIFY_DONE; +} + int -dhd_net_attach(dhd_pub_t *dhdp, int ifidx) +dhd_register_if(dhd_pub_t *dhdp, int ifidx, bool need_rtnl_lock) { dhd_info_t *dhd = (dhd_info_t *)dhdp->info; struct net_device *net = NULL; @@ -4723,7 +5482,6 @@ dhd_net_attach(dhd_pub_t *dhdp, int ifidx) DHD_TRACE(("%s: ifidx %d\n", __FUNCTION__, ifidx)); ASSERT(dhd && dhd->iflist[ifidx]); - net = dhd->iflist[ifidx]->net; ASSERT(net); @@ -4776,42 +5534,56 @@ dhd_net_attach(dhd_pub_t *dhdp, int ifidx) net->ethtool_ops = &dhd_ethtool_ops; #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) */ -#if defined(CONFIG_WIRELESS_EXT) +#if defined(WL_WIRELESS_EXT) #if WIRELESS_EXT < 19 net->get_wireless_stats = dhd_get_wireless_stats; #endif /* WIRELESS_EXT < 19 */ #if WIRELESS_EXT > 12 net->wireless_handlers = (struct iw_handler_def *)&wl_iw_handler_def; #endif /* WIRELESS_EXT > 12 */ -#endif /* defined(CONFIG_WIRELESS_EXT) */ +#endif /* defined(WL_WIRELESS_EXT) */ dhd->pub.rxsz = DBUS_RX_BUFFER_SIZE_DHD(net); memcpy(net->dev_addr, temp_addr, ETHER_ADDR_LEN); - if ((err = register_netdev(net)) != 0) { - DHD_ERROR(("couldn't register the net device, err %d\n", err)); + if (ifidx == 0) + printf("%s\n", dhd_version); + + if (need_rtnl_lock) + err = register_netdev(net); + else + err = register_netdevice(net); + + if (err != 0) { + DHD_ERROR(("couldn't register the net device [%s], err %d\n", net->name, err)); goto fail; } - printf("Broadcom Dongle Host Driver: register interface [%s]" - " MAC: "MACDBG"\n", - net->name, + + + printf("Register interface [%s] MAC: "MACDBG"\n\n", net->name, #if defined(CUSTOMER_HW4) MAC2STRDBG(dhd->pub.mac.octet)); #else MAC2STRDBG(net->dev_addr)); #endif /* CUSTOMER_HW4 */ -#if defined(SOFTAP) && defined(CONFIG_WIRELESS_EXT) && !defined(WL_CFG80211) +#if defined(SOFTAP) && defined(WL_WIRELESS_EXT) && !defined(WL_CFG80211) wl_iw_iscan_set_scan_broadcast_prep(net, 1); #endif -#if 1 && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) +#if defined(BCMLXSDMMC) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) if (ifidx == 0) { - dhd_registration_check = TRUE; +#ifdef BCMLXSDMMC up(&dhd_registration_sem); +#endif + if (!dhd_download_fw_on_driverload) { + dhd_net_bus_devreset(net, TRUE); + dhd_net_bus_suspend(net); + wifi_platform_set_power(dhdp->info->adapter, FALSE, WIFI_TURNOFF_DELAY); + } } -#endif +#endif /* OEM_ANDROID && BCMLXSDMMC && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ return 0; fail: @@ -4847,7 +5619,7 @@ dhd_bus_detach(dhd_pub_t *dhdp) } #if defined(OOB_INTR_ONLY) || defined(BCMSPI_ANDROID) - bcmsdh_unregister_oob_intr(); + dhd_bus_oob_intr_unregister(dhdp); #endif /* defined(OOB_INTR_ONLY) || defined(BCMSPI_ANDROID) */ } } @@ -4874,7 +5646,7 @@ void dhd_detach(dhd_pub_t *dhdp) /* Give sufficient time for threads to start running in case * dhd_attach() has failed */ - osl_delay(1000*100); + OSL_SLEEP(100); } if (dhd->dhd_state & DHD_ATTACH_STATE_PROT_ATTACH) { @@ -4885,8 +5657,15 @@ void dhd_detach(dhd_pub_t *dhdp) } #ifdef ARP_OFFLOAD_SUPPORT - unregister_inetaddr_notifier(&dhd_notifier); + if (dhd_inetaddr_notifier_registered) { + dhd_inetaddr_notifier_registered = FALSE; + unregister_inetaddr_notifier(&dhd_inetaddr_notifier); + } #endif /* ARP_OFFLOAD_SUPPORT */ + if (dhd_inet6addr_notifier_registered) { + dhd_inet6addr_notifier_registered = FALSE; + unregister_inet6addr_notifier(&dhd_inet6addr_notifier); + } #if defined(CONFIG_HAS_EARLYSUSPEND) && defined(DHD_USE_EARLYSUSPEND) if (dhd->dhd_state & DHD_ATTACH_STATE_EARLYSUSPEND_DONE) { @@ -4895,20 +5674,12 @@ void dhd_detach(dhd_pub_t *dhdp) } #endif /* CONFIG_HAS_EARLYSUSPEND && DHD_USE_EARLYSUSPEND */ -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) - cancel_work_sync(&dhd->work_hang); -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ - -#if defined(CONFIG_WIRELESS_EXT) +#if defined(WL_WIRELESS_EXT) if (dhd->dhd_state & DHD_ATTACH_STATE_WL_ATTACH) { /* Detatch and unlink in the iw */ wl_iw_detach(); } -#endif /* defined(CONFIG_WIRELESS_EXT) */ - - if (dhd->thr_sysioc_ctl.thr_pid >= 0) { - PROC_STOP(&dhd->thr_sysioc_ctl); - } +#endif /* defined(WL_WIRELESS_EXT) */ /* delete all interfaces, start with virtual */ if (dhd->dhd_state & DHD_ATTACH_STATE_ADD_IF) { @@ -4916,33 +5687,28 @@ void dhd_detach(dhd_pub_t *dhdp) dhd_if_t *ifp; /* Cleanup virtual interfaces */ + dhd_net_if_lock_local(dhd); for (i = 1; i < DHD_MAX_IFS; i++) { - dhd_net_if_lock_local(dhd); - if (dhd->iflist[i]) { - dhd->iflist[i]->state = DHD_IF_DEL; - dhd->iflist[i]->idx = i; - dhd_op_if(dhd->iflist[i]); - } - - dhd_net_if_unlock_local(dhd); + if (dhd->iflist[i]) + dhd_remove_if(&dhd->pub, i, TRUE); } + dhd_net_if_unlock_local(dhd); + /* delete primary interface 0 */ ifp = dhd->iflist[0]; ASSERT(ifp); ASSERT(ifp->net); if (ifp && ifp->net) { -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)) - if (ifp->net->open) -#else - if (ifp->net->netdev_ops == &dhd_ops_pri) -#endif - { - unregister_netdev(ifp->net); + /* in unregister_netdev case, the interface gets freed by net->destructor + * (which is set to free_netdev) + */ + if (ifp->net->reg_state == NETREG_UNINITIALIZED) free_netdev(ifp->net); - ifp->net = NULL; - MFREE(dhd->pub.osh, ifp, sizeof(*ifp)); - dhd->iflist[0] = NULL; - } + else + unregister_netdev(ifp->net); + ifp->net = NULL; + MFREE(dhd->pub.osh, ifp, sizeof(*ifp)); + dhd->iflist[0] = NULL; } } @@ -4955,22 +5721,18 @@ void dhd_detach(dhd_pub_t *dhdp) del_timer_sync(&dhd->timer); if (dhd->dhd_state & DHD_ATTACH_STATE_THREADS_CREATED) { -#ifdef DHDTHREAD if (dhd->thr_wdt_ctl.thr_pid >= 0) { PROC_STOP(&dhd->thr_wdt_ctl); } - if (dhd->thr_dpc_ctl.thr_pid >= 0) { - PROC_STOP(&dhd->thr_dpc_ctl); - } -#ifdef RXFRAME_THREAD - if (dhd->thr_rxf_ctl.thr_pid >= 0) { + if (dhd->rxthread_enabled && dhd->thr_rxf_ctl.thr_pid >= 0) { PROC_STOP(&dhd->thr_rxf_ctl); } -#endif - else -#endif /* DHDTHREAD */ - tasklet_kill(&dhd->tasklet); + + if (dhd->thr_dpc_ctl.thr_pid >= 0) { + PROC_STOP(&dhd->thr_dpc_ctl); + } else + tasklet_kill(&dhd->tasklet); } #ifdef WL_CFG80211 if (dhd->dhd_state & DHD_ATTACH_STATE_CFG80211) { @@ -4978,42 +5740,40 @@ void dhd_detach(dhd_pub_t *dhdp) dhd_monitor_uninit(); } #endif + /* free deferred work queue */ + dhd_deferred_work_deinit(dhd->dhd_deferred_wq); + dhd->dhd_deferred_wq = NULL; - -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) - unregister_pm_notifier(&dhd_sleep_pm_notifier); -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && defined(CONFIG_PM_SLEEP) */ - /* && defined(CONFIG_PM_SLEEP) */ - +#ifdef PNO_SUPPORT + if (dhdp->pno_state) + dhd_pno_deinit(dhdp); +#endif +#if defined(CONFIG_PM_SLEEP) + if (dhd_pm_notifier_registered) { + unregister_pm_notifier(&dhd->pm_notifier); + dhd_pm_notifier_registered = FALSE; + } +#endif /* CONFIG_PM_SLEEP */ +#ifdef DEBUG_CPU_FREQ + if (dhd->new_freq) + free_percpu(dhd->new_freq); + dhd->new_freq = NULL; + cpufreq_unregister_notifier(&dhd->freq_trans, CPUFREQ_TRANSITION_NOTIFIER); +#endif if (dhd->dhd_state & DHD_ATTACH_STATE_WAKELOCKS_INIT) { - printk("wd wakelock count:%d\n", dhd->wakelock_wd_counter); + DHD_TRACE(("wd wakelock count:%d\n", dhd->wakelock_wd_counter)); #ifdef CONFIG_HAS_WAKELOCK dhd->wakelock_counter = 0; dhd->wakelock_wd_counter = 0; dhd->wakelock_rx_timeout_enable = 0; dhd->wakelock_ctrl_timeout_enable = 0; - if (dhd->wl_wifi) { - wake_lock_destroy(dhd->wl_wifi); - MFREE(dhd->pub.osh, dhd->wl_wifi, sizeof(struct wake_lock)); - dhd->wl_wifi = NULL; - } - if (dhd->wl_rxwake) { - wake_lock_destroy(dhd->wl_rxwake); - MFREE(dhd->pub.osh, dhd->wl_rxwake, sizeof(struct wake_lock)); - dhd->wl_rxwake = NULL; - } - if (dhd->wl_ctrlwake) { - wake_lock_destroy(dhd->wl_ctrlwake); - MFREE(dhd->pub.osh, dhd->wl_ctrlwake, sizeof(struct wake_lock)); - dhd->wl_ctrlwake = NULL; - } - if (dhd->wl_wdwake) { - wake_lock_destroy(dhd->wl_wdwake); - MFREE(dhd->pub.osh, dhd->wl_wdwake, sizeof(struct wake_lock)); - dhd->wl_wdwake = NULL; - } + wake_lock_destroy(&dhd->wl_wifi); + wake_lock_destroy(&dhd->wl_rxwake); + wake_lock_destroy(&dhd->wl_ctrlwake); + wake_lock_destroy(&dhd->wl_wdwake); #endif /* CONFIG_HAS_WAKELOCK */ } + } @@ -5041,19 +5801,11 @@ dhd_free(dhd_pub_t *dhdp) } } dhd = (dhd_info_t *)dhdp->info; -#if defined(CONFIG_DHD_USE_STATIC_BUF) /* If pointer is allocated by dhd_os_prealloc then avoid MFREE */ - if (dhd != (dhd_info_t *)dhd_os_prealloc(NULL, DHD_PREALLOC_DHD_INFO, 0)) { -#endif /* CONFIG_DHD_USE_STATIC_BUF */ - if (dhd) - MFREE(dhd->pub.osh, dhd, sizeof(*dhd)); -#if defined(CONFIG_DHD_USE_STATIC_BUF) - } - else { - if (dhd) - dhd = NULL; - } -#endif /* CONFIG_DHD_USE_STATIC_BUF */ + if (dhd && + dhd != (dhd_info_t *)dhd_os_prealloc(dhdp, DHD_PREALLOC_DHD_INFO, 0, FALSE)) + MFREE(dhd->pub.osh, dhd, sizeof(*dhd)); + dhd = NULL; } } @@ -5064,156 +5816,34 @@ dhd_module_cleanup(void) dhd_bus_unregister(); -#if defined(CONFIG_WIFI_CONTROL_FUNC) - wl_android_wifictrl_func_del(); -#endif /* CONFIG_WIFI_CONTROL_FUNC */ wl_android_exit(); - /* Call customer gpio to turn off power with WL_REG_ON signal */ - dhd_customer_gpio_wlan_ctrl(WLAN_POWER_OFF); + dhd_wifi_platform_unregister_drv(); } - -#if defined(CONFIG_WIFI_CONTROL_FUNC) -extern bool g_wifi_poweron; -#endif /* CONFIG_WIFI_CONTROL_FUNC */ - static int __init dhd_module_init(void) { - int error = 0; - -#if 1 && defined(BCMLXSDMMC) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) - int retry = POWERUP_MAX_RETRY; - int chip_up = 0; -#endif - - DHD_TRACE(("%s: Enter\n", __FUNCTION__)); - - wl_android_init(); - -#if defined(DHDTHREAD) - /* Sanity check on the module parameters */ - do { - /* Both watchdog and DPC as tasklets are ok */ - if ((dhd_watchdog_prio < 0) && (dhd_dpc_prio < 0)) - break; - - /* If both watchdog and DPC are threads, TX must be deferred */ - if ((dhd_watchdog_prio >= 0) && (dhd_dpc_prio >= 0) && dhd_deferred_tx) - break; - - DHD_ERROR(("Invalid module parameters.\n")); - return -EINVAL; - } while (0); -#endif - -#if 1 && defined(BCMLXSDMMC) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) - do { - sema_init(&dhd_chipup_sem, 0); - dhd_bus_reg_sdio_notify(&dhd_chipup_sem); - dhd_customer_gpio_wlan_ctrl(WLAN_POWER_ON); -#if defined(CONFIG_WIFI_CONTROL_FUNC) - if (wl_android_wifictrl_func_add() < 0) { - dhd_bus_unreg_sdio_notify(); - goto fail_1; - } -#endif /* defined(CONFIG_WIFI_CONTROL_FUNC) */ - if (down_timeout(&dhd_chipup_sem, - msecs_to_jiffies(POWERUP_WAIT_MS)) == 0) { - dhd_bus_unreg_sdio_notify(); - chip_up = 1; - break; - } - DHD_ERROR(("\nfailed to power up wifi chip, retry again (%d left) **\n\n", - retry+1)); - dhd_bus_unreg_sdio_notify(); -#if defined(CONFIG_WIFI_CONTROL_FUNC) - wl_android_wifictrl_func_del(); -#endif /* defined(CONFIG_WIFI_CONTROL_FUNC) */ - dhd_customer_gpio_wlan_ctrl(WLAN_POWER_OFF); - } while (retry-- > 0); - - if (!chip_up) { - DHD_ERROR(("\nfailed to power up wifi chip, max retry reached, exits **\n\n")); - return -ENODEV; - } -#else - dhd_customer_gpio_wlan_ctrl(WLAN_POWER_ON); -#if defined(CONFIG_WIFI_CONTROL_FUNC) - if (wl_android_wifictrl_func_add() < 0) - goto fail_1; -#endif /* defined(CONFIG_WIFI_CONTROL_FUNC) */ - -#endif - -#if defined(CONFIG_WIFI_CONTROL_FUNC) - /* If the wifi_set_power() is failed, - * we need to jump error handling routines. - */ - if (!g_wifi_poweron) { - printk("%s: wifi_set_power() failed\n", __FUNCTION__); - error = -ENODEV; - goto fail_1; - } -#endif /* defined(CONFIG_WIFI_CONTROL_FUNC) */ - -#if 1 && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) - sema_init(&dhd_registration_sem, 0); -#endif - - - error = dhd_bus_register(); - - if (!error) - printf("\n%s\n", dhd_version); - else { - DHD_ERROR(("%s: sdio_register_driver failed\n", __FUNCTION__)); - goto fail_1; - } - -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) - /* - * Wait till MMC sdio_register_driver callback called and made driver attach. - * It's needed to make sync up exit from dhd insmod and - * Kernel MMC sdio device callback registration - */ - if ((down_timeout(&dhd_registration_sem, - msecs_to_jiffies(DHD_REGISTRATION_TIMEOUT)) != 0) || - (dhd_registration_check != TRUE)) { - error = -ENODEV; - DHD_ERROR(("%s: sdio_register_driver timeout or error \n", __FUNCTION__)); - goto fail_2; - } -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ -#if defined(WL_CFG80211) - wl_android_post_init(); -#endif /* defined(WL_CFG80211) */ - - return error; - -#if 1 && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) -fail_2: - dhd_bus_unregister(); -#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ - -fail_1: - -#if defined(CONFIG_WIFI_CONTROL_FUNC) - wl_android_wifictrl_func_del(); -#endif + int err; - /* Call customer gpio to turn off power with WL_REG_ON signal */ - dhd_customer_gpio_wlan_ctrl(WLAN_POWER_OFF); + DHD_ERROR(("%s in\n", __FUNCTION__)); + err = dhd_wifi_platform_register_drv(); - return error; + return err; } + #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) +#if defined(CONFIG_DEFERRED_INITCALLS) +deferred_module_init(dhd_module_init); +#elif defined(USE_LATE_INITCALL_SYNC) +late_initcall_sync(dhd_module_init); +#else late_initcall(dhd_module_init); +#endif /* USE_LATE_INITCALL_SYNC */ #else module_init(dhd_module_init); -#endif +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) */ module_exit(dhd_module_cleanup); @@ -5280,14 +5910,24 @@ dhd_os_ioctl_resp_wake(dhd_pub_t *pub) { dhd_info_t *dhd = (dhd_info_t *)(pub->info); - if (waitqueue_active(&dhd->ioctl_resp_wait)) { - wake_up(&dhd->ioctl_resp_wait); - } - + wake_up(&dhd->ioctl_resp_wait); return 0; } void +dhd_os_wd_timer_extend(void *bus, bool extend) +{ + dhd_pub_t *pub = bus; + dhd_info_t *dhd = (dhd_info_t *)pub->info; + + if (extend) + dhd_os_wd_timer(bus, WATCHDOG_EXTEND_INTERVAL); + else + dhd_os_wd_timer(bus, dhd->default_wd_interval); +} + + +void dhd_os_wd_timer(void *bus, uint wdtick) { dhd_pub_t *pub = bus; @@ -5300,15 +5940,14 @@ dhd_os_wd_timer(void *bus, uint wdtick) DHD_ERROR(("%s: dhd NULL\n", __FUNCTION__)); return; } - if (wdtick) - DHD_OS_WD_WAKE_LOCK(pub); flags = dhd_os_spin_lock(pub); /* don't start the wd until fw is loaded */ if (pub->busstate == DHD_BUS_DOWN) { dhd_os_spin_unlock(pub, flags); - DHD_OS_WD_WAKE_UNLOCK(pub); + if (!wdtick) + DHD_OS_WD_WAKE_UNLOCK(pub); return; } @@ -5316,16 +5955,13 @@ dhd_os_wd_timer(void *bus, uint wdtick) if (!wdtick && dhd->wd_timer_valid == TRUE) { dhd->wd_timer_valid = FALSE; dhd_os_spin_unlock(pub, flags); -#ifdef DHDTHREAD del_timer_sync(&dhd->timer); -#else - del_timer(&dhd->timer); -#endif /* DHDTHREAD */ DHD_OS_WD_WAKE_UNLOCK(pub); return; } if (wdtick) { + DHD_OS_WD_WAKE_LOCK(pub); dhd_watchdog_ms = (uint)wdtick; /* Re arm the timer, at last watchdog period */ mod_timer(&dhd->timer, jiffies + msecs_to_jiffies(dhd_watchdog_ms)); @@ -5375,7 +6011,6 @@ dhd_os_close_image(void *image) filp_close((struct file *)image, NULL); } - void dhd_os_sdlock(dhd_pub_t *pub) { @@ -5383,12 +6018,10 @@ dhd_os_sdlock(dhd_pub_t *pub) dhd = (dhd_info_t *)(pub->info); -#ifdef DHDTHREAD - if (dhd->threads_only) + if (dhd_dpc_prio >= 0) down(&dhd->sdsem); else -#endif /* DHDTHREAD */ - spin_lock_bh(&dhd->sdlock); + spin_lock_bh(&dhd->sdlock); } void @@ -5398,12 +6031,10 @@ dhd_os_sdunlock(dhd_pub_t *pub) dhd = (dhd_info_t *)(pub->info); -#ifdef DHDTHREAD - if (dhd->threads_only) + if (dhd_dpc_prio >= 0) up(&dhd->sdsem); else -#endif /* DHDTHREAD */ - spin_unlock_bh(&dhd->sdlock); + spin_unlock_bh(&dhd->sdlock); } void @@ -5446,7 +6077,6 @@ dhd_os_sdtxunlock(dhd_pub_t *pub) dhd_os_sdunlock(pub); } -#if defined(DHDTHREAD) && defined(RXFRAME_THREAD) static void dhd_os_rxflock(dhd_pub_t *pub) { @@ -5465,7 +6095,6 @@ dhd_os_rxfunlock(dhd_pub_t *pub) dhd = (dhd_info_t *)(pub->info); spin_unlock_bh(&dhd->rxf_lock); } -#endif /* defined(DHDTHREAD) && defined(RXFRAME_THREAD) */ #ifdef DHDTCPACK_SUPPRESS void @@ -5488,18 +6117,23 @@ dhd_os_tcpackunlock(dhd_pub_t *pub) } #endif /* DHDTCPACK_SUPPRESS */ -#if defined(CONFIG_DHD_USE_STATIC_BUF) -uint8* dhd_os_prealloc(void *osh, int section, uint size) +uint8* dhd_os_prealloc(dhd_pub_t *dhdpub, int section, uint size, bool kmalloc_if_fail) { - return (uint8*)wl_android_prealloc(section, size); + uint8* buf; + gfp_t flags = CAN_SLEEP() ? GFP_KERNEL: GFP_ATOMIC; + + buf = (uint8*)wifi_platform_prealloc(dhdpub->info->adapter, section, size); + if (buf == NULL && kmalloc_if_fail) + buf = kmalloc(size, flags); + + return buf; } -void dhd_os_prefree(void *osh, void *addr, uint size) +void dhd_os_prefree(dhd_pub_t *dhdpub, void *addr, uint size) { } -#endif /* defined(CONFIG_WIFI_CONTROL_FUNC) */ -#if defined(CONFIG_WIRELESS_EXT) +#if defined(WL_WIRELESS_EXT) struct iw_statistics * dhd_get_wireless_stats(struct net_device *dev) { @@ -5517,7 +6151,7 @@ dhd_get_wireless_stats(struct net_device *dev) else return NULL; } -#endif /* defined(CONFIG_WIRELESS_EXT) */ +#endif /* defined(WL_WIRELESS_EXT) */ static int dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata, @@ -5530,7 +6164,7 @@ dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata, if (bcmerror != BCME_OK) return (bcmerror); -#if defined(CONFIG_WIRELESS_EXT) +#if defined(WL_WIRELESS_EXT) if (event->bsscfgidx == 0) { /* * Wireless ext is on primary interface only @@ -5543,30 +6177,13 @@ dhd_wl_host_event(dhd_info_t *dhd, int *ifidx, void *pktdata, wl_iw_event(dhd->iflist[*ifidx]->net, event, *data); } } -#endif /* defined(CONFIG_WIRELESS_EXT) */ +#endif /* defined(WL_WIRELESS_EXT) */ #ifdef WL_CFG80211 - if ((ntoh32(event->event_type) == WLC_E_IF) && - (((dhd_if_event_t *)*data)->action == WLC_E_IF_ADD)) - /* If ADD_IF has been called directly by wl utility then we - * should not report this. In case if ADD_IF was called from - * CFG stack, then too this event need not be reported back - */ - return (BCME_OK); - if ((wl_cfg80211_is_progress_ifchange() || - wl_cfg80211_is_progress_ifadd()) && (*ifidx != 0)) { - /* - * If IF_ADD/CHANGE operation is going on, - * discard any event received on the virtual I/F - */ - return (BCME_OK); - } - ASSERT(dhd->iflist[*ifidx] != NULL); ASSERT(dhd->iflist[*ifidx]->net != NULL); - if (dhd->iflist[*ifidx]->event2cfg80211 && dhd->iflist[*ifidx]->net) { + if (dhd->iflist[*ifidx]->net) wl_cfg80211_event(dhd->iflist[*ifidx]->net, event, *data); - } #endif /* defined(WL_CFG80211) */ return (bcmerror); @@ -5679,9 +6296,67 @@ dhd_sendup_event(dhd_pub_t *dhdp, wl_event_msg_t *event, void *data) } } +#ifdef LOG_INTO_TCPDUMP +void +dhd_sendup_log(dhd_pub_t *dhdp, void *data, int data_len) +{ + struct sk_buff *p, *skb; + uint32 pktlen; + int len; + dhd_if_t *ifp; + dhd_info_t *dhd; + uchar *skb_data; + int ifidx = 0; + struct ether_header eth; + + pktlen = sizeof(eth) + data_len; + dhd = dhdp->info; + + if ((p = PKTGET(dhdp->osh, pktlen, FALSE))) { + ASSERT(ISALIGNED((uintptr)PKTDATA(dhdp->osh, p), sizeof(uint32))); + + bcopy(&dhdp->mac, ð.ether_dhost, ETHER_ADDR_LEN); + bcopy(&dhdp->mac, ð.ether_shost, ETHER_ADDR_LEN); + ETHER_TOGGLE_LOCALADDR(ð.ether_shost); + eth.ether_type = hton16(ETHER_TYPE_BRCM); + + bcopy((void *)ð, PKTDATA(dhdp->osh, p), sizeof(eth)); + bcopy(data, PKTDATA(dhdp->osh, p) + sizeof(eth), data_len); + skb = PKTTONATIVE(dhdp->osh, p); + skb_data = skb->data; + len = skb->len; + + ifidx = dhd_ifname2idx(dhd, "wlan0"); + ifp = dhd->iflist[ifidx]; + if (ifp == NULL) + ifp = dhd->iflist[0]; + + ASSERT(ifp); + skb->dev = ifp->net; + skb->protocol = eth_type_trans(skb, skb->dev); + skb->data = skb_data; + skb->len = len; + + /* Strip header, count, deliver upward */ + skb_pull(skb, ETH_HLEN); + + /* Send the packet */ + if (in_interrupt()) { + netif_rx(skb); + } else { + netif_rx_ni(skb); + } + } + else { + /* Could not allocate a sk_buf */ + DHD_ERROR(("%s: unable to alloc sk_buf", __FUNCTION__)); + } +} +#endif /* LOG_INTO_TCPDUMP */ + void dhd_wait_for_event(dhd_pub_t *dhd, bool *lockvar) { -#if 1 && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) struct dhd_info *dhdinfo = dhd->info; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) @@ -5699,7 +6374,7 @@ void dhd_wait_for_event(dhd_pub_t *dhd, bool *lockvar) void dhd_wait_event_wakeup(dhd_pub_t *dhd) { -#if 1 && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) struct dhd_info *dhdinfo = dhd->info; if (waitqueue_active(&dhdinfo->ctrl_wait)) wake_up(&dhdinfo->ctrl_wait); @@ -5708,7 +6383,7 @@ void dhd_wait_event_wakeup(dhd_pub_t *dhd) } int -dhd_dev_reset(struct net_device *dev, uint8 flag) +dhd_net_bus_devreset(struct net_device *dev, uint8 flag) { int ret; @@ -5719,17 +6394,50 @@ dhd_dev_reset(struct net_device *dev, uint8 flag) if (dhd_wl_ioctl_cmd(&dhd->pub, WLC_DOWN, NULL, 0, TRUE, 0) < 0) { DHD_TRACE(("%s: wl down failed\n", __FUNCTION__)); } +#ifdef PROP_TXSTATUS + if (dhd->pub.wlfc_enabled) + dhd_wlfc_deinit(&dhd->pub); +#endif /* PROP_TXSTATUS */ +#ifdef PNO_SUPPORT + if (dhd->pub.pno_state) + dhd_pno_deinit(&dhd->pub); +#endif + } + + if (!flag) { + dhd_update_fw_nv_path(dhd); + /* update firmware and nvram path to sdio bus */ + dhd_bus_update_fw_nv_path(dhd->pub.bus, + dhd->fw_path, dhd->nv_path); + } else { + dhd_prot_pending(&dhd->pub, TRUE); } ret = dhd_bus_devreset(&dhd->pub, flag); if (ret) { DHD_ERROR(("%s: dhd_bus_devreset: %d\n", __FUNCTION__, ret)); - return ret; } + if (flag) + dhd_prot_pending(&dhd->pub, FALSE); + return ret; } +int +dhd_net_bus_suspend(struct net_device *dev) +{ + dhd_info_t *dhdinfo = *(dhd_info_t **)netdev_priv(dev); + return dhd_bus_suspend(&dhdinfo->pub); +} + +int +dhd_net_bus_resume(struct net_device *dev, uint8 stage) +{ + dhd_info_t *dhdinfo = *(dhd_info_t **)netdev_priv(dev); + return dhd_bus_resume(&dhdinfo->pub, stage); +} + int net_os_set_suspend_disable(struct net_device *dev, int val) { dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); @@ -5753,6 +6461,9 @@ int net_os_set_suspend(struct net_device *dev, int val, int force) #else ret = dhd_suspend_resume_helper(dhd, val, force); #endif +#ifdef WL_CFG80211 + wl_cfg80211_update_power_mode(dev); +#endif } return ret; } @@ -5770,41 +6481,52 @@ int net_os_set_suspend_bcn_li_dtim(struct net_device *dev, int val) #ifdef PKT_FILTER_SUPPORT int net_os_rxfilter_add_remove(struct net_device *dev, int add_remove, int num) { -#ifndef GAN_LITE_NAT_KEEPALIVE_FILTER +#if defined(CUSTOMER_HW4) && defined(GAN_LITE_NAT_KEEPALIVE_FILTER) + return 0; +#else dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); char *filterp = NULL; + int filter_id = 0; int ret = 0; if (!dhd || (num == DHD_UNICAST_FILTER_NUM) || - (num == DHD_MDNS_FILTER_NUM)) + (num == DHD_MDNS_FILTER_NUM)) return ret; if (num >= dhd->pub.pktfilter_count) return -EINVAL; - if (add_remove) { - switch (num) { + switch (num) { case DHD_BROADCAST_FILTER_NUM: filterp = "101 0 0 0 0xFFFFFFFFFFFF 0xFFFFFFFFFFFF"; + filter_id = 101; break; case DHD_MULTICAST4_FILTER_NUM: filterp = "102 0 0 0 0xFFFFFF 0x01005E"; + filter_id = 102; break; case DHD_MULTICAST6_FILTER_NUM: #if defined(BLOCK_IPV6_PACKET) && defined(CUSTOMER_HW4) -/* customer want to use NO IPV6 packets only */ + /* customer want to use NO IPV6 packets only */ return ret; #endif /* BLOCK_IPV6_PACKET && CUSTOMER_HW4 */ filterp = "103 0 0 0 0xFFFF 0x3333"; + filter_id = 103; break; default: return -EINVAL; + } + + /* Add filter */ + if (add_remove) { + dhd->pub.pktfilter[num] = filterp; + dhd_pktfilter_offload_set(&dhd->pub, dhd->pub.pktfilter[num]); + } else { /* Delete filter */ + if (dhd->pub.pktfilter[num] != NULL) { + dhd_pktfilter_offload_delete(&dhd->pub, filter_id); + dhd->pub.pktfilter[num] = NULL; } } - dhd->pub.pktfilter[num] = filterp; - dhd_pktfilter_offload_set(&dhd->pub, dhd->pub.pktfilter[num]); return ret; -#else - return 0; -#endif /* GAN_LITE_NAT_KEEPALIVE_FILTER */ +#endif /* CUSTOMER_HW4 && GAN_LITE_NAT_KEEPALIVE_FILTER */ } int dhd_os_enable_packet_filter(dhd_pub_t *dhdp, int val) @@ -5839,71 +6561,94 @@ int dhd_dev_init_ioctl(struct net_device *dev) { dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + int ret; -#if defined(CUSTOMER_HW4) && defined(USE_STAMAC_4SOFTAP) - /* Writing STA's MAC ID to the Dongle for SOFTAP */ - if (_dhd_set_mac_address(dhd, 0, &dhd->pub.mac) == 0) - DHD_INFO(("dhd_bus_start: MAC ID is overwritten\n")); -#endif /* CUSTOMER_HW4 && USE_STAMAC_4SOFTAP */ + dhd_process_cid_mac(&dhd->pub, TRUE); - return dhd_preinit_ioctls(&dhd->pub); + if ((ret = dhd_prot_init(&dhd->pub)) < 0) + goto done; + + dhd_process_cid_mac(&dhd->pub, FALSE); + +done: + return ret; } #ifdef PNO_SUPPORT -/* Linux wrapper to call common dhd_pno_clean */ +/* Linux wrapper to call common dhd_pno_stop_for_ssid */ int -dhd_dev_pno_reset(struct net_device *dev) +dhd_dev_pno_stop_for_ssid(struct net_device *dev) { dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); - return (dhd_pno_clean(&dhd->pub)); + return (dhd_pno_stop_for_ssid(&dhd->pub)); } +/* Linux wrapper to call common dhd_pno_set_for_ssid */ +int +dhd_dev_pno_set_for_ssid(struct net_device *dev, wlc_ssid_t* ssids_local, int nssid, + uint16 scan_fr, int pno_repeat, int pno_freq_expo_max, uint16 *channel_list, int nchan) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + return (dhd_pno_set_for_ssid(&dhd->pub, ssids_local, nssid, scan_fr, + pno_repeat, pno_freq_expo_max, channel_list, nchan)); +} /* Linux wrapper to call common dhd_pno_enable */ int -dhd_dev_pno_enable(struct net_device *dev, int pfn_enabled) +dhd_dev_pno_enable(struct net_device *dev, int enable) { dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); - return (dhd_pno_enable(&dhd->pub, pfn_enabled)); + return (dhd_pno_enable(&dhd->pub, enable)); } - -/* Linux wrapper to call common dhd_pno_set */ +/* Linux wrapper to call common dhd_pno_set_for_hotlist */ int -dhd_dev_pno_set(struct net_device *dev, wlc_ssid_t* ssids_local, int nssid, - ushort scan_fr, int pno_repeat, int pno_freq_expo_max) +dhd_dev_pno_set_for_hotlist(struct net_device *dev, wl_pfn_bssid_t *p_pfn_bssid, + struct dhd_pno_hotlist_params *hotlist_params) { dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); - - return (dhd_pno_set(&dhd->pub, ssids_local, nssid, scan_fr, pno_repeat, pno_freq_expo_max)); + return (dhd_pno_set_for_hotlist(&dhd->pub, p_pfn_bssid, hotlist_params)); } - -/* Linux wrapper to get pno status */ +/* Linux wrapper to call common dhd_dev_pno_stop_for_batch */ int -dhd_dev_get_pno_status(struct net_device *dev) +dhd_dev_pno_stop_for_batch(struct net_device *dev) { dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); - - return (dhd_pno_get_status(&dhd->pub)); + return (dhd_pno_stop_for_batch(&dhd->pub)); +} +/* Linux wrapper to call common dhd_dev_pno_set_for_batch */ +int +dhd_dev_pno_set_for_batch(struct net_device *dev, struct dhd_pno_batch_params *batch_params) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + return (dhd_pno_set_for_batch(&dhd->pub, batch_params)); +} +/* Linux wrapper to call common dhd_dev_pno_get_for_batch */ +int +dhd_dev_pno_get_for_batch(struct net_device *dev, char *buf, int bufsize) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + return (dhd_pno_get_for_batch(&dhd->pub, buf, bufsize, PNO_STATUS_NORMAL)); } - #endif /* PNO_SUPPORT */ -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) && (1) -static void dhd_hang_process(struct work_struct *work) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) +static void dhd_hang_process(void *dhd_info, void *event_info, u8 event) { dhd_info_t *dhd; struct net_device *dev; - dhd = (dhd_info_t *)container_of(work, dhd_info_t, work_hang); + dhd = (dhd_info_t *)dhd_info; dev = dhd->iflist[0]->net; if (dev) { +#if !defined(CUSTOMER_HW4) rtnl_lock(); dev_close(dev); rtnl_unlock(); +#endif /* !defined(CUSTOMER_HW4) */ #if defined(WL_WIRELESS_EXT) wl_iw_send_priv_event(dev, "HANG"); #endif @@ -5916,20 +6661,13 @@ static void dhd_hang_process(struct work_struct *work) int dhd_os_send_hang_message(dhd_pub_t *dhdp) { int ret = 0; - DHD_ERROR(("%s : Enter \n",__FUNCTION__)); - if (dhdp) { if (!dhdp->hang_was_sent) { dhdp->hang_was_sent = 1; - ret = schedule_work(&dhdp->info->work_hang); - if(!ret) - DHD_ERROR(("%s : schedule_task fail \n",__FUNCTION__)); - else - DHD_ERROR(("%s : schedule_task success \n",__FUNCTION__)); + dhd_deferred_schedule_work((void *)dhdp, DHD_WQ_WORK_HANG_MSG, + dhd_hang_process, DHD_WORK_PRIORITY_HIGH); } } - else - DHD_ERROR(("%s : dhdp is null \n",__FUNCTION__)); return ret; } @@ -5938,23 +6676,45 @@ int net_os_send_hang_message(struct net_device *dev) dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); int ret = 0; - if (dhd) + if (dhd) { + /* Report FW problem when enabled */ + if (dhd->pub.hang_report) { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) - ret = dhd_os_send_hang_message(&dhd->pub); + ret = dhd_os_send_hang_message(&dhd->pub); #else - ret = wl_cfg80211_hang(dev, WLAN_REASON_UNSPECIFIED); + ret = wl_cfg80211_hang(dev, WLAN_REASON_UNSPECIFIED); #endif + } else { + DHD_ERROR(("%s: FW HANG ignored (for testing purpose) and not sent up\n", + __FUNCTION__)); + /* Enforce bus down to stop any future traffic */ + dhd->pub.busstate = DHD_BUS_DOWN; + } + } return ret; } #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) && OEM_ANDROID */ -void dhd_bus_country_set(struct net_device *dev, wl_country_t *cspec) + +int dhd_net_wifi_platform_set_power(struct net_device *dev, bool on, unsigned long delay_msec) +{ + dhd_info_t *dhdinfo = *(dhd_info_t **)netdev_priv(dev); + return wifi_platform_set_power(dhdinfo->adapter, on, delay_msec); +} + +void dhd_get_customized_country_code(struct net_device *dev, char *country_iso_code, + wl_country_t *cspec) +{ + dhd_info_t *dhdinfo = *(dhd_info_t **)netdev_priv(dev); + get_customized_country_code(dhdinfo->adapter, country_iso_code, cspec); +} +void dhd_bus_country_set(struct net_device *dev, wl_country_t *cspec, bool notify) { dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); if (dhd && dhd->pub.up) { memcpy(&dhd->pub.dhd_cspec, cspec, sizeof(wl_country_t)); #ifdef WL_CFG80211 - wl_update_wiphybands(NULL); + wl_update_wiphybands(NULL, notify); #endif } } @@ -5964,11 +6724,33 @@ void dhd_bus_band_set(struct net_device *dev, uint band) dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); if (dhd && dhd->pub.up) { #ifdef WL_CFG80211 - wl_update_wiphybands(NULL); + wl_update_wiphybands(NULL, true); #endif } } +int dhd_net_set_fw_path(struct net_device *dev, char *fw) +{ + dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); + + if (!fw || fw[0] == '\0') + return -EINVAL; + + strncpy(dhd->fw_path, fw, sizeof(dhd->fw_path) - 1); + dhd->fw_path[sizeof(dhd->fw_path)-1] = '\0'; + +#if defined(SOFTAP) + if (strstr(fw, "apsta") != NULL) { + DHD_INFO(("GOT APSTA FIRMWARE\n")); + ap_fw_loaded = TRUE; + } else { + DHD_INFO(("GOT STA FIRMWARE\n")); + ap_fw_loaded = FALSE; + } +#endif + return 0; +} + void dhd_net_if_lock(struct net_device *dev) { dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); @@ -5983,7 +6765,7 @@ void dhd_net_if_unlock(struct net_device *dev) static void dhd_net_if_lock_local(dhd_info_t *dhd) { -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) if (dhd) mutex_lock(&dhd->dhd_net_if_mutex); #endif @@ -5991,7 +6773,7 @@ static void dhd_net_if_lock_local(dhd_info_t *dhd) static void dhd_net_if_unlock_local(dhd_info_t *dhd) { -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) if (dhd) mutex_unlock(&dhd->dhd_net_if_mutex); #endif @@ -5999,7 +6781,7 @@ static void dhd_net_if_unlock_local(dhd_info_t *dhd) static void dhd_suspend_lock(dhd_pub_t *pub) { -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) dhd_info_t *dhd = (dhd_info_t *)(pub->info); if (dhd) mutex_lock(&dhd->dhd_suspend_mutex); @@ -6008,7 +6790,7 @@ static void dhd_suspend_lock(dhd_pub_t *pub) static void dhd_suspend_unlock(dhd_pub_t *pub) { -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) && 1 +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)) dhd_info_t *dhd = (dhd_info_t *)(pub->info); if (dhd) mutex_unlock(&dhd->dhd_suspend_mutex); @@ -6116,10 +6898,10 @@ int dhd_os_wake_lock_timeout(dhd_pub_t *pub) dhd->wakelock_rx_timeout_enable : dhd->wakelock_ctrl_timeout_enable; #ifdef CONFIG_HAS_WAKELOCK if (dhd->wakelock_rx_timeout_enable) - wake_lock_timeout(dhd->wl_rxwake, + wake_lock_timeout(&dhd->wl_rxwake, msecs_to_jiffies(dhd->wakelock_rx_timeout_enable)); if (dhd->wakelock_ctrl_timeout_enable) - wake_lock_timeout(dhd->wl_ctrlwake, + wake_lock_timeout(&dhd->wl_ctrlwake, msecs_to_jiffies(dhd->wakelock_ctrl_timeout_enable)); #endif dhd->wakelock_rx_timeout_enable = 0; @@ -6167,6 +6949,23 @@ int dhd_os_wake_lock_ctrl_timeout_enable(dhd_pub_t *pub, int val) return 0; } +int dhd_os_wake_lock_ctrl_timeout_cancel(dhd_pub_t *pub) +{ + dhd_info_t *dhd = (dhd_info_t *)(pub->info); + unsigned long flags; + + if (dhd) { + spin_lock_irqsave(&dhd->wakelock_spinlock, flags); + dhd->wakelock_ctrl_timeout_enable = 0; +#ifdef CONFIG_HAS_WAKELOCK + if (wake_lock_active(&dhd->wl_ctrlwake)) + wake_unlock(&dhd->wl_ctrlwake); +#endif + spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); + } + return 0; +} + int net_os_wake_lock_rx_timeout_enable(struct net_device *dev, int val) { dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); @@ -6195,13 +6994,13 @@ int dhd_os_wake_lock(dhd_pub_t *pub) if (dhd) { spin_lock_irqsave(&dhd->wakelock_spinlock, flags); + if (dhd->wakelock_counter == 0 && !dhd->waive_wakelock) { #ifdef CONFIG_HAS_WAKELOCK - if (!dhd->wakelock_counter) - wake_lock(dhd->wl_wifi); -#elif 1 && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) - if (pm_dev) - pm_stay_awake(pm_dev); + wake_lock(&dhd->wl_wifi); +#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) + dhd_bus_dev_pm_stay_awake(pub); #endif + } dhd->wakelock_counter++; ret = dhd->wakelock_counter; spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); @@ -6228,15 +7027,15 @@ int dhd_os_wake_unlock(dhd_pub_t *pub) dhd_os_wake_lock_timeout(pub); if (dhd) { spin_lock_irqsave(&dhd->wakelock_spinlock, flags); - if (dhd->wakelock_counter) { + if (dhd->wakelock_counter > 0) { dhd->wakelock_counter--; + if (dhd->wakelock_counter == 0 && !dhd->waive_wakelock) { #ifdef CONFIG_HAS_WAKELOCK - if (!dhd->wakelock_counter) - wake_unlock(dhd->wl_wifi); -#elif 1 && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) - if (pm_dev) - pm_relax(pm_dev); + wake_unlock(&dhd->wl_wifi); +#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) + dhd_bus_dev_pm_relax(pub); #endif + } ret = dhd->wakelock_counter; } spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); @@ -6244,11 +7043,9 @@ int dhd_os_wake_unlock(dhd_pub_t *pub) return ret; } -int dhd_os_check_wakelock(void *dhdp) +int dhd_os_check_wakelock(dhd_pub_t *pub) { -#if defined(CONFIG_HAS_WAKELOCK) || (1 && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, \ - 36))) - dhd_pub_t *pub = (dhd_pub_t *)dhdp; +#if defined(CONFIG_HAS_WAKELOCK) || (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) dhd_info_t *dhd; if (!pub) @@ -6257,10 +7054,12 @@ int dhd_os_check_wakelock(void *dhdp) #endif /* CONFIG_HAS_WAKELOCK || BCMSDIO */ #ifdef CONFIG_HAS_WAKELOCK - if (dhd && wake_lock_active(dhd->wl_wifi)) + /* Indicate to the SD Host to avoid going to suspend if internal locks are up */ + if (dhd && (wake_lock_active(&dhd->wl_wifi) || + (wake_lock_active(&dhd->wl_wdwake)))) return 1; -#elif 1 && (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) - if (dhd && (dhd->wakelock_counter > 0) && pm_dev) +#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) + if (dhd && (dhd->wakelock_counter > 0) && dhd_bus_dev_pm_enabled(pub)) return 1; #endif return 0; @@ -6283,17 +7082,12 @@ int dhd_os_wd_wake_lock(dhd_pub_t *pub) if (dhd) { spin_lock_irqsave(&dhd->wakelock_spinlock, flags); + if (dhd->wakelock_wd_counter == 0 && !dhd->waive_wakelock) { #ifdef CONFIG_HAS_WAKELOCK - /* if wakelock_wd_counter was never used : lock it at once */ - if (!dhd->wakelock_wd_counter) { - if (dhd->wl_wdwake) - wake_lock(dhd->wl_wdwake); - else { - spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); - return 0; - } - } + /* if wakelock_wd_counter was never used : lock it at once */ + wake_lock(&dhd->wl_wdwake); #endif + } dhd->wakelock_wd_counter++; ret = dhd->wakelock_wd_counter; spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); @@ -6309,24 +7103,100 @@ int dhd_os_wd_wake_unlock(dhd_pub_t *pub) if (dhd) { spin_lock_irqsave(&dhd->wakelock_spinlock, flags); - if (dhd->wakelock_wd_counter) { + if (dhd->wakelock_wd_counter > 0) { dhd->wakelock_wd_counter = 0; + if (!dhd->waive_wakelock) { #ifdef CONFIG_HAS_WAKELOCK - wake_unlock(dhd->wl_wdwake); + wake_unlock(&dhd->wl_wdwake); #endif + } } spin_unlock_irqrestore(&dhd->wakelock_spinlock, flags); } return ret; } -int dhd_os_check_if_up(void *dhdp) + +#ifdef PROP_TXSTATUS +/* waive wakelocks for operations such as IOVARs in suspend function, must be closed + * by a paired function call to dhd_wakelock_restore. returns current wakelock counter + */ +int dhd_wakelock_waive(dhd_info_t *dhdinfo) { - dhd_pub_t *pub = (dhd_pub_t *)dhdp; + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&dhdinfo->wakelock_spinlock, flags); + /* dhd_wakelock_waive/dhd_wakelock_restore must be paired */ + if (dhdinfo->waive_wakelock) + goto exit; + /* record current lock status */ + dhdinfo->wakelock_before_waive = dhdinfo->wakelock_counter; + dhdinfo->waive_wakelock = TRUE; +exit: + ret = dhdinfo->wakelock_wd_counter; + spin_unlock_irqrestore(&dhdinfo->wakelock_spinlock, flags); + return ret; +} + +int dhd_wakelock_restore(dhd_info_t *dhdinfo) +{ + unsigned long flags; + int ret = 0; + + spin_lock_irqsave(&dhdinfo->wakelock_spinlock, flags); + /* dhd_wakelock_waive/dhd_wakelock_restore must be paired */ + if (!dhdinfo->waive_wakelock) + goto exit; + + dhdinfo->waive_wakelock = FALSE; + /* if somebody else acquires wakelock between dhd_wakelock_waive/dhd_wakelock_restore, + * we need to make it up by calling wake_lock or pm_stay_awake. or if somebody releases + * the lock in between, do the same by calling wake_unlock or pm_relax + */ + if (dhdinfo->wakelock_before_waive == 0 && dhdinfo->wakelock_counter > 0) { +#ifdef CONFIG_HAS_WAKELOCK + wake_lock(&dhdinfo->wl_wifi); +#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) + dhd_bus_dev_pm_stay_awake(&dhdinfo->pub); +#endif + } else if (dhdinfo->wakelock_before_waive > 0 && dhdinfo->wakelock_counter == 0) { +#ifdef CONFIG_HAS_WAKELOCK + wake_unlock(&dhdinfo->wl_wifi); +#elif (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 36)) + dhd_bus_dev_pm_relax(&dhdinfo->pub); +#endif + } + dhdinfo->wakelock_before_waive = 0; +exit: + ret = dhdinfo->wakelock_wd_counter; + spin_unlock_irqrestore(&dhdinfo->wakelock_spinlock, flags); + return ret; +} +#endif /* PROP_TXSTATUS */ + +bool dhd_os_check_if_up(dhd_pub_t *pub) +{ if (!pub) - return 0; + return FALSE; return pub->up; } + +/* function to collect firmware, chip id and chip version info */ +void dhd_set_version_info(dhd_pub_t *dhdp, char *fw) +{ + int i; + + i = snprintf(info_string, sizeof(info_string), + " Driver: %s\n Firmware: %s ", EPI_VERSION_STR, fw); + + if (!dhdp) + return; + + i = snprintf(&info_string[i], sizeof(info_string) - i, + "\n Chip: %x Rev %x Pkg %x", dhd_bus_chip_id(dhdp), + dhd_bus_chiprev_id(dhdp), dhd_bus_chippkg_id(dhdp)); +} int dhd_ioctl_entry_local(struct net_device *net, wl_ioctl_t *ioc, int cmd) { int ifidx; @@ -6361,6 +7231,11 @@ bool dhd_os_check_hang(dhd_pub_t *dhdp, int ifidx, int ret) struct net_device *net; net = dhd_idx2net(dhdp, ifidx); + if (!net) { + DHD_ERROR(("%s : Invalid index : %d\n", __FUNCTION__, ifidx)); + return -EINVAL; + } + return dhd_check_hang(net, dhdp, ret); } @@ -6381,8 +7256,8 @@ int dhd_deepsleep(struct net_device *dev, int flag) switch (flag) { case 1 : /* Deepsleep on */ DHD_ERROR(("[WiFi] Deepsleep On\n")); - /* give some time to _dhd_sysioc_thread() before deepsleep */ - msleep(200); + /* give some time to sysioc_work before deepsleep */ + OSL_SLEEP(200); #ifdef PKT_FILTER_SUPPORT /* disable pkt filter */ dhd_enable_packet_filter(0, dhdp); @@ -6442,30 +7317,38 @@ int dhd_deepsleep(struct net_device *dev, int flag) #endif /* WL_CFG80211 && SUPPORT_DEEP_SLEEP */ #ifdef PROP_TXSTATUS -extern int dhd_wlfc_interface_entry_update(void* state, ewlfc_mac_entry_action_t action, uint8 ifid, - uint8 iftype, uint8* ea); -extern int dhd_wlfc_FIFOcreditmap_update(void* state, uint8* credits); -int dhd_wlfc_interface_event(struct dhd_info *dhd, - ewlfc_mac_entry_action_t action, uint8 ifid, uint8 iftype, uint8* ea) +void dhd_wlfc_plat_init(void *dhd) { - if (dhd->pub.wlfc_state == NULL) - return BCME_OK; - - return dhd_wlfc_interface_entry_update(dhd->pub.wlfc_state, action, ifid, iftype, ea); +#if defined(CUSTOMER_HW4) && defined(USE_DYNAMIC_F2_BLKSIZE) + dhdsdio_func_blocksize((dhd_pub_t *)dhd, 2, DYNAMIC_F2_BLKSIZE_FOR_NONLEGACY); +#endif /* CUSTOMER_HW4 && USE_DYNAMIC_F2_BLKSIZE */ + return; } -int dhd_wlfc_FIFOcreditmap_event(struct dhd_info *dhd, uint8* event_data) +void dhd_wlfc_plat_deinit(void *dhd) { - if (dhd->pub.wlfc_state == NULL) - return BCME_OK; - - return dhd_wlfc_FIFOcreditmap_update(dhd->pub.wlfc_state, event_data); +#if defined(CUSTOMER_HW4) && defined(USE_DYNAMIC_F2_BLKSIZE) + dhdsdio_func_blocksize((dhd_pub_t *)dhd, 2, sd_f2_blocksize); +#endif /* CUSTOMER_HW4 && USE_DYNAMIC_F2_BLKSIZE */ + return; } -int dhd_wlfc_event(struct dhd_info *dhd) +bool dhd_wlfc_skip_fc(void) { - return dhd_wlfc_enable(&dhd->pub); +#ifdef CUSTOMER_HW4 + +#ifdef WL_CFG80211 + + /* enable flow control in vsdb mode */ + return !(wl_cfg80211_is_vsdb_mode()); +#else + return TRUE; /* skip flow control */ +#endif /* WL_CFG80211 */ + +#else + return FALSE; +#endif /* CUSTOMER_HW4 */ } #endif /* PROP_TXSTATUS */ @@ -6935,3 +7818,56 @@ void htsf_update(dhd_info_t *dhd, void *data) } #endif /* WLMEDIA_HTSF */ + +#ifdef CUSTOM_SET_CPUCORE +void dhd_set_cpucore(dhd_pub_t *dhd, int set) +{ + int e_dpc = 0, e_rxf = 0, retry_set = 0; + + if (!(dhd->chan_isvht80)) { + DHD_ERROR(("%s: chan_status(%d) cpucore!!!\n", __FUNCTION__, dhd->chan_isvht80)); + return; + } + + if (DPC_CPUCORE) { + do { + if (set == TRUE) { + e_dpc = set_cpus_allowed_ptr(dhd->current_dpc, + cpumask_of(DPC_CPUCORE)); + } else { + e_dpc = set_cpus_allowed_ptr(dhd->current_dpc, + cpumask_of(PRIMARY_CPUCORE)); + } + if (retry_set++ > MAX_RETRY_SET_CPUCORE) { + DHD_ERROR(("%s: dpc(%d) invalid cpu!\n", __FUNCTION__, e_dpc)); + return; + } + if (e_dpc < 0) + OSL_SLEEP(1); + } while (e_dpc < 0); + } + if (RXF_CPUCORE) { + do { + if (set == TRUE) { + e_rxf = set_cpus_allowed_ptr(dhd->current_rxf, + cpumask_of(RXF_CPUCORE)); + } else { + e_rxf = set_cpus_allowed_ptr(dhd->current_rxf, + cpumask_of(PRIMARY_CPUCORE)); + } + if (retry_set++ > MAX_RETRY_SET_CPUCORE) { + DHD_ERROR(("%s: rxf(%d) invalid cpu!\n", __FUNCTION__, e_rxf)); + return; + } + if (e_rxf < 0) + OSL_SLEEP(1); + } while (e_rxf < 0); + } +#ifdef DHD_OF_SUPPORT + interrupt_set_cpucore(set); +#endif /* DHD_OF_SUPPORT */ + DHD_TRACE(("%s: set(%d) cpucore success!\n", __FUNCTION__, set)); + + return; +} +#endif /* CUSTOM_SET_CPUCORE */ |