From dsd at laptop.org  Mon Aug  1 11:43:27 2011
From: dsd at laptop.org (Daniel Drake)
Date: Mon,  1 Aug 2011 16:43:27 +0100 (BST)
Subject: [PATCH 2/2] libertas: implement if_sdio runtime power management
Message-ID: <20110801154327.D0BB09D4020@zog.reactivated.net>

The SDIO card is now fully powered down when the network interface is
brought down.

Signed-off-by: Daniel Drake <dsd at laptop.org>
---
 drivers/net/wireless/libertas/if_sdio.c |  277 +++++++++++++++++++------------
 1 files changed, 171 insertions(+), 106 deletions(-)

diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c
index 387786e..c962e21 100644
--- a/drivers/net/wireless/libertas/if_sdio.c
+++ b/drivers/net/wireless/libertas/if_sdio.c
@@ -39,6 +39,7 @@
 #include <linux/mmc/sdio_ids.h>
 #include <linux/mmc/sdio.h>
 #include <linux/mmc/host.h>
+#include <linux/pm_runtime.h>
 
 #include "host.h"
 #include "decl.h"
@@ -47,6 +48,8 @@
 #include "cmd.h"
 #include "if_sdio.h"
 
+static void if_sdio_interrupt(struct sdio_func *func);
+
 /* The if_sdio_remove() callback function is called when
  * user removes this module from kernel space or ejects
  * the card from the slot. The driver handles these 2 cases
@@ -757,6 +760,136 @@ out:
 	return ret;
 }
 
+/********************************************************************/
+/* Power management                                                 */
+/********************************************************************/
+
+static int if_sdio_power_on(struct if_sdio_card *card)
+{
+	struct sdio_func *func = card->func;
+	struct lbs_private *priv = card->priv;
+	struct mmc_host *host = func->card->host;
+	int ret;
+
+	sdio_claim_host(func);
+
+	ret = sdio_enable_func(func);
+	if (ret)
+		goto release;
+
+	/* For 1-bit transfers to the 8686 model, we need to enable the
+	 * interrupt flag in the CCCR register. Set the MMC_QUIRK_LENIENT_FN0
+	 * bit to allow access to non-vendor registers. */
+	if ((card->model == MODEL_8686) &&
+	    (host->caps & MMC_CAP_SDIO_IRQ) &&
+	    (host->ios.bus_width == MMC_BUS_WIDTH_1)) {
+		u8 reg;
+
+		func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
+		reg = sdio_f0_readb(func, SDIO_CCCR_IF, &ret);
+		if (ret)
+			goto disable;
+
+		reg |= SDIO_BUS_ECSI;
+		sdio_f0_writeb(func, reg, SDIO_CCCR_IF, &ret);
+		if (ret)
+			goto disable;
+	}
+
+	card->ioport = sdio_readb(func, IF_SDIO_IOPORT, &ret);
+	if (ret)
+		goto disable;
+
+	card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 1, &ret) << 8;
+	if (ret)
+		goto disable;
+
+	card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 2, &ret) << 16;
+	if (ret)
+		goto disable;
+
+	sdio_release_host(func);
+	ret = if_sdio_prog_firmware(card);
+	sdio_claim_host(func);
+	if (ret)
+		goto disable;
+
+	/*
+	 * Get rx_unit if the chip is SD8688 or newer.
+	 * SD8385 & SD8686 do not have rx_unit.
+	 */
+	if ((card->model != MODEL_8385)
+			&& (card->model != MODEL_8686))
+		card->rx_unit = if_sdio_read_rx_unit(card);
+	else
+		card->rx_unit = 0;
+
+	/*
+	 * Set up the interrupt handler late.
+	 *
+	 * If we set it up earlier, the (buggy) hardware generates a spurious
+	 * interrupt, even before the interrupt has been enabled, with
+	 * CCCR_INTx = 0.
+	 *
+	 * We register the interrupt handler late so that we can handle any
+	 * spurious interrupts, and also to avoid generation of that known
+	 * spurious interrupt in the first place.
+	 */
+	ret = sdio_claim_irq(func, if_sdio_interrupt);
+	if (ret)
+		goto disable;
+
+	/*
+	 * Enable interrupts now that everything is set up
+	 */
+	sdio_writeb(func, 0x0f, IF_SDIO_H_INT_MASK, &ret);
+	if (ret)
+		goto release_irq;
+
+	sdio_release_host(func);
+
+	/*
+	 * FUNC_INIT is required for SD8688 WLAN/BT multiple functions
+	 */
+	if (card->model == MODEL_8688) {
+		struct cmd_header cmd;
+
+		memset(&cmd, 0, sizeof(cmd));
+
+		lbs_deb_sdio("send function INIT command\n");
+		if (__lbs_cmd(priv, CMD_FUNC_INIT, &cmd, sizeof(cmd),
+				lbs_cmd_copyback, (unsigned long) &cmd))
+			netdev_alert(priv->dev, "CMD_FUNC_INIT cmd failed\n");
+	}
+
+	priv->fw_ready = 1;
+
+	return 0;
+
+release_irq:
+	sdio_release_irq(func);
+disable:
+	sdio_disable_func(func);
+release:
+	sdio_release_host(func);
+	return ret;
+}
+
+static int if_sdio_power_off(struct if_sdio_card *card)
+{
+	struct sdio_func *func = card->func;
+	struct lbs_private *priv = card->priv;
+
+	priv->fw_ready = 0;
+
+	sdio_claim_host(func);
+	sdio_release_irq(func);
+	sdio_disable_func(func);
+	sdio_release_host(func);
+	return 0;
+}
+
+
 /*******************************************************************/
 /* Libertas callbacks                                              */
 /*******************************************************************/
@@ -923,6 +1056,32 @@ static void if_sdio_reset_card(struct lbs_private *priv)
 	schedule_work(&card_reset_work);
 }
 
+static int if_sdio_power_save(struct lbs_private *priv)
+{
+	struct if_sdio_card *card = priv->card;
+	int ret;
+
+	flush_workqueue(card->workqueue);
+
+	ret = if_sdio_power_off(card);
+
+	/* Let runtime PM know the card is powered off */
+	pm_runtime_put_sync(&card->func->dev);
+
+	return ret;
+}
+
+static int if_sdio_power_restore(struct lbs_private *priv)
+{
+	struct if_sdio_card *card = priv->card;
+
+	/* Make sure the card will not be powered off by runtime PM */
+	pm_runtime_get_sync(&card->func->dev);
+
+	return if_sdio_power_on(card);
+}
+
+
 /*******************************************************************/
 /* SDIO callbacks                                                  */
 /*******************************************************************/
@@ -976,7 +1135,6 @@ static int if_sdio_probe(struct sdio_func *func,
 	int ret, i;
 	unsigned int model;
 	struct if_sdio_packet *packet;
-	struct mmc_host *host = func->card->host;
 
 	lbs_deb_enter(LBS_DEB_SDIO);
 
@@ -1033,45 +1191,6 @@ static int if_sdio_probe(struct sdio_func *func,
 		goto free;
 	}
 
-	sdio_claim_host(func);
-
-	ret = sdio_enable_func(func);
-	if (ret)
-		goto release;
-
-	/* For 1-bit transfers to the 8686 model, we need to enable the
-	 * interrupt flag in the CCCR register. Set the MMC_QUIRK_LENIENT_FN0
-	 * bit to allow access to non-vendor registers. */
-	if ((card->model == MODEL_8686) &&
-	    (host->caps & MMC_CAP_SDIO_IRQ) &&
-	    (host->ios.bus_width == MMC_BUS_WIDTH_1)) {
-		u8 reg;
-
-		func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
-		reg = sdio_f0_readb(func, SDIO_CCCR_IF, &ret);
-		if (ret)
-			goto release_int;
-
-		reg |= SDIO_BUS_ECSI;
-		sdio_f0_writeb(func, reg, SDIO_CCCR_IF, &ret);
-		if (ret)
-			goto release_int;
-	}
-
-	card->ioport = sdio_readb(func, IF_SDIO_IOPORT, &ret);
-	if (ret)
-		goto release_int;
-
-	card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 1, &ret) << 8;
-	if (ret)
-		goto release_int;
-
-	card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 2, &ret) << 16;
-	if (ret)
-		goto release_int;
-
-	sdio_release_host(func);
-
 	sdio_set_drvdata(func, card);
 
 	lbs_deb_sdio("class = 0x%X, vendor = 0x%X, "
@@ -1079,14 +1198,11 @@ static int if_sdio_probe(struct sdio_func *func,
 			func->class, func->vendor, func->device,
 			model, (unsigned)card->ioport);
 
-	ret = if_sdio_prog_firmware(card);
-	if (ret)
-		goto reclaim;
 
 	priv = lbs_add_card(card, &func->dev);
 	if (!priv) {
 		ret = -ENOMEM;
-		goto reclaim;
+		goto free;
 	}
 
 	card->priv = priv;
@@ -1097,62 +1213,21 @@ static int if_sdio_probe(struct sdio_func *func,
 	priv->exit_deep_sleep = if_sdio_exit_deep_sleep;
 	priv->reset_deep_sleep_wakeup = if_sdio_reset_deep_sleep_wakeup;
 	priv->reset_card = if_sdio_reset_card;
+	priv->power_save = if_sdio_power_save;
+	priv->power_restore = if_sdio_power_restore;
 
-	sdio_claim_host(func);
-
-	/*
-	 * Get rx_unit if the chip is SD8688 or newer.
-	 * SD8385 & SD8686 do not have rx_unit.
-	 */
-	if ((card->model != MODEL_8385)
-			&& (card->model != MODEL_8686))
-		card->rx_unit = if_sdio_read_rx_unit(card);
-	else
-		card->rx_unit = 0;
-
-	/*
-	 * Set up the interrupt handler late.
-	 *
-	 * If we set it up earlier, the (buggy) hardware generates a spurious
-	 * interrupt, even before the interrupt has been enabled, with
-	 * CCCR_INTx = 0.
-	 *
-	 * We register the interrupt handler late so that we can handle any
-	 * spurious interrupts, and also to avoid generation of that known
-	 * spurious interrupt in the first place.
-	 */
-	ret = sdio_claim_irq(func, if_sdio_interrupt);
-	if (ret)
-		goto disable;
-
-	/*
-	 * Enable interrupts now that everything is set up
-	 */
-	sdio_writeb(func, 0x0f, IF_SDIO_H_INT_MASK, &ret);
-	sdio_release_host(func);
+	ret = if_sdio_power_on(card);
 	if (ret)
-		goto reclaim;
-
-	priv->fw_ready = 1;
-
-	/*
-	 * FUNC_INIT is required for SD8688 WLAN/BT multiple functions
-	 */
-	if (card->model == MODEL_8688) {
-		struct cmd_header cmd;
-
-		memset(&cmd, 0, sizeof(cmd));
-
-		lbs_deb_sdio("send function INIT command\n");
-		if (__lbs_cmd(priv, CMD_FUNC_INIT, &cmd, sizeof(cmd),
-				lbs_cmd_copyback, (unsigned long) &cmd))
-			netdev_alert(priv->dev, "CMD_FUNC_INIT cmd failed\n");
-	}
+		goto err_activate_card;
 
 	ret = lbs_start_card(priv);
+	if_sdio_power_off(card);
 	if (ret)
 		goto err_activate_card;
 
+	/* Tell PM core that we don't need the card to be powered now */
+	pm_runtime_put_noidle(&func->dev);
+
 out:
 	lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
 
@@ -1161,14 +1236,6 @@ out:
 err_activate_card:
 	flush_workqueue(card->workqueue);
 	lbs_remove_card(priv);
-reclaim:
-	sdio_claim_host(func);
-release_int:
-	sdio_release_irq(func);
-disable:
-	sdio_disable_func(func);
-release:
-	sdio_release_host(func);
 free:
 	destroy_workqueue(card->workqueue);
 	while (card->packets) {
@@ -1195,6 +1262,9 @@ static void if_sdio_remove(struct sdio_func *func)
 
 	card = sdio_get_drvdata(func);
 
+	/* Undo decrement done above in if_sdio_probe */
+	pm_runtime_get_noresume(&func->dev);
+
 	if (user_rmmod && (card->model == MODEL_8688)) {
 		/*
 		 * FUNC_SHUTDOWN is required for SD8688 WLAN/BT
@@ -1219,11 +1289,6 @@ static void if_sdio_remove(struct sdio_func *func)
 	flush_workqueue(card->workqueue);
 	destroy_workqueue(card->workqueue);
 
-	sdio_claim_host(func);
-	sdio_release_irq(func);
-	sdio_disable_func(func);
-	sdio_release_host(func);
-
 	while (card->packets) {
 		packet = card->packets;
 		card->packets = card->packets->next;
-- 
1.7.6



From dsd at laptop.org  Mon Aug  1 11:43:13 2011
From: dsd at laptop.org (Daniel Drake)
Date: Mon,  1 Aug 2011 16:43:13 +0100 (BST)
Subject: [PATCH 1/2] libertas: disable functionality when interface is down
Message-ID: <20110801154313.8C9389D401F@zog.reactivated.net>

Modify the driver so that it does not function when the interface is
down, in preparation for runtime power management.

No commands can be run while the interface is down, so the ndo_dev_stop
routine now directly does all necessary work (including asking the device
to disconnect from the network and disabling multicast functionality)
directly.

power_save and power_restore hooks are added meaning that card drivers
can take steps to turn the device off when the interface is down.

The MAC address can now only be changed when all interfaces are down;
the new address will be programmed when an interface gets brought up.
This matches mac80211 behaviour.

Also, some small cleanups/simplifications were made in the surrounding
device handling logic.

Signed-off-by: Daniel Drake <dsd at laptop.org>
---
 drivers/net/wireless/libertas/cfg.c    |   39 +++++---
 drivers/net/wireless/libertas/cfg.h    |    1 +
 drivers/net/wireless/libertas/cmd.c    |    6 +-
 drivers/net/wireless/libertas/decl.h   |    4 +
 drivers/net/wireless/libertas/dev.h    |   16 +++-
 drivers/net/wireless/libertas/if_usb.c |    2 +-
 drivers/net/wireless/libertas/main.c   |  168 ++++++++++++++++++++-----------
 drivers/net/wireless/libertas/mesh.c   |    9 ++-
 8 files changed, 163 insertions(+), 82 deletions(-)

diff --git a/drivers/net/wireless/libertas/cfg.c b/drivers/net/wireless/libertas/cfg.c
index 63009c7..85b3169 100644
--- a/drivers/net/wireless/libertas/cfg.c
+++ b/drivers/net/wireless/libertas/cfg.c
@@ -712,7 +712,7 @@ static void lbs_scan_worker(struct work_struct *work)
 
 	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));
 	}
@@ -1409,31 +1409,23 @@ static int lbs_cfg_connect(struct wiphy *wiphy, struct net_device *dev,
 	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;
@@ -1441,6 +1433,21 @@ static int lbs_cfg_disconnect(struct wiphy *wiphy, struct net_device *dev,
 	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,
diff --git a/drivers/net/wireless/libertas/cfg.h b/drivers/net/wireless/libertas/cfg.h
index 4f46bb7..a02ee15 100644
--- a/drivers/net/wireless/libertas/cfg.h
+++ b/drivers/net/wireless/libertas/cfg.h
@@ -17,5 +17,6 @@ void lbs_send_disconnect_notification(struct lbs_private *priv);
 void lbs_send_mic_failureevent(struct lbs_private *priv, u32 event);
 
 void lbs_scan_deinit(struct lbs_private *priv);
+int lbs_disconnect(struct lbs_private *priv, u16 reason);
 
 #endif
diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c
index dbd24a4..e08ab1d 100644
--- a/drivers/net/wireless/libertas/cmd.c
+++ b/drivers/net/wireless/libertas/cmd.c
@@ -1088,7 +1088,7 @@ void __lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd,
 	if (!cmd->callback || cmd->callback == lbs_cmd_async_callback)
 		__lbs_cleanup_and_insert_cmd(priv, cmd);
 	priv->cur_cmd = NULL;
-	wake_up_interruptible(&priv->waitq);
+	wake_up(&priv->waitq);
 }
 
 void lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node *cmd,
@@ -1627,7 +1627,7 @@ struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv,
 		lbs_deb_host("PREP_CMD: cmdnode is NULL\n");
 
 		/* Wake up main thread to execute next command */
-		wake_up_interruptible(&priv->waitq);
+		wake_up(&priv->waitq);
 		cmdnode = ERR_PTR(-ENOBUFS);
 		goto done;
 	}
@@ -1647,7 +1647,7 @@ struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv,
 
 	cmdnode->cmdwaitqwoken = 0;
 	lbs_queue_cmd(priv, cmdnode);
-	wake_up_interruptible(&priv->waitq);
+	wake_up(&priv->waitq);
 
  done:
 	lbs_deb_leave_args(LBS_DEB_HOST, "ret %p", cmdnode);
diff --git a/drivers/net/wireless/libertas/decl.h b/drivers/net/wireless/libertas/decl.h
index da0b05b..9304e6f 100644
--- a/drivers/net/wireless/libertas/decl.h
+++ b/drivers/net/wireless/libertas/decl.h
@@ -43,10 +43,14 @@ int lbs_start_card(struct lbs_private *priv);
 void lbs_stop_card(struct lbs_private *priv);
 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_rtap_supported(struct lbs_private *priv);
 
 int lbs_set_mac_address(struct net_device *dev, void *addr);
 void lbs_set_multicast_list(struct net_device *dev);
+void lbs_update_mcast(struct lbs_private *priv);
 
 int lbs_suspend(struct lbs_private *priv);
 int lbs_resume(struct lbs_private *priv);
diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h
index 133ff1c..8148389 100644
--- a/drivers/net/wireless/libertas/dev.h
+++ b/drivers/net/wireless/libertas/dev.h
@@ -46,7 +46,6 @@ struct lbs_private {
 	/* CFG80211 */
 	struct wireless_dev *wdev;
 	bool wiphy_registered;
-	bool stopping;
 	struct cfg80211_scan_request *scan_req;
 	u8 assoc_bss[ETH_ALEN];
 	u8 disassoc_reason;
@@ -96,11 +95,14 @@ struct lbs_private {
 
 	/* Hardware access */
 	void *card;
+	bool iface_running;
 	u8 fw_ready;
 	u8 surpriseremoved;
 	u8 setup_fw_on_resume;
 	int (*hw_host_to_card) (struct lbs_private *priv, u8 type, u8 *payload, u16 nb);
 	void (*reset_card) (struct lbs_private *priv);
+	int (*power_save) (struct lbs_private *priv);
+	int (*power_restore) (struct lbs_private *priv);
 	int (*enter_deep_sleep) (struct lbs_private *priv);
 	int (*exit_deep_sleep) (struct lbs_private *priv);
 	int (*reset_deep_sleep_wakeup) (struct lbs_private *priv);
@@ -182,4 +184,16 @@ struct lbs_private {
 
 extern struct cmd_confirm_sleep confirm_sleep;
 
+/* Check if there is an interface active. */
+static inline int lbs_iface_active(struct lbs_private *priv)
+{
+	int r;
+
+	r = netif_running(priv->dev);
+	if (priv->mesh_dev);
+		r |= netif_running(priv->dev);
+
+	return r;
+}
+
 #endif
diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/libertas/if_usb.c
index e368b29..0775f45 100644
--- a/drivers/net/wireless/libertas/if_usb.c
+++ b/drivers/net/wireless/libertas/if_usb.c
@@ -956,7 +956,7 @@ static int if_usb_prog_firmware(struct if_usb_card *cardp,
 	priv->dnld_sent = DNLD_RES_RECEIVED;
 	spin_unlock_irqrestore(&priv->driver_lock, flags);
 
-	wake_up_interruptible(&priv->waitq);
+	wake_up(&priv->waitq);
 
 	return ret;
 }
diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c
index ee28ae5..d62d1fb 100644
--- a/drivers/net/wireless/libertas/main.c
+++ b/drivers/net/wireless/libertas/main.c
@@ -99,6 +99,37 @@ u8 lbs_data_rate_to_fw_index(u32 rate)
 	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
@@ -112,23 +143,64 @@ static int lbs_dev_open(struct net_device *dev)
 	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
  *
@@ -141,18 +213,25 @@ static int lbs_eth_stop(struct net_device *dev)
 
 	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;
 }
@@ -170,7 +249,7 @@ void lbs_host_to_card_done(struct lbs_private *priv)
 	/* 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);
@@ -183,29 +262,24 @@ int lbs_set_mac_address(struct net_device *dev, void *addr)
 	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;
 }
@@ -259,18 +333,18 @@ static int lbs_add_mcast_addrs(struct cmd_ds_mac_multicast_adr *cmd,
 	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) {
@@ -316,6 +390,12 @@ static void lbs_set_mcast_worker(struct work_struct *work)
 	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;
@@ -648,7 +728,7 @@ static void lbs_cmd_timeout_handler(unsigned long data)
 	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);
@@ -890,10 +970,6 @@ void lbs_remove_card(struct lbs_private *priv)
 	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().
 	 */
@@ -964,8 +1040,6 @@ int lbs_start_card(struct lbs_private *priv)
 	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");
@@ -982,8 +1056,6 @@ EXPORT_SYMBOL_GPL(lbs_start_card);
 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);
 
@@ -996,30 +1068,6 @@ void lbs_stop_card(struct lbs_private *priv)
 
 	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:
@@ -1040,7 +1088,7 @@ void lbs_queue_event(struct lbs_private *priv, u32 event)
 
 	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);
@@ -1058,7 +1106,7 @@ void lbs_notify_command_response(struct lbs_private *priv, u8 resp_idx)
 	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);
 }
diff --git a/drivers/net/wireless/libertas/mesh.c b/drivers/net/wireless/libertas/mesh.c
index 2a635d2..138699b 100644
--- a/drivers/net/wireless/libertas/mesh.c
+++ b/drivers/net/wireless/libertas/mesh.c
@@ -924,7 +924,9 @@ static int lbs_mesh_stop(struct net_device *dev)
 
 	spin_unlock_irq(&priv->driver_lock);
 
-	schedule_work(&priv->mcast_work);
+	lbs_update_mcast(priv);
+	if (!lbs_iface_active(priv))
+		lbs_stop_iface(priv);
 
 	lbs_deb_leave(LBS_DEB_MESH);
 	return 0;
@@ -942,6 +944,11 @@ static int lbs_mesh_dev_open(struct net_device *dev)
 	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);
 
-- 
1.7.6



From anarsoul at gmail.com  Mon Aug  1 13:38:39 2011
From: anarsoul at gmail.com (Vasily Khoruzhick)
Date: Mon, 1 Aug 2011 20:38:39 +0300
Subject: [PATCH 1/2] libertas: disable functionality when interface is down
In-Reply-To: <20110801154313.8C9389D401F@zog.reactivated.net>
References: <20110801154313.8C9389D401F@zog.reactivated.net>
Message-ID: <201108012038.40155.anarsoul@gmail.com>

On Monday 01 August 2011 18:43:13 Daniel Drake wrote:

Hi, Daniel

> Modify the driver so that it does not function when the interface is
> down, in preparation for runtime power management.
> 
> No commands can be run while the interface is down, so the ndo_dev_stop
> routine now directly does all necessary work (including asking the device
> to disconnect from the network and disabling multicast functionality)
> directly.
> 
> power_save and power_restore hooks are added meaning that card drivers
> can take steps to turn the device off when the interface is down.

You mean completely off? And firmware needs to be reloaded after power up?

> The MAC address can now only be changed when all interfaces are down;
> the new address will be programmed when an interface gets brought up.
> This matches mac80211 behaviour.
> 
> Also, some small cleanups/simplifications were made in the surrounding
> device handling logic.

Regards
Vasily


From dsd at laptop.org  Mon Aug  1 13:50:03 2011
From: dsd at laptop.org (Daniel Drake)
Date: Mon, 1 Aug 2011 18:50:03 +0100
Subject: [PATCH 1/2] libertas: disable functionality when interface is down
In-Reply-To: <201108012038.40155.anarsoul@gmail.com>
References: <20110801154313.8C9389D401F@zog.reactivated.net>
	<201108012038.40155.anarsoul@gmail.com>
Message-ID: <CAGq3pz50EamvOmsshmZaPp5-ToW8eVi78r-BdDG-rX2154sH2A@mail.gmail.com>

On 1 August 2011 18:38, Vasily Khoruzhick <anarsoul at gmail.com> wrote:
>> power_save and power_restore hooks are added meaning that card drivers
>> can take steps to turn the device off when the interface is down.
>
> You mean completely off? And firmware needs to be reloaded after power up?

Yep, exactly like that. Same as various mac80211-based drivers.

The 2nd patch in this series implements it for libertas_sdio, the
other libertas interfaces have their behaviour as before.

Daniel


From dcbw at redhat.com  Mon Aug  1 13:17:23 2011
From: dcbw at redhat.com (Dan Williams)
Date: Mon, 01 Aug 2011 12:17:23 -0500
Subject: [PATCH] libertas: prioritize usb8388_v5.bin firmware on OLPC
	machines
In-Reply-To: <CAGq3pz75qVMuR_Hh3GKg8xK7T+_ZAW5gBZns6D6PfDYh4CF5GA@mail.gmail.com>
References: <20110731000300.5c08e95f@debxo> <20110731000521.45e48638@debxo>
	<CAGq3pz75qVMuR_Hh3GKg8xK7T+_ZAW5gBZns6D6PfDYh4CF5GA@mail.gmail.com>
Message-ID: <1312219075.6121.20.camel@dcbw.foobar.com>

On Sun, 2011-07-31 at 13:59 +0100, Daniel Drake wrote:
> On 31 July 2011 08:05, Andres Salomon <dilinger at queued.net> wrote:
> > Note: I've not tested this out yet on an XO-1.  I'll do that after some
> > feedback regarding the matching linux-firmware pull request.  This
> > should address dsd's concern about changing the loading order of the
> > firmware.
> 
> That indeed was a concern, but only until Dan Williams and Chris
> pointed out that OLPC is the only usb8388 user. With that in mind, I
> am in agreement with them that simply putting usb8388_olpc_v5.bin (or
> whatever name is chosen) at the top of the firmware loading list is
> the best option.

Yeah, patches for that would be immediately acked.

Dan



From dilinger at queued.net  Mon Aug  1 14:25:22 2011
From: dilinger at queued.net (Andres Salomon)
Date: Mon, 1 Aug 2011 11:25:22 -0700
Subject: [PATCH] libertas: prioritize usb8388_v5.bin firmware on OLPC
	machines
In-Reply-To: <1312219075.6121.20.camel@dcbw.foobar.com>
References: <20110731000300.5c08e95f@debxo> <20110731000521.45e48638@debxo>
	<CAGq3pz75qVMuR_Hh3GKg8xK7T+_ZAW5gBZns6D6PfDYh4CF5GA@mail.gmail.com>
	<1312219075.6121.20.camel@dcbw.foobar.com>
Message-ID: <20110801112522.1ba02f5e@debxo>

On Mon, 01 Aug 2011 12:17:23 -0500
Dan Williams <dcbw at redhat.com> wrote:

> On Sun, 2011-07-31 at 13:59 +0100, Daniel Drake wrote:
> > On 31 July 2011 08:05, Andres Salomon <dilinger at queued.net> wrote:
> > > Note: I've not tested this out yet on an XO-1.  I'll do that
> > > after some feedback regarding the matching linux-firmware pull
> > > request.  This should address dsd's concern about changing the
> > > loading order of the firmware.
> > 
> > That indeed was a concern, but only until Dan Williams and Chris
> > pointed out that OLPC is the only usb8388 user. With that in mind, I
> > am in agreement with them that simply putting usb8388_olpc_v5.bin
> > (or whatever name is chosen) at the top of the firmware loading
> > list is the best option.
> 
> Yeah, patches for that would be immediately acked.
> 

What's your opinion on overwriting usb8388_v5.bin with OLPC's version,
versus having both a usb8388_v5.bin and a usb8388_olpc_v5.bin?


From anarsoul at gmail.com  Mon Aug  1 15:13:12 2011
From: anarsoul at gmail.com (Vasily Khoruzhick)
Date: Mon, 1 Aug 2011 22:13:12 +0300
Subject: [PATCH 1/2] libertas: disable functionality when interface is down
In-Reply-To: <CAGq3pz50EamvOmsshmZaPp5-ToW8eVi78r-BdDG-rX2154sH2A@mail.gmail.com>
References: <20110801154313.8C9389D401F@zog.reactivated.net>
	<201108012038.40155.anarsoul@gmail.com>
	<CAGq3pz50EamvOmsshmZaPp5-ToW8eVi78r-BdDG-rX2154sH2A@mail.gmail.com>
Message-ID: <201108012213.13032.anarsoul@gmail.com>

On Monday 01 August 2011 20:50:03 Daniel Drake wrote:
> On 1 August 2011 18:38, Vasily Khoruzhick <anarsoul at gmail.com> wrote:
> >> power_save and power_restore hooks are added meaning that card drivers
> >> can take steps to turn the device off when the interface is down.
> > 
> > You mean completely off? And firmware needs to be reloaded after power
> > up?
> 
> Yep, exactly like that. Same as various mac80211-based drivers.
> 
> The 2nd patch in this series implements it for libertas_sdio, the
> other libertas interfaces have their behaviour as before.

Cool, I hope I'll get some time tomorrow to test your changes with 
libertas_spi.

Regards
Vasily


From dsd at laptop.org  Mon Aug  1 15:19:12 2011
From: dsd at laptop.org (Daniel Drake)
Date: Mon, 1 Aug 2011 20:19:12 +0100
Subject: [PATCH 1/2] libertas: disable functionality when interface is down
In-Reply-To: <201108012213.13032.anarsoul@gmail.com>
References: <20110801154313.8C9389D401F@zog.reactivated.net>
	<201108012038.40155.anarsoul@gmail.com>
	<CAGq3pz50EamvOmsshmZaPp5-ToW8eVi78r-BdDG-rX2154sH2A@mail.gmail.com>
	<201108012213.13032.anarsoul@gmail.com>
Message-ID: <CAGq3pz5xAZqofOJSanDfayabNd-i434QpAZFrKatui8WxMtBvQ@mail.gmail.com>

On 1 August 2011 20:13, Vasily Khoruzhick <anarsoul at gmail.com> wrote:
> Cool, I hope I'll get some time tomorrow to test your changes with
> libertas_spi.

Just to clarify, you won't see any power saving effects until you
implement power_save and power_restore on the SPI level. See patch 2/2
for how I did it for SDIO (it builds upon quite a bit of hard work
implementing runtime power management in the MMC layer).

Testing is appreciated anyway, because the driver shape changed in
general with this patch: e.g. it used to let you scan while the
interface is down, and now it doesn't. Would be good to have
additional confirmation that nothing has broken :)

Thanks,
Daniel


From anarsoul at gmail.com  Mon Aug  1 15:38:17 2011
From: anarsoul at gmail.com (Vasily Khoruzhick)
Date: Mon, 1 Aug 2011 22:38:17 +0300
Subject: [PATCH 1/2] libertas: disable functionality when interface is down
In-Reply-To: <CAGq3pz5xAZqofOJSanDfayabNd-i434QpAZFrKatui8WxMtBvQ@mail.gmail.com>
References: <20110801154313.8C9389D401F@zog.reactivated.net>
	<201108012213.13032.anarsoul@gmail.com>
	<CAGq3pz5xAZqofOJSanDfayabNd-i434QpAZFrKatui8WxMtBvQ@mail.gmail.com>
Message-ID: <201108012238.18114.anarsoul@gmail.com>

On Monday 01 August 2011 22:19:12 Daniel Drake wrote:
> On 1 August 2011 20:13, Vasily Khoruzhick <anarsoul at gmail.com> wrote:
> > Cool, I hope I'll get some time tomorrow to test your changes with
> > libertas_spi.
> 
> Just to clarify, you won't see any power saving effects until you
> implement power_save and power_restore on the SPI level. See patch 2/2
> for how I did it for SDIO (it builds upon quite a bit of hard work
> implementing runtime power management in the MMC layer).

Yep, I understand. I sent pretty similar patch with same functionality some 
month ago, but it was not clean enough, and I had no time to clean it up. So I 
have almost all things ready, just need some tuning :)

Regards
Vasily


From dcbw at redhat.com  Mon Aug  1 17:20:07 2011
From: dcbw at redhat.com (Dan Williams)
Date: Mon, 01 Aug 2011 16:20:07 -0500
Subject: [PATCH] libertas: prioritize usb8388_v5.bin firmware on OLPC
	machines
In-Reply-To: <20110801112522.1ba02f5e@debxo>
References: <20110731000300.5c08e95f@debxo> <20110731000521.45e48638@debxo>
	<CAGq3pz75qVMuR_Hh3GKg8xK7T+_ZAW5gBZns6D6PfDYh4CF5GA@mail.gmail.com>
	<1312219075.6121.20.camel@dcbw.foobar.com>
	<20110801112522.1ba02f5e@debxo>
Message-ID: <1312233609.3299.24.camel@dcbw.foobar.com>

On Mon, 2011-08-01 at 11:25 -0700, Andres Salomon wrote:
> On Mon, 01 Aug 2011 12:17:23 -0500
> Dan Williams <dcbw at redhat.com> wrote:
> 
> > On Sun, 2011-07-31 at 13:59 +0100, Daniel Drake wrote:
> > > On 31 July 2011 08:05, Andres Salomon <dilinger at queued.net> wrote:
> > > > Note: I've not tested this out yet on an XO-1.  I'll do that
> > > > after some feedback regarding the matching linux-firmware pull
> > > > request.  This should address dsd's concern about changing the
> > > > loading order of the firmware.
> > > 
> > > That indeed was a concern, but only until Dan Williams and Chris
> > > pointed out that OLPC is the only usb8388 user. With that in mind, I
> > > am in agreement with them that simply putting usb8388_olpc_v5.bin
> > > (or whatever name is chosen) at the top of the firmware loading
> > > list is the best option.
> > 
> > Yeah, patches for that would be immediately acked.
> > 
> 
> What's your opinion on overwriting usb8388_v5.bin with OLPC's version,
> versus having both a usb8388_v5.bin and a usb8388_olpc_v5.bin?

I'd slightly prefer to keep them separate, but I don't really care that
much.  Whatever you'd like to do here is fine.

Dan




From anarsoul at gmail.com  Tue Aug  2 05:02:03 2011
From: anarsoul at gmail.com (Vasily Khoruzhick)
Date: Tue, 2 Aug 2011 12:02:03 +0300
Subject: [PATCH 1/2] libertas: disable functionality when interface is down
In-Reply-To: <20110801154313.8C9389D401F@zog.reactivated.net>
References: <20110801154313.8C9389D401F@zog.reactivated.net>
Message-ID: <201108021202.03890.anarsoul@gmail.com>

On Monday 01 August 2011 18:43:13 Daniel Drake wrote:
> Modify the driver so that it does not function when the interface is
> down, in preparation for runtime power management.
> 
> No commands can be run while the interface is down, so the ndo_dev_stop
> routine now directly does all necessary work (including asking the device
> to disconnect from the network and disabling multicast functionality)
> directly.
> 
> power_save and power_restore hooks are added meaning that card drivers
> can take steps to turn the device off when the interface is down.
> 
> The MAC address can now only be changed when all interfaces are down;
> the new address will be programmed when an interface gets brought up.
> This matches mac80211 behaviour.
> 
> Also, some small cleanups/simplifications were made in the surrounding
> device handling logic.

Ok, one more question: what about suspend? Maybe it worth to add flag to 
disable card before going suspend and re-enable it during resume?

> Signed-off-by: Daniel Drake <dsd at laptop.org>
> ---
>  drivers/net/wireless/libertas/cfg.c    |   39 +++++---
>  drivers/net/wireless/libertas/cfg.h    |    1 +
>  drivers/net/wireless/libertas/cmd.c    |    6 +-
>  drivers/net/wireless/libertas/decl.h   |    4 +
>  drivers/net/wireless/libertas/dev.h    |   16 +++-
>  drivers/net/wireless/libertas/if_usb.c |    2 +-
>  drivers/net/wireless/libertas/main.c   |  168
> ++++++++++++++++++++----------- drivers/net/wireless/libertas/mesh.c   |  
>  9 ++-
>  8 files changed, 163 insertions(+), 82 deletions(-)
> 
> diff --git a/drivers/net/wireless/libertas/cfg.c
> b/drivers/net/wireless/libertas/cfg.c index 63009c7..85b3169 100644
> --- a/drivers/net/wireless/libertas/cfg.c
> +++ b/drivers/net/wireless/libertas/cfg.c
> @@ -712,7 +712,7 @@ static void lbs_scan_worker(struct work_struct *work)
> 
>  	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));
>  	}
> @@ -1409,31 +1409,23 @@ static int lbs_cfg_connect(struct wiphy *wiphy,
> struct net_device *dev, 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;
> @@ -1441,6 +1433,21 @@ static int lbs_cfg_disconnect(struct wiphy *wiphy,
> struct net_device *dev, 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,
> diff --git a/drivers/net/wireless/libertas/cfg.h
> b/drivers/net/wireless/libertas/cfg.h index 4f46bb7..a02ee15 100644
> --- a/drivers/net/wireless/libertas/cfg.h
> +++ b/drivers/net/wireless/libertas/cfg.h
> @@ -17,5 +17,6 @@ void lbs_send_disconnect_notification(struct lbs_private
> *priv); void lbs_send_mic_failureevent(struct lbs_private *priv, u32
> event);
> 
>  void lbs_scan_deinit(struct lbs_private *priv);
> +int lbs_disconnect(struct lbs_private *priv, u16 reason);
> 
>  #endif
> diff --git a/drivers/net/wireless/libertas/cmd.c
> b/drivers/net/wireless/libertas/cmd.c index dbd24a4..e08ab1d 100644
> --- a/drivers/net/wireless/libertas/cmd.c
> +++ b/drivers/net/wireless/libertas/cmd.c
> @@ -1088,7 +1088,7 @@ void __lbs_complete_command(struct lbs_private *priv,
> struct cmd_ctrl_node *cmd, if (!cmd->callback || cmd->callback ==
> lbs_cmd_async_callback)
>  		__lbs_cleanup_and_insert_cmd(priv, cmd);
>  	priv->cur_cmd = NULL;
> -	wake_up_interruptible(&priv->waitq);
> +	wake_up(&priv->waitq);
>  }
> 
>  void lbs_complete_command(struct lbs_private *priv, struct cmd_ctrl_node
> *cmd, @@ -1627,7 +1627,7 @@ struct cmd_ctrl_node *__lbs_cmd_async(struct
> lbs_private *priv, lbs_deb_host("PREP_CMD: cmdnode is NULL\n");
> 
>  		/* Wake up main thread to execute next command */
> -		wake_up_interruptible(&priv->waitq);
> +		wake_up(&priv->waitq);
>  		cmdnode = ERR_PTR(-ENOBUFS);
>  		goto done;
>  	}
> @@ -1647,7 +1647,7 @@ struct cmd_ctrl_node *__lbs_cmd_async(struct
> lbs_private *priv,
> 
>  	cmdnode->cmdwaitqwoken = 0;
>  	lbs_queue_cmd(priv, cmdnode);
> -	wake_up_interruptible(&priv->waitq);
> +	wake_up(&priv->waitq);
> 
>   done:
>  	lbs_deb_leave_args(LBS_DEB_HOST, "ret %p", cmdnode);
> diff --git a/drivers/net/wireless/libertas/decl.h
> b/drivers/net/wireless/libertas/decl.h index da0b05b..9304e6f 100644
> --- a/drivers/net/wireless/libertas/decl.h
> +++ b/drivers/net/wireless/libertas/decl.h
> @@ -43,10 +43,14 @@ int lbs_start_card(struct lbs_private *priv);
>  void lbs_stop_card(struct lbs_private *priv);
>  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_rtap_supported(struct lbs_private *priv);
> 
>  int lbs_set_mac_address(struct net_device *dev, void *addr);
>  void lbs_set_multicast_list(struct net_device *dev);
> +void lbs_update_mcast(struct lbs_private *priv);
> 
>  int lbs_suspend(struct lbs_private *priv);
>  int lbs_resume(struct lbs_private *priv);
> diff --git a/drivers/net/wireless/libertas/dev.h
> b/drivers/net/wireless/libertas/dev.h index 133ff1c..8148389 100644
> --- a/drivers/net/wireless/libertas/dev.h
> +++ b/drivers/net/wireless/libertas/dev.h
> @@ -46,7 +46,6 @@ struct lbs_private {
>  	/* CFG80211 */
>  	struct wireless_dev *wdev;
>  	bool wiphy_registered;
> -	bool stopping;
>  	struct cfg80211_scan_request *scan_req;
>  	u8 assoc_bss[ETH_ALEN];
>  	u8 disassoc_reason;
> @@ -96,11 +95,14 @@ struct lbs_private {
> 
>  	/* Hardware access */
>  	void *card;
> +	bool iface_running;
>  	u8 fw_ready;
>  	u8 surpriseremoved;
>  	u8 setup_fw_on_resume;
>  	int (*hw_host_to_card) (struct lbs_private *priv, u8 type, u8 *payload,
> u16 nb); void (*reset_card) (struct lbs_private *priv);
> +	int (*power_save) (struct lbs_private *priv);
> +	int (*power_restore) (struct lbs_private *priv);
>  	int (*enter_deep_sleep) (struct lbs_private *priv);
>  	int (*exit_deep_sleep) (struct lbs_private *priv);
>  	int (*reset_deep_sleep_wakeup) (struct lbs_private *priv);
> @@ -182,4 +184,16 @@ struct lbs_private {
> 
>  extern struct cmd_confirm_sleep confirm_sleep;
> 
> +/* Check if there is an interface active. */
> +static inline int lbs_iface_active(struct lbs_private *priv)
> +{
> +	int r;
> +
> +	r = netif_running(priv->dev);
> +	if (priv->mesh_dev);
> +		r |= netif_running(priv->dev);
> +
> +	return r;
> +}
> +
>  #endif
> diff --git a/drivers/net/wireless/libertas/if_usb.c
> b/drivers/net/wireless/libertas/if_usb.c index e368b29..0775f45 100644
> --- a/drivers/net/wireless/libertas/if_usb.c
> +++ b/drivers/net/wireless/libertas/if_usb.c
> @@ -956,7 +956,7 @@ static int if_usb_prog_firmware(struct if_usb_card
> *cardp, priv->dnld_sent = DNLD_RES_RECEIVED;
>  	spin_unlock_irqrestore(&priv->driver_lock, flags);
> 
> -	wake_up_interruptible(&priv->waitq);
> +	wake_up(&priv->waitq);
> 
>  	return ret;
>  }
> diff --git a/drivers/net/wireless/libertas/main.c
> b/drivers/net/wireless/libertas/main.c index ee28ae5..d62d1fb 100644
> --- a/drivers/net/wireless/libertas/main.c
> +++ b/drivers/net/wireless/libertas/main.c
> @@ -99,6 +99,37 @@ u8 lbs_data_rate_to_fw_index(u32 rate)
>  	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
> @@ -112,23 +143,64 @@ static int lbs_dev_open(struct net_device *dev)
>  	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
>   *
> @@ -141,18 +213,25 @@ static int lbs_eth_stop(struct net_device *dev)
> 
>  	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;
>  }
> @@ -170,7 +249,7 @@ void lbs_host_to_card_done(struct lbs_private *priv)
>  	/* 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);
> @@ -183,29 +262,24 @@ int lbs_set_mac_address(struct net_device *dev, void
> *addr) 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;
>  }
> @@ -259,18 +333,18 @@ static int lbs_add_mcast_addrs(struct
> cmd_ds_mac_multicast_adr *cmd, 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) {
> @@ -316,6 +390,12 @@ static void lbs_set_mcast_worker(struct work_struct
> *work) 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;
> @@ -648,7 +728,7 @@ static void lbs_cmd_timeout_handler(unsigned long data)
>  	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);
> @@ -890,10 +970,6 @@ void lbs_remove_card(struct lbs_private *priv)
>  	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().
>  	 */
> @@ -964,8 +1040,6 @@ int lbs_start_card(struct lbs_private *priv)
>  	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");
> @@ -982,8 +1056,6 @@ EXPORT_SYMBOL_GPL(lbs_start_card);
>  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);
> 
> @@ -996,30 +1068,6 @@ void lbs_stop_card(struct lbs_private *priv)
> 
>  	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:
> @@ -1040,7 +1088,7 @@ void lbs_queue_event(struct lbs_private *priv, u32
> event)
> 
>  	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);
> @@ -1058,7 +1106,7 @@ void lbs_notify_command_response(struct lbs_private
> *priv, u8 resp_idx) 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);
>  }
> diff --git a/drivers/net/wireless/libertas/mesh.c
> b/drivers/net/wireless/libertas/mesh.c index 2a635d2..138699b 100644
> --- a/drivers/net/wireless/libertas/mesh.c
> +++ b/drivers/net/wireless/libertas/mesh.c
> @@ -924,7 +924,9 @@ static int lbs_mesh_stop(struct net_device *dev)
> 
>  	spin_unlock_irq(&priv->driver_lock);
> 
> -	schedule_work(&priv->mcast_work);
> +	lbs_update_mcast(priv);
> +	if (!lbs_iface_active(priv))
> +		lbs_stop_iface(priv);
> 
>  	lbs_deb_leave(LBS_DEB_MESH);
>  	return 0;
> @@ -942,6 +944,11 @@ static int lbs_mesh_dev_open(struct net_device *dev)
>  	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);


From anarsoul at gmail.com  Tue Aug  2 05:04:08 2011
From: anarsoul at gmail.com (Vasily Khoruzhick)
Date: Tue, 2 Aug 2011 12:04:08 +0300
Subject: [PATCH 2/2] libertas: implement if_sdio runtime power management
In-Reply-To: <20110801154327.D0BB09D4020@zog.reactivated.net>
References: <20110801154327.D0BB09D4020@zog.reactivated.net>
Message-ID: <201108021204.08632.anarsoul@gmail.com>

On Monday 01 August 2011 18:43:27 Daniel Drake wrote:
> The SDIO card is now fully powered down when the network interface is
> brought down.
> 
> Signed-off-by: Daniel Drake <dsd at laptop.org>
> ---
>  drivers/net/wireless/libertas/if_sdio.c |  277
> +++++++++++++++++++------------ 1 files changed, 171 insertions(+), 106
> deletions(-)
> 
> diff --git a/drivers/net/wireless/libertas/if_sdio.c
> b/drivers/net/wireless/libertas/if_sdio.c index 387786e..c962e21 100644
> --- a/drivers/net/wireless/libertas/if_sdio.c
> +++ b/drivers/net/wireless/libertas/if_sdio.c
> @@ -39,6 +39,7 @@
>  #include <linux/mmc/sdio_ids.h>
>  #include <linux/mmc/sdio.h>
>  #include <linux/mmc/host.h>
> +#include <linux/pm_runtime.h>
> 
>  #include "host.h"
>  #include "decl.h"
> @@ -47,6 +48,8 @@
>  #include "cmd.h"
>  #include "if_sdio.h"
> 
> +static void if_sdio_interrupt(struct sdio_func *func);
> +
>  /* The if_sdio_remove() callback function is called when
>   * user removes this module from kernel space or ejects
>   * the card from the slot. The driver handles these 2 cases
> @@ -757,6 +760,136 @@ out:
>  	return ret;
>  }
> 
> +/********************************************************************/
> +/* Power management                                                 */
> +/********************************************************************/
> +
> +static int if_sdio_power_on(struct if_sdio_card *card)
> +{
> +	struct sdio_func *func = card->func;
> +	struct lbs_private *priv = card->priv;
> +	struct mmc_host *host = func->card->host;
> +	int ret;
> +
> +	sdio_claim_host(func);
> +
> +	ret = sdio_enable_func(func);
> +	if (ret)
> +		goto release;
> +
> +	/* For 1-bit transfers to the 8686 model, we need to enable the
> +	 * interrupt flag in the CCCR register. Set the MMC_QUIRK_LENIENT_FN0
> +	 * bit to allow access to non-vendor registers. */
> +	if ((card->model == MODEL_8686) &&
> +	    (host->caps & MMC_CAP_SDIO_IRQ) &&
> +	    (host->ios.bus_width == MMC_BUS_WIDTH_1)) {
> +		u8 reg;
> +
> +		func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
> +		reg = sdio_f0_readb(func, SDIO_CCCR_IF, &ret);
> +		if (ret)
> +			goto disable;
> +
> +		reg |= SDIO_BUS_ECSI;
> +		sdio_f0_writeb(func, reg, SDIO_CCCR_IF, &ret);
> +		if (ret)
> +			goto disable;
> +	}
> +
> +	card->ioport = sdio_readb(func, IF_SDIO_IOPORT, &ret);
> +	if (ret)
> +		goto disable;
> +
> +	card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 1, &ret) << 8;
> +	if (ret)
> +		goto disable;
> +
> +	card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 2, &ret) << 16;
> +	if (ret)
> +		goto disable;
> +
> +	sdio_release_host(func);
> +	ret = if_sdio_prog_firmware(card);
> +	sdio_claim_host(func);
> +	if (ret)
> +		goto disable;
> +
> +	/*
> +	 * Get rx_unit if the chip is SD8688 or newer.
> +	 * SD8385 & SD8686 do not have rx_unit.
> +	 */
> +	if ((card->model != MODEL_8385)
> +			&& (card->model != MODEL_8686))
> +		card->rx_unit = if_sdio_read_rx_unit(card);
> +	else
> +		card->rx_unit = 0;
> +
> +	/*
> +	 * Set up the interrupt handler late.
> +	 *
> +	 * If we set it up earlier, the (buggy) hardware generates a spurious
> +	 * interrupt, even before the interrupt has been enabled, with
> +	 * CCCR_INTx = 0.
> +	 *
> +	 * We register the interrupt handler late so that we can handle any
> +	 * spurious interrupts, and also to avoid generation of that known
> +	 * spurious interrupt in the first place.
> +	 */
> +	ret = sdio_claim_irq(func, if_sdio_interrupt);
> +	if (ret)
> +		goto disable;
> +
> +	/*
> +	 * Enable interrupts now that everything is set up
> +	 */
> +	sdio_writeb(func, 0x0f, IF_SDIO_H_INT_MASK, &ret);
> +	if (ret)
> +		goto release_irq;
> +
> +	sdio_release_host(func);
> +
> +	/*
> +	 * FUNC_INIT is required for SD8688 WLAN/BT multiple functions
> +	 */
> +	if (card->model == MODEL_8688) {
> +		struct cmd_header cmd;
> +
> +		memset(&cmd, 0, sizeof(cmd));
> +
> +		lbs_deb_sdio("send function INIT command\n");
> +		if (__lbs_cmd(priv, CMD_FUNC_INIT, &cmd, sizeof(cmd),
> +				lbs_cmd_copyback, (unsigned long) &cmd))
> +			netdev_alert(priv->dev, "CMD_FUNC_INIT cmd failed\n");
> +	}
> +
> +	priv->fw_ready = 1;
> +
> +	return 0;
> +
> +release_irq:
> +	sdio_release_irq(func);
> +disable:
> +	sdio_disable_func(func);
> +release:
> +	sdio_release_host(func);
> +	return ret;
> +}
> +
> +static int if_sdio_power_off(struct if_sdio_card *card)
> +{
> +	struct sdio_func *func = card->func;
> +	struct lbs_private *priv = card->priv;
> +
> +	priv->fw_ready = 0;
> +
> +	sdio_claim_host(func);
> +	sdio_release_irq(func);
> +	sdio_disable_func(func);
> +	sdio_release_host(func);
> +	return 0;
> +}
> +
> +
>  /*******************************************************************/
>  /* Libertas callbacks                                              */
>  /*******************************************************************/
> @@ -923,6 +1056,32 @@ static void if_sdio_reset_card(struct lbs_private
> *priv) schedule_work(&card_reset_work);
>  }
> 
> +static int if_sdio_power_save(struct lbs_private *priv)
> +{
> +	struct if_sdio_card *card = priv->card;
> +	int ret;
> +
> +	flush_workqueue(card->workqueue);
> +
> +	ret = if_sdio_power_off(card);
> +
> +	/* Let runtime PM know the card is powered off */
> +	pm_runtime_put_sync(&card->func->dev);
> +
> +	return ret;
> +}
> +
> +static int if_sdio_power_restore(struct lbs_private *priv)
> +{
> +	struct if_sdio_card *card = priv->card;
> +
> +	/* Make sure the card will not be powered off by runtime PM */
> +	pm_runtime_get_sync(&card->func->dev);
> +
> +	return if_sdio_power_on(card);
> +}
> +
> +
>  /*******************************************************************/
>  /* SDIO callbacks                                                  */
>  /*******************************************************************/
> @@ -976,7 +1135,6 @@ static int if_sdio_probe(struct sdio_func *func,
>  	int ret, i;
>  	unsigned int model;
>  	struct if_sdio_packet *packet;
> -	struct mmc_host *host = func->card->host;
> 
>  	lbs_deb_enter(LBS_DEB_SDIO);
> 
> @@ -1033,45 +1191,6 @@ static int if_sdio_probe(struct sdio_func *func,
>  		goto free;
>  	}
> 
> -	sdio_claim_host(func);
> -
> -	ret = sdio_enable_func(func);
> -	if (ret)
> -		goto release;
> -
> -	/* For 1-bit transfers to the 8686 model, we need to enable the
> -	 * interrupt flag in the CCCR register. Set the MMC_QUIRK_LENIENT_FN0
> -	 * bit to allow access to non-vendor registers. */
> -	if ((card->model == MODEL_8686) &&
> -	    (host->caps & MMC_CAP_SDIO_IRQ) &&
> -	    (host->ios.bus_width == MMC_BUS_WIDTH_1)) {
> -		u8 reg;
> -
> -		func->card->quirks |= MMC_QUIRK_LENIENT_FN0;
> -		reg = sdio_f0_readb(func, SDIO_CCCR_IF, &ret);
> -		if (ret)
> -			goto release_int;
> -
> -		reg |= SDIO_BUS_ECSI;
> -		sdio_f0_writeb(func, reg, SDIO_CCCR_IF, &ret);
> -		if (ret)
> -			goto release_int;
> -	}
> -
> -	card->ioport = sdio_readb(func, IF_SDIO_IOPORT, &ret);
> -	if (ret)
> -		goto release_int;
> -
> -	card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 1, &ret) << 8;
> -	if (ret)
> -		goto release_int;
> -
> -	card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 2, &ret) << 16;
> -	if (ret)
> -		goto release_int;
> -
> -	sdio_release_host(func);
> -
>  	sdio_set_drvdata(func, card);
> 
>  	lbs_deb_sdio("class = 0x%X, vendor = 0x%X, "
> @@ -1079,14 +1198,11 @@ static int if_sdio_probe(struct sdio_func *func,
>  			func->class, func->vendor, func->device,
>  			model, (unsigned)card->ioport);
> 
> -	ret = if_sdio_prog_firmware(card);
> -	if (ret)
> -		goto reclaim;
> 
>  	priv = lbs_add_card(card, &func->dev);
>  	if (!priv) {
>  		ret = -ENOMEM;
> -		goto reclaim;
> +		goto free;
>  	}
> 
>  	card->priv = priv;
> @@ -1097,62 +1213,21 @@ static int if_sdio_probe(struct sdio_func *func,
>  	priv->exit_deep_sleep = if_sdio_exit_deep_sleep;
>  	priv->reset_deep_sleep_wakeup = if_sdio_reset_deep_sleep_wakeup;
>  	priv->reset_card = if_sdio_reset_card;
> +	priv->power_save = if_sdio_power_save;
> +	priv->power_restore = if_sdio_power_restore;
> 
> -	sdio_claim_host(func);
> -
> -	/*
> -	 * Get rx_unit if the chip is SD8688 or newer.
> -	 * SD8385 & SD8686 do not have rx_unit.
> -	 */
> -	if ((card->model != MODEL_8385)
> -			&& (card->model != MODEL_8686))
> -		card->rx_unit = if_sdio_read_rx_unit(card);
> -	else
> -		card->rx_unit = 0;
> -
> -	/*
> -	 * Set up the interrupt handler late.
> -	 *
> -	 * If we set it up earlier, the (buggy) hardware generates a spurious
> -	 * interrupt, even before the interrupt has been enabled, with
> -	 * CCCR_INTx = 0.
> -	 *
> -	 * We register the interrupt handler late so that we can handle any
> -	 * spurious interrupts, and also to avoid generation of that known
> -	 * spurious interrupt in the first place.
> -	 */
> -	ret = sdio_claim_irq(func, if_sdio_interrupt);
> -	if (ret)
> -		goto disable;
> -
> -	/*
> -	 * Enable interrupts now that everything is set up
> -	 */
> -	sdio_writeb(func, 0x0f, IF_SDIO_H_INT_MASK, &ret);
> -	sdio_release_host(func);
> +	ret = if_sdio_power_on(card);

Why do you power on card here? Maybe it's possible do it in lbs_start_card?

>  	if (ret)
> -		goto reclaim;
> -
> -	priv->fw_ready = 1;
> -
> -	/*
> -	 * FUNC_INIT is required for SD8688 WLAN/BT multiple functions
> -	 */
> -	if (card->model == MODEL_8688) {
> -		struct cmd_header cmd;
> -
> -		memset(&cmd, 0, sizeof(cmd));
> -
> -		lbs_deb_sdio("send function INIT command\n");
> -		if (__lbs_cmd(priv, CMD_FUNC_INIT, &cmd, sizeof(cmd),
> -				lbs_cmd_copyback, (unsigned long) &cmd))
> -			netdev_alert(priv->dev, "CMD_FUNC_INIT cmd failed\n");
> -	}
> +		goto err_activate_card;
> 
>  	ret = lbs_start_card(priv);
> +	if_sdio_power_off(card);

Same for power off.

>  	if (ret)
>  		goto err_activate_card;
> 
> +	/* Tell PM core that we don't need the card to be powered now */
> +	pm_runtime_put_noidle(&func->dev);
> +
>  out:
>  	lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
> 
> @@ -1161,14 +1236,6 @@ out:
>  err_activate_card:
>  	flush_workqueue(card->workqueue);
>  	lbs_remove_card(priv);
> -reclaim:
> -	sdio_claim_host(func);
> -release_int:
> -	sdio_release_irq(func);
> -disable:
> -	sdio_disable_func(func);
> -release:
> -	sdio_release_host(func);
>  free:
>  	destroy_workqueue(card->workqueue);
>  	while (card->packets) {
> @@ -1195,6 +1262,9 @@ static void if_sdio_remove(struct sdio_func *func)
> 
>  	card = sdio_get_drvdata(func);
> 
> +	/* Undo decrement done above in if_sdio_probe */
> +	pm_runtime_get_noresume(&func->dev);
> +
>  	if (user_rmmod && (card->model == MODEL_8688)) {
>  		/*
>  		 * FUNC_SHUTDOWN is required for SD8688 WLAN/BT
> @@ -1219,11 +1289,6 @@ static void if_sdio_remove(struct sdio_func *func)
>  	flush_workqueue(card->workqueue);
>  	destroy_workqueue(card->workqueue);
> 
> -	sdio_claim_host(func);
> -	sdio_release_irq(func);
> -	sdio_disable_func(func);
> -	sdio_release_host(func);
> -
>  	while (card->packets) {
>  		packet = card->packets;
>  		card->packets = card->packets->next;

Regards
Vasily


From anarsoul at gmail.com  Tue Aug  2 06:12:22 2011
From: anarsoul at gmail.com (Vasily Khoruzhick)
Date: Tue, 2 Aug 2011 13:12:22 +0300
Subject: [PATCH 1/2] libertas: disable functionality when interface is down
In-Reply-To: <201108021202.03890.anarsoul@gmail.com>
References: <20110801154313.8C9389D401F@zog.reactivated.net>
	<201108021202.03890.anarsoul@gmail.com>
Message-ID: <201108021312.23153.anarsoul@gmail.com>

On Tuesday 02 August 2011 12:02:03 Vasily Khoruzhick wrote:
> On Monday 01 August 2011 18:43:13 Daniel Drake wrote:
> > Modify the driver so that it does not function when the interface is
> > down, in preparation for runtime power management.
> > 
> > No commands can be run while the interface is down, so the ndo_dev_stop
> > routine now directly does all necessary work (including asking the device
> > to disconnect from the network and disabling multicast functionality)
> > directly.
> > 
> > power_save and power_restore hooks are added meaning that card drivers
> > can take steps to turn the device off when the interface is down.
> > 
> > The MAC address can now only be changed when all interfaces are down;
> > the new address will be programmed when an interface gets brought up.
> > This matches mac80211 behaviour.
> > 
> > Also, some small cleanups/simplifications were made in the surrounding
> > device handling logic.
> 
> Ok, one more question: what about suspend? Maybe it worth to add flag to
> disable card before going suspend and re-enable it during resume?

Implemented it. The only remaining question is messing with power_{on,off} 
functions in if_sdio, but I'm OK with it. Will send my patches in few minutes.

Regards
Vasily


From anarsoul at gmail.com  Tue Aug  2 06:27:11 2011
From: anarsoul at gmail.com (Vasily Khoruzhick)
Date: Tue,  2 Aug 2011 13:27:11 +0300
Subject: [PATCH 1/2] libertas: add ability to power off card on suspend
In-Reply-To: <201108021312.23153.anarsoul@gmail.com>
References: <201108021312.23153.anarsoul@gmail.com>
Message-ID: <1312280832-27675-1-git-send-email-anarsoul@gmail.com>

Could be usefull if it's not possible to keep power on the card
when host goes into suspend.

Signed-off-by: Vasily Khoruzhick <anarsoul at gmail.com>
---
 drivers/net/wireless/libertas/dev.h  |    3 ++-
 drivers/net/wireless/libertas/main.c |   22 +++++++++++++++-------
 2 files changed, 17 insertions(+), 8 deletions(-)

diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h
index f526633..563a8b0 100644
--- a/drivers/net/wireless/libertas/dev.h
+++ b/drivers/net/wireless/libertas/dev.h
@@ -86,9 +86,10 @@ struct lbs_private {
 	/* Hardware access */
 	void *card;
 	bool iface_running;
+	bool suspend_iface_status;
 	u8 fw_ready;
 	u8 surpriseremoved;
-	u8 setup_fw_on_resume;
+	u8 disable_on_suspend;
 	int (*hw_host_to_card) (struct lbs_private *priv, u8 type, u8 *payload, u16 nb);
 	void (*reset_card) (struct lbs_private *priv);
 	int (*power_save) (struct lbs_private *priv);
diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c
index 208e4d9..5522202 100644
--- a/drivers/net/wireless/libertas/main.c
+++ b/drivers/net/wireless/libertas/main.c
@@ -644,7 +644,7 @@ done:
 
 int lbs_suspend(struct lbs_private *priv)
 {
-	int ret;
+	int ret = 0;
 
 	lbs_deb_enter(LBS_DEB_FW);
 
@@ -658,7 +658,13 @@ int lbs_suspend(struct lbs_private *priv)
 		priv->deep_sleep_required = 1;
 	}
 
-	ret = lbs_set_host_sleep(priv, 1);
+	if (priv->disable_on_suspend) {
+		/* Disable card */
+		priv->suspend_iface_status = priv->iface_running;
+		if (priv->iface_running)
+			lbs_stop_iface(priv);
+	} else
+		ret = lbs_set_host_sleep(priv, 1);
 
 	netif_device_detach(priv->dev);
 	if (priv->mesh_dev)
@@ -671,11 +677,16 @@ EXPORT_SYMBOL_GPL(lbs_suspend);
 
 int lbs_resume(struct lbs_private *priv)
 {
-	int ret;
+	int ret = 0;
 
 	lbs_deb_enter(LBS_DEB_FW);
 
-	ret = lbs_set_host_sleep(priv, 0);
+	if (priv->disable_on_suspend) {
+		/* Enable card */
+		if (priv->suspend_iface_status)
+			lbs_start_iface(priv);
+	} else
+		ret = lbs_set_host_sleep(priv, 0);
 
 	netif_device_attach(priv->dev);
 	if (priv->mesh_dev)
@@ -689,9 +700,6 @@ int lbs_resume(struct lbs_private *priv)
 				   "deep sleep activation failed: %d\n", ret);
 	}
 
-	if (priv->setup_fw_on_resume)
-		ret = lbs_setup_firmware(priv);
-
 	lbs_deb_leave_args(LBS_DEB_FW, "ret %d", ret);
 	return ret;
 }
-- 
1.7.5.rc3



From anarsoul at gmail.com  Tue Aug  2 06:27:12 2011
From: anarsoul at gmail.com (Vasily Khoruzhick)
Date: Tue,  2 Aug 2011 13:27:12 +0300
Subject: [PATCH 2/2] libertas: implement if_spi runtime power management
In-Reply-To: <1312280832-27675-1-git-send-email-anarsoul@gmail.com>
References: <201108021312.23153.anarsoul@gmail.com>
	<1312280832-27675-1-git-send-email-anarsoul@gmail.com>
Message-ID: <1312280832-27675-2-git-send-email-anarsoul@gmail.com>

The SPI card is now fully powered down when the network
interface is brought down.

Signed-off-by: Vasily Khoruzhick <anarsoul at gmail.com>
---
 drivers/net/wireless/libertas/if_spi.c |   80 +++++++++++++++++--------------
 1 files changed, 44 insertions(+), 36 deletions(-)

diff --git a/drivers/net/wireless/libertas/if_spi.c b/drivers/net/wireless/libertas/if_spi.c
index 9a1468c..c07db7d 100644
--- a/drivers/net/wireless/libertas/if_spi.c
+++ b/drivers/net/wireless/libertas/if_spi.c
@@ -1113,21 +1113,39 @@ static void if_spi_resume_worker(struct work_struct *work)
 	card = container_of(work, struct if_spi_card, resume_work);
 
 	if (card->suspended) {
-		if (card->pdata->setup)
-			card->pdata->setup(card->spi);
-
-		/* Init card ... */
-		if_spi_init_card(card);
-
-		enable_irq(card->spi->irq);
-
-		/* And resume it ... */
 		lbs_resume(card->priv);
 
 		card->suspended = 0;
 	}
 }
 
+static int if_spi_power_save(struct lbs_private *priv)
+{
+	struct if_spi_card *card = priv->card;
+
+	flush_workqueue(card->workqueue);
+	disable_irq(card->spi->irq);
+	card->pdata->teardown(card->spi);
+	priv->fw_ready = 0;
+
+	return 0;
+}
+
+static int if_spi_power_restore(struct lbs_private *priv)
+{
+	struct if_spi_card *card = priv->card;
+	int ret;
+
+	card->pdata->setup(card->spi);
+	ret = if_spi_init_card(card);
+	if (ret)
+		return ret;
+	enable_irq(card->spi->irq);
+	priv->fw_ready = 1;
+
+	return 0;
+}
+
 static int __devinit if_spi_probe(struct spi_device *spi)
 {
 	struct if_spi_card *card;
@@ -1142,17 +1160,11 @@ static int __devinit if_spi_probe(struct spi_device *spi)
 		goto out;
 	}
 
-	if (pdata->setup) {
-		err = pdata->setup(spi);
-		if (err)
-			goto out;
-	}
-
 	/* Allocate card structure to represent this specific device */
 	card = kzalloc(sizeof(struct if_spi_card), GFP_KERNEL);
 	if (!card) {
 		err = -ENOMEM;
-		goto teardown;
+		goto out;
 	}
 	spi_set_drvdata(spi, card);
 	card->pdata = pdata;
@@ -1163,13 +1175,6 @@ static int __devinit if_spi_probe(struct spi_device *spi)
 	INIT_LIST_HEAD(&card->data_packet_list);
 	spin_lock_init(&card->buffer_lock);
 
-	/* Initialize the SPI Interface Unit */
-
-	/* Firmware load */
-	err = if_spi_init_card(card);
-	if (err)
-		goto free_card;
-
 	/*
 	 * Register our card with libertas.
 	 * This will call alloc_etherdev.
@@ -1180,13 +1185,16 @@ static int __devinit if_spi_probe(struct spi_device *spi)
 		goto free_card;
 	}
 	card->priv = priv;
-	priv->setup_fw_on_resume = 1;
 	priv->card = card;
 	priv->hw_host_to_card = if_spi_host_to_card;
+	if (pdata->setup && pdata->teardown) {
+		priv->power_save = if_spi_power_save;
+		priv->power_restore = if_spi_power_restore;
+	}
 	priv->enter_deep_sleep = NULL;
 	priv->exit_deep_sleep = NULL;
 	priv->reset_deep_sleep_wakeup = NULL;
-	priv->fw_ready = 1;
+	priv->disable_on_suspend = 1;
 
 	/* Initialize interrupt handling stuff. */
 	card->workqueue = create_workqueue("libertas_spi");
@@ -1200,6 +1208,13 @@ static int __devinit if_spi_probe(struct spi_device *spi)
 		goto terminate_workqueue;
 	}
 
+	/* Disable IRQ, hw is not ready yet */
+	disable_irq(spi->irq);
+
+	err = if_spi_power_restore(priv);
+	if (err)
+		goto release_irq;
+
 	/*
 	 * Start the card.
 	 * This will call register_netdev, and we'll start
@@ -1207,13 +1222,16 @@ static int __devinit if_spi_probe(struct spi_device *spi)
 	 */
 	err = lbs_start_card(priv);
 	if (err)
-		goto release_irq;
+		goto teardown;
+	if_spi_power_save(priv);
 
 	lbs_deb_spi("Finished initializing WLAN module.\n");
 
 	/* successful exit */
 	goto out;
 
+teardown:
+	if_spi_power_save(priv);
 release_irq:
 	free_irq(spi->irq, card);
 terminate_workqueue:
@@ -1222,9 +1240,6 @@ terminate_workqueue:
 	lbs_remove_card(priv); /* will call free_netdev */
 free_card:
 	free_if_spi_card(card);
-teardown:
-	if (pdata->teardown)
-		pdata->teardown(spi);
 out:
 	lbs_deb_leave_args(LBS_DEB_SPI, "err %d\n", err);
 	return err;
@@ -1247,8 +1262,6 @@ static int __devexit libertas_spi_remove(struct spi_device *spi)
 	cancel_work_sync(&card->packet_work);
 	flush_workqueue(card->workqueue);
 	destroy_workqueue(card->workqueue);
-	if (card->pdata->teardown)
-		card->pdata->teardown(spi);
 	free_if_spi_card(card);
 	lbs_deb_leave(LBS_DEB_SPI);
 	return 0;
@@ -1261,11 +1274,6 @@ static int if_spi_suspend(struct device *dev)
 
 	if (!card->suspended) {
 		lbs_suspend(card->priv);
-		flush_workqueue(card->workqueue);
-		disable_irq(spi->irq);
-
-		if (card->pdata->teardown)
-			card->pdata->teardown(spi);
 		card->suspended = 1;
 	}
 
-- 
1.7.5.rc3



From dsd at laptop.org  Tue Aug  2 06:54:07 2011
From: dsd at laptop.org (Daniel Drake)
Date: Tue, 2 Aug 2011 11:54:07 +0100
Subject: [PATCH 1/2] libertas: add ability to power off card on suspend
In-Reply-To: <1312280832-27675-1-git-send-email-anarsoul@gmail.com>
References: <201108021312.23153.anarsoul@gmail.com>
	<1312280832-27675-1-git-send-email-anarsoul@gmail.com>
Message-ID: <CAGq3pz4bQ_2eQbVye==3Xqq3gK4PV6+JJXoHX0TXz=Y7kWC0wQ@mail.gmail.com>

On 2 August 2011 11:27, Vasily Khoruzhick <anarsoul at gmail.com> wrote:
> Could be usefull if it's not possible to keep power on the card
> when host goes into suspend.

If it's not possible to keep power during suspend, then I don't see
why you need to explicitly turn it off on the way down.

If power can be maintained, this is going to confuse userspace. If the
network interface is up while you go into suspend, this code would
kick in and power down the device. Then, during resume, it would power
it up again. During this whole time, the network interface has
remained UP, so userspace isn't aware that anything has happened. But,
since the hardware has been powered off and on again, it has lost all
state (association, encryption keys, etc).

The way we handle this in libertas_sdio is that if we don't want the
card powered during suspend, we ask the MMC layer to remove the
device. This causes the remove function to be run, and the interface
disappears. During resume, it gets probed again from scratch, which
then prompts userspace into deciding which network to connect to,
reprogramming keys, etc.

Daniel


From dsd at laptop.org  Tue Aug  2 12:05:31 2011
From: dsd at laptop.org (Daniel Drake)
Date: Tue, 2 Aug 2011 17:05:31 +0100
Subject: [PATCH 2/2] libertas: implement if_sdio runtime power management
In-Reply-To: <201108021204.08632.anarsoul@gmail.com>
References: <20110801154327.D0BB09D4020@zog.reactivated.net>
	<201108021204.08632.anarsoul@gmail.com>
Message-ID: <CAGq3pz7FCfHhdV2XY3LvZdWE6u4vPPV3HOa82SwqTY6NMHKmeA@mail.gmail.com>

On 2 August 2011 10:04, Vasily Khoruzhick <anarsoul at gmail.com> wrote:
>> + ? ? ret = if_sdio_power_on(card);
>
> Why do you power on card here? Maybe it's possible do it in lbs_start_card?

The device must be powered while lbs_start_card() is executed. This is
because lbs_start_card() runs a few commands, such as reading the MAC
address.

Moving the poweron/poweroff into lbs_start card() is possible, and I
guess you are also finding for SPI that the only thing you need to
power up the card for during probe is to execute lbs_start_card().

But personally I think it makes more sense at the interface driver
level. Moving it into lbs_start_card() would be problematic if you
ever needed the device powered for something else during the probe
routine - you'd be forced to power-cycle the card again before or
after running lbs_start_card().

>> + ? ? if_sdio_power_off(card);
>
> Same for power off.

The card should be powered off at this point because the network
interface is not up. It will then be powered on when userspace brings
it up.

Daniel


From anarsoul at gmail.com  Tue Aug  2 16:09:07 2011
From: anarsoul at gmail.com (Vasily Khoruzhick)
Date: Tue, 2 Aug 2011 23:09:07 +0300
Subject: [PATCH 1/2] libertas: add ability to power off card on suspend
In-Reply-To: <CAGq3pz4bQ_2eQbVye==3Xqq3gK4PV6+JJXoHX0TXz=Y7kWC0wQ@mail.gmail.com>
References: <201108021312.23153.anarsoul@gmail.com>
	<1312280832-27675-1-git-send-email-anarsoul@gmail.com>
	<CAGq3pz4bQ_2eQbVye==3Xqq3gK4PV6+JJXoHX0TXz=Y7kWC0wQ@mail.gmail.com>
Message-ID: <201108022309.07783.anarsoul@gmail.com>

On Tuesday 02 August 2011 13:54:07 Daniel Drake wrote:
> On 2 August 2011 11:27, Vasily Khoruzhick <anarsoul at gmail.com> wrote:
> > Could be usefull if it's not possible to keep power on the card
> > when host goes into suspend.
> 
> If it's not possible to keep power during suspend, then I don't see
> why you need to explicitly turn it off on the way down.

Because no one puts interface down before suspend?
I just re-tested - if interface is up - kernel does not put it down before 
suspend, so I got a lot of timeouts and errors on dmesg after resume.

Regards
Vasily


From dsd at laptop.org  Tue Aug  2 16:32:27 2011
From: dsd at laptop.org (Daniel Drake)
Date: Tue, 2 Aug 2011 21:32:27 +0100
Subject: [PATCH 1/2] libertas: add ability to power off card on suspend
In-Reply-To: <201108022309.07783.anarsoul@gmail.com>
References: <201108021312.23153.anarsoul@gmail.com>
	<1312280832-27675-1-git-send-email-anarsoul@gmail.com>
	<CAGq3pz4bQ_2eQbVye==3Xqq3gK4PV6+JJXoHX0TXz=Y7kWC0wQ@mail.gmail.com>
	<201108022309.07783.anarsoul@gmail.com>
Message-ID: <CAGq3pz6R=8eRNRME-X9e-S12cWvRq_-xvcjCa7KXyRfE0sJ8gw@mail.gmail.com>

On 2 August 2011 21:09, Vasily Khoruzhick <anarsoul at gmail.com> wrote:
> Because no one puts interface down before suspend?
> I just re-tested - if interface is up - kernel does not put it down before
> suspend, so I got a lot of timeouts and errors on dmesg after resume.

I can't see how this would have been influenced by my work.

Your resume handler should already be fixing up the hardware state so
that it can be operated (regardless of any power saving that might
come into effect), or otherwise your suspend handler should cause the
device to be removed so that it can be reprobed completely during
resume.

Daniel


From anarsoul at gmail.com  Tue Aug  2 17:12:19 2011
From: anarsoul at gmail.com (Vasily Khoruzhick)
Date: Wed, 3 Aug 2011 00:12:19 +0300
Subject: [PATCH 1/2] libertas: add ability to power off card on suspend
In-Reply-To: <CAGq3pz6R=8eRNRME-X9e-S12cWvRq_-xvcjCa7KXyRfE0sJ8gw@mail.gmail.com>
References: <201108021312.23153.anarsoul@gmail.com>
	<201108022309.07783.anarsoul@gmail.com>
	<CAGq3pz6R=8eRNRME-X9e-S12cWvRq_-xvcjCa7KXyRfE0sJ8gw@mail.gmail.com>
Message-ID: <201108030012.20241.anarsoul@gmail.com>

On Tuesday 02 August 2011 23:32:27 Daniel Drake wrote:
> On 2 August 2011 21:09, Vasily Khoruzhick <anarsoul at gmail.com> wrote:
> > Because no one puts interface down before suspend?
> > I just re-tested - if interface is up - kernel does not put it down
> > before suspend, so I got a lot of timeouts and errors on dmesg after
> > resume.
> 
> I can't see how this would have been influenced by my work.

Now generic libertas driver controls power, not if_spi. So it should be able 
to turn off card before going suspend. Messing with power in if_* and generic 
part does not look like a good idea to me.

> Your resume handler should already be fixing up the hardware state so
> that it can be operated (regardless of any power saving that might
> come into effect), or otherwise your suspend handler should cause the
> device to be removed so that it can be reprobed completely during
> resume.

My resume handler calls lbs_suspend to detach device, and it expects that 
lbs_suspend will disable card power.
 
> Daniel

Regards
Vasily


From dsd at laptop.org  Tue Aug  2 18:32:21 2011
From: dsd at laptop.org (Daniel Drake)
Date: Tue, 2 Aug 2011 23:32:21 +0100
Subject: [PATCH 1/2] libertas: add ability to power off card on suspend
In-Reply-To: <201108030012.20241.anarsoul@gmail.com>
References: <201108021312.23153.anarsoul@gmail.com>
	<201108022309.07783.anarsoul@gmail.com>
	<CAGq3pz6R=8eRNRME-X9e-S12cWvRq_-xvcjCa7KXyRfE0sJ8gw@mail.gmail.com>
	<201108030012.20241.anarsoul@gmail.com>
Message-ID: <CAGq3pz7gzgaZU2TfVHy5Q6AdKuob2zavPosgXTJtkovtmWpuow@mail.gmail.com>

On 2 August 2011 22:12, Vasily Khoruzhick <anarsoul at gmail.com> wrote:
> Now generic libertas driver controls power, not if_spi. So it should be able
> to turn off card before going suspend. Messing with power in if_* and generic
> part does not look like a good idea to me.

But there's no way your approach will bring good results. Userspace
won't see any interface state change, but the entire hardware and
firmware will be fully reset. Things will get very confused.

A lot of the stuff done in stop_iface is not relevant for the suspend
case. e.g. If commands were queued before suspend, they should execute
after resume. stop_iface is designed to run when the userspace-visible
interfaces are brought down - by running it while they are still up,
you will confuse things.

You are battling with a general suspend/resume problem which should be
handled by suspend/resume handlers alone. libertas already has plenty
of experience in this area which you can build upon.

Daniel


From dilinger at queued.net  Wed Aug  3 02:37:04 2011
From: dilinger at queued.net (Andres Salomon)
Date: Tue, 2 Aug 2011 23:37:04 -0700
Subject: [PATCH] libertas: prioritize usb8388_v5.bin firmware on OLPC
	machines
In-Reply-To: <1312233609.3299.24.camel@dcbw.foobar.com>
References: <20110731000300.5c08e95f@debxo> <20110731000521.45e48638@debxo>
	<CAGq3pz75qVMuR_Hh3GKg8xK7T+_ZAW5gBZns6D6PfDYh4CF5GA@mail.gmail.com>
	<1312219075.6121.20.camel@dcbw.foobar.com>
	<20110801112522.1ba02f5e@debxo>
	<1312233609.3299.24.camel@dcbw.foobar.com>
Message-ID: <20110802233704.0e0d93fa@debxo>

On Mon, 01 Aug 2011 16:20:07 -0500
Dan Williams <dcbw at redhat.com> wrote:

> On Mon, 2011-08-01 at 11:25 -0700, Andres Salomon wrote:
> > On Mon, 01 Aug 2011 12:17:23 -0500
> > Dan Williams <dcbw at redhat.com> wrote:
> > 
> > > On Sun, 2011-07-31 at 13:59 +0100, Daniel Drake wrote:
> > > > On 31 July 2011 08:05, Andres Salomon <dilinger at queued.net>
> > > > wrote:
> > > > > Note: I've not tested this out yet on an XO-1.  I'll do that
> > > > > after some feedback regarding the matching linux-firmware pull
> > > > > request.  This should address dsd's concern about changing the
> > > > > loading order of the firmware.
> > > > 
> > > > That indeed was a concern, but only until Dan Williams and Chris
> > > > pointed out that OLPC is the only usb8388 user. With that in
> > > > mind, I am in agreement with them that simply putting
> > > > usb8388_olpc_v5.bin (or whatever name is chosen) at the top of
> > > > the firmware loading list is the best option.
> > > 
> > > Yeah, patches for that would be immediately acked.
> > > 
> > 
> > What's your opinion on overwriting usb8388_v5.bin with OLPC's
> > version, versus having both a usb8388_v5.bin and a
> > usb8388_olpc_v5.bin?
> 
> I'd slightly prefer to keep them separate, but I don't really care
> that much.  Whatever you'd like to do here is fine.
> 

Alright, since people seem to prefer keeping it around, I'll redo the
pull request (and libertas patch) to do that.




From bzhao at marvell.com  Wed Aug  3 23:31:50 2011
From: bzhao at marvell.com (Bing Zhao)
Date: Wed, 3 Aug 2011 20:31:50 -0700
Subject: [PATCH 1/4] libertas: remove auto_deep_sleep functionality
In-Reply-To: <1311177831.21004.0.camel@dcbw.foobar.com>
References: <20110717170310.CAD139D401C@zog.reactivated.net>
	<477F20668A386D41ADCC57781B1F704307FFB0947D@SC-VEXCH1.marvell.com>
	<CAGq3pz6DbsHG9JY8oZqaeRp4=EC1-sKunSuF2Q1+tfBoYbcyLg@mail.gmail.com>
	<477F20668A386D41ADCC57781B1F704307FFBB244E@SC-VEXCH1.marvell.com>
	<CAGq3pz4cTSZj+_MwJrxG=Ri1zELKbx2i9Hz==3_=t+aho_Y+Xg@mail.gmail.com>
	<477F20668A386D41ADCC57781B1F704307FFBB25D6@SC-VEXCH1.marvell.com>
	<1311177831.21004.0.camel@dcbw.foobar.com>
Message-ID: <477F20668A386D41ADCC57781B1F704307FFD55062@SC-VEXCH1.marvell.com>

> > Anyway, for users who need this feature we can add a module parameter to enable it with the patch
> below. Please let me know what you think.
> 
> I'd rather we figure out some nl80211/cfg80211 knob for this rather than
> a module parameter; we can certainly add calls and attributes to nl80211
> if we need to.
> 
> Dan

Hi Dan,

In "struct cfg80211_ops" (cfg80211.h) a "set_power_mgmt" handler is defined for IEEE Power Save.

---
 * @set_power_mgmt: Configure WLAN power management. A timeout value of -1
 *      allows the driver to adjust the dynamic ps timeout value.

        int     (*set_power_mgmt)(struct wiphy *wiphy, struct net_device *dev,
                                  bool enabled, int timeout);
---

And the corresponding iw command is "set power_save on|off".

Currently 'ath6kl', 'iwmc3200wifi', 'rndis_wlan', and 'mwifiex' drivers have implemented the set_power_mgmt callback function for power saving handling.

Do you think if we should add new ops for auto deep sleep?

Thanks,
Bing


From david.abd at gmail.com  Thu Aug  4 02:43:25 2011
From: david.abd at gmail.com (David Abdurachmanov)
Date: Thu, 4 Aug 2011 09:43:25 +0300
Subject: Significant difference between 2.6.34 and 2.6.39?
Message-ID: <D856EA6F-78AB-4BF1-ADA5-1438C16B8512@gmail.com>

Hi,

I am using Gumstix Overo Fire COM, which has Wi2Wi chip (based on Marvell's 88W8686). The Angstrom Linux distribution build uses *libertas* driver for WiFi. I am trying to create WEP encrypted and restricted Ad-hoc (IBSS) network.

This sequence of commands are used for testing:
ip link set wlan0 down
iwconfig wlan0 mode ad-hoc
iwconfig wlan0 channel 4
iwconfig wlan0 essid 'TestMeNow'
iwconfig wlan0 key restricted 0123456789
ip link set wlan0 up

*NOTE:* With iw utility I don't see how to make restricted network. Any hints?

2.6.34 output:
---
wlan0     IEEE 802.11b/g  ESSID:"TestMeNow"  
         Mode:Ad-Hoc  Frequency:2.427 GHz  Cell: 02:26:F6:97:61:87   
         Bit Rate:0 kb/s   Tx-Power=13 dBm   
         Retry short limit:8   RTS thr=2347 B   Fragment thr=2346 B   
         Encryption key:0123-4567-89   Security mode:restricted
         Power Management:off
         Link Quality=0/100  Signal level=-94 dBm  Noise level=-94 dBm
         Rx invalid nwid:0  Rx invalid crypt:0  Rx invalid frag:0
         Tx excessive retries:0  Invalid misc:0   Missed beacon:0

MacBook WiFi scanning tool:
---
Enc/Cipher:	WEP
Encrypted:	Yes

2.6.39 (same results with 3.0)
---
wlan0     IEEE 802.11bg  ESSID:"TestMeNow"  
         Mode:Ad-Hoc  Frequency:2.427 GHz  Cell: 02:2D:5D:76:F8:0A   
         Retry  long limit:7   RTS thr:off   Fragment thr:off
         Encryption key:0123-4567-89
         Power Management:on

Scanning tool:
---
Enc/Cipher:	None
Encrypted:	No

What you get on .39 kernel is open network without WEP encryption. Were there any significant changes in the kernel between .34 and .39 that affects wireless networks or at least libertas driver?

Thanks in advance, 
David Abdurachmanov

From dilinger at queued.net  Thu Aug  4 18:47:22 2011
From: dilinger at queued.net (Andres Salomon)
Date: Thu, 4 Aug 2011 15:47:22 -0700
Subject: [GIT PULL linux-firmware.git v3] OLPC libertas firmware blobs
Message-ID: <20110804154722.55ab153b@debxo>

The following changes since commit 13f5008494268031d569f830d0955479bd5b9dfc:

  Merge git://anongit.freedesktop.org/~dcbw/linux-firmware (2011-07-31 00:15:37 +0100)

are available in the git repository at:

  git://lunge.queued.net/git/linux-firmware master

Andres Salomon (1):
      libertas: add OLPC libertas blobs

 LICENCE.OLPC              |   33 +++++++++++++++++++++++++++++++++
 WHENCE                    |   17 +++++++++++++++++
 libertas/lbtf_sdio.bin    |  Bin 0 -> 120924 bytes
 libertas/lbtf_usb.bin     |  Bin 0 -> 118888 bytes
 libertas/usb8388_olpc.bin |  Bin 0 -> 127824 bytes
 5 files changed, 50 insertions(+), 0 deletions(-)
 create mode 100644 LICENCE.OLPC
 create mode 100644 libertas/lbtf_sdio.bin
 create mode 100644 libertas/lbtf_usb.bin
 create mode 100644 libertas/usb8388_olpc.bin


From dilinger at queued.net  Thu Aug  4 19:06:52 2011
From: dilinger at queued.net (Andres Salomon)
Date: Thu, 4 Aug 2011 16:06:52 -0700
Subject: [PATCH v2] libertas: prioritize usb8388_olpc.bin firmware on OLPC
	machines
Message-ID: <20110804160652.2c87efef@debxo>

Normally, the v9 firmware will be loaded if it's available.  However, on
OLPC XO-1 machines, the olpc-specific firmware supports extra functionality.
This makes the libertas driver attempt to load the custom firmware first
if the machine is an OLPC machine; if that fails (or it's not an OLPC
machine), fall back to attempting to load the other firmwares.

Signed-off-by: Andres Salomon <dilinger at queued.net>
---
 drivers/net/wireless/libertas/if_usb.c |   21 +++++++++++++++++++++
 1 files changed, 21 insertions(+), 0 deletions(-)

diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/libertas/if_usb.c
index b5acc39..527cb11 100644
--- a/drivers/net/wireless/libertas/if_usb.c
+++ b/drivers/net/wireless/libertas/if_usb.c
@@ -973,6 +973,23 @@ static const struct {
 	{ MODEL_8682, "libertas/usb8682.bin" }
 };
 
+#ifdef CONFIG_OLPC
+
+static int try_olpc_fw(struct if_usb_card *cardp)
+{
+	int retval = -ENOENT;
+
+	/* try the OLPC firmware first; fall back to fw_table list */
+	if (machine_is_olpc() && cardp->model == MODEL_8388)
+		retval = request_firmware(&cardp->fw, "usb8388_olpc.bin",
+				&cardp->udev->dev);
+	return retval;
+}
+
+#else
+static int try_olpc_fw(struct if_usb_card *cardp) { return -ENOENT; }
+#endif /* !CONFIG_OLPC */
+
 static int get_fw(struct if_usb_card *cardp, const char *fwname)
 {
 	int i;
@@ -981,6 +998,10 @@ static int get_fw(struct if_usb_card *cardp, const char *fwname)
 	if (fwname)
 		return request_firmware(&cardp->fw, fwname, &cardp->udev->dev);
 
+	/* Handle OLPC firmware */
+	if (try_olpc_fw(cardp) == 0)
+		return 0;
+
 	/* Otherwise search for firmware to use */
 	for (i = 0; i < ARRAY_SIZE(fw_table); i++) {
 		if (fw_table[i].model != cardp->model)
-- 
1.7.2.5



From chaenel at free.fr  Fri Aug  5 06:02:00 2011
From: chaenel at free.fr (Cyril HAENEL)
Date: Fri, 05 Aug 2011 12:02:00 +0200
Subject: Wep problem
Message-ID: <4E3BBF98.8010508@free.fr>

Hello all,

I use a marvell 8385 based CF card on a blackfin processor board 
(bf537), and I found a strange problem. For now I am not able to 
determine if the problem comes from libertas driver or not.
I fact, when the wifi is configured without WEP encryption, everything 
works very well, the transfer rate is very good, more than 1200KB/s. But 
when the wifi is configured with a WEP encryption key, the transfer rate 
fall decrease completely when 2 processes use the network.
But when there is only one process which use the network the transfer 
rate is normal !!

For example :

curl -O http://192.168.1.100/file.bin  --> transfer rate more than 
1200KB/s on the complete 10MB file size.

And if there is at least another process using network, transfer rate 
fall completely :

curl -O http://192.168.1.100/file.bin & ping 192.168.1.100  --> transfer 
rate fall to <10Ko/s, and ping delay increase up to 1700ms !!!!!!!

It's really an annoying problem, and for now I just want to determine if 
the problem comes from the libertas driver or if the problem is 
elsewhere. Is someone knows where the WEP encryption is done ? In the 
libertas driver or in the kernel ?

Regards,
Cyril HAENEL


-- 

Cyril Haenel
Registered Linux User #332632



From error27 at gmail.com  Sat Aug  6 10:23:44 2011
From: error27 at gmail.com (Dan Carpenter)
Date: Sat, 6 Aug 2011 07:23:44 -0700
Subject: [patch] libertas: remove some dead code in
	if_spi_prog_helper_firmware()
Message-ID: <20110806142344.GA10373@mwanda>

We always hit the goto and skip the printk().  The original code does
the right thing even though it looks messy.

Signed-off-by: Dan Carpenter <error27 at gmail.com>

diff --git a/drivers/net/wireless/libertas/if_spi.c b/drivers/net/wireless/libertas/if_spi.c
index e0286cf..622ae6d 100644
--- a/drivers/net/wireless/libertas/if_spi.c
+++ b/drivers/net/wireless/libertas/if_spi.c
@@ -531,10 +531,6 @@ static int if_spi_prog_helper_firmware(struct if_spi_card *card,
 		goto out;
 	err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG,
 				IF_SPI_CIC_CMD_DOWNLOAD_OVER);
-		goto out;
-
-	lbs_deb_spi("waiting for helper to boot...\n");
-
 out:
 	if (err)
 		pr_err("failed to load helper firmware (err=%d)\n", err);


From error27 at gmail.com  Mon Aug 15 07:25:35 2011
From: error27 at gmail.com (Dan Carpenter)
Date: Mon, 15 Aug 2011 14:25:35 +0300
Subject: [patch -next] libertas: handle mesh networks in lbs_iface_active()
Message-ID: <20110815112535.GC10560@shale.localdomain>

There was an extra semicolon so the if condition wasn't used.  We
checked "priv->dev" twice instead of "priv->mesh_dev".

Signed-off-by: Dan Carpenter <error27 at gmail.com>
---
Compile tested only.

diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h
index 8148389..b9ff0dc 100644
--- a/drivers/net/wireless/libertas/dev.h
+++ b/drivers/net/wireless/libertas/dev.h
@@ -190,8 +190,8 @@ static inline int lbs_iface_active(struct lbs_private *priv)
 	int r;
 
 	r = netif_running(priv->dev);
-	if (priv->mesh_dev);
-		r |= netif_running(priv->dev);
+	if (priv->mesh_dev)
+		r |= netif_running(priv->mesh_dev);
 
 	return r;
 }


From dilinger at queued.net  Mon Aug 15 15:20:51 2011
From: dilinger at queued.net (Andres Salomon)
Date: Mon, 15 Aug 2011 12:20:51 -0700
Subject: [GIT PULL linux-firmware.git v3] OLPC libertas firmware blobs
In-Reply-To: <20110804154722.55ab153b@debxo>
References: <20110804154722.55ab153b@debxo>
Message-ID: <20110815122051.73bd4bd0@debxo>

Sounds like no one has any issues with this pull (and related libertas
patch)?  If that's the case.. Dave, mind doing that pull?  Sorry for
the multiple pull requests.


On Thu, 4
Aug 2011 15:47:22 -0700 Andres Salomon <dilinger at queued.net> wrote:

> The following changes since commit
> 13f5008494268031d569f830d0955479bd5b9dfc:
> 
>   Merge git://anongit.freedesktop.org/~dcbw/linux-firmware
> (2011-07-31 00:15:37 +0100)
> 
> are available in the git repository at:
> 
>   git://lunge.queued.net/git/linux-firmware master
> 
> Andres Salomon (1):
>       libertas: add OLPC libertas blobs
> 
>  LICENCE.OLPC              |   33 +++++++++++++++++++++++++++++++++
>  WHENCE                    |   17 +++++++++++++++++
>  libertas/lbtf_sdio.bin    |  Bin 0 -> 120924 bytes
>  libertas/lbtf_usb.bin     |  Bin 0 -> 118888 bytes
>  libertas/usb8388_olpc.bin |  Bin 0 -> 127824 bytes
>  5 files changed, 50 insertions(+), 0 deletions(-)
>  create mode 100644 LICENCE.OLPC
>  create mode 100644 libertas/lbtf_sdio.bin
>  create mode 100644 libertas/lbtf_usb.bin
>  create mode 100644 libertas/usb8388_olpc.bin



From zamek42 at gmail.com  Mon Aug  1 08:11:45 2011
From: zamek42 at gmail.com (zamek42)
Date: Mon, 01 Aug 2011 14:11:45 +0200
Subject: libertas-uap on imx25 cannot receive [solved]
Message-ID: <4E369801.5040707@gmail.com>

Hi All,

I found the problem. The driver didn't set the MediaConnected variable 
in priv structure. Normally it sets the MICRO_AP_EV_BSS_ACTIVE event or 
after open it checks.
The kernel cannot send any of packet to this device, because it was 
"unusable".

First I made a little workaround, when a packet is received, I set this 
variable directly, because if it could receive a packet, it means media 
is connected. And I think so it is a firmware error, and I try to change 
to another firmware, it  becomes good to work.

-- 
thx
Zoltan (Zamek) Zidarics
programmer
email:zamek42 at gmail.com
skype:zamek_z



From linux_turbowing at 163.com  Thu Aug  4 10:52:55 2011
From: linux_turbowing at 163.com (Brian.Wang)
Date: Thu, 4 Aug 2011 22:52:55 +0800 (CST)
Subject: HELP: libertas: Can't read bus mode register.    libertas_spi:
	probe of spi1.0 failed with error -5
Message-ID: <37f59c7d.193a2.1319548676a.Coremail.linux_turbowing@163.com>

Hi, all
Now I am going to port libertas driver into my mainboard, I suffered some difficuties.
Mainborad platform is Mini2440 using samsung S3C2440. Wireless unit consists of  marvell 88W8686 connected with S3C2440 by GSPI interface. Linux kernel version is linux-2.6.32.2.
 I use GPG5_6_7 as SPIMISO1, SPIMOSI1,SPICLK1 and GPG3 as nSS1.
The problem is that when I insmod libertas.ko and insmod libertas_spi.ko, print message is following:
libertas_spi: Libertas SPI driver
libertas: Can't read bus mode register.
libertas_spi: probe of spi1.0 failed with error -5


However, I short circuit SPIMISO1 and SPIMOSI1 then execute spi_test. I can read bus mode and receive data correctly.


What I do for driving 88W8686 is following:
/arch/arm/mach-s3c2440/mach-mini2440.c
/* SPI1 Marvell8686 */
static int libertas_spi_setup(struct spi_device *spi)
{
void __iomem *base_addr;
spi->bits_per_word = 16;
base_addr = ioremap(0x560000d0, 0x0c);
if(base_addr == NULL) {
printk(KERN_ERR "map error\n");
return -ENOMEM;
}
iowrite32(ioread32(base_addr)&(~(0x3<<4))|(0x1<<4), base_addr);//config GPJ2 output
iowrite32(ioread32(base_addr+0x4)|(0x1<<2), base_addr+0x4);// GPJ2 HIGH
iowrite32(ioread32(base_addr)&(~(0x3<<20))|(0x1<<20), base_addr);//config GPJ10 output
iowrite32(ioread32(base_addr+0x4)|(0x1<<10), base_addr+0x4);//GPJ10 HIGH
iowrite32(ioread32(base_addr)&(~(0x3<<22))|(0x1<<22), base_addr);//config GPJ11 output
iowrite32(ioread32(base_addr+0x4)&(~(0x1<<11)), base_addr+0x4);// GPJ11 LOW


s3c2410_gpio_cfgpin(S3C2410_GPG(12), S3C2410_GPIO_IRQ);
set_irq_type(IRQ_EINT20, IRQ_TYPE_EDGE_FALLING);
iounmap(base_addr);
return 0;
}


static int libertas_spi_teardown(struct spi_device *spi)
{
void __iomem *base_addr;
base_addr = ioremap(0x560000d0, 0x0c);
if(base_addr == NULL) {
printk(KERN_ERR "map error\n");
return -ENOMEM;
}
iowrite32(ioread32(base_addr)&(~(0x3<<4))|(0x1<<4), base_addr);//config GPJ2 output
iowrite32(ioread32(base_addr+0x4)&(~(0x1<<2)), base_addr+0x4);// GPJ2 LOW
iounmap(base_addr);
return 0;
}
static struct libertas_spi_platform_data libertas_spi_info = {
.use_dummy_writes = 0,
.setup = libertas_spi_setup,
.teardown = libertas_spi_teardown,
};
 
static struct spi_board_info libertas_spi_device[] __initdata = {
[0] = {
.modalias = "libertas_spi",
.platform_data = &libertas_spi_info,
.irq = IRQ_EINT20,
.max_speed_hz = 1*1000*1000,
.bus_num = 1,
.chip_select = 0,
.mode = SPI_MODE_0,
},
[1] = {
.modalias = "spidev",
.bus_num = 1,
.chip_select = 1,
.max_speed_hz = 500 * 1000,
},
};


/* init s3c2410_spi_info */
static struct s3c2410_spi_info mini2440_spi1_info = {
.pin_cs = S3C2410_GPG(3),
.num_cs = 2,//0--Marvell 8686    1--spidev
.bus_num = 1,
.gpio_setup = s3c24xx_spi_gpiocfg_bus1_gpg5_6_7,
};


static struct platform_device *mini2440_devices[] __initdata = {
&s3c_device_usb,
&s3c_device_rtc,
&s3c_device_lcd,
&s3c_device_wdt,
&s3c_device_i2c0,
&s3c_device_iis,
&mini2440_device_eth,
&s3c24xx_uda134x,
&s3c_device_nand,
&s3c_device_sdi,
&s3c_device_usbgadget,
//&s3c_device_spi0,
&s3c_device_spi1,
};


static void __init mini2440_map_io(void)
{
s3c24xx_init_io(mini2440_iodesc, ARRAY_SIZE(mini2440_iodesc));
s3c24xx_init_clocks(12000000);
s3c24xx_init_uarts(mini2440_uartcfgs, ARRAY_SIZE(mini2440_uartcfgs));
}


static void __init mini2440_machine_init(void)
{
#if defined (LCD_WIDTH)
s3c24xx_fb_set_platdata(&mini2440_fb_info);
#endif
s3c_i2c0_set_platdata(NULL);


s3c2410_gpio_cfgpin(S3C2410_GPC(0), S3C2410_GPC0_LEND);


s3c_device_nand.dev.platform_data = &friendly_arm_nand_info;
s3c_device_sdi.dev.platform_data = &mini2440_mmc_cfg;
s3c_device_spi1.dev.platform_data = &mini2440_spi1_info;
platform_add_devices(mini2440_devices, ARRAY_SIZE(mini2440_devices));
spi_register_board_info(libertas_spi_device, ARRAY_SIZE(libertas_spi_device));
s3c_pm_init();
}
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.infradead.org/pipermail/libertas-dev/attachments/20110804/dc1d9540/attachment.html>

From dcbw at redhat.com  Mon Aug 15 18:51:18 2011
From: dcbw at redhat.com (Dan Williams)
Date: Mon, 15 Aug 2011 17:51:18 -0500
Subject: [PATCH v2] libertas: prioritize usb8388_olpc.bin firmware on
	OLPC machines
In-Reply-To: <20110804160652.2c87efef@debxo>
References: <20110804160652.2c87efef@debxo>
Message-ID: <1313448679.6103.13.camel@dcbw.foobar.com>

On Thu, 2011-08-04 at 16:06 -0700, Andres Salomon wrote:
> Normally, the v9 firmware will be loaded if it's available.  However, on
> OLPC XO-1 machines, the olpc-specific firmware supports extra functionality.
> This makes the libertas driver attempt to load the custom firmware first
> if the machine is an OLPC machine; if that fails (or it's not an OLPC
> machine), fall back to attempting to load the other firmwares.
> 
> Signed-off-by: Andres Salomon <dilinger at queued.net>

Signed-off-by: Dan Williams <dcbw at redhat.com>

works for me...

> ---
>  drivers/net/wireless/libertas/if_usb.c |   21 +++++++++++++++++++++
>  1 files changed, 21 insertions(+), 0 deletions(-)
> 
> diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/libertas/if_usb.c
> index b5acc39..527cb11 100644
> --- a/drivers/net/wireless/libertas/if_usb.c
> +++ b/drivers/net/wireless/libertas/if_usb.c
> @@ -973,6 +973,23 @@ static const struct {
>  	{ MODEL_8682, "libertas/usb8682.bin" }
>  };
>  
> +#ifdef CONFIG_OLPC
> +
> +static int try_olpc_fw(struct if_usb_card *cardp)
> +{
> +	int retval = -ENOENT;
> +
> +	/* try the OLPC firmware first; fall back to fw_table list */
> +	if (machine_is_olpc() && cardp->model == MODEL_8388)
> +		retval = request_firmware(&cardp->fw, "usb8388_olpc.bin",
> +				&cardp->udev->dev);
> +	return retval;
> +}
> +
> +#else
> +static int try_olpc_fw(struct if_usb_card *cardp) { return -ENOENT; }
> +#endif /* !CONFIG_OLPC */
> +
>  static int get_fw(struct if_usb_card *cardp, const char *fwname)
>  {
>  	int i;
> @@ -981,6 +998,10 @@ static int get_fw(struct if_usb_card *cardp, const char *fwname)
>  	if (fwname)
>  		return request_firmware(&cardp->fw, fwname, &cardp->udev->dev);
>  
> +	/* Handle OLPC firmware */
> +	if (try_olpc_fw(cardp) == 0)
> +		return 0;
> +
>  	/* Otherwise search for firmware to use */
>  	for (i = 0; i < ARRAY_SIZE(fw_table); i++) {
>  		if (fw_table[i].model != cardp->model)




From dcbw at redhat.com  Mon Aug 15 18:51:54 2011
From: dcbw at redhat.com (Dan Williams)
Date: Mon, 15 Aug 2011 17:51:54 -0500
Subject: [patch] libertas: remove some dead code in
	if_spi_prog_helper_firmware()
In-Reply-To: <20110806142344.GA10373@mwanda>
References: <20110806142344.GA10373@mwanda>
Message-ID: <1313448716.6103.14.camel@dcbw.foobar.com>

On Sat, 2011-08-06 at 07:23 -0700, Dan Carpenter wrote:
> We always hit the goto and skip the printk().  The original code does
> the right thing even though it looks messy.
> 
> Signed-off-by: Dan Carpenter <error27 at gmail.com>

Acked-by: Dan Williams <dcbw at redhat.com>

> 
> diff --git a/drivers/net/wireless/libertas/if_spi.c b/drivers/net/wireless/libertas/if_spi.c
> index e0286cf..622ae6d 100644
> --- a/drivers/net/wireless/libertas/if_spi.c
> +++ b/drivers/net/wireless/libertas/if_spi.c
> @@ -531,10 +531,6 @@ static int if_spi_prog_helper_firmware(struct if_spi_card *card,
>  		goto out;
>  	err = spu_write_u16(card, IF_SPI_CARD_INT_CAUSE_REG,
>  				IF_SPI_CIC_CMD_DOWNLOAD_OVER);
> -		goto out;
> -
> -	lbs_deb_spi("waiting for helper to boot...\n");
> -
>  out:
>  	if (err)
>  		pr_err("failed to load helper firmware (err=%d)\n", err);




From dcbw at redhat.com  Mon Aug 15 18:52:29 2011
From: dcbw at redhat.com (Dan Williams)
Date: Mon, 15 Aug 2011 17:52:29 -0500
Subject: [patch -next] libertas: handle mesh networks in lbs_iface_active()
In-Reply-To: <20110815112535.GC10560@shale.localdomain>
References: <20110815112535.GC10560@shale.localdomain>
Message-ID: <1313448751.6103.15.camel@dcbw.foobar.com>

On Mon, 2011-08-15 at 14:25 +0300, Dan Carpenter wrote:
> There was an extra semicolon so the if condition wasn't used.  We
> checked "priv->dev" twice instead of "priv->mesh_dev".
> 
> Signed-off-by: Dan Carpenter <error27 at gmail.com>

Acked-by: Dan Williams <dcbw at redhat.com>

> ---
> Compile tested only.
> 
> diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h
> index 8148389..b9ff0dc 100644
> --- a/drivers/net/wireless/libertas/dev.h
> +++ b/drivers/net/wireless/libertas/dev.h
> @@ -190,8 +190,8 @@ static inline int lbs_iface_active(struct lbs_private *priv)
>  	int r;
>  
>  	r = netif_running(priv->dev);
> -	if (priv->mesh_dev);
> -		r |= netif_running(priv->dev);
> +	if (priv->mesh_dev)
> +		r |= netif_running(priv->mesh_dev);
>  
>  	return r;
>  }




From dcbw at redhat.com  Mon Aug 15 18:53:35 2011
From: dcbw at redhat.com (Dan Williams)
Date: Mon, 15 Aug 2011 17:53:35 -0500
Subject: Wep problem
In-Reply-To: <4E3BBF98.8010508@free.fr>
References: <4E3BBF98.8010508@free.fr>
Message-ID: <1313448815.6103.16.camel@dcbw.foobar.com>

On Fri, 2011-08-05 at 12:02 +0200, Cyril HAENEL wrote:
> Hello all,
> 
> I use a marvell 8385 based CF card on a blackfin processor board 
> (bf537), and I found a strange problem. For now I am not able to 
> determine if the problem comes from libertas driver or not.
> I fact, when the wifi is configured without WEP encryption, everything 
> works very well, the transfer rate is very good, more than 1200KB/s. But 
> when the wifi is configured with a WEP encryption key, the transfer rate 
> fall decrease completely when 2 processes use the network.
> But when there is only one process which use the network the transfer 
> rate is normal !!
> 
> For example :
> 
> curl -O http://192.168.1.100/file.bin  --> transfer rate more than 
> 1200KB/s on the complete 10MB file size.
> 
> And if there is at least another process using network, transfer rate 
> fall completely :
> 
> curl -O http://192.168.1.100/file.bin & ping 192.168.1.100  --> transfer 
> rate fall to <10Ko/s, and ping delay increase up to 1700ms !!!!!!!
> 
> It's really an annoying problem, and for now I just want to determine if 
> the problem comes from the libertas driver or if the problem is 
> elsewhere. Is someone knows where the WEP encryption is done ? In the 
> libertas driver or in the kernel ?

It's done in the card's firmware, not in the kernel or the driver.  The
firmware passes back a completely decrypted packet to the driver.

Dan




From srasmus2 at asu.edu  Wed Aug 17 18:03:22 2011
From: srasmus2 at asu.edu (Samuel Rasmussen)
Date: Wed, 17 Aug 2011 15:03:22 -0700
Subject: Deep Sleep Question
Message-ID: <CA+ZjjTd+P2QagQQOVCkihs218Nmc3vkE7DXLw6+fufk2ABCTuQ@mail.gmail.com>

I am currently working on a project that uses a Marvell 8686 chip and
Libertas driver to control WiFi (kernel 2.6.39).  My hardware is power
sensitive, and I need to turn deep sleep on.  The Libertas README file
states that I can use "iwconfig wlan0 power period 0" to enable deep sleep
mode.  In order for this command to work properly, I had to install a more
recent version of the Wireless Tools (v30 pre9).  However, I am still
getting an error:

 $ iwconfig wlan2 power period 0

Error for wireless request ?Set Power Management? (8B2C) :

      SET failed on device wlan2 ; Operation not supported.

I went through the mailing list archives and saw that you guys were saying
the iwconfig power commands would not work:


"Bing Zhao

*Tue Jul 19 22:33:23 EDT 2011*

Hi Daniel,

>* > To enable deep sleep mode:
*>* > iwconfig wlan0 power period 0
*>* >
*>* > To enable auto deep sleep mode with idle time period set to 5 seconds:
*>* > iwconfig wlan0 power period 5
*>* >
*>* > To disable deep sleep/auto deep sleep mode:
*>* > iwconfig wlan0 power period -1
*>*
*>* Paul pointed out that indeed, in old kernels, this functionality was
*>* hooked up to the power wireless extension.
*>*
*>* In recent kernels, this is not the case - its not hooked up to
*>* anything. The commands you write above will fail.
*
Yeah. I didn't know that the entire wext has been removed from libertas.

Anyway, for users who need this feature we can add a module parameter
to enable it with the patch below. Please let me know what you think.

Thanks,
Bing"


After discussing the situation, it sounded like you were looking into using
the cfg80211 API for power options.

What is the current method I can use to turn deep sleep on and off?

Thanks,
Samuel
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.infradead.org/pipermail/libertas-dev/attachments/20110817/9d241d20/attachment.html>

From bzhao at marvell.com  Mon Aug 22 14:49:04 2011
From: bzhao at marvell.com (Bing Zhao)
Date: Mon, 22 Aug 2011 11:49:04 -0700
Subject: Deep Sleep Question
In-Reply-To: <477F20668A386D41ADCC57781B1F70430802DD7B53@SC-VEXCH1.marvell.com>
References: <477F20668A386D41ADCC57781B1F70430802DD7B53@SC-VEXCH1.marvell.com>
Message-ID: <477F20668A386D41ADCC57781B1F70430802DD7B73@SC-VEXCH1.marvell.com>

Hi Samuel,

> Subject: Deep Sleep Question
> 
> I am currently working on a project that uses a Marvell 8686 chip and Libertas driver to control WiFi
> (kernel 2.6.39).? My hardware is power sensitive, and I need to turn deep sleep on.? The Libertas
> README file states that I can use "iwconfig wlan0 power period 0" to enable deep sleep mode.? In
> order for this command to work properly, I had to install a more recent version of the Wireless Tools
> (v30 pre9).? However, I am still getting an error:
> $ iwconfig wlan2 power period 0
> Error for wireless request ?Set Power Management? (8B2C) :
> ????? SET failed on device wlan2 ; Operation not supported.

Wext has been removed in recent kernel. So these commands do not work any more.
I'll send a patch to remove these commands from libertas README.

> 
> I went through the mailing list archives and saw that you guys were saying the iwconfig power
> commands would not work:
> 
> "Bing Zhao
> 
> Tue Jul 19 22:33:23 EDT 2011
> 
> Hi Daniel,
> 
> 
> > > To enable deep sleep mode:
> > > iwconfig wlan0 power period 0
> > >
> > > To enable auto deep sleep mode with idle time period set to 5 seconds:
> > > iwconfig wlan0 power period 5
> 
> > >
> > > To disable deep sleep/auto deep sleep mode:
> > > iwconfig wlan0 power period -1
> >
> > Paul pointed out that indeed, in old kernels, this functionality was
> 
> > hooked up to the power wireless extension.
> >
> > In recent kernels, this is not the case - its not hooked up to
> > anything. The commands you write above will fail.
> 
> 
> Yeah. I didn't know that the entire wext has been removed from libertas.
> 
> Anyway, for users who need this feature we can add a module parameter to enable it with the patch
> below. Please let me know what you think.
> 
> 
> Thanks,
> Bing"
> 
> After discussing the situation, it sounded like you were looking into using the cfg80211 API for
> power options.

"set_power_mgmt" API is currently supported but that is for IEEE Power Save configuration only.
For "auto deep sleep" support I can see two options here:
1) enhance the existing "set_power_mgmt" API so that it can support both IEEE Power Save and Auto Deep Sleep.
2) add a new API, e.g., set_power_auto_deep_sleep

> 
> What is the current method I can use to turn deep sleep on and off?

I had posted a patch that added a module parameter to enable/disable auto deep sleep.
For the time being you can apply that patch for your tests.

Thanks,
Bing

> 
> Thanks,
> Samuel


From bzhao at marvell.com  Mon Aug 22 20:26:37 2011
From: bzhao at marvell.com (Bing Zhao)
Date: Mon, 22 Aug 2011 17:26:37 -0700
Subject: [PATCH] libertas: update readme file
Message-ID: <1314059197-8377-1-git-send-email-bzhao@marvell.com>

From: Amitkumar Karwar <akarwar at marvell.com>

Since all wext specific code is removed, currently there is no
way to configure deep sleep mode. This patch removes deep sleep
configuration information in readme file.

Signed-off-by: Amitkumar Karwar <akarwar at marvell.com>
---
 drivers/net/wireless/libertas/README |   25 -------------------------
 1 files changed, 0 insertions(+), 25 deletions(-)

diff --git a/drivers/net/wireless/libertas/README b/drivers/net/wireless/libertas/README
index 1453eec..91f2ca9 100644
--- a/drivers/net/wireless/libertas/README
+++ b/drivers/net/wireless/libertas/README
@@ -238,28 +238,3 @@ hostsleep
 		echo "1" > hostsleep : enable host sleep.
 		echo "0" > hostsleep : disable host sleep
 
-========================
-IWCONFIG COMMANDS
-========================
-power period
-
-	This command is used to configure the station in deep sleep mode /
-	auto deep sleep mode.
-
-	The timer is implemented to monitor the activities (command, event,
-	etc.). When an activity is detected station will exit from deep
-	sleep mode automatically and restart the timer. At timer expiry
-	(no activity for defined time period) the deep sleep mode is entered
-	automatically.
-
-	Note: this command is for SDIO interface only.
-
-	Usage:
-	To enable deep sleep mode do:
-		iwconfig wlan0 power period 0
-	To enable auto deep sleep mode with idle time period 5 seconds do:
-		iwconfig wlan0 power period 5
-	To disable deep sleep/auto deep sleep mode do:
-		iwconfig wlan0 power period -1
-
-==============================================================================
-- 
1.5.3.4



From dcbw at redhat.com  Thu Aug 25 15:44:57 2011
From: dcbw at redhat.com (Dan Williams)
Date: Thu, 25 Aug 2011 14:44:57 -0500
Subject: [PATCH] libertas: update readme file
In-Reply-To: <1314059197-8377-1-git-send-email-bzhao@marvell.com>
References: <1314059197-8377-1-git-send-email-bzhao@marvell.com>
Message-ID: <1314301498.3922.10.camel@dcbw.foobar.com>

On Mon, 2011-08-22 at 17:26 -0700, Bing Zhao wrote:
> From: Amitkumar Karwar <akarwar at marvell.com>
> 
> Since all wext specific code is removed, currently there is no
> way to configure deep sleep mode. This patch removes deep sleep
> configuration information in readme file.
> 
> Signed-off-by: Amitkumar Karwar <akarwar at marvell.com>

Acked-by: Dan Williams <dcbw at redhat.com>

> ---
>  drivers/net/wireless/libertas/README |   25 -------------------------
>  1 files changed, 0 insertions(+), 25 deletions(-)
> 
> diff --git a/drivers/net/wireless/libertas/README b/drivers/net/wireless/libertas/README
> index 1453eec..91f2ca9 100644
> --- a/drivers/net/wireless/libertas/README
> +++ b/drivers/net/wireless/libertas/README
> @@ -238,28 +238,3 @@ hostsleep
>  		echo "1" > hostsleep : enable host sleep.
>  		echo "0" > hostsleep : disable host sleep
>  
> -========================
> -IWCONFIG COMMANDS
> -========================
> -power period
> -
> -	This command is used to configure the station in deep sleep mode /
> -	auto deep sleep mode.
> -
> -	The timer is implemented to monitor the activities (command, event,
> -	etc.). When an activity is detected station will exit from deep
> -	sleep mode automatically and restart the timer. At timer expiry
> -	(no activity for defined time period) the deep sleep mode is entered
> -	automatically.
> -
> -	Note: this command is for SDIO interface only.
> -
> -	Usage:
> -	To enable deep sleep mode do:
> -		iwconfig wlan0 power period 0
> -	To enable auto deep sleep mode with idle time period 5 seconds do:
> -		iwconfig wlan0 power period 5
> -	To disable deep sleep/auto deep sleep mode do:
> -		iwconfig wlan0 power period -1
> -
> -==============================================================================




From linville at tuxdriver.com  Tue Aug 30 15:14:37 2011
From: linville at tuxdriver.com (John W. Linville)
Date: Tue, 30 Aug 2011 15:14:37 -0400
Subject: [PATCH] cfg80211: hold reg_mutex when updating regulatory
In-Reply-To: <1312100661.687.8.camel@bender>
References: <1310456639-16468-1-git-send-email-s.neumann@raumfeld.com>
	<1310457159-16698-2-git-send-email-s.neumann@raumfeld.com>
	<20110715173240.GD2566@tuxdriver.com>
	<1311367076.27468.4.camel@bender>
	<CAB=NE6U+3VgAz19-r-oY=WCbvw=LtX5OZc-c3Nop3Zdm9m3fvQ@mail.gmail.com>
	<1312100661.687.8.camel@bender>
Message-ID: <20110830191437.GC2660@tuxdriver.com>

On Sun, Jul 31, 2011 at 10:24:21AM +0200, Sven Neumann wrote:
> On Mon, 2011-07-25 at 13:21 -0700, Luis R. Rodriguez wrote:
> > On Fri, Jul 22, 2011 at 1:37 PM, Sven Neumann <s.neumann at raumfeld.com> wrote:
> > > Hi,
> > >
> > > On Fri, 2011-07-15 at 13:32 -0400, John W. Linville wrote:
> > >> Luis, any comment on this?
> > >>
> > >> On Tue, Jul 12, 2011 at 09:52:39AM +0200, Sven Neumann wrote:
> > >> > The function wiphy_update_regulatory() uses the static variable
> > >> > last_request and thus needs to be called with reg_mutex held.
> > >> > This is the case for all users in reg.c, but the function was
> > >> > exported for use by wiphy_register(), from where it is called
> > >> > without the lock being held.
> > >> >
> > >> > Fix this by making wiphy_update_regulatory() private and introducing
> > >> > regulatory_update() as a wrapper that acquires and holds the lock.
> > >> >
> > >> > Signed-off-by: Sven Neumann <s.neumann at raumfeld.com>
> > >
> > > I'd appreciate if someone would review this patch. But probably this is
> > > not really an issue except that it's somewhat ugly to export a function
> > > that should be called with a lock held and that lock is actually
> > > private. But in this particular case it is not a problem, as far as I
> > > can see, since the only user of wiphy_update_regulatory() outside
> > > net/wireless/reg.c is initialization code. So there is not likely going
> > > to be a race condition here.
> > 
> > Apologies for the delay and thanks for the patch. The patch seems good
> > except for the fact that there are so many changes reflected on the
> > patch itself and this can be avoided by splitting the work into a few
> > more patches so that the actual code changes required are reflected
> > cleanly in one patch. Can you perhaps split up your changes so that
> > moves of code are just that and actual code changes are reflected
> > elsewhere?
> 
> Sure, I can do that. But I won't get to it before next week as I am
> currently on vacation.

Did you have a nice vacation?  Are you ready to work on Linux again? :-)

John
-- 
John W. Linville		Someday the world will need a hero, and you
linville at tuxdriver.com			might be all we have.  Be ready.


