if (priv->scan_channel < priv->scan_req->n_channels) {
                cancel_delayed_work(&priv->scan_work);
-               if (!priv->stopping)
+               if (netif_running(priv->dev))
                        queue_delayed_work(priv->work_thread, &priv->scan_work,
                                msecs_to_jiffies(300));
        }
        return ret;
 }
 
-static int lbs_cfg_disconnect(struct wiphy *wiphy, struct net_device *dev,
-       u16 reason_code)
+int lbs_disconnect(struct lbs_private *priv, u16 reason)
 {
-       struct lbs_private *priv = wiphy_priv(wiphy);
        struct cmd_ds_802_11_deauthenticate cmd;
-
-       if (dev == priv->mesh_dev)
-               return -EOPNOTSUPP;
-
-       lbs_deb_enter_args(LBS_DEB_CFG80211, "reason_code %d", reason_code);
-
-       /* store for lbs_cfg_ret_disconnect() */
-       priv->disassoc_reason = reason_code;
+       int ret;
 
        memset(&cmd, 0, sizeof(cmd));
        cmd.hdr.size = cpu_to_le16(sizeof(cmd));
        /* Mildly ugly to use a locally store my own BSSID ... */
        memcpy(cmd.macaddr, &priv->assoc_bss, ETH_ALEN);
-       cmd.reasoncode = cpu_to_le16(reason_code);
+       cmd.reasoncode = cpu_to_le16(reason);
 
-       if (lbs_cmd_with_response(priv, CMD_802_11_DEAUTHENTICATE, &cmd))
-               return -EFAULT;
+       ret = lbs_cmd_with_response(priv, CMD_802_11_DEAUTHENTICATE, &cmd);
+       if (ret)
+               return ret;
 
        cfg80211_disconnected(priv->dev,
-                       priv->disassoc_reason,
+                       reason,
                        NULL, 0,
                        GFP_KERNEL);
        priv->connect_status = LBS_DISCONNECTED;
        return 0;
 }
 
+static int lbs_cfg_disconnect(struct wiphy *wiphy, struct net_device *dev,
+       u16 reason_code)
+{
+       struct lbs_private *priv = wiphy_priv(wiphy);
+
+       if (dev == priv->mesh_dev)
+               return -EOPNOTSUPP;
+
+       lbs_deb_enter_args(LBS_DEB_CFG80211, "reason_code %d", reason_code);
+
+       /* store for lbs_cfg_ret_disconnect() */
+       priv->disassoc_reason = reason_code;
+
+       return lbs_disconnect(priv, reason_code);
+}
 
 static int lbs_cfg_set_default_key(struct wiphy *wiphy,
                                   struct net_device *netdev,
 
        return 0;
 }
 
+int lbs_start_iface(struct lbs_private *priv)
+{
+       struct cmd_ds_802_11_mac_address cmd;
+       int ret;
+
+       if (priv->power_restore) {
+               ret = priv->power_restore(priv);
+               if (ret)
+                       return ret;
+       }
+
+       cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+       cmd.action = cpu_to_le16(CMD_ACT_SET);
+       memcpy(cmd.macadd, priv->current_addr, ETH_ALEN);
+
+       ret = lbs_cmd_with_response(priv, CMD_802_11_MAC_ADDRESS, &cmd);
+       if (ret) {
+               lbs_deb_net("set MAC address failed\n");
+               goto err;
+       }
+
+       lbs_update_channel(priv);
+
+       priv->iface_running = true;
+       return 0;
+
+err:
+       if (priv->power_save)
+               priv->power_save(priv);
+       return ret;
+}
 
 /**
  *  lbs_dev_open - open the ethX interface
        int ret = 0;
 
        lbs_deb_enter(LBS_DEB_NET);
+       if (!priv->iface_running) {
+               ret = lbs_start_iface(priv);
+               if (ret)
+                       goto out;
+       }
 
        spin_lock_irq(&priv->driver_lock);
-       priv->stopping = false;
 
-       if (priv->connect_status == LBS_CONNECTED)
-               netif_carrier_on(dev);
-       else
-               netif_carrier_off(dev);
+       netif_carrier_off(dev);
 
        if (!priv->tx_pending_len)
                netif_wake_queue(dev);
 
        spin_unlock_irq(&priv->driver_lock);
+
+out:
        lbs_deb_leave_args(LBS_DEB_NET, "ret %d", ret);
        return ret;
 }
 
+static bool lbs_command_queue_empty(struct lbs_private *priv)
+{
+       unsigned long flags;
+       bool ret;
+       spin_lock_irqsave(&priv->driver_lock, flags);
+       ret = priv->cur_cmd == NULL && list_empty(&priv->cmdpendingq);
+       spin_unlock_irqrestore(&priv->driver_lock, flags);
+       return ret;
+}
+
+int lbs_stop_iface(struct lbs_private *priv)
+{
+       unsigned long flags;
+       int ret = 0;
+
+       lbs_deb_enter(LBS_DEB_MAIN);
+
+       spin_lock_irqsave(&priv->driver_lock, flags);
+       priv->iface_running = false;
+       kfree_skb(priv->currenttxskb);
+       priv->currenttxskb = NULL;
+       priv->tx_pending_len = 0;
+       spin_unlock_irqrestore(&priv->driver_lock, flags);
+
+       cancel_work_sync(&priv->mcast_work);
+
+       /* Disable command processing, and wait for all commands to complete */
+       lbs_deb_main("waiting for commands to complete\n");
+       wait_event(priv->waitq, lbs_command_queue_empty(priv));
+       lbs_deb_main("all commands completed\n");
+
+       if (priv->power_save)
+               ret = priv->power_save(priv);
+
+       lbs_deb_leave(LBS_DEB_MAIN);
+       return ret;
+}
+
 /**
  *  lbs_eth_stop - close the ethX interface
  *
 
        lbs_deb_enter(LBS_DEB_NET);
 
+       if (priv->connect_status == LBS_CONNECTED)
+               lbs_disconnect(priv, WLAN_REASON_DEAUTH_LEAVING);
+
        spin_lock_irq(&priv->driver_lock);
-       priv->stopping = true;
        netif_stop_queue(dev);
        spin_unlock_irq(&priv->driver_lock);
 
-       schedule_work(&priv->mcast_work);
+       lbs_update_mcast(priv);
        cancel_delayed_work_sync(&priv->scan_work);
        if (priv->scan_req) {
                cfg80211_scan_done(priv->scan_req, false);
                priv->scan_req = NULL;
        }
 
+       netif_carrier_off(priv->dev);
+
+       if (!lbs_iface_active(priv))
+               lbs_stop_iface(priv);
+
        lbs_deb_leave(LBS_DEB_NET);
        return 0;
 }
        /* Wake main thread if commands are pending */
        if (!priv->cur_cmd || priv->tx_pending_len > 0) {
                if (!priv->wakeup_dev_required)
-                       wake_up_interruptible(&priv->waitq);
+                       wake_up(&priv->waitq);
        }
 
        spin_unlock_irqrestore(&priv->driver_lock, flags);
        int ret = 0;
        struct lbs_private *priv = dev->ml_priv;
        struct sockaddr *phwaddr = addr;
-       struct cmd_ds_802_11_mac_address cmd;
 
        lbs_deb_enter(LBS_DEB_NET);
 
+       /*
+        * Can only set MAC address when all interfaces are down, to be written
+        * to the hardware when one of them is brought up.
+        */
+       if (lbs_iface_active(priv))
+               return -EBUSY;
+
        /* In case it was called from the mesh device */
        dev = priv->dev;
 
-       cmd.hdr.size = cpu_to_le16(sizeof(cmd));
-       cmd.action = cpu_to_le16(CMD_ACT_SET);
-       memcpy(cmd.macadd, phwaddr->sa_data, ETH_ALEN);
-
-       ret = lbs_cmd_with_response(priv, CMD_802_11_MAC_ADDRESS, &cmd);
-       if (ret) {
-               lbs_deb_net("set MAC address failed\n");
-               goto done;
-       }
-
        memcpy(priv->current_addr, phwaddr->sa_data, ETH_ALEN);
        memcpy(dev->dev_addr, phwaddr->sa_data, ETH_ALEN);
        if (priv->mesh_dev)
                memcpy(priv->mesh_dev->dev_addr, phwaddr->sa_data, ETH_ALEN);
 
-done:
        lbs_deb_leave_args(LBS_DEB_NET, "ret %d", ret);
        return ret;
 }
        return i;
 }
 
-static void lbs_set_mcast_worker(struct work_struct *work)
+void lbs_update_mcast(struct lbs_private *priv)
 {
-       struct lbs_private *priv = container_of(work, struct lbs_private, mcast_work);
        struct cmd_ds_mac_multicast_adr mcast_cmd;
-       int dev_flags;
+       int dev_flags = 0;
        int nr_addrs;
        int old_mac_control = priv->mac_control;
 
        lbs_deb_enter(LBS_DEB_NET);
 
-       dev_flags = priv->dev->flags;
-       if (priv->mesh_dev)
+       if (netif_running(priv->dev))
+               dev_flags |= priv->dev->flags;
+       if (priv->mesh_dev && netif_running(priv->mesh_dev))
                dev_flags |= priv->mesh_dev->flags;
 
        if (dev_flags & IFF_PROMISC) {
        lbs_deb_leave(LBS_DEB_NET);
 }
 
+static void lbs_set_mcast_worker(struct work_struct *work)
+{
+       struct lbs_private *priv = container_of(work, struct lbs_private, mcast_work);
+       lbs_update_mcast(priv);
+}
+
 void lbs_set_multicast_list(struct net_device *dev)
 {
        struct lbs_private *priv = dev->ml_priv;
        if (priv->dnld_sent == DNLD_CMD_SENT)
                priv->dnld_sent = DNLD_RES_RECEIVED;
 
-       wake_up_interruptible(&priv->waitq);
+       wake_up(&priv->waitq);
 out:
        spin_unlock_irqrestore(&priv->driver_lock, flags);
        lbs_deb_leave(LBS_DEB_CMD);
        lbs_remove_mesh(priv);
        lbs_scan_deinit(priv);
 
-       dev = priv->dev;
-
-       cancel_work_sync(&priv->mcast_work);
-
        /* worker thread destruction blocks on the in-flight command which
         * should have been cleared already in lbs_stop_card().
         */
        if (lbs_mesh_activated(priv))
                lbs_start_mesh(priv);
 
-       lbs_update_channel(priv);
-
        lbs_debugfs_init_one(priv, dev);
 
        netdev_info(dev, "Marvell WLAN 802.11 adapter\n");
 void lbs_stop_card(struct lbs_private *priv)
 {
        struct net_device *dev;
-       struct cmd_ctrl_node *cmdnode;
-       unsigned long flags;
 
        lbs_deb_enter(LBS_DEB_MAIN);
 
 
        lbs_debugfs_remove_one(priv);
        lbs_deinit_mesh(priv);
-
-       /* Delete the timeout of the currently processing command */
-       del_timer_sync(&priv->command_timer);
-       del_timer_sync(&priv->auto_deepsleep_timer);
-
-       /* Flush pending command nodes */
-       spin_lock_irqsave(&priv->driver_lock, flags);
-       lbs_deb_main("clearing pending commands\n");
-       list_for_each_entry(cmdnode, &priv->cmdpendingq, list) {
-               cmdnode->result = -ENOENT;
-               cmdnode->cmdwaitqwoken = 1;
-               wake_up(&cmdnode->cmdwait_q);
-       }
-
-       /* Flush the command the card is currently processing */
-       if (priv->cur_cmd) {
-               lbs_deb_main("clearing current command\n");
-               priv->cur_cmd->result = -ENOENT;
-               priv->cur_cmd->cmdwaitqwoken = 1;
-               wake_up(&priv->cur_cmd->cmdwait_q);
-       }
-       lbs_deb_main("done clearing commands\n");
-       spin_unlock_irqrestore(&priv->driver_lock, flags);
-
        unregister_netdev(dev);
 
 out:
 
        kfifo_in(&priv->event_fifo, (unsigned char *) &event, sizeof(u32));
 
-       wake_up_interruptible(&priv->waitq);
+       wake_up(&priv->waitq);
 
        spin_unlock_irqrestore(&priv->driver_lock, flags);
        lbs_deb_leave(LBS_DEB_THREAD);
        BUG_ON(resp_idx > 1);
        priv->resp_idx = resp_idx;
 
-       wake_up_interruptible(&priv->waitq);
+       wake_up(&priv->waitq);
 
        lbs_deb_leave(LBS_DEB_THREAD);
 }