From 0660b6fac4ddbd82696bcd2c01c8f62637511c2d Mon Sep 17 00:00:00 2001 From: Dmitry Shmidt Date: Tue, 5 Jun 2012 15:36:59 -0700 Subject: [PATCH] net: wireless: bcmdhd: Fix FW hang recovery Signed-off-by: Dmitry Shmidt --- drivers/net/wireless/bcmdhd/dhd_linux.c | 63 ++++++++++++++++++------- 1 file changed, 45 insertions(+), 18 deletions(-) diff --git a/drivers/net/wireless/bcmdhd/dhd_linux.c b/drivers/net/wireless/bcmdhd/dhd_linux.c index 0d1ba9d92ee1..805ff9db9a35 100644 --- a/drivers/net/wireless/bcmdhd/dhd_linux.c +++ b/drivers/net/wireless/bcmdhd/dhd_linux.c @@ -135,6 +135,9 @@ DECLARE_WAIT_QUEUE_HEAD(dhd_dpc_wait); #if defined(OOB_INTR_ONLY) 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)) +static void dhd_hang_process(struct work_struct *work); +#endif #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0)) MODULE_LICENSE("GPL v2"); #endif /* LinuxVer */ @@ -257,6 +260,9 @@ typedef struct dhd_info { #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 /* Wakelocks */ #if defined(CONFIG_HAS_WAKELOCK) && (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) @@ -2058,6 +2064,7 @@ dhd_ethtool(dhd_info_t *dhd, void *uaddr) static bool dhd_check_hang(struct net_device *net, dhd_pub_t *dhdp, int error) { +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) if (!dhdp) return FALSE; if ((error == -ETIMEDOUT) || ((dhdp->busstate == DHD_BUS_DOWN) && @@ -2067,6 +2074,7 @@ static bool dhd_check_hang(struct net_device *net, dhd_pub_t *dhdp, int error) net_os_send_hang_message(net); return TRUE; } +#endif return FALSE; } @@ -2333,7 +2341,7 @@ dhd_cleanup_virt_ifaces(dhd_info_t *dhd) static int dhd_stop(struct net_device *net) { - int ifidx; + int ifidx = 0; dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(net); DHD_OS_WAKE_LOCK(&dhd->pub); DHD_TRACE(("%s: Enter %p\n", __FUNCTION__, net)); @@ -2368,15 +2376,15 @@ dhd_stop(struct net_device *net) /* Stop the protocol module */ dhd_prot_stop(&dhd->pub); + OLD_MOD_DEC_USE_COUNT; +exit: #if defined(WL_CFG80211) if (ifidx == 0 && !dhd_download_fw_on_driverload) wl_android_wifi_off(net); -#endif - dhd->pub.hang_was_sent = 0; +#endif dhd->pub.rxcnt_timeout = 0; dhd->pub.txcnt_timeout = 0; - OLD_MOD_DEC_USE_COUNT; -exit: + DHD_OS_WAKE_UNLOCK(&dhd->pub); return 0; } @@ -2400,6 +2408,7 @@ dhd_open(struct net_device *net) firmware_path[0] = '\0'; } + dhd->pub.hang_was_sent = 0; #if !defined(WL_CFG80211) /* * Force start if ifconfig_up gets called before START command @@ -2801,7 +2810,9 @@ dhd_attach(osl_t *osh, struct dhd_bus *bus, uint bus_hdrlen) dhd->thr_sysioc_ctl.thr_pid = -1; } dhd_state |= DHD_ATTACH_STATE_THREADS_CREATED; - +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) + INIT_WORK(&dhd->work_hang, dhd_hang_process); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) */ /* * Save the dhd_info into the priv */ @@ -3655,6 +3666,9 @@ void dhd_detach(dhd_pub_t *dhdp) } #endif /* defined(CONFIG_HAS_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 (dhd->dhd_state & DHD_ATTACH_STATE_WL_ATTACH) { @@ -3985,14 +3999,11 @@ dhd_os_ioctl_resp_wait(dhd_pub_t *pub, uint *condition, bool *pending) * Can be changed by another processor. */ smp_mb(); - while (!(*condition) && (!signal_pending(current) && timeout)) { + while (!(*condition) && timeout) { timeout = schedule_timeout(timeout); smp_mb(); } - if (signal_pending(current)) - *pending = TRUE; - set_current_state(TASK_RUNNING); remove_wait_queue(&dhd->ioctl_resp_wait, &wait); @@ -4523,6 +4534,28 @@ dhd_dev_get_pno_status(struct net_device *dev) #endif /* PNO_SUPPORT */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)) +static void dhd_hang_process(struct work_struct *work) +{ + dhd_info_t *dhd; + struct net_device *dev; + + dhd = (dhd_info_t *)container_of(work, dhd_info_t, work_hang); + dev = dhd->iflist[0]->net; + + if (dev) { + rtnl_lock(); + dev_close(dev); + rtnl_unlock(); +#if defined(WL_WIRELESS_EXT) + wl_iw_send_priv_event(dev, "HANG"); +#endif +#if defined(WL_CFG80211) + wl_cfg80211_hang(dev, WLAN_REASON_UNSPECIFIED); +#endif + } +} + int net_os_send_hang_message(struct net_device *dev) { dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev); @@ -4531,18 +4564,12 @@ int net_os_send_hang_message(struct net_device *dev) if (dhd) { if (!dhd->pub.hang_was_sent) { dhd->pub.hang_was_sent = 1; -#if defined(CONFIG_WIRELESS_EXT) - ret = wl_iw_send_priv_event(dev, "HANG"); -#endif -#if defined(WL_CFG80211) - ret = wl_cfg80211_hang(dev, WLAN_REASON_UNSPECIFIED); - dev_close(dev); - dev_open(dev); -#endif + schedule_work(&dhd->work_hang); } } return ret; } +#endif void dhd_bus_country_set(struct net_device *dev, wl_country_t *cspec) {