libertas: fix changing interface type when interface is down
authorDaniel Drake <dsd@laptop.org>
Fri, 14 Oct 2011 11:05:26 +0000 (12:05 +0100)
committerJohn W. Linville <linville@tuxdriver.com>
Fri, 14 Oct 2011 18:48:24 +0000 (14:48 -0400)
The recent changes to only power the device when the interface up
introduced a bug: changing interface type, legal when the interface
is down, performs device I/O.

Fix this functionality by validating and recording the interface
type when the change is requested, but only applying the change
if/when the interface is brought up.

Signed-off-by: Daniel Drake <dsd@laptop.org>
Acked-by: Dan Williams <dcbw@redhat.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/libertas/cfg.c
drivers/net/wireless/libertas/decl.h
drivers/net/wireless/libertas/main.c

index 610bfcee3cf6462c37c880866840a76398bcb3e8..ff6378276ff08d5f06f29ac604304466aff37aac 100644 (file)
@@ -1666,28 +1666,20 @@ static int lbs_change_intf(struct wiphy *wiphy, struct net_device *dev,
        if (dev == priv->mesh_dev)
                return -EOPNOTSUPP;
 
-       lbs_deb_enter(LBS_DEB_CFG80211);
-
        switch (type) {
        case NL80211_IFTYPE_MONITOR:
-               ret = lbs_set_monitor_mode(priv, 1);
-               break;
        case NL80211_IFTYPE_STATION:
-               if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR)
-                       ret = lbs_set_monitor_mode(priv, 0);
-               if (!ret)
-                       ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_BSS_TYPE, 1);
-               break;
        case NL80211_IFTYPE_ADHOC:
-               if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR)
-                       ret = lbs_set_monitor_mode(priv, 0);
-               if (!ret)
-                       ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_BSS_TYPE, 2);
                break;
        default:
-               ret = -ENOTSUPP;
+               return -EOPNOTSUPP;
        }
 
+       lbs_deb_enter(LBS_DEB_CFG80211);
+
+       if (priv->iface_running)
+               ret = lbs_set_iface_type(priv, type);
+
        if (!ret)
                priv->wdev->iftype = type;
 
index 9304e6fc421f064eeacfd5ce5419f39302a64ce8..bc951ab4b6818954c756d6da0c0455915757a31d 100644 (file)
@@ -9,6 +9,7 @@
 
 #include <linux/netdevice.h>
 #include <linux/firmware.h>
+#include <linux/nl80211.h>
 
 /* Should be terminated by a NULL entry */
 struct lbs_fw_table {
@@ -45,6 +46,7 @@ void lbs_host_to_card_done(struct lbs_private *priv);
 
 int lbs_start_iface(struct lbs_private *priv);
 int lbs_stop_iface(struct lbs_private *priv);
+int lbs_set_iface_type(struct lbs_private *priv, enum nl80211_iftype type);
 
 int lbs_rtap_supported(struct lbs_private *priv);
 
index 6a326233391fde7ec0b49e7943e7c64aa8bb4566..f78afd7bb768e0fb7f0358c7a3d9a4baafe077a9 100644 (file)
@@ -99,6 +99,32 @@ u8 lbs_data_rate_to_fw_index(u32 rate)
        return 0;
 }
 
+int lbs_set_iface_type(struct lbs_private *priv, enum nl80211_iftype type)
+{
+       int ret = 0;
+
+       switch (type) {
+       case NL80211_IFTYPE_MONITOR:
+               ret = lbs_set_monitor_mode(priv, 1);
+               break;
+       case NL80211_IFTYPE_STATION:
+               if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR)
+                       ret = lbs_set_monitor_mode(priv, 0);
+               if (!ret)
+                       ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_BSS_TYPE, 1);
+               break;
+       case NL80211_IFTYPE_ADHOC:
+               if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR)
+                       ret = lbs_set_monitor_mode(priv, 0);
+               if (!ret)
+                       ret = lbs_set_snmp_mib(priv, SNMP_MIB_OID_BSS_TYPE, 2);
+               break;
+       default:
+               ret = -ENOTSUPP;
+       }
+       return ret;
+}
+
 int lbs_start_iface(struct lbs_private *priv)
 {
        struct cmd_ds_802_11_mac_address cmd;
@@ -120,6 +146,12 @@ int lbs_start_iface(struct lbs_private *priv)
                goto err;
        }
 
+       ret = lbs_set_iface_type(priv, priv->wdev->iftype);
+       if (ret) {
+               lbs_deb_net("set iface type failed\n");
+               goto err;
+       }
+
        lbs_update_channel(priv);
 
        priv->iface_running = true;