From andrey at cozybit.com  Tue Sep  1 00:22:04 2009
From: andrey at cozybit.com (Andrey Yurovsky)
Date: Mon, 31 Aug 2009 21:22:04 -0700
Subject: power management on the 8686 chipset
In-Reply-To: <200908310954.23703.jeffs@fomsystems.com>
References: <200908251234.45160.jeffs@fomsystems.com>
	<477F20668A386D41ADCC57781B1F704306D2369F5E@SC-VEXCH1.marvell.com>
	<60ce8d250908261919w588fa991jaf3b3f10e6da379a@mail.gmail.com>
	<200908310954.23703.jeffs@fomsystems.com>
Message-ID: <45e8e6c40908312122r7a5eeee5v962d330e22117cf6@mail.gmail.com>

On Mon, Aug 31, 2009 at 6:54 AM, Jeff Sutherland<jeffs at fomsystems.com> wrote:
> I've backported the latest driver code from John's wireless-testing git repo
> to my 2.6.30 kernel, and whilst normal operation is fine, I still can't get
> the part into IEEE power saving mode using iwconfig or iwlist. ?What is the
> command to put the libertas 8686 chip (sdio interface) into power saving
> mode? ?Platform is PXA270 using the OpenEmbedded distro.

# iwconfig ethN power on

How are you verifying that IEEE PS mode is activated?

  -Andrey


From jeffs at fomsystems.com  Tue Sep  1 08:22:46 2009
From: jeffs at fomsystems.com (Jeff Sutherland)
Date: Tue, 1 Sep 2009 08:22:46 -0400
Subject: power management on the 8686 chipset
In-Reply-To: <45e8e6c40908312122r7a5eeee5v962d330e22117cf6@mail.gmail.com>
References: <200908251234.45160.jeffs@fomsystems.com>
	<200908310954.23703.jeffs@fomsystems.com>
	<45e8e6c40908312122r7a5eeee5v962d330e22117cf6@mail.gmail.com>
Message-ID: <200909010822.47997.jeffs@fomsystems.com>

On Tuesday 01 September 2009, Andrey Yurovsky wrote:
> On Mon, Aug 31, 2009 at 6:54 AM, Jeff Sutherland<jeffs at fomsystems.com> 
wrote:
> > I've backported the latest driver code from John's wireless-testing git
> > repo to my 2.6.30 kernel, and whilst normal operation is fine, I still
> > can't get the part into IEEE power saving mode using iwconfig or iwlist.
> > ?What is the command to put the libertas 8686 chip (sdio interface) into
> > power saving mode? ?Platform is PXA270 using the OpenEmbedded distro.
>
> # iwconfig ethN power on
>
> How are you verifying that IEEE PS mode is activated?
>
>   -Andrey

I don't know how many times in the past I tried that command and never noticed 
anything taking effect.  Ideed, 'iwconfig wlan0' would always report power 
management as being off even after running that command.  This morning I try 
it again and for the first time I'm seeing current reduction plus 'iwconfig 
wlan0' reports power management as on.  Must be some magic in the air today 
or something, as I thought I was running the latest libertas code from 
wireless-testing.  Anyway, seems to work now :-)

With the system at idle, no other power management in the kernel enabled, 
associated with an A/P, without power management enabled battery drain is a 
consistent 365mA.  Once power management is enabled, input current drops to 
about 191mA most of the time, which briefly jumps back up to 365mA every two 
seconds as the link sits idle with no traffic.  Current jumps around a little 
bit depending on network traffic.  A big rsync transfer shows little increase 
in average power (probably because it's waiting for the flash file system, 
the transfer rate isn't all that fast).  But bottom line is the power savings 
are considerable with little apparent effect on operations.

For all list members:
Another thing I'm concerned with is what's happening at the A/P end, as it has 
to keep track of which clients are in IEEE power saving mode and do some 
buffering of traffic for them.  I'm using a Linksys WRT54GS with the dd-wrt 
firmware, and for one client it works fine.  But I suspect that a little box 
like this could possibly get overwhelmed rather quickly if it had a lot of 
wireless clients in IEEE mode and a moderate amount of network traffic.  
Opinions? Experiences?

Regards,
-Jeff
-- 
FOM Systems Inc.   www.fomsystems.com
phone:  (USA) +1-330-628-2102 or 800-936-0561, (UK) +44-161-408-3072
mobile: (USA) +1-330-802-1364, (World) +44-7793-827386, Skype: Adekguru
=+=+=+=+=+=+=+=+=+=+=+=+=+=
And He saith unto them, "Follow me, and I will make you fishers of men."
(Matthew 4:19)


From pierre at ossman.eu  Sun Sep  6 09:32:44 2009
From: pierre at ossman.eu (Pierre Ossman)
Date: Sun, 6 Sep 2009 15:32:44 +0200
Subject: Suspend/resume without bringing down SDIO
In-Reply-To: <m3iqh4k2u0.fsf@pullcord.laptop.org>
References: <m3iqh4k2u0.fsf@pullcord.laptop.org>
Message-ID: <20090906153244.5b9ba70a@mjolnir.ossman.eu>

On Mon, 03 Aug 2009 17:24:55 -0400
Chris Ball <cjb at laptop.org> wrote:

> Hi Pierre, and libertas-dev,
> 
> We're using a Marvell SDIO 8686 wifi module in the new OLPC XO-1.5,
> and need the SDIO card to stay up across suspend so that it can wake
> us with an SDIO interrupt when a packet arrives while we're suspended.
> (We do this with the USB 8388 on the XO-1, too.)
> 
> I see that the SD stack doesn't yet have support for per-driver
> suspend/resume hooks, though.  We're using a standard sdhci host,
> which we keep powered during suspend.  Any suggestions on what we
> should work on, or pointers to patches that accomplish these if
> they exist already?  It sounds like we're looking for:
> 
> * a way to tell the core not to remove the card on the way into
>   suspend; it's always going to be present when we resume.

The missing key here really is that the MMC layer never grew the
functionality to keep cards powered during suspend. Once it has that it
can just poke the card after resume to see if it is still running, and
only do a reinit if that fails.

> * a way to register driver hooks to call suspend/resume functions
>   in the same way current libertas if_usb.c does.

That would be sensible once the above is fixed.

Rgds
-- 
     -- Pierre Ossman

  WARNING: This correspondence is being monitored by the
  Swedish government. Make sure your server uses encryption
  for SMTP traffic and consider using PGP for end-to-end
  encryption.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 198 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/libertas-dev/attachments/20090906/31590427/attachment.sig>

From lijinlei1 at 163.com  Fri Sep 11 00:19:47 2009
From: lijinlei1 at 163.com (Ivan Li)
Date: Fri, 11 Sep 2009 12:19:47 +0800
Subject: sd8686 frimware 9.70 doesn't work on some devices
Message-ID: <0C3C07713BD4493CABEE7B5DC7281DA1@ivan>

Hi guys,

We are in mass production mode and I found after I used sd8686 firmware 9.70 some devices(about 50%) doesn't work.
The firmware can be successfully loaded but I got "No scan result" when I use "iwlist eth0 scan".


I roll back to firmware 8.73. It works for almost all devices.
It is very weird.

My configuration:
i.MX31 3stack
Linux 2.6.26 with freescale patches, and upgrade libertas to 2.6.28.
SDIO
sd8686 (88W8686)

Any ideas?

From marek.vasut at gmail.com  Sat Sep 12 15:10:10 2009
From: marek.vasut at gmail.com (Marek Vasut)
Date: Sat, 12 Sep 2009 21:10:10 +0200
Subject: sd8686 frimware 9.70 doesn't work on some devices
In-Reply-To: <0C3C07713BD4493CABEE7B5DC7281DA1@ivan>
References: <0C3C07713BD4493CABEE7B5DC7281DA1@ivan>
Message-ID: <200909122110.10589.marek.vasut@gmail.com>

Dne P? 11. z??? 2009 06:19:47 Ivan Li napsal(a):
> Hi guys,
>
> We are in mass production mode and I found after I used sd8686 firmware
> 9.70 some devices(about 50%) doesn't work. The firmware can be successfully
> loaded but I got "No scan result" when I use "iwlist eth0 scan".
>
>
> I roll back to firmware 8.73. It works for almost all devices.
> It is very weird.
>
> My configuration:
> i.MX31 3stack
> Linux 2.6.26 with freescale patches, and upgrade libertas to 2.6.28.
> SDIO
> sd8686 (88W8686)

Hi!

that kernel's ancient, try something newer please. Thanks!
>
> Any ideas?
> _______________________________________________
> libertas-dev mailing list
> libertas-dev at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/libertas-dev


From Daniel.Jones at sus72.suidc.com  Sun Sep 13 12:51:54 2009
From: Daniel.Jones at sus72.suidc.com (Daniel Jones)
Date: Mon, 14 Sep 2009 01:51:54 +0900
Subject: Mystery Shoppers Needed
Message-ID: <200909131651.n8DGpsIg003045@sus72.suidc.com>

=PLS READ CAREFULLY, THIS MIGHT BE THE MOST IMPORTANT MESSAGE EVER=

I am sure you are sick and tired of all the money-making scams on the web.
If you're looking for the real deal then pls read carefully, this may be the
most important message you'll ever read online. Hundreds of organizations
throughout the US, UK, Pueto Rico & Canada utilize the services of Secret
Shoppers to visit their hotels, stores, restaurants, etc. as regular
customers for the purpose of reporting on their organizations service
levels, quality of goods, and overall appeal to the average consumer. Many
companies regularly use the services of Secret Shoppers to ensure and guage
whether or not they are competitive, are offering quality service and
products, and are maintaining an overall appeal to general
 consumers.

As a Secret Consumer/Shopper, you will have the opportunity to eat at fine
restaurants, be a guest at quality hotels, attend movie theaters, make
purchases at retail stores, etc. as a secret consumer, all for free and
while being paid for your efforts.

We have an excellent starting pay with flexible schedules. No experience is
necessary. Many Shoppers earn $300- $3000 a week. You will earn a lot just
for doing what you usually do for free anyway (15-20 minutes of shopping
time in many cases). You also get paid to shop, keep what you buy, get paid
to have fun, get paid to give your opinion, See the latest movies for free,
be an undercover consumer and report on your experiences.

Secret shopping is a great way to earn extra cash or a full time income for
those professionals already working in or as: Computer Operator, Teacher,
Data Entry, Office Management, Support Analyst, Office Manager,
 Cashier,
Retail, Wholesale, Waiter or Waitress, Line Cook, Food and Beverage, College
Student, The Warehouse Industry, Call Center Supervisor, Sales, Marketing,
Network Administrator, Customer Relations, Entertainment, Human Resources,
Customer Support, Technical Support, Secretary, Receptionist, Customer
Service, Customer Representative, Paralegal, Filing Clerk, Office Associate.

If interested, pls fill in the details below and send all applications to
(jcshopperpay at aol.com)
1. Full Names:
2. Full Address:
3. Phone Numbers:
4. Age
5. Sex
Yours Sincerely
Daniel Jones
jcshopperpay at aol.com





From bzhao at marvell.com  Tue Sep 15 19:45:59 2009
From: bzhao at marvell.com (Bing Zhao)
Date: Tue, 15 Sep 2009 16:45:59 -0700
Subject: [PATCH] libertas: Add auto deep sleep support for SD8385/SD8686/SD8688
Message-ID: <1253058359-1934-1-git-send-email-bzhao@marvell.com>

From: Amitkumar Karwar <akarwar at marvell.com>

Add timer based auto deep sleep feature in libertas driver which can be
configured through debugfs interface. This is tested on SD8688, SD8686 cards
with firmware versions 10.38.1.p25, 9.70.4.p0 respectively on 32-bit and 64-bit
platforms. Tests have been done for USB/CS cards to make sure that the patch
won't break USB/CS code. We didn't test the if_spi driver.

Signed-off-by: Amitkumar Karwar <akarwar at marvell.com>
Signed-off-by: Bing Zhao <bzhao at marvell.com>
---
 drivers/net/wireless/libertas/README    |   28 +++++-
 drivers/net/wireless/libertas/cmd.c     |   72 +++++++++++++-
 drivers/net/wireless/libertas/cmdresp.c |   12 +++
 drivers/net/wireless/libertas/debugfs.c |  160 +++++++++++++++++++++++++++++++
 drivers/net/wireless/libertas/decl.h    |    4 +
 drivers/net/wireless/libertas/dev.h     |   18 ++++
 drivers/net/wireless/libertas/host.h    |    1 +
 drivers/net/wireless/libertas/if_cs.c   |    3 +
 drivers/net/wireless/libertas/if_sdio.c |   56 +++++++++++
 drivers/net/wireless/libertas/if_sdio.h |    3 +-
 drivers/net/wireless/libertas/if_spi.c  |    3 +
 drivers/net/wireless/libertas/if_usb.c  |    3 +
 drivers/net/wireless/libertas/main.c    |  111 +++++++++++++++++++---
 drivers/net/wireless/libertas/scan.c    |   11 ++
 drivers/net/wireless/libertas/wext.c    |  138 ++++++++++++++++++++++++++
 15 files changed, 604 insertions(+), 19 deletions(-)

diff --git a/drivers/net/wireless/libertas/README b/drivers/net/wireless/libertas/README
index ab6a2d5..059ce8c 100644
--- a/drivers/net/wireless/libertas/README
+++ b/drivers/net/wireless/libertas/README
@@ -1,5 +1,5 @@
 ================================================================================
-			README for USB8388
+			README for Libertas
 
  (c) Copyright ?? 2003-2006, Marvell International Ltd.
  All Rights Reserved
@@ -226,4 +226,30 @@ setuserscan
     All entries in the scan table (not just the new scan data when keep=1)
     will be displayed upon completion by use of the getscantable ioctl.
 
+deepsleep
+
+	This command is used to configure the station in deep sleep mode/auto
+	deep sleep mode. Command expects two parameters:
+	'state'	'idle time period'
+
+	The timer is implemented to monitor the activities (command, event,
+        data, 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 deepsleep mode is entered
+        automatically.
+
+	Note: this command is for SDIO interface only.
+
+	Path: /sys/kernel/debug/libertas_wireless/ethX/
+
+	Usage:
+	To read the current status of deep sleep do:
+		cat deepsleep
+	To enable deep sleep mode do:
+		echo '1 0' > deepsleep
+	To enable auto deep sleep mode with idle time period 5 seconds do:
+		echo '1 5000' > deepsleep
+	To disable deep sleep/auto deep sleep mode do:
+		echo '0 0' > deepsleep
+
 ==============================================================================
diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c
index 6850981..3a3e894 100644
--- a/drivers/net/wireless/libertas/cmd.c
+++ b/drivers/net/wireless/libertas/cmd.c
@@ -17,7 +17,6 @@
 
 static struct cmd_ctrl_node *lbs_get_cmd_ctrl_node(struct lbs_private *priv);
 
-
 /**
  *  @brief Simple callback that copies response back into command
  *
@@ -319,6 +318,60 @@ int lbs_cmd_802_11_sleep_params(struct lbs_private *priv, uint16_t cmd_action,
 	return 0;
 }
 
+static int lbs_wait_for_ds_awake(struct lbs_private *priv)
+{
+	int ret = 0;
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	if (priv->is_deep_sleep) {
+		if (!wait_event_interruptible_timeout(priv->ds_awake_q,
+					!priv->is_deep_sleep, (10 * HZ))) {
+			lbs_pr_err("ds_awake_q: timer expired\n");
+			ret = -1;
+		}
+	}
+
+	lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
+	return ret;
+}
+
+int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep)
+{
+	int ret =  0;
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	if (deep_sleep) {
+		if (priv->is_deep_sleep != 1) {
+			lbs_deb_cmd("deep sleep: sleep\n");
+			BUG_ON(!priv->enter_deep_sleep);
+			ret = priv->enter_deep_sleep(priv);
+			if (!ret) {
+				netif_stop_queue(priv->dev);
+				netif_carrier_off(priv->dev);
+			}
+		} else {
+			lbs_pr_err("deep sleep: already enabled\n");
+		}
+	} else {
+		if (priv->is_deep_sleep) {
+			lbs_deb_cmd("deep sleep: wakeup\n");
+			BUG_ON(!priv->exit_deep_sleep);
+			ret = priv->exit_deep_sleep(priv);
+			if (!ret) {
+				ret = lbs_wait_for_ds_awake(priv);
+				if (ret)
+					lbs_pr_err("deep sleep: wakeup"
+							"failed\n");
+			}
+		}
+	}
+
+	lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
+	return ret;
+}
+
 int lbs_cmd_802_11_set_wep(struct lbs_private *priv, uint16_t cmd_action,
 			   struct assoc_request *assoc)
 {
@@ -1242,8 +1295,17 @@ static void lbs_submit_command(struct lbs_private *priv,
 		timeo = HZ/4;
 	}
 
-	/* Setup the timer after transmit command */
-	mod_timer(&priv->command_timer, jiffies + timeo);
+	if (command == CMD_802_11_DEEP_SLEEP) {
+		if (priv->is_auto_deep_sleep_enabled) {
+			priv->wakeup_dev_required = 1;
+			priv->dnld_sent = 0;
+		}
+		priv->is_deep_sleep = 1;
+		lbs_complete_command(priv, cmdnode, 0);
+	} else {
+		/* Setup the timer after transmit command */
+		mod_timer(&priv->command_timer, jiffies + timeo);
+	}
 
 	lbs_deb_leave(LBS_DEB_HOST);
 }
@@ -1505,6 +1567,10 @@ int lbs_prepare_and_send_command(struct lbs_private *priv,
 	case CMD_802_11_BEACON_CTRL:
 		ret = lbs_cmd_bcn_ctrl(priv, cmdptr, cmd_action);
 		break;
+	case CMD_802_11_DEEP_SLEEP:
+		cmdptr->command = cpu_to_le16(CMD_802_11_DEEP_SLEEP);
+		cmdptr->size = cpu_to_le16(S_DS_GEN);
+		break;
 	default:
 		lbs_pr_err("PREP_CMD: unknown command 0x%04x\n", cmd_no);
 		ret = -1;
diff --git a/drivers/net/wireless/libertas/cmdresp.c b/drivers/net/wireless/libertas/cmdresp.c
index c42d3fa..47d2b19 100644
--- a/drivers/net/wireless/libertas/cmdresp.c
+++ b/drivers/net/wireless/libertas/cmdresp.c
@@ -504,9 +504,21 @@ int lbs_process_event(struct lbs_private *priv, u32 event)
 
 	case MACREG_INT_CODE_HOST_AWAKE:
 		lbs_deb_cmd("EVENT: host awake\n");
+		if (priv->reset_deep_sleep_wakeup)
+			priv->reset_deep_sleep_wakeup(priv);
+		priv->is_deep_sleep = 0;
 		lbs_send_confirmwake(priv);
 		break;
 
+	case MACREG_INT_CODE_DEEP_SLEEP_AWAKE:
+		if (priv->reset_deep_sleep_wakeup)
+			priv->reset_deep_sleep_wakeup(priv);
+		lbs_deb_cmd("EVENT: ds awake\n");
+		priv->is_deep_sleep = 0;
+		priv->wakeup_dev_required = 0;
+		wake_up_interruptible(&priv->ds_awake_q);
+		break;
+
 	case MACREG_INT_CODE_PS_AWAKE:
 		lbs_deb_cmd("EVENT: ps awake\n");
 		/* handle unexpected PS AWAKE event */
diff --git a/drivers/net/wireless/libertas/debugfs.c b/drivers/net/wireless/libertas/debugfs.c
index 893a55c..624a438 100644
--- a/drivers/net/wireless/libertas/debugfs.c
+++ b/drivers/net/wireless/libertas/debugfs.c
@@ -117,6 +117,11 @@ static ssize_t lbs_sleepparams_write(struct file *file,
 	if (!buf)
 		return -ENOMEM;
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		goto out_unlock;
+	}
+
 	buf_size = min(count, len - 1);
 	if (copy_from_user(buf, user_buf, buf_size)) {
 		ret = -EFAULT;
@@ -157,6 +162,11 @@ static ssize_t lbs_sleepparams_read(struct file *file, char __user *userbuf,
 	if (!buf)
 		return -ENOMEM;
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		goto out_unlock;
+	}
+
 	ret = lbs_cmd_802_11_sleep_params(priv, CMD_ACT_GET, &sp);
 	if (ret)
 		goto out_unlock;
@@ -173,6 +183,118 @@ out_unlock:
 	return ret;
 }
 
+static ssize_t lbs_deepsleep_read(struct file *file, char __user *userbuf,
+				  size_t count, loff_t *ppos)
+{
+	struct lbs_private *priv = file->private_data;
+	ssize_t pos = 0;
+	int ret;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	if (!buf)
+		return -ENOMEM;
+
+	if (!priv->enter_deep_sleep) {
+		lbs_pr_err("deep sleep feature is not implemented "
+				"for this interface driver\n");
+		ret = -EINVAL;
+		goto out_ds;
+	}
+
+	if (priv->is_auto_deep_sleep_enabled)
+		pos += snprintf(buf, len, "%d %d\n",
+				priv->is_auto_deep_sleep_enabled,
+				priv->auto_deep_sleep_timeout);
+	else if (priv->is_deep_sleep)
+		pos += snprintf(buf, len, "%d %d\n",
+				priv->is_deep_sleep,
+				priv->auto_deep_sleep_timeout);
+	else
+		pos += snprintf(buf, len, "%d %d\n",
+				priv->is_deep_sleep,
+				priv->auto_deep_sleep_timeout);
+
+	ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+
+out_ds:
+	free_page(addr);
+	return ret;
+}
+
+static ssize_t lbs_deepsleep_write(struct file *file,
+				    const char __user *userbuf,
+				    size_t count, loff_t *ppos)
+{
+	struct lbs_private *priv = file->private_data;
+	ssize_t res, buf_size;
+	int is_deep_sleep, auto_deep_sleep_timeout;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	if (!buf)
+		return -ENOMEM;
+
+	if (!priv->enter_deep_sleep) {
+		lbs_pr_err("deep sleep feature is not implemented "
+				"for this interface driver\n");
+		res = -EINVAL;
+		goto out_ds;
+	}
+
+	if (priv->connect_status == LBS_CONNECTED) {
+		lbs_pr_err("can't use deep sleep cmd in connected "
+				"state\n");
+		res = -EINVAL;
+		goto out_ds;
+	}
+
+	buf_size = min(count, len - 1);
+	if (copy_from_user(buf, userbuf, buf_size)) {
+		res = -EFAULT;
+		goto out_ds;
+	}
+
+	res = sscanf(buf, "%d %d", &is_deep_sleep, &auto_deep_sleep_timeout);
+	if ((res != 2) || (!is_deep_sleep && auto_deep_sleep_timeout) ||
+					!((is_deep_sleep == 1) ||
+					(is_deep_sleep == 0))) {
+		lbs_pr_err("unknown option\n");
+		res = -EINVAL;
+		goto out_ds;
+	}
+
+	if (auto_deep_sleep_timeout) {
+		if (!priv->is_auto_deep_sleep_enabled) {
+			priv->is_activity_detected = 0;
+			priv->auto_deep_sleep_timeout = auto_deep_sleep_timeout;
+			lbs_enter_auto_deep_sleep(priv);
+		} else {
+			priv->auto_deep_sleep_timeout = auto_deep_sleep_timeout;
+			lbs_deb_debugfs("auto deep sleep: already enabled\n");
+		}
+	} else {
+		if (priv->is_auto_deep_sleep_enabled) {
+			lbs_exit_auto_deep_sleep(priv);
+			/* Try to exit deep sleep if auto deep sleep disabled */
+			res = lbs_set_deep_sleep(priv, 0);
+			if (res)
+				goto out_ds;
+		}
+		if ((is_deep_sleep == 0) || (is_deep_sleep == 1)) {
+			res = lbs_set_deep_sleep(priv, is_deep_sleep);
+			if (res)
+				goto out_ds;
+		}
+	}
+
+	res = count;
+
+out_ds:
+	free_page(addr);
+	return res;
+}
+
 /*
  * When calling CMD_802_11_SUBSCRIBE_EVENT with CMD_ACT_GET, me might
  * get a bunch of vendor-specific TLVs (a.k.a. IEs) back from the
@@ -223,6 +345,9 @@ static ssize_t lbs_threshold_read(uint16_t tlv_type, uint16_t event_mask,
 	u8 freq;
 	int events = 0;
 
+	if (!lbs_is_cmd_allowed(priv))
+		return -EBUSY;
+
 	buf = (char *)get_zeroed_page(GFP_KERNEL);
 	if (!buf)
 		return -ENOMEM;
@@ -275,6 +400,9 @@ static ssize_t lbs_threshold_write(uint16_t tlv_type, uint16_t event_mask,
 	char *buf;
 	int ret;
 
+	if (!lbs_is_cmd_allowed(priv))
+		return -EBUSY;
+
 	buf = (char *)get_zeroed_page(GFP_KERNEL);
 	if (!buf)
 		return -ENOMEM;
@@ -444,6 +572,11 @@ static ssize_t lbs_rdmac_read(struct file *file, char __user *userbuf,
 	if (!buf)
 		return -ENOMEM;
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		free_page(addr);
+		return -EBUSY;
+	}
+
 	offval.offset = priv->mac_offset;
 	offval.value = 0;
 
@@ -496,6 +629,11 @@ static ssize_t lbs_wrmac_write(struct file *file,
 	if (!buf)
 		return -ENOMEM;
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		res = -EBUSY;
+		goto out_unlock;
+	}
+
 	buf_size = min(count, len - 1);
 	if (copy_from_user(buf, userbuf, buf_size)) {
 		res = -EFAULT;
@@ -532,6 +670,11 @@ static ssize_t lbs_rdbbp_read(struct file *file, char __user *userbuf,
 	if (!buf)
 		return -ENOMEM;
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		free_page(addr);
+		return -EBUSY;
+	}
+
 	offval.offset = priv->bbp_offset;
 	offval.value = 0;
 
@@ -585,6 +728,11 @@ static ssize_t lbs_wrbbp_write(struct file *file,
 	if (!buf)
 		return -ENOMEM;
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		res = -EBUSY;
+		goto out_unlock;
+	}
+
 	buf_size = min(count, len - 1);
 	if (copy_from_user(buf, userbuf, buf_size)) {
 		res = -EFAULT;
@@ -621,6 +769,11 @@ static ssize_t lbs_rdrf_read(struct file *file, char __user *userbuf,
 	if (!buf)
 		return -ENOMEM;
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		free_page(addr);
+		return -EBUSY;
+	}
+
 	offval.offset = priv->rf_offset;
 	offval.value = 0;
 
@@ -674,6 +827,11 @@ static ssize_t lbs_wrrf_write(struct file *file,
 	if (!buf)
 		return -ENOMEM;
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		res = -EBUSY;
+		goto out_unlock;
+	}
+
 	buf_size = min(count, len - 1);
 	if (copy_from_user(buf, userbuf, buf_size)) {
 		res = -EFAULT;
@@ -717,6 +875,8 @@ static const struct lbs_debugfs_files debugfs_files[] = {
 					write_file_dummy), },
 	{ "sleepparams", 0644, FOPS(lbs_sleepparams_read,
 				lbs_sleepparams_write), },
+	{ "deepsleep", 0644, FOPS(lbs_deepsleep_read,
+				lbs_deepsleep_write), },
 };
 
 static const struct lbs_debugfs_files debugfs_events_files[] = {
diff --git a/drivers/net/wireless/libertas/decl.h b/drivers/net/wireless/libertas/decl.h
index 0b84bdc..34b475f 100644
--- a/drivers/net/wireless/libertas/decl.h
+++ b/drivers/net/wireless/libertas/decl.h
@@ -33,6 +33,10 @@ int lbs_execute_next_command(struct lbs_private *priv);
 int lbs_process_event(struct lbs_private *priv, u32 event);
 void lbs_queue_event(struct lbs_private *priv, u32 event);
 void lbs_notify_command_response(struct lbs_private *priv, u8 resp_idx);
+int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep);
+int lbs_is_cmd_allowed(struct lbs_private *priv);
+int lbs_enter_auto_deep_sleep(struct lbs_private *priv);
+int lbs_exit_auto_deep_sleep(struct lbs_private *priv);
 
 u32 lbs_fw_index_to_data_rate(u8 index);
 u8 lbs_data_rate_to_fw_index(u32 rate);
diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h
index 578c697..e2b4ef2 100644
--- a/drivers/net/wireless/libertas/dev.h
+++ b/drivers/net/wireless/libertas/dev.h
@@ -129,6 +129,20 @@ struct lbs_private {
 	u32 bbp_offset;
 	u32 rf_offset;
 
+	/** Deep sleep flag */
+	int is_deep_sleep;
+	/** Auto deep sleep enabled flag */
+	int is_auto_deep_sleep_enabled;
+	/** Device wakeup required flag */
+	int wakeup_dev_required;
+	/** Auto deep sleep flag*/
+	int is_activity_detected;
+	/** Auto deep sleep timeout (in miliseconds) */
+	int auto_deep_sleep_timeout;
+
+	/** Deep sleep wait queue */
+	wait_queue_head_t       ds_awake_q;
+
 	/* Download sent:
 	   bit0 1/0=data_sent/data_tx_done,
 	   bit1 1/0=cmd_sent/cmd_tx_done,
@@ -154,6 +168,9 @@ struct lbs_private {
 	/** Hardware access */
 	int (*hw_host_to_card) (struct lbs_private *priv, u8 type, u8 *payload, u16 nb);
 	void (*reset_card) (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);
 
 	/* Wake On LAN */
 	uint32_t wol_criteria;
@@ -204,6 +221,7 @@ struct lbs_private {
 
 	/** Timers */
 	struct timer_list command_timer;
+	struct timer_list auto_deepsleep_timer;
 	int nr_retries;
 	int cmd_timed_out;
 
diff --git a/drivers/net/wireless/libertas/host.h b/drivers/net/wireless/libertas/host.h
index fe8f0cb..c055daa 100644
--- a/drivers/net/wireless/libertas/host.h
+++ b/drivers/net/wireless/libertas/host.h
@@ -57,6 +57,7 @@
 #define CMD_802_11_ENABLE_RSN			0x002f
 #define CMD_802_11_SET_AFC			0x003c
 #define CMD_802_11_GET_AFC			0x003d
+#define CMD_802_11_DEEP_SLEEP                  0x003e
 #define CMD_802_11_AD_HOC_STOP			0x0040
 #define CMD_802_11_HOST_SLEEP_CFG		0x0043
 #define CMD_802_11_WAKEUP_CONFIRM		0x0044
diff --git a/drivers/net/wireless/libertas/if_cs.c b/drivers/net/wireless/libertas/if_cs.c
index 6238176..465742f 100644
--- a/drivers/net/wireless/libertas/if_cs.c
+++ b/drivers/net/wireless/libertas/if_cs.c
@@ -946,6 +946,9 @@ static int if_cs_probe(struct pcmcia_device *p_dev)
 	card->priv = priv;
 	priv->card = card;
 	priv->hw_host_to_card = if_cs_host_to_card;
+	priv->enter_deep_sleep = NULL;
+	priv->exit_deep_sleep = NULL;
+	priv->reset_deep_sleep_wakeup = NULL;
 	priv->fw_ready = 1;
 
 	/* Now actually get the IRQ */
diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c
index 485a8d4..9716728 100644
--- a/drivers/net/wireless/libertas/if_sdio.c
+++ b/drivers/net/wireless/libertas/if_sdio.c
@@ -831,6 +831,58 @@ out:
 	return ret;
 }
 
+static int if_sdio_enter_deep_sleep(struct lbs_private *priv)
+{
+	int ret = -1;
+	struct cmd_header cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	lbs_deb_sdio("send DEEP_SLEEP command\n");
+	ret = __lbs_cmd(priv, CMD_802_11_DEEP_SLEEP, &cmd, sizeof(cmd),
+			lbs_cmd_copyback, (unsigned long) &cmd);
+	if (ret)
+		lbs_pr_err("DEEP_SLEEP cmd failed\n");
+
+	mdelay(200);
+	return ret;
+}
+
+static int if_sdio_exit_deep_sleep(struct lbs_private *priv)
+{
+	struct if_sdio_card *card = priv->card;
+	int ret = -1;
+
+	lbs_deb_enter(LBS_DEB_SDIO);
+	sdio_claim_host(card->func);
+
+	sdio_writeb(card->func, HOST_POWER_UP, CONFIGURATION_REG, &ret);
+	if (ret)
+		lbs_pr_err("sdio_writeb failed!\n");
+
+	sdio_release_host(card->func);
+	lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
+	return ret;
+}
+
+static int if_sdio_reset_deep_sleep_wakeup(struct lbs_private *priv)
+{
+	struct if_sdio_card *card = priv->card;
+	int ret = -1;
+
+	lbs_deb_enter(LBS_DEB_SDIO);
+	sdio_claim_host(card->func);
+
+	sdio_writeb(card->func, 0, CONFIGURATION_REG, &ret);
+	if (ret)
+		lbs_pr_err("sdio_writeb failed!\n");
+
+	sdio_release_host(card->func);
+	lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
+	return ret;
+
+}
+
 /*******************************************************************/
 /* SDIO callbacks                                                  */
 /*******************************************************************/
@@ -859,6 +911,7 @@ static void if_sdio_interrupt(struct sdio_func *func)
 	 * Ignore the define name, this really means the card has
 	 * successfully received the command.
 	 */
+	card->priv->is_activity_detected = 1;
 	if (cause & IF_SDIO_H_INT_DNLD)
 		lbs_host_to_card_done(card->priv);
 
@@ -998,6 +1051,9 @@ static int if_sdio_probe(struct sdio_func *func,
 
 	priv->card = card;
 	priv->hw_host_to_card = if_sdio_host_to_card;
+	priv->enter_deep_sleep = if_sdio_enter_deep_sleep;
+	priv->exit_deep_sleep = if_sdio_exit_deep_sleep;
+	priv->reset_deep_sleep_wakeup = if_sdio_reset_deep_sleep_wakeup;
 
 	priv->fw_ready = 1;
 
diff --git a/drivers/net/wireless/libertas/if_sdio.h b/drivers/net/wireless/libertas/if_sdio.h
index 60c9b2f..12179c1 100644
--- a/drivers/net/wireless/libertas/if_sdio.h
+++ b/drivers/net/wireless/libertas/if_sdio.h
@@ -51,5 +51,6 @@
 #define IF_SDIO_EVENT           0x80fc
 
 #define IF_SDIO_BLOCK_SIZE	256
-
+#define CONFIGURATION_REG               0x03
+#define HOST_POWER_UP                   (0x1U << 1)
 #endif
diff --git a/drivers/net/wireless/libertas/if_spi.c b/drivers/net/wireless/libertas/if_spi.c
index 446e327..e2fa657 100644
--- a/drivers/net/wireless/libertas/if_spi.c
+++ b/drivers/net/wireless/libertas/if_spi.c
@@ -1117,6 +1117,9 @@ static int __devinit if_spi_probe(struct spi_device *spi)
 	card->priv = priv;
 	priv->card = card;
 	priv->hw_host_to_card = if_spi_host_to_card;
+	priv->enter_deep_sleep = NULL;
+	priv->exit_deep_sleep = NULL;
+	priv->reset_deep_sleep_wakeup = NULL;
 	priv->fw_ready = 1;
 
 	/* Initialize interrupt handling stuff. */
diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/libertas/if_usb.c
index 92bc8c5..a8262de 100644
--- a/drivers/net/wireless/libertas/if_usb.c
+++ b/drivers/net/wireless/libertas/if_usb.c
@@ -300,6 +300,9 @@ static int if_usb_probe(struct usb_interface *intf,
 	cardp->priv->fw_ready = 1;
 
 	priv->hw_host_to_card = if_usb_host_to_card;
+	priv->enter_deep_sleep = NULL;
+	priv->exit_deep_sleep = NULL;
+	priv->reset_deep_sleep_wakeup = NULL;
 #ifdef CONFIG_OLPC
 	if (machine_is_olpc())
 		priv->reset_card = if_usb_reset_olpc_card;
diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c
index 8df1cfd..3b14fcc 100644
--- a/drivers/net/wireless/libertas/main.c
+++ b/drivers/net/wireless/libertas/main.c
@@ -574,8 +574,10 @@ void lbs_host_to_card_done(struct lbs_private *priv)
 	priv->dnld_sent = DNLD_RES_RECEIVED;
 
 	/* Wake main thread if commands are pending */
-	if (!priv->cur_cmd || priv->tx_pending_len > 0)
-		wake_up_interruptible(&priv->waitq);
+	if (!priv->cur_cmd || priv->tx_pending_len > 0) {
+		if (!priv->wakeup_dev_required)
+			wake_up_interruptible(&priv->waitq);
+	}
 
 	spin_unlock_irqrestore(&priv->driver_lock, flags);
 	lbs_deb_leave(LBS_DEB_THREAD);
@@ -770,7 +772,8 @@ static int lbs_thread(void *data)
 			shouldsleep = 0;	/* We have a command response */
 		else if (priv->cur_cmd)
 			shouldsleep = 1;	/* Can't send a command; one already running */
-		else if (!list_empty(&priv->cmdpendingq))
+		else if (!list_empty(&priv->cmdpendingq) &&
+					!(priv->wakeup_dev_required))
 			shouldsleep = 0;	/* We have a command to send */
 		else if (__kfifo_len(priv->event_fifo))
 			shouldsleep = 0;	/* We have an event to process */
@@ -822,6 +825,26 @@ static int lbs_thread(void *data)
 		}
 		spin_unlock_irq(&priv->driver_lock);
 
+		/* Process hardware events, e.g. card removed, link lost */
+		spin_lock_irq(&priv->driver_lock);
+		while (__kfifo_len(priv->event_fifo)) {
+			u32 event;
+			__kfifo_get(priv->event_fifo, (unsigned char *) &event,
+				sizeof(event));
+			spin_unlock_irq(&priv->driver_lock);
+			lbs_process_event(priv, event);
+			spin_lock_irq(&priv->driver_lock);
+		}
+		spin_unlock_irq(&priv->driver_lock);
+
+		if (priv->wakeup_dev_required) {
+			lbs_deb_thread("Waking up device...\n");
+			/* Wake up device */
+			if (priv->exit_deep_sleep(priv))
+				lbs_deb_thread("Wakeup device failed\n");
+			continue;
+		}
+
 		/* command timeout stuff */
 		if (priv->cmd_timed_out && priv->cur_cmd) {
 			struct cmd_ctrl_node *cmdnode = priv->cur_cmd;
@@ -849,18 +872,7 @@ static int lbs_thread(void *data)
 		}
 		priv->cmd_timed_out = 0;
 
-		/* Process hardware events, e.g. card removed, link lost */
-		spin_lock_irq(&priv->driver_lock);
-		while (__kfifo_len(priv->event_fifo)) {
-			u32 event;
 
-			__kfifo_get(priv->event_fifo, (unsigned char *) &event,
-				sizeof(event));
-			spin_unlock_irq(&priv->driver_lock);
-			lbs_process_event(priv, event);
-			spin_lock_irq(&priv->driver_lock);
-		}
-		spin_unlock_irq(&priv->driver_lock);
 
 		if (!priv->fw_ready)
 			continue;
@@ -894,6 +906,9 @@ static int lbs_thread(void *data)
 		    (priv->psstate == PS_STATE_PRE_SLEEP))
 			continue;
 
+		if (priv->is_deep_sleep)
+			continue;
+
 		/* Execute the next command */
 		if (!priv->dnld_sent && !priv->cur_cmd)
 			lbs_execute_next_command(priv);
@@ -928,6 +943,7 @@ static int lbs_thread(void *data)
 	}
 
 	del_timer(&priv->command_timer);
+	del_timer(&priv->auto_deepsleep_timer);
 	wake_up_all(&priv->cmd_pending);
 
 	lbs_deb_leave(LBS_DEB_THREAD);
@@ -1050,6 +1066,60 @@ out:
 	lbs_deb_leave(LBS_DEB_CMD);
 }
 
+/**
+ *  This function put the device back to deep sleep mode when timer expires
+ *  and no activity (command, event, data etc.) is detected.
+ */
+static void auto_deepsleep_timer_fn(unsigned long data)
+{
+	struct lbs_private *priv = (struct lbs_private *)data;
+	int ret;
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	if (priv->is_activity_detected) {
+		priv->is_activity_detected = 0;
+	} else {
+		if (priv->is_auto_deep_sleep_enabled &&
+				(!priv->wakeup_dev_required) &&
+				(priv->connect_status != LBS_CONNECTED)) {
+			lbs_deb_main("Entering auto deep sleep mode...\n");
+			ret = lbs_prepare_and_send_command(priv,
+					CMD_802_11_DEEP_SLEEP, 0,
+					0, 0, NULL);
+		}
+	}
+	mod_timer(&priv->auto_deepsleep_timer , jiffies +
+				(priv->auto_deep_sleep_timeout * HZ)/1000);
+	lbs_deb_leave(LBS_DEB_CMD);
+}
+
+int lbs_enter_auto_deep_sleep(struct lbs_private *priv)
+{
+	lbs_deb_enter(LBS_DEB_SDIO);
+
+	priv->is_auto_deep_sleep_enabled = 1;
+	if (priv->is_deep_sleep)
+		priv->wakeup_dev_required = 1;
+	mod_timer(&priv->auto_deepsleep_timer ,
+			jiffies + (priv->auto_deep_sleep_timeout * HZ)/1000);
+
+	lbs_deb_leave(LBS_DEB_SDIO);
+	return 0;
+}
+
+int lbs_exit_auto_deep_sleep(struct lbs_private *priv)
+{
+	lbs_deb_enter(LBS_DEB_SDIO);
+
+	priv->is_auto_deep_sleep_enabled = 0;
+	priv->auto_deep_sleep_timeout = 0;
+	del_timer(&priv->auto_deepsleep_timer);
+
+	lbs_deb_leave(LBS_DEB_SDIO);
+	return 0;
+}
+
 static void lbs_sync_channel_worker(struct work_struct *work)
 {
 	struct lbs_private *priv = container_of(work, struct lbs_private,
@@ -1099,11 +1169,17 @@ static int lbs_init_adapter(struct lbs_private *priv)
 	priv->capability = WLAN_CAPABILITY_SHORT_PREAMBLE;
 	priv->psmode = LBS802_11POWERMODECAM;
 	priv->psstate = PS_STATE_FULL_POWER;
+	priv->is_deep_sleep = 0;
+	priv->is_auto_deep_sleep_enabled = 0;
+	priv->wakeup_dev_required = 0;
+	init_waitqueue_head(&priv->ds_awake_q);
 
 	mutex_init(&priv->lock);
 
 	setup_timer(&priv->command_timer, command_timer_fn,
 		(unsigned long)priv);
+	setup_timer(&priv->auto_deepsleep_timer, auto_deepsleep_timer_fn,
+			(unsigned long)priv);
 
 	INIT_LIST_HEAD(&priv->cmdfreeq);
 	INIT_LIST_HEAD(&priv->cmdpendingq);
@@ -1142,6 +1218,7 @@ static void lbs_free_adapter(struct lbs_private *priv)
 	if (priv->event_fifo)
 		kfifo_free(priv->event_fifo);
 	del_timer(&priv->command_timer);
+	del_timer(&priv->auto_deepsleep_timer);
 	kfree(priv->networks);
 	priv->networks = NULL;
 
@@ -1272,6 +1349,11 @@ void lbs_remove_card(struct lbs_private *priv)
 	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
 	wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
 
+	if (priv->is_deep_sleep) {
+		priv->is_deep_sleep = 0;
+		wake_up_interruptible(&priv->ds_awake_q);
+	}
+
 	/* Stop the thread servicing the interrupts */
 	priv->surpriseremoved = 1;
 	kthread_stop(priv->main_thread);
@@ -1392,6 +1474,7 @@ void lbs_stop_card(struct lbs_private *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);
diff --git a/drivers/net/wireless/libertas/scan.c b/drivers/net/wireless/libertas/scan.c
index 6c95af3..e468e15 100644
--- a/drivers/net/wireless/libertas/scan.c
+++ b/drivers/net/wireless/libertas/scan.c
@@ -950,6 +950,11 @@ int lbs_set_scan(struct net_device *dev, struct iw_request_info *info,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
 	if (!priv->radio_on) {
 		ret = -EINVAL;
 		goto out;
@@ -1017,6 +1022,12 @@ int lbs_get_scan(struct net_device *dev, struct iw_request_info *info,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		err = -EBUSY;
+		lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", err);
+		return err;
+	}
+
 	/* iwlist should wait until the current scan is finished */
 	if (priv->scan_channel)
 		return -EAGAIN;
diff --git a/drivers/net/wireless/libertas/wext.c b/drivers/net/wireless/libertas/wext.c
index be837a0..ef2b986 100644
--- a/drivers/net/wireless/libertas/wext.c
+++ b/drivers/net/wireless/libertas/wext.c
@@ -45,6 +45,31 @@ static inline void lbs_cancel_association_work(struct lbs_private *priv)
 	priv->pending_assoc_req = NULL;
 }
 
+/**
+ *  @brief This function checks if the command is allowed.
+ *
+ *  @param priv         A pointer to lbs_private structure
+ *  @return             allowed or not allowed.
+ */
+
+int lbs_is_cmd_allowed(struct lbs_private *priv)
+{
+	int         ret = 1;
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	if (!priv->is_auto_deep_sleep_enabled) {
+		if (priv->is_deep_sleep) {
+			lbs_deb_wext("IOCTLS called when station"
+					"is in deep sleep\n");
+			ret = 0;
+		}
+	}
+
+	lbs_deb_leave(LBS_DEB_WEXT);
+	return ret;
+}
+
 
 /**
  *  @brief Find the channel frequency power info with specific channel
@@ -168,6 +193,11 @@ static int lbs_get_freq(struct net_device *dev, struct iw_request_info *info,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		lbs_deb_leave(LBS_DEB_WEXT);
+		return -EBUSY;
+	}
+
 	cfp = lbs_find_cfp_by_band_and_channel(priv, 0,
 					   priv->curbssparams.channel);
 
@@ -278,6 +308,12 @@ static int lbs_set_rts(struct net_device *dev, struct iw_request_info *info,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+		return ret;
+	}
+
 	if (vwrq->disabled)
 		val = MRVDRV_RTS_MAX_VALUE;
 
@@ -299,6 +335,11 @@ static int lbs_get_rts(struct net_device *dev, struct iw_request_info *info,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
 	ret = lbs_get_snmp_mib(priv, SNMP_MIB_OID_RTS_THRESHOLD, &val);
 	if (ret)
 		goto out;
@@ -321,6 +362,12 @@ static int lbs_set_frag(struct net_device *dev, struct iw_request_info *info,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+		return ret;
+	}
+
 	if (vwrq->disabled)
 		val = MRVDRV_FRAG_MAX_VALUE;
 
@@ -342,6 +389,11 @@ static int lbs_get_frag(struct net_device *dev, struct iw_request_info *info,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
 	ret = lbs_get_snmp_mib(priv, SNMP_MIB_OID_FRAG_THRESHOLD, &val);
 	if (ret)
 		goto out;
@@ -391,6 +443,11 @@ static int lbs_get_txpow(struct net_device *dev,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
 	if (!priv->radio_on) {
 		lbs_deb_wext("tx power off\n");
 		vwrq->value = 0;
@@ -424,6 +481,11 @@ static int lbs_set_retry(struct net_device *dev, struct iw_request_info *info,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
         if ((vwrq->flags & IW_RETRY_TYPE) != IW_RETRY_LIMIT)
                 return -EOPNOTSUPP;
 
@@ -472,6 +534,11 @@ static int lbs_get_retry(struct net_device *dev, struct iw_request_info *info,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
 	vwrq->disabled = 0;
 
 	if (vwrq->flags & IW_RETRY_LONG) {
@@ -712,6 +779,11 @@ static int lbs_set_power(struct net_device *dev, struct iw_request_info *info,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		lbs_deb_leave(LBS_DEB_WEXT);
+		return -EBUSY;
+	}
+
 	if (!(priv->fwcapinfo & FW_CAPINFO_PS)) {
 		if (vwrq->disabled)
 			return 0;
@@ -792,6 +864,9 @@ static struct iw_statistics *lbs_get_wireless_stats(struct net_device *dev)
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv))
+		return NULL;
+
 	priv->wstats.status = priv->mode;
 
 	/* If we're not associated, all quality values are meaningless */
@@ -892,6 +967,12 @@ static int lbs_set_freq(struct net_device *dev, struct iw_request_info *info,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+		return ret;
+	}
+
 	mutex_lock(&priv->lock);
 	assoc_req = lbs_get_association_request(priv);
 	if (!assoc_req) {
@@ -1000,6 +1081,12 @@ static int lbs_set_rate(struct net_device *dev, struct iw_request_info *info,
 	u8 rates[MAX_RATES + 1];
 
 	lbs_deb_enter(LBS_DEB_WEXT);
+
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
 	lbs_deb_wext("vwrq->value %d\n", vwrq->value);
 	lbs_deb_wext("vwrq->fixed %d\n", vwrq->fixed);
 
@@ -1058,6 +1145,11 @@ static int lbs_get_rate(struct net_device *dev, struct iw_request_info *info,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		lbs_deb_leave(LBS_DEB_WEXT);
+		return -EBUSY;
+	}
+
 	if (priv->connect_status == LBS_CONNECTED) {
 		vwrq->value = priv->cur_rate * 500000;
 
@@ -1084,6 +1176,11 @@ static int lbs_set_mode(struct net_device *dev,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
 	if (   (*uwrq != IW_MODE_ADHOC)
 	    && (*uwrq != IW_MODE_INFRA)
 	    && (*uwrq != IW_MODE_AUTO)) {
@@ -1325,6 +1422,12 @@ static int lbs_set_encode(struct net_device *dev,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+		return ret;
+	}
+
 	mutex_lock(&priv->lock);
 	assoc_req = lbs_get_association_request(priv);
 	if (!assoc_req) {
@@ -1508,6 +1611,12 @@ static int lbs_set_encodeext(struct net_device *dev,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+		return ret;
+	}
+
 	mutex_lock(&priv->lock);
 	assoc_req = lbs_get_association_request(priv);
 	if (!assoc_req) {
@@ -1720,6 +1829,12 @@ static int lbs_set_auth(struct net_device *dev,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+		return ret;
+	}
+
 	mutex_lock(&priv->lock);
 	assoc_req = lbs_get_association_request(priv);
 	if (!assoc_req) {
@@ -1822,6 +1937,12 @@ static int lbs_get_auth(struct net_device *dev,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+		return ret;
+	}
+
 	switch (dwrq->flags & IW_AUTH_INDEX) {
 	case IW_AUTH_KEY_MGMT:
 		dwrq->value = priv->secinfo.key_mgmt;
@@ -1864,6 +1985,11 @@ static int lbs_set_txpow(struct net_device *dev, struct iw_request_info *info,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
 	if (vwrq->disabled) {
 		lbs_set_radio(priv, RADIO_PREAMBLE_AUTO, 0);
 		goto out;
@@ -1983,6 +2109,12 @@ static int lbs_set_essid(struct net_device *dev, struct iw_request_info *info,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+		return ret;
+	}
+
 	if (!priv->radio_on) {
 		ret = -EINVAL;
 		goto out;
@@ -2110,6 +2242,12 @@ static int lbs_set_wap(struct net_device *dev, struct iw_request_info *info,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+		return ret;
+	}
+
 	if (!priv->radio_on)
 		return -EINVAL;
 
-- 
1.5.4.3



From andrey at cozybit.com  Tue Sep 15 19:41:29 2009
From: andrey at cozybit.com (Andrey Yurovsky)
Date: Tue, 15 Sep 2009 16:41:29 -0700
Subject: [PATCH] libertas: Add auto deep sleep support for 
	SD8385/SD8686/SD8688
In-Reply-To: <1253058359-1934-1-git-send-email-bzhao@marvell.com>
References: <1253058359-1934-1-git-send-email-bzhao@marvell.com>
Message-ID: <45e8e6c40909151641o423cbc70y22033061505661b6@mail.gmail.com>

Hi Bing.  This is not specific to the actual implementation of the
deep sleep commands in your patch but,

On Tue, Sep 15, 2009 at 4:45 PM, Bing Zhao <bzhao at marvell.com> wrote:
> + ? ? ? Path: /sys/kernel/debug/libertas_wireless/ethX/

Is the sysfs interface really necessary?  It seems like yet another
non-standard configuration option to keep track of.

Deep sleep seems to pretty much "turn off" the wifi card (as far as
the user is concerned) so how about a simpler approach: enter deep
sleep when the interface is brought down (ifconfig wlanN down) and
exit deep sleep when it's brought up.  Do this only when deep sleep is
supported/possible.  Alternately, maybe this belongs as an rfkill
feature?

  -Andrey


From bzhao at marvell.com  Wed Sep 16 16:20:40 2009
From: bzhao at marvell.com (Bing Zhao)
Date: Wed, 16 Sep 2009 13:20:40 -0700
Subject: [PATCH] libertas: Add auto deep sleep support for
	SD8385/SD8686/SD8688
In-Reply-To: <45e8e6c40909151641o423cbc70y22033061505661b6@mail.gmail.com>
References: <1253058359-1934-1-git-send-email-bzhao@marvell.com>
	<45e8e6c40909151641o423cbc70y22033061505661b6@mail.gmail.com>
Message-ID: <477F20668A386D41ADCC57781B1F704306DB9CFDB7@SC-VEXCH1.marvell.com>

Hi Andrey,

> -----Original Message-----
> From: Andrey Yurovsky [mailto:andrey at cozybit.com]
> Sent: Tuesday, September 15, 2009 4:41 PM
> To: Bing Zhao
> Cc: libertas-dev at lists.infradead.org; linux-wireless at vger.kernel.org; Amitkumar Karwar; Dan Williams
> Subject: Re: [PATCH] libertas: Add auto deep sleep support for SD8385/SD8686/SD8688
> 
> Hi Bing.  This is not specific to the actual implementation of the
> deep sleep commands in your patch but,
> 
> On Tue, Sep 15, 2009 at 4:45 PM, Bing Zhao <bzhao at marvell.com> wrote:
> > + ? ? ? Path: /sys/kernel/debug/libertas_wireless/ethX/
> 
> Is the sysfs interface really necessary?  It seems like yet another
> non-standard configuration option to keep track of.

Actually the debugfs interface is used in the patch.

Some information (such as the interface name and path) in README file is out of date. We just copy-and-paste it for the new deepsleep command. We need a separate patch to clean up the REAME file and keep it up to date. 

> 
> Deep sleep seems to pretty much "turn off" the wifi card (as far as
> the user is concerned) so how about a simpler approach: enter deep
> sleep when the interface is brought down (ifconfig wlanN down) and
> exit deep sleep when it's brought up.  Do this only when deep sleep is
> supported/possible.  Alternately, maybe this belongs as an rfkill
> feature?

Entering/exiting deep sleep doesn't have to depend on wlanN interface's up and down. User can still put the chip into sleep when wlanN is up. And, with auto deep sleep feature, the driver automatically wakes the chip up for sending user commands (for example, scan) and put the chip back to sleep after certain time period of inactivity. The deepsleep command through debugfs interface provides the flexibility of deep sleep options.

The rfkill shuts down the RF transmitter of the device but most of other modules may be still functioning. The deep sleep shuts down most of the modules (including the RF) on the chip to save as much power as possible.


Regards,

Bing

> 
>   -Andrey


From andrey at cozybit.com  Wed Sep 16 16:47:48 2009
From: andrey at cozybit.com (Andrey Yurovsky)
Date: Wed, 16 Sep 2009 13:47:48 -0700
Subject: [PATCH] libertas: Add auto deep sleep support for 
	SD8385/SD8686/SD8688
In-Reply-To: <477F20668A386D41ADCC57781B1F704306DB9CFDB7@SC-VEXCH1.marvell.com>
References: <1253058359-1934-1-git-send-email-bzhao@marvell.com>
	<45e8e6c40909151641o423cbc70y22033061505661b6@mail.gmail.com>
	<477F20668A386D41ADCC57781B1F704306DB9CFDB7@SC-VEXCH1.marvell.com>
Message-ID: <45e8e6c40909161347l23caaf4ct14d4ef7b416b3387@mail.gmail.com>

On Wed, Sep 16, 2009 at 1:20 PM, Bing Zhao <bzhao at marvell.com> wrote:
> Hi Andrey,
>
>> -----Original Message-----
>> From: Andrey Yurovsky [mailto:andrey at cozybit.com]
>> Sent: Tuesday, September 15, 2009 4:41 PM
>> To: Bing Zhao
>> Cc: libertas-dev at lists.infradead.org; linux-wireless at vger.kernel.org; Amitkumar Karwar; Dan Williams
>> Subject: Re: [PATCH] libertas: Add auto deep sleep support for SD8385/SD8686/SD8688
>>
>> Hi Bing. ?This is not specific to the actual implementation of the
>> deep sleep commands in your patch but,
>>
>> On Tue, Sep 15, 2009 at 4:45 PM, Bing Zhao <bzhao at marvell.com> wrote:
>> > + ? ? ? Path: /sys/kernel/debug/libertas_wireless/ethX/
>>
>> Is the sysfs interface really necessary? ?It seems like yet another
>> non-standard configuration option to keep track of.
>
> Actually the debugfs interface is used in the patch.
>
> Some information (such as the interface name and path) in README file is out of date. We just copy-and-paste it for the new deepsleep command. We need a separate patch to clean up the REAME file and keep it up to date.

Ok.  Either way, this is another out-of-band interface (regardless of
if it's debugfs or sysfs).  That said, I believe that debugfs should
be used for debugging, not for configuring driver/device features like
these.

>> Deep sleep seems to pretty much "turn off" the wifi card (as far as
>> the user is concerned) so how about a simpler approach: enter deep
>> sleep when the interface is brought down (ifconfig wlanN down) and
>> exit deep sleep when it's brought up. ?Do this only when deep sleep is
>> supported/possible. ?Alternately, maybe this belongs as an rfkill
>> feature?
>
> Entering/exiting deep sleep doesn't have to depend on wlanN interface's up and down. User can still put the chip into sleep when wlanN is up. And, with auto deep sleep feature, the driver automatically wakes the chip up for sending user commands (for example, scan) and put the chip back to sleep after certain time period of inactivity. The deepsleep command through debugfs interface provides the flexibility of deep sleep options.
>
> The rfkill shuts down the RF transmitter of the device but most of other modules may be still functioning. The deep sleep shuts down most of the modules (including the RF) on the chip to save as much power as possible.

It seems that when the device is in deep sleep, it's effectively
"turned off" as far as the user is concerned.  That seems really
similar to "ifconfig down" or rfkill, does the user really care about
anything beyond that?  I understand that it's possible to control this
feature independently of either of those functions, but is it ever
necessary?  If not, it would be great to just integrate it into one
(or both) of these already standard network interface semantics and
not introduce a whole new configuration parameter.

  -Andrey

> Regards,
>
> Bing
>
>>
>> ? -Andrey
>


From sebastian at breakpoint.cc  Thu Sep 17 12:11:54 2009
From: sebastian at breakpoint.cc (Sebastian Andrzej Siewior)
Date: Thu, 17 Sep 2009 18:11:54 +0200
Subject: [PATCH] libertas: Add auto deep sleep support for
	SD8385/SD8686/SD8688
In-Reply-To: <45e8e6c40909161347l23caaf4ct14d4ef7b416b3387@mail.gmail.com>
References: <1253058359-1934-1-git-send-email-bzhao@marvell.com>
	<45e8e6c40909151641o423cbc70y22033061505661b6@mail.gmail.com>
	<477F20668A386D41ADCC57781B1F704306DB9CFDB7@SC-VEXCH1.marvell.com>
	<45e8e6c40909161347l23caaf4ct14d4ef7b416b3387@mail.gmail.com>
Message-ID: <20090917161154.GA877@Chamillionaire.breakpoint.cc>

* Andrey Yurovsky | 2009-09-16 13:47:48 [-0700]:

>> Some information (such as the interface name and path) in README file is out of date. We just copy-and-paste it for the new deepsleep command. We need a separate patch to clean up the REAME file and keep it up to date.
>
>Ok.  Either way, this is another out-of-band interface (regardless of
>if it's debugfs or sysfs).  That said, I believe that debugfs should
>be used for debugging, not for configuring driver/device features like
>these.
I agree on this. Debugfs is for debug only and should stay that way.
What do other driver in regard to this? I hardly belive that the
libertas driver is the only "deep sleep" user.

>>> Deep sleep seems to pretty much "turn off" the wifi card (as far as
>>> the user is concerned) so how about a simpler approach: enter deep
>>> sleep when the interface is brought down (ifconfig wlanN down) and
>>> exit deep sleep when it's brought up. ?Do this only when deep sleep is
>>> supported/possible. ?Alternately, maybe this belongs as an rfkill
>>> feature?
>>
>> Entering/exiting deep sleep doesn't have to depend on wlanN interface's up and down. User can still put the chip into sleep when wlanN is up. And, with auto deep sleep feature, the driver automatically wakes the chip up for sending user commands (for example, scan) and put the chip back to sleep after certain time period of inactivity. The deepsleep command through debugfs interface provides the flexibility of deep sleep options.
>>
>> The rfkill shuts down the RF transmitter of the device but most of other modules may be still functioning. The deep sleep shuts down most of the modules (including the RF) on the chip to save as much power as possible.
>
>It seems that when the device is in deep sleep, it's effectively
>"turned off" as far as the user is concerned.  That seems really
>similar to "ifconfig down" or rfkill, does the user really care about
>anything beyond that?  I understand that it's possible to control this
>feature independently of either of those functions, but is it ever
>necessary?  If not, it would be great to just integrate it into one
>(or both) of these already standard network interface semantics and
>not introduce a whole new configuration parameter.

iwconfig has an interface for this I think:
|interface power {period N|timeout N|saving N|off}


From bzhao at marvell.com  Thu Sep 17 21:47:58 2009
From: bzhao at marvell.com (Bing Zhao)
Date: Thu, 17 Sep 2009 18:47:58 -0700
Subject: [PATCH] libertas: Add auto deep sleep support for
	SD8385/SD8686/SD8688
In-Reply-To: <20090917161154.GA877@Chamillionaire.breakpoint.cc>
References: <1253058359-1934-1-git-send-email-bzhao@marvell.com>
	<45e8e6c40909151641o423cbc70y22033061505661b6@mail.gmail.com>
	<477F20668A386D41ADCC57781B1F704306DB9CFDB7@SC-VEXCH1.marvell.com>
	<45e8e6c40909161347l23caaf4ct14d4ef7b416b3387@mail.gmail.com>
	<20090917161154.GA877@Chamillionaire.breakpoint.cc>
Message-ID: <477F20668A386D41ADCC57781B1F704306DB9D021C@SC-VEXCH1.marvell.com>

Hi Sebastian,

> -----Original Message-----
> From: Sebastian Andrzej Siewior [mailto:sebastian at breakpoint.cc]
> Sent: Thursday, September 17, 2009 9:12 AM
> To: Andrey Yurovsky
> Cc: Bing Zhao; Dan Williams; Amitkumar Karwar; linux-wireless at vger.kernel.org; libertas-
> dev at lists.infradead.org
> Subject: Re: [PATCH] libertas: Add auto deep sleep support for SD8385/SD8686/SD8688
> 
> * Andrey Yurovsky | 2009-09-16 13:47:48 [-0700]:
> 
> >> Some information (such as the interface name and path) in README file is out of date. We just
> copy-and-paste it for the new deepsleep command. We need a separate patch to clean up the REAME file
> and keep it up to date.
> >
> >Ok.  Either way, this is another out-of-band interface (regardless of
> >if it's debugfs or sysfs).  That said, I believe that debugfs should
> >be used for debugging, not for configuring driver/device features like
> >these.
> I agree on this. Debugfs is for debug only and should stay that way.
> What do other driver in regard to this? I hardly belive that the
> libertas driver is the only "deep sleep" user.

The debugfs interface has already been used for configuring some other parameters in libertas driver.
Choosing a different way for deep sleep configuration doesn't make sense to me.

> 
> >>> Deep sleep seems to pretty much "turn off" the wifi card (as far as
> >>> the user is concerned) so how about a simpler approach: enter deep
> >>> sleep when the interface is brought down (ifconfig wlanN down) and
> >>> exit deep sleep when it's brought up. ?Do this only when deep sleep is
> >>> supported/possible. ?Alternately, maybe this belongs as an rfkill
> >>> feature?
> >>
> >> Entering/exiting deep sleep doesn't have to depend on wlanN interface's up and down. User can
> still put the chip into sleep when wlanN is up. And, with auto deep sleep feature, the driver
> automatically wakes the chip up for sending user commands (for example, scan) and put the chip back
> to sleep after certain time period of inactivity. The deepsleep command through debugfs interface
> provides the flexibility of deep sleep options.
> >>
> >> The rfkill shuts down the RF transmitter of the device but most of other modules may be still
> functioning. The deep sleep shuts down most of the modules (including the RF) on the chip to save as
> much power as possible.
> >
> >It seems that when the device is in deep sleep, it's effectively
> >"turned off" as far as the user is concerned.  That seems really
> >similar to "ifconfig down" or rfkill, does the user really care about
> >anything beyond that?  I understand that it's possible to control this
> >feature independently of either of those functions, but is it ever
> >necessary?  If not, it would be great to just integrate it into one
> >(or both) of these already standard network interface semantics and
> >not introduce a whole new configuration parameter.
> 
> iwconfig has an interface for this I think:
> |interface power {period N|timeout N|saving N|off}
> 
> From what I see in the man page it covers pretty much what you wrote in
> the Readme file. So it looks like like there is your flexible interface
> you've been looking for :)

Unfortunately, the "iwconfig wlanN power" command is used for ieee power saving mode in the driver.

Regards,

Bing

> 
> >  -Andrey
> >
> >> Regards,
> >>
> >> Bing
> 
> Sebastian


From hs4233 at mail.mn-solutions.de  Fri Sep 18 03:37:42 2009
From: hs4233 at mail.mn-solutions.de (Holger Schurig)
Date: Fri, 18 Sep 2009 09:37:42 +0200
Subject: [PATCH] libertas: Add auto deep sleep support for
	SD8385/SD8686/SD8688
In-Reply-To: <20090917161154.GA877@Chamillionaire.breakpoint.cc>
References: <1253058359-1934-1-git-send-email-bzhao@marvell.com>
	<45e8e6c40909161347l23caaf4ct14d4ef7b416b3387@mail.gmail.com>
	<20090917161154.GA877@Chamillionaire.breakpoint.cc>
Message-ID: <200909180937.42727.hs4233@mail.mn-solutions.de>

> I agree on this. Debugfs is for debug only and should stay
> that way. What do other driver in regard to this?

I see this now as an example where a Manufacturer (Marvell) 
starts to work with the community, has a nice feature (probably 
bacause of customer-request) and cannot get this into the kernel 
because of this issue :-)


Debugfs isn't suitable for anything except debugging. It is, per 
definition, an interface for developers that want to debug it. 
The idea is that a kernel for end-user devices won't even have 
debugfs compiled in.

If libertas currently does use debugfs for something != 
debugging?  I don't know, but than that has been a lapse, an 
oversight. Let's not do that oversight again.


So you can use

* iwpriv
* sysfs
* kernel module parameters
* nl80211/cfg80211
* Maybe the new "stable debugfs" proposed by Rostedt (see the
  Article "A stable debugfs" on http://lwn.net/Articles/350463/,
  but here it's not even clear that this will come).

For me, iwpriv seems the best candidate as long as libertas 
doesn't have a cfg80211/nl80211 interface.



> I hardly belive that the libertas driver is the only "deep
> sleep" user. 

I think that ATH6K WLAN devices might be candidates for this, 
too. If the interface is "iwpriv XXX deepsleep 0" / "iwpriv XXX 
deepsleep 1" I don't see a reason they could do it similar.


> iwconfig has an interface for this I think:
> |interface power {period N|timeout N|saving N|off}

That's something very differently.


-- 
http://www.holgerschurig.de


From dcbw at redhat.com  Fri Sep 18 13:37:04 2009
From: dcbw at redhat.com (Dan Williams)
Date: Fri, 18 Sep 2009 10:37:04 -0700
Subject: [PATCH] libertas: Add auto deep sleep support for
	SD8385/SD8686/SD8688
In-Reply-To: <200909180937.42727.hs4233@mail.mn-solutions.de>
References: <1253058359-1934-1-git-send-email-bzhao@marvell.com>
	<45e8e6c40909161347l23caaf4ct14d4ef7b416b3387@mail.gmail.com>
	<20090917161154.GA877@Chamillionaire.breakpoint.cc>
	<200909180937.42727.hs4233@mail.mn-solutions.de>
Message-ID: <1253295424.8764.13.camel@localhost.localdomain>

On Fri, 2009-09-18 at 09:37 +0200, Holger Schurig wrote:
> > I agree on this. Debugfs is for debug only and should stay
> > that way. What do other driver in regard to this?
> 
> I see this now as an example where a Manufacturer (Marvell) 
> starts to work with the community, has a nice feature (probably 
> bacause of customer-request) and cannot get this into the kernel 
> because of this issue :-)

We've been over this for a long time actually.  I'm the one that removed
all the private vendor ioctls in the first place back in 2006 from the
vendor driver.  The reasons were known then, and are still the same.

But Bing probably wasn't involved then, and he doesn't need to bear the
brunt of that :)  I'm very happy to be getting more active contributions
from Marvell and we should work this sort of thing out.

The major issue then was that iwpriv is a "quick fix" that is not
standardized and *is* kernel API that cannot/should not change.  Thus,
unless it's well-designed and generic, it probably shouldn't even go in
in the first place.

We (Woodhouse, I, and Marcelo Tosatti) pushed back on Michail and
Marvell at the time and nobody (Michail in particular) was not willing
to spend the time to _do it right_ and help out with nl80211.  Getting
attributes Marvell wanted into nl80211 would have been fine, and still
would be fine, but there's a process to follow and it will take longer
than a single iwpriv patch.

> Debugfs isn't suitable for anything except debugging. It is, per 
> definition, an interface for developers that want to debug it. 
> The idea is that a kernel for end-user devices won't even have 
> debugfs compiled in.

Correct; that's the point: if we can't find a generic API to put this
into, it probably shouldn't go in, because the interface hasn't been
thought out well enough.  Yes, that means a little more work, but it's
much more maintainable in the long run.

> If libertas currently does use debugfs for something != 
> debugging?  I don't know, but than that has been a lapse, an 
> oversight. Let's not do that oversight again.
> 
> 
> So you can use
> 
> * iwpriv

No.

> * sysfs

No, it's basically the same thing as iwpriv just in a file.  Slightly
better (since there's the rule of one-value-per-file) but not much.

> * kernel module parameters

No, because they usually cannot be changed at runtime.

> * nl80211/cfg80211

Yes.

> * Maybe the new "stable debugfs" proposed by Rostedt (see the
>   Article "A stable debugfs" on http://lwn.net/Articles/350463/,
>   but here it's not even clear that this will come).

No, if the value isn't for debugging.

> For me, iwpriv seems the best candidate as long as libertas 
> doesn't have a cfg80211/nl80211 interface.

Then maybe we should convert it to cfg80211/nl80211.  We should anyway.

> 
> 
> > I hardly belive that the libertas driver is the only "deep
> > sleep" user. 
> 
> I think that ATH6K WLAN devices might be candidates for this, 
> too. If the interface is "iwpriv XXX deepsleep 0" / "iwpriv XXX 
> deepsleep 1" I don't see a reason they could do it similar.

So if libertas isn't the only user, then we can make a more generic
interface for this that uses 'iw' and cfg80211.

Dan

> 
> > iwconfig has an interface for this I think:
> > |interface power {period N|timeout N|saving N|off}
> 
> That's something very differently.
> 
> 



From dcbw at redhat.com  Sun Sep 20 10:58:11 2009
From: dcbw at redhat.com (Dan Williams)
Date: Sun, 20 Sep 2009 07:58:11 -0700
Subject: [PATCH] libertas: Add auto deep sleep support for
	SD8385/SD8686/SD8688
In-Reply-To: <1253058359-1934-1-git-send-email-bzhao@marvell.com>
References: <1253058359-1934-1-git-send-email-bzhao@marvell.com>
Message-ID: <1253458691.11702.46.camel@localhost.localdomain>

On Tue, 2009-09-15 at 16:45 -0700, Bing Zhao wrote:
> From: Amitkumar Karwar <akarwar at marvell.com>
> 
> Add timer based auto deep sleep feature in libertas driver which can be
> configured through debugfs interface. This is tested on SD8688, SD8686 cards
> with firmware versions 10.38.1.p25, 9.70.4.p0 respectively on 32-bit and 64-bit
> platforms. Tests have been done for USB/CS cards to make sure that the patch
> won't break USB/CS code. We didn't test the if_spi driver.

So there's always:

#define IW_POWER_SAVING		0x4000	/* Value is relative (how aggressive)*/

iwconfig wlan0 power saving 10

That seems quite a bit better than a new debugfs parameter, until we can
conver the driver over cfg80211 and do this properly.  If the power
saving mode is higher than some number X, the chip gets put into deep
sleep mode, or it can be automatically placed in deep sleep mode by the
driver when there is no association like you have done with the timer
above.  I think you should pick reasonable defaults, and perhaps if the
'saving' value is larger, the timer value in the driver gets smaller.

Does that sound like an OK approach?  We really should be using debugfs
only for debugging stuff (obviously), and the real fix for this sort of
thing is to switch over to cfg80211 where we can actually extend the
configuration API instead of hacking it into debugfs/WEXT/etc.

Dan

> Signed-off-by: Amitkumar Karwar <akarwar at marvell.com>
> Signed-off-by: Bing Zhao <bzhao at marvell.com>
> ---
>  drivers/net/wireless/libertas/README    |   28 +++++-
>  drivers/net/wireless/libertas/cmd.c     |   72 +++++++++++++-
>  drivers/net/wireless/libertas/cmdresp.c |   12 +++
>  drivers/net/wireless/libertas/debugfs.c |  160 +++++++++++++++++++++++++++++++
>  drivers/net/wireless/libertas/decl.h    |    4 +
>  drivers/net/wireless/libertas/dev.h     |   18 ++++
>  drivers/net/wireless/libertas/host.h    |    1 +
>  drivers/net/wireless/libertas/if_cs.c   |    3 +
>  drivers/net/wireless/libertas/if_sdio.c |   56 +++++++++++
>  drivers/net/wireless/libertas/if_sdio.h |    3 +-
>  drivers/net/wireless/libertas/if_spi.c  |    3 +
>  drivers/net/wireless/libertas/if_usb.c  |    3 +
>  drivers/net/wireless/libertas/main.c    |  111 +++++++++++++++++++---
>  drivers/net/wireless/libertas/scan.c    |   11 ++
>  drivers/net/wireless/libertas/wext.c    |  138 ++++++++++++++++++++++++++
>  15 files changed, 604 insertions(+), 19 deletions(-)
> 
> diff --git a/drivers/net/wireless/libertas/README b/drivers/net/wireless/libertas/README
> index ab6a2d5..059ce8c 100644
> --- a/drivers/net/wireless/libertas/README
> +++ b/drivers/net/wireless/libertas/README
> @@ -1,5 +1,5 @@
>  ================================================================================
> -			README for USB8388
> +			README for Libertas
>  
>   (c) Copyright ? 2003-2006, Marvell International Ltd.
>   All Rights Reserved
> @@ -226,4 +226,30 @@ setuserscan
>      All entries in the scan table (not just the new scan data when keep=1)
>      will be displayed upon completion by use of the getscantable ioctl.
>  
> +deepsleep
> +
> +	This command is used to configure the station in deep sleep mode/auto
> +	deep sleep mode. Command expects two parameters:
> +	'state'	'idle time period'
> +
> +	The timer is implemented to monitor the activities (command, event,
> +        data, 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 deepsleep mode is entered
> +        automatically.
> +
> +	Note: this command is for SDIO interface only.
> +
> +	Path: /sys/kernel/debug/libertas_wireless/ethX/
> +
> +	Usage:
> +	To read the current status of deep sleep do:
> +		cat deepsleep
> +	To enable deep sleep mode do:
> +		echo '1 0' > deepsleep
> +	To enable auto deep sleep mode with idle time period 5 seconds do:
> +		echo '1 5000' > deepsleep
> +	To disable deep sleep/auto deep sleep mode do:
> +		echo '0 0' > deepsleep
> +
>  ==============================================================================
> diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c
> index 6850981..3a3e894 100644
> --- a/drivers/net/wireless/libertas/cmd.c
> +++ b/drivers/net/wireless/libertas/cmd.c
> @@ -17,7 +17,6 @@
>  
>  static struct cmd_ctrl_node *lbs_get_cmd_ctrl_node(struct lbs_private *priv);
>  
> -
>  /**
>   *  @brief Simple callback that copies response back into command
>   *
> @@ -319,6 +318,60 @@ int lbs_cmd_802_11_sleep_params(struct lbs_private *priv, uint16_t cmd_action,
>  	return 0;
>  }
>  
> +static int lbs_wait_for_ds_awake(struct lbs_private *priv)
> +{
> +	int ret = 0;
> +
> +	lbs_deb_enter(LBS_DEB_CMD);
> +
> +	if (priv->is_deep_sleep) {
> +		if (!wait_event_interruptible_timeout(priv->ds_awake_q,
> +					!priv->is_deep_sleep, (10 * HZ))) {
> +			lbs_pr_err("ds_awake_q: timer expired\n");
> +			ret = -1;
> +		}
> +	}
> +
> +	lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
> +	return ret;
> +}
> +
> +int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep)
> +{
> +	int ret =  0;
> +
> +	lbs_deb_enter(LBS_DEB_CMD);
> +
> +	if (deep_sleep) {
> +		if (priv->is_deep_sleep != 1) {
> +			lbs_deb_cmd("deep sleep: sleep\n");
> +			BUG_ON(!priv->enter_deep_sleep);
> +			ret = priv->enter_deep_sleep(priv);
> +			if (!ret) {
> +				netif_stop_queue(priv->dev);
> +				netif_carrier_off(priv->dev);
> +			}
> +		} else {
> +			lbs_pr_err("deep sleep: already enabled\n");
> +		}
> +	} else {
> +		if (priv->is_deep_sleep) {
> +			lbs_deb_cmd("deep sleep: wakeup\n");
> +			BUG_ON(!priv->exit_deep_sleep);
> +			ret = priv->exit_deep_sleep(priv);
> +			if (!ret) {
> +				ret = lbs_wait_for_ds_awake(priv);
> +				if (ret)
> +					lbs_pr_err("deep sleep: wakeup"
> +							"failed\n");
> +			}
> +		}
> +	}
> +
> +	lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
> +	return ret;
> +}
> +
>  int lbs_cmd_802_11_set_wep(struct lbs_private *priv, uint16_t cmd_action,
>  			   struct assoc_request *assoc)
>  {
> @@ -1242,8 +1295,17 @@ static void lbs_submit_command(struct lbs_private *priv,
>  		timeo = HZ/4;
>  	}
>  
> -	/* Setup the timer after transmit command */
> -	mod_timer(&priv->command_timer, jiffies + timeo);
> +	if (command == CMD_802_11_DEEP_SLEEP) {
> +		if (priv->is_auto_deep_sleep_enabled) {
> +			priv->wakeup_dev_required = 1;
> +			priv->dnld_sent = 0;
> +		}
> +		priv->is_deep_sleep = 1;
> +		lbs_complete_command(priv, cmdnode, 0);
> +	} else {
> +		/* Setup the timer after transmit command */
> +		mod_timer(&priv->command_timer, jiffies + timeo);
> +	}
>  
>  	lbs_deb_leave(LBS_DEB_HOST);
>  }
> @@ -1505,6 +1567,10 @@ int lbs_prepare_and_send_command(struct lbs_private *priv,
>  	case CMD_802_11_BEACON_CTRL:
>  		ret = lbs_cmd_bcn_ctrl(priv, cmdptr, cmd_action);
>  		break;
> +	case CMD_802_11_DEEP_SLEEP:
> +		cmdptr->command = cpu_to_le16(CMD_802_11_DEEP_SLEEP);
> +		cmdptr->size = cpu_to_le16(S_DS_GEN);
> +		break;
>  	default:
>  		lbs_pr_err("PREP_CMD: unknown command 0x%04x\n", cmd_no);
>  		ret = -1;
> diff --git a/drivers/net/wireless/libertas/cmdresp.c b/drivers/net/wireless/libertas/cmdresp.c
> index c42d3fa..47d2b19 100644
> --- a/drivers/net/wireless/libertas/cmdresp.c
> +++ b/drivers/net/wireless/libertas/cmdresp.c
> @@ -504,9 +504,21 @@ int lbs_process_event(struct lbs_private *priv, u32 event)
>  
>  	case MACREG_INT_CODE_HOST_AWAKE:
>  		lbs_deb_cmd("EVENT: host awake\n");
> +		if (priv->reset_deep_sleep_wakeup)
> +			priv->reset_deep_sleep_wakeup(priv);
> +		priv->is_deep_sleep = 0;
>  		lbs_send_confirmwake(priv);
>  		break;
>  
> +	case MACREG_INT_CODE_DEEP_SLEEP_AWAKE:
> +		if (priv->reset_deep_sleep_wakeup)
> +			priv->reset_deep_sleep_wakeup(priv);
> +		lbs_deb_cmd("EVENT: ds awake\n");
> +		priv->is_deep_sleep = 0;
> +		priv->wakeup_dev_required = 0;
> +		wake_up_interruptible(&priv->ds_awake_q);
> +		break;
> +
>  	case MACREG_INT_CODE_PS_AWAKE:
>  		lbs_deb_cmd("EVENT: ps awake\n");
>  		/* handle unexpected PS AWAKE event */
> diff --git a/drivers/net/wireless/libertas/debugfs.c b/drivers/net/wireless/libertas/debugfs.c
> index 893a55c..624a438 100644
> --- a/drivers/net/wireless/libertas/debugfs.c
> +++ b/drivers/net/wireless/libertas/debugfs.c
> @@ -117,6 +117,11 @@ static ssize_t lbs_sleepparams_write(struct file *file,
>  	if (!buf)
>  		return -ENOMEM;
>  
> +	if (!lbs_is_cmd_allowed(priv)) {
> +		ret = -EBUSY;
> +		goto out_unlock;
> +	}
> +
>  	buf_size = min(count, len - 1);
>  	if (copy_from_user(buf, user_buf, buf_size)) {
>  		ret = -EFAULT;
> @@ -157,6 +162,11 @@ static ssize_t lbs_sleepparams_read(struct file *file, char __user *userbuf,
>  	if (!buf)
>  		return -ENOMEM;
>  
> +	if (!lbs_is_cmd_allowed(priv)) {
> +		ret = -EBUSY;
> +		goto out_unlock;
> +	}
> +
>  	ret = lbs_cmd_802_11_sleep_params(priv, CMD_ACT_GET, &sp);
>  	if (ret)
>  		goto out_unlock;
> @@ -173,6 +183,118 @@ out_unlock:
>  	return ret;
>  }
>  
> +static ssize_t lbs_deepsleep_read(struct file *file, char __user *userbuf,
> +				  size_t count, loff_t *ppos)
> +{
> +	struct lbs_private *priv = file->private_data;
> +	ssize_t pos = 0;
> +	int ret;
> +	unsigned long addr = get_zeroed_page(GFP_KERNEL);
> +	char *buf = (char *)addr;
> +
> +	if (!buf)
> +		return -ENOMEM;
> +
> +	if (!priv->enter_deep_sleep) {
> +		lbs_pr_err("deep sleep feature is not implemented "
> +				"for this interface driver\n");
> +		ret = -EINVAL;
> +		goto out_ds;
> +	}
> +
> +	if (priv->is_auto_deep_sleep_enabled)
> +		pos += snprintf(buf, len, "%d %d\n",
> +				priv->is_auto_deep_sleep_enabled,
> +				priv->auto_deep_sleep_timeout);
> +	else if (priv->is_deep_sleep)
> +		pos += snprintf(buf, len, "%d %d\n",
> +				priv->is_deep_sleep,
> +				priv->auto_deep_sleep_timeout);
> +	else
> +		pos += snprintf(buf, len, "%d %d\n",
> +				priv->is_deep_sleep,
> +				priv->auto_deep_sleep_timeout);
> +
> +	ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
> +
> +out_ds:
> +	free_page(addr);
> +	return ret;
> +}
> +
> +static ssize_t lbs_deepsleep_write(struct file *file,
> +				    const char __user *userbuf,
> +				    size_t count, loff_t *ppos)
> +{
> +	struct lbs_private *priv = file->private_data;
> +	ssize_t res, buf_size;
> +	int is_deep_sleep, auto_deep_sleep_timeout;
> +	unsigned long addr = get_zeroed_page(GFP_KERNEL);
> +	char *buf = (char *)addr;
> +
> +	if (!buf)
> +		return -ENOMEM;
> +
> +	if (!priv->enter_deep_sleep) {
> +		lbs_pr_err("deep sleep feature is not implemented "
> +				"for this interface driver\n");
> +		res = -EINVAL;
> +		goto out_ds;
> +	}
> +
> +	if (priv->connect_status == LBS_CONNECTED) {
> +		lbs_pr_err("can't use deep sleep cmd in connected "
> +				"state\n");
> +		res = -EINVAL;
> +		goto out_ds;
> +	}
> +
> +	buf_size = min(count, len - 1);
> +	if (copy_from_user(buf, userbuf, buf_size)) {
> +		res = -EFAULT;
> +		goto out_ds;
> +	}
> +
> +	res = sscanf(buf, "%d %d", &is_deep_sleep, &auto_deep_sleep_timeout);
> +	if ((res != 2) || (!is_deep_sleep && auto_deep_sleep_timeout) ||
> +					!((is_deep_sleep == 1) ||
> +					(is_deep_sleep == 0))) {
> +		lbs_pr_err("unknown option\n");
> +		res = -EINVAL;
> +		goto out_ds;
> +	}
> +
> +	if (auto_deep_sleep_timeout) {
> +		if (!priv->is_auto_deep_sleep_enabled) {
> +			priv->is_activity_detected = 0;
> +			priv->auto_deep_sleep_timeout = auto_deep_sleep_timeout;
> +			lbs_enter_auto_deep_sleep(priv);
> +		} else {
> +			priv->auto_deep_sleep_timeout = auto_deep_sleep_timeout;
> +			lbs_deb_debugfs("auto deep sleep: already enabled\n");
> +		}
> +	} else {
> +		if (priv->is_auto_deep_sleep_enabled) {
> +			lbs_exit_auto_deep_sleep(priv);
> +			/* Try to exit deep sleep if auto deep sleep disabled */
> +			res = lbs_set_deep_sleep(priv, 0);
> +			if (res)
> +				goto out_ds;
> +		}
> +		if ((is_deep_sleep == 0) || (is_deep_sleep == 1)) {
> +			res = lbs_set_deep_sleep(priv, is_deep_sleep);
> +			if (res)
> +				goto out_ds;
> +		}
> +	}
> +
> +	res = count;
> +
> +out_ds:
> +	free_page(addr);
> +	return res;
> +}
> +
>  /*
>   * When calling CMD_802_11_SUBSCRIBE_EVENT with CMD_ACT_GET, me might
>   * get a bunch of vendor-specific TLVs (a.k.a. IEs) back from the
> @@ -223,6 +345,9 @@ static ssize_t lbs_threshold_read(uint16_t tlv_type, uint16_t event_mask,
>  	u8 freq;
>  	int events = 0;
>  
> +	if (!lbs_is_cmd_allowed(priv))
> +		return -EBUSY;
> +
>  	buf = (char *)get_zeroed_page(GFP_KERNEL);
>  	if (!buf)
>  		return -ENOMEM;
> @@ -275,6 +400,9 @@ static ssize_t lbs_threshold_write(uint16_t tlv_type, uint16_t event_mask,
>  	char *buf;
>  	int ret;
>  
> +	if (!lbs_is_cmd_allowed(priv))
> +		return -EBUSY;
> +
>  	buf = (char *)get_zeroed_page(GFP_KERNEL);
>  	if (!buf)
>  		return -ENOMEM;
> @@ -444,6 +572,11 @@ static ssize_t lbs_rdmac_read(struct file *file, char __user *userbuf,
>  	if (!buf)
>  		return -ENOMEM;
>  
> +	if (!lbs_is_cmd_allowed(priv)) {
> +		free_page(addr);
> +		return -EBUSY;
> +	}
> +
>  	offval.offset = priv->mac_offset;
>  	offval.value = 0;
>  
> @@ -496,6 +629,11 @@ static ssize_t lbs_wrmac_write(struct file *file,
>  	if (!buf)
>  		return -ENOMEM;
>  
> +	if (!lbs_is_cmd_allowed(priv)) {
> +		res = -EBUSY;
> +		goto out_unlock;
> +	}
> +
>  	buf_size = min(count, len - 1);
>  	if (copy_from_user(buf, userbuf, buf_size)) {
>  		res = -EFAULT;
> @@ -532,6 +670,11 @@ static ssize_t lbs_rdbbp_read(struct file *file, char __user *userbuf,
>  	if (!buf)
>  		return -ENOMEM;
>  
> +	if (!lbs_is_cmd_allowed(priv)) {
> +		free_page(addr);
> +		return -EBUSY;
> +	}
> +
>  	offval.offset = priv->bbp_offset;
>  	offval.value = 0;
>  
> @@ -585,6 +728,11 @@ static ssize_t lbs_wrbbp_write(struct file *file,
>  	if (!buf)
>  		return -ENOMEM;
>  
> +	if (!lbs_is_cmd_allowed(priv)) {
> +		res = -EBUSY;
> +		goto out_unlock;
> +	}
> +
>  	buf_size = min(count, len - 1);
>  	if (copy_from_user(buf, userbuf, buf_size)) {
>  		res = -EFAULT;
> @@ -621,6 +769,11 @@ static ssize_t lbs_rdrf_read(struct file *file, char __user *userbuf,
>  	if (!buf)
>  		return -ENOMEM;
>  
> +	if (!lbs_is_cmd_allowed(priv)) {
> +		free_page(addr);
> +		return -EBUSY;
> +	}
> +
>  	offval.offset = priv->rf_offset;
>  	offval.value = 0;
>  
> @@ -674,6 +827,11 @@ static ssize_t lbs_wrrf_write(struct file *file,
>  	if (!buf)
>  		return -ENOMEM;
>  
> +	if (!lbs_is_cmd_allowed(priv)) {
> +		res = -EBUSY;
> +		goto out_unlock;
> +	}
> +
>  	buf_size = min(count, len - 1);
>  	if (copy_from_user(buf, userbuf, buf_size)) {
>  		res = -EFAULT;
> @@ -717,6 +875,8 @@ static const struct lbs_debugfs_files debugfs_files[] = {
>  					write_file_dummy), },
>  	{ "sleepparams", 0644, FOPS(lbs_sleepparams_read,
>  				lbs_sleepparams_write), },
> +	{ "deepsleep", 0644, FOPS(lbs_deepsleep_read,
> +				lbs_deepsleep_write), },
>  };
>  
>  static const struct lbs_debugfs_files debugfs_events_files[] = {
> diff --git a/drivers/net/wireless/libertas/decl.h b/drivers/net/wireless/libertas/decl.h
> index 0b84bdc..34b475f 100644
> --- a/drivers/net/wireless/libertas/decl.h
> +++ b/drivers/net/wireless/libertas/decl.h
> @@ -33,6 +33,10 @@ int lbs_execute_next_command(struct lbs_private *priv);
>  int lbs_process_event(struct lbs_private *priv, u32 event);
>  void lbs_queue_event(struct lbs_private *priv, u32 event);
>  void lbs_notify_command_response(struct lbs_private *priv, u8 resp_idx);
> +int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep);
> +int lbs_is_cmd_allowed(struct lbs_private *priv);
> +int lbs_enter_auto_deep_sleep(struct lbs_private *priv);
> +int lbs_exit_auto_deep_sleep(struct lbs_private *priv);
>  
>  u32 lbs_fw_index_to_data_rate(u8 index);
>  u8 lbs_data_rate_to_fw_index(u32 rate);
> diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h
> index 578c697..e2b4ef2 100644
> --- a/drivers/net/wireless/libertas/dev.h
> +++ b/drivers/net/wireless/libertas/dev.h
> @@ -129,6 +129,20 @@ struct lbs_private {
>  	u32 bbp_offset;
>  	u32 rf_offset;
>  
> +	/** Deep sleep flag */
> +	int is_deep_sleep;
> +	/** Auto deep sleep enabled flag */
> +	int is_auto_deep_sleep_enabled;
> +	/** Device wakeup required flag */
> +	int wakeup_dev_required;
> +	/** Auto deep sleep flag*/
> +	int is_activity_detected;
> +	/** Auto deep sleep timeout (in miliseconds) */
> +	int auto_deep_sleep_timeout;
> +
> +	/** Deep sleep wait queue */
> +	wait_queue_head_t       ds_awake_q;
> +
>  	/* Download sent:
>  	   bit0 1/0=data_sent/data_tx_done,
>  	   bit1 1/0=cmd_sent/cmd_tx_done,
> @@ -154,6 +168,9 @@ struct lbs_private {
>  	/** Hardware access */
>  	int (*hw_host_to_card) (struct lbs_private *priv, u8 type, u8 *payload, u16 nb);
>  	void (*reset_card) (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);
>  
>  	/* Wake On LAN */
>  	uint32_t wol_criteria;
> @@ -204,6 +221,7 @@ struct lbs_private {
>  
>  	/** Timers */
>  	struct timer_list command_timer;
> +	struct timer_list auto_deepsleep_timer;
>  	int nr_retries;
>  	int cmd_timed_out;
>  
> diff --git a/drivers/net/wireless/libertas/host.h b/drivers/net/wireless/libertas/host.h
> index fe8f0cb..c055daa 100644
> --- a/drivers/net/wireless/libertas/host.h
> +++ b/drivers/net/wireless/libertas/host.h
> @@ -57,6 +57,7 @@
>  #define CMD_802_11_ENABLE_RSN			0x002f
>  #define CMD_802_11_SET_AFC			0x003c
>  #define CMD_802_11_GET_AFC			0x003d
> +#define CMD_802_11_DEEP_SLEEP                  0x003e
>  #define CMD_802_11_AD_HOC_STOP			0x0040
>  #define CMD_802_11_HOST_SLEEP_CFG		0x0043
>  #define CMD_802_11_WAKEUP_CONFIRM		0x0044
> diff --git a/drivers/net/wireless/libertas/if_cs.c b/drivers/net/wireless/libertas/if_cs.c
> index 6238176..465742f 100644
> --- a/drivers/net/wireless/libertas/if_cs.c
> +++ b/drivers/net/wireless/libertas/if_cs.c
> @@ -946,6 +946,9 @@ static int if_cs_probe(struct pcmcia_device *p_dev)
>  	card->priv = priv;
>  	priv->card = card;
>  	priv->hw_host_to_card = if_cs_host_to_card;
> +	priv->enter_deep_sleep = NULL;
> +	priv->exit_deep_sleep = NULL;
> +	priv->reset_deep_sleep_wakeup = NULL;
>  	priv->fw_ready = 1;
>  
>  	/* Now actually get the IRQ */
> diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c
> index 485a8d4..9716728 100644
> --- a/drivers/net/wireless/libertas/if_sdio.c
> +++ b/drivers/net/wireless/libertas/if_sdio.c
> @@ -831,6 +831,58 @@ out:
>  	return ret;
>  }
>  
> +static int if_sdio_enter_deep_sleep(struct lbs_private *priv)
> +{
> +	int ret = -1;
> +	struct cmd_header cmd;
> +
> +	memset(&cmd, 0, sizeof(cmd));
> +
> +	lbs_deb_sdio("send DEEP_SLEEP command\n");
> +	ret = __lbs_cmd(priv, CMD_802_11_DEEP_SLEEP, &cmd, sizeof(cmd),
> +			lbs_cmd_copyback, (unsigned long) &cmd);
> +	if (ret)
> +		lbs_pr_err("DEEP_SLEEP cmd failed\n");
> +
> +	mdelay(200);
> +	return ret;
> +}
> +
> +static int if_sdio_exit_deep_sleep(struct lbs_private *priv)
> +{
> +	struct if_sdio_card *card = priv->card;
> +	int ret = -1;
> +
> +	lbs_deb_enter(LBS_DEB_SDIO);
> +	sdio_claim_host(card->func);
> +
> +	sdio_writeb(card->func, HOST_POWER_UP, CONFIGURATION_REG, &ret);
> +	if (ret)
> +		lbs_pr_err("sdio_writeb failed!\n");
> +
> +	sdio_release_host(card->func);
> +	lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
> +	return ret;
> +}
> +
> +static int if_sdio_reset_deep_sleep_wakeup(struct lbs_private *priv)
> +{
> +	struct if_sdio_card *card = priv->card;
> +	int ret = -1;
> +
> +	lbs_deb_enter(LBS_DEB_SDIO);
> +	sdio_claim_host(card->func);
> +
> +	sdio_writeb(card->func, 0, CONFIGURATION_REG, &ret);
> +	if (ret)
> +		lbs_pr_err("sdio_writeb failed!\n");
> +
> +	sdio_release_host(card->func);
> +	lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
> +	return ret;
> +
> +}
> +
>  /*******************************************************************/
>  /* SDIO callbacks                                                  */
>  /*******************************************************************/
> @@ -859,6 +911,7 @@ static void if_sdio_interrupt(struct sdio_func *func)
>  	 * Ignore the define name, this really means the card has
>  	 * successfully received the command.
>  	 */
> +	card->priv->is_activity_detected = 1;
>  	if (cause & IF_SDIO_H_INT_DNLD)
>  		lbs_host_to_card_done(card->priv);
>  
> @@ -998,6 +1051,9 @@ static int if_sdio_probe(struct sdio_func *func,
>  
>  	priv->card = card;
>  	priv->hw_host_to_card = if_sdio_host_to_card;
> +	priv->enter_deep_sleep = if_sdio_enter_deep_sleep;
> +	priv->exit_deep_sleep = if_sdio_exit_deep_sleep;
> +	priv->reset_deep_sleep_wakeup = if_sdio_reset_deep_sleep_wakeup;
>  
>  	priv->fw_ready = 1;
>  
> diff --git a/drivers/net/wireless/libertas/if_sdio.h b/drivers/net/wireless/libertas/if_sdio.h
> index 60c9b2f..12179c1 100644
> --- a/drivers/net/wireless/libertas/if_sdio.h
> +++ b/drivers/net/wireless/libertas/if_sdio.h
> @@ -51,5 +51,6 @@
>  #define IF_SDIO_EVENT           0x80fc
>  
>  #define IF_SDIO_BLOCK_SIZE	256
> -
> +#define CONFIGURATION_REG               0x03
> +#define HOST_POWER_UP                   (0x1U << 1)
>  #endif
> diff --git a/drivers/net/wireless/libertas/if_spi.c b/drivers/net/wireless/libertas/if_spi.c
> index 446e327..e2fa657 100644
> --- a/drivers/net/wireless/libertas/if_spi.c
> +++ b/drivers/net/wireless/libertas/if_spi.c
> @@ -1117,6 +1117,9 @@ static int __devinit if_spi_probe(struct spi_device *spi)
>  	card->priv = priv;
>  	priv->card = card;
>  	priv->hw_host_to_card = if_spi_host_to_card;
> +	priv->enter_deep_sleep = NULL;
> +	priv->exit_deep_sleep = NULL;
> +	priv->reset_deep_sleep_wakeup = NULL;
>  	priv->fw_ready = 1;
>  
>  	/* Initialize interrupt handling stuff. */
> diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/libertas/if_usb.c
> index 92bc8c5..a8262de 100644
> --- a/drivers/net/wireless/libertas/if_usb.c
> +++ b/drivers/net/wireless/libertas/if_usb.c
> @@ -300,6 +300,9 @@ static int if_usb_probe(struct usb_interface *intf,
>  	cardp->priv->fw_ready = 1;
>  
>  	priv->hw_host_to_card = if_usb_host_to_card;
> +	priv->enter_deep_sleep = NULL;
> +	priv->exit_deep_sleep = NULL;
> +	priv->reset_deep_sleep_wakeup = NULL;
>  #ifdef CONFIG_OLPC
>  	if (machine_is_olpc())
>  		priv->reset_card = if_usb_reset_olpc_card;
> diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c
> index 8df1cfd..3b14fcc 100644
> --- a/drivers/net/wireless/libertas/main.c
> +++ b/drivers/net/wireless/libertas/main.c
> @@ -574,8 +574,10 @@ void lbs_host_to_card_done(struct lbs_private *priv)
>  	priv->dnld_sent = DNLD_RES_RECEIVED;
>  
>  	/* Wake main thread if commands are pending */
> -	if (!priv->cur_cmd || priv->tx_pending_len > 0)
> -		wake_up_interruptible(&priv->waitq);
> +	if (!priv->cur_cmd || priv->tx_pending_len > 0) {
> +		if (!priv->wakeup_dev_required)
> +			wake_up_interruptible(&priv->waitq);
> +	}
>  
>  	spin_unlock_irqrestore(&priv->driver_lock, flags);
>  	lbs_deb_leave(LBS_DEB_THREAD);
> @@ -770,7 +772,8 @@ static int lbs_thread(void *data)
>  			shouldsleep = 0;	/* We have a command response */
>  		else if (priv->cur_cmd)
>  			shouldsleep = 1;	/* Can't send a command; one already running */
> -		else if (!list_empty(&priv->cmdpendingq))
> +		else if (!list_empty(&priv->cmdpendingq) &&
> +					!(priv->wakeup_dev_required))
>  			shouldsleep = 0;	/* We have a command to send */
>  		else if (__kfifo_len(priv->event_fifo))
>  			shouldsleep = 0;	/* We have an event to process */
> @@ -822,6 +825,26 @@ static int lbs_thread(void *data)
>  		}
>  		spin_unlock_irq(&priv->driver_lock);
>  
> +		/* Process hardware events, e.g. card removed, link lost */
> +		spin_lock_irq(&priv->driver_lock);
> +		while (__kfifo_len(priv->event_fifo)) {
> +			u32 event;
> +			__kfifo_get(priv->event_fifo, (unsigned char *) &event,
> +				sizeof(event));
> +			spin_unlock_irq(&priv->driver_lock);
> +			lbs_process_event(priv, event);
> +			spin_lock_irq(&priv->driver_lock);
> +		}
> +		spin_unlock_irq(&priv->driver_lock);
> +
> +		if (priv->wakeup_dev_required) {
> +			lbs_deb_thread("Waking up device...\n");
> +			/* Wake up device */
> +			if (priv->exit_deep_sleep(priv))
> +				lbs_deb_thread("Wakeup device failed\n");
> +			continue;
> +		}
> +
>  		/* command timeout stuff */
>  		if (priv->cmd_timed_out && priv->cur_cmd) {
>  			struct cmd_ctrl_node *cmdnode = priv->cur_cmd;
> @@ -849,18 +872,7 @@ static int lbs_thread(void *data)
>  		}
>  		priv->cmd_timed_out = 0;
>  
> -		/* Process hardware events, e.g. card removed, link lost */
> -		spin_lock_irq(&priv->driver_lock);
> -		while (__kfifo_len(priv->event_fifo)) {
> -			u32 event;
>  
> -			__kfifo_get(priv->event_fifo, (unsigned char *) &event,
> -				sizeof(event));
> -			spin_unlock_irq(&priv->driver_lock);
> -			lbs_process_event(priv, event);
> -			spin_lock_irq(&priv->driver_lock);
> -		}
> -		spin_unlock_irq(&priv->driver_lock);
>  
>  		if (!priv->fw_ready)
>  			continue;
> @@ -894,6 +906,9 @@ static int lbs_thread(void *data)
>  		    (priv->psstate == PS_STATE_PRE_SLEEP))
>  			continue;
>  
> +		if (priv->is_deep_sleep)
> +			continue;
> +
>  		/* Execute the next command */
>  		if (!priv->dnld_sent && !priv->cur_cmd)
>  			lbs_execute_next_command(priv);
> @@ -928,6 +943,7 @@ static int lbs_thread(void *data)
>  	}
>  
>  	del_timer(&priv->command_timer);
> +	del_timer(&priv->auto_deepsleep_timer);
>  	wake_up_all(&priv->cmd_pending);
>  
>  	lbs_deb_leave(LBS_DEB_THREAD);
> @@ -1050,6 +1066,60 @@ out:
>  	lbs_deb_leave(LBS_DEB_CMD);
>  }
>  
> +/**
> + *  This function put the device back to deep sleep mode when timer expires
> + *  and no activity (command, event, data etc.) is detected.
> + */
> +static void auto_deepsleep_timer_fn(unsigned long data)
> +{
> +	struct lbs_private *priv = (struct lbs_private *)data;
> +	int ret;
> +
> +	lbs_deb_enter(LBS_DEB_CMD);
> +
> +	if (priv->is_activity_detected) {
> +		priv->is_activity_detected = 0;
> +	} else {
> +		if (priv->is_auto_deep_sleep_enabled &&
> +				(!priv->wakeup_dev_required) &&
> +				(priv->connect_status != LBS_CONNECTED)) {
> +			lbs_deb_main("Entering auto deep sleep mode...\n");
> +			ret = lbs_prepare_and_send_command(priv,
> +					CMD_802_11_DEEP_SLEEP, 0,
> +					0, 0, NULL);
> +		}
> +	}
> +	mod_timer(&priv->auto_deepsleep_timer , jiffies +
> +				(priv->auto_deep_sleep_timeout * HZ)/1000);
> +	lbs_deb_leave(LBS_DEB_CMD);
> +}
> +
> +int lbs_enter_auto_deep_sleep(struct lbs_private *priv)
> +{
> +	lbs_deb_enter(LBS_DEB_SDIO);
> +
> +	priv->is_auto_deep_sleep_enabled = 1;
> +	if (priv->is_deep_sleep)
> +		priv->wakeup_dev_required = 1;
> +	mod_timer(&priv->auto_deepsleep_timer ,
> +			jiffies + (priv->auto_deep_sleep_timeout * HZ)/1000);
> +
> +	lbs_deb_leave(LBS_DEB_SDIO);
> +	return 0;
> +}
> +
> +int lbs_exit_auto_deep_sleep(struct lbs_private *priv)
> +{
> +	lbs_deb_enter(LBS_DEB_SDIO);
> +
> +	priv->is_auto_deep_sleep_enabled = 0;
> +	priv->auto_deep_sleep_timeout = 0;
> +	del_timer(&priv->auto_deepsleep_timer);
> +
> +	lbs_deb_leave(LBS_DEB_SDIO);
> +	return 0;
> +}
> +
>  static void lbs_sync_channel_worker(struct work_struct *work)
>  {
>  	struct lbs_private *priv = container_of(work, struct lbs_private,
> @@ -1099,11 +1169,17 @@ static int lbs_init_adapter(struct lbs_private *priv)
>  	priv->capability = WLAN_CAPABILITY_SHORT_PREAMBLE;
>  	priv->psmode = LBS802_11POWERMODECAM;
>  	priv->psstate = PS_STATE_FULL_POWER;
> +	priv->is_deep_sleep = 0;
> +	priv->is_auto_deep_sleep_enabled = 0;
> +	priv->wakeup_dev_required = 0;
> +	init_waitqueue_head(&priv->ds_awake_q);
>  
>  	mutex_init(&priv->lock);
>  
>  	setup_timer(&priv->command_timer, command_timer_fn,
>  		(unsigned long)priv);
> +	setup_timer(&priv->auto_deepsleep_timer, auto_deepsleep_timer_fn,
> +			(unsigned long)priv);
>  
>  	INIT_LIST_HEAD(&priv->cmdfreeq);
>  	INIT_LIST_HEAD(&priv->cmdpendingq);
> @@ -1142,6 +1218,7 @@ static void lbs_free_adapter(struct lbs_private *priv)
>  	if (priv->event_fifo)
>  		kfifo_free(priv->event_fifo);
>  	del_timer(&priv->command_timer);
> +	del_timer(&priv->auto_deepsleep_timer);
>  	kfree(priv->networks);
>  	priv->networks = NULL;
>  
> @@ -1272,6 +1349,11 @@ void lbs_remove_card(struct lbs_private *priv)
>  	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
>  	wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
>  
> +	if (priv->is_deep_sleep) {
> +		priv->is_deep_sleep = 0;
> +		wake_up_interruptible(&priv->ds_awake_q);
> +	}
> +
>  	/* Stop the thread servicing the interrupts */
>  	priv->surpriseremoved = 1;
>  	kthread_stop(priv->main_thread);
> @@ -1392,6 +1474,7 @@ void lbs_stop_card(struct lbs_private *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);
> diff --git a/drivers/net/wireless/libertas/scan.c b/drivers/net/wireless/libertas/scan.c
> index 6c95af3..e468e15 100644
> --- a/drivers/net/wireless/libertas/scan.c
> +++ b/drivers/net/wireless/libertas/scan.c
> @@ -950,6 +950,11 @@ int lbs_set_scan(struct net_device *dev, struct iw_request_info *info,
>  
>  	lbs_deb_enter(LBS_DEB_WEXT);
>  
> +	if (!lbs_is_cmd_allowed(priv)) {
> +		ret = -EBUSY;
> +		goto out;
> +	}
> +
>  	if (!priv->radio_on) {
>  		ret = -EINVAL;
>  		goto out;
> @@ -1017,6 +1022,12 @@ int lbs_get_scan(struct net_device *dev, struct iw_request_info *info,
>  
>  	lbs_deb_enter(LBS_DEB_WEXT);
>  
> +	if (!lbs_is_cmd_allowed(priv)) {
> +		err = -EBUSY;
> +		lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", err);
> +		return err;
> +	}
> +
>  	/* iwlist should wait until the current scan is finished */
>  	if (priv->scan_channel)
>  		return -EAGAIN;
> diff --git a/drivers/net/wireless/libertas/wext.c b/drivers/net/wireless/libertas/wext.c
> index be837a0..ef2b986 100644
> --- a/drivers/net/wireless/libertas/wext.c
> +++ b/drivers/net/wireless/libertas/wext.c
> @@ -45,6 +45,31 @@ static inline void lbs_cancel_association_work(struct lbs_private *priv)
>  	priv->pending_assoc_req = NULL;
>  }
>  
> +/**
> + *  @brief This function checks if the command is allowed.
> + *
> + *  @param priv         A pointer to lbs_private structure
> + *  @return             allowed or not allowed.
> + */
> +
> +int lbs_is_cmd_allowed(struct lbs_private *priv)
> +{
> +	int         ret = 1;
> +
> +	lbs_deb_enter(LBS_DEB_WEXT);
> +
> +	if (!priv->is_auto_deep_sleep_enabled) {
> +		if (priv->is_deep_sleep) {
> +			lbs_deb_wext("IOCTLS called when station"
> +					"is in deep sleep\n");
> +			ret = 0;
> +		}
> +	}
> +
> +	lbs_deb_leave(LBS_DEB_WEXT);
> +	return ret;
> +}
> +
>  
>  /**
>   *  @brief Find the channel frequency power info with specific channel
> @@ -168,6 +193,11 @@ static int lbs_get_freq(struct net_device *dev, struct iw_request_info *info,
>  
>  	lbs_deb_enter(LBS_DEB_WEXT);
>  
> +	if (!lbs_is_cmd_allowed(priv)) {
> +		lbs_deb_leave(LBS_DEB_WEXT);
> +		return -EBUSY;
> +	}
> +
>  	cfp = lbs_find_cfp_by_band_and_channel(priv, 0,
>  					   priv->curbssparams.channel);
>  
> @@ -278,6 +308,12 @@ static int lbs_set_rts(struct net_device *dev, struct iw_request_info *info,
>  
>  	lbs_deb_enter(LBS_DEB_WEXT);
>  
> +	if (!lbs_is_cmd_allowed(priv)) {
> +		ret = -EBUSY;
> +		lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
> +		return ret;
> +	}
> +
>  	if (vwrq->disabled)
>  		val = MRVDRV_RTS_MAX_VALUE;
>  
> @@ -299,6 +335,11 @@ static int lbs_get_rts(struct net_device *dev, struct iw_request_info *info,
>  
>  	lbs_deb_enter(LBS_DEB_WEXT);
>  
> +	if (!lbs_is_cmd_allowed(priv)) {
> +		ret = -EBUSY;
> +		goto out;
> +	}
> +
>  	ret = lbs_get_snmp_mib(priv, SNMP_MIB_OID_RTS_THRESHOLD, &val);
>  	if (ret)
>  		goto out;
> @@ -321,6 +362,12 @@ static int lbs_set_frag(struct net_device *dev, struct iw_request_info *info,
>  
>  	lbs_deb_enter(LBS_DEB_WEXT);
>  
> +	if (!lbs_is_cmd_allowed(priv)) {
> +		ret = -EBUSY;
> +		lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
> +		return ret;
> +	}
> +
>  	if (vwrq->disabled)
>  		val = MRVDRV_FRAG_MAX_VALUE;
>  
> @@ -342,6 +389,11 @@ static int lbs_get_frag(struct net_device *dev, struct iw_request_info *info,
>  
>  	lbs_deb_enter(LBS_DEB_WEXT);
>  
> +	if (!lbs_is_cmd_allowed(priv)) {
> +		ret = -EBUSY;
> +		goto out;
> +	}
> +
>  	ret = lbs_get_snmp_mib(priv, SNMP_MIB_OID_FRAG_THRESHOLD, &val);
>  	if (ret)
>  		goto out;
> @@ -391,6 +443,11 @@ static int lbs_get_txpow(struct net_device *dev,
>  
>  	lbs_deb_enter(LBS_DEB_WEXT);
>  
> +	if (!lbs_is_cmd_allowed(priv)) {
> +		ret = -EBUSY;
> +		goto out;
> +	}
> +
>  	if (!priv->radio_on) {
>  		lbs_deb_wext("tx power off\n");
>  		vwrq->value = 0;
> @@ -424,6 +481,11 @@ static int lbs_set_retry(struct net_device *dev, struct iw_request_info *info,
>  
>  	lbs_deb_enter(LBS_DEB_WEXT);
>  
> +	if (!lbs_is_cmd_allowed(priv)) {
> +		ret = -EBUSY;
> +		goto out;
> +	}
> +
>          if ((vwrq->flags & IW_RETRY_TYPE) != IW_RETRY_LIMIT)
>                  return -EOPNOTSUPP;
>  
> @@ -472,6 +534,11 @@ static int lbs_get_retry(struct net_device *dev, struct iw_request_info *info,
>  
>  	lbs_deb_enter(LBS_DEB_WEXT);
>  
> +	if (!lbs_is_cmd_allowed(priv)) {
> +		ret = -EBUSY;
> +		goto out;
> +	}
> +
>  	vwrq->disabled = 0;
>  
>  	if (vwrq->flags & IW_RETRY_LONG) {
> @@ -712,6 +779,11 @@ static int lbs_set_power(struct net_device *dev, struct iw_request_info *info,
>  
>  	lbs_deb_enter(LBS_DEB_WEXT);
>  
> +	if (!lbs_is_cmd_allowed(priv)) {
> +		lbs_deb_leave(LBS_DEB_WEXT);
> +		return -EBUSY;
> +	}
> +
>  	if (!(priv->fwcapinfo & FW_CAPINFO_PS)) {
>  		if (vwrq->disabled)
>  			return 0;
> @@ -792,6 +864,9 @@ static struct iw_statistics *lbs_get_wireless_stats(struct net_device *dev)
>  
>  	lbs_deb_enter(LBS_DEB_WEXT);
>  
> +	if (!lbs_is_cmd_allowed(priv))
> +		return NULL;
> +
>  	priv->wstats.status = priv->mode;
>  
>  	/* If we're not associated, all quality values are meaningless */
> @@ -892,6 +967,12 @@ static int lbs_set_freq(struct net_device *dev, struct iw_request_info *info,
>  
>  	lbs_deb_enter(LBS_DEB_WEXT);
>  
> +	if (!lbs_is_cmd_allowed(priv)) {
> +		ret = -EBUSY;
> +		lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
> +		return ret;
> +	}
> +
>  	mutex_lock(&priv->lock);
>  	assoc_req = lbs_get_association_request(priv);
>  	if (!assoc_req) {
> @@ -1000,6 +1081,12 @@ static int lbs_set_rate(struct net_device *dev, struct iw_request_info *info,
>  	u8 rates[MAX_RATES + 1];
>  
>  	lbs_deb_enter(LBS_DEB_WEXT);
> +
> +	if (!lbs_is_cmd_allowed(priv)) {
> +		ret = -EBUSY;
> +		goto out;
> +	}
> +
>  	lbs_deb_wext("vwrq->value %d\n", vwrq->value);
>  	lbs_deb_wext("vwrq->fixed %d\n", vwrq->fixed);
>  
> @@ -1058,6 +1145,11 @@ static int lbs_get_rate(struct net_device *dev, struct iw_request_info *info,
>  
>  	lbs_deb_enter(LBS_DEB_WEXT);
>  
> +	if (!lbs_is_cmd_allowed(priv)) {
> +		lbs_deb_leave(LBS_DEB_WEXT);
> +		return -EBUSY;
> +	}
> +
>  	if (priv->connect_status == LBS_CONNECTED) {
>  		vwrq->value = priv->cur_rate * 500000;
>  
> @@ -1084,6 +1176,11 @@ static int lbs_set_mode(struct net_device *dev,
>  
>  	lbs_deb_enter(LBS_DEB_WEXT);
>  
> +	if (!lbs_is_cmd_allowed(priv)) {
> +		ret = -EBUSY;
> +		goto out;
> +	}
> +
>  	if (   (*uwrq != IW_MODE_ADHOC)
>  	    && (*uwrq != IW_MODE_INFRA)
>  	    && (*uwrq != IW_MODE_AUTO)) {
> @@ -1325,6 +1422,12 @@ static int lbs_set_encode(struct net_device *dev,
>  
>  	lbs_deb_enter(LBS_DEB_WEXT);
>  
> +	if (!lbs_is_cmd_allowed(priv)) {
> +		ret = -EBUSY;
> +		lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
> +		return ret;
> +	}
> +
>  	mutex_lock(&priv->lock);
>  	assoc_req = lbs_get_association_request(priv);
>  	if (!assoc_req) {
> @@ -1508,6 +1611,12 @@ static int lbs_set_encodeext(struct net_device *dev,
>  
>  	lbs_deb_enter(LBS_DEB_WEXT);
>  
> +	if (!lbs_is_cmd_allowed(priv)) {
> +		ret = -EBUSY;
> +		lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
> +		return ret;
> +	}
> +
>  	mutex_lock(&priv->lock);
>  	assoc_req = lbs_get_association_request(priv);
>  	if (!assoc_req) {
> @@ -1720,6 +1829,12 @@ static int lbs_set_auth(struct net_device *dev,
>  
>  	lbs_deb_enter(LBS_DEB_WEXT);
>  
> +	if (!lbs_is_cmd_allowed(priv)) {
> +		ret = -EBUSY;
> +		lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
> +		return ret;
> +	}
> +
>  	mutex_lock(&priv->lock);
>  	assoc_req = lbs_get_association_request(priv);
>  	if (!assoc_req) {
> @@ -1822,6 +1937,12 @@ static int lbs_get_auth(struct net_device *dev,
>  
>  	lbs_deb_enter(LBS_DEB_WEXT);
>  
> +	if (!lbs_is_cmd_allowed(priv)) {
> +		ret = -EBUSY;
> +		lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
> +		return ret;
> +	}
> +
>  	switch (dwrq->flags & IW_AUTH_INDEX) {
>  	case IW_AUTH_KEY_MGMT:
>  		dwrq->value = priv->secinfo.key_mgmt;
> @@ -1864,6 +1985,11 @@ static int lbs_set_txpow(struct net_device *dev, struct iw_request_info *info,
>  
>  	lbs_deb_enter(LBS_DEB_WEXT);
>  
> +	if (!lbs_is_cmd_allowed(priv)) {
> +		ret = -EBUSY;
> +		goto out;
> +	}
> +
>  	if (vwrq->disabled) {
>  		lbs_set_radio(priv, RADIO_PREAMBLE_AUTO, 0);
>  		goto out;
> @@ -1983,6 +2109,12 @@ static int lbs_set_essid(struct net_device *dev, struct iw_request_info *info,
>  
>  	lbs_deb_enter(LBS_DEB_WEXT);
>  
> +	if (!lbs_is_cmd_allowed(priv)) {
> +		ret = -EBUSY;
> +		lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
> +		return ret;
> +	}
> +
>  	if (!priv->radio_on) {
>  		ret = -EINVAL;
>  		goto out;
> @@ -2110,6 +2242,12 @@ static int lbs_set_wap(struct net_device *dev, struct iw_request_info *info,
>  
>  	lbs_deb_enter(LBS_DEB_WEXT);
>  
> +	if (!lbs_is_cmd_allowed(priv)) {
> +		ret = -EBUSY;
> +		lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
> +		return ret;
> +	}
> +
>  	if (!priv->radio_on)
>  		return -EINVAL;
>  



From dwmw2 at infradead.org  Sun Sep 20 14:25:05 2009
From: dwmw2 at infradead.org (David Woodhouse)
Date: Sun, 20 Sep 2009 11:25:05 -0700
Subject: [PATCH 1/3] libertas: support boot commands to write
	persistent firmware and bootloader
In-Reply-To: <487f84aa.1f068e0a.7ee7.3360@mx.google.com>
References: <487f84aa.1f068e0a.7ee7.3360@mx.google.com>
Message-ID: <1253471105.6317.167.camel@macbook.infradead.org>

On Tue, 2008-07-15 at 17:45 -0700, Brian Cavagnolo wrote:
> Add locking and non-locking versions of if_usb_prog_firmware to support
> programming firmware after and before driver bring-up respectively.
> Add more suitable error codes for firmware programming process.
> Add capability checks for persistent features before attempting to use them.
> 
> Based on patches from Brajesh Dave and Priyank Singh.
> 
> Signed-off-by: Brian Cavagnolo <brian at cozybit.com>

Grrrr. We already ripped this _out_ of the driver once, and Dan
implemented a tool to do it in userspace where it belongs.

What was wrong with using the tool at git.infradead.org/libertas-flash? 

-- 
David Woodhouse                            Open Source Technology Centre
David.Woodhouse at intel.com                              Intel Corporation



From dongas86 at gmail.com  Mon Sep 21 00:03:33 2009
From: dongas86 at gmail.com (Dongas)
Date: Mon, 21 Sep 2009 12:03:33 +0800
Subject: Suspend/resume without bringing down SDIO
In-Reply-To: <20090906153244.5b9ba70a@mjolnir.ossman.eu>
References: <m3iqh4k2u0.fsf@pullcord.laptop.org>
	<20090906153244.5b9ba70a@mjolnir.ossman.eu>
Message-ID: <60ce8d250909202103t6252fca5teb26d2490103eacf@mail.gmail.com>

2009/9/6, Pierre Ossman <pierre at ossman.eu>:
> On Mon, 03 Aug 2009 17:24:55 -0400
> Chris Ball <cjb at laptop.org> wrote:
>
> > Hi Pierre, and libertas-dev,
> >
> > We're using a Marvell SDIO 8686 wifi module in the new OLPC XO-1.5,
> > and need the SDIO card to stay up across suspend so that it can wake
> > us with an SDIO interrupt when a packet arrives while we're suspended.
> > (We do this with the USB 8388 on the XO-1, too.)
> >
> > I see that the SD stack doesn't yet have support for per-driver
> > suspend/resume hooks, though.  We're using a standard sdhci host,
> > which we keep powered during suspend.  Any suggestions on what we
> > should work on, or pointers to patches that accomplish these if
> > they exist already?  It sounds like we're looking for:
> >
> > * a way to tell the core not to remove the card on the way into
> >   suspend; it's always going to be present when we resume.
>
> The missing key here really is that the MMC layer never grew the
> functionality to keep cards powered during suspend. Once it has that it
> can just poke the card after resume to see if it is still running, and
> only do a reinit if that fails.

Is there any story behind that why all cards must be powered down
during suspend?

There're real cases that some SDIO WiFi cards indeed DO NOT like to be
powered down during suspend, e.g they want to enter deep sleep.

Thanks

Regards
Dongas

> > * a way to register driver hooks to call suspend/resume functions
> >   in the same way current libertas if_usb.c does.
>
> That would be sensible once the above is fixed.
>
> Rgds
> --
>     -- Pierre Ossman
>
>  WARNING: This correspondence is being monitored by the
>  Swedish government. Make sure your server uses encryption
>  for SMTP traffic and consider using PGP for end-to-end
>  encryption.
>
> _______________________________________________
> libertas-dev mailing list
> libertas-dev at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/libertas-dev
>
>
>


From cjb at laptop.org  Mon Sep 21 00:11:16 2009
From: cjb at laptop.org (Chris Ball)
Date: Mon, 21 Sep 2009 00:11:16 -0400
Subject: Suspend/resume without bringing down SDIO
In-Reply-To: <60ce8d250909202103t6252fca5teb26d2490103eacf@mail.gmail.com>
	(Dongas's message of "Mon, 21 Sep 2009 12:03:33 +0800")
References: <m3iqh4k2u0.fsf@pullcord.laptop.org>
	<20090906153244.5b9ba70a@mjolnir.ossman.eu>
	<60ce8d250909202103t6252fca5teb26d2490103eacf@mail.gmail.com>
Message-ID: <m33a6hos2j.fsf@pullcord.laptop.org>

Hi,

   > Is there any story behind that why all cards must be powered down
   > during suspend?

Just that the common case for SD cards is that they contain filesystems,
and those cards should be unmounted and powered off during suspend
because there's no point in leaving them on, and it can be dangerous
to the filesystem to do so.  The Linux SD stack is still new enough
that it hasn't yet grown the ability to differentiate cards that
should stay powered in suspend and cards that shouldn't.

(That said, see Nicolas Pitre's recent patches on the linux-mmc@ list,
which attempt to solve this SDIO problem.)

   > There're real cases that some SDIO WiFi cards indeed DO NOT like
   > to be powered down during suspend, e.g they want to enter deep
   > sleep.

Yes.

- Chris.
-- 
Chris Ball   <cjb at laptop.org>
One Laptop Per Child


From dongas86 at gmail.com  Mon Sep 21 00:59:17 2009
From: dongas86 at gmail.com (Dongas)
Date: Mon, 21 Sep 2009 12:59:17 +0800
Subject: Suspend/resume without bringing down SDIO
In-Reply-To: <m33a6hos2j.fsf@pullcord.laptop.org>
References: <m3iqh4k2u0.fsf@pullcord.laptop.org>
	<20090906153244.5b9ba70a@mjolnir.ossman.eu>
	<60ce8d250909202103t6252fca5teb26d2490103eacf@mail.gmail.com>
	<m33a6hos2j.fsf@pullcord.laptop.org>
Message-ID: <60ce8d250909202159t1f0edfceo4afac7707bf34df5@mail.gmail.com>

2009/9/21, Chris Ball <cjb at laptop.org>:
> Hi,
>
>   > Is there any story behind that why all cards must be powered down
>   > during suspend?
>
> Just that the common case for SD cards is that they contain filesystems,
> and those cards should be unmounted and powered off during suspend
> because there's no point in leaving them on, and it can be dangerous
> to the filesystem to do so.  The Linux SD stack is still new enough
> that it hasn't yet grown the ability to differentiate cards that
> should stay powered in suspend and cards that shouldn't.
Got it.

> (That said, see Nicolas Pitre's recent patches on the linux-mmc@ list,
> which attempt to solve this SDIO problem.)

Thanks.
I will check it.

Regards
Dognas

>   > There're real cases that some SDIO WiFi cards indeed DO NOT like
>   > to be powered down during suspend, e.g they want to enter deep
>   > sleep.
>
> Yes.
>
> - Chris.
> --
> Chris Ball   <cjb at laptop.org>
> One Laptop Per Child
>


From irretrievablefqmgtztnlkme at ferreras.com.ar  Tue Sep 22 19:18:17 2009
From: irretrievablefqmgtztnlkme at ferreras.com.ar (Jarrett Kaufman)
Date: 23 Sep 2009 01:18:17 +0200
Subject: Looking for lists?
Message-ID: <DC01BlrrAZ6T1F2Gae80000143a@ramservice.info>




Beat the Recession - get more new clients now.
Medical Community Contact Lists that will generate results:
Doctors, Dentists, Chiropractors, Hospitals etc.. 

Drop me a line here for more info & samples: Tracey at bestandcheapest.us








send an email to exit at bestandcheapest.us to be taken off future mailing.



From h.schurig at mn-solutions.de  Mon Sep 28 07:21:21 2009
From: h.schurig at mn-solutions.de (Holger Schurig)
Date: Mon, 28 Sep 2009 13:21:21 +0200
Subject: [RFC] libertas: first stab at cfg80211 support
Message-ID: <200909281321.21600.h.schurig@mn-solutions.de>

Signed-off-by: Holger Schurig <hs4233 at mail.mn-solutions.de>

---

This patch currently just create a wdev, so you can do "iw list".

I'd like to get comments if the position where I create/destroy
the wdev makes sense.

Index: linux-wl/drivers/net/wireless/Kconfig
===================================================================
--- linux-wl.orig/drivers/net/wireless/Kconfig	2009-09-28 12:06:02.000000000 +0200
+++ linux-wl/drivers/net/wireless/Kconfig	2009-09-28 12:06:30.000000000 +0200
@@ -138,6 +138,7 @@ config LIBERTAS
 	depends on WLAN_80211
 	select WIRELESS_EXT
 	select LIB80211
+	select CFG80211
 	select FW_LOADER
 	---help---
 	  A library for Marvell Libertas 8xxx devices.
Index: linux-wl/drivers/net/wireless/libertas/Makefile
===================================================================
--- linux-wl.orig/drivers/net/wireless/libertas/Makefile	2009-09-28 12:06:02.000000000 +0200
+++ linux-wl/drivers/net/wireless/libertas/Makefile	2009-09-28 12:06:30.000000000 +0200
@@ -1,5 +1,5 @@
 libertas-objs := main.o wext.o rx.o tx.o cmd.o cmdresp.o scan.o 11d.o	\
-		 debugfs.o persistcfg.o ethtool.o assoc.o
+		 debugfs.o persistcfg.o ethtool.o assoc.o cfg.o
 
 usb8xxx-objs += if_usb.o
 libertas_cs-objs += if_cs.o
Index: linux-wl/drivers/net/wireless/libertas/dev.h
===================================================================
--- linux-wl.orig/drivers/net/wireless/libertas/dev.h	2009-09-28 12:06:02.000000000 +0200
+++ linux-wl/drivers/net/wireless/libertas/dev.h	2009-09-28 12:06:30.000000000 +0200
@@ -100,6 +100,7 @@ struct lbs_mesh_stats {
 
 /** Private structure for the MV device */
 struct lbs_private {
+	struct wireless_dev *wdev;
 	int mesh_open;
 	int mesh_fw_ver;
 	int infra_open;
Index: linux-wl/drivers/net/wireless/libertas/cfg.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-wl/drivers/net/wireless/libertas/cfg.h	2009-09-28 12:07:23.000000000 +0200
@@ -0,0 +1,9 @@
+#ifndef __LBS_CFG80211_H__
+#define __LBS_CFG80211_H__
+
+#include "dev.h"
+
+struct wireless_dev *lbs_wdev_alloc(int sizeof_priv, struct device *dev);
+void lbs_wdev_free(struct lbs_private *lbs);
+
+#endif
Index: linux-wl/drivers/net/wireless/libertas/cfg.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ linux-wl/drivers/net/wireless/libertas/cfg.c	2009-09-28 12:09:06.000000000 +0200
@@ -0,0 +1,160 @@
+/*
+ * Implement cfg80211 ("iw") support.
+ *
+ * Copyright (C) 2009 M&N Solutions GmbH, 61191 Rosbach, Germany
+ * Holger Schurig <hs4233 at mail.mn-solutions.de>
+ *
+ * Based on cfg80211.h:
+ *   Copyright (C) 2009 Intel Corporation <ilw at linux.intel.com>
+ *   Samuel Ortiz <samuel.ortiz at intel.com>
+ *   Zhu Yi <yi.zhu at intel.com>
+ */
+
+#include <net/cfg80211.h>
+
+#include "cfg.h"
+
+
+#define CHAN2G(_channel, _freq, _flags) {                       \
+	.band                   = IEEE80211_BAND_2GHZ,          \
+	.center_freq            = (_freq),                      \
+	.hw_value               = (_channel),                   \
+	.flags                  = (_flags),                     \
+	.max_antenna_gain       = 0,                            \
+	.max_power              = 30,                           \
+}
+
+static struct ieee80211_channel lbs_2ghz_channels[] = {
+	CHAN2G(1, 2412, 0),
+	CHAN2G(2, 2417, 0),
+	CHAN2G(3, 2422, 0),
+	CHAN2G(4, 2427, 0),
+	CHAN2G(5, 2432, 0),
+	CHAN2G(6, 2437, 0),
+	CHAN2G(7, 2442, 0),
+	CHAN2G(8, 2447, 0),
+	CHAN2G(9, 2452, 0),
+	CHAN2G(10, 2457, 0),
+	CHAN2G(11, 2462, 0),
+	CHAN2G(12, 2467, 0),
+	CHAN2G(13, 2472, 0),
+	CHAN2G(14, 2484, 0),
+};
+
+#define RATETAB_ENT(_rate, _rateid, _flags) \
+	{                                                               \
+		.bitrate        = (_rate),                              \
+		.hw_value       = (_rateid),                            \
+		.flags          = (_flags),                             \
+	}
+
+
+static struct ieee80211_rate lbs_rates[] = {
+	RATETAB_ENT(10,  0x1,   0),
+	RATETAB_ENT(20,  0x2,   0),
+	RATETAB_ENT(55,  0x4,   0),
+	RATETAB_ENT(110, 0x8,   0),
+	RATETAB_ENT(60,  0x10,  0),
+	RATETAB_ENT(90,  0x20,  0),
+	RATETAB_ENT(120, 0x40,  0),
+	RATETAB_ENT(180, 0x80,  0),
+	RATETAB_ENT(240, 0x100, 0),
+	RATETAB_ENT(360, 0x200, 0),
+	RATETAB_ENT(480, 0x400, 0),
+	RATETAB_ENT(540, 0x800, 0),
+};
+
+static struct ieee80211_supported_band lbs_band_2ghz = {
+	.channels = lbs_2ghz_channels,
+	.n_channels = ARRAY_SIZE(lbs_2ghz_channels),
+	.bitrates = lbs_rates,
+	.n_bitrates = ARRAY_SIZE(lbs_rates),
+};
+
+
+static const u32 cipher_suites[] = {
+	WLAN_CIPHER_SUITE_WEP40,
+	WLAN_CIPHER_SUITE_WEP104,
+	WLAN_CIPHER_SUITE_TKIP,
+	WLAN_CIPHER_SUITE_CCMP,
+};
+
+
+
+static struct cfg80211_ops lbs_cfg80211_ops = {
+/* TODO
+	.change_virtual_intf = iwm_cfg80211_change_iface,
+	.add_key = iwm_cfg80211_add_key,
+	.get_key = iwm_cfg80211_get_key,
+	.del_key = iwm_cfg80211_del_key,
+	.set_default_key = iwm_cfg80211_set_default_key,
+	.get_station = iwm_cfg80211_get_station,
+	.scan = iwm_cfg80211_scan,
+	.set_wiphy_params = iwm_cfg80211_set_wiphy_params,
+	.connect = iwm_cfg80211_connect,
+	.disconnect = iwm_cfg80211_disconnect,
+	.join_ibss = iwm_cfg80211_join_ibss,
+	.leave_ibss = iwm_cfg80211_leave_ibss,
+	.set_tx_power = iwm_cfg80211_set_txpower,
+	.get_tx_power = iwm_cfg80211_get_txpower,
+	.set_power_mgmt = iwm_cfg80211_set_power_mgmt,
+*/
+};
+
+struct wireless_dev *lbs_wdev_alloc(int sizeof_priv, struct device *dev)
+{
+	int ret = 0;
+	struct wireless_dev *wdev;
+
+	wdev = kzalloc(sizeof(struct wireless_dev), GFP_KERNEL);
+	if (!wdev) {
+		dev_err(dev, "Couldn't allocate wireless device\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	wdev->wiphy = wiphy_new(&lbs_cfg80211_ops,
+		sizeof(struct lbs_private) + sizeof_priv);
+	if (!wdev->wiphy) {
+		dev_err(dev, "Couldn't allocate wiphy device\n");
+		ret = -ENOMEM;
+		goto out_err_new;
+	}
+
+	set_wiphy_dev(wdev->wiphy, dev);
+	wdev->wiphy->max_scan_ssids = 1; /* TODO */
+	wdev->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
+		BIT(NL80211_IFTYPE_ADHOC);
+	wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &lbs_band_2ghz;
+	wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+	wdev->wiphy->cipher_suites = cipher_suites;
+	wdev->wiphy->n_cipher_suites = ARRAY_SIZE(cipher_suites);
+
+	ret = wiphy_register(wdev->wiphy);
+	if (ret < 0) {
+		dev_err(dev, "Couldn't register wiphy device\n");
+		goto out_err_register;
+	}
+
+	return wdev;
+
+ out_err_register:
+	wiphy_free(wdev->wiphy);
+
+ out_err_new:
+	kfree(wdev);
+
+	return ERR_PTR(ret);
+}
+
+
+void lbs_wdev_free(struct lbs_private *lbs)
+{
+	struct wireless_dev *wdev = lbs->wdev;
+
+	if (!wdev)
+		return;
+
+	wiphy_unregister(wdev->wiphy);
+	wiphy_free(wdev->wiphy);
+	kfree(wdev);
+}
Index: linux-wl/drivers/net/wireless/libertas/main.c
===================================================================
--- linux-wl.orig/drivers/net/wireless/libertas/main.c	2009-09-28 12:06:02.000000000 +0200
+++ linux-wl/drivers/net/wireless/libertas/main.c	2009-09-28 12:11:22.000000000 +0200
@@ -14,11 +14,13 @@
 #include <linux/stddef.h>
 #include <linux/ieee80211.h>
 #include <net/iw_handler.h>
+#include <net/cfg80211.h>
 
 #include "host.h"
 #include "decl.h"
 #include "dev.h"
 #include "wext.h"
+#include "cfg.h"
 #include "debugfs.h"
 #include "scan.h"
 #include "assoc.h"
@@ -1168,31 +1170,42 @@ static const struct net_device_ops lbs_n
  */
 struct lbs_private *lbs_add_card(void *card, struct device *dmdev)
 {
-	struct net_device *dev = NULL;
+	struct net_device *dev;
+	struct wireless_dev *wdev;
 	struct lbs_private *priv = NULL;
 
 	lbs_deb_enter(LBS_DEB_MAIN);
 
 	/* Allocate an Ethernet device and register it */
-	dev = alloc_etherdev(sizeof(struct lbs_private));
-	if (!dev) {
+	wdev = lbs_wdev_alloc(sizeof(struct lbs_private), dmdev);
+	if (IS_ERR(wdev)) {
 		lbs_pr_err("init wlanX device failed\n");
 		goto done;
 	}
-	priv = netdev_priv(dev);
-	dev->ml_priv = priv;
+	/* TODO? */
+	wdev->iftype = NL80211_IFTYPE_STATION;
+	priv = wdev_priv(wdev);
+	priv->wdev = wdev;
 
 	if (lbs_init_adapter(priv)) {
 		lbs_pr_err("failed to initialize adapter structure.\n");
-		goto err_init_adapter;
+		goto err_wdev;
+	}
+
+	//TODO? dev = alloc_netdev_mq(0, "wlan%d", ether_setup, IWM_TX_QUEUES);
+	dev = alloc_netdev(0, "wlan%d", ether_setup);
+	if (!dev) {
+		dev_err(dmdev, "no memory for network device instance\n");
+		goto err_adapter;
 	}
 
+	dev->netdev_ops = &lbs_netdev_ops;
+	dev->ieee80211_ptr = wdev;
+	dev->ml_priv = priv;
+	SET_NETDEV_DEV(dev, dmdev);
+	wdev->netdev = dev;
 	priv->dev = dev;
-	priv->card = card;
-	priv->mesh_open = 0;
-	priv->infra_open = 0;
 
-	/* Setup the OS Interface to our functions */
  	dev->netdev_ops = &lbs_netdev_ops;
 	dev->watchdog_timeo = 5 * HZ;
 	dev->ethtool_ops = &lbs_ethtool_ops;
@@ -1201,7 +1214,14 @@ struct lbs_private *lbs_add_card(void *c
 #endif
 	dev->flags |= IFF_BROADCAST | IFF_MULTICAST;
 
-	SET_NETDEV_DEV(dev, dmdev);
+
+	// TODO: kzalloc + iwm_init_default_profile(iwm, iwm->umac_profile); ??
+
+
+	priv->card = card;
+	priv->mesh_open = 0;
+	priv->infra_open = 0;
+
 
 	priv->rtap_net_dev = NULL;
 	strcpy(dev->name, "wlan%d");
@@ -1211,7 +1231,7 @@ struct lbs_private *lbs_add_card(void *c
 	priv->main_thread = kthread_run(lbs_thread, dev, "lbs_main");
 	if (IS_ERR(priv->main_thread)) {
 		lbs_deb_thread("Error creating main thread.\n");
-		goto err_init_adapter;
+		goto err_ndev;
 	}
 
 	priv->work_thread = create_singlethread_workqueue("lbs_worker");
@@ -1228,9 +1248,15 @@ struct lbs_private *lbs_add_card(void *c
 
 	goto done;
 
-err_init_adapter:
-	lbs_free_adapter(priv);
+ err_ndev:
 	free_netdev(dev);
+
+ err_adapter:
+	lbs_free_adapter(priv);
+
+ err_wdev:
+	lbs_wdev_free(priv);
+
 	priv = NULL;
 
 done:
@@ -1277,6 +1303,7 @@ void lbs_remove_card(struct lbs_private 
 	kthread_stop(priv->main_thread);
 
 	lbs_free_adapter(priv);
+	lbs_wdev_free(priv);
 
 	priv->dev = NULL;
 	free_netdev(dev);

-- 
M&N Solutions GmbH          Ein Unternehmen der Datagroup AG
Holger Schurig
Raiffeisenstr. 10
61191 Rosbach
Tel: 06003/9141-15          Fax 06003/9141-49
http://www.mn-solutions.de/

Handelsregister Friedberg, HRB 5903
Gesch?ftsf?hrer: P.Schrittenlocher


From johannes at sipsolutions.net  Mon Sep 28 09:31:54 2009
From: johannes at sipsolutions.net (Johannes Berg)
Date: Mon, 28 Sep 2009 15:31:54 +0200
Subject: [RFC] libertas: first stab at cfg80211 support
In-Reply-To: <200909281321.21600.h.schurig@mn-solutions.de>
References: <200909281321.21600.h.schurig@mn-solutions.de>
Message-ID: <1254144714.3151.1.camel@johannes.local>

On Mon, 2009-09-28 at 13:21 +0200, Holger Schurig wrote:
> Signed-off-by: Holger Schurig <hs4233 at mail.mn-solutions.de>
> 
> ---
> 
> This patch currently just create a wdev, so you can do "iw list".
> 
> I'd like to get comments if the position where I create/destroy
> the wdev makes sense.
> 
> Index: linux-wl/drivers/net/wireless/Kconfig
> ===================================================================
> --- linux-wl.orig/drivers/net/wireless/Kconfig	2009-09-28 12:06:02.000000000 +0200
> +++ linux-wl/drivers/net/wireless/Kconfig	2009-09-28 12:06:30.000000000 +0200
> @@ -138,6 +138,7 @@ config LIBERTAS
>  	depends on WLAN_80211
>  	select WIRELESS_EXT
>  	select LIB80211
> +	select CFG80211
>  	select FW_LOADER
>  	---help---
>  	  A library for Marvell Libertas 8xxx devices.
> Index: linux-wl/drivers/net/wireless/libertas/Makefile
> ===================================================================
> --- linux-wl.orig/drivers/net/wireless/libertas/Makefile	2009-09-28 12:06:02.000000000 +0200
> +++ linux-wl/drivers/net/wireless/libertas/Makefile	2009-09-28 12:06:30.000000000 +0200
> @@ -1,5 +1,5 @@
>  libertas-objs := main.o wext.o rx.o tx.o cmd.o cmdresp.o scan.o 11d.o	\
> -		 debugfs.o persistcfg.o ethtool.o assoc.o
> +		 debugfs.o persistcfg.o ethtool.o assoc.o cfg.o
>  
>  usb8xxx-objs += if_usb.o
>  libertas_cs-objs += if_cs.o
> Index: linux-wl/drivers/net/wireless/libertas/dev.h
> ===================================================================
> --- linux-wl.orig/drivers/net/wireless/libertas/dev.h	2009-09-28 12:06:02.000000000 +0200
> +++ linux-wl/drivers/net/wireless/libertas/dev.h	2009-09-28 12:06:30.000000000 +0200
> @@ -100,6 +100,7 @@ struct lbs_mesh_stats {
>  
>  /** Private structure for the MV device */
>  struct lbs_private {
> +	struct wireless_dev *wdev;
>  	int mesh_open;
>  	int mesh_fw_ver;
>  	int infra_open;

This seems wrong -- doesn't libertas have multiple netdevs? You're
treating wdev == wiphy it seems here, rather than wdev == netdev.

johannes
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 801 bytes
Desc: This is a digitally signed message part
URL: <http://lists.infradead.org/pipermail/libertas-dev/attachments/20090928/a2ee9d90/attachment.sig>

From h.schurig at mn-solutions.de  Mon Sep 28 10:54:08 2009
From: h.schurig at mn-solutions.de (Holger Schurig)
Date: Mon, 28 Sep 2009 16:54:08 +0200
Subject: [RFC] libertas: first stab at cfg80211 support
In-Reply-To: <1254144714.3151.1.camel@johannes.local>
References: <200909281321.21600.h.schurig@mn-solutions.de>
	<1254144714.3151.1.camel@johannes.local>
Message-ID: <200909281654.08859.h.schurig@mn-solutions.de>

> This seems wrong -- doesn't libertas have multiple netdevs?
> You're treating wdev == wiphy it seems here, rather than wdev
> == netdev. 

Yes, it has three netdevs, but they aren't all setup at 
card-initialization time.


Some details: "struct private" contains:

        struct net_device *dev;
        struct net_device *mesh_dev; /* Virtual device */
        struct net_device *rtap_net_dev;

The old code did have an

        dev = alloc_etherdev(sizeof(struct lbs_private));"

and the other two netdevs got initialized at some other location:

* priv->lbs_add_mesh() in lbs_add_mesh()
  via "alloc_netdev(0, "msh%d", ether_setup)". And lbs_add_mesh()
  is called only when the hardware supports this.
* rtap_net_dev is created in lbs_add_rtap() and again I haven't
  change the creation of this thingy.

Please note that when I'm using the card in station mode, both 
mesh_dev and rtap_net_dev are NULL. My firmware doesn't have 
mesh code, and only when I set the card into monitor mode will 
the rtap_net_dev created.


I kept this behavior for now: at initialzation I call:

        wdev = lbs_wdev_alloc(sizeof(struct lbs_private), dmdev);
	wdev->xyz = initialization;
        ...
        dev = alloc_netdev()


For me it's not clear if I should create wdev's for mesh_dev and 
rtap_net_dev as well. However, I'm not able to check the mesh 
code anyway: my firmware doesn't support this. And for 
rtap_net_dev I'm not sure if this shouldn't be handled 
differently in a cfg80211 scenario, e.g. via 
lbs_cfg80211_change_iface().

A hint of what I should then would be most welcome :-)


-- 
M&N Solutions GmbH          Ein Unternehmen der Datagroup AG
Holger Schurig
Raiffeisenstr. 10
61191 Rosbach
Tel: 06003/9141-15          Fax 06003/9141-49
http://www.mn-solutions.de/

Handelsregister Friedberg, HRB 5903
Gesch?ftsf?hrer: P.Schrittenlocher


From johannes at sipsolutions.net  Mon Sep 28 10:57:37 2009
From: johannes at sipsolutions.net (Johannes Berg)
Date: Mon, 28 Sep 2009 16:57:37 +0200
Subject: [RFC] libertas: first stab at cfg80211 support
In-Reply-To: <200909281654.08859.h.schurig@mn-solutions.de>
References: <200909281321.21600.h.schurig@mn-solutions.de>
	<1254144714.3151.1.camel@johannes.local>
	<200909281654.08859.h.schurig@mn-solutions.de>
Message-ID: <1254149857.3151.5.camel@johannes.local>

On Mon, 2009-09-28 at 16:54 +0200, Holger Schurig wrote:

> Yes, it has three netdevs, but they aren't all setup at 
> card-initialization time.
> 
> 
> Some details: "struct private" contains:
> 
>         struct net_device *dev;
>         struct net_device *mesh_dev; /* Virtual device */
>         struct net_device *rtap_net_dev;
> 
> The old code did have an
> 
>         dev = alloc_etherdev(sizeof(struct lbs_private));"
> 
> and the other two netdevs got initialized at some other location:
> 
> * priv->lbs_add_mesh() in lbs_add_mesh()
>   via "alloc_netdev(0, "msh%d", ether_setup)". And lbs_add_mesh()
>   is called only when the hardware supports this.
> * rtap_net_dev is created in lbs_add_rtap() and again I haven't
>   change the creation of this thingy.
> 
> Please note that when I'm using the card in station mode, both 
> mesh_dev and rtap_net_dev are NULL. My firmware doesn't have 
> mesh code, and only when I set the card into monitor mode will 
> the rtap_net_dev created.
> 
> 
> I kept this behavior for now: at initialzation I call:
> 
>         wdev = lbs_wdev_alloc(sizeof(struct lbs_private), dmdev);
> 	wdev->xyz = initialization;
>         ...
>         dev = alloc_netdev()
> 
> 
> For me it's not clear if I should create wdev's for mesh_dev and 
> rtap_net_dev as well. However, I'm not able to check the mesh 
> code anyway: my firmware doesn't support this. And for 
> rtap_net_dev I'm not sure if this shouldn't be handled 
> differently in a cfg80211 scenario, e.g. via 
> lbs_cfg80211_change_iface().

Interesting.

> A hint of what I should then would be most welcome :-)

It might make sense to support adding interfaces with nl80211, since
apparently it has mesh and monitor interfaces.

johannes
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 801 bytes
Desc: This is a digitally signed message part
URL: <http://lists.infradead.org/pipermail/libertas-dev/attachments/20090928/d75011db/attachment.sig>

From h.schurig at mn-solutions.de  Mon Sep 28 11:07:59 2009
From: h.schurig at mn-solutions.de (Holger Schurig)
Date: Mon, 28 Sep 2009 17:07:59 +0200
Subject: [RFC] libertas: first stab at cfg80211 support
In-Reply-To: <1254149857.3151.5.camel@johannes.local>
References: <200909281321.21600.h.schurig@mn-solutions.de>
	<200909281654.08859.h.schurig@mn-solutions.de>
	<1254149857.3151.5.camel@johannes.local>
Message-ID: <200909281707.59846.h.schurig@mn-solutions.de>

> It might make sense to support adding interfaces with nl80211,
> since apparently it has mesh and monitor interfaces.

Yes, this will eventually be added.


In the end I'd like to have all WEXT code in it's own file or 
#ifdef'd and I'd like to have a working libertas_cs and 
libertas_usb that don't need WEXT at all. But that will take 
considerable time :-)   One step towards this goal will be 
monitor mode via NL80211.

Mesh-support via NL80211 must be done by someone else, I don't 
have neither the knowledge, mesh nodes nor a mesh-capable 
firmware. And I don't really care about mesh. So if no one else 
does it, the CFG80211-only libertas-driver won't have mesh at 
all.

-- 
M&N Solutions GmbH          Ein Unternehmen der Datagroup AG
Holger Schurig
Raiffeisenstr. 10
61191 Rosbach
Tel: 06003/9141-15          Fax 06003/9141-49
http://www.mn-solutions.de/

Handelsregister Friedberg, HRB 5903
Gesch?ftsf?hrer: P.Schrittenlocher


From hs4233 at mail.mn-solutions.de  Mon Sep 28 11:24:59 2009
From: hs4233 at mail.mn-solutions.de (Holger Schurig)
Date: Mon, 28 Sep 2009 17:24:59 +0200
Subject: [RFC] libertas: first stab at cfg80211 support
In-Reply-To: <200909281321.21600.h.schurig@mn-solutions.de>
References: <200909281321.21600.h.schurig@mn-solutions.de>
Message-ID: <200909281724.59854.hs4233@mail.mn-solutions.de>

Hmm, I see two problems with my code:

> +struct wireless_dev *lbs_wdev_alloc(int sizeof_priv, struct 
device *dev)
> +{
...
> +	wdev->wiphy = wiphy_new(&lbs_cfg80211_ops,
> +		sizeof(struct lbs_private) + sizeof_priv);
...

and later:

> +	wdev = lbs_wdev_alloc(sizeof(struct lbs_private), dmdev);

That's rubbish, I now allocate "sizeof(lbs_private)" bytes twice.



Another thing is that it might be necessary to unbundle the
call of "wiphy_new()" and wiphy_register().

I need lbs_private allocated and usable, e.g. in 
lbs_start_card().

Later, in lbs_start_card(), when I query the firmware about it's 
capabilites I know enought to populate wiphy->bands. Only then 
should I call wiphy_register().

-- 
http://www.holgerschurig.de


From bzhao at marvell.com  Mon Sep 28 18:42:22 2009
From: bzhao at marvell.com (Bing Zhao)
Date: Mon, 28 Sep 2009 15:42:22 -0700
Subject: [PATCH] libertas: Add auto deep sleep support for
	SD8385/SD8686/SD8688
In-Reply-To: <1253458691.11702.46.camel@localhost.localdomain>
References: <1253058359-1934-1-git-send-email-bzhao@marvell.com>
	<1253458691.11702.46.camel@localhost.localdomain>
Message-ID: <477F20668A386D41ADCC57781B1F704306DDA6C1B6@SC-VEXCH1.marvell.com>

Hi Dan,

> -----Original Message-----
> From: Dan Williams [mailto:dcbw at redhat.com]
> Sent: Sunday, September 20, 2009 7:58 AM
> To: Bing Zhao
> Cc: libertas-dev at lists.infradead.org; linux-wireless at vger.kernel.org; Amitkumar Karwar
> Subject: Re: [PATCH] libertas: Add auto deep sleep support for SD8385/SD8686/SD8688
>
> On Tue, 2009-09-15 at 16:45 -0700, Bing Zhao wrote:
> > From: Amitkumar Karwar <akarwar at marvell.com>
> >
> > Add timer based auto deep sleep feature in libertas driver which can be
> > configured through debugfs interface. This is tested on SD8688, SD8686 cards
> > with firmware versions 10.38.1.p25, 9.70.4.p0 respectively on 32-bit and 64-bit
> > platforms. Tests have been done for USB/CS cards to make sure that the patch
> > won't break USB/CS code. We didn't test the if_spi driver.
>
> So there's always:
>
> #define IW_POWER_SAVING               0x4000  /* Value is relative (how aggressive)*/
>
> iwconfig wlan0 power saving 10
>
> That seems quite a bit better than a new debugfs parameter, until we can
> conver the driver over cfg80211 and do this properly.  If the power
> saving mode is higher than some number X, the chip gets put into deep
> sleep mode, or it can be automatically placed in deep sleep mode by the
> driver when there is no association like you have done with the timer
> above.  I think you should pick reasonable defaults, and perhaps if the
> 'saving' value is larger, the timer value in the driver gets smaller.
>
> Does that sound like an OK approach?  We really should be using debugfs
> only for debugging stuff (obviously), and the real fix for this sort of
> thing is to switch over to cfg80211 where we can actually extend the
> configuration API instead of hacking it into debugfs/WEXT/etc.
>
> Dan

Thanks for your suggestion.

In latest kernel, IW_POWER_SAVING is not defined in include/linux/wireless.h.
IW_POWER_PERIOD and IW_POWER_TIMEOUT are defined though.

We are planning to remove debugfs for configurations and use the following commands instead.
Please let us know if there is any concern.

iwconfig wlan0 power period 0   -> enable deep sleep (enter deep sleep immediately)
iwconfig wlan0 power period 5   -> enable auto deep sleep (enter deep sleep automatically after 5s idle time)
iwconfig wlan0 power period -1  -> disable deep sleep / auto deep sleep

By the way, there is a bug in iwconfig tool v29. It doesn?t take any values right after "period" or "timeout". The new version of Wireless Tools v30-pre8 (link below) fixed the bug:

http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/wireless_tools.30.pre8.tar.gz

Thanks,

Bing

>
> > Signed-off-by: Amitkumar Karwar <akarwar at marvell.com>
> > Signed-off-by: Bing Zhao <bzhao at marvell.com>
> > ---
> >  drivers/net/wireless/libertas/README    |   28 +++++-
> >  drivers/net/wireless/libertas/cmd.c     |   72 +++++++++++++-
> >  drivers/net/wireless/libertas/cmdresp.c |   12 +++
> >  drivers/net/wireless/libertas/debugfs.c |  160 +++++++++++++++++++++++++++++++
> >  drivers/net/wireless/libertas/decl.h    |    4 +
> >  drivers/net/wireless/libertas/dev.h     |   18 ++++
> >  drivers/net/wireless/libertas/host.h    |    1 +
> >  drivers/net/wireless/libertas/if_cs.c   |    3 +
> >  drivers/net/wireless/libertas/if_sdio.c |   56 +++++++++++
> >  drivers/net/wireless/libertas/if_sdio.h |    3 +-
> >  drivers/net/wireless/libertas/if_spi.c  |    3 +
> >  drivers/net/wireless/libertas/if_usb.c  |    3 +
> >  drivers/net/wireless/libertas/main.c    |  111 +++++++++++++++++++---
> >  drivers/net/wireless/libertas/scan.c    |   11 ++
> >  drivers/net/wireless/libertas/wext.c    |  138 ++++++++++++++++++++++++++
> >  15 files changed, 604 insertions(+), 19 deletions(-)
> >
> > diff --git a/drivers/net/wireless/libertas/README b/drivers/net/wireless/libertas/README
> > index ab6a2d5..059ce8c 100644
> > --- a/drivers/net/wireless/libertas/README
> > +++ b/drivers/net/wireless/libertas/README
> > @@ -1,5 +1,5 @@
> >  ================================================================================
> > -                   README for USB8388
> > +                   README for Libertas
> >
> >   (c) Copyright ? 2003-2006, Marvell International Ltd.
> >   All Rights Reserved
> > @@ -226,4 +226,30 @@ setuserscan
> >      All entries in the scan table (not just the new scan data when keep=1)
> >      will be displayed upon completion by use of the getscantable ioctl.
> >
> > +deepsleep
> > +
> > +   This command is used to configure the station in deep sleep mode/auto
> > +   deep sleep mode. Command expects two parameters:
> > +   'state' 'idle time period'
> > +
> > +   The timer is implemented to monitor the activities (command, event,
> > +        data, 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 deepsleep mode is entered
> > +        automatically.
> > +
> > +   Note: this command is for SDIO interface only.
> > +
> > +   Path: /sys/kernel/debug/libertas_wireless/ethX/
> > +
> > +   Usage:
> > +   To read the current status of deep sleep do:
> > +           cat deepsleep
> > +   To enable deep sleep mode do:
> > +           echo '1 0' > deepsleep
> > +   To enable auto deep sleep mode with idle time period 5 seconds do:
> > +           echo '1 5000' > deepsleep
> > +   To disable deep sleep/auto deep sleep mode do:
> > +           echo '0 0' > deepsleep
> > +
> >  ==============================================================================
> > diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c
> > index 6850981..3a3e894 100644
> > --- a/drivers/net/wireless/libertas/cmd.c
> > +++ b/drivers/net/wireless/libertas/cmd.c
> > @@ -17,7 +17,6 @@
> >
> >  static struct cmd_ctrl_node *lbs_get_cmd_ctrl_node(struct lbs_private *priv);
> >
> > -
> >  /**
> >   *  @brief Simple callback that copies response back into command
> >   *
> > @@ -319,6 +318,60 @@ int lbs_cmd_802_11_sleep_params(struct lbs_private *priv, uint16_t cmd_action,
> >     return 0;
> >  }
> >
> > +static int lbs_wait_for_ds_awake(struct lbs_private *priv)
> > +{
> > +   int ret = 0;
> > +
> > +   lbs_deb_enter(LBS_DEB_CMD);
> > +
> > +   if (priv->is_deep_sleep) {
> > +           if (!wait_event_interruptible_timeout(priv->ds_awake_q,
> > +                                   !priv->is_deep_sleep, (10 * HZ))) {
> > +                   lbs_pr_err("ds_awake_q: timer expired\n");
> > +                   ret = -1;
> > +           }
> > +   }
> > +
> > +   lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
> > +   return ret;
> > +}
> > +
> > +int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep)
> > +{
> > +   int ret =  0;
> > +
> > +   lbs_deb_enter(LBS_DEB_CMD);
> > +
> > +   if (deep_sleep) {
> > +           if (priv->is_deep_sleep != 1) {
> > +                   lbs_deb_cmd("deep sleep: sleep\n");
> > +                   BUG_ON(!priv->enter_deep_sleep);
> > +                   ret = priv->enter_deep_sleep(priv);
> > +                   if (!ret) {
> > +                           netif_stop_queue(priv->dev);
> > +                           netif_carrier_off(priv->dev);
> > +                   }
> > +           } else {
> > +                   lbs_pr_err("deep sleep: already enabled\n");
> > +           }
> > +   } else {
> > +           if (priv->is_deep_sleep) {
> > +                   lbs_deb_cmd("deep sleep: wakeup\n");
> > +                   BUG_ON(!priv->exit_deep_sleep);
> > +                   ret = priv->exit_deep_sleep(priv);
> > +                   if (!ret) {
> > +                           ret = lbs_wait_for_ds_awake(priv);
> > +                           if (ret)
> > +                                   lbs_pr_err("deep sleep: wakeup"
> > +                                                   "failed\n");
> > +                   }
> > +           }
> > +   }
> > +
> > +   lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
> > +   return ret;
> > +}
> > +
> >  int lbs_cmd_802_11_set_wep(struct lbs_private *priv, uint16_t cmd_action,
> >                        struct assoc_request *assoc)
> >  {
> > @@ -1242,8 +1295,17 @@ static void lbs_submit_command(struct lbs_private *priv,
> >             timeo = HZ/4;
> >     }
> >
> > -   /* Setup the timer after transmit command */
> > -   mod_timer(&priv->command_timer, jiffies + timeo);
> > +   if (command == CMD_802_11_DEEP_SLEEP) {
> > +           if (priv->is_auto_deep_sleep_enabled) {
> > +                   priv->wakeup_dev_required = 1;
> > +                   priv->dnld_sent = 0;
> > +           }
> > +           priv->is_deep_sleep = 1;
> > +           lbs_complete_command(priv, cmdnode, 0);
> > +   } else {
> > +           /* Setup the timer after transmit command */
> > +           mod_timer(&priv->command_timer, jiffies + timeo);
> > +   }
> >
> >     lbs_deb_leave(LBS_DEB_HOST);
> >  }
> > @@ -1505,6 +1567,10 @@ int lbs_prepare_and_send_command(struct lbs_private *priv,
> >     case CMD_802_11_BEACON_CTRL:
> >             ret = lbs_cmd_bcn_ctrl(priv, cmdptr, cmd_action);
> >             break;
> > +   case CMD_802_11_DEEP_SLEEP:
> > +           cmdptr->command = cpu_to_le16(CMD_802_11_DEEP_SLEEP);
> > +           cmdptr->size = cpu_to_le16(S_DS_GEN);
> > +           break;
> >     default:
> >             lbs_pr_err("PREP_CMD: unknown command 0x%04x\n", cmd_no);
> >             ret = -1;
> > diff --git a/drivers/net/wireless/libertas/cmdresp.c b/drivers/net/wireless/libertas/cmdresp.c
> > index c42d3fa..47d2b19 100644
> > --- a/drivers/net/wireless/libertas/cmdresp.c
> > +++ b/drivers/net/wireless/libertas/cmdresp.c
> > @@ -504,9 +504,21 @@ int lbs_process_event(struct lbs_private *priv, u32 event)
> >
> >     case MACREG_INT_CODE_HOST_AWAKE:
> >             lbs_deb_cmd("EVENT: host awake\n");
> > +           if (priv->reset_deep_sleep_wakeup)
> > +                   priv->reset_deep_sleep_wakeup(priv);
> > +           priv->is_deep_sleep = 0;
> >             lbs_send_confirmwake(priv);
> >             break;
> >
> > +   case MACREG_INT_CODE_DEEP_SLEEP_AWAKE:
> > +           if (priv->reset_deep_sleep_wakeup)
> > +                   priv->reset_deep_sleep_wakeup(priv);
> > +           lbs_deb_cmd("EVENT: ds awake\n");
> > +           priv->is_deep_sleep = 0;
> > +           priv->wakeup_dev_required = 0;
> > +           wake_up_interruptible(&priv->ds_awake_q);
> > +           break;
> > +
> >     case MACREG_INT_CODE_PS_AWAKE:
> >             lbs_deb_cmd("EVENT: ps awake\n");
> >             /* handle unexpected PS AWAKE event */
> > diff --git a/drivers/net/wireless/libertas/debugfs.c b/drivers/net/wireless/libertas/debugfs.c
> > index 893a55c..624a438 100644
> > --- a/drivers/net/wireless/libertas/debugfs.c
> > +++ b/drivers/net/wireless/libertas/debugfs.c
> > @@ -117,6 +117,11 @@ static ssize_t lbs_sleepparams_write(struct file *file,
> >     if (!buf)
> >             return -ENOMEM;
> >
> > +   if (!lbs_is_cmd_allowed(priv)) {
> > +           ret = -EBUSY;
> > +           goto out_unlock;
> > +   }
> > +
> >     buf_size = min(count, len - 1);
> >     if (copy_from_user(buf, user_buf, buf_size)) {
> >             ret = -EFAULT;
> > @@ -157,6 +162,11 @@ static ssize_t lbs_sleepparams_read(struct file *file, char __user *userbuf,
> >     if (!buf)
> >             return -ENOMEM;
> >
> > +   if (!lbs_is_cmd_allowed(priv)) {
> > +           ret = -EBUSY;
> > +           goto out_unlock;
> > +   }
> > +
> >     ret = lbs_cmd_802_11_sleep_params(priv, CMD_ACT_GET, &sp);
> >     if (ret)
> >             goto out_unlock;
> > @@ -173,6 +183,118 @@ out_unlock:
> >     return ret;
> >  }
> >
> > +static ssize_t lbs_deepsleep_read(struct file *file, char __user *userbuf,
> > +                             size_t count, loff_t *ppos)
> > +{
> > +   struct lbs_private *priv = file->private_data;
> > +   ssize_t pos = 0;
> > +   int ret;
> > +   unsigned long addr = get_zeroed_page(GFP_KERNEL);
> > +   char *buf = (char *)addr;
> > +
> > +   if (!buf)
> > +           return -ENOMEM;
> > +
> > +   if (!priv->enter_deep_sleep) {
> > +           lbs_pr_err("deep sleep feature is not implemented "
> > +                           "for this interface driver\n");
> > +           ret = -EINVAL;
> > +           goto out_ds;
> > +   }
> > +
> > +   if (priv->is_auto_deep_sleep_enabled)
> > +           pos += snprintf(buf, len, "%d %d\n",
> > +                           priv->is_auto_deep_sleep_enabled,
> > +                           priv->auto_deep_sleep_timeout);
> > +   else if (priv->is_deep_sleep)
> > +           pos += snprintf(buf, len, "%d %d\n",
> > +                           priv->is_deep_sleep,
> > +                           priv->auto_deep_sleep_timeout);
> > +   else
> > +           pos += snprintf(buf, len, "%d %d\n",
> > +                           priv->is_deep_sleep,
> > +                           priv->auto_deep_sleep_timeout);
> > +
> > +   ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
> > +
> > +out_ds:
> > +   free_page(addr);
> > +   return ret;
> > +}
> > +
> > +static ssize_t lbs_deepsleep_write(struct file *file,
> > +                               const char __user *userbuf,
> > +                               size_t count, loff_t *ppos)
> > +{
> > +   struct lbs_private *priv = file->private_data;
> > +   ssize_t res, buf_size;
> > +   int is_deep_sleep, auto_deep_sleep_timeout;
> > +   unsigned long addr = get_zeroed_page(GFP_KERNEL);
> > +   char *buf = (char *)addr;
> > +
> > +   if (!buf)
> > +           return -ENOMEM;
> > +
> > +   if (!priv->enter_deep_sleep) {
> > +           lbs_pr_err("deep sleep feature is not implemented "
> > +                           "for this interface driver\n");
> > +           res = -EINVAL;
> > +           goto out_ds;
> > +   }
> > +
> > +   if (priv->connect_status == LBS_CONNECTED) {
> > +           lbs_pr_err("can't use deep sleep cmd in connected "
> > +                           "state\n");
> > +           res = -EINVAL;
> > +           goto out_ds;
> > +   }
> > +
> > +   buf_size = min(count, len - 1);
> > +   if (copy_from_user(buf, userbuf, buf_size)) {
> > +           res = -EFAULT;
> > +           goto out_ds;
> > +   }
> > +
> > +   res = sscanf(buf, "%d %d", &is_deep_sleep, &auto_deep_sleep_timeout);
> > +   if ((res != 2) || (!is_deep_sleep && auto_deep_sleep_timeout) ||
> > +                                   !((is_deep_sleep == 1) ||
> > +                                   (is_deep_sleep == 0))) {
> > +           lbs_pr_err("unknown option\n");
> > +           res = -EINVAL;
> > +           goto out_ds;
> > +   }
> > +
> > +   if (auto_deep_sleep_timeout) {
> > +           if (!priv->is_auto_deep_sleep_enabled) {
> > +                   priv->is_activity_detected = 0;
> > +                   priv->auto_deep_sleep_timeout = auto_deep_sleep_timeout;
> > +                   lbs_enter_auto_deep_sleep(priv);
> > +           } else {
> > +                   priv->auto_deep_sleep_timeout = auto_deep_sleep_timeout;
> > +                   lbs_deb_debugfs("auto deep sleep: already enabled\n");
> > +           }
> > +   } else {
> > +           if (priv->is_auto_deep_sleep_enabled) {
> > +                   lbs_exit_auto_deep_sleep(priv);
> > +                   /* Try to exit deep sleep if auto deep sleep disabled */
> > +                   res = lbs_set_deep_sleep(priv, 0);
> > +                   if (res)
> > +                           goto out_ds;
> > +           }
> > +           if ((is_deep_sleep == 0) || (is_deep_sleep == 1)) {
> > +                   res = lbs_set_deep_sleep(priv, is_deep_sleep);
> > +                   if (res)
> > +                           goto out_ds;
> > +           }
> > +   }
> > +
> > +   res = count;
> > +
> > +out_ds:
> > +   free_page(addr);
> > +   return res;
> > +}
> > +
> >  /*
> >   * When calling CMD_802_11_SUBSCRIBE_EVENT with CMD_ACT_GET, me might
> >   * get a bunch of vendor-specific TLVs (a.k.a. IEs) back from the
> > @@ -223,6 +345,9 @@ static ssize_t lbs_threshold_read(uint16_t tlv_type, uint16_t event_mask,
> >     u8 freq;
> >     int events = 0;
> >
> > +   if (!lbs_is_cmd_allowed(priv))
> > +           return -EBUSY;
> > +
> >     buf = (char *)get_zeroed_page(GFP_KERNEL);
> >     if (!buf)
> >             return -ENOMEM;
> > @@ -275,6 +400,9 @@ static ssize_t lbs_threshold_write(uint16_t tlv_type, uint16_t event_mask,
> >     char *buf;
> >     int ret;
> >
> > +   if (!lbs_is_cmd_allowed(priv))
> > +           return -EBUSY;
> > +
> >     buf = (char *)get_zeroed_page(GFP_KERNEL);
> >     if (!buf)
> >             return -ENOMEM;
> > @@ -444,6 +572,11 @@ static ssize_t lbs_rdmac_read(struct file *file, char __user *userbuf,
> >     if (!buf)
> >             return -ENOMEM;
> >
> > +   if (!lbs_is_cmd_allowed(priv)) {
> > +           free_page(addr);
> > +           return -EBUSY;
> > +   }
> > +
> >     offval.offset = priv->mac_offset;
> >     offval.value = 0;
> >
> > @@ -496,6 +629,11 @@ static ssize_t lbs_wrmac_write(struct file *file,
> >     if (!buf)
> >             return -ENOMEM;
> >
> > +   if (!lbs_is_cmd_allowed(priv)) {
> > +           res = -EBUSY;
> > +           goto out_unlock;
> > +   }
> > +
> >     buf_size = min(count, len - 1);
> >     if (copy_from_user(buf, userbuf, buf_size)) {
> >             res = -EFAULT;
> > @@ -532,6 +670,11 @@ static ssize_t lbs_rdbbp_read(struct file *file, char __user *userbuf,
> >     if (!buf)
> >             return -ENOMEM;
> >
> > +   if (!lbs_is_cmd_allowed(priv)) {
> > +           free_page(addr);
> > +           return -EBUSY;
> > +   }
> > +
> >     offval.offset = priv->bbp_offset;
> >     offval.value = 0;
> >
> > @@ -585,6 +728,11 @@ static ssize_t lbs_wrbbp_write(struct file *file,
> >     if (!buf)
> >             return -ENOMEM;
> >
> > +   if (!lbs_is_cmd_allowed(priv)) {
> > +           res = -EBUSY;
> > +           goto out_unlock;
> > +   }
> > +
> >     buf_size = min(count, len - 1);
> >     if (copy_from_user(buf, userbuf, buf_size)) {
> >             res = -EFAULT;
> > @@ -621,6 +769,11 @@ static ssize_t lbs_rdrf_read(struct file *file, char __user *userbuf,
> >     if (!buf)
> >             return -ENOMEM;
> >
> > +   if (!lbs_is_cmd_allowed(priv)) {
> > +           free_page(addr);
> > +           return -EBUSY;
> > +   }
> > +
> >     offval.offset = priv->rf_offset;
> >     offval.value = 0;
> >
> > @@ -674,6 +827,11 @@ static ssize_t lbs_wrrf_write(struct file *file,
> >     if (!buf)
> >             return -ENOMEM;
> >
> > +   if (!lbs_is_cmd_allowed(priv)) {
> > +           res = -EBUSY;
> > +           goto out_unlock;
> > +   }
> > +
> >     buf_size = min(count, len - 1);
> >     if (copy_from_user(buf, userbuf, buf_size)) {
> >             res = -EFAULT;
> > @@ -717,6 +875,8 @@ static const struct lbs_debugfs_files debugfs_files[] = {
> >                                     write_file_dummy), },
> >     { "sleepparams", 0644, FOPS(lbs_sleepparams_read,
> >                             lbs_sleepparams_write), },
> > +   { "deepsleep", 0644, FOPS(lbs_deepsleep_read,
> > +                           lbs_deepsleep_write), },
> >  };
> >
> >  static const struct lbs_debugfs_files debugfs_events_files[] = {
> > diff --git a/drivers/net/wireless/libertas/decl.h b/drivers/net/wireless/libertas/decl.h
> > index 0b84bdc..34b475f 100644
> > --- a/drivers/net/wireless/libertas/decl.h
> > +++ b/drivers/net/wireless/libertas/decl.h
> > @@ -33,6 +33,10 @@ int lbs_execute_next_command(struct lbs_private *priv);
> >  int lbs_process_event(struct lbs_private *priv, u32 event);
> >  void lbs_queue_event(struct lbs_private *priv, u32 event);
> >  void lbs_notify_command_response(struct lbs_private *priv, u8 resp_idx);
> > +int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep);
> > +int lbs_is_cmd_allowed(struct lbs_private *priv);
> > +int lbs_enter_auto_deep_sleep(struct lbs_private *priv);
> > +int lbs_exit_auto_deep_sleep(struct lbs_private *priv);
> >
> >  u32 lbs_fw_index_to_data_rate(u8 index);
> >  u8 lbs_data_rate_to_fw_index(u32 rate);
> > diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h
> > index 578c697..e2b4ef2 100644
> > --- a/drivers/net/wireless/libertas/dev.h
> > +++ b/drivers/net/wireless/libertas/dev.h
> > @@ -129,6 +129,20 @@ struct lbs_private {
> >     u32 bbp_offset;
> >     u32 rf_offset;
> >
> > +   /** Deep sleep flag */
> > +   int is_deep_sleep;
> > +   /** Auto deep sleep enabled flag */
> > +   int is_auto_deep_sleep_enabled;
> > +   /** Device wakeup required flag */
> > +   int wakeup_dev_required;
> > +   /** Auto deep sleep flag*/
> > +   int is_activity_detected;
> > +   /** Auto deep sleep timeout (in miliseconds) */
> > +   int auto_deep_sleep_timeout;
> > +
> > +   /** Deep sleep wait queue */
> > +   wait_queue_head_t       ds_awake_q;
> > +
> >     /* Download sent:
> >        bit0 1/0=data_sent/data_tx_done,
> >        bit1 1/0=cmd_sent/cmd_tx_done,
> > @@ -154,6 +168,9 @@ struct lbs_private {
> >     /** Hardware access */
> >     int (*hw_host_to_card) (struct lbs_private *priv, u8 type, u8 *payload, u16 nb);
> >     void (*reset_card) (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);
> >
> >     /* Wake On LAN */
> >     uint32_t wol_criteria;
> > @@ -204,6 +221,7 @@ struct lbs_private {
> >
> >     /** Timers */
> >     struct timer_list command_timer;
> > +   struct timer_list auto_deepsleep_timer;
> >     int nr_retries;
> >     int cmd_timed_out;
> >
> > diff --git a/drivers/net/wireless/libertas/host.h b/drivers/net/wireless/libertas/host.h
> > index fe8f0cb..c055daa 100644
> > --- a/drivers/net/wireless/libertas/host.h
> > +++ b/drivers/net/wireless/libertas/host.h
> > @@ -57,6 +57,7 @@
> >  #define CMD_802_11_ENABLE_RSN                      0x002f
> >  #define CMD_802_11_SET_AFC                 0x003c
> >  #define CMD_802_11_GET_AFC                 0x003d
> > +#define CMD_802_11_DEEP_SLEEP                  0x003e
> >  #define CMD_802_11_AD_HOC_STOP                     0x0040
> >  #define CMD_802_11_HOST_SLEEP_CFG          0x0043
> >  #define CMD_802_11_WAKEUP_CONFIRM          0x0044
> > diff --git a/drivers/net/wireless/libertas/if_cs.c b/drivers/net/wireless/libertas/if_cs.c
> > index 6238176..465742f 100644
> > --- a/drivers/net/wireless/libertas/if_cs.c
> > +++ b/drivers/net/wireless/libertas/if_cs.c
> > @@ -946,6 +946,9 @@ static int if_cs_probe(struct pcmcia_device *p_dev)
> >     card->priv = priv;
> >     priv->card = card;
> >     priv->hw_host_to_card = if_cs_host_to_card;
> > +   priv->enter_deep_sleep = NULL;
> > +   priv->exit_deep_sleep = NULL;
> > +   priv->reset_deep_sleep_wakeup = NULL;
> >     priv->fw_ready = 1;
> >
> >     /* Now actually get the IRQ */
> > diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c
> > index 485a8d4..9716728 100644
> > --- a/drivers/net/wireless/libertas/if_sdio.c
> > +++ b/drivers/net/wireless/libertas/if_sdio.c
> > @@ -831,6 +831,58 @@ out:
> >     return ret;
> >  }
> >
> > +static int if_sdio_enter_deep_sleep(struct lbs_private *priv)
> > +{
> > +   int ret = -1;
> > +   struct cmd_header cmd;
> > +
> > +   memset(&cmd, 0, sizeof(cmd));
> > +
> > +   lbs_deb_sdio("send DEEP_SLEEP command\n");
> > +   ret = __lbs_cmd(priv, CMD_802_11_DEEP_SLEEP, &cmd, sizeof(cmd),
> > +                   lbs_cmd_copyback, (unsigned long) &cmd);
> > +   if (ret)
> > +           lbs_pr_err("DEEP_SLEEP cmd failed\n");
> > +
> > +   mdelay(200);
> > +   return ret;
> > +}
> > +
> > +static int if_sdio_exit_deep_sleep(struct lbs_private *priv)
> > +{
> > +   struct if_sdio_card *card = priv->card;
> > +   int ret = -1;
> > +
> > +   lbs_deb_enter(LBS_DEB_SDIO);
> > +   sdio_claim_host(card->func);
> > +
> > +   sdio_writeb(card->func, HOST_POWER_UP, CONFIGURATION_REG, &ret);
> > +   if (ret)
> > +           lbs_pr_err("sdio_writeb failed!\n");
> > +
> > +   sdio_release_host(card->func);
> > +   lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
> > +   return ret;
> > +}
> > +
> > +static int if_sdio_reset_deep_sleep_wakeup(struct lbs_private *priv)
> > +{
> > +   struct if_sdio_card *card = priv->card;
> > +   int ret = -1;
> > +
> > +   lbs_deb_enter(LBS_DEB_SDIO);
> > +   sdio_claim_host(card->func);
> > +
> > +   sdio_writeb(card->func, 0, CONFIGURATION_REG, &ret);
> > +   if (ret)
> > +           lbs_pr_err("sdio_writeb failed!\n");
> > +
> > +   sdio_release_host(card->func);
> > +   lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
> > +   return ret;
> > +
> > +}
> > +
> >  /*******************************************************************/
> >  /* SDIO callbacks                                                  */
> >  /*******************************************************************/
> > @@ -859,6 +911,7 @@ static void if_sdio_interrupt(struct sdio_func *func)
> >      * Ignore the define name, this really means the card has
> >      * successfully received the command.
> >      */
> > +   card->priv->is_activity_detected = 1;
> >     if (cause & IF_SDIO_H_INT_DNLD)
> >             lbs_host_to_card_done(card->priv);
> >
> > @@ -998,6 +1051,9 @@ static int if_sdio_probe(struct sdio_func *func,
> >
> >     priv->card = card;
> >     priv->hw_host_to_card = if_sdio_host_to_card;
> > +   priv->enter_deep_sleep = if_sdio_enter_deep_sleep;
> > +   priv->exit_deep_sleep = if_sdio_exit_deep_sleep;
> > +   priv->reset_deep_sleep_wakeup = if_sdio_reset_deep_sleep_wakeup;
> >
> >     priv->fw_ready = 1;
> >
> > diff --git a/drivers/net/wireless/libertas/if_sdio.h b/drivers/net/wireless/libertas/if_sdio.h
> > index 60c9b2f..12179c1 100644
> > --- a/drivers/net/wireless/libertas/if_sdio.h
> > +++ b/drivers/net/wireless/libertas/if_sdio.h
> > @@ -51,5 +51,6 @@
> >  #define IF_SDIO_EVENT           0x80fc
> >
> >  #define IF_SDIO_BLOCK_SIZE 256
> > -
> > +#define CONFIGURATION_REG               0x03
> > +#define HOST_POWER_UP                   (0x1U << 1)
> >  #endif
> > diff --git a/drivers/net/wireless/libertas/if_spi.c b/drivers/net/wireless/libertas/if_spi.c
> > index 446e327..e2fa657 100644
> > --- a/drivers/net/wireless/libertas/if_spi.c
> > +++ b/drivers/net/wireless/libertas/if_spi.c
> > @@ -1117,6 +1117,9 @@ static int __devinit if_spi_probe(struct spi_device *spi)
> >     card->priv = priv;
> >     priv->card = card;
> >     priv->hw_host_to_card = if_spi_host_to_card;
> > +   priv->enter_deep_sleep = NULL;
> > +   priv->exit_deep_sleep = NULL;
> > +   priv->reset_deep_sleep_wakeup = NULL;
> >     priv->fw_ready = 1;
> >
> >     /* Initialize interrupt handling stuff. */
> > diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/libertas/if_usb.c
> > index 92bc8c5..a8262de 100644
> > --- a/drivers/net/wireless/libertas/if_usb.c
> > +++ b/drivers/net/wireless/libertas/if_usb.c
> > @@ -300,6 +300,9 @@ static int if_usb_probe(struct usb_interface *intf,
> >     cardp->priv->fw_ready = 1;
> >
> >     priv->hw_host_to_card = if_usb_host_to_card;
> > +   priv->enter_deep_sleep = NULL;
> > +   priv->exit_deep_sleep = NULL;
> > +   priv->reset_deep_sleep_wakeup = NULL;
> >  #ifdef CONFIG_OLPC
> >     if (machine_is_olpc())
> >             priv->reset_card = if_usb_reset_olpc_card;
> > diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c
> > index 8df1cfd..3b14fcc 100644
> > --- a/drivers/net/wireless/libertas/main.c
> > +++ b/drivers/net/wireless/libertas/main.c
> > @@ -574,8 +574,10 @@ void lbs_host_to_card_done(struct lbs_private *priv)
> >     priv->dnld_sent = DNLD_RES_RECEIVED;
> >
> >     /* Wake main thread if commands are pending */
> > -   if (!priv->cur_cmd || priv->tx_pending_len > 0)
> > -           wake_up_interruptible(&priv->waitq);
> > +   if (!priv->cur_cmd || priv->tx_pending_len > 0) {
> > +           if (!priv->wakeup_dev_required)
> > +                   wake_up_interruptible(&priv->waitq);
> > +   }
> >
> >     spin_unlock_irqrestore(&priv->driver_lock, flags);
> >     lbs_deb_leave(LBS_DEB_THREAD);
> > @@ -770,7 +772,8 @@ static int lbs_thread(void *data)
> >                     shouldsleep = 0;        /* We have a command response */
> >             else if (priv->cur_cmd)
> >                     shouldsleep = 1;        /* Can't send a command; one already running */
> > -           else if (!list_empty(&priv->cmdpendingq))
> > +           else if (!list_empty(&priv->cmdpendingq) &&
> > +                                   !(priv->wakeup_dev_required))
> >                     shouldsleep = 0;        /* We have a command to send */
> >             else if (__kfifo_len(priv->event_fifo))
> >                     shouldsleep = 0;        /* We have an event to process */
> > @@ -822,6 +825,26 @@ static int lbs_thread(void *data)
> >             }
> >             spin_unlock_irq(&priv->driver_lock);
> >
> > +           /* Process hardware events, e.g. card removed, link lost */
> > +           spin_lock_irq(&priv->driver_lock);
> > +           while (__kfifo_len(priv->event_fifo)) {
> > +                   u32 event;
> > +                   __kfifo_get(priv->event_fifo, (unsigned char *) &event,
> > +                           sizeof(event));
> > +                   spin_unlock_irq(&priv->driver_lock);
> > +                   lbs_process_event(priv, event);
> > +                   spin_lock_irq(&priv->driver_lock);
> > +           }
> > +           spin_unlock_irq(&priv->driver_lock);
> > +
> > +           if (priv->wakeup_dev_required) {
> > +                   lbs_deb_thread("Waking up device...\n");
> > +                   /* Wake up device */
> > +                   if (priv->exit_deep_sleep(priv))
> > +                           lbs_deb_thread("Wakeup device failed\n");
> > +                   continue;
> > +           }
> > +
> >             /* command timeout stuff */
> >             if (priv->cmd_timed_out && priv->cur_cmd) {
> >                     struct cmd_ctrl_node *cmdnode = priv->cur_cmd;
> > @@ -849,18 +872,7 @@ static int lbs_thread(void *data)
> >             }
> >             priv->cmd_timed_out = 0;
> >
> > -           /* Process hardware events, e.g. card removed, link lost */
> > -           spin_lock_irq(&priv->driver_lock);
> > -           while (__kfifo_len(priv->event_fifo)) {
> > -                   u32 event;
> >
> > -                   __kfifo_get(priv->event_fifo, (unsigned char *) &event,
> > -                           sizeof(event));
> > -                   spin_unlock_irq(&priv->driver_lock);
> > -                   lbs_process_event(priv, event);
> > -                   spin_lock_irq(&priv->driver_lock);
> > -           }
> > -           spin_unlock_irq(&priv->driver_lock);
> >
> >             if (!priv->fw_ready)
> >                     continue;
> > @@ -894,6 +906,9 @@ static int lbs_thread(void *data)
> >                 (priv->psstate == PS_STATE_PRE_SLEEP))
> >                     continue;
> >
> > +           if (priv->is_deep_sleep)
> > +                   continue;
> > +
> >             /* Execute the next command */
> >             if (!priv->dnld_sent && !priv->cur_cmd)
> >                     lbs_execute_next_command(priv);
> > @@ -928,6 +943,7 @@ static int lbs_thread(void *data)
> >     }
> >
> >     del_timer(&priv->command_timer);
> > +   del_timer(&priv->auto_deepsleep_timer);
> >     wake_up_all(&priv->cmd_pending);
> >
> >     lbs_deb_leave(LBS_DEB_THREAD);
> > @@ -1050,6 +1066,60 @@ out:
> >     lbs_deb_leave(LBS_DEB_CMD);
> >  }
> >
> > +/**
> > + *  This function put the device back to deep sleep mode when timer expires
> > + *  and no activity (command, event, data etc.) is detected.
> > + */
> > +static void auto_deepsleep_timer_fn(unsigned long data)
> > +{
> > +   struct lbs_private *priv = (struct lbs_private *)data;
> > +   int ret;
> > +
> > +   lbs_deb_enter(LBS_DEB_CMD);
> > +
> > +   if (priv->is_activity_detected) {
> > +           priv->is_activity_detected = 0;
> > +   } else {
> > +           if (priv->is_auto_deep_sleep_enabled &&
> > +                           (!priv->wakeup_dev_required) &&
> > +                           (priv->connect_status != LBS_CONNECTED)) {
> > +                   lbs_deb_main("Entering auto deep sleep mode...\n");
> > +                   ret = lbs_prepare_and_send_command(priv,
> > +                                   CMD_802_11_DEEP_SLEEP, 0,
> > +                                   0, 0, NULL);
> > +           }
> > +   }
> > +   mod_timer(&priv->auto_deepsleep_timer , jiffies +
> > +                           (priv->auto_deep_sleep_timeout * HZ)/1000);
> > +   lbs_deb_leave(LBS_DEB_CMD);
> > +}
> > +
> > +int lbs_enter_auto_deep_sleep(struct lbs_private *priv)
> > +{
> > +   lbs_deb_enter(LBS_DEB_SDIO);
> > +
> > +   priv->is_auto_deep_sleep_enabled = 1;
> > +   if (priv->is_deep_sleep)
> > +           priv->wakeup_dev_required = 1;
> > +   mod_timer(&priv->auto_deepsleep_timer ,
> > +                   jiffies + (priv->auto_deep_sleep_timeout * HZ)/1000);
> > +
> > +   lbs_deb_leave(LBS_DEB_SDIO);
> > +   return 0;
> > +}
> > +
> > +int lbs_exit_auto_deep_sleep(struct lbs_private *priv)
> > +{
> > +   lbs_deb_enter(LBS_DEB_SDIO);
> > +
> > +   priv->is_auto_deep_sleep_enabled = 0;
> > +   priv->auto_deep_sleep_timeout = 0;
> > +   del_timer(&priv->auto_deepsleep_timer);
> > +
> > +   lbs_deb_leave(LBS_DEB_SDIO);
> > +   return 0;
> > +}
> > +
> >  static void lbs_sync_channel_worker(struct work_struct *work)
> >  {
> >     struct lbs_private *priv = container_of(work, struct lbs_private,
> > @@ -1099,11 +1169,17 @@ static int lbs_init_adapter(struct lbs_private *priv)
> >     priv->capability = WLAN_CAPABILITY_SHORT_PREAMBLE;
> >     priv->psmode = LBS802_11POWERMODECAM;
> >     priv->psstate = PS_STATE_FULL_POWER;
> > +   priv->is_deep_sleep = 0;
> > +   priv->is_auto_deep_sleep_enabled = 0;
> > +   priv->wakeup_dev_required = 0;
> > +   init_waitqueue_head(&priv->ds_awake_q);
> >
> >     mutex_init(&priv->lock);
> >
> >     setup_timer(&priv->command_timer, command_timer_fn,
> >             (unsigned long)priv);
> > +   setup_timer(&priv->auto_deepsleep_timer, auto_deepsleep_timer_fn,
> > +                   (unsigned long)priv);
> >
> >     INIT_LIST_HEAD(&priv->cmdfreeq);
> >     INIT_LIST_HEAD(&priv->cmdpendingq);
> > @@ -1142,6 +1218,7 @@ static void lbs_free_adapter(struct lbs_private *priv)
> >     if (priv->event_fifo)
> >             kfifo_free(priv->event_fifo);
> >     del_timer(&priv->command_timer);
> > +   del_timer(&priv->auto_deepsleep_timer);
> >     kfree(priv->networks);
> >     priv->networks = NULL;
> >
> > @@ -1272,6 +1349,11 @@ void lbs_remove_card(struct lbs_private *priv)
> >     wrqu.ap_addr.sa_family = ARPHRD_ETHER;
> >     wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
> >
> > +   if (priv->is_deep_sleep) {
> > +           priv->is_deep_sleep = 0;
> > +           wake_up_interruptible(&priv->ds_awake_q);
> > +   }
> > +
> >     /* Stop the thread servicing the interrupts */
> >     priv->surpriseremoved = 1;
> >     kthread_stop(priv->main_thread);
> > @@ -1392,6 +1474,7 @@ void lbs_stop_card(struct lbs_private *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);
> > diff --git a/drivers/net/wireless/libertas/scan.c b/drivers/net/wireless/libertas/scan.c
> > index 6c95af3..e468e15 100644
> > --- a/drivers/net/wireless/libertas/scan.c
> > +++ b/drivers/net/wireless/libertas/scan.c
> > @@ -950,6 +950,11 @@ int lbs_set_scan(struct net_device *dev, struct iw_request_info *info,
> >
> >     lbs_deb_enter(LBS_DEB_WEXT);
> >
> > +   if (!lbs_is_cmd_allowed(priv)) {
> > +           ret = -EBUSY;
> > +           goto out;
> > +   }
> > +
> >     if (!priv->radio_on) {
> >             ret = -EINVAL;
> >             goto out;
> > @@ -1017,6 +1022,12 @@ int lbs_get_scan(struct net_device *dev, struct iw_request_info *info,
> >
> >     lbs_deb_enter(LBS_DEB_WEXT);
> >
> > +   if (!lbs_is_cmd_allowed(priv)) {
> > +           err = -EBUSY;
> > +           lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", err);
> > +           return err;
> > +   }
> > +
> >     /* iwlist should wait until the current scan is finished */
> >     if (priv->scan_channel)
> >             return -EAGAIN;
> > diff --git a/drivers/net/wireless/libertas/wext.c b/drivers/net/wireless/libertas/wext.c
> > index be837a0..ef2b986 100644
> > --- a/drivers/net/wireless/libertas/wext.c
> > +++ b/drivers/net/wireless/libertas/wext.c
> > @@ -45,6 +45,31 @@ static inline void lbs_cancel_association_work(struct lbs_private *priv)
> >     priv->pending_assoc_req = NULL;
> >  }
> >
> > +/**
> > + *  @brief This function checks if the command is allowed.
> > + *
> > + *  @param priv         A pointer to lbs_private structure
> > + *  @return             allowed or not allowed.
> > + */
> > +
> > +int lbs_is_cmd_allowed(struct lbs_private *priv)
> > +{
> > +   int         ret = 1;
> > +
> > +   lbs_deb_enter(LBS_DEB_WEXT);
> > +
> > +   if (!priv->is_auto_deep_sleep_enabled) {
> > +           if (priv->is_deep_sleep) {
> > +                   lbs_deb_wext("IOCTLS called when station"
> > +                                   "is in deep sleep\n");
> > +                   ret = 0;
> > +           }
> > +   }
> > +
> > +   lbs_deb_leave(LBS_DEB_WEXT);
> > +   return ret;
> > +}
> > +
> >
> >  /**
> >   *  @brief Find the channel frequency power info with specific channel
> > @@ -168,6 +193,11 @@ static int lbs_get_freq(struct net_device *dev, struct iw_request_info *info,
> >
> >     lbs_deb_enter(LBS_DEB_WEXT);
> >
> > +   if (!lbs_is_cmd_allowed(priv)) {
> > +           lbs_deb_leave(LBS_DEB_WEXT);
> > +           return -EBUSY;
> > +   }
> > +
> >     cfp = lbs_find_cfp_by_band_and_channel(priv, 0,
> >                                        priv->curbssparams.channel);
> >
> > @@ -278,6 +308,12 @@ static int lbs_set_rts(struct net_device *dev, struct iw_request_info *info,
> >
> >     lbs_deb_enter(LBS_DEB_WEXT);
> >
> > +   if (!lbs_is_cmd_allowed(priv)) {
> > +           ret = -EBUSY;
> > +           lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
> > +           return ret;
> > +   }
> > +
> >     if (vwrq->disabled)
> >             val = MRVDRV_RTS_MAX_VALUE;
> >
> > @@ -299,6 +335,11 @@ static int lbs_get_rts(struct net_device *dev, struct iw_request_info *info,
> >
> >     lbs_deb_enter(LBS_DEB_WEXT);
> >
> > +   if (!lbs_is_cmd_allowed(priv)) {
> > +           ret = -EBUSY;
> > +           goto out;
> > +   }
> > +
> >     ret = lbs_get_snmp_mib(priv, SNMP_MIB_OID_RTS_THRESHOLD, &val);
> >     if (ret)
> >             goto out;
> > @@ -321,6 +362,12 @@ static int lbs_set_frag(struct net_device *dev, struct iw_request_info *info,
> >
> >     lbs_deb_enter(LBS_DEB_WEXT);
> >
> > +   if (!lbs_is_cmd_allowed(priv)) {
> > +           ret = -EBUSY;
> > +           lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
> > +           return ret;
> > +   }
> > +
> >     if (vwrq->disabled)
> >             val = MRVDRV_FRAG_MAX_VALUE;
> >
> > @@ -342,6 +389,11 @@ static int lbs_get_frag(struct net_device *dev, struct iw_request_info *info,
> >
> >     lbs_deb_enter(LBS_DEB_WEXT);
> >
> > +   if (!lbs_is_cmd_allowed(priv)) {
> > +           ret = -EBUSY;
> > +           goto out;
> > +   }
> > +
> >     ret = lbs_get_snmp_mib(priv, SNMP_MIB_OID_FRAG_THRESHOLD, &val);
> >     if (ret)
> >             goto out;
> > @@ -391,6 +443,11 @@ static int lbs_get_txpow(struct net_device *dev,
> >
> >     lbs_deb_enter(LBS_DEB_WEXT);
> >
> > +   if (!lbs_is_cmd_allowed(priv)) {
> > +           ret = -EBUSY;
> > +           goto out;
> > +   }
> > +
> >     if (!priv->radio_on) {
> >             lbs_deb_wext("tx power off\n");
> >             vwrq->value = 0;
> > @@ -424,6 +481,11 @@ static int lbs_set_retry(struct net_device *dev, struct iw_request_info *info,
> >
> >     lbs_deb_enter(LBS_DEB_WEXT);
> >
> > +   if (!lbs_is_cmd_allowed(priv)) {
> > +           ret = -EBUSY;
> > +           goto out;
> > +   }
> > +
> >          if ((vwrq->flags & IW_RETRY_TYPE) != IW_RETRY_LIMIT)
> >                  return -EOPNOTSUPP;
> >
> > @@ -472,6 +534,11 @@ static int lbs_get_retry(struct net_device *dev, struct iw_request_info *info,
> >
> >     lbs_deb_enter(LBS_DEB_WEXT);
> >
> > +   if (!lbs_is_cmd_allowed(priv)) {
> > +           ret = -EBUSY;
> > +           goto out;
> > +   }
> > +
> >     vwrq->disabled = 0;
> >
> >     if (vwrq->flags & IW_RETRY_LONG) {
> > @@ -712,6 +779,11 @@ static int lbs_set_power(struct net_device *dev, struct iw_request_info *info,
> >
> >     lbs_deb_enter(LBS_DEB_WEXT);
> >
> > +   if (!lbs_is_cmd_allowed(priv)) {
> > +           lbs_deb_leave(LBS_DEB_WEXT);
> > +           return -EBUSY;
> > +   }
> > +
> >     if (!(priv->fwcapinfo & FW_CAPINFO_PS)) {
> >             if (vwrq->disabled)
> >                     return 0;
> > @@ -792,6 +864,9 @@ static struct iw_statistics *lbs_get_wireless_stats(struct net_device *dev)
> >
> >     lbs_deb_enter(LBS_DEB_WEXT);
> >
> > +   if (!lbs_is_cmd_allowed(priv))
> > +           return NULL;
> > +
> >     priv->wstats.status = priv->mode;
> >
> >     /* If we're not associated, all quality values are meaningless */
> > @@ -892,6 +967,12 @@ static int lbs_set_freq(struct net_device *dev, struct iw_request_info *info,
> >
> >     lbs_deb_enter(LBS_DEB_WEXT);
> >
> > +   if (!lbs_is_cmd_allowed(priv)) {
> > +           ret = -EBUSY;
> > +           lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
> > +           return ret;
> > +   }
> > +
> >     mutex_lock(&priv->lock);
> >     assoc_req = lbs_get_association_request(priv);
> >     if (!assoc_req) {
> > @@ -1000,6 +1081,12 @@ static int lbs_set_rate(struct net_device *dev, struct iw_request_info *info,
> >     u8 rates[MAX_RATES + 1];
> >
> >     lbs_deb_enter(LBS_DEB_WEXT);
> > +
> > +   if (!lbs_is_cmd_allowed(priv)) {
> > +           ret = -EBUSY;
> > +           goto out;
> > +   }
> > +
> >     lbs_deb_wext("vwrq->value %d\n", vwrq->value);
> >     lbs_deb_wext("vwrq->fixed %d\n", vwrq->fixed);
> >
> > @@ -1058,6 +1145,11 @@ static int lbs_get_rate(struct net_device *dev, struct iw_request_info *info,
> >
> >     lbs_deb_enter(LBS_DEB_WEXT);
> >
> > +   if (!lbs_is_cmd_allowed(priv)) {
> > +           lbs_deb_leave(LBS_DEB_WEXT);
> > +           return -EBUSY;
> > +   }
> > +
> >     if (priv->connect_status == LBS_CONNECTED) {
> >             vwrq->value = priv->cur_rate * 500000;
> >
> > @@ -1084,6 +1176,11 @@ static int lbs_set_mode(struct net_device *dev,
> >
> >     lbs_deb_enter(LBS_DEB_WEXT);
> >
> > +   if (!lbs_is_cmd_allowed(priv)) {
> > +           ret = -EBUSY;
> > +           goto out;
> > +   }
> > +
> >     if (   (*uwrq != IW_MODE_ADHOC)
> >         && (*uwrq != IW_MODE_INFRA)
> >         && (*uwrq != IW_MODE_AUTO)) {
> > @@ -1325,6 +1422,12 @@ static int lbs_set_encode(struct net_device *dev,
> >
> >     lbs_deb_enter(LBS_DEB_WEXT);
> >
> > +   if (!lbs_is_cmd_allowed(priv)) {
> > +           ret = -EBUSY;
> > +           lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
> > +           return ret;
> > +   }
> > +
> >     mutex_lock(&priv->lock);
> >     assoc_req = lbs_get_association_request(priv);
> >     if (!assoc_req) {
> > @@ -1508,6 +1611,12 @@ static int lbs_set_encodeext(struct net_device *dev,
> >
> >     lbs_deb_enter(LBS_DEB_WEXT);
> >
> > +   if (!lbs_is_cmd_allowed(priv)) {
> > +           ret = -EBUSY;
> > +           lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
> > +           return ret;
> > +   }
> > +
> >     mutex_lock(&priv->lock);
> >     assoc_req = lbs_get_association_request(priv);
> >     if (!assoc_req) {
> > @@ -1720,6 +1829,12 @@ static int lbs_set_auth(struct net_device *dev,
> >
> >     lbs_deb_enter(LBS_DEB_WEXT);
> >
> > +   if (!lbs_is_cmd_allowed(priv)) {
> > +           ret = -EBUSY;
> > +           lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
> > +           return ret;
> > +   }
> > +
> >     mutex_lock(&priv->lock);
> >     assoc_req = lbs_get_association_request(priv);
> >     if (!assoc_req) {
> > @@ -1822,6 +1937,12 @@ static int lbs_get_auth(struct net_device *dev,
> >
> >     lbs_deb_enter(LBS_DEB_WEXT);
> >
> > +   if (!lbs_is_cmd_allowed(priv)) {
> > +           ret = -EBUSY;
> > +           lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
> > +           return ret;
> > +   }
> > +
> >     switch (dwrq->flags & IW_AUTH_INDEX) {
> >     case IW_AUTH_KEY_MGMT:
> >             dwrq->value = priv->secinfo.key_mgmt;
> > @@ -1864,6 +1985,11 @@ static int lbs_set_txpow(struct net_device *dev, struct iw_request_info
> *info,
> >
> >     lbs_deb_enter(LBS_DEB_WEXT);
> >
> > +   if (!lbs_is_cmd_allowed(priv)) {
> > +           ret = -EBUSY;
> > +           goto out;
> > +   }
> > +
> >     if (vwrq->disabled) {
> >             lbs_set_radio(priv, RADIO_PREAMBLE_AUTO, 0);
> >             goto out;
> > @@ -1983,6 +2109,12 @@ static int lbs_set_essid(struct net_device *dev, struct iw_request_info
> *info,
> >
> >     lbs_deb_enter(LBS_DEB_WEXT);
> >
> > +   if (!lbs_is_cmd_allowed(priv)) {
> > +           ret = -EBUSY;
> > +           lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
> > +           return ret;
> > +   }
> > +
> >     if (!priv->radio_on) {
> >             ret = -EINVAL;
> >             goto out;
> > @@ -2110,6 +2242,12 @@ static int lbs_set_wap(struct net_device *dev, struct iw_request_info *info,
> >
> >     lbs_deb_enter(LBS_DEB_WEXT);
> >
> > +   if (!lbs_is_cmd_allowed(priv)) {
> > +           ret = -EBUSY;
> > +           lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
> > +           return ret;
> > +   }
> > +
> >     if (!priv->radio_on)
> >             return -EINVAL;
> >


From hs4233 at mail.mn-solutions.de  Tue Sep 29 03:04:00 2009
From: hs4233 at mail.mn-solutions.de (Holger Schurig)
Date: Tue, 29 Sep 2009 09:04:00 +0200
Subject: [PATCH] libertas: Add auto deep sleep support for
	SD8385/SD8686/SD8688
In-Reply-To: <477F20668A386D41ADCC57781B1F704306DDA6C1B6@SC-VEXCH1.marvell.com>
References: <1253058359-1934-1-git-send-email-bzhao@marvell.com>
	<1253458691.11702.46.camel@localhost.localdomain>
	<477F20668A386D41ADCC57781B1F704306DDA6C1B6@SC-VEXCH1.marvell.com>
Message-ID: <200909290904.00971.hs4233@mail.mn-solutions.de>

Hi Bing !

Please note that I just sent an experimental patch to enable 
cfg80211 for libertas.

Once this is polished, you can immediately start using 
nl80211/cfg80211 to configure such things, there's no need to 
have the full thingy (e.g. wpa_supplicant via -Dnl80211 working 
with WEP/WPA/WPA2) working, those things are not interrelated.


-- 
http://www.holgerschurig.de


From hs4233 at mail.mn-solutions.de  Tue Sep 29 03:24:07 2009
From: hs4233 at mail.mn-solutions.de (Holger Schurig)
Date: Tue, 29 Sep 2009 09:24:07 +0200
Subject: [PATCH] libertas: Add auto deep sleep support for
	SD8385/SD8686/SD8688
In-Reply-To: <477F20668A386D41ADCC57781B1F704306DDA6C1B6@SC-VEXCH1.marvell.com>
References: <1253058359-1934-1-git-send-email-bzhao@marvell.com>
	<1253458691.11702.46.camel@localhost.localdomain>
	<477F20668A386D41ADCC57781B1F704306DDA6C1B6@SC-VEXCH1.marvell.com>
Message-ID: <200909290924.07758.hs4233@mail.mn-solutions.de>

> iwconfig wlan0 power period 0   -> enable deep sleep (enter
> deep sleep immediately) 

> iwconfig wlan0 power period 5   -> enable auto deep sleep
> (enter deep sleep automatically after 5s idle time) 

> iwconfig wlan0 power period -1  -> disable deep sleep / auto
> deep sleep 

ACK from my side (not that I'm the maintainer ...)

-- 
http://www.holgerschurig.de


From bzhao at marvell.com  Tue Sep 29 14:36:01 2009
From: bzhao at marvell.com (Bing Zhao)
Date: Tue, 29 Sep 2009 11:36:01 -0700
Subject: [PATCH] libertas: Add auto deep sleep support for
	SD8385/SD8686/SD8688
In-Reply-To: <200909290904.00971.hs4233@mail.mn-solutions.de>
References: <1253058359-1934-1-git-send-email-bzhao@marvell.com>
	<1253458691.11702.46.camel@localhost.localdomain>
	<477F20668A386D41ADCC57781B1F704306DDA6C1B6@SC-VEXCH1.marvell.com>,
	<200909290904.00971.hs4233@mail.mn-solutions.de>
Message-ID: <477F20668A386D41ADCC57781B1F704306DDC45F51@SC-VEXCH1.marvell.com>

Hi Holger,

> From: Holger Schurig [hs4233 at mail.mn-solutions.de]
> Sent: Tuesday, September 29, 2009 12:04 AM
> To: Bing Zhao
> Cc: Dan Williams; libertas-dev at lists.infradead.org; linux-wireless at vger.kernel.org; Amitkumar Karwar
> Subject: Re: [PATCH] libertas: Add auto deep sleep support for SD8385/SD8686/SD8688
>
> Hi Bing !
> 
> Please note that I just sent an experimental patch to enable
> cfg80211 for libertas.
> 
> Once this is polished, you can immediately start using
> nl80211/cfg80211 to configure such things, there's no need to
> have the full thingy (e.g. wpa_supplicant via -Dnl80211 working
> with WEP/WPA/WPA2) working, those things are not interrelated.

Thanks for the info.

I will start to use nl80211/cfg80211 to configure my parameters as soon as your patch is in libertas driver.

Best regards,

Bing

>
>
> --
> http://www.holgerschurig.de

From bzhao at marvell.com  Wed Sep 30 23:04:38 2009
From: bzhao at marvell.com (Bing Zhao)
Date: Wed, 30 Sep 2009 20:04:38 -0700
Subject: [PATCH v2] libertas: Add auto deep sleep support for
	SD8385/SD8686/SD8688
Message-ID: <1254366278-15483-1-git-send-email-bzhao@marvell.com>

From: Amitkumar Karwar <akarwar at marvell.com>

Add timer based auto deep sleep feature in libertas driver which can be
configured using iwconfig command. This is tested on SD8688, SD8686 cards
with firmware versions 10.38.1.p25, 9.70.4.p0 respectively on 32-bit and 64-bit
platforms. Tests have been done for USB/CS cards to make sure that the patch
won't break USB/CS code. We didn't test the if_spi driver.

Signed-off-by: Amitkumar Karwar <akarwar at marvell.com>
Signed-off-by: Bing Zhao <bzhao at marvell.com>
---
 drivers/net/wireless/libertas/README    |   26 ++++-
 drivers/net/wireless/libertas/cmd.c     |   72 ++++++++++++-
 drivers/net/wireless/libertas/cmdresp.c |   12 ++
 drivers/net/wireless/libertas/debugfs.c |   46 ++++++++
 drivers/net/wireless/libertas/decl.h    |    4 +
 drivers/net/wireless/libertas/dev.h     |   18 +++
 drivers/net/wireless/libertas/host.h    |    1 +
 drivers/net/wireless/libertas/if_cs.c   |    3 +
 drivers/net/wireless/libertas/if_sdio.c |   56 +++++++++
 drivers/net/wireless/libertas/if_sdio.h |    3 +-
 drivers/net/wireless/libertas/if_spi.c  |    3 +
 drivers/net/wireless/libertas/if_usb.c  |    3 +
 drivers/net/wireless/libertas/main.c    |  111 ++++++++++++++++---
 drivers/net/wireless/libertas/scan.c    |   11 ++
 drivers/net/wireless/libertas/wext.c    |  185 ++++++++++++++++++++++++++++++-
 15 files changed, 533 insertions(+), 21 deletions(-)

diff --git a/drivers/net/wireless/libertas/README b/drivers/net/wireless/libertas/README
index ab6a2d5..635002d 100644
--- a/drivers/net/wireless/libertas/README
+++ b/drivers/net/wireless/libertas/README
@@ -1,5 +1,5 @@
 ================================================================================
-			README for USB8388
+			README for Libertas
 
  (c) Copyright ?? 2003-2006, Marvell International Ltd.
  All Rights Reserved
@@ -226,4 +226,28 @@ setuserscan
     All entries in the scan table (not just the new scan data when keep=1)
     will be displayed upon completion by use of the getscantable ioctl.
 
+========================
+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
+
 ==============================================================================
diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c
index 6850981..3a3e894 100644
--- a/drivers/net/wireless/libertas/cmd.c
+++ b/drivers/net/wireless/libertas/cmd.c
@@ -17,7 +17,6 @@
 
 static struct cmd_ctrl_node *lbs_get_cmd_ctrl_node(struct lbs_private *priv);
 
-
 /**
  *  @brief Simple callback that copies response back into command
  *
@@ -319,6 +318,60 @@ int lbs_cmd_802_11_sleep_params(struct lbs_private *priv, uint16_t cmd_action,
 	return 0;
 }
 
+static int lbs_wait_for_ds_awake(struct lbs_private *priv)
+{
+	int ret = 0;
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	if (priv->is_deep_sleep) {
+		if (!wait_event_interruptible_timeout(priv->ds_awake_q,
+					!priv->is_deep_sleep, (10 * HZ))) {
+			lbs_pr_err("ds_awake_q: timer expired\n");
+			ret = -1;
+		}
+	}
+
+	lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
+	return ret;
+}
+
+int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep)
+{
+	int ret =  0;
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	if (deep_sleep) {
+		if (priv->is_deep_sleep != 1) {
+			lbs_deb_cmd("deep sleep: sleep\n");
+			BUG_ON(!priv->enter_deep_sleep);
+			ret = priv->enter_deep_sleep(priv);
+			if (!ret) {
+				netif_stop_queue(priv->dev);
+				netif_carrier_off(priv->dev);
+			}
+		} else {
+			lbs_pr_err("deep sleep: already enabled\n");
+		}
+	} else {
+		if (priv->is_deep_sleep) {
+			lbs_deb_cmd("deep sleep: wakeup\n");
+			BUG_ON(!priv->exit_deep_sleep);
+			ret = priv->exit_deep_sleep(priv);
+			if (!ret) {
+				ret = lbs_wait_for_ds_awake(priv);
+				if (ret)
+					lbs_pr_err("deep sleep: wakeup"
+							"failed\n");
+			}
+		}
+	}
+
+	lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
+	return ret;
+}
+
 int lbs_cmd_802_11_set_wep(struct lbs_private *priv, uint16_t cmd_action,
 			   struct assoc_request *assoc)
 {
@@ -1242,8 +1295,17 @@ static void lbs_submit_command(struct lbs_private *priv,
 		timeo = HZ/4;
 	}
 
-	/* Setup the timer after transmit command */
-	mod_timer(&priv->command_timer, jiffies + timeo);
+	if (command == CMD_802_11_DEEP_SLEEP) {
+		if (priv->is_auto_deep_sleep_enabled) {
+			priv->wakeup_dev_required = 1;
+			priv->dnld_sent = 0;
+		}
+		priv->is_deep_sleep = 1;
+		lbs_complete_command(priv, cmdnode, 0);
+	} else {
+		/* Setup the timer after transmit command */
+		mod_timer(&priv->command_timer, jiffies + timeo);
+	}
 
 	lbs_deb_leave(LBS_DEB_HOST);
 }
@@ -1505,6 +1567,10 @@ int lbs_prepare_and_send_command(struct lbs_private *priv,
 	case CMD_802_11_BEACON_CTRL:
 		ret = lbs_cmd_bcn_ctrl(priv, cmdptr, cmd_action);
 		break;
+	case CMD_802_11_DEEP_SLEEP:
+		cmdptr->command = cpu_to_le16(CMD_802_11_DEEP_SLEEP);
+		cmdptr->size = cpu_to_le16(S_DS_GEN);
+		break;
 	default:
 		lbs_pr_err("PREP_CMD: unknown command 0x%04x\n", cmd_no);
 		ret = -1;
diff --git a/drivers/net/wireless/libertas/cmdresp.c b/drivers/net/wireless/libertas/cmdresp.c
index c42d3fa..47d2b19 100644
--- a/drivers/net/wireless/libertas/cmdresp.c
+++ b/drivers/net/wireless/libertas/cmdresp.c
@@ -504,9 +504,21 @@ int lbs_process_event(struct lbs_private *priv, u32 event)
 
 	case MACREG_INT_CODE_HOST_AWAKE:
 		lbs_deb_cmd("EVENT: host awake\n");
+		if (priv->reset_deep_sleep_wakeup)
+			priv->reset_deep_sleep_wakeup(priv);
+		priv->is_deep_sleep = 0;
 		lbs_send_confirmwake(priv);
 		break;
 
+	case MACREG_INT_CODE_DEEP_SLEEP_AWAKE:
+		if (priv->reset_deep_sleep_wakeup)
+			priv->reset_deep_sleep_wakeup(priv);
+		lbs_deb_cmd("EVENT: ds awake\n");
+		priv->is_deep_sleep = 0;
+		priv->wakeup_dev_required = 0;
+		wake_up_interruptible(&priv->ds_awake_q);
+		break;
+
 	case MACREG_INT_CODE_PS_AWAKE:
 		lbs_deb_cmd("EVENT: ps awake\n");
 		/* handle unexpected PS AWAKE event */
diff --git a/drivers/net/wireless/libertas/debugfs.c b/drivers/net/wireless/libertas/debugfs.c
index 893a55c..8a7e931 100644
--- a/drivers/net/wireless/libertas/debugfs.c
+++ b/drivers/net/wireless/libertas/debugfs.c
@@ -117,6 +117,11 @@ static ssize_t lbs_sleepparams_write(struct file *file,
 	if (!buf)
 		return -ENOMEM;
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		goto out_unlock;
+	}
+
 	buf_size = min(count, len - 1);
 	if (copy_from_user(buf, user_buf, buf_size)) {
 		ret = -EFAULT;
@@ -157,6 +162,11 @@ static ssize_t lbs_sleepparams_read(struct file *file, char __user *userbuf,
 	if (!buf)
 		return -ENOMEM;
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		goto out_unlock;
+	}
+
 	ret = lbs_cmd_802_11_sleep_params(priv, CMD_ACT_GET, &sp);
 	if (ret)
 		goto out_unlock;
@@ -223,6 +233,9 @@ static ssize_t lbs_threshold_read(uint16_t tlv_type, uint16_t event_mask,
 	u8 freq;
 	int events = 0;
 
+	if (!lbs_is_cmd_allowed(priv))
+		return -EBUSY;
+
 	buf = (char *)get_zeroed_page(GFP_KERNEL);
 	if (!buf)
 		return -ENOMEM;
@@ -275,6 +288,9 @@ static ssize_t lbs_threshold_write(uint16_t tlv_type, uint16_t event_mask,
 	char *buf;
 	int ret;
 
+	if (!lbs_is_cmd_allowed(priv))
+		return -EBUSY;
+
 	buf = (char *)get_zeroed_page(GFP_KERNEL);
 	if (!buf)
 		return -ENOMEM;
@@ -444,6 +460,11 @@ static ssize_t lbs_rdmac_read(struct file *file, char __user *userbuf,
 	if (!buf)
 		return -ENOMEM;
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		free_page(addr);
+		return -EBUSY;
+	}
+
 	offval.offset = priv->mac_offset;
 	offval.value = 0;
 
@@ -496,6 +517,11 @@ static ssize_t lbs_wrmac_write(struct file *file,
 	if (!buf)
 		return -ENOMEM;
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		res = -EBUSY;
+		goto out_unlock;
+	}
+
 	buf_size = min(count, len - 1);
 	if (copy_from_user(buf, userbuf, buf_size)) {
 		res = -EFAULT;
@@ -532,6 +558,11 @@ static ssize_t lbs_rdbbp_read(struct file *file, char __user *userbuf,
 	if (!buf)
 		return -ENOMEM;
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		free_page(addr);
+		return -EBUSY;
+	}
+
 	offval.offset = priv->bbp_offset;
 	offval.value = 0;
 
@@ -585,6 +616,11 @@ static ssize_t lbs_wrbbp_write(struct file *file,
 	if (!buf)
 		return -ENOMEM;
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		res = -EBUSY;
+		goto out_unlock;
+	}
+
 	buf_size = min(count, len - 1);
 	if (copy_from_user(buf, userbuf, buf_size)) {
 		res = -EFAULT;
@@ -621,6 +657,11 @@ static ssize_t lbs_rdrf_read(struct file *file, char __user *userbuf,
 	if (!buf)
 		return -ENOMEM;
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		free_page(addr);
+		return -EBUSY;
+	}
+
 	offval.offset = priv->rf_offset;
 	offval.value = 0;
 
@@ -674,6 +715,11 @@ static ssize_t lbs_wrrf_write(struct file *file,
 	if (!buf)
 		return -ENOMEM;
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		res = -EBUSY;
+		goto out_unlock;
+	}
+
 	buf_size = min(count, len - 1);
 	if (copy_from_user(buf, userbuf, buf_size)) {
 		res = -EFAULT;
diff --git a/drivers/net/wireless/libertas/decl.h b/drivers/net/wireless/libertas/decl.h
index 0b84bdc..34b475f 100644
--- a/drivers/net/wireless/libertas/decl.h
+++ b/drivers/net/wireless/libertas/decl.h
@@ -33,6 +33,10 @@ int lbs_execute_next_command(struct lbs_private *priv);
 int lbs_process_event(struct lbs_private *priv, u32 event);
 void lbs_queue_event(struct lbs_private *priv, u32 event);
 void lbs_notify_command_response(struct lbs_private *priv, u8 resp_idx);
+int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep);
+int lbs_is_cmd_allowed(struct lbs_private *priv);
+int lbs_enter_auto_deep_sleep(struct lbs_private *priv);
+int lbs_exit_auto_deep_sleep(struct lbs_private *priv);
 
 u32 lbs_fw_index_to_data_rate(u8 index);
 u8 lbs_data_rate_to_fw_index(u32 rate);
diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h
index 578c697..e2b4ef2 100644
--- a/drivers/net/wireless/libertas/dev.h
+++ b/drivers/net/wireless/libertas/dev.h
@@ -129,6 +129,20 @@ struct lbs_private {
 	u32 bbp_offset;
 	u32 rf_offset;
 
+	/** Deep sleep flag */
+	int is_deep_sleep;
+	/** Auto deep sleep enabled flag */
+	int is_auto_deep_sleep_enabled;
+	/** Device wakeup required flag */
+	int wakeup_dev_required;
+	/** Auto deep sleep flag*/
+	int is_activity_detected;
+	/** Auto deep sleep timeout (in miliseconds) */
+	int auto_deep_sleep_timeout;
+
+	/** Deep sleep wait queue */
+	wait_queue_head_t       ds_awake_q;
+
 	/* Download sent:
 	   bit0 1/0=data_sent/data_tx_done,
 	   bit1 1/0=cmd_sent/cmd_tx_done,
@@ -154,6 +168,9 @@ struct lbs_private {
 	/** Hardware access */
 	int (*hw_host_to_card) (struct lbs_private *priv, u8 type, u8 *payload, u16 nb);
 	void (*reset_card) (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);
 
 	/* Wake On LAN */
 	uint32_t wol_criteria;
@@ -204,6 +221,7 @@ struct lbs_private {
 
 	/** Timers */
 	struct timer_list command_timer;
+	struct timer_list auto_deepsleep_timer;
 	int nr_retries;
 	int cmd_timed_out;
 
diff --git a/drivers/net/wireless/libertas/host.h b/drivers/net/wireless/libertas/host.h
index fe8f0cb..c055daa 100644
--- a/drivers/net/wireless/libertas/host.h
+++ b/drivers/net/wireless/libertas/host.h
@@ -57,6 +57,7 @@
 #define CMD_802_11_ENABLE_RSN			0x002f
 #define CMD_802_11_SET_AFC			0x003c
 #define CMD_802_11_GET_AFC			0x003d
+#define CMD_802_11_DEEP_SLEEP                  0x003e
 #define CMD_802_11_AD_HOC_STOP			0x0040
 #define CMD_802_11_HOST_SLEEP_CFG		0x0043
 #define CMD_802_11_WAKEUP_CONFIRM		0x0044
diff --git a/drivers/net/wireless/libertas/if_cs.c b/drivers/net/wireless/libertas/if_cs.c
index 6238176..465742f 100644
--- a/drivers/net/wireless/libertas/if_cs.c
+++ b/drivers/net/wireless/libertas/if_cs.c
@@ -946,6 +946,9 @@ static int if_cs_probe(struct pcmcia_device *p_dev)
 	card->priv = priv;
 	priv->card = card;
 	priv->hw_host_to_card = if_cs_host_to_card;
+	priv->enter_deep_sleep = NULL;
+	priv->exit_deep_sleep = NULL;
+	priv->reset_deep_sleep_wakeup = NULL;
 	priv->fw_ready = 1;
 
 	/* Now actually get the IRQ */
diff --git a/drivers/net/wireless/libertas/if_sdio.c b/drivers/net/wireless/libertas/if_sdio.c
index 485a8d4..9716728 100644
--- a/drivers/net/wireless/libertas/if_sdio.c
+++ b/drivers/net/wireless/libertas/if_sdio.c
@@ -831,6 +831,58 @@ out:
 	return ret;
 }
 
+static int if_sdio_enter_deep_sleep(struct lbs_private *priv)
+{
+	int ret = -1;
+	struct cmd_header cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+
+	lbs_deb_sdio("send DEEP_SLEEP command\n");
+	ret = __lbs_cmd(priv, CMD_802_11_DEEP_SLEEP, &cmd, sizeof(cmd),
+			lbs_cmd_copyback, (unsigned long) &cmd);
+	if (ret)
+		lbs_pr_err("DEEP_SLEEP cmd failed\n");
+
+	mdelay(200);
+	return ret;
+}
+
+static int if_sdio_exit_deep_sleep(struct lbs_private *priv)
+{
+	struct if_sdio_card *card = priv->card;
+	int ret = -1;
+
+	lbs_deb_enter(LBS_DEB_SDIO);
+	sdio_claim_host(card->func);
+
+	sdio_writeb(card->func, HOST_POWER_UP, CONFIGURATION_REG, &ret);
+	if (ret)
+		lbs_pr_err("sdio_writeb failed!\n");
+
+	sdio_release_host(card->func);
+	lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
+	return ret;
+}
+
+static int if_sdio_reset_deep_sleep_wakeup(struct lbs_private *priv)
+{
+	struct if_sdio_card *card = priv->card;
+	int ret = -1;
+
+	lbs_deb_enter(LBS_DEB_SDIO);
+	sdio_claim_host(card->func);
+
+	sdio_writeb(card->func, 0, CONFIGURATION_REG, &ret);
+	if (ret)
+		lbs_pr_err("sdio_writeb failed!\n");
+
+	sdio_release_host(card->func);
+	lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
+	return ret;
+
+}
+
 /*******************************************************************/
 /* SDIO callbacks                                                  */
 /*******************************************************************/
@@ -859,6 +911,7 @@ static void if_sdio_interrupt(struct sdio_func *func)
 	 * Ignore the define name, this really means the card has
 	 * successfully received the command.
 	 */
+	card->priv->is_activity_detected = 1;
 	if (cause & IF_SDIO_H_INT_DNLD)
 		lbs_host_to_card_done(card->priv);
 
@@ -998,6 +1051,9 @@ static int if_sdio_probe(struct sdio_func *func,
 
 	priv->card = card;
 	priv->hw_host_to_card = if_sdio_host_to_card;
+	priv->enter_deep_sleep = if_sdio_enter_deep_sleep;
+	priv->exit_deep_sleep = if_sdio_exit_deep_sleep;
+	priv->reset_deep_sleep_wakeup = if_sdio_reset_deep_sleep_wakeup;
 
 	priv->fw_ready = 1;
 
diff --git a/drivers/net/wireless/libertas/if_sdio.h b/drivers/net/wireless/libertas/if_sdio.h
index 60c9b2f..12179c1 100644
--- a/drivers/net/wireless/libertas/if_sdio.h
+++ b/drivers/net/wireless/libertas/if_sdio.h
@@ -51,5 +51,6 @@
 #define IF_SDIO_EVENT           0x80fc
 
 #define IF_SDIO_BLOCK_SIZE	256
-
+#define CONFIGURATION_REG               0x03
+#define HOST_POWER_UP                   (0x1U << 1)
 #endif
diff --git a/drivers/net/wireless/libertas/if_spi.c b/drivers/net/wireless/libertas/if_spi.c
index 446e327..e2fa657 100644
--- a/drivers/net/wireless/libertas/if_spi.c
+++ b/drivers/net/wireless/libertas/if_spi.c
@@ -1117,6 +1117,9 @@ static int __devinit if_spi_probe(struct spi_device *spi)
 	card->priv = priv;
 	priv->card = card;
 	priv->hw_host_to_card = if_spi_host_to_card;
+	priv->enter_deep_sleep = NULL;
+	priv->exit_deep_sleep = NULL;
+	priv->reset_deep_sleep_wakeup = NULL;
 	priv->fw_ready = 1;
 
 	/* Initialize interrupt handling stuff. */
diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/libertas/if_usb.c
index 92bc8c5..a8262de 100644
--- a/drivers/net/wireless/libertas/if_usb.c
+++ b/drivers/net/wireless/libertas/if_usb.c
@@ -300,6 +300,9 @@ static int if_usb_probe(struct usb_interface *intf,
 	cardp->priv->fw_ready = 1;
 
 	priv->hw_host_to_card = if_usb_host_to_card;
+	priv->enter_deep_sleep = NULL;
+	priv->exit_deep_sleep = NULL;
+	priv->reset_deep_sleep_wakeup = NULL;
 #ifdef CONFIG_OLPC
 	if (machine_is_olpc())
 		priv->reset_card = if_usb_reset_olpc_card;
diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c
index 8df1cfd..3b14fcc 100644
--- a/drivers/net/wireless/libertas/main.c
+++ b/drivers/net/wireless/libertas/main.c
@@ -574,8 +574,10 @@ void lbs_host_to_card_done(struct lbs_private *priv)
 	priv->dnld_sent = DNLD_RES_RECEIVED;
 
 	/* Wake main thread if commands are pending */
-	if (!priv->cur_cmd || priv->tx_pending_len > 0)
-		wake_up_interruptible(&priv->waitq);
+	if (!priv->cur_cmd || priv->tx_pending_len > 0) {
+		if (!priv->wakeup_dev_required)
+			wake_up_interruptible(&priv->waitq);
+	}
 
 	spin_unlock_irqrestore(&priv->driver_lock, flags);
 	lbs_deb_leave(LBS_DEB_THREAD);
@@ -770,7 +772,8 @@ static int lbs_thread(void *data)
 			shouldsleep = 0;	/* We have a command response */
 		else if (priv->cur_cmd)
 			shouldsleep = 1;	/* Can't send a command; one already running */
-		else if (!list_empty(&priv->cmdpendingq))
+		else if (!list_empty(&priv->cmdpendingq) &&
+					!(priv->wakeup_dev_required))
 			shouldsleep = 0;	/* We have a command to send */
 		else if (__kfifo_len(priv->event_fifo))
 			shouldsleep = 0;	/* We have an event to process */
@@ -822,6 +825,26 @@ static int lbs_thread(void *data)
 		}
 		spin_unlock_irq(&priv->driver_lock);
 
+		/* Process hardware events, e.g. card removed, link lost */
+		spin_lock_irq(&priv->driver_lock);
+		while (__kfifo_len(priv->event_fifo)) {
+			u32 event;
+			__kfifo_get(priv->event_fifo, (unsigned char *) &event,
+				sizeof(event));
+			spin_unlock_irq(&priv->driver_lock);
+			lbs_process_event(priv, event);
+			spin_lock_irq(&priv->driver_lock);
+		}
+		spin_unlock_irq(&priv->driver_lock);
+
+		if (priv->wakeup_dev_required) {
+			lbs_deb_thread("Waking up device...\n");
+			/* Wake up device */
+			if (priv->exit_deep_sleep(priv))
+				lbs_deb_thread("Wakeup device failed\n");
+			continue;
+		}
+
 		/* command timeout stuff */
 		if (priv->cmd_timed_out && priv->cur_cmd) {
 			struct cmd_ctrl_node *cmdnode = priv->cur_cmd;
@@ -849,18 +872,7 @@ static int lbs_thread(void *data)
 		}
 		priv->cmd_timed_out = 0;
 
-		/* Process hardware events, e.g. card removed, link lost */
-		spin_lock_irq(&priv->driver_lock);
-		while (__kfifo_len(priv->event_fifo)) {
-			u32 event;
 
-			__kfifo_get(priv->event_fifo, (unsigned char *) &event,
-				sizeof(event));
-			spin_unlock_irq(&priv->driver_lock);
-			lbs_process_event(priv, event);
-			spin_lock_irq(&priv->driver_lock);
-		}
-		spin_unlock_irq(&priv->driver_lock);
 
 		if (!priv->fw_ready)
 			continue;
@@ -894,6 +906,9 @@ static int lbs_thread(void *data)
 		    (priv->psstate == PS_STATE_PRE_SLEEP))
 			continue;
 
+		if (priv->is_deep_sleep)
+			continue;
+
 		/* Execute the next command */
 		if (!priv->dnld_sent && !priv->cur_cmd)
 			lbs_execute_next_command(priv);
@@ -928,6 +943,7 @@ static int lbs_thread(void *data)
 	}
 
 	del_timer(&priv->command_timer);
+	del_timer(&priv->auto_deepsleep_timer);
 	wake_up_all(&priv->cmd_pending);
 
 	lbs_deb_leave(LBS_DEB_THREAD);
@@ -1050,6 +1066,60 @@ out:
 	lbs_deb_leave(LBS_DEB_CMD);
 }
 
+/**
+ *  This function put the device back to deep sleep mode when timer expires
+ *  and no activity (command, event, data etc.) is detected.
+ */
+static void auto_deepsleep_timer_fn(unsigned long data)
+{
+	struct lbs_private *priv = (struct lbs_private *)data;
+	int ret;
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	if (priv->is_activity_detected) {
+		priv->is_activity_detected = 0;
+	} else {
+		if (priv->is_auto_deep_sleep_enabled &&
+				(!priv->wakeup_dev_required) &&
+				(priv->connect_status != LBS_CONNECTED)) {
+			lbs_deb_main("Entering auto deep sleep mode...\n");
+			ret = lbs_prepare_and_send_command(priv,
+					CMD_802_11_DEEP_SLEEP, 0,
+					0, 0, NULL);
+		}
+	}
+	mod_timer(&priv->auto_deepsleep_timer , jiffies +
+				(priv->auto_deep_sleep_timeout * HZ)/1000);
+	lbs_deb_leave(LBS_DEB_CMD);
+}
+
+int lbs_enter_auto_deep_sleep(struct lbs_private *priv)
+{
+	lbs_deb_enter(LBS_DEB_SDIO);
+
+	priv->is_auto_deep_sleep_enabled = 1;
+	if (priv->is_deep_sleep)
+		priv->wakeup_dev_required = 1;
+	mod_timer(&priv->auto_deepsleep_timer ,
+			jiffies + (priv->auto_deep_sleep_timeout * HZ)/1000);
+
+	lbs_deb_leave(LBS_DEB_SDIO);
+	return 0;
+}
+
+int lbs_exit_auto_deep_sleep(struct lbs_private *priv)
+{
+	lbs_deb_enter(LBS_DEB_SDIO);
+
+	priv->is_auto_deep_sleep_enabled = 0;
+	priv->auto_deep_sleep_timeout = 0;
+	del_timer(&priv->auto_deepsleep_timer);
+
+	lbs_deb_leave(LBS_DEB_SDIO);
+	return 0;
+}
+
 static void lbs_sync_channel_worker(struct work_struct *work)
 {
 	struct lbs_private *priv = container_of(work, struct lbs_private,
@@ -1099,11 +1169,17 @@ static int lbs_init_adapter(struct lbs_private *priv)
 	priv->capability = WLAN_CAPABILITY_SHORT_PREAMBLE;
 	priv->psmode = LBS802_11POWERMODECAM;
 	priv->psstate = PS_STATE_FULL_POWER;
+	priv->is_deep_sleep = 0;
+	priv->is_auto_deep_sleep_enabled = 0;
+	priv->wakeup_dev_required = 0;
+	init_waitqueue_head(&priv->ds_awake_q);
 
 	mutex_init(&priv->lock);
 
 	setup_timer(&priv->command_timer, command_timer_fn,
 		(unsigned long)priv);
+	setup_timer(&priv->auto_deepsleep_timer, auto_deepsleep_timer_fn,
+			(unsigned long)priv);
 
 	INIT_LIST_HEAD(&priv->cmdfreeq);
 	INIT_LIST_HEAD(&priv->cmdpendingq);
@@ -1142,6 +1218,7 @@ static void lbs_free_adapter(struct lbs_private *priv)
 	if (priv->event_fifo)
 		kfifo_free(priv->event_fifo);
 	del_timer(&priv->command_timer);
+	del_timer(&priv->auto_deepsleep_timer);
 	kfree(priv->networks);
 	priv->networks = NULL;
 
@@ -1272,6 +1349,11 @@ void lbs_remove_card(struct lbs_private *priv)
 	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
 	wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
 
+	if (priv->is_deep_sleep) {
+		priv->is_deep_sleep = 0;
+		wake_up_interruptible(&priv->ds_awake_q);
+	}
+
 	/* Stop the thread servicing the interrupts */
 	priv->surpriseremoved = 1;
 	kthread_stop(priv->main_thread);
@@ -1392,6 +1474,7 @@ void lbs_stop_card(struct lbs_private *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);
diff --git a/drivers/net/wireless/libertas/scan.c b/drivers/net/wireless/libertas/scan.c
index 6c95af3..e468e15 100644
--- a/drivers/net/wireless/libertas/scan.c
+++ b/drivers/net/wireless/libertas/scan.c
@@ -950,6 +950,11 @@ int lbs_set_scan(struct net_device *dev, struct iw_request_info *info,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
 	if (!priv->radio_on) {
 		ret = -EINVAL;
 		goto out;
@@ -1017,6 +1022,12 @@ int lbs_get_scan(struct net_device *dev, struct iw_request_info *info,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		err = -EBUSY;
+		lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", err);
+		return err;
+	}
+
 	/* iwlist should wait until the current scan is finished */
 	if (priv->scan_channel)
 		return -EAGAIN;
diff --git a/drivers/net/wireless/libertas/wext.c b/drivers/net/wireless/libertas/wext.c
index be837a0..38a451e 100644
--- a/drivers/net/wireless/libertas/wext.c
+++ b/drivers/net/wireless/libertas/wext.c
@@ -45,6 +45,31 @@ static inline void lbs_cancel_association_work(struct lbs_private *priv)
 	priv->pending_assoc_req = NULL;
 }
 
+/**
+ *  @brief This function checks if the command is allowed.
+ *
+ *  @param priv         A pointer to lbs_private structure
+ *  @return             allowed or not allowed.
+ */
+
+int lbs_is_cmd_allowed(struct lbs_private *priv)
+{
+	int         ret = 1;
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	if (!priv->is_auto_deep_sleep_enabled) {
+		if (priv->is_deep_sleep) {
+			lbs_deb_wext("IOCTLS called when station"
+					"is in deep sleep\n");
+			ret = 0;
+		}
+	}
+
+	lbs_deb_leave(LBS_DEB_WEXT);
+	return ret;
+}
+
 
 /**
  *  @brief Find the channel frequency power info with specific channel
@@ -168,6 +193,11 @@ static int lbs_get_freq(struct net_device *dev, struct iw_request_info *info,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		lbs_deb_leave(LBS_DEB_WEXT);
+		return -EBUSY;
+	}
+
 	cfp = lbs_find_cfp_by_band_and_channel(priv, 0,
 					   priv->curbssparams.channel);
 
@@ -278,6 +308,12 @@ static int lbs_set_rts(struct net_device *dev, struct iw_request_info *info,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+		return ret;
+	}
+
 	if (vwrq->disabled)
 		val = MRVDRV_RTS_MAX_VALUE;
 
@@ -299,6 +335,11 @@ static int lbs_get_rts(struct net_device *dev, struct iw_request_info *info,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
 	ret = lbs_get_snmp_mib(priv, SNMP_MIB_OID_RTS_THRESHOLD, &val);
 	if (ret)
 		goto out;
@@ -321,6 +362,12 @@ static int lbs_set_frag(struct net_device *dev, struct iw_request_info *info,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+		return ret;
+	}
+
 	if (vwrq->disabled)
 		val = MRVDRV_FRAG_MAX_VALUE;
 
@@ -342,6 +389,11 @@ static int lbs_get_frag(struct net_device *dev, struct iw_request_info *info,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
 	ret = lbs_get_snmp_mib(priv, SNMP_MIB_OID_FRAG_THRESHOLD, &val);
 	if (ret)
 		goto out;
@@ -391,6 +443,11 @@ static int lbs_get_txpow(struct net_device *dev,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
 	if (!priv->radio_on) {
 		lbs_deb_wext("tx power off\n");
 		vwrq->value = 0;
@@ -424,6 +481,11 @@ static int lbs_set_retry(struct net_device *dev, struct iw_request_info *info,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
         if ((vwrq->flags & IW_RETRY_TYPE) != IW_RETRY_LIMIT)
                 return -EOPNOTSUPP;
 
@@ -472,6 +534,11 @@ static int lbs_get_retry(struct net_device *dev, struct iw_request_info *info,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
 	vwrq->disabled = 0;
 
 	if (vwrq->flags & IW_RETRY_LONG) {
@@ -709,6 +776,7 @@ static int lbs_set_power(struct net_device *dev, struct iw_request_info *info,
 			  struct iw_param *vwrq, char *extra)
 {
 	struct lbs_private *priv = dev->ml_priv;
+	int ret = 0;
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
@@ -737,8 +805,54 @@ static int lbs_set_power(struct net_device *dev, struct iw_request_info *info,
 		       "setting power timeout is not supported\n");
 		return -EINVAL;
 	} else if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_PERIOD) {
-		lbs_deb_wext("setting power period not supported\n");
-		return -EINVAL;
+		vwrq->value = vwrq->value / 1000;
+		if (!priv->enter_deep_sleep) {
+			lbs_pr_err("deep sleep feature is not implemented "
+					"for this interface driver\n");
+			return -EINVAL;
+		}
+
+		if (priv->connect_status == LBS_CONNECTED) {
+			if ((priv->is_auto_deep_sleep_enabled) &&
+						(vwrq->value == -1000)) {
+				lbs_exit_auto_deep_sleep(priv);
+				return 0;
+			} else {
+				lbs_pr_err("can't use deep sleep cmd in "
+						"connected state\n");
+				return -EINVAL;
+			}
+		}
+
+		if ((vwrq->value < 0) && (vwrq->value != -1000)) {
+			lbs_pr_err("unknown option\n");
+			return -EINVAL;
+		}
+
+		if (vwrq->value > 0) {
+			if (!priv->is_auto_deep_sleep_enabled) {
+				priv->is_activity_detected = 0;
+				priv->auto_deep_sleep_timeout = vwrq->value;
+				lbs_enter_auto_deep_sleep(priv);
+			} else {
+				priv->auto_deep_sleep_timeout = vwrq->value;
+				lbs_deb_debugfs("auto deep sleep: "
+						"already enabled\n");
+			}
+			return 0;
+		} else {
+			if (priv->is_auto_deep_sleep_enabled) {
+				lbs_exit_auto_deep_sleep(priv);
+				/* Try to exit deep sleep if auto */
+				/*deep sleep disabled */
+				ret = lbs_set_deep_sleep(priv, 0);
+			}
+			if (vwrq->value == 0)
+				ret = lbs_set_deep_sleep(priv, 1);
+			else if (vwrq->value == -1000)
+				ret = lbs_set_deep_sleep(priv, 0);
+			return ret;
+		}
 	}
 
 	if (priv->psmode != LBS802_11POWERMODECAM) {
@@ -752,6 +866,7 @@ static int lbs_set_power(struct net_device *dev, struct iw_request_info *info,
 	}
 
 	lbs_deb_leave(LBS_DEB_WEXT);
+
 	return 0;
 }
 
@@ -792,6 +907,9 @@ static struct iw_statistics *lbs_get_wireless_stats(struct net_device *dev)
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv))
+		return NULL;
+
 	priv->wstats.status = priv->mode;
 
 	/* If we're not associated, all quality values are meaningless */
@@ -892,6 +1010,12 @@ static int lbs_set_freq(struct net_device *dev, struct iw_request_info *info,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+		return ret;
+	}
+
 	mutex_lock(&priv->lock);
 	assoc_req = lbs_get_association_request(priv);
 	if (!assoc_req) {
@@ -1000,6 +1124,12 @@ static int lbs_set_rate(struct net_device *dev, struct iw_request_info *info,
 	u8 rates[MAX_RATES + 1];
 
 	lbs_deb_enter(LBS_DEB_WEXT);
+
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
 	lbs_deb_wext("vwrq->value %d\n", vwrq->value);
 	lbs_deb_wext("vwrq->fixed %d\n", vwrq->fixed);
 
@@ -1058,6 +1188,11 @@ static int lbs_get_rate(struct net_device *dev, struct iw_request_info *info,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		lbs_deb_leave(LBS_DEB_WEXT);
+		return -EBUSY;
+	}
+
 	if (priv->connect_status == LBS_CONNECTED) {
 		vwrq->value = priv->cur_rate * 500000;
 
@@ -1084,6 +1219,11 @@ static int lbs_set_mode(struct net_device *dev,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
 	if (   (*uwrq != IW_MODE_ADHOC)
 	    && (*uwrq != IW_MODE_INFRA)
 	    && (*uwrq != IW_MODE_AUTO)) {
@@ -1325,6 +1465,12 @@ static int lbs_set_encode(struct net_device *dev,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+		return ret;
+	}
+
 	mutex_lock(&priv->lock);
 	assoc_req = lbs_get_association_request(priv);
 	if (!assoc_req) {
@@ -1508,6 +1654,12 @@ static int lbs_set_encodeext(struct net_device *dev,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+		return ret;
+	}
+
 	mutex_lock(&priv->lock);
 	assoc_req = lbs_get_association_request(priv);
 	if (!assoc_req) {
@@ -1720,6 +1872,12 @@ static int lbs_set_auth(struct net_device *dev,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+		return ret;
+	}
+
 	mutex_lock(&priv->lock);
 	assoc_req = lbs_get_association_request(priv);
 	if (!assoc_req) {
@@ -1822,6 +1980,12 @@ static int lbs_get_auth(struct net_device *dev,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+		return ret;
+	}
+
 	switch (dwrq->flags & IW_AUTH_INDEX) {
 	case IW_AUTH_KEY_MGMT:
 		dwrq->value = priv->secinfo.key_mgmt;
@@ -1864,6 +2028,11 @@ static int lbs_set_txpow(struct net_device *dev, struct iw_request_info *info,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		goto out;
+	}
+
 	if (vwrq->disabled) {
 		lbs_set_radio(priv, RADIO_PREAMBLE_AUTO, 0);
 		goto out;
@@ -1983,6 +2152,12 @@ static int lbs_set_essid(struct net_device *dev, struct iw_request_info *info,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+		return ret;
+	}
+
 	if (!priv->radio_on) {
 		ret = -EINVAL;
 		goto out;
@@ -2110,6 +2285,12 @@ static int lbs_set_wap(struct net_device *dev, struct iw_request_info *info,
 
 	lbs_deb_enter(LBS_DEB_WEXT);
 
+	if (!lbs_is_cmd_allowed(priv)) {
+		ret = -EBUSY;
+		lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+		return ret;
+	}
+
 	if (!priv->radio_on)
 		return -EINVAL;
 
-- 
1.5.4.3



