net: wireless: bcm4329: Fix roaming setting on resume
[firefly-linux-kernel-4.4.55.git] / drivers / net / wireless / bcm4329 / dhd_linux.c
index aa1e7aa2ce34297e11844afccf5e13e1124266a2..da8dac3237db10d2365b05812af738c976851ac6 100644 (file)
@@ -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,v 1.65.4.9.2.12.2.89 2010/07/21 18:07:11 Exp $
+ * $Id: dhd_linux.c,v 1.65.4.9.2.12.2.104 2010/08/20 19:15:40 Exp $
  */
 
 #ifdef CONFIG_WIFI_CONTROL_FUNC
@@ -217,14 +217,16 @@ print_tainted()
 #include <wl_iw.h>
 #endif /* defined(CONFIG_WIRELESS_EXT) */
 
-extern int dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len);
-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);
-
 #if defined(CONFIG_HAS_EARLYSUSPEND)
 #include <linux/earlysuspend.h>
+extern int dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf, uint len);
 #endif /* defined(CONFIG_HAS_EARLYSUSPEND) */
 
+#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);
+#endif
+
 /* Interface control information */
 typedef struct dhd_if {
        struct dhd_info *info;                  /* back pointer to dhd_info */
@@ -277,6 +279,8 @@ typedef struct dhd_info {
        int wl_count;
        int wl_packet;
 
+       int hang_was_sent;
+
        /* Thread to issue ioctl for multicast */
        long sysioc_pid;
        struct semaphore sysioc_sem;
@@ -355,7 +359,7 @@ module_param(dhd_dpc_prio, int, 0);
 extern int dhd_dongle_memsize;
 module_param(dhd_dongle_memsize, int, 0);
 
-/* Contorl fw roaming */
+/* Control fw roaming */
 #ifdef CUSTOMER_HW2
 uint dhd_roam = 0;
 #else
@@ -498,20 +502,23 @@ extern int unregister_pm_notifier(struct notifier_block *nb);
 
 static void dhd_set_packet_filter(int value, dhd_pub_t *dhd)
 {
-       int i;
-
-       DHD_TRACE(("%s: %d\n", __func__, value));
+#ifdef PKT_FILTER_SUPPORT
+       DHD_TRACE(("%s: %d\n", __FUNCTION__, value));
        /* 1 - Enable packet filter, only allow unicast packet to send up */
        /* 0 - Disable packet filter */
        if (dhd_pkt_filter_enable) {
+               int i;
+
                for (i = 0; i < dhd->pktfilter_count; i++) {
                        dhd_pktfilter_offload_set(dhd, dhd->pktfilter[i]);
                        dhd_pktfilter_offload_enable(dhd, dhd->pktfilter[i],
                                        value, dhd_master_mode);
                }
        }
+#endif
 }
 
+#if defined(CONFIG_HAS_EARLYSUSPEND)
 static int dhd_set_suspend(int value, dhd_pub_t *dhd)
 {
        int power_mode = PM_MAX;
@@ -522,19 +529,29 @@ static int dhd_set_suspend(int value, dhd_pub_t *dhd)
        uint roamvar = 1;
 #endif /* CUSTOMER_HW2 */
 
-       DHD_TRACE(("%s: enter, value = %d\n", __FUNCTION__, value));
+       DHD_TRACE(("%s: enter, value = %d in_suspend = %d\n",
+                       __FUNCTION__, value, dhd->in_suspend));
 
        if (dhd && dhd->up) {
                if (value && dhd->in_suspend) {
 
                        /* Kernel suspended */
+                               DHD_TRACE(("%s: force extra Suspend setting \n", __FUNCTION__));
+
                        dhdcdc_set_ioctl(dhd, 0, WLC_SET_PM,
                                (char *)&power_mode, sizeof(power_mode));
 
                        /* Enable packet filter, only allow unicast packet to send up */
                        dhd_set_packet_filter(1, dhd);
 
-                       /* set bcn_li_dtim */
+                               /* if dtim skip setup as default force it to wake each thrid dtim
+                                *  for better power saving.
+                                *  Note that side effect is chance to miss BC/MC packet
+                               */
+                               if ((dhd->dtim_skip == 0) || (dhd->dtim_skip == 1))
+                                       bcn_li_dtim = 3;
+                               else
+                                       bcn_li_dtim = dhd->dtim_skip;
                        bcm_mkiovar("bcn_li_dtim", (char *)&bcn_li_dtim,
                                4, iovbuf, sizeof(iovbuf));
                        dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
@@ -546,6 +563,8 @@ static int dhd_set_suspend(int value, dhd_pub_t *dhd)
                } else {
 
                        /* Kernel resumed  */
+                               DHD_TRACE(("%s: Remove extra suspend setting \n", __FUNCTION__));
+
                        power_mode = PM_FAST;
                        dhdcdc_set_ioctl(dhd, 0, WLC_SET_PM, (char *)&power_mode,
                                sizeof(power_mode));
@@ -553,13 +572,13 @@ static int dhd_set_suspend(int value, dhd_pub_t *dhd)
                        /* disable pkt filter */
                        dhd_set_packet_filter(0, dhd);
 
-                       /* set bcn_li_dtim */
-                       bcn_li_dtim = 0;
-                       bcm_mkiovar("bcn_li_dtim", (char *)&bcn_li_dtim,
-                               4, iovbuf, sizeof(iovbuf));
-                       dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
+                               /* restore pre-suspend setting for dtim_skip */
+                               bcm_mkiovar("bcn_li_dtim", (char *)&dhd->dtim_skip,
+                                       4, iovbuf, sizeof(iovbuf));
+
+                               dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
 #ifdef CUSTOMER_HW2
-                       roamvar = 0;
+                       roamvar = dhd_roam;
                        bcm_mkiovar("roam_off", (char *)&roamvar, 4, iovbuf, sizeof(iovbuf));
                        dhdcdc_set_ioctl(dhd, 0, WLC_SET_VAR, iovbuf, sizeof(iovbuf));
 #endif /* CUSTOMER_HW2 */
@@ -569,13 +588,13 @@ static int dhd_set_suspend(int value, dhd_pub_t *dhd)
        return 0;
 }
 
-#if defined(CONFIG_HAS_EARLYSUSPEND)
 static void dhd_suspend_resume_helper(struct dhd_info *dhd, int val)
 {
        dhd_pub_t *dhdp = &dhd->pub;
 
        dhd_os_wake_lock(dhdp);
        dhd_os_proto_block(dhdp);
+       /* Set flag when early suspend was called */
        dhdp->in_suspend = val;
        if (!dhdp->suspend_disable_flag)
                dhd_set_suspend(val, dhdp);
@@ -1109,7 +1128,7 @@ dhd_start_xmit(struct sk_buff *skb, struct net_device *net)
                /* Send Event when bus down detected during data session */
                if (dhd->pub.busstate == DHD_BUS_DOWN)  {
                        DHD_ERROR(("%s: Event HANG send up\n", __FUNCTION__));
-                       wl_iw_send_priv_event(net, "HANG");
+                       net_os_send_hang_message(net);
                }
                dhd_os_wake_unlock(&dhd->pub);
                return -ENODEV;
@@ -1802,7 +1821,7 @@ done:
        if ((bcmerror == -ETIMEDOUT) || ((dhd->pub.busstate == DHD_BUS_DOWN) &&
                        (!dhd->pub.dongle_reset))) {
                DHD_ERROR(("%s: Event HANG send up\n", __FUNCTION__));
-               wl_iw_send_priv_event(net, "HANG");
+               net_os_send_hang_message(net);
        }
 
        if (!bcmerror && buf && ioc.buf) {
@@ -2192,6 +2211,12 @@ dhd_bus_start(dhd_pub_t *dhdp)
        setbit(dhdp->eventmask, WLC_E_TXFAIL);
        setbit(dhdp->eventmask, WLC_E_JOIN_START);
        setbit(dhdp->eventmask, WLC_E_SCAN_COMPLETE);
+#ifdef PNO_SUPPORT
+       setbit(dhdp->eventmask, WLC_E_PFN_NET_FOUND);
+#endif /* PNO_SUPPORT */
+
+/* enable dongle roaming event */
+       setbit(dhdp->eventmask, WLC_E_ROAM);
 
        dhdp->pktfilter_count = 1;
        /* Setup filter to allow only unicast */
@@ -2870,12 +2895,70 @@ dhd_dev_reset(struct net_device *dev, uint8 flag)
        /* Turning on watchdog back */
        if (!flag)
                dhd_os_wd_timer(&dhd->pub, dhd_watchdog_ms);
-
        DHD_ERROR(("%s:  WLAN OFF DONE\n", __FUNCTION__));
 
        return 1;
 }
 
+int net_os_set_suspend_disable(struct net_device *dev, int val)
+{
+       dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+       int ret = 0;
+
+       if (dhd) {
+               ret = dhd->pub.suspend_disable_flag;
+               dhd->pub.suspend_disable_flag = val;
+       }
+       return ret;
+}
+
+int net_os_set_suspend(struct net_device *dev, int val)
+{
+       int ret = 0;
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+       dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+
+       if (dhd) {
+               dhd_os_proto_block(&dhd->pub);
+               ret = dhd_set_suspend(val, &dhd->pub);
+               dhd_os_proto_unblock(&dhd->pub);
+       }
+#endif /* defined(CONFIG_HAS_EARLYSUSPEND) */
+       return ret;
+}
+
+int net_os_set_dtim_skip(struct net_device *dev, int val)
+{
+       dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+
+       if (dhd)
+               dhd->pub.dtim_skip = val;
+
+       return 0;
+}
+
+int net_os_set_packet_filter(struct net_device *dev, int val)
+{
+       dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+       int ret = 0;
+
+       /* Packet filtering is set only if we still in early-suspend and
+        * we need either to turn it ON or turn it OFF
+        * We can always turn it OFF in case of early-suspend, but we turn it
+        * back ON only if suspend_disable_flag was not set
+       */
+       if (dhd && dhd->pub.up) {
+               dhd_os_proto_block(&dhd->pub);
+               if (dhd->pub.in_suspend) {
+                       if (!val || (val && !dhd->pub.suspend_disable_flag))
+                               dhd_set_packet_filter(val, &dhd->pub);
+               }
+               dhd_os_proto_unblock(&dhd->pub);
+       }
+       return ret;
+}
+
+
 void
 dhd_dev_init_ioctl(struct net_device *dev)
 {
@@ -2884,6 +2967,47 @@ dhd_dev_init_ioctl(struct net_device *dev)
        dhd_preinit_ioctls(&dhd->pub);
 }
 
+#ifdef PNO_SUPPORT
+/* Linux wrapper to call common dhd_pno_clean */
+int
+dhd_dev_pno_reset(struct net_device *dev)
+{
+       dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+
+       return (dhd_pno_clean(&dhd->pub));
+}
+
+
+/* Linux wrapper to call common dhd_pno_enable */
+int
+dhd_dev_pno_enable(struct net_device *dev,  int pfn_enabled)
+{
+       dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+
+       return (dhd_pno_enable(&dhd->pub, pfn_enabled));
+}
+
+
+/* Linux wrapper to call common dhd_pno_set */
+int
+dhd_dev_pno_set(struct net_device *dev, wlc_ssid_t* ssids_local, int nssid, uchar  scan_fr)
+{
+       dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+
+       return (dhd_pno_set(&dhd->pub, ssids_local, nssid, scan_fr));
+}
+
+/* Linux wrapper to get  pno status */
+int
+dhd_dev_get_pno_status(struct net_device *dev)
+{
+       dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
+
+       return (dhd_pno_get_status(&dhd->pub));
+}
+
+#endif /* PNO_SUPPORT */
+
 static int
 dhd_get_pend_8021x_cnt(dhd_info_t *dhd)
 {
@@ -3066,47 +3190,16 @@ int net_os_wake_unlock(struct net_device *dev)
        return ret;
 }
 
-int net_os_set_suspend_disable(struct net_device *dev, int val)
-{
-       dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
-       int ret = 0;
-
-       if (dhd) {
-               ret = dhd->pub.suspend_disable_flag;
-               dhd->pub.suspend_disable_flag = val;
-       }
-       return ret;
-}
-
-int net_os_set_suspend(struct net_device *dev, int val)
+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) {
-               dhd_os_proto_block(&dhd->pub);
-               ret = dhd_set_suspend(val, &dhd->pub);
-               dhd_os_proto_unblock(&dhd->pub);
-       }
-       return ret;
-}
-
-int net_os_set_packet_filter(struct net_device *dev, int val)
-{
-       dhd_info_t *dhd = *(dhd_info_t **)netdev_priv(dev);
-       int ret = 0;
-
-       /* Packet filtering is set only if we still in early-suspend and
-        * we need either to turn it ON or turn it OFF
-        * We can always turn it OFF in case of early-suspend, but we turn it
-        * back ON only if suspend_disable_flag was not set */
-       if (dhd && dhd->pub.up) {
-               dhd_os_proto_block(&dhd->pub);
-               if (dhd->pub.in_suspend) {
-                       if (!val || (val && !dhd->pub.suspend_disable_flag))
-                               dhd_set_packet_filter(val, &dhd->pub);
+               if (!dhd->hang_was_sent) {
+                       dhd->hang_was_sent = 1;
+                       ret = wl_iw_send_priv_event(dev, "HANG");
                }
-               dhd_os_proto_unblock(&dhd->pub);
        }
        return ret;
 }