Ver Fonte

* adding network config for olpc * adding libertas * config issue * quiet mode for bootloader

SVN-Revision: 9768
Jens Muecke há 18 anos atrás
pai
commit
7eeb4e6f7f
41 ficheiros alterados com 19037 adições e 12 exclusões
  1. 56 0
      package/libertas/Makefile
  2. 33 0
      package/libertas/files/LICENSE
  3. 700 0
      package/libertas/src/11d.c
  4. 105 0
      package/libertas/src/11d.h
  5. 62 0
      package/libertas/src/:0
  6. 16 0
      package/libertas/src/LICENSE
  7. 15 0
      package/libertas/src/Makefile
  8. 229 0
      package/libertas/src/README
  9. 766 0
      package/libertas/src/assoc.c
  10. 12 0
      package/libertas/src/assoc.h
  11. 2233 0
      package/libertas/src/cmd.c
  12. 38 0
      package/libertas/src/cmd.h
  13. 897 0
      package/libertas/src/cmdresp.c
  14. 17 0
      package/libertas/src/compat.h
  15. 1163 0
      package/libertas/src/debugfs.c
  16. 10 0
      package/libertas/src/debugfs.h
  17. 80 0
      package/libertas/src/decl.h
  18. 379 0
      package/libertas/src/defs.h
  19. 365 0
      package/libertas/src/dev.h
  20. 172 0
      package/libertas/src/ethtool.c
  21. 286 0
      package/libertas/src/host.h
  22. 713 0
      package/libertas/src/hostcmd.h
  23. 973 0
      package/libertas/src/if_cs.c
  24. 1079 0
      package/libertas/src/if_sdio.c
  25. 45 0
      package/libertas/src/if_sdio.h
  26. 1043 0
      package/libertas/src/if_usb.c
  27. 108 0
      package/libertas/src/if_usb.h
  28. 894 0
      package/libertas/src/join.c
  29. 53 0
      package/libertas/src/join.h
  30. 1474 0
      package/libertas/src/main.c
  31. 57 0
      package/libertas/src/radiotap.h
  32. 400 0
      package/libertas/src/rx.c
  33. 1643 0
      package/libertas/src/scan.c
  34. 205 0
      package/libertas/src/scan.h
  35. 221 0
      package/libertas/src/tx.c
  36. 242 0
      package/libertas/src/types.h
  37. 2213 0
      package/libertas/src/wext.c
  38. 23 0
      package/libertas/src/wext.h
  39. 11 0
      target/linux/olpc/base-files/etc/config/network
  40. 5 11
      target/linux/olpc/config-2.6.23
  41. 1 1
      target/linux/olpc/image/olpc.fth

+ 56 - 0
package/libertas/Makefile

@@ -0,0 +1,56 @@
+# 
+# Copyright (C) 2007 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+# $Id: Makefile 8694 2007-09-08 19:55:42Z nbd $
+
+include $(TOPDIR)/rules.mk
+include $(INCLUDE_DIR)/kernel.mk
+
+PKG_NAME:=kmod-libertas
+PKG_RELEASE:=1
+
+include $(INCLUDE_DIR)/package.mk
+
+define KernelPackage/libertas
+  SUBMENU:=Other modules
+  DEPENDS:=@TARGET_olpc +kmod-ieee80211
+  TITLE:=Marvell 88W8015 Wireless Driver
+  FILES:= \
+	$(PKG_BUILD_DIR)/libertas.$(LINUX_KMOD_SUFFIX)  \
+	$(PKG_BUILD_DIR)/usb8xxx.$(LINUX_KMOD_SUFFIX) 
+  AUTOLOAD:=$(call AutoLoad,20,libertas usb8xxx)
+endef
+
+define Download/firmware
+  URL:=http://dev.laptop.org/pub/firmware/libertas
+  FILE:=usb8388-5.220.11.p5.bin
+  MD5SUM=37cc814d5a475fcf8f8fbe89a9c5d546
+endef
+
+define Build/Prepare
+	mkdir -p $(PKG_BUILD_DIR)
+	$(CP) ./src/* $(PKG_BUILD_DIR)/
+endef
+
+define Build/Compile
+	$(MAKE) -C "$(LINUX_DIR)" \
+		CROSS_COMPILE="$(TARGET_CROSS)" \
+		ARCH="$(LINUX_KARCH)" \
+		SUBDIRS="$(PKG_BUILD_DIR)" \
+		CONFIG_LIBERTAS=m \
+		CONFIG_LIBERTAS_USB=m \
+		EXTRA_CFLAGS="-I$(PKG_BUILD_DIR) -include compat.h -I$(STAGING_DIR)/usr/include/mac80211" \
+		modules 
+endef
+
+define KernelPackage/libertas/install
+	$(INSTALL_DIR) $(1)/lib/firmware
+	$(INSTALL_BIN) $(DL_DIR)/usb8388-5.220.11.p5.bin $(1)/lib/firmware/usb8388.bin
+	$(INSTALL_DATA) ./files/LICENSE $(1)/lib/firmware/
+endef
+
+$(eval $(call KernelPackage,libertas))
+$(eval $(call Download,firmware))

+ 33 - 0
package/libertas/files/LICENSE

@@ -0,0 +1,33 @@
+Copyright (c) 2006, One Laptop per Child and Marvell Corporation.
+All rights reserved.
+
+Redistribution.  Redistribution and use in binary form, without 
+modification, are permitted provided that the following conditions are 
+met:
+
+* Redistributions must reproduce the above copyright notice and the 
+  following disclaimer in the documentation and/or other materials 
+  provided with the distribution.
+* Neither the name of Marvell Corporation nor the names of its suppliers 
+  may be used to endorse or promote products derived from this software 
+  without specific prior written permission.
+* No reverse engineering, decompilation, or disassembly of this software 
+  is permitted.
+* You may not use or attempt to use this software in conjunction with
+  any product that is offered by a third party as a replacement,
+  substitute or alternative to a Marvell Product where a Marvell Product
+  is defined as a proprietary wireless LAN embedded client solution of
+  Marvell or a Marvell Affiliate.
+
+DISCLAIMER.  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS 
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 
+TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE 
+USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 
+DAMAGE.

+ 700 - 0
package/libertas/src/11d.c

@@ -0,0 +1,700 @@
+/**
+  * This file contains functions for 802.11D.
+  */
+#include <linux/ctype.h>
+#include <linux/kernel.h>
+#include <linux/wireless.h>
+
+#include "host.h"
+#include "decl.h"
+#include "11d.h"
+#include "dev.h"
+#include "wext.h"
+
+#define TX_PWR_DEFAULT	10
+
+static struct region_code_mapping region_code_mapping[] = {
+	{"US ", 0x10},		/* US FCC      */
+	{"CA ", 0x10},		/* IC Canada   */
+	{"SG ", 0x10},		/* Singapore   */
+	{"EU ", 0x30},		/* ETSI        */
+	{"AU ", 0x30},		/* Australia   */
+	{"KR ", 0x30},		/* Republic Of Korea */
+	{"ES ", 0x31},		/* Spain       */
+	{"FR ", 0x32},		/* France      */
+	{"JP ", 0x40},		/* Japan       */
+};
+
+/* Following 2 structure defines the supported channels */
+static struct chan_freq_power channel_freq_power_UN_BG[] = {
+	{1, 2412, TX_PWR_DEFAULT},
+	{2, 2417, TX_PWR_DEFAULT},
+	{3, 2422, TX_PWR_DEFAULT},
+	{4, 2427, TX_PWR_DEFAULT},
+	{5, 2432, TX_PWR_DEFAULT},
+	{6, 2437, TX_PWR_DEFAULT},
+	{7, 2442, TX_PWR_DEFAULT},
+	{8, 2447, TX_PWR_DEFAULT},
+	{9, 2452, TX_PWR_DEFAULT},
+	{10, 2457, TX_PWR_DEFAULT},
+	{11, 2462, TX_PWR_DEFAULT},
+	{12, 2467, TX_PWR_DEFAULT},
+	{13, 2472, TX_PWR_DEFAULT},
+	{14, 2484, TX_PWR_DEFAULT}
+};
+
+static u8 lbs_region_2_code(u8 *region)
+{
+	u8 i;
+
+	for (i = 0; region[i] && i < COUNTRY_CODE_LEN; i++)
+		region[i] = toupper(region[i]);
+
+	for (i = 0; i < ARRAY_SIZE(region_code_mapping); i++) {
+		if (!memcmp(region, region_code_mapping[i].region,
+			    COUNTRY_CODE_LEN))
+			return (region_code_mapping[i].code);
+	}
+
+	/* default is US */
+	return (region_code_mapping[0].code);
+}
+
+static u8 *lbs_code_2_region(u8 code)
+{
+	u8 i;
+
+	for (i = 0; i < ARRAY_SIZE(region_code_mapping); i++) {
+		if (region_code_mapping[i].code == code)
+			return (region_code_mapping[i].region);
+	}
+	/* default is US */
+	return (region_code_mapping[0].region);
+}
+
+/**
+ *  @brief This function finds the nrchan-th chan after the firstchan
+ *  @param band       band
+ *  @param firstchan  first channel number
+ *  @param nrchan   number of channels
+ *  @return 	      the nrchan-th chan number
+*/
+static u8 lbs_get_chan_11d(u8 band, u8 firstchan, u8 nrchan, u8 *chan)
+/*find the nrchan-th chan after the firstchan*/
+{
+	u8 i;
+	struct chan_freq_power *cfp;
+	u8 cfp_no;
+
+	cfp = channel_freq_power_UN_BG;
+	cfp_no = ARRAY_SIZE(channel_freq_power_UN_BG);
+
+	for (i = 0; i < cfp_no; i++) {
+		if ((cfp + i)->channel == firstchan) {
+			lbs_deb_11d("firstchan found\n");
+			break;
+		}
+	}
+
+	if (i < cfp_no) {
+		/*if beyond the boundary */
+		if (i + nrchan < cfp_no) {
+			*chan = (cfp + i + nrchan)->channel;
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ *  @brief This function Checks if chan txpwr is learned from AP/IBSS
+ *  @param chan                 chan number
+ *  @param parsed_region_chan   pointer to parsed_region_chan_11d
+ *  @return 	                TRUE; FALSE
+*/
+static u8 lbs_channel_known_11d(u8 chan,
+			  struct parsed_region_chan_11d * parsed_region_chan)
+{
+	struct chan_power_11d *chanpwr = parsed_region_chan->chanpwr;
+	u8 nr_chan = parsed_region_chan->nr_chan;
+	u8 i = 0;
+
+	lbs_deb_hex(LBS_DEB_11D, "parsed_region_chan", (char *)chanpwr,
+		sizeof(struct chan_power_11d) * nr_chan);
+
+	for (i = 0; i < nr_chan; i++) {
+		if (chan == chanpwr[i].chan) {
+			lbs_deb_11d("found chan %d\n", chan);
+			return 1;
+		}
+	}
+
+	lbs_deb_11d("chan %d not found\n", chan);
+	return 0;
+}
+
+u32 lbs_chan_2_freq(u8 chan, u8 band)
+{
+	struct chan_freq_power *cf;
+	u16 i;
+	u32 freq = 0;
+
+	cf = channel_freq_power_UN_BG;
+
+	for (i = 0; i < ARRAY_SIZE(channel_freq_power_UN_BG); i++) {
+		if (chan == cf[i].channel)
+			freq = cf[i].freq;
+	}
+
+	return freq;
+}
+
+static int generate_domain_info_11d(struct parsed_region_chan_11d
+				  *parsed_region_chan,
+				  struct lbs_802_11d_domain_reg *domaininfo)
+{
+	u8 nr_subband = 0;
+
+	u8 nr_chan = parsed_region_chan->nr_chan;
+	u8 nr_parsedchan = 0;
+
+	u8 firstchan = 0, nextchan = 0, maxpwr = 0;
+
+	u8 i, flag = 0;
+
+	memcpy(domaininfo->countrycode, parsed_region_chan->countrycode,
+	       COUNTRY_CODE_LEN);
+
+	lbs_deb_11d("nrchan %d\n", nr_chan);
+	lbs_deb_hex(LBS_DEB_11D, "parsed_region_chan", (char *)parsed_region_chan,
+		sizeof(struct parsed_region_chan_11d));
+
+	for (i = 0; i < nr_chan; i++) {
+		if (!flag) {
+			flag = 1;
+			nextchan = firstchan =
+			    parsed_region_chan->chanpwr[i].chan;
+			maxpwr = parsed_region_chan->chanpwr[i].pwr;
+			nr_parsedchan = 1;
+			continue;
+		}
+
+		if (parsed_region_chan->chanpwr[i].chan == nextchan + 1 &&
+		    parsed_region_chan->chanpwr[i].pwr == maxpwr) {
+			nextchan++;
+			nr_parsedchan++;
+		} else {
+			domaininfo->subband[nr_subband].firstchan = firstchan;
+			domaininfo->subband[nr_subband].nrchan =
+			    nr_parsedchan;
+			domaininfo->subband[nr_subband].maxtxpwr = maxpwr;
+			nr_subband++;
+			nextchan = firstchan =
+			    parsed_region_chan->chanpwr[i].chan;
+			maxpwr = parsed_region_chan->chanpwr[i].pwr;
+		}
+	}
+
+	if (flag) {
+		domaininfo->subband[nr_subband].firstchan = firstchan;
+		domaininfo->subband[nr_subband].nrchan = nr_parsedchan;
+		domaininfo->subband[nr_subband].maxtxpwr = maxpwr;
+		nr_subband++;
+	}
+	domaininfo->nr_subband = nr_subband;
+
+	lbs_deb_11d("nr_subband=%x\n", domaininfo->nr_subband);
+	lbs_deb_hex(LBS_DEB_11D, "domaininfo", (char *)domaininfo,
+		COUNTRY_CODE_LEN + 1 +
+		sizeof(struct ieeetypes_subbandset) * nr_subband);
+	return 0;
+}
+
+/**
+ *  @brief This function generates parsed_region_chan from Domain Info learned from AP/IBSS
+ *  @param region_chan          pointer to struct region_channel
+ *  @param *parsed_region_chan  pointer to parsed_region_chan_11d
+ *  @return 	                N/A
+*/
+static void lbs_generate_parsed_region_chan_11d(struct region_channel *region_chan,
+					  struct parsed_region_chan_11d *
+					  parsed_region_chan)
+{
+	u8 i;
+	struct chan_freq_power *cfp;
+
+	if (region_chan == NULL) {
+		lbs_deb_11d("region_chan is NULL\n");
+		return;
+	}
+
+	cfp = region_chan->CFP;
+	if (cfp == NULL) {
+		lbs_deb_11d("cfp is NULL \n");
+		return;
+	}
+
+	parsed_region_chan->band = region_chan->band;
+	parsed_region_chan->region = region_chan->region;
+	memcpy(parsed_region_chan->countrycode,
+	       lbs_code_2_region(region_chan->region), COUNTRY_CODE_LEN);
+
+	lbs_deb_11d("region 0x%x, band %d\n", parsed_region_chan->region,
+	       parsed_region_chan->band);
+
+	for (i = 0; i < region_chan->nrcfp; i++, cfp++) {
+		parsed_region_chan->chanpwr[i].chan = cfp->channel;
+		parsed_region_chan->chanpwr[i].pwr = cfp->maxtxpower;
+		lbs_deb_11d("chan %d, pwr %d\n",
+		       parsed_region_chan->chanpwr[i].chan,
+		       parsed_region_chan->chanpwr[i].pwr);
+	}
+	parsed_region_chan->nr_chan = region_chan->nrcfp;
+
+	lbs_deb_11d("nrchan %d\n", parsed_region_chan->nr_chan);
+
+	return;
+}
+
+/**
+ *  @brief generate parsed_region_chan from Domain Info learned from AP/IBSS
+ *  @param region               region ID
+ *  @param band                 band
+ *  @param chan                 chan
+ *  @return 	                TRUE;FALSE
+*/
+static u8 lbs_region_chan_supported_11d(u8 region, u8 band, u8 chan)
+{
+	struct chan_freq_power *cfp;
+	int cfp_no;
+	u8 idx;
+	int ret = 0;
+
+	lbs_deb_enter(LBS_DEB_11D);
+
+	cfp = lbs_get_region_cfp_table(region, band, &cfp_no);
+	if (cfp == NULL)
+		return 0;
+
+	for (idx = 0; idx < cfp_no; idx++) {
+		if (chan == (cfp + idx)->channel) {
+			/* If Mrvl Chip Supported? */
+			if ((cfp + idx)->unsupported) {
+				ret = 0;
+			} else {
+				ret = 1;
+			}
+			goto done;
+		}
+	}
+
+	/*chan is not in the region table */
+
+done:
+	lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret);
+	return ret;
+}
+
+/**
+ *  @brief This function checks if chan txpwr is learned from AP/IBSS
+ *  @param chan                 chan number
+ *  @param parsed_region_chan   pointer to parsed_region_chan_11d
+ *  @return 	                0
+*/
+static int parse_domain_info_11d(struct ieeetypes_countryinfofullset*
+				 countryinfo,
+				 u8 band,
+				 struct parsed_region_chan_11d *
+				 parsed_region_chan)
+{
+	u8 nr_subband, nrchan;
+	u8 lastchan, firstchan;
+	u8 region;
+	u8 curchan = 0;
+
+	u8 idx = 0;		/*chan index in parsed_region_chan */
+
+	u8 j, i;
+
+	lbs_deb_enter(LBS_DEB_11D);
+
+	/*validation Rules:
+	   1. valid region Code
+	   2. First Chan increment
+	   3. channel range no overlap
+	   4. channel is valid?
+	   5. channel is supported by region?
+	   6. Others
+	 */
+
+	lbs_deb_hex(LBS_DEB_11D, "countryinfo", (u8 *) countryinfo, 30);
+
+	if ((*(countryinfo->countrycode)) == 0
+	    || (countryinfo->len <= COUNTRY_CODE_LEN)) {
+		/* No region Info or Wrong region info: treat as No 11D info */
+		goto done;
+	}
+
+	/*Step1: check region_code */
+	parsed_region_chan->region = region =
+	    lbs_region_2_code(countryinfo->countrycode);
+
+	lbs_deb_11d("regioncode=%x\n", (u8) parsed_region_chan->region);
+	lbs_deb_hex(LBS_DEB_11D, "countrycode", (char *)countryinfo->countrycode,
+		COUNTRY_CODE_LEN);
+
+	parsed_region_chan->band = band;
+
+	memcpy(parsed_region_chan->countrycode, countryinfo->countrycode,
+	       COUNTRY_CODE_LEN);
+
+	nr_subband = (countryinfo->len - COUNTRY_CODE_LEN) /
+	    sizeof(struct ieeetypes_subbandset);
+
+	for (j = 0, lastchan = 0; j < nr_subband; j++) {
+
+		if (countryinfo->subband[j].firstchan <= lastchan) {
+			/*Step2&3. Check First Chan Num increment and no overlap */
+			lbs_deb_11d("chan %d>%d, overlap\n",
+			       countryinfo->subband[j].firstchan, lastchan);
+			continue;
+		}
+
+		firstchan = countryinfo->subband[j].firstchan;
+		nrchan = countryinfo->subband[j].nrchan;
+
+		for (i = 0; idx < MAX_NO_OF_CHAN && i < nrchan; i++) {
+			/*step4: channel is supported? */
+
+			if (!lbs_get_chan_11d(band, firstchan, i, &curchan)) {
+				/* Chan is not found in UN table */
+				lbs_deb_11d("chan is not supported: %d \n", i);
+				break;
+			}
+
+			lastchan = curchan;
+
+			if (lbs_region_chan_supported_11d
+			    (region, band, curchan)) {
+				/*step5: Check if curchan is supported by mrvl in region */
+				parsed_region_chan->chanpwr[idx].chan = curchan;
+				parsed_region_chan->chanpwr[idx].pwr =
+				    countryinfo->subband[j].maxtxpwr;
+				idx++;
+			} else {
+				/*not supported and ignore the chan */
+				lbs_deb_11d(
+				       "i %d, chan %d unsupported in region %x, band %d\n",
+				       i, curchan, region, band);
+			}
+		}
+
+		/*Step6: Add other checking if any */
+
+	}
+
+	parsed_region_chan->nr_chan = idx;
+
+	lbs_deb_11d("nrchan=%x\n", parsed_region_chan->nr_chan);
+	lbs_deb_hex(LBS_DEB_11D, "parsed_region_chan", (u8 *) parsed_region_chan,
+		2 + COUNTRY_CODE_LEN + sizeof(struct parsed_region_chan_11d) * idx);
+
+done:
+	lbs_deb_enter(LBS_DEB_11D);
+	return 0;
+}
+
+/**
+ *  @brief This function calculates the scan type for channels
+ *  @param chan                 chan number
+ *  @param parsed_region_chan   pointer to parsed_region_chan_11d
+ *  @return 	                PASSIVE if chan is unknown; ACTIVE if chan is known
+*/
+u8 lbs_get_scan_type_11d(u8 chan,
+			  struct parsed_region_chan_11d * parsed_region_chan)
+{
+	u8 scan_type = CMD_SCAN_TYPE_PASSIVE;
+
+	lbs_deb_enter(LBS_DEB_11D);
+
+	if (lbs_channel_known_11d(chan, parsed_region_chan)) {
+		lbs_deb_11d("found, do active scan\n");
+		scan_type = CMD_SCAN_TYPE_ACTIVE;
+	} else {
+		lbs_deb_11d("not found, do passive scan\n");
+	}
+
+	lbs_deb_leave_args(LBS_DEB_11D, "ret scan_type %d", scan_type);
+	return scan_type;
+
+}
+
+void lbs_init_11d(struct lbs_private *priv)
+{
+	priv->enable11d = 0;
+	memset(&(priv->parsed_region_chan), 0,
+	       sizeof(struct parsed_region_chan_11d));
+	return;
+}
+
+/**
+ *  @brief This function sets DOMAIN INFO to FW
+ *  @param priv       pointer to struct lbs_private
+ *  @return 	      0; -1
+*/
+static int set_domain_info_11d(struct lbs_private *priv)
+{
+	int ret;
+
+	if (!priv->enable11d) {
+		lbs_deb_11d("dnld domain Info with 11d disabled\n");
+		return 0;
+	}
+
+	ret = lbs_prepare_and_send_command(priv, CMD_802_11D_DOMAIN_INFO,
+				    CMD_ACT_SET,
+				    CMD_OPTION_WAITFORRSP, 0, NULL);
+	if (ret)
+		lbs_deb_11d("fail to dnld domain info\n");
+
+	return ret;
+}
+
+/**
+ *  @brief This function setups scan channels
+ *  @param priv       pointer to struct lbs_private
+ *  @param band       band
+ *  @return 	      0
+*/
+int lbs_set_universaltable(struct lbs_private *priv, u8 band)
+{
+	u16 size = sizeof(struct chan_freq_power);
+	u16 i = 0;
+
+	memset(priv->universal_channel, 0,
+	       sizeof(priv->universal_channel));
+
+	priv->universal_channel[i].nrcfp =
+	    sizeof(channel_freq_power_UN_BG) / size;
+	lbs_deb_11d("BG-band nrcfp %d\n",
+	       priv->universal_channel[i].nrcfp);
+
+	priv->universal_channel[i].CFP = channel_freq_power_UN_BG;
+	priv->universal_channel[i].valid = 1;
+	priv->universal_channel[i].region = UNIVERSAL_REGION_CODE;
+	priv->universal_channel[i].band = band;
+	i++;
+
+	return 0;
+}
+
+/**
+ *  @brief This function implements command CMD_802_11D_DOMAIN_INFO
+ *  @param priv       pointer to struct lbs_private
+ *  @param cmd        pointer to cmd buffer
+ *  @param cmdno      cmd ID
+ *  @param cmdOption  cmd action
+ *  @return 	      0
+*/
+int lbs_cmd_802_11d_domain_info(struct lbs_private *priv,
+				 struct cmd_ds_command *cmd, u16 cmdno,
+				 u16 cmdoption)
+{
+	struct cmd_ds_802_11d_domain_info *pdomaininfo =
+	    &cmd->params.domaininfo;
+	struct mrvlietypes_domainparamset *domain = &pdomaininfo->domain;
+	u8 nr_subband = priv->domainreg.nr_subband;
+
+	lbs_deb_enter(LBS_DEB_11D);
+
+	lbs_deb_11d("nr_subband=%x\n", nr_subband);
+
+	cmd->command = cpu_to_le16(cmdno);
+	pdomaininfo->action = cpu_to_le16(cmdoption);
+	if (cmdoption == CMD_ACT_GET) {
+		cmd->size =
+		    cpu_to_le16(sizeof(pdomaininfo->action) + S_DS_GEN);
+		lbs_deb_hex(LBS_DEB_11D, "802_11D_DOMAIN_INFO", (u8 *) cmd,
+			le16_to_cpu(cmd->size));
+		goto done;
+	}
+
+	domain->header.type = cpu_to_le16(TLV_TYPE_DOMAIN);
+	memcpy(domain->countrycode, priv->domainreg.countrycode,
+	       sizeof(domain->countrycode));
+
+	domain->header.len =
+	    cpu_to_le16(nr_subband * sizeof(struct ieeetypes_subbandset) +
+			     sizeof(domain->countrycode));
+
+	if (nr_subband) {
+		memcpy(domain->subband, priv->domainreg.subband,
+		       nr_subband * sizeof(struct ieeetypes_subbandset));
+
+		cmd->size = cpu_to_le16(sizeof(pdomaininfo->action) +
+					     le16_to_cpu(domain->header.len) +
+					     sizeof(struct mrvlietypesheader) +
+					     S_DS_GEN);
+	} else {
+		cmd->size =
+		    cpu_to_le16(sizeof(pdomaininfo->action) + S_DS_GEN);
+	}
+
+	lbs_deb_hex(LBS_DEB_11D, "802_11D_DOMAIN_INFO", (u8 *) cmd, le16_to_cpu(cmd->size));
+
+done:
+	lbs_deb_enter(LBS_DEB_11D);
+	return 0;
+}
+
+/**
+ *  @brief This function parses countryinfo from AP and download country info to FW
+ *  @param priv    pointer to struct lbs_private
+ *  @param resp    pointer to command response buffer
+ *  @return 	   0; -1
+ */
+int lbs_ret_802_11d_domain_info(struct lbs_private *priv,
+				 struct cmd_ds_command *resp)
+{
+	struct cmd_ds_802_11d_domain_info *domaininfo = &resp->params.domaininforesp;
+	struct mrvlietypes_domainparamset *domain = &domaininfo->domain;
+	u16 action = le16_to_cpu(domaininfo->action);
+	s16 ret = 0;
+	u8 nr_subband = 0;
+
+	lbs_deb_enter(LBS_DEB_11D);
+
+	lbs_deb_hex(LBS_DEB_11D, "domain info resp", (u8 *) resp,
+		(int)le16_to_cpu(resp->size));
+
+	nr_subband = (le16_to_cpu(domain->header.len) - COUNTRY_CODE_LEN) /
+		      sizeof(struct ieeetypes_subbandset);
+
+	lbs_deb_11d("domain info resp: nr_subband %d\n", nr_subband);
+
+	if (nr_subband > MRVDRV_MAX_SUBBAND_802_11D) {
+		lbs_deb_11d("Invalid Numrer of Subband returned!!\n");
+		return -1;
+	}
+
+	switch (action) {
+	case CMD_ACT_SET:	/*Proc Set action */
+		break;
+
+	case CMD_ACT_GET:
+		break;
+	default:
+		lbs_deb_11d("Invalid action:%d\n", domaininfo->action);
+		ret = -1;
+		break;
+	}
+
+	lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret);
+	return ret;
+}
+
+/**
+ *  @brief This function parses countryinfo from AP and download country info to FW
+ *  @param priv    pointer to struct lbs_private
+ *  @return 	   0; -1
+ */
+int lbs_parse_dnld_countryinfo_11d(struct lbs_private *priv,
+                                        struct bss_descriptor * bss)
+{
+	int ret;
+
+	lbs_deb_enter(LBS_DEB_11D);
+	if (priv->enable11d) {
+		memset(&priv->parsed_region_chan, 0,
+		       sizeof(struct parsed_region_chan_11d));
+		ret = parse_domain_info_11d(&bss->countryinfo, 0,
+					       &priv->parsed_region_chan);
+
+		if (ret == -1) {
+			lbs_deb_11d("error parsing domain_info from AP\n");
+			goto done;
+		}
+
+		memset(&priv->domainreg, 0,
+		       sizeof(struct lbs_802_11d_domain_reg));
+		generate_domain_info_11d(&priv->parsed_region_chan,
+				      &priv->domainreg);
+
+		ret = set_domain_info_11d(priv);
+
+		if (ret) {
+			lbs_deb_11d("error setting domain info\n");
+			goto done;
+		}
+	}
+	ret = 0;
+
+done:
+	lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret);
+	return ret;
+}
+
+/**
+ *  @brief This function generates 11D info from user specified regioncode and download to FW
+ *  @param priv    pointer to struct lbs_private
+ *  @return 	   0; -1
+ */
+int lbs_create_dnld_countryinfo_11d(struct lbs_private *priv)
+{
+	int ret;
+	struct region_channel *region_chan;
+	u8 j;
+
+	lbs_deb_enter(LBS_DEB_11D);
+	lbs_deb_11d("curbssparams.band %d\n", priv->curbssparams.band);
+
+	if (priv->enable11d) {
+		/* update parsed_region_chan_11; dnld domaininf to FW */
+
+		for (j = 0; j < ARRAY_SIZE(priv->region_channel); j++) {
+			region_chan = &priv->region_channel[j];
+
+			lbs_deb_11d("%d region_chan->band %d\n", j,
+			       region_chan->band);
+
+			if (!region_chan || !region_chan->valid
+			    || !region_chan->CFP)
+				continue;
+			if (region_chan->band != priv->curbssparams.band)
+				continue;
+			break;
+		}
+
+		if (j >= ARRAY_SIZE(priv->region_channel)) {
+			lbs_deb_11d("region_chan not found, band %d\n",
+			       priv->curbssparams.band);
+			ret = -1;
+			goto done;
+		}
+
+		memset(&priv->parsed_region_chan, 0,
+		       sizeof(struct parsed_region_chan_11d));
+		lbs_generate_parsed_region_chan_11d(region_chan,
+						     &priv->
+						     parsed_region_chan);
+
+		memset(&priv->domainreg, 0,
+		       sizeof(struct lbs_802_11d_domain_reg));
+		generate_domain_info_11d(&priv->parsed_region_chan,
+					 &priv->domainreg);
+
+		ret = set_domain_info_11d(priv);
+
+		if (ret) {
+			lbs_deb_11d("error setting domain info\n");
+			goto done;
+		}
+
+	}
+	ret = 0;
+
+done:
+	lbs_deb_leave_args(LBS_DEB_11D, "ret %d", ret);
+	return ret;
+}

+ 105 - 0
package/libertas/src/11d.h

@@ -0,0 +1,105 @@
+/**
+  * This header file contains data structures and
+  * function declarations of 802.11d
+  */
+#ifndef _LBS_11D_
+#define _LBS_11D_
+
+#include "types.h"
+#include "defs.h"
+
+#define UNIVERSAL_REGION_CODE			0xff
+
+/** (Beaconsize(256)-5(IEId,len,contrystr(3))/3(FirstChan,NoOfChan,MaxPwr)
+ */
+#define MRVDRV_MAX_SUBBAND_802_11D		83
+
+#define COUNTRY_CODE_LEN			3
+#define MAX_NO_OF_CHAN 				40
+
+struct cmd_ds_command;
+
+/** Data structure for Country IE*/
+struct ieeetypes_subbandset {
+	u8 firstchan;
+	u8 nrchan;
+	u8 maxtxpwr;
+} __attribute__ ((packed));
+
+struct ieeetypes_countryinfoset {
+	u8 element_id;
+	u8 len;
+	u8 countrycode[COUNTRY_CODE_LEN];
+	struct ieeetypes_subbandset subband[1];
+};
+
+struct ieeetypes_countryinfofullset {
+	u8 element_id;
+	u8 len;
+	u8 countrycode[COUNTRY_CODE_LEN];
+	struct ieeetypes_subbandset subband[MRVDRV_MAX_SUBBAND_802_11D];
+} __attribute__ ((packed));
+
+struct mrvlietypes_domainparamset {
+	struct mrvlietypesheader header;
+	u8 countrycode[COUNTRY_CODE_LEN];
+	struct ieeetypes_subbandset subband[1];
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11d_domain_info {
+	__le16 action;
+	struct mrvlietypes_domainparamset domain;
+} __attribute__ ((packed));
+
+/** domain regulatory information */
+struct lbs_802_11d_domain_reg {
+	/** country Code*/
+	u8 countrycode[COUNTRY_CODE_LEN];
+	/** No. of subband*/
+	u8 nr_subband;
+	struct ieeetypes_subbandset subband[MRVDRV_MAX_SUBBAND_802_11D];
+};
+
+struct chan_power_11d {
+	u8 chan;
+	u8 pwr;
+} __attribute__ ((packed));
+
+struct parsed_region_chan_11d {
+	u8 band;
+	u8 region;
+	s8 countrycode[COUNTRY_CODE_LEN];
+	struct chan_power_11d chanpwr[MAX_NO_OF_CHAN];
+	u8 nr_chan;
+} __attribute__ ((packed));
+
+struct region_code_mapping {
+	u8 region[COUNTRY_CODE_LEN];
+	u8 code;
+};
+
+struct lbs_private;
+
+u8 lbs_get_scan_type_11d(u8 chan,
+			  struct parsed_region_chan_11d *parsed_region_chan);
+
+u32 lbs_chan_2_freq(u8 chan, u8 band);
+
+void lbs_init_11d(struct lbs_private *priv);
+
+int lbs_set_universaltable(struct lbs_private *priv, u8 band);
+
+int lbs_cmd_802_11d_domain_info(struct lbs_private *priv,
+				 struct cmd_ds_command *cmd, u16 cmdno,
+				 u16 cmdOption);
+
+int lbs_ret_802_11d_domain_info(struct lbs_private *priv,
+				 struct cmd_ds_command *resp);
+
+struct bss_descriptor;
+int lbs_parse_dnld_countryinfo_11d(struct lbs_private *priv,
+                                        struct bss_descriptor * bss);
+
+int lbs_create_dnld_countryinfo_11d(struct lbs_private *priv);
+
+#endif

+ 62 - 0
package/libertas/src/:0

@@ -0,0 +1,62 @@
+# 
+# Copyright (C) 2007 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+# $Id: Makefile 8694 2007-09-08 19:55:42Z nbd $
+
+include $(TOPDIR)/rules.mk
+include $(INCLUDE_DIR)/kernel.mk
+
+PKG_NAME:=kmod-libertas
+PKG_RELEASE:=1
+
+include $(INCLUDE_DIR)/package.mk
+
+define KernelPackage/libertas
+  SUBMENU:=Other modules
+  DEPENDS:=@TARGET_olpc +kmod-mac80211
+  TITLE:=Marvell 88W8015 Wireless Driver
+  FILES:= \
+	$(PKG_BUILD_DIR)/libertas.$(LINUX_KMOD_SUFFIX)  \
+	$(PKG_BUILD_DIR)/usb8xxx.$(LINUX_KMOD_SUFFIX) 
+#  AUTOLOAD:=$(call AutoLoad,20,switch-core switch-robo switch-adm)
+endef
+
+define Download/firmware
+  URL:=http://dev.laptop.org/pub/firmware/libertas
+  FILE:=usb8388-5.220.11.p5.bin
+  MD5SUM=123
+endef
+
+define Download/firmware_license
+  URL:=http://dev.laptop.org/pub/firmware/libertas
+  FILE:=LICENSE
+  MD5SUM=123
+endef
+
+define Build/Prepare
+	mkdir -p $(PKG_BUILD_DIR)
+	$(CP) ./src/* $(PKG_BUILD_DIR)/
+endef
+
+define Build/Compile
+	$(MAKE) -C "$(LINUX_DIR)" \
+		CROSS_COMPILE="$(TARGET_CROSS)" \
+		ARCH="$(LINUX_KARCH)" \
+		SUBDIRS="$(PKG_BUILD_DIR)" \
+		CONFIG_LIBERTAS=m \
+		CONFIG_LIBERTAS_USB=m \
+		EXTRA_CFLAGS="-I$(PKG_BUILD_DIR) -include compat.h -I$(STAGING_DIR)/usr/include/mac80211" \
+		modules 
+endef
+
+define KernelPackage/libertas/install
+	$(INSTALL_DIR) $(1)/lib/firmware
+	$(INSTALL_BIN) $(DL_DIR)/usb8388-5.220.11.p5.bin $(1)/lib/firmware/usb8388.bin
+	$(INSTALL_BIN) $(DL_DIR)/LICENSE $(1)/lib/firmware/
+endef
+
+$(eval $(call KernelPackage,libertas))
+$(eval $(call Download,firmware))

+ 16 - 0
package/libertas/src/LICENSE

@@ -0,0 +1,16 @@
+  Copyright (c) 2003-2006, Marvell International Ltd.
+  All Rights Reserved
+
+  This program is free software; you can redistribute it and/or modify it
+  under the terms of version 2 of the GNU General Public License as
+  published by the Free Software Foundation.
+
+  This program is distributed in the hope that it will be useful, but WITHOUT
+  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+  more details.
+
+  You should have received a copy of the GNU General Public License along with
+  this program; if not, write to the Free Software Foundation, Inc., 59
+  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+

+ 15 - 0
package/libertas/src/Makefile

@@ -0,0 +1,15 @@
+libertas-objs := main.o wext.o \
+		rx.o tx.o cmd.o 	  \
+		cmdresp.o scan.o	  \
+		join.o 11d.o 		  \
+		debugfs.o	  \
+		ethtool.o assoc.o
+
+usb8xxx-objs += if_usb.o
+libertas_cs-objs += if_cs.o
+libertas_sdio-objs += if_sdio.o
+
+obj-$(CONFIG_LIBERTAS)     += libertas.o
+obj-$(CONFIG_LIBERTAS_USB) += usb8xxx.o
+obj-$(CONFIG_LIBERTAS_CS)  += libertas_cs.o
+obj-$(CONFIG_LIBERTAS_SDIO) += libertas_sdio.o

+ 229 - 0
package/libertas/src/README

@@ -0,0 +1,229 @@
+================================================================================
+			README for USB8388
+
+ (c) Copyright © 2003-2006, Marvell International Ltd.
+ All Rights Reserved
+
+ This software file (the "File") is distributed by Marvell International
+ Ltd. under the terms of the GNU General Public License Version 2, June 1991
+ (the "License").  You may use, redistribute and/or modify this File in
+ accordance with the terms and conditions of the License, a copy of which
+ is available along with the File in the license.txt file or by writing to
+ the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ 02111-1307 or on the worldwide web at http://www.gnu.org/licenses/gpl.txt.
+
+ THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ ARE EXPRESSLY DISCLAIMED.  The License provides additional details about
+ this warranty disclaimer.
+================================================================================
+
+=====================
+DRIVER LOADING
+=====================
+
+	o. Copy the firmware image (e.g. usb8388.bin) to /lib/firmware/
+
+	o. Load driver by using the following command:
+
+		insmod usb8388.ko [fw_name=usb8388.bin]
+
+=========================
+ETHTOOL
+=========================
+
+
+Use the -i option to retrieve version information from the driver.
+
+# ethtool -i eth0
+driver: libertas
+version: COMM-USB8388-318.p4
+firmware-version: 5.110.7
+bus-info:
+
+Use the -e option to read the EEPROM contents of the card.
+
+	Usage:
+	ethtool -e ethX [raw on|off] [offset N] [length N]
+
+       -e     retrieves and prints an EEPROM dump for the  specified  ethernet
+              device.   When raw is enabled, then it dumps the raw EEPROM data
+              to stdout. The length and offset parameters allow  dumping  cer-
+              tain portions of the EEPROM.  Default is to dump the entire EEP-
+              ROM.
+
+# ethtool -e eth0 offset 0 length 16
+Offset          Values
+------          ------
+0x0000          38 33 30 58 00 00 34 f4 00 00 10 00 00 c4 17 00
+
+========================
+DEBUGFS COMMANDS
+========================
+
+those commands are used via debugfs interface
+
+===========
+rdmac
+rdbbp
+rdrf
+	These commands are used to read the MAC, BBP and RF registers from the
+	card.  These commands take one parameter that specifies the offset
+	location that is to be read.  This parameter must be specified in
+	hexadecimal (its possible to preceed preceding the number with a "0x").
+
+	Path: /debugfs/libertas_wireless/ethX/registers/
+
+	Usage:
+		echo "0xa123" > rdmac ; cat rdmac
+		echo "0xa123" > rdbbp ; cat rdbbp
+		echo "0xa123" > rdrf ; cat rdrf
+wrmac
+wrbbp
+wrrf
+	These commands are used to write the MAC, BBP and RF registers in the
+	card.  These commands take two parameters that specify the offset
+	location and the value that is to be written. This parameters must
+	be specified in hexadecimal (its possible to preceed the number
+	with a "0x").
+
+	Usage:
+		echo "0xa123 0xaa" > wrmac
+		echo "0xa123 0xaa" > wrbbp
+		echo "0xa123 0xaa" > wrrf
+
+sleepparams
+	This command is used to set the sleepclock configurations
+
+	Path: /debugfs/libertas_wireless/ethX/
+
+	Usage:
+		cat sleepparams: reads the current sleepclock configuration
+
+		echo "p1 p2 p3 p4 p5 p6" > sleepparams: writes the sleepclock configuration.
+
+		where:
+			p1 is Sleep clock error in ppm (0-65535)
+			p2 is Wakeup offset in usec (0-65535)
+			p3 is Clock stabilization time in usec (0-65535)
+			p4 is Control periodic calibration (0-2)
+			p5 is Control the use of external sleep clock (0-2)
+			p6 is reserved for debug (0-65535)
+
+subscribed_events
+
+	The subscribed_events directory contains the interface for the
+	subscribed events API.
+
+	Path: /debugfs/libertas_wireless/ethX/subscribed_events/
+
+	Each event is represented by a filename. Each filename consists of the
+	following three fields:
+	Value Frequency Subscribed
+
+	To read the current values for a given event, do:
+		cat event
+	To set the current values, do:
+		echo "60 2 1" > event
+
+	Frequency field specifies the reporting frequency for this event.
+	If it is set to 0, then the event is reported only once, and then
+	automatically unsubscribed. If it is set to 1, then the event is
+	reported every time it occurs. If it is set to N, then the event is
+	reported every Nth time it occurs.
+
+	beacon_missed
+	Value field specifies the number of consecutive missing beacons which
+	triggers the LINK_LOSS event. This event is generated only once after
+	which the firmware resets its state. At initialization, the LINK_LOSS
+	event is subscribed by default. The default value of MissedBeacons is
+	60.
+
+	failure_count
+	Value field specifies the consecutive failure count threshold which
+	triggers the generation of the MAX_FAIL event. Once this event is
+	generated, the consecutive failure count is reset to 0.
+	At initialization, the MAX_FAIL event is NOT subscribed by
+	default.
+
+	high_rssi
+	This event is generated when the average received RSSI in beacons goes
+	above a threshold, specified by Value.
+
+	low_rssi
+	This event is generated when the average received RSSI in beacons goes
+	below a threshold, specified by Value.
+
+	high_snr
+	This event is generated when the average received SNR in beacons goes
+	above a threshold, specified by Value.
+
+	low_snr
+	This event is generated when the average received SNR in beacons goes
+	below a threshold, specified by Value.
+
+extscan
+	This command is used to do a specific scan.
+
+	Path: /debugfs/libertas_wireless/ethX/
+
+	Usage: echo "SSID" > extscan
+
+	Example:
+		echo "LINKSYS-AP" > extscan
+
+	To see the results of use getscantable command.
+
+getscantable
+
+	Display the current contents of the driver scan table (ie. get the
+	scan results).
+
+	Path: /debugfs/libertas_wireless/ethX/
+
+	Usage:
+		cat getscantable
+
+setuserscan
+	Initiate a customized scan and retrieve the results
+
+
+	Path: /debugfs/libertas_wireless/ethX/
+
+    Usage:
+       echo "[ARGS]" > setuserscan
+
+         where [ARGS]:
+
+      bssid=xx:xx:xx:xx:xx:xx  specify a BSSID filter for the scan
+      ssid="[SSID]"            specify a SSID filter for the scan
+      keep=[0 or 1]            keep the previous scan results (1), discard (0)
+      dur=[scan time]          time to scan for each channel in milliseconds
+      type=[1,2,3]             BSS type: 1 (Infra), 2(Adhoc), 3(Any)
+
+    Any combination of the above arguments can be supplied on the command
+    line. If dur tokens are absent, the driver default setting will be used.
+    The bssid and ssid fields, if blank, will produce an unfiltered scan.
+    The type field will default to 3 (Any) and the keep field will default
+    to 0 (Discard).
+
+    Examples:
+    1) Perform a passive scan on all channels for 20 ms per channel:
+            echo "dur=20" > setuserscan
+
+    2) Perform an active scan for a specific SSID:
+            echo "ssid="TestAP"" > setuserscan
+
+    3) Scan all available channels (B/G, A bands) for a specific BSSID, keep
+       the current scan table intact, update existing or append new scan data:
+            echo "bssid=00:50:43:20:12:82 keep=1" > setuserscan
+
+    4) Scan for all infrastructure networks.
+       Keep the previous scan table intact. Update any duplicate BSSID/SSID
+       matches with the new scan data:
+            echo "type=1 keep=1" > 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.
+
+==============================================================================

+ 766 - 0
package/libertas/src/assoc.c

@@ -0,0 +1,766 @@
+/* Copyright (C) 2006, Red Hat, Inc. */
+
+#include <linux/bitops.h>
+#include <net/ieee80211.h>
+#include <linux/etherdevice.h>
+
+#include "assoc.h"
+#include "join.h"
+#include "decl.h"
+#include "hostcmd.h"
+#include "host.h"
+#include "cmd.h"
+
+
+static const u8 bssid_any[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+static const u8 bssid_off[ETH_ALEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+
+static int assoc_helper_essid(struct lbs_private *priv,
+                              struct assoc_request * assoc_req)
+{
+	int ret = 0;
+	struct bss_descriptor * bss;
+	int channel = -1;
+
+	lbs_deb_enter(LBS_DEB_ASSOC);
+
+	/* FIXME: take channel into account when picking SSIDs if a channel
+	 * is set.
+	 */
+
+	if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags))
+		channel = assoc_req->channel;
+
+	lbs_deb_assoc("SSID '%s' requested\n",
+	              escape_essid(assoc_req->ssid, assoc_req->ssid_len));
+	if (assoc_req->mode == IW_MODE_INFRA) {
+		lbs_send_specific_ssid_scan(priv, assoc_req->ssid,
+			assoc_req->ssid_len, 0);
+
+		bss = lbs_find_ssid_in_list(priv, assoc_req->ssid,
+				assoc_req->ssid_len, NULL, IW_MODE_INFRA, channel);
+		if (bss != NULL) {
+			memcpy(&assoc_req->bss, bss, sizeof(struct bss_descriptor));
+			ret = lbs_associate(priv, assoc_req);
+		} else {
+			lbs_deb_assoc("SSID not found; cannot associate\n");
+		}
+	} else if (assoc_req->mode == IW_MODE_ADHOC) {
+		/* Scan for the network, do not save previous results.  Stale
+		 *   scan data will cause us to join a non-existant adhoc network
+		 */
+		lbs_send_specific_ssid_scan(priv, assoc_req->ssid,
+			assoc_req->ssid_len, 1);
+
+		/* Search for the requested SSID in the scan table */
+		bss = lbs_find_ssid_in_list(priv, assoc_req->ssid,
+				assoc_req->ssid_len, NULL, IW_MODE_ADHOC, channel);
+		if (bss != NULL) {
+			lbs_deb_assoc("SSID found, will join\n");
+			memcpy(&assoc_req->bss, bss, sizeof(struct bss_descriptor));
+			lbs_join_adhoc_network(priv, assoc_req);
+		} else {
+			/* else send START command */
+			lbs_deb_assoc("SSID not found, creating adhoc network\n");
+			memcpy(&assoc_req->bss.ssid, &assoc_req->ssid,
+				IW_ESSID_MAX_SIZE);
+			assoc_req->bss.ssid_len = assoc_req->ssid_len;
+			lbs_start_adhoc_network(priv, assoc_req);
+		}
+	}
+
+	lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
+	return ret;
+}
+
+
+static int assoc_helper_bssid(struct lbs_private *priv,
+                              struct assoc_request * assoc_req)
+{
+	int ret = 0;
+	struct bss_descriptor * bss;
+	DECLARE_MAC_BUF(mac);
+
+	lbs_deb_enter_args(LBS_DEB_ASSOC, "BSSID %s",
+		print_mac(mac, assoc_req->bssid));
+
+	/* Search for index position in list for requested MAC */
+	bss = lbs_find_bssid_in_list(priv, assoc_req->bssid,
+			    assoc_req->mode);
+	if (bss == NULL) {
+		lbs_deb_assoc("ASSOC: WAP: BSSID %s not found, "
+			"cannot associate.\n", print_mac(mac, assoc_req->bssid));
+		goto out;
+	}
+
+	memcpy(&assoc_req->bss, bss, sizeof(struct bss_descriptor));
+	if (assoc_req->mode == IW_MODE_INFRA) {
+		ret = lbs_associate(priv, assoc_req);
+		lbs_deb_assoc("ASSOC: lbs_associate(bssid) returned %d\n", ret);
+	} else if (assoc_req->mode == IW_MODE_ADHOC) {
+		lbs_join_adhoc_network(priv, assoc_req);
+	}
+
+out:
+	lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
+	return ret;
+}
+
+
+static int assoc_helper_associate(struct lbs_private *priv,
+                                  struct assoc_request * assoc_req)
+{
+	int ret = 0, done = 0;
+
+	lbs_deb_enter(LBS_DEB_ASSOC);
+
+	/* If we're given and 'any' BSSID, try associating based on SSID */
+
+	if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) {
+		if (compare_ether_addr(bssid_any, assoc_req->bssid)
+		    && compare_ether_addr(bssid_off, assoc_req->bssid)) {
+			ret = assoc_helper_bssid(priv, assoc_req);
+			done = 1;
+		}
+	}
+
+	if (!done && test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
+		ret = assoc_helper_essid(priv, assoc_req);
+	}
+
+	lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
+	return ret;
+}
+
+
+static int assoc_helper_mode(struct lbs_private *priv,
+                             struct assoc_request * assoc_req)
+{
+	int ret = 0;
+
+	lbs_deb_enter(LBS_DEB_ASSOC);
+
+	if (assoc_req->mode == priv->mode)
+		goto done;
+
+	if (assoc_req->mode == IW_MODE_INFRA) {
+		if (priv->psstate != PS_STATE_FULL_POWER)
+			lbs_ps_wakeup(priv, CMD_OPTION_WAITFORRSP);
+		priv->psmode = LBS802_11POWERMODECAM;
+	}
+
+	priv->mode = assoc_req->mode;
+	ret = lbs_prepare_and_send_command(priv,
+				    CMD_802_11_SNMP_MIB,
+				    0, CMD_OPTION_WAITFORRSP,
+				    OID_802_11_INFRASTRUCTURE_MODE,
+		/* Shoot me now */  (void *) (size_t) assoc_req->mode);
+
+done:
+	lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
+	return ret;
+}
+
+
+static int update_channel(struct lbs_private *priv)
+{
+	int ret;
+
+	/* the channel in f/w could be out of sync, get the current channel */
+	lbs_deb_enter(LBS_DEB_ASSOC);
+
+	ret = lbs_get_channel(priv);
+	if (ret > 0)
+		priv->curbssparams.channel = (u8) ret;
+
+	lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
+	return ret;
+}
+
+void lbs_sync_channel(struct work_struct *work)
+{
+	struct lbs_private *priv = container_of(work, struct lbs_private,
+		sync_channel);
+
+	lbs_deb_enter(LBS_DEB_ASSOC);
+	if (update_channel(priv) != 0)
+		lbs_pr_info("Channel synchronization failed.");
+	lbs_deb_leave(LBS_DEB_ASSOC);
+}
+
+static int assoc_helper_channel(struct lbs_private *priv,
+                                struct assoc_request * assoc_req)
+{
+	int ret = 0;
+
+	lbs_deb_enter(LBS_DEB_ASSOC);
+
+	ret = update_channel(priv);
+	if (ret < 0) {
+		lbs_deb_assoc("ASSOC: channel: error getting channel.");
+	}
+
+	if (assoc_req->channel == priv->curbssparams.channel)
+		goto done;
+
+	if (priv->mesh_dev) {
+		/* Disconnect mesh while associating -- otherwise it
+		   won't let us change channels */
+		lbs_mesh_config(priv, 0);
+	}
+
+	lbs_deb_assoc("ASSOC: channel: %d -> %d\n",
+	       priv->curbssparams.channel, assoc_req->channel);
+
+	ret = lbs_set_channel(priv, assoc_req->channel);
+	if (ret < 0)
+		lbs_deb_assoc("ASSOC: channel: error setting channel.");
+
+	/* FIXME: shouldn't need to grab the channel _again_ after setting
+	 * it since the firmware is supposed to return the new channel, but
+	 * whatever... */
+	ret = update_channel(priv);
+	if (ret < 0)
+		lbs_deb_assoc("ASSOC: channel: error getting channel.");
+
+	if (assoc_req->channel != priv->curbssparams.channel) {
+		lbs_deb_assoc("ASSOC: channel: failed to update channel to %d\n",
+		              assoc_req->channel);
+		goto restore_mesh;
+	}
+
+	if (   assoc_req->secinfo.wep_enabled
+	    &&   (assoc_req->wep_keys[0].len
+	       || assoc_req->wep_keys[1].len
+	       || assoc_req->wep_keys[2].len
+	       || assoc_req->wep_keys[3].len)) {
+		/* Make sure WEP keys are re-sent to firmware */
+		set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags);
+	}
+
+	/* Must restart/rejoin adhoc networks after channel change */
+	set_bit(ASSOC_FLAG_SSID, &assoc_req->flags);
+
+ restore_mesh:
+	if (priv->mesh_dev)
+		lbs_mesh_config(priv, 1);
+
+ done:
+	lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
+	return ret;
+}
+
+
+static int assoc_helper_wep_keys(struct lbs_private *priv,
+                                 struct assoc_request * assoc_req)
+{
+	int i;
+	int ret = 0;
+
+	lbs_deb_enter(LBS_DEB_ASSOC);
+
+	/* Set or remove WEP keys */
+	if (   assoc_req->wep_keys[0].len
+	    || assoc_req->wep_keys[1].len
+	    || assoc_req->wep_keys[2].len
+	    || assoc_req->wep_keys[3].len) {
+		ret = lbs_prepare_and_send_command(priv,
+					    CMD_802_11_SET_WEP,
+					    CMD_ACT_ADD,
+					    CMD_OPTION_WAITFORRSP,
+					    0, assoc_req);
+	} else {
+		ret = lbs_prepare_and_send_command(priv,
+					    CMD_802_11_SET_WEP,
+					    CMD_ACT_REMOVE,
+					    CMD_OPTION_WAITFORRSP,
+					    0, NULL);
+	}
+
+	if (ret)
+		goto out;
+
+	/* enable/disable the MAC's WEP packet filter */
+	if (assoc_req->secinfo.wep_enabled)
+		priv->currentpacketfilter |= CMD_ACT_MAC_WEP_ENABLE;
+	else
+		priv->currentpacketfilter &= ~CMD_ACT_MAC_WEP_ENABLE;
+	ret = lbs_set_mac_packet_filter(priv);
+	if (ret)
+		goto out;
+
+	mutex_lock(&priv->lock);
+
+	/* Copy WEP keys into priv wep key fields */
+	for (i = 0; i < 4; i++) {
+		memcpy(&priv->wep_keys[i], &assoc_req->wep_keys[i],
+			sizeof(struct enc_key));
+	}
+	priv->wep_tx_keyidx = assoc_req->wep_tx_keyidx;
+
+	mutex_unlock(&priv->lock);
+
+out:
+	lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
+	return ret;
+}
+
+static int assoc_helper_secinfo(struct lbs_private *priv,
+                                struct assoc_request * assoc_req)
+{
+	int ret = 0;
+	u32 do_wpa;
+	u32 rsn = 0;
+
+	lbs_deb_enter(LBS_DEB_ASSOC);
+
+	memcpy(&priv->secinfo, &assoc_req->secinfo,
+		sizeof(struct lbs_802_11_security));
+
+	ret = lbs_set_mac_packet_filter(priv);
+	if (ret)
+		goto out;
+
+	/* If RSN is already enabled, don't try to enable it again, since
+	 * ENABLE_RSN resets internal state machines and will clobber the
+	 * 4-way WPA handshake.
+	 */
+
+	/* Get RSN enabled/disabled */
+	ret = lbs_prepare_and_send_command(priv,
+				    CMD_802_11_ENABLE_RSN,
+				    CMD_ACT_GET,
+				    CMD_OPTION_WAITFORRSP,
+				    0, &rsn);
+	if (ret) {
+		lbs_deb_assoc("Failed to get RSN status: %d", ret);
+		goto out;
+	}
+
+	/* Don't re-enable RSN if it's already enabled */
+	do_wpa = (assoc_req->secinfo.WPAenabled || assoc_req->secinfo.WPA2enabled);
+	if (do_wpa == rsn)
+		goto out;
+
+	/* Set RSN enabled/disabled */
+	rsn = do_wpa;
+	ret = lbs_prepare_and_send_command(priv,
+				    CMD_802_11_ENABLE_RSN,
+				    CMD_ACT_SET,
+				    CMD_OPTION_WAITFORRSP,
+				    0, &rsn);
+
+out:
+	lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
+	return ret;
+}
+
+
+static int assoc_helper_wpa_keys(struct lbs_private *priv,
+                                 struct assoc_request * assoc_req)
+{
+	int ret = 0;
+	unsigned int flags = assoc_req->flags;
+
+	lbs_deb_enter(LBS_DEB_ASSOC);
+
+	/* Work around older firmware bug where WPA unicast and multicast
+	 * keys must be set independently.  Seen in SDIO parts with firmware
+	 * version 5.0.11p0.
+	 */
+
+	if (test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) {
+		clear_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags);
+		ret = lbs_prepare_and_send_command(priv,
+					CMD_802_11_KEY_MATERIAL,
+					CMD_ACT_SET,
+					CMD_OPTION_WAITFORRSP,
+					0, assoc_req);
+		assoc_req->flags = flags;
+	}
+
+	if (ret)
+		goto out;
+
+	if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)) {
+		clear_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags);
+
+		ret = lbs_prepare_and_send_command(priv,
+					CMD_802_11_KEY_MATERIAL,
+					CMD_ACT_SET,
+					CMD_OPTION_WAITFORRSP,
+					0, assoc_req);
+		assoc_req->flags = flags;
+	}
+
+out:
+	lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
+	return ret;
+}
+
+
+static int assoc_helper_wpa_ie(struct lbs_private *priv,
+                               struct assoc_request * assoc_req)
+{
+	int ret = 0;
+
+	lbs_deb_enter(LBS_DEB_ASSOC);
+
+	if (assoc_req->secinfo.WPAenabled || assoc_req->secinfo.WPA2enabled) {
+		memcpy(&priv->wpa_ie, &assoc_req->wpa_ie, assoc_req->wpa_ie_len);
+		priv->wpa_ie_len = assoc_req->wpa_ie_len;
+	} else {
+		memset(&priv->wpa_ie, 0, MAX_WPA_IE_LEN);
+		priv->wpa_ie_len = 0;
+	}
+
+	lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
+	return ret;
+}
+
+
+static int should_deauth_infrastructure(struct lbs_private *priv,
+                                        struct assoc_request * assoc_req)
+{
+	int ret = 0;
+
+	lbs_deb_enter(LBS_DEB_ASSOC);
+
+	if (priv->connect_status != LBS_CONNECTED)
+		return 0;
+
+	if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
+		lbs_deb_assoc("Deauthenticating due to new SSID\n");
+		ret = 1;
+		goto out;
+	}
+
+	if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) {
+		if (priv->secinfo.auth_mode != assoc_req->secinfo.auth_mode) {
+			lbs_deb_assoc("Deauthenticating due to new security\n");
+			ret = 1;
+			goto out;
+		}
+	}
+
+	if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) {
+		lbs_deb_assoc("Deauthenticating due to new BSSID\n");
+		ret = 1;
+		goto out;
+	}
+
+	if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) {
+		lbs_deb_assoc("Deauthenticating due to channel switch\n");
+		ret = 1;
+		goto out;
+	}
+
+	/* FIXME: deal with 'auto' mode somehow */
+	if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) {
+		if (assoc_req->mode != IW_MODE_INFRA) {
+			lbs_deb_assoc("Deauthenticating due to leaving "
+				"infra mode\n");
+			ret = 1;
+			goto out;
+		}
+	}
+
+out:
+	lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
+	return 0;
+}
+
+
+static int should_stop_adhoc(struct lbs_private *priv,
+                             struct assoc_request * assoc_req)
+{
+	lbs_deb_enter(LBS_DEB_ASSOC);
+
+	if (priv->connect_status != LBS_CONNECTED)
+		return 0;
+
+	if (lbs_ssid_cmp(priv->curbssparams.ssid,
+	                      priv->curbssparams.ssid_len,
+	                      assoc_req->ssid, assoc_req->ssid_len) != 0)
+		return 1;
+
+	/* FIXME: deal with 'auto' mode somehow */
+	if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) {
+		if (assoc_req->mode != IW_MODE_ADHOC)
+			return 1;
+	}
+
+	if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) {
+		if (assoc_req->channel != priv->curbssparams.channel)
+			return 1;
+	}
+
+	lbs_deb_leave(LBS_DEB_ASSOC);
+	return 0;
+}
+
+
+void lbs_association_worker(struct work_struct *work)
+{
+	struct lbs_private *priv = container_of(work, struct lbs_private,
+		assoc_work.work);
+	struct assoc_request * assoc_req = NULL;
+	int ret = 0;
+	int find_any_ssid = 0;
+	DECLARE_MAC_BUF(mac);
+
+	lbs_deb_enter(LBS_DEB_ASSOC);
+
+	mutex_lock(&priv->lock);
+	assoc_req = priv->pending_assoc_req;
+	priv->pending_assoc_req = NULL;
+	priv->in_progress_assoc_req = assoc_req;
+	mutex_unlock(&priv->lock);
+
+	if (!assoc_req)
+		goto done;
+
+	lbs_deb_assoc(
+		"Association Request:\n"
+		"    flags:     0x%08lx\n"
+		"    SSID:      '%s'\n"
+		"    chann:     %d\n"
+		"    band:      %d\n"
+		"    mode:      %d\n"
+		"    BSSID:     %s\n"
+		"    secinfo:  %s%s%s\n"
+		"    auth_mode: %d\n",
+		assoc_req->flags,
+		escape_essid(assoc_req->ssid, assoc_req->ssid_len),
+		assoc_req->channel, assoc_req->band, assoc_req->mode,
+		print_mac(mac, assoc_req->bssid),
+		assoc_req->secinfo.WPAenabled ? " WPA" : "",
+		assoc_req->secinfo.WPA2enabled ? " WPA2" : "",
+		assoc_req->secinfo.wep_enabled ? " WEP" : "",
+		assoc_req->secinfo.auth_mode);
+
+	/* If 'any' SSID was specified, find an SSID to associate with */
+	if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)
+	    && !assoc_req->ssid_len)
+		find_any_ssid = 1;
+
+	/* But don't use 'any' SSID if there's a valid locked BSSID to use */
+	if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) {
+		if (compare_ether_addr(assoc_req->bssid, bssid_any)
+		    && compare_ether_addr(assoc_req->bssid, bssid_off))
+			find_any_ssid = 0;
+	}
+
+	if (find_any_ssid) {
+		u8 new_mode;
+
+		ret = lbs_find_best_network_ssid(priv, assoc_req->ssid,
+				&assoc_req->ssid_len, assoc_req->mode, &new_mode);
+		if (ret) {
+			lbs_deb_assoc("Could not find best network\n");
+			ret = -ENETUNREACH;
+			goto out;
+		}
+
+		/* Ensure we switch to the mode of the AP */
+		if (assoc_req->mode == IW_MODE_AUTO) {
+			set_bit(ASSOC_FLAG_MODE, &assoc_req->flags);
+			assoc_req->mode = new_mode;
+		}
+	}
+
+	/*
+	 * Check if the attributes being changing require deauthentication
+	 * from the currently associated infrastructure access point.
+	 */
+	if (priv->mode == IW_MODE_INFRA) {
+		if (should_deauth_infrastructure(priv, assoc_req)) {
+			ret = lbs_send_deauthentication(priv);
+			if (ret) {
+				lbs_deb_assoc("Deauthentication due to new "
+					"configuration request failed: %d\n",
+					ret);
+			}
+		}
+	} else if (priv->mode == IW_MODE_ADHOC) {
+		if (should_stop_adhoc(priv, assoc_req)) {
+			ret = lbs_stop_adhoc_network(priv);
+			if (ret) {
+				lbs_deb_assoc("Teardown of AdHoc network due to "
+					"new configuration request failed: %d\n",
+					ret);
+			}
+
+		}
+	}
+
+	/* Send the various configuration bits to the firmware */
+	if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) {
+		ret = assoc_helper_mode(priv, assoc_req);
+		if (ret)
+			goto out;
+	}
+
+	if (test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags)) {
+		ret = assoc_helper_channel(priv, assoc_req);
+		if (ret)
+			goto out;
+	}
+
+	if (   test_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags)
+	    || test_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags)) {
+		ret = assoc_helper_wep_keys(priv, assoc_req);
+		if (ret)
+			goto out;
+	}
+
+	if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) {
+		ret = assoc_helper_secinfo(priv, assoc_req);
+		if (ret)
+			goto out;
+	}
+
+	if (test_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags)) {
+		ret = assoc_helper_wpa_ie(priv, assoc_req);
+		if (ret)
+			goto out;
+	}
+
+	if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)
+	    || test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) {
+		ret = assoc_helper_wpa_keys(priv, assoc_req);
+		if (ret)
+			goto out;
+	}
+
+	/* SSID/BSSID should be the _last_ config option set, because they
+	 * trigger the association attempt.
+	 */
+	if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)
+	    || test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
+		int success = 1;
+
+		ret = assoc_helper_associate(priv, assoc_req);
+		if (ret) {
+			lbs_deb_assoc("ASSOC: association unsuccessful: %d\n",
+				ret);
+			success = 0;
+		}
+
+		if (priv->connect_status != LBS_CONNECTED) {
+			lbs_deb_assoc("ASSOC: association unsuccessful, "
+				"not connected\n");
+			success = 0;
+		}
+
+		if (success) {
+			lbs_deb_assoc("ASSOC: associated to '%s', %s\n",
+				escape_essid(priv->curbssparams.ssid,
+				             priv->curbssparams.ssid_len),
+				print_mac(mac, priv->curbssparams.bssid));
+			lbs_prepare_and_send_command(priv,
+				CMD_802_11_RSSI,
+				0, CMD_OPTION_WAITFORRSP, 0, NULL);
+
+			lbs_prepare_and_send_command(priv,
+				CMD_802_11_GET_LOG,
+				0, CMD_OPTION_WAITFORRSP, 0, NULL);
+		} else {
+			ret = -1;
+		}
+	}
+
+out:
+	if (ret) {
+		lbs_deb_assoc("ASSOC: reconfiguration attempt unsuccessful: %d\n",
+			ret);
+	}
+
+	mutex_lock(&priv->lock);
+	priv->in_progress_assoc_req = NULL;
+	mutex_unlock(&priv->lock);
+	kfree(assoc_req);
+
+done:
+	lbs_deb_leave(LBS_DEB_ASSOC);
+}
+
+
+/*
+ * Caller MUST hold any necessary locks
+ */
+struct assoc_request *lbs_get_association_request(struct lbs_private *priv)
+{
+	struct assoc_request * assoc_req;
+
+	lbs_deb_enter(LBS_DEB_ASSOC);
+	if (!priv->pending_assoc_req) {
+		priv->pending_assoc_req = kzalloc(sizeof(struct assoc_request),
+		                                     GFP_KERNEL);
+		if (!priv->pending_assoc_req) {
+			lbs_pr_info("Not enough memory to allocate association"
+				" request!\n");
+			return NULL;
+		}
+	}
+
+	/* Copy current configuration attributes to the association request,
+	 * but don't overwrite any that are already set.
+	 */
+	assoc_req = priv->pending_assoc_req;
+	if (!test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
+		memcpy(&assoc_req->ssid, &priv->curbssparams.ssid,
+		       IW_ESSID_MAX_SIZE);
+		assoc_req->ssid_len = priv->curbssparams.ssid_len;
+	}
+
+	if (!test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags))
+		assoc_req->channel = priv->curbssparams.channel;
+
+	if (!test_bit(ASSOC_FLAG_BAND, &assoc_req->flags))
+		assoc_req->band = priv->curbssparams.band;
+
+	if (!test_bit(ASSOC_FLAG_MODE, &assoc_req->flags))
+		assoc_req->mode = priv->mode;
+
+	if (!test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) {
+		memcpy(&assoc_req->bssid, priv->curbssparams.bssid,
+			ETH_ALEN);
+	}
+
+	if (!test_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags)) {
+		int i;
+		for (i = 0; i < 4; i++) {
+			memcpy(&assoc_req->wep_keys[i], &priv->wep_keys[i],
+				sizeof(struct enc_key));
+		}
+	}
+
+	if (!test_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags))
+		assoc_req->wep_tx_keyidx = priv->wep_tx_keyidx;
+
+	if (!test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)) {
+		memcpy(&assoc_req->wpa_mcast_key, &priv->wpa_mcast_key,
+			sizeof(struct enc_key));
+	}
+
+	if (!test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) {
+		memcpy(&assoc_req->wpa_unicast_key, &priv->wpa_unicast_key,
+			sizeof(struct enc_key));
+	}
+
+	if (!test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) {
+		memcpy(&assoc_req->secinfo, &priv->secinfo,
+			sizeof(struct lbs_802_11_security));
+	}
+
+	if (!test_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags)) {
+		memcpy(&assoc_req->wpa_ie, &priv->wpa_ie,
+			MAX_WPA_IE_LEN);
+		assoc_req->wpa_ie_len = priv->wpa_ie_len;
+	}
+
+	lbs_deb_leave(LBS_DEB_ASSOC);
+	return assoc_req;
+}

+ 12 - 0
package/libertas/src/assoc.h

@@ -0,0 +1,12 @@
+/* Copyright (C) 2006, Red Hat, Inc. */
+
+#ifndef _LBS_ASSOC_H_
+#define _LBS_ASSOC_H_
+
+#include "dev.h"
+
+void lbs_association_worker(struct work_struct *work);
+struct assoc_request *lbs_get_association_request(struct lbs_private *priv);
+void lbs_sync_channel(struct work_struct *work);
+
+#endif /* _LBS_ASSOC_H */

+ 2233 - 0
package/libertas/src/cmd.c

@@ -0,0 +1,2233 @@
+/**
+  * This file contains the handling of command.
+  * It prepares command and sends it to firmware when it is ready.
+  */
+
+#include <net/iw_handler.h>
+#include "host.h"
+#include "hostcmd.h"
+#include "decl.h"
+#include "defs.h"
+#include "dev.h"
+#include "join.h"
+#include "wext.h"
+#include "cmd.h"
+
+static void cleanup_cmdnode(struct cmd_ctrl_node *ptempnode);
+static struct cmd_ctrl_node *lbs_get_cmd_ctrl_node(struct lbs_private *priv);
+static void lbs_set_cmd_ctrl_node(struct lbs_private *priv,
+		    struct cmd_ctrl_node *ptempnode,
+		    u16 wait_option, void *pdata_buf);
+
+
+/**
+ *  @brief Checks whether a command is allowed in Power Save mode
+ *
+ *  @param command the command ID
+ *  @return 	   1 if allowed, 0 if not allowed
+ */
+static u8 is_command_allowed_in_ps(u16 cmd)
+{
+	switch (cmd) {
+	case CMD_802_11_RSSI:
+		return 1;
+	default:
+		break;
+	}
+	return 0;
+}
+
+/**
+ *  @brief Updates the hardware details like MAC address and regulatory region
+ *
+ *  @param priv    	A pointer to struct lbs_private structure
+ *
+ *  @return 	   	0 on success, error on failure
+ */
+int lbs_update_hw_spec(struct lbs_private *priv)
+{
+	struct cmd_ds_get_hw_spec cmd;
+	int ret = -1;
+	u32 i;
+	DECLARE_MAC_BUF(mac);
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+	memcpy(cmd.permanentaddr, priv->current_addr, ETH_ALEN);
+	ret = lbs_cmd_with_response(priv, CMD_GET_HW_SPEC, cmd);
+	if (ret)
+		goto out;
+
+	priv->fwcapinfo = le32_to_cpu(cmd.fwcapinfo);
+	memcpy(priv->fwreleasenumber, cmd.fwreleasenumber, 4);
+
+	lbs_deb_cmd("GET_HW_SPEC: firmware release %u.%u.%up%u\n",
+		    priv->fwreleasenumber[2], priv->fwreleasenumber[1],
+		    priv->fwreleasenumber[0], priv->fwreleasenumber[3]);
+	lbs_deb_cmd("GET_HW_SPEC: MAC addr %s\n",
+		    print_mac(mac, cmd.permanentaddr));
+	lbs_deb_cmd("GET_HW_SPEC: hardware interface 0x%x, hardware spec 0x%04x\n",
+		    cmd.hwifversion, cmd.version);
+
+	/* Clamp region code to 8-bit since FW spec indicates that it should
+	 * only ever be 8-bit, even though the field size is 16-bit.  Some firmware
+	 * returns non-zero high 8 bits here.
+	 */
+	priv->regioncode = le16_to_cpu(cmd.regioncode) & 0xFF;
+
+	for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) {
+		/* use the region code to search for the index */
+		if (priv->regioncode == lbs_region_code_to_index[i])
+			break;
+	}
+
+	/* if it's unidentified region code, use the default (USA) */
+	if (i >= MRVDRV_MAX_REGION_CODE) {
+		priv->regioncode = 0x10;
+		lbs_pr_info("unidentified region code; using the default (USA)\n");
+	}
+
+	if (priv->current_addr[0] == 0xff)
+		memmove(priv->current_addr, cmd.permanentaddr, ETH_ALEN);
+
+	memcpy(priv->dev->dev_addr, priv->current_addr, ETH_ALEN);
+	if (priv->mesh_dev)
+		memcpy(priv->mesh_dev->dev_addr, priv->current_addr, ETH_ALEN);
+
+	if (lbs_set_regiontable(priv, priv->regioncode, 0)) {
+		ret = -1;
+		goto out;
+	}
+
+	if (lbs_set_universaltable(priv, 0)) {
+		ret = -1;
+		goto out;
+	}
+
+out:
+	lbs_deb_leave(LBS_DEB_CMD);
+	return ret;
+}
+
+static int lbs_cmd_802_11_ps_mode(struct lbs_private *priv,
+				   struct cmd_ds_command *cmd,
+				   u16 cmd_action)
+{
+	struct cmd_ds_802_11_ps_mode *psm = &cmd->params.psmode;
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	cmd->command = cpu_to_le16(CMD_802_11_PS_MODE);
+	cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_ps_mode) +
+				S_DS_GEN);
+	psm->action = cpu_to_le16(cmd_action);
+	psm->multipledtim = 0;
+	switch (cmd_action) {
+	case CMD_SUBCMD_ENTER_PS:
+		lbs_deb_cmd("PS command:" "SubCode- Enter PS\n");
+
+		psm->locallisteninterval = 0;
+		psm->nullpktinterval = 0;
+		psm->multipledtim =
+		    cpu_to_le16(MRVDRV_DEFAULT_MULTIPLE_DTIM);
+		break;
+
+	case CMD_SUBCMD_EXIT_PS:
+		lbs_deb_cmd("PS command:" "SubCode- Exit PS\n");
+		break;
+
+	case CMD_SUBCMD_SLEEP_CONFIRMED:
+		lbs_deb_cmd("PS command: SubCode- sleep confirm\n");
+		break;
+
+	default:
+		break;
+	}
+
+	lbs_deb_leave(LBS_DEB_CMD);
+	return 0;
+}
+
+static int lbs_cmd_802_11_inactivity_timeout(struct lbs_private *priv,
+					      struct cmd_ds_command *cmd,
+					      u16 cmd_action, void *pdata_buf)
+{
+	u16 *timeout = pdata_buf;
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	cmd->command = cpu_to_le16(CMD_802_11_INACTIVITY_TIMEOUT);
+	cmd->size =
+	    cpu_to_le16(sizeof(struct cmd_ds_802_11_inactivity_timeout)
+			     + S_DS_GEN);
+
+	cmd->params.inactivity_timeout.action = cpu_to_le16(cmd_action);
+
+	if (cmd_action)
+		cmd->params.inactivity_timeout.timeout = cpu_to_le16(*timeout);
+	else
+		cmd->params.inactivity_timeout.timeout = 0;
+
+	lbs_deb_leave(LBS_DEB_CMD);
+	return 0;
+}
+
+static int lbs_cmd_802_11_sleep_params(struct lbs_private *priv,
+					struct cmd_ds_command *cmd,
+					u16 cmd_action)
+{
+	struct cmd_ds_802_11_sleep_params *sp = &cmd->params.sleep_params;
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	cmd->size = cpu_to_le16((sizeof(struct cmd_ds_802_11_sleep_params)) +
+				S_DS_GEN);
+	cmd->command = cpu_to_le16(CMD_802_11_SLEEP_PARAMS);
+
+	if (cmd_action == CMD_ACT_GET) {
+		memset(&priv->sp, 0, sizeof(struct sleep_params));
+		memset(sp, 0, sizeof(struct cmd_ds_802_11_sleep_params));
+		sp->action = cpu_to_le16(cmd_action);
+	} else if (cmd_action == CMD_ACT_SET) {
+		sp->action = cpu_to_le16(cmd_action);
+		sp->error = cpu_to_le16(priv->sp.sp_error);
+		sp->offset = cpu_to_le16(priv->sp.sp_offset);
+		sp->stabletime = cpu_to_le16(priv->sp.sp_stabletime);
+		sp->calcontrol = (u8) priv->sp.sp_calcontrol;
+		sp->externalsleepclk = (u8) priv->sp.sp_extsleepclk;
+		sp->reserved = cpu_to_le16(priv->sp.sp_reserved);
+	}
+
+	lbs_deb_leave(LBS_DEB_CMD);
+	return 0;
+}
+
+static int lbs_cmd_802_11_set_wep(struct lbs_private *priv,
+                                   struct cmd_ds_command *cmd,
+                                   u32 cmd_act,
+                                   void * pdata_buf)
+{
+	struct cmd_ds_802_11_set_wep *wep = &cmd->params.wep;
+	int ret = 0;
+	struct assoc_request * assoc_req = pdata_buf;
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	cmd->command = cpu_to_le16(CMD_802_11_SET_WEP);
+	cmd->size = cpu_to_le16(sizeof(*wep) + S_DS_GEN);
+
+	if (cmd_act == CMD_ACT_ADD) {
+		int i;
+
+		if (!assoc_req) {
+			lbs_deb_cmd("Invalid association request!");
+			ret = -1;
+			goto done;
+		}
+
+		wep->action = cpu_to_le16(CMD_ACT_ADD);
+
+		/* default tx key index */
+		wep->keyindex = cpu_to_le16((u16)(assoc_req->wep_tx_keyidx &
+						  (u32)CMD_WEP_KEY_INDEX_MASK));
+
+		/* Copy key types and material to host command structure */
+		for (i = 0; i < 4; i++) {
+			struct enc_key * pkey = &assoc_req->wep_keys[i];
+
+			switch (pkey->len) {
+			case KEY_LEN_WEP_40:
+				wep->keytype[i] = CMD_TYPE_WEP_40_BIT;
+				memmove(&wep->keymaterial[i], pkey->key,
+				        pkey->len);
+				lbs_deb_cmd("SET_WEP: add key %d (40 bit)\n", i);
+				break;
+			case KEY_LEN_WEP_104:
+				wep->keytype[i] = CMD_TYPE_WEP_104_BIT;
+				memmove(&wep->keymaterial[i], pkey->key,
+				        pkey->len);
+				lbs_deb_cmd("SET_WEP: add key %d (104 bit)\n", i);
+				break;
+			case 0:
+				break;
+			default:
+				lbs_deb_cmd("SET_WEP: invalid key %d, length %d\n",
+				       i, pkey->len);
+				ret = -1;
+				goto done;
+				break;
+			}
+		}
+	} else if (cmd_act == CMD_ACT_REMOVE) {
+		/* ACT_REMOVE clears _all_ WEP keys */
+		wep->action = cpu_to_le16(CMD_ACT_REMOVE);
+
+		/* default tx key index */
+		wep->keyindex = cpu_to_le16((u16)(priv->wep_tx_keyidx &
+						  (u32)CMD_WEP_KEY_INDEX_MASK));
+		lbs_deb_cmd("SET_WEP: remove key %d\n", priv->wep_tx_keyidx);
+	}
+
+	ret = 0;
+
+done:
+	lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
+	return ret;
+}
+
+static int lbs_cmd_802_11_enable_rsn(struct lbs_private *priv,
+				      struct cmd_ds_command *cmd,
+				      u16 cmd_action,
+				      void * pdata_buf)
+{
+	struct cmd_ds_802_11_enable_rsn *penableRSN = &cmd->params.enbrsn;
+	u32 * enable = pdata_buf;
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	cmd->command = cpu_to_le16(CMD_802_11_ENABLE_RSN);
+	cmd->size = cpu_to_le16(sizeof(*penableRSN) + S_DS_GEN);
+	penableRSN->action = cpu_to_le16(cmd_action);
+
+	if (cmd_action == CMD_ACT_SET) {
+		if (*enable)
+			penableRSN->enable = cpu_to_le16(CMD_ENABLE_RSN);
+		else
+			penableRSN->enable = cpu_to_le16(CMD_DISABLE_RSN);
+		lbs_deb_cmd("ENABLE_RSN: %d\n", *enable);
+	}
+
+	lbs_deb_leave(LBS_DEB_CMD);
+	return 0;
+}
+
+
+static ssize_t lbs_tlv_size(const u8 *tlv, u16 size)
+{
+	ssize_t pos = 0;
+	struct mrvlietypesheader *tlv_h;
+	while (pos < size) {
+		u16 length;
+		tlv_h = (struct mrvlietypesheader *) tlv;
+		if (tlv_h->len == 0)
+			return pos;
+		length = le16_to_cpu(tlv_h->len) +
+			sizeof(struct mrvlietypesheader);
+		pos += length;
+		tlv += length;
+	}
+	return pos;
+}
+
+
+static void lbs_cmd_802_11_subscribe_event(struct lbs_private *priv,
+	struct cmd_ds_command *cmd, u16 cmd_action,
+	void *pdata_buf)
+{
+	struct cmd_ds_802_11_subscribe_event *events =
+		(struct cmd_ds_802_11_subscribe_event *) pdata_buf;
+
+	/* pdata_buf points to a struct cmd_ds_802_11_subscribe_event and room
+	 * for various Marvell TLVs */
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	cmd->size = cpu_to_le16(sizeof(*events)
+			- sizeof(events->tlv)
+			+ S_DS_GEN);
+	cmd->params.subscribe_event.action = cpu_to_le16(cmd_action);
+	if (cmd_action == CMD_ACT_GET) {
+		cmd->params.subscribe_event.events = 0;
+	} else {
+		ssize_t sz = lbs_tlv_size(events->tlv, sizeof(events->tlv));
+		cmd->size = cpu_to_le16(le16_to_cpu(cmd->size) + sz);
+		cmd->params.subscribe_event.events = events->events;
+		memcpy(cmd->params.subscribe_event.tlv, events->tlv, sz);
+	}
+
+	lbs_deb_leave(LBS_DEB_CMD);
+}
+
+static void set_one_wpa_key(struct MrvlIEtype_keyParamSet * pkeyparamset,
+                            struct enc_key * pkey)
+{
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	if (pkey->flags & KEY_INFO_WPA_ENABLED) {
+		pkeyparamset->keyinfo |= cpu_to_le16(KEY_INFO_WPA_ENABLED);
+	}
+	if (pkey->flags & KEY_INFO_WPA_UNICAST) {
+		pkeyparamset->keyinfo |= cpu_to_le16(KEY_INFO_WPA_UNICAST);
+	}
+	if (pkey->flags & KEY_INFO_WPA_MCAST) {
+		pkeyparamset->keyinfo |= cpu_to_le16(KEY_INFO_WPA_MCAST);
+	}
+
+	pkeyparamset->type = cpu_to_le16(TLV_TYPE_KEY_MATERIAL);
+	pkeyparamset->keytypeid = cpu_to_le16(pkey->type);
+	pkeyparamset->keylen = cpu_to_le16(pkey->len);
+	memcpy(pkeyparamset->key, pkey->key, pkey->len);
+	pkeyparamset->length = cpu_to_le16(  sizeof(pkeyparamset->keytypeid)
+	                                        + sizeof(pkeyparamset->keyinfo)
+	                                        + sizeof(pkeyparamset->keylen)
+	                                        + sizeof(pkeyparamset->key));
+	lbs_deb_leave(LBS_DEB_CMD);
+}
+
+static int lbs_cmd_802_11_key_material(struct lbs_private *priv,
+					struct cmd_ds_command *cmd,
+					u16 cmd_action,
+					u32 cmd_oid, void *pdata_buf)
+{
+	struct cmd_ds_802_11_key_material *pkeymaterial =
+	    &cmd->params.keymaterial;
+	struct assoc_request * assoc_req = pdata_buf;
+	int ret = 0;
+	int index = 0;
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	cmd->command = cpu_to_le16(CMD_802_11_KEY_MATERIAL);
+	pkeymaterial->action = cpu_to_le16(cmd_action);
+
+	if (cmd_action == CMD_ACT_GET) {
+		cmd->size = cpu_to_le16(S_DS_GEN + sizeof (pkeymaterial->action));
+		ret = 0;
+		goto done;
+	}
+
+	memset(&pkeymaterial->keyParamSet, 0, sizeof(pkeymaterial->keyParamSet));
+
+	if (test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) {
+		set_one_wpa_key(&pkeymaterial->keyParamSet[index],
+		                &assoc_req->wpa_unicast_key);
+		index++;
+	}
+
+	if (test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)) {
+		set_one_wpa_key(&pkeymaterial->keyParamSet[index],
+		                &assoc_req->wpa_mcast_key);
+		index++;
+	}
+
+	cmd->size = cpu_to_le16(  S_DS_GEN
+	                        + sizeof (pkeymaterial->action)
+	                        + (index * sizeof(struct MrvlIEtype_keyParamSet)));
+
+	ret = 0;
+
+done:
+	lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
+	return ret;
+}
+
+static int lbs_cmd_802_11_reset(struct lbs_private *priv,
+				 struct cmd_ds_command *cmd, int cmd_action)
+{
+	struct cmd_ds_802_11_reset *reset = &cmd->params.reset;
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	cmd->command = cpu_to_le16(CMD_802_11_RESET);
+	cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_reset) + S_DS_GEN);
+	reset->action = cpu_to_le16(cmd_action);
+
+	lbs_deb_leave(LBS_DEB_CMD);
+	return 0;
+}
+
+static int lbs_cmd_802_11_get_log(struct lbs_private *priv,
+				   struct cmd_ds_command *cmd)
+{
+	lbs_deb_enter(LBS_DEB_CMD);
+	cmd->command = cpu_to_le16(CMD_802_11_GET_LOG);
+	cmd->size =
+		cpu_to_le16(sizeof(struct cmd_ds_802_11_get_log) + S_DS_GEN);
+
+	lbs_deb_leave(LBS_DEB_CMD);
+	return 0;
+}
+
+static int lbs_cmd_802_11_get_stat(struct lbs_private *priv,
+				    struct cmd_ds_command *cmd)
+{
+	lbs_deb_enter(LBS_DEB_CMD);
+	cmd->command = cpu_to_le16(CMD_802_11_GET_STAT);
+	cmd->size =
+	    cpu_to_le16(sizeof(struct cmd_ds_802_11_get_stat) + S_DS_GEN);
+
+	lbs_deb_leave(LBS_DEB_CMD);
+	return 0;
+}
+
+static int lbs_cmd_802_11_snmp_mib(struct lbs_private *priv,
+				    struct cmd_ds_command *cmd,
+				    int cmd_action,
+				    int cmd_oid, void *pdata_buf)
+{
+	struct cmd_ds_802_11_snmp_mib *pSNMPMIB = &cmd->params.smib;
+	u8 ucTemp;
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	lbs_deb_cmd("SNMP_CMD: cmd_oid = 0x%x\n", cmd_oid);
+
+	cmd->command = cpu_to_le16(CMD_802_11_SNMP_MIB);
+	cmd->size = cpu_to_le16(sizeof(*pSNMPMIB) + S_DS_GEN);
+
+	switch (cmd_oid) {
+	case OID_802_11_INFRASTRUCTURE_MODE:
+	{
+		u8 mode = (u8) (size_t) pdata_buf;
+		pSNMPMIB->querytype = cpu_to_le16(CMD_ACT_SET);
+		pSNMPMIB->oid = cpu_to_le16((u16) DESIRED_BSSTYPE_I);
+		pSNMPMIB->bufsize = cpu_to_le16(sizeof(u8));
+		if (mode == IW_MODE_ADHOC) {
+			ucTemp = SNMP_MIB_VALUE_ADHOC;
+		} else {
+			/* Infra and Auto modes */
+			ucTemp = SNMP_MIB_VALUE_INFRA;
+		}
+
+		memmove(pSNMPMIB->value, &ucTemp, sizeof(u8));
+
+		break;
+	}
+
+	case OID_802_11D_ENABLE:
+		{
+			u32 ulTemp;
+
+			pSNMPMIB->oid = cpu_to_le16((u16) DOT11D_I);
+
+			if (cmd_action == CMD_ACT_SET) {
+				pSNMPMIB->querytype = cpu_to_le16(CMD_ACT_SET);
+				pSNMPMIB->bufsize = cpu_to_le16(sizeof(u16));
+				ulTemp = *(u32 *)pdata_buf;
+				*((__le16 *)(pSNMPMIB->value)) =
+				    cpu_to_le16((u16) ulTemp);
+			}
+			break;
+		}
+
+	case OID_802_11_FRAGMENTATION_THRESHOLD:
+		{
+			u32 ulTemp;
+
+			pSNMPMIB->oid = cpu_to_le16((u16) FRAGTHRESH_I);
+
+			if (cmd_action == CMD_ACT_GET) {
+				pSNMPMIB->querytype = cpu_to_le16(CMD_ACT_GET);
+			} else if (cmd_action == CMD_ACT_SET) {
+				pSNMPMIB->querytype = cpu_to_le16(CMD_ACT_SET);
+				pSNMPMIB->bufsize = cpu_to_le16(sizeof(u16));
+				ulTemp = *((u32 *) pdata_buf);
+				*((__le16 *)(pSNMPMIB->value)) =
+				    cpu_to_le16((u16) ulTemp);
+
+			}
+
+			break;
+		}
+
+	case OID_802_11_RTS_THRESHOLD:
+		{
+
+			u32 ulTemp;
+			pSNMPMIB->oid = cpu_to_le16(RTSTHRESH_I);
+
+			if (cmd_action == CMD_ACT_GET) {
+				pSNMPMIB->querytype = cpu_to_le16(CMD_ACT_GET);
+			} else if (cmd_action == CMD_ACT_SET) {
+				pSNMPMIB->querytype = cpu_to_le16(CMD_ACT_SET);
+				pSNMPMIB->bufsize = cpu_to_le16(sizeof(u16));
+				ulTemp = *((u32 *)pdata_buf);
+				*(__le16 *)(pSNMPMIB->value) =
+				    cpu_to_le16((u16) ulTemp);
+
+			}
+			break;
+		}
+	case OID_802_11_TX_RETRYCOUNT:
+		pSNMPMIB->oid = cpu_to_le16((u16) SHORT_RETRYLIM_I);
+
+		if (cmd_action == CMD_ACT_GET) {
+			pSNMPMIB->querytype = cpu_to_le16(CMD_ACT_GET);
+		} else if (cmd_action == CMD_ACT_SET) {
+			pSNMPMIB->querytype = cpu_to_le16(CMD_ACT_SET);
+			pSNMPMIB->bufsize = cpu_to_le16(sizeof(u16));
+			*((__le16 *)(pSNMPMIB->value)) =
+			    cpu_to_le16((u16) priv->txretrycount);
+		}
+
+		break;
+	default:
+		break;
+	}
+
+	lbs_deb_cmd(
+	       "SNMP_CMD: command=0x%x, size=0x%x, seqnum=0x%x, result=0x%x\n",
+	       le16_to_cpu(cmd->command), le16_to_cpu(cmd->size),
+	       le16_to_cpu(cmd->seqnum), le16_to_cpu(cmd->result));
+
+	lbs_deb_cmd(
+	       "SNMP_CMD: action 0x%x, oid 0x%x, oidsize 0x%x, value 0x%x\n",
+	       le16_to_cpu(pSNMPMIB->querytype), le16_to_cpu(pSNMPMIB->oid),
+	       le16_to_cpu(pSNMPMIB->bufsize),
+	       le16_to_cpu(*(__le16 *) pSNMPMIB->value));
+
+	lbs_deb_leave(LBS_DEB_CMD);
+	return 0;
+}
+
+static int lbs_cmd_802_11_radio_control(struct lbs_private *priv,
+					 struct cmd_ds_command *cmd,
+					 int cmd_action)
+{
+	struct cmd_ds_802_11_radio_control *pradiocontrol = &cmd->params.radio;
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	cmd->size =
+	    cpu_to_le16((sizeof(struct cmd_ds_802_11_radio_control)) +
+			     S_DS_GEN);
+	cmd->command = cpu_to_le16(CMD_802_11_RADIO_CONTROL);
+
+	pradiocontrol->action = cpu_to_le16(cmd_action);
+
+	switch (priv->preamble) {
+	case CMD_TYPE_SHORT_PREAMBLE:
+		pradiocontrol->control = cpu_to_le16(SET_SHORT_PREAMBLE);
+		break;
+
+	case CMD_TYPE_LONG_PREAMBLE:
+		pradiocontrol->control = cpu_to_le16(SET_LONG_PREAMBLE);
+		break;
+
+	case CMD_TYPE_AUTO_PREAMBLE:
+	default:
+		pradiocontrol->control = cpu_to_le16(SET_AUTO_PREAMBLE);
+		break;
+	}
+
+	if (priv->radioon)
+		pradiocontrol->control |= cpu_to_le16(TURN_ON_RF);
+	else
+		pradiocontrol->control &= cpu_to_le16(~TURN_ON_RF);
+
+	lbs_deb_leave(LBS_DEB_CMD);
+	return 0;
+}
+
+static int lbs_cmd_802_11_rf_tx_power(struct lbs_private *priv,
+				       struct cmd_ds_command *cmd,
+				       u16 cmd_action, void *pdata_buf)
+{
+
+	struct cmd_ds_802_11_rf_tx_power *prtp = &cmd->params.txp;
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	cmd->size =
+	    cpu_to_le16((sizeof(struct cmd_ds_802_11_rf_tx_power)) + S_DS_GEN);
+	cmd->command = cpu_to_le16(CMD_802_11_RF_TX_POWER);
+	prtp->action = cpu_to_le16(cmd_action);
+
+	lbs_deb_cmd("RF_TX_POWER_CMD: size:%d cmd:0x%x Act:%d\n",
+		    le16_to_cpu(cmd->size), le16_to_cpu(cmd->command),
+		    le16_to_cpu(prtp->action));
+
+	switch (cmd_action) {
+	case CMD_ACT_TX_POWER_OPT_GET:
+		prtp->action = cpu_to_le16(CMD_ACT_GET);
+		prtp->currentlevel = 0;
+		break;
+
+	case CMD_ACT_TX_POWER_OPT_SET_HIGH:
+		prtp->action = cpu_to_le16(CMD_ACT_SET);
+		prtp->currentlevel = cpu_to_le16(CMD_ACT_TX_POWER_INDEX_HIGH);
+		break;
+
+	case CMD_ACT_TX_POWER_OPT_SET_MID:
+		prtp->action = cpu_to_le16(CMD_ACT_SET);
+		prtp->currentlevel = cpu_to_le16(CMD_ACT_TX_POWER_INDEX_MID);
+		break;
+
+	case CMD_ACT_TX_POWER_OPT_SET_LOW:
+		prtp->action = cpu_to_le16(CMD_ACT_SET);
+		prtp->currentlevel = cpu_to_le16(*((u16 *) pdata_buf));
+		break;
+	}
+
+	lbs_deb_leave(LBS_DEB_CMD);
+	return 0;
+}
+
+static int lbs_cmd_802_11_monitor_mode(struct lbs_private *priv,
+				      struct cmd_ds_command *cmd,
+				      u16 cmd_action, void *pdata_buf)
+{
+	struct cmd_ds_802_11_monitor_mode *monitor = &cmd->params.monitor;
+
+	cmd->command = cpu_to_le16(CMD_802_11_MONITOR_MODE);
+	cmd->size =
+	    cpu_to_le16(sizeof(struct cmd_ds_802_11_monitor_mode) +
+			     S_DS_GEN);
+
+	monitor->action = cpu_to_le16(cmd_action);
+	if (cmd_action == CMD_ACT_SET) {
+		monitor->mode =
+		    cpu_to_le16((u16) (*(u32 *) pdata_buf));
+	}
+
+	return 0;
+}
+
+static int lbs_cmd_802_11_rate_adapt_rateset(struct lbs_private *priv,
+					      struct cmd_ds_command *cmd,
+					      u16 cmd_action)
+{
+	struct cmd_ds_802_11_rate_adapt_rateset
+	*rateadapt = &cmd->params.rateset;
+
+	lbs_deb_enter(LBS_DEB_CMD);
+	cmd->size =
+	    cpu_to_le16(sizeof(struct cmd_ds_802_11_rate_adapt_rateset)
+			     + S_DS_GEN);
+	cmd->command = cpu_to_le16(CMD_802_11_RATE_ADAPT_RATESET);
+
+	rateadapt->action = cpu_to_le16(cmd_action);
+	rateadapt->enablehwauto = cpu_to_le16(priv->enablehwauto);
+	rateadapt->bitmap = cpu_to_le16(priv->ratebitmap);
+
+	lbs_deb_leave(LBS_DEB_CMD);
+	return 0;
+}
+
+/**
+ *  @brief Get the current data rate
+ *
+ *  @param priv    	A pointer to struct lbs_private structure
+ *
+ *  @return 	   	The data rate on success, error on failure
+ */
+int lbs_get_data_rate(struct lbs_private *priv)
+{
+	struct cmd_ds_802_11_data_rate cmd;
+	int ret = -1;
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+	cmd.action = cpu_to_le16(CMD_ACT_GET_TX_RATE);
+
+	ret = lbs_cmd_with_response(priv, CMD_802_11_DATA_RATE, cmd);
+	if (ret)
+		goto out;
+
+	lbs_deb_hex(LBS_DEB_CMD, "DATA_RATE_RESP", (u8 *) &cmd, sizeof (cmd));
+
+	ret = (int) lbs_fw_index_to_data_rate(cmd.rates[0]);
+	lbs_deb_cmd("DATA_RATE: current rate 0x%02x\n", ret);
+
+out:
+	lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
+	return ret;
+}
+
+/**
+ *  @brief Set the data rate
+ *
+ *  @param priv    	A pointer to struct lbs_private structure
+ *  @param rate  	The desired data rate, or 0 to clear a locked rate
+ *
+ *  @return 	   	0 on success, error on failure
+ */
+int lbs_set_data_rate(struct lbs_private *priv, u8 rate)
+{
+	struct cmd_ds_802_11_data_rate cmd;
+	int ret = 0;
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+
+	if (rate > 0) {
+		cmd.action = cpu_to_le16(CMD_ACT_SET_TX_FIX_RATE);
+		cmd.rates[0] = lbs_data_rate_to_fw_index(rate);
+		if (cmd.rates[0] == 0) {
+			lbs_deb_cmd("DATA_RATE: invalid requested rate of"
+			            " 0x%02X\n", rate);
+			ret = 0;
+			goto out;
+		}
+		lbs_deb_cmd("DATA_RATE: set fixed 0x%02X\n", cmd.rates[0]);
+	} else {
+		cmd.action = cpu_to_le16(CMD_ACT_SET_TX_AUTO);
+		lbs_deb_cmd("DATA_RATE: setting auto\n");
+	}
+
+	ret = lbs_cmd_with_response(priv, CMD_802_11_DATA_RATE, cmd);
+	if (ret)
+		goto out;
+
+	lbs_deb_hex(LBS_DEB_CMD, "DATA_RATE_RESP", (u8 *) &cmd, sizeof (cmd));
+
+	/* FIXME: get actual rates FW can do if this command actually returns
+	 * all data rates supported.
+	 */
+	priv->cur_rate = lbs_fw_index_to_data_rate(cmd.rates[0]);
+	lbs_deb_cmd("DATA_RATE: current rate is 0x%02x\n", priv->cur_rate);
+
+out:
+	lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
+	return ret;
+}
+
+static int lbs_cmd_mac_multicast_adr(struct lbs_private *priv,
+				      struct cmd_ds_command *cmd,
+				      u16 cmd_action)
+{
+	struct cmd_ds_mac_multicast_adr *pMCastAdr = &cmd->params.madr;
+
+	lbs_deb_enter(LBS_DEB_CMD);
+	cmd->size = cpu_to_le16(sizeof(struct cmd_ds_mac_multicast_adr) +
+			     S_DS_GEN);
+	cmd->command = cpu_to_le16(CMD_MAC_MULTICAST_ADR);
+
+	lbs_deb_cmd("MULTICAST_ADR: setting %d addresses\n", pMCastAdr->nr_of_adrs);
+	pMCastAdr->action = cpu_to_le16(cmd_action);
+	pMCastAdr->nr_of_adrs =
+	    cpu_to_le16((u16) priv->nr_of_multicastmacaddr);
+	memcpy(pMCastAdr->maclist, priv->multicastlist,
+	       priv->nr_of_multicastmacaddr * ETH_ALEN);
+
+	lbs_deb_leave(LBS_DEB_CMD);
+	return 0;
+}
+
+/**
+ *  @brief Get the radio channel
+ *
+ *  @param priv    	A pointer to struct lbs_private structure
+ *
+ *  @return 	   	The channel on success, error on failure
+ */
+int lbs_get_channel(struct lbs_private *priv)
+{
+	struct cmd_ds_802_11_rf_channel cmd;
+	int ret = 0;
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+	cmd.action = cpu_to_le16(CMD_OPT_802_11_RF_CHANNEL_GET);
+
+	ret = lbs_cmd_with_response(priv, CMD_802_11_RF_CHANNEL, cmd);
+	if (ret)
+		goto out;
+
+	ret = le16_to_cpu(cmd.channel);
+	lbs_deb_cmd("current radio channel is %d\n", ret);
+
+out:
+	lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
+	return ret;
+}
+
+/**
+ *  @brief Set the radio channel
+ *
+ *  @param priv    	A pointer to struct lbs_private structure
+ *  @param channel  	The desired channel, or 0 to clear a locked channel
+ *
+ *  @return 	   	0 on success, error on failure
+ */
+int lbs_set_channel(struct lbs_private *priv, u8 channel)
+{
+	struct cmd_ds_802_11_rf_channel cmd;
+	u8 old_channel = priv->curbssparams.channel;
+	int ret = 0;
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	cmd.hdr.size = cpu_to_le16(sizeof(cmd));
+	cmd.action = cpu_to_le16(CMD_OPT_802_11_RF_CHANNEL_SET);
+	cmd.channel = cpu_to_le16(channel);
+
+	ret = lbs_cmd_with_response(priv, CMD_802_11_RF_CHANNEL, cmd);
+	if (ret)
+		goto out;
+
+	priv->curbssparams.channel = (uint8_t) le16_to_cpu(cmd.channel);
+	lbs_deb_cmd("channel switch from %d to %d\n", old_channel,
+		priv->curbssparams.channel);
+
+out:
+	lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
+	return ret;
+}
+
+static int lbs_cmd_802_11_rssi(struct lbs_private *priv,
+				struct cmd_ds_command *cmd)
+{
+
+	lbs_deb_enter(LBS_DEB_CMD);
+	cmd->command = cpu_to_le16(CMD_802_11_RSSI);
+	cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_rssi) + S_DS_GEN);
+	cmd->params.rssi.N = cpu_to_le16(DEFAULT_BCN_AVG_FACTOR);
+
+	/* reset Beacon SNR/NF/RSSI values */
+	priv->SNR[TYPE_BEACON][TYPE_NOAVG] = 0;
+	priv->SNR[TYPE_BEACON][TYPE_AVG] = 0;
+	priv->NF[TYPE_BEACON][TYPE_NOAVG] = 0;
+	priv->NF[TYPE_BEACON][TYPE_AVG] = 0;
+	priv->RSSI[TYPE_BEACON][TYPE_NOAVG] = 0;
+	priv->RSSI[TYPE_BEACON][TYPE_AVG] = 0;
+
+	lbs_deb_leave(LBS_DEB_CMD);
+	return 0;
+}
+
+static int lbs_cmd_reg_access(struct lbs_private *priv,
+			       struct cmd_ds_command *cmdptr,
+			       u8 cmd_action, void *pdata_buf)
+{
+	struct lbs_offset_value *offval;
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	offval = (struct lbs_offset_value *)pdata_buf;
+
+	switch (le16_to_cpu(cmdptr->command)) {
+	case CMD_MAC_REG_ACCESS:
+		{
+			struct cmd_ds_mac_reg_access *macreg;
+
+			cmdptr->size =
+			    cpu_to_le16(sizeof (struct cmd_ds_mac_reg_access)
+					+ S_DS_GEN);
+			macreg =
+			    (struct cmd_ds_mac_reg_access *)&cmdptr->params.
+			    macreg;
+
+			macreg->action = cpu_to_le16(cmd_action);
+			macreg->offset = cpu_to_le16((u16) offval->offset);
+			macreg->value = cpu_to_le32(offval->value);
+
+			break;
+		}
+
+	case CMD_BBP_REG_ACCESS:
+		{
+			struct cmd_ds_bbp_reg_access *bbpreg;
+
+			cmdptr->size =
+			    cpu_to_le16(sizeof
+					     (struct cmd_ds_bbp_reg_access)
+					     + S_DS_GEN);
+			bbpreg =
+			    (struct cmd_ds_bbp_reg_access *)&cmdptr->params.
+			    bbpreg;
+
+			bbpreg->action = cpu_to_le16(cmd_action);
+			bbpreg->offset = cpu_to_le16((u16) offval->offset);
+			bbpreg->value = (u8) offval->value;
+
+			break;
+		}
+
+	case CMD_RF_REG_ACCESS:
+		{
+			struct cmd_ds_rf_reg_access *rfreg;
+
+			cmdptr->size =
+			    cpu_to_le16(sizeof
+					     (struct cmd_ds_rf_reg_access) +
+					     S_DS_GEN);
+			rfreg =
+			    (struct cmd_ds_rf_reg_access *)&cmdptr->params.
+			    rfreg;
+
+			rfreg->action = cpu_to_le16(cmd_action);
+			rfreg->offset = cpu_to_le16((u16) offval->offset);
+			rfreg->value = (u8) offval->value;
+
+			break;
+		}
+
+	default:
+		break;
+	}
+
+	lbs_deb_leave(LBS_DEB_CMD);
+	return 0;
+}
+
+static int lbs_cmd_802_11_mac_address(struct lbs_private *priv,
+				       struct cmd_ds_command *cmd,
+				       u16 cmd_action)
+{
+
+	lbs_deb_enter(LBS_DEB_CMD);
+	cmd->command = cpu_to_le16(CMD_802_11_MAC_ADDRESS);
+	cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_mac_address) +
+			     S_DS_GEN);
+	cmd->result = 0;
+
+	cmd->params.macadd.action = cpu_to_le16(cmd_action);
+
+	if (cmd_action == CMD_ACT_SET) {
+		memcpy(cmd->params.macadd.macadd,
+		       priv->current_addr, ETH_ALEN);
+		lbs_deb_hex(LBS_DEB_CMD, "SET_CMD: MAC addr", priv->current_addr, 6);
+	}
+
+	lbs_deb_leave(LBS_DEB_CMD);
+	return 0;
+}
+
+static int lbs_cmd_802_11_eeprom_access(struct lbs_private *priv,
+					 struct cmd_ds_command *cmd,
+					 int cmd_action, void *pdata_buf)
+{
+	struct lbs_ioctl_regrdwr *ea = pdata_buf;
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	cmd->command = cpu_to_le16(CMD_802_11_EEPROM_ACCESS);
+	cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_eeprom_access) +
+				S_DS_GEN);
+	cmd->result = 0;
+
+	cmd->params.rdeeprom.action = cpu_to_le16(ea->action);
+	cmd->params.rdeeprom.offset = cpu_to_le16(ea->offset);
+	cmd->params.rdeeprom.bytecount = cpu_to_le16(ea->NOB);
+	cmd->params.rdeeprom.value = 0;
+
+	lbs_deb_leave(LBS_DEB_CMD);
+	return 0;
+}
+
+static int lbs_cmd_bt_access(struct lbs_private *priv,
+			       struct cmd_ds_command *cmd,
+			       u16 cmd_action, void *pdata_buf)
+{
+	struct cmd_ds_bt_access *bt_access = &cmd->params.bt;
+	lbs_deb_enter_args(LBS_DEB_CMD, "action %d", cmd_action);
+
+	cmd->command = cpu_to_le16(CMD_BT_ACCESS);
+	cmd->size = cpu_to_le16(sizeof(struct cmd_ds_bt_access) + S_DS_GEN);
+	cmd->result = 0;
+	bt_access->action = cpu_to_le16(cmd_action);
+
+	switch (cmd_action) {
+	case CMD_ACT_BT_ACCESS_ADD:
+		memcpy(bt_access->addr1, pdata_buf, 2 * ETH_ALEN);
+		lbs_deb_hex(LBS_DEB_MESH, "BT_ADD: blinded MAC addr", bt_access->addr1, 6);
+		break;
+	case CMD_ACT_BT_ACCESS_DEL:
+		memcpy(bt_access->addr1, pdata_buf, 1 * ETH_ALEN);
+		lbs_deb_hex(LBS_DEB_MESH, "BT_DEL: blinded MAC addr", bt_access->addr1, 6);
+		break;
+	case CMD_ACT_BT_ACCESS_LIST:
+		bt_access->id = cpu_to_le32(*(u32 *) pdata_buf);
+		break;
+	case CMD_ACT_BT_ACCESS_RESET:
+		break;
+	case CMD_ACT_BT_ACCESS_SET_INVERT:
+		bt_access->id = cpu_to_le32(*(u32 *) pdata_buf);
+		break;
+	case CMD_ACT_BT_ACCESS_GET_INVERT:
+		break;
+	default:
+		break;
+	}
+	lbs_deb_leave(LBS_DEB_CMD);
+	return 0;
+}
+
+static int lbs_cmd_fwt_access(struct lbs_private *priv,
+			       struct cmd_ds_command *cmd,
+			       u16 cmd_action, void *pdata_buf)
+{
+	struct cmd_ds_fwt_access *fwt_access = &cmd->params.fwt;
+	lbs_deb_enter_args(LBS_DEB_CMD, "action %d", cmd_action);
+
+	cmd->command = cpu_to_le16(CMD_FWT_ACCESS);
+	cmd->size = cpu_to_le16(sizeof(struct cmd_ds_fwt_access) + S_DS_GEN);
+	cmd->result = 0;
+
+	if (pdata_buf)
+		memcpy(fwt_access, pdata_buf, sizeof(*fwt_access));
+	else
+		memset(fwt_access, 0, sizeof(*fwt_access));
+
+	fwt_access->action = cpu_to_le16(cmd_action);
+
+	lbs_deb_leave(LBS_DEB_CMD);
+	return 0;
+}
+
+int lbs_mesh_access(struct lbs_private *priv, uint16_t cmd_action,
+		    struct cmd_ds_mesh_access *cmd)
+{
+	int ret;
+
+	lbs_deb_enter_args(LBS_DEB_CMD, "action %d", cmd_action);
+
+	cmd->hdr.command = cpu_to_le16(CMD_MESH_ACCESS);
+	cmd->hdr.size = cpu_to_le16(sizeof(struct cmd_ds_mesh_access) + S_DS_GEN);
+	cmd->hdr.result = 0;
+
+	cmd->action = cpu_to_le16(cmd_action);
+
+	ret = lbs_cmd_with_response(priv, CMD_MESH_ACCESS, (*cmd));
+
+	lbs_deb_leave(LBS_DEB_CMD);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(lbs_mesh_access);
+
+int lbs_mesh_config(struct lbs_private *priv, int enable)
+{
+	struct cmd_ds_mesh_config cmd;
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.action = cpu_to_le16(enable);
+	cmd.channel = cpu_to_le16(priv->curbssparams.channel);
+	cmd.type = cpu_to_le16(0x100 + 37);
+	
+	if (enable) {
+		cmd.length = cpu_to_le16(priv->mesh_ssid_len);
+		memcpy(cmd.data, priv->mesh_ssid, priv->mesh_ssid_len);
+	}
+
+	return lbs_cmd_with_response(priv, CMD_MESH_CONFIG, cmd);
+}
+
+static int lbs_cmd_bcn_ctrl(struct lbs_private * priv,
+				struct cmd_ds_command *cmd,
+				u16 cmd_action)
+{
+	struct cmd_ds_802_11_beacon_control
+		*bcn_ctrl = &cmd->params.bcn_ctrl;
+
+	lbs_deb_enter(LBS_DEB_CMD);
+	cmd->size =
+	    cpu_to_le16(sizeof(struct cmd_ds_802_11_beacon_control)
+			     + S_DS_GEN);
+	cmd->command = cpu_to_le16(CMD_802_11_BEACON_CTRL);
+
+	bcn_ctrl->action = cpu_to_le16(cmd_action);
+	bcn_ctrl->beacon_enable = cpu_to_le16(priv->beacon_enable);
+	bcn_ctrl->beacon_period = cpu_to_le16(priv->beacon_period);
+
+	lbs_deb_leave(LBS_DEB_CMD);
+	return 0;
+}
+
+/*
+ * Note: NEVER use lbs_queue_cmd() with addtail==0 other than for
+ * the command timer, because it does not account for queued commands.
+ */
+void lbs_queue_cmd(struct lbs_private *priv,
+	struct cmd_ctrl_node *cmdnode,
+	u8 addtail)
+{
+	unsigned long flags;
+
+	lbs_deb_enter(LBS_DEB_HOST);
+
+	if (!cmdnode || !cmdnode->cmdbuf) {
+		lbs_deb_host("QUEUE_CMD: cmdnode or cmdbuf is NULL\n");
+		goto done;
+	}
+
+	/* Exit_PS command needs to be queued in the header always. */
+	if (le16_to_cpu(cmdnode->cmdbuf->command) == CMD_802_11_PS_MODE) {
+		struct cmd_ds_802_11_ps_mode *psm = (void *) cmdnode->cmdbuf;
+
+		if (psm->action == cpu_to_le16(CMD_SUBCMD_EXIT_PS)) {
+			if (priv->psstate != PS_STATE_FULL_POWER)
+				addtail = 0;
+		}
+	}
+
+	spin_lock_irqsave(&priv->driver_lock, flags);
+
+	if (addtail)
+		list_add_tail(&cmdnode->list, &priv->cmdpendingq);
+	else
+		list_add(&cmdnode->list, &priv->cmdpendingq);
+
+	spin_unlock_irqrestore(&priv->driver_lock, flags);
+
+	lbs_deb_host("QUEUE_CMD: inserted command 0x%04x into cmdpendingq\n",
+	       le16_to_cpu(cmdnode->cmdbuf->command));
+
+done:
+	lbs_deb_leave(LBS_DEB_HOST);
+}
+
+/*
+ * TODO: Fix the issue when DownloadcommandToStation is being called the
+ * second time when the command times out. All the cmdptr->xxx are in little
+ * endian and therefore all the comparissions will fail.
+ * For now - we are not performing the endian conversion the second time - but
+ * for PS and DEEP_SLEEP we need to worry
+ */
+static int DownloadcommandToStation(struct lbs_private *priv,
+				    struct cmd_ctrl_node *cmdnode)
+{
+	unsigned long flags;
+	struct cmd_header *cmd;
+	int ret = -1;
+	u16 cmdsize;
+	u16 command;
+
+	lbs_deb_enter(LBS_DEB_HOST);
+
+	if (!priv || !cmdnode) {
+		lbs_deb_host("DNLD_CMD: priv or cmdmode is NULL\n");
+		goto done;
+	}
+
+	cmd = cmdnode->cmdbuf;
+
+	spin_lock_irqsave(&priv->driver_lock, flags);
+	if (!cmd || !cmd->size) {
+		lbs_deb_host("DNLD_CMD: cmdptr is NULL or zero\n");
+		__lbs_cleanup_and_insert_cmd(priv, cmdnode);
+		spin_unlock_irqrestore(&priv->driver_lock, flags);
+		goto done;
+	}
+
+	priv->cur_cmd = cmdnode;
+	priv->cur_cmd_retcode = 0;
+	spin_unlock_irqrestore(&priv->driver_lock, flags);
+
+	cmdsize = le16_to_cpu(cmd->size);
+	command = le16_to_cpu(cmd->command);
+
+	lbs_deb_host("DNLD_CMD: command 0x%04x, size %d, jiffies %lu\n",
+		    command, cmdsize, jiffies);
+	lbs_deb_hex(LBS_DEB_HOST, "DNLD_CMD", (void *) cmdnode->cmdbuf, cmdsize);
+
+	cmdnode->cmdwaitqwoken = 0;
+
+	ret = priv->hw_host_to_card(priv, MVMS_CMD, (u8 *) cmd, cmdsize);
+
+	if (ret != 0) {
+		lbs_deb_host("DNLD_CMD: hw_host_to_card failed\n");
+		spin_lock_irqsave(&priv->driver_lock, flags);
+		priv->cur_cmd_retcode = ret;
+		__lbs_cleanup_and_insert_cmd(priv, priv->cur_cmd);
+		priv->cur_cmd = NULL;
+		spin_unlock_irqrestore(&priv->driver_lock, flags);
+		goto done;
+	}
+
+	lbs_deb_cmd("DNLD_CMD: sent command 0x%04x, jiffies %lu\n", command, jiffies);
+
+	/* Setup the timer after transmit command */
+	if (command == CMD_802_11_SCAN || command == CMD_802_11_AUTHENTICATE
+	    || command == CMD_802_11_ASSOCIATE)
+		mod_timer(&priv->command_timer, jiffies + (10*HZ));
+	else
+		mod_timer(&priv->command_timer, jiffies + (5*HZ));
+
+	ret = 0;
+
+done:
+	lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret);
+	return ret;
+}
+
+static int lbs_cmd_mac_control(struct lbs_private *priv,
+				struct cmd_ds_command *cmd)
+{
+	struct cmd_ds_mac_control *mac = &cmd->params.macctrl;
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	cmd->command = cpu_to_le16(CMD_MAC_CONTROL);
+	cmd->size = cpu_to_le16(sizeof(struct cmd_ds_mac_control) + S_DS_GEN);
+	mac->action = cpu_to_le16(priv->currentpacketfilter);
+
+	lbs_deb_cmd("MAC_CONTROL: action 0x%x, size %d\n",
+		    le16_to_cpu(mac->action), le16_to_cpu(cmd->size));
+
+	lbs_deb_leave(LBS_DEB_CMD);
+	return 0;
+}
+
+/**
+ *  This function inserts command node to cmdfreeq
+ *  after cleans it. Requires priv->driver_lock held.
+ */
+void __lbs_cleanup_and_insert_cmd(struct lbs_private *priv,
+	struct cmd_ctrl_node *ptempcmd)
+{
+
+	if (!ptempcmd)
+		return;
+
+	cleanup_cmdnode(ptempcmd);
+	list_add_tail(&ptempcmd->list, &priv->cmdfreeq);
+}
+
+static void lbs_cleanup_and_insert_cmd(struct lbs_private *priv,
+	struct cmd_ctrl_node *ptempcmd)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->driver_lock, flags);
+	__lbs_cleanup_and_insert_cmd(priv, ptempcmd);
+	spin_unlock_irqrestore(&priv->driver_lock, flags);
+}
+
+int lbs_set_radio_control(struct lbs_private *priv)
+{
+	int ret = 0;
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	ret = lbs_prepare_and_send_command(priv,
+				    CMD_802_11_RADIO_CONTROL,
+				    CMD_ACT_SET,
+				    CMD_OPTION_WAITFORRSP, 0, NULL);
+
+	lbs_deb_cmd("RADIO_SET: radio %d, preamble %d\n",
+	       priv->radioon, priv->preamble);
+
+	lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
+	return ret;
+}
+
+int lbs_set_mac_packet_filter(struct lbs_private *priv)
+{
+	int ret = 0;
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	/* Send MAC control command to station */
+	ret = lbs_prepare_and_send_command(priv,
+				    CMD_MAC_CONTROL, 0, 0, 0, NULL);
+
+	lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
+	return ret;
+}
+
+/**
+ *  @brief This function prepare the command before send to firmware.
+ *
+ *  @param priv		A pointer to struct lbs_private structure
+ *  @param cmd_no	command number
+ *  @param cmd_action	command action: GET or SET
+ *  @param wait_option	wait option: wait response or not
+ *  @param cmd_oid	cmd oid: treated as sub command
+ *  @param pdata_buf	A pointer to informaion buffer
+ *  @return 		0 or -1
+ */
+int lbs_prepare_and_send_command(struct lbs_private *priv,
+			  u16 cmd_no,
+			  u16 cmd_action,
+			  u16 wait_option, u32 cmd_oid, void *pdata_buf)
+{
+	int ret = 0;
+	struct cmd_ctrl_node *cmdnode;
+	struct cmd_ds_command *cmdptr;
+	unsigned long flags;
+
+	lbs_deb_enter(LBS_DEB_HOST);
+
+	if (!priv) {
+		lbs_deb_host("PREP_CMD: priv is NULL\n");
+		ret = -1;
+		goto done;
+	}
+
+	if (priv->surpriseremoved) {
+		lbs_deb_host("PREP_CMD: card removed\n");
+		ret = -1;
+		goto done;
+	}
+
+	cmdnode = lbs_get_cmd_ctrl_node(priv);
+
+	if (cmdnode == NULL) {
+		lbs_deb_host("PREP_CMD: cmdnode is NULL\n");
+
+		/* Wake up main thread to execute next command */
+		wake_up_interruptible(&priv->waitq);
+		ret = -1;
+		goto done;
+	}
+
+	lbs_set_cmd_ctrl_node(priv, cmdnode, wait_option, pdata_buf);
+
+	cmdptr = (struct cmd_ds_command *)cmdnode->cmdbuf;
+
+	lbs_deb_host("PREP_CMD: command 0x%04x\n", cmd_no);
+
+	if (!cmdptr) {
+		lbs_deb_host("PREP_CMD: cmdptr is NULL\n");
+		lbs_cleanup_and_insert_cmd(priv, cmdnode);
+		ret = -1;
+		goto done;
+	}
+
+	/* Set sequence number, command and INT option */
+	priv->seqnum++;
+	cmdptr->seqnum = cpu_to_le16(priv->seqnum);
+
+	cmdptr->command = cpu_to_le16(cmd_no);
+	cmdptr->result = 0;
+
+	switch (cmd_no) {
+	case CMD_802_11_PS_MODE:
+		ret = lbs_cmd_802_11_ps_mode(priv, cmdptr, cmd_action);
+		break;
+
+	case CMD_802_11_SCAN:
+		ret = lbs_cmd_80211_scan(priv, cmdptr, pdata_buf);
+		break;
+
+	case CMD_MAC_CONTROL:
+		ret = lbs_cmd_mac_control(priv, cmdptr);
+		break;
+
+	case CMD_802_11_ASSOCIATE:
+	case CMD_802_11_REASSOCIATE:
+		ret = lbs_cmd_80211_associate(priv, cmdptr, pdata_buf);
+		break;
+
+	case CMD_802_11_DEAUTHENTICATE:
+		ret = lbs_cmd_80211_deauthenticate(priv, cmdptr);
+		break;
+
+	case CMD_802_11_SET_WEP:
+		ret = lbs_cmd_802_11_set_wep(priv, cmdptr, cmd_action, pdata_buf);
+		break;
+
+	case CMD_802_11_AD_HOC_START:
+		ret = lbs_cmd_80211_ad_hoc_start(priv, cmdptr, pdata_buf);
+		break;
+	case CMD_CODE_DNLD:
+		break;
+
+	case CMD_802_11_RESET:
+		ret = lbs_cmd_802_11_reset(priv, cmdptr, cmd_action);
+		break;
+
+	case CMD_802_11_GET_LOG:
+		ret = lbs_cmd_802_11_get_log(priv, cmdptr);
+		break;
+
+	case CMD_802_11_AUTHENTICATE:
+		ret = lbs_cmd_80211_authenticate(priv, cmdptr, pdata_buf);
+		break;
+
+	case CMD_802_11_GET_STAT:
+		ret = lbs_cmd_802_11_get_stat(priv, cmdptr);
+		break;
+
+	case CMD_802_11_SNMP_MIB:
+		ret = lbs_cmd_802_11_snmp_mib(priv, cmdptr,
+					       cmd_action, cmd_oid, pdata_buf);
+		break;
+
+	case CMD_MAC_REG_ACCESS:
+	case CMD_BBP_REG_ACCESS:
+	case CMD_RF_REG_ACCESS:
+		ret = lbs_cmd_reg_access(priv, cmdptr, cmd_action, pdata_buf);
+		break;
+
+	case CMD_802_11_RF_TX_POWER:
+		ret = lbs_cmd_802_11_rf_tx_power(priv, cmdptr,
+						  cmd_action, pdata_buf);
+		break;
+
+	case CMD_802_11_RADIO_CONTROL:
+		ret = lbs_cmd_802_11_radio_control(priv, cmdptr, cmd_action);
+		break;
+
+	case CMD_802_11_RATE_ADAPT_RATESET:
+		ret = lbs_cmd_802_11_rate_adapt_rateset(priv,
+							 cmdptr, cmd_action);
+		break;
+
+	case CMD_MAC_MULTICAST_ADR:
+		ret = lbs_cmd_mac_multicast_adr(priv, cmdptr, cmd_action);
+		break;
+
+	case CMD_802_11_MONITOR_MODE:
+		ret = lbs_cmd_802_11_monitor_mode(priv, cmdptr,
+				          cmd_action, pdata_buf);
+		break;
+
+	case CMD_802_11_AD_HOC_JOIN:
+		ret = lbs_cmd_80211_ad_hoc_join(priv, cmdptr, pdata_buf);
+		break;
+
+	case CMD_802_11_RSSI:
+		ret = lbs_cmd_802_11_rssi(priv, cmdptr);
+		break;
+
+	case CMD_802_11_AD_HOC_STOP:
+		ret = lbs_cmd_80211_ad_hoc_stop(priv, cmdptr);
+		break;
+
+	case CMD_802_11_ENABLE_RSN:
+		ret = lbs_cmd_802_11_enable_rsn(priv, cmdptr, cmd_action,
+				pdata_buf);
+		break;
+
+	case CMD_802_11_KEY_MATERIAL:
+		ret = lbs_cmd_802_11_key_material(priv, cmdptr, cmd_action,
+				cmd_oid, pdata_buf);
+		break;
+
+	case CMD_802_11_PAIRWISE_TSC:
+		break;
+	case CMD_802_11_GROUP_TSC:
+		break;
+
+	case CMD_802_11_MAC_ADDRESS:
+		ret = lbs_cmd_802_11_mac_address(priv, cmdptr, cmd_action);
+		break;
+
+	case CMD_802_11_EEPROM_ACCESS:
+		ret = lbs_cmd_802_11_eeprom_access(priv, cmdptr,
+						    cmd_action, pdata_buf);
+		break;
+
+	case CMD_802_11_SET_AFC:
+	case CMD_802_11_GET_AFC:
+
+		cmdptr->command = cpu_to_le16(cmd_no);
+		cmdptr->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_afc) +
+					   S_DS_GEN);
+
+		memmove(&cmdptr->params.afc,
+			pdata_buf, sizeof(struct cmd_ds_802_11_afc));
+
+		ret = 0;
+		goto done;
+
+	case CMD_802_11D_DOMAIN_INFO:
+		ret = lbs_cmd_802_11d_domain_info(priv, cmdptr,
+						   cmd_no, cmd_action);
+		break;
+
+	case CMD_802_11_SLEEP_PARAMS:
+		ret = lbs_cmd_802_11_sleep_params(priv, cmdptr, cmd_action);
+		break;
+	case CMD_802_11_INACTIVITY_TIMEOUT:
+		ret = lbs_cmd_802_11_inactivity_timeout(priv, cmdptr,
+							 cmd_action, pdata_buf);
+		lbs_set_cmd_ctrl_node(priv, cmdnode, 0, pdata_buf);
+		break;
+
+	case CMD_802_11_TPC_CFG:
+		cmdptr->command = cpu_to_le16(CMD_802_11_TPC_CFG);
+		cmdptr->size =
+		    cpu_to_le16(sizeof(struct cmd_ds_802_11_tpc_cfg) +
+				     S_DS_GEN);
+
+		memmove(&cmdptr->params.tpccfg,
+			pdata_buf, sizeof(struct cmd_ds_802_11_tpc_cfg));
+
+		ret = 0;
+		break;
+	case CMD_802_11_LED_GPIO_CTRL:
+		{
+			struct mrvlietypes_ledgpio *gpio =
+			    (struct mrvlietypes_ledgpio*)
+			    cmdptr->params.ledgpio.data;
+
+			memmove(&cmdptr->params.ledgpio,
+				pdata_buf,
+				sizeof(struct cmd_ds_802_11_led_ctrl));
+
+			cmdptr->command =
+			    cpu_to_le16(CMD_802_11_LED_GPIO_CTRL);
+
+#define ACTION_NUMLED_TLVTYPE_LEN_FIELDS_LEN 8
+			cmdptr->size =
+			    cpu_to_le16(le16_to_cpu(gpio->header.len)
+				+ S_DS_GEN
+				+ ACTION_NUMLED_TLVTYPE_LEN_FIELDS_LEN);
+			gpio->header.len = gpio->header.len;
+
+			ret = 0;
+			break;
+		}
+	case CMD_802_11_SUBSCRIBE_EVENT:
+		lbs_cmd_802_11_subscribe_event(priv, cmdptr,
+			cmd_action, pdata_buf);
+		break;
+	case CMD_802_11_PWR_CFG:
+		cmdptr->command = cpu_to_le16(CMD_802_11_PWR_CFG);
+		cmdptr->size =
+		    cpu_to_le16(sizeof(struct cmd_ds_802_11_pwr_cfg) +
+				     S_DS_GEN);
+		memmove(&cmdptr->params.pwrcfg, pdata_buf,
+			sizeof(struct cmd_ds_802_11_pwr_cfg));
+
+		ret = 0;
+		break;
+	case CMD_BT_ACCESS:
+		ret = lbs_cmd_bt_access(priv, cmdptr, cmd_action, pdata_buf);
+		break;
+
+	case CMD_FWT_ACCESS:
+		ret = lbs_cmd_fwt_access(priv, cmdptr, cmd_action, pdata_buf);
+		break;
+
+	case CMD_GET_TSF:
+		cmdptr->command = cpu_to_le16(CMD_GET_TSF);
+		cmdptr->size = cpu_to_le16(sizeof(struct cmd_ds_get_tsf) +
+					   S_DS_GEN);
+		ret = 0;
+		break;
+	case CMD_802_11_BEACON_CTRL:
+		ret = lbs_cmd_bcn_ctrl(priv, cmdptr, cmd_action);
+		break;
+	default:
+		lbs_deb_host("PREP_CMD: unknown command 0x%04x\n", cmd_no);
+		ret = -1;
+		break;
+	}
+
+	/* return error, since the command preparation failed */
+	if (ret != 0) {
+		lbs_deb_host("PREP_CMD: command preparation failed\n");
+		lbs_cleanup_and_insert_cmd(priv, cmdnode);
+		ret = -1;
+		goto done;
+	}
+
+	cmdnode->cmdwaitqwoken = 0;
+
+	lbs_queue_cmd(priv, cmdnode, 1);
+	wake_up_interruptible(&priv->waitq);
+
+	if (wait_option & CMD_OPTION_WAITFORRSP) {
+		lbs_deb_host("PREP_CMD: wait for response\n");
+		might_sleep();
+		wait_event_interruptible(cmdnode->cmdwait_q,
+					 cmdnode->cmdwaitqwoken);
+	}
+
+	spin_lock_irqsave(&priv->driver_lock, flags);
+	if (priv->cur_cmd_retcode) {
+		lbs_deb_host("PREP_CMD: command failed with return code %d\n",
+		       priv->cur_cmd_retcode);
+		priv->cur_cmd_retcode = 0;
+		ret = -1;
+	}
+	spin_unlock_irqrestore(&priv->driver_lock, flags);
+
+done:
+	lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(lbs_prepare_and_send_command);
+
+/**
+ *  @brief This function allocates the command buffer and link
+ *  it to command free queue.
+ *
+ *  @param priv		A pointer to struct lbs_private structure
+ *  @return 		0 or -1
+ */
+int lbs_allocate_cmd_buffer(struct lbs_private *priv)
+{
+	int ret = 0;
+	u32 bufsize;
+	u32 i;
+	struct cmd_ctrl_node *cmdarray;
+
+	lbs_deb_enter(LBS_DEB_HOST);
+
+	/* Allocate and initialize the command array */
+	bufsize = sizeof(struct cmd_ctrl_node) * LBS_NUM_CMD_BUFFERS;
+	if (!(cmdarray = kzalloc(bufsize, GFP_KERNEL))) {
+		lbs_deb_host("ALLOC_CMD_BUF: tempcmd_array is NULL\n");
+		ret = -1;
+		goto done;
+	}
+	priv->cmd_array = cmdarray;
+
+	/* Allocate and initialize each command buffer in the command array */
+	for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) {
+		cmdarray[i].cmdbuf = kzalloc(LBS_CMD_BUFFER_SIZE, GFP_KERNEL);
+		if (!cmdarray[i].cmdbuf) {
+			lbs_deb_host("ALLOC_CMD_BUF: ptempvirtualaddr is NULL\n");
+			ret = -1;
+			goto done;
+		}
+	}
+
+	for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) {
+		init_waitqueue_head(&cmdarray[i].cmdwait_q);
+		lbs_cleanup_and_insert_cmd(priv, &cmdarray[i]);
+	}
+	ret = 0;
+
+done:
+	lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret);
+	return ret;
+}
+
+/**
+ *  @brief This function frees the command buffer.
+ *
+ *  @param priv		A pointer to struct lbs_private structure
+ *  @return 		0 or -1
+ */
+int lbs_free_cmd_buffer(struct lbs_private *priv)
+{
+	struct cmd_ctrl_node *cmdarray;
+	unsigned int i;
+
+	lbs_deb_enter(LBS_DEB_HOST);
+
+	/* need to check if cmd array is allocated or not */
+	if (priv->cmd_array == NULL) {
+		lbs_deb_host("FREE_CMD_BUF: cmd_array is NULL\n");
+		goto done;
+	}
+
+	cmdarray = priv->cmd_array;
+
+	/* Release shared memory buffers */
+	for (i = 0; i < LBS_NUM_CMD_BUFFERS; i++) {
+		if (cmdarray[i].cmdbuf) {
+			kfree(cmdarray[i].cmdbuf);
+			cmdarray[i].cmdbuf = NULL;
+		}
+	}
+
+	/* Release cmd_ctrl_node */
+	if (priv->cmd_array) {
+		kfree(priv->cmd_array);
+		priv->cmd_array = NULL;
+	}
+
+done:
+	lbs_deb_leave(LBS_DEB_HOST);
+	return 0;
+}
+
+/**
+ *  @brief This function gets a free command node if available in
+ *  command free queue.
+ *
+ *  @param priv		A pointer to struct lbs_private structure
+ *  @return cmd_ctrl_node A pointer to cmd_ctrl_node structure or NULL
+ */
+static struct cmd_ctrl_node *lbs_get_cmd_ctrl_node(struct lbs_private *priv)
+{
+	struct cmd_ctrl_node *tempnode;
+	unsigned long flags;
+
+	lbs_deb_enter(LBS_DEB_HOST);
+
+	if (!priv)
+		return NULL;
+
+	spin_lock_irqsave(&priv->driver_lock, flags);
+
+	if (!list_empty(&priv->cmdfreeq)) {
+		tempnode = list_first_entry(&priv->cmdfreeq,
+					    struct cmd_ctrl_node, list);
+		list_del(&tempnode->list);
+	} else {
+		lbs_deb_host("GET_CMD_NODE: cmd_ctrl_node is not available\n");
+		tempnode = NULL;
+	}
+
+	spin_unlock_irqrestore(&priv->driver_lock, flags);
+
+	if (tempnode)
+		cleanup_cmdnode(tempnode);
+
+	lbs_deb_leave(LBS_DEB_HOST);
+	return tempnode;
+}
+
+/**
+ *  @brief This function cleans command node.
+ *
+ *  @param ptempnode	A pointer to cmdCtrlNode structure
+ *  @return 		n/a
+ */
+static void cleanup_cmdnode(struct cmd_ctrl_node *cmdnode)
+{
+	lbs_deb_enter(LBS_DEB_HOST);
+
+	if (!cmdnode)
+		return;
+	cmdnode->cmdwaitqwoken = 1;
+	wake_up_interruptible(&cmdnode->cmdwait_q);
+	cmdnode->wait_option = 0;
+	cmdnode->pdata_buf = NULL;
+	cmdnode->callback = NULL;
+	cmdnode->callback_arg = 0;
+
+	if (cmdnode->cmdbuf != NULL)
+		memset(cmdnode->cmdbuf, 0, LBS_CMD_BUFFER_SIZE);
+
+	lbs_deb_leave(LBS_DEB_HOST);
+}
+
+/**
+ *  @brief This function initializes the command node.
+ *
+ *  @param priv		A pointer to struct lbs_private structure
+ *  @param ptempnode	A pointer to cmd_ctrl_node structure
+ *  @param wait_option	wait option: wait response or not
+ *  @param pdata_buf	A pointer to informaion buffer
+ *  @return 		0 or -1
+ */
+static void lbs_set_cmd_ctrl_node(struct lbs_private *priv,
+				  struct cmd_ctrl_node *ptempnode,
+				  u16 wait_option, void *pdata_buf)
+{
+	lbs_deb_enter(LBS_DEB_HOST);
+
+	if (!ptempnode)
+		return;
+
+	ptempnode->wait_option = wait_option;
+	ptempnode->pdata_buf = pdata_buf;
+	ptempnode->callback = NULL;
+	ptempnode->callback_arg = 0;
+
+	lbs_deb_leave(LBS_DEB_HOST);
+}
+
+/**
+ *  @brief This function executes next command in command
+ *  pending queue. It will put fimware back to PS mode
+ *  if applicable.
+ *
+ *  @param priv     A pointer to struct lbs_private structure
+ *  @return 	   0 or -1
+ */
+int lbs_execute_next_command(struct lbs_private *priv)
+{
+	struct cmd_ctrl_node *cmdnode = NULL;
+	struct cmd_header *cmd;
+	unsigned long flags;
+	int ret = 0;
+
+	// Debug group is LBS_DEB_THREAD and not LBS_DEB_HOST, because the
+	// only caller to us is lbs_thread() and we get even when a
+	// data packet is received
+	lbs_deb_enter(LBS_DEB_THREAD);
+
+	spin_lock_irqsave(&priv->driver_lock, flags);
+
+	if (priv->cur_cmd) {
+		lbs_pr_alert( "EXEC_NEXT_CMD: already processing command!\n");
+		spin_unlock_irqrestore(&priv->driver_lock, flags);
+		ret = -1;
+		goto done;
+	}
+
+	if (!list_empty(&priv->cmdpendingq)) {
+		cmdnode = list_first_entry(&priv->cmdpendingq,
+					   struct cmd_ctrl_node, list);
+	}
+
+	spin_unlock_irqrestore(&priv->driver_lock, flags);
+
+	if (cmdnode) {
+		cmd = cmdnode->cmdbuf;
+
+		if (is_command_allowed_in_ps(le16_to_cpu(cmd->command))) {
+			if ((priv->psstate == PS_STATE_SLEEP) ||
+			    (priv->psstate == PS_STATE_PRE_SLEEP)) {
+				lbs_deb_host(
+				       "EXEC_NEXT_CMD: cannot send cmd 0x%04x in psstate %d\n",
+				       le16_to_cpu(cmd->command),
+				       priv->psstate);
+				ret = -1;
+				goto done;
+			}
+			lbs_deb_host("EXEC_NEXT_CMD: OK to send command "
+				     "0x%04x in psstate %d\n",
+				     le16_to_cpu(cmd->command), priv->psstate);
+		} else if (priv->psstate != PS_STATE_FULL_POWER) {
+			/*
+			 * 1. Non-PS command:
+			 * Queue it. set needtowakeup to TRUE if current state
+			 * is SLEEP, otherwise call lbs_ps_wakeup to send Exit_PS.
+			 * 2. PS command but not Exit_PS:
+			 * Ignore it.
+			 * 3. PS command Exit_PS:
+			 * Set needtowakeup to TRUE if current state is SLEEP,
+			 * otherwise send this command down to firmware
+			 * immediately.
+			 */
+			if (cmd->command != cpu_to_le16(CMD_802_11_PS_MODE)) {
+				/*  Prepare to send Exit PS,
+				 *  this non PS command will be sent later */
+				if ((priv->psstate == PS_STATE_SLEEP)
+				    || (priv->psstate == PS_STATE_PRE_SLEEP)
+				    ) {
+					/* w/ new scheme, it will not reach here.
+					   since it is blocked in main_thread. */
+					priv->needtowakeup = 1;
+				} else
+					lbs_ps_wakeup(priv, 0);
+
+				ret = 0;
+				goto done;
+			} else {
+				/*
+				 * PS command. Ignore it if it is not Exit_PS.
+				 * otherwise send it down immediately.
+				 */
+				struct cmd_ds_802_11_ps_mode *psm = (void *)cmd;
+
+				lbs_deb_host(
+				       "EXEC_NEXT_CMD: PS cmd, action 0x%02x\n",
+				       psm->action);
+				if (psm->action !=
+				    cpu_to_le16(CMD_SUBCMD_EXIT_PS)) {
+					lbs_deb_host(
+					       "EXEC_NEXT_CMD: ignore ENTER_PS cmd\n");
+					list_del(&cmdnode->list);
+					lbs_cleanup_and_insert_cmd(priv, cmdnode);
+
+					ret = 0;
+					goto done;
+				}
+
+				if ((priv->psstate == PS_STATE_SLEEP) ||
+				    (priv->psstate == PS_STATE_PRE_SLEEP)) {
+					lbs_deb_host(
+					       "EXEC_NEXT_CMD: ignore EXIT_PS cmd in sleep\n");
+					list_del(&cmdnode->list);
+					lbs_cleanup_and_insert_cmd(priv, cmdnode);
+					priv->needtowakeup = 1;
+
+					ret = 0;
+					goto done;
+				}
+
+				lbs_deb_host(
+				       "EXEC_NEXT_CMD: sending EXIT_PS\n");
+			}
+		}
+		list_del(&cmdnode->list);
+		lbs_deb_host("EXEC_NEXT_CMD: sending command 0x%04x\n",
+			    le16_to_cpu(cmd->command));
+		DownloadcommandToStation(priv, cmdnode);
+	} else {
+		/*
+		 * check if in power save mode, if yes, put the device back
+		 * to PS mode
+		 */
+		if ((priv->psmode != LBS802_11POWERMODECAM) &&
+		    (priv->psstate == PS_STATE_FULL_POWER) &&
+		    ((priv->connect_status == LBS_CONNECTED) ||
+		    (priv->mesh_connect_status == LBS_CONNECTED))) {
+			if (priv->secinfo.WPAenabled ||
+			    priv->secinfo.WPA2enabled) {
+				/* check for valid WPA group keys */
+				if (priv->wpa_mcast_key.len ||
+				    priv->wpa_unicast_key.len) {
+					lbs_deb_host(
+					       "EXEC_NEXT_CMD: WPA enabled and GTK_SET"
+					       " go back to PS_SLEEP");
+					lbs_ps_sleep(priv, 0);
+				}
+			} else {
+				lbs_deb_host(
+				       "EXEC_NEXT_CMD: cmdpendingq empty, "
+				       "go back to PS_SLEEP");
+				lbs_ps_sleep(priv, 0);
+			}
+		}
+	}
+
+	ret = 0;
+done:
+	lbs_deb_leave(LBS_DEB_THREAD);
+	return ret;
+}
+
+void lbs_send_iwevcustom_event(struct lbs_private *priv, s8 *str)
+{
+	union iwreq_data iwrq;
+	u8 buf[50];
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	memset(&iwrq, 0, sizeof(union iwreq_data));
+	memset(buf, 0, sizeof(buf));
+
+	snprintf(buf, sizeof(buf) - 1, "%s", str);
+
+	iwrq.data.length = strlen(buf) + 1 + IW_EV_LCP_LEN;
+
+	/* Send Event to upper layer */
+	lbs_deb_wext("event indication string %s\n", (char *)buf);
+	lbs_deb_wext("event indication length %d\n", iwrq.data.length);
+	lbs_deb_wext("sending wireless event IWEVCUSTOM for %s\n", str);
+
+	wireless_send_event(priv->dev, IWEVCUSTOM, &iwrq, buf);
+
+	lbs_deb_leave(LBS_DEB_WEXT);
+}
+
+static int sendconfirmsleep(struct lbs_private *priv, u8 *cmdptr, u16 size)
+{
+	unsigned long flags;
+	int ret = 0;
+
+	lbs_deb_enter(LBS_DEB_HOST);
+
+	lbs_deb_host("SEND_SLEEPC_CMD: before download, cmd size %d\n",
+	       size);
+
+	lbs_deb_hex(LBS_DEB_HOST, "sleep confirm command", cmdptr, size);
+
+	ret = priv->hw_host_to_card(priv, MVMS_CMD, cmdptr, size);
+	priv->dnld_sent = DNLD_RES_RECEIVED;
+
+	spin_lock_irqsave(&priv->driver_lock, flags);
+	if (priv->intcounter || priv->currenttxskb)
+		lbs_deb_host("SEND_SLEEPC_CMD: intcounter %d, currenttxskb %p\n",
+		       priv->intcounter, priv->currenttxskb);
+	spin_unlock_irqrestore(&priv->driver_lock, flags);
+
+	if (ret) {
+		lbs_pr_alert(
+		       "SEND_SLEEPC_CMD: Host to Card failed for Confirm Sleep\n");
+	} else {
+		spin_lock_irqsave(&priv->driver_lock, flags);
+		if (!priv->intcounter) {
+			priv->psstate = PS_STATE_SLEEP;
+		} else {
+			lbs_deb_host("SEND_SLEEPC_CMD: after sent, intcounter %d\n",
+			       priv->intcounter);
+		}
+		spin_unlock_irqrestore(&priv->driver_lock, flags);
+
+		lbs_deb_host("SEND_SLEEPC_CMD: sent confirm sleep\n");
+	}
+
+	lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret);
+	return ret;
+}
+
+void lbs_ps_sleep(struct lbs_private *priv, int wait_option)
+{
+	lbs_deb_enter(LBS_DEB_HOST);
+
+	/*
+	 * PS is currently supported only in Infrastructure mode
+	 * Remove this check if it is to be supported in IBSS mode also
+	 */
+
+	lbs_prepare_and_send_command(priv, CMD_802_11_PS_MODE,
+			      CMD_SUBCMD_ENTER_PS, wait_option, 0, NULL);
+
+	lbs_deb_leave(LBS_DEB_HOST);
+}
+
+/**
+ *  @brief This function sends Exit_PS command to firmware.
+ *
+ *  @param priv    	A pointer to struct lbs_private structure
+ *  @param wait_option	wait response or not
+ *  @return 	   	n/a
+ */
+void lbs_ps_wakeup(struct lbs_private *priv, int wait_option)
+{
+	__le32 Localpsmode;
+
+	lbs_deb_enter(LBS_DEB_HOST);
+
+	Localpsmode = cpu_to_le32(LBS802_11POWERMODECAM);
+
+	lbs_prepare_and_send_command(priv, CMD_802_11_PS_MODE,
+			      CMD_SUBCMD_EXIT_PS,
+			      wait_option, 0, &Localpsmode);
+
+	lbs_deb_leave(LBS_DEB_HOST);
+}
+
+/**
+ *  @brief This function checks condition and prepares to
+ *  send sleep confirm command to firmware if ok.
+ *
+ *  @param priv    	A pointer to struct lbs_private structure
+ *  @param psmode  	Power Saving mode
+ *  @return 	   	n/a
+ */
+void lbs_ps_confirm_sleep(struct lbs_private *priv, u16 psmode)
+{
+	unsigned long flags =0;
+	u8 allowed = 1;
+
+	lbs_deb_enter(LBS_DEB_HOST);
+
+	if (priv->dnld_sent) {
+		allowed = 0;
+		lbs_deb_host("dnld_sent was set");
+	}
+
+	spin_lock_irqsave(&priv->driver_lock, flags);
+	if (priv->cur_cmd) {
+		allowed = 0;
+		lbs_deb_host("cur_cmd was set");
+	}
+	if (priv->intcounter > 0) {
+		allowed = 0;
+		lbs_deb_host("intcounter %d", priv->intcounter);
+	}
+	spin_unlock_irqrestore(&priv->driver_lock, flags);
+
+	if (allowed) {
+		lbs_deb_host("sending lbs_ps_confirm_sleep\n");
+		sendconfirmsleep(priv, (u8 *) & priv->lbs_ps_confirm_sleep,
+				 sizeof(struct PS_CMD_ConfirmSleep));
+	} else {
+		lbs_deb_host("sleep confirm has been delayed\n");
+	}
+
+	lbs_deb_leave(LBS_DEB_HOST);
+}
+
+
+/**
+ *  @brief Simple callback that copies response back into command
+ *
+ *  @param priv    	A pointer to struct lbs_private structure
+ *  @param extra  	A pointer to the original command structure for which
+ *                      'resp' is a response
+ *  @param resp         A pointer to the command response
+ *
+ *  @return 	   	0 on success, error on failure
+ */
+int lbs_cmd_copyback(struct lbs_private *priv, unsigned long extra,
+		     struct cmd_header *resp)
+{
+	struct cmd_header *buf = (void *)extra;
+	uint16_t copy_len;
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	copy_len = min(le16_to_cpu(buf->size), le16_to_cpu(resp->size));
+	lbs_deb_cmd("Copying back %u bytes; command response was %u bytes, "
+		    "copy back buffer was %u bytes\n", copy_len,
+		    le16_to_cpu(resp->size), le16_to_cpu(buf->size));
+	memcpy(buf, resp, copy_len);
+
+	lbs_deb_leave(LBS_DEB_CMD);
+	return 0;
+}
+
+/**
+ *  @brief Simple way to call firmware functions
+ *
+ *  @param priv    	A pointer to struct lbs_private structure
+ *  @param psmode  	one of the many CMD_802_11_xxxx
+ *  @param cmd          pointer to the parameters structure for above command
+ *                      (this should not include the command, size, sequence
+ *                      and result fields from struct cmd_ds_gen)
+ *  @param cmd_size     size structure pointed to by cmd
+ *  @param rsp          pointer to an area where the result should be placed
+ *  @param rsp_size     pointer to the size of the rsp area. If the firmware
+ *                      returns fewer bytes, then this *rsp_size will be
+ *                      changed to the actual size.
+ *  @return 	   	-1 in case of a higher level error, otherwise
+ *                      the result code from the firmware
+ */
+int __lbs_cmd(struct lbs_private *priv, uint16_t command,
+	      struct cmd_header *in_cmd, int in_cmd_size,
+	      int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *),
+	      unsigned long callback_arg)
+{
+	struct cmd_ctrl_node *cmdnode;
+	unsigned long flags;
+	int ret = 0;
+
+	lbs_deb_enter(LBS_DEB_HOST);
+
+	if (!priv) {
+		lbs_deb_host("PREP_CMD: priv is NULL\n");
+		ret = -1;
+		goto done;
+	}
+
+	if (priv->surpriseremoved) {
+		lbs_deb_host("PREP_CMD: card removed\n");
+		ret = -1;
+		goto done;
+	}
+
+	cmdnode = lbs_get_cmd_ctrl_node(priv);
+	if (cmdnode == NULL) {
+		lbs_deb_host("PREP_CMD: cmdnode is NULL\n");
+
+		/* Wake up main thread to execute next command */
+		wake_up_interruptible(&priv->waitq);
+		ret = -1;
+		goto done;
+	}
+
+	cmdnode->wait_option = CMD_OPTION_WAITFORRSP;
+	cmdnode->callback = callback;
+	cmdnode->callback_arg = callback_arg;
+
+	/* Copy the incoming command to the buffer */
+	memcpy(cmdnode->cmdbuf, in_cmd, in_cmd_size);
+
+	/* Set sequence number, clean result, move to buffer */
+	priv->seqnum++;
+	cmdnode->cmdbuf->command = cpu_to_le16(command);
+	cmdnode->cmdbuf->size    = cpu_to_le16(in_cmd_size);
+	cmdnode->cmdbuf->seqnum  = cpu_to_le16(priv->seqnum);
+	cmdnode->cmdbuf->result  = 0;
+
+	lbs_deb_host("PREP_CMD: command 0x%04x\n", command);
+
+	/* here was the big old switch() statement, which is now obsolete,
+	 * because the caller of lbs_cmd() sets up all of *cmd for us. */
+
+	cmdnode->cmdwaitqwoken = 0;
+	lbs_queue_cmd(priv, cmdnode, 1);
+	wake_up_interruptible(&priv->waitq);
+
+	might_sleep();
+	wait_event_interruptible(cmdnode->cmdwait_q, cmdnode->cmdwaitqwoken);
+
+	spin_lock_irqsave(&priv->driver_lock, flags);
+	if (priv->cur_cmd_retcode) {
+		lbs_deb_host("PREP_CMD: command failed with return code %d\n",
+		       priv->cur_cmd_retcode);
+		priv->cur_cmd_retcode = 0;
+		ret = -1;
+	}
+	spin_unlock_irqrestore(&priv->driver_lock, flags);
+
+done:
+	lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(__lbs_cmd);
+
+

+ 38 - 0
package/libertas/src/cmd.h

@@ -0,0 +1,38 @@
+/* Copyright (C) 2007, Red Hat, Inc. */
+
+#ifndef _LBS_CMD_H_
+#define _LBS_CMD_H_
+
+#include "hostcmd.h"
+#include "dev.h"
+
+#define lbs_cmd(priv, cmdnr, cmd, callback, callback_arg) \
+	__lbs_cmd(priv, cmdnr, &(cmd).hdr, sizeof(cmd),	  \
+			callback, callback_arg)
+
+#define lbs_cmd_with_response(priv, cmdnr, cmd) \
+	__lbs_cmd(priv, cmdnr, &(cmd).hdr, sizeof(cmd), \
+		  lbs_cmd_copyback, (unsigned long) &cmd)
+ 
+int __lbs_cmd(struct lbs_private *priv, uint16_t command,
+	      struct cmd_header *in_cmd, int in_cmd_size, 
+	      int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *),
+	      unsigned long callback_arg);
+
+int lbs_cmd_copyback(struct lbs_private *priv, unsigned long extra,
+		     struct cmd_header *resp);
+
+int lbs_update_hw_spec(struct lbs_private *priv);
+
+int lbs_mesh_access(struct lbs_private *priv, uint16_t cmd_action,
+		    struct cmd_ds_mesh_access *cmd);
+
+int lbs_get_data_rate(struct lbs_private *priv);
+int lbs_set_data_rate(struct lbs_private *priv, u8 rate);
+
+int lbs_get_channel(struct lbs_private *priv);
+int lbs_set_channel(struct lbs_private *priv, u8 channel);
+
+int lbs_mesh_config(struct lbs_private *priv, int enable);
+
+#endif /* _LBS_CMD_H */

+ 897 - 0
package/libertas/src/cmdresp.c

@@ -0,0 +1,897 @@
+/**
+  * This file contains the handling of command
+  * responses as well as events generated by firmware.
+  */
+#include <linux/delay.h>
+#include <linux/if_arp.h>
+#include <linux/netdevice.h>
+
+#include <net/iw_handler.h>
+
+#include "host.h"
+#include "decl.h"
+#include "defs.h"
+#include "dev.h"
+#include "join.h"
+#include "wext.h"
+
+/**
+ *  @brief This function handles disconnect event. it
+ *  reports disconnect to upper layer, clean tx/rx packets,
+ *  reset link state etc.
+ *
+ *  @param priv    A pointer to struct lbs_private structure
+ *  @return 	   n/a
+ */
+void lbs_mac_event_disconnected(struct lbs_private *priv)
+{
+	union iwreq_data wrqu;
+
+	if (priv->connect_status != LBS_CONNECTED)
+		return;
+
+	lbs_deb_enter(LBS_DEB_ASSOC);
+
+	memset(wrqu.ap_addr.sa_data, 0x00, ETH_ALEN);
+	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+
+	/*
+	 * Cisco AP sends EAP failure and de-auth in less than 0.5 ms.
+	 * It causes problem in the Supplicant
+	 */
+
+	msleep_interruptible(1000);
+	wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
+
+	/* Free Tx and Rx packets */
+	kfree_skb(priv->currenttxskb);
+	priv->currenttxskb = NULL;
+
+	/* report disconnect to upper layer */
+	netif_stop_queue(priv->dev);
+	netif_carrier_off(priv->dev);
+
+	/* reset SNR/NF/RSSI values */
+	memset(priv->SNR, 0x00, sizeof(priv->SNR));
+	memset(priv->NF, 0x00, sizeof(priv->NF));
+	memset(priv->RSSI, 0x00, sizeof(priv->RSSI));
+	memset(priv->rawSNR, 0x00, sizeof(priv->rawSNR));
+	memset(priv->rawNF, 0x00, sizeof(priv->rawNF));
+	priv->nextSNRNF = 0;
+	priv->numSNRNF = 0;
+	priv->connect_status = LBS_DISCONNECTED;
+
+	/* Clear out associated SSID and BSSID since connection is
+	 * no longer valid.
+	 */
+	memset(&priv->curbssparams.bssid, 0, ETH_ALEN);
+	memset(&priv->curbssparams.ssid, 0, IW_ESSID_MAX_SIZE);
+	priv->curbssparams.ssid_len = 0;
+
+	if (priv->psstate != PS_STATE_FULL_POWER) {
+		/* make firmware to exit PS mode */
+		lbs_deb_cmd("disconnected, so exit PS mode\n");
+		lbs_ps_wakeup(priv, 0);
+	}
+	lbs_deb_leave(LBS_DEB_CMD);
+}
+
+/**
+ *  @brief This function handles MIC failure event.
+ *
+ *  @param priv    A pointer to struct lbs_private structure
+ *  @para  event   the event id
+ *  @return 	   n/a
+ */
+static void handle_mic_failureevent(struct lbs_private *priv, u32 event)
+{
+	char buf[50];
+
+	lbs_deb_enter(LBS_DEB_CMD);
+	memset(buf, 0, sizeof(buf));
+
+	sprintf(buf, "%s", "MLME-MICHAELMICFAILURE.indication ");
+
+	if (event == MACREG_INT_CODE_MIC_ERR_UNICAST) {
+		strcat(buf, "unicast ");
+	} else {
+		strcat(buf, "multicast ");
+	}
+
+	lbs_send_iwevcustom_event(priv, buf);
+	lbs_deb_leave(LBS_DEB_CMD);
+}
+
+static int lbs_ret_reg_access(struct lbs_private *priv,
+			       u16 type, struct cmd_ds_command *resp)
+{
+	int ret = 0;
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	switch (type) {
+	case CMD_RET(CMD_MAC_REG_ACCESS):
+		{
+			struct cmd_ds_mac_reg_access *reg = &resp->params.macreg;
+
+			priv->offsetvalue.offset = (u32)le16_to_cpu(reg->offset);
+			priv->offsetvalue.value = le32_to_cpu(reg->value);
+			break;
+		}
+
+	case CMD_RET(CMD_BBP_REG_ACCESS):
+		{
+			struct cmd_ds_bbp_reg_access *reg = &resp->params.bbpreg;
+
+			priv->offsetvalue.offset = (u32)le16_to_cpu(reg->offset);
+			priv->offsetvalue.value = reg->value;
+			break;
+		}
+
+	case CMD_RET(CMD_RF_REG_ACCESS):
+		{
+			struct cmd_ds_rf_reg_access *reg = &resp->params.rfreg;
+
+			priv->offsetvalue.offset = (u32)le16_to_cpu(reg->offset);
+			priv->offsetvalue.value = reg->value;
+			break;
+		}
+
+	default:
+		ret = -1;
+	}
+
+	lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
+	return ret;
+}
+
+static int lbs_ret_802_11_sleep_params(struct lbs_private *priv,
+					struct cmd_ds_command *resp)
+{
+	struct cmd_ds_802_11_sleep_params *sp = &resp->params.sleep_params;
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	lbs_deb_cmd("error 0x%x, offset 0x%x, stabletime 0x%x, calcontrol 0x%x "
+		    "extsleepclk 0x%x\n", le16_to_cpu(sp->error),
+		    le16_to_cpu(sp->offset), le16_to_cpu(sp->stabletime),
+		    sp->calcontrol, sp->externalsleepclk);
+
+	priv->sp.sp_error = le16_to_cpu(sp->error);
+	priv->sp.sp_offset = le16_to_cpu(sp->offset);
+	priv->sp.sp_stabletime = le16_to_cpu(sp->stabletime);
+	priv->sp.sp_calcontrol = sp->calcontrol;
+	priv->sp.sp_extsleepclk = sp->externalsleepclk;
+	priv->sp.sp_reserved = le16_to_cpu(sp->reserved);
+
+	lbs_deb_enter(LBS_DEB_CMD);
+	return 0;
+}
+
+static int lbs_ret_802_11_stat(struct lbs_private *priv,
+				struct cmd_ds_command *resp)
+{
+	lbs_deb_enter(LBS_DEB_CMD);
+/*	currently priv->wlan802_11Stat is unused
+
+	struct cmd_ds_802_11_get_stat *p11Stat = &resp->params.gstat;
+
+	// TODO Convert it to Big endian befor copy
+	memcpy(&priv->wlan802_11Stat,
+	       p11Stat, sizeof(struct cmd_ds_802_11_get_stat));
+*/
+	lbs_deb_leave(LBS_DEB_CMD);
+	return 0;
+}
+
+static int lbs_ret_802_11_snmp_mib(struct lbs_private *priv,
+				    struct cmd_ds_command *resp)
+{
+	struct cmd_ds_802_11_snmp_mib *smib = &resp->params.smib;
+	u16 oid = le16_to_cpu(smib->oid);
+	u16 querytype = le16_to_cpu(smib->querytype);
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	lbs_deb_cmd("SNMP_RESP: oid 0x%x, querytype 0x%x\n", oid,
+	       querytype);
+	lbs_deb_cmd("SNMP_RESP: Buf size %d\n", le16_to_cpu(smib->bufsize));
+
+	if (querytype == CMD_ACT_GET) {
+		switch (oid) {
+		case FRAGTHRESH_I:
+			priv->fragthsd =
+				le16_to_cpu(*((__le16 *)(smib->value)));
+			lbs_deb_cmd("SNMP_RESP: frag threshold %u\n",
+				    priv->fragthsd);
+			break;
+		case RTSTHRESH_I:
+			priv->rtsthsd =
+				le16_to_cpu(*((__le16 *)(smib->value)));
+			lbs_deb_cmd("SNMP_RESP: rts threshold %u\n",
+				    priv->rtsthsd);
+			break;
+		case SHORT_RETRYLIM_I:
+			priv->txretrycount =
+				le16_to_cpu(*((__le16 *)(smib->value)));
+			lbs_deb_cmd("SNMP_RESP: tx retry count %u\n",
+				    priv->rtsthsd);
+			break;
+		default:
+			break;
+		}
+	}
+
+	lbs_deb_enter(LBS_DEB_CMD);
+	return 0;
+}
+
+static int lbs_ret_802_11_key_material(struct lbs_private *priv,
+					struct cmd_ds_command *resp)
+{
+	struct cmd_ds_802_11_key_material *pkeymaterial =
+	    &resp->params.keymaterial;
+	u16 action = le16_to_cpu(pkeymaterial->action);
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	/* Copy the returned key to driver private data */
+	if (action == CMD_ACT_GET) {
+		u8 * buf_ptr = (u8 *) &pkeymaterial->keyParamSet;
+		u8 * resp_end = (u8 *) (resp + le16_to_cpu(resp->size));
+
+		while (buf_ptr < resp_end) {
+			struct MrvlIEtype_keyParamSet * pkeyparamset =
+			    (struct MrvlIEtype_keyParamSet *) buf_ptr;
+			struct enc_key * pkey;
+			u16 param_set_len = le16_to_cpu(pkeyparamset->length);
+			u16 key_len = le16_to_cpu(pkeyparamset->keylen);
+			u16 key_flags = le16_to_cpu(pkeyparamset->keyinfo);
+			u16 key_type = le16_to_cpu(pkeyparamset->keytypeid);
+			u8 * end;
+
+			end = (u8 *) pkeyparamset + sizeof (pkeyparamset->type)
+			                          + sizeof (pkeyparamset->length)
+			                          + param_set_len;
+			/* Make sure we don't access past the end of the IEs */
+			if (end > resp_end)
+				break;
+
+			if (key_flags & KEY_INFO_WPA_UNICAST)
+				pkey = &priv->wpa_unicast_key;
+			else if (key_flags & KEY_INFO_WPA_MCAST)
+				pkey = &priv->wpa_mcast_key;
+			else
+				break;
+
+			/* Copy returned key into driver */
+			memset(pkey, 0, sizeof(struct enc_key));
+			if (key_len > sizeof(pkey->key))
+				break;
+			pkey->type = key_type;
+			pkey->flags = key_flags;
+			pkey->len = key_len;
+			memcpy(pkey->key, pkeyparamset->key, pkey->len);
+
+			buf_ptr = end + 1;
+		}
+	}
+
+	lbs_deb_enter(LBS_DEB_CMD);
+	return 0;
+}
+
+static int lbs_ret_802_11_mac_address(struct lbs_private *priv,
+				       struct cmd_ds_command *resp)
+{
+	struct cmd_ds_802_11_mac_address *macadd = &resp->params.macadd;
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	memcpy(priv->current_addr, macadd->macadd, ETH_ALEN);
+
+	lbs_deb_enter(LBS_DEB_CMD);
+	return 0;
+}
+
+static int lbs_ret_802_11_rf_tx_power(struct lbs_private *priv,
+				       struct cmd_ds_command *resp)
+{
+	struct cmd_ds_802_11_rf_tx_power *rtp = &resp->params.txp;
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	priv->txpowerlevel = le16_to_cpu(rtp->currentlevel);
+
+	lbs_deb_cmd("TX power currently %d\n", priv->txpowerlevel);
+
+	lbs_deb_leave(LBS_DEB_CMD);
+	return 0;
+}
+
+static int lbs_ret_802_11_rate_adapt_rateset(struct lbs_private *priv,
+					      struct cmd_ds_command *resp)
+{
+	struct cmd_ds_802_11_rate_adapt_rateset *rates = &resp->params.rateset;
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	if (rates->action == CMD_ACT_GET) {
+		priv->enablehwauto = le16_to_cpu(rates->enablehwauto);
+		priv->ratebitmap = le16_to_cpu(rates->bitmap);
+	}
+
+	lbs_deb_leave(LBS_DEB_CMD);
+	return 0;
+}
+
+static int lbs_ret_802_11_rssi(struct lbs_private *priv,
+				struct cmd_ds_command *resp)
+{
+	struct cmd_ds_802_11_rssi_rsp *rssirsp = &resp->params.rssirsp;
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	/* store the non average value */
+	priv->SNR[TYPE_BEACON][TYPE_NOAVG] = le16_to_cpu(rssirsp->SNR);
+	priv->NF[TYPE_BEACON][TYPE_NOAVG] = le16_to_cpu(rssirsp->noisefloor);
+
+	priv->SNR[TYPE_BEACON][TYPE_AVG] = le16_to_cpu(rssirsp->avgSNR);
+	priv->NF[TYPE_BEACON][TYPE_AVG] = le16_to_cpu(rssirsp->avgnoisefloor);
+
+	priv->RSSI[TYPE_BEACON][TYPE_NOAVG] =
+	    CAL_RSSI(priv->SNR[TYPE_BEACON][TYPE_NOAVG],
+		     priv->NF[TYPE_BEACON][TYPE_NOAVG]);
+
+	priv->RSSI[TYPE_BEACON][TYPE_AVG] =
+	    CAL_RSSI(priv->SNR[TYPE_BEACON][TYPE_AVG] / AVG_SCALE,
+		     priv->NF[TYPE_BEACON][TYPE_AVG] / AVG_SCALE);
+
+	lbs_deb_cmd("RSSI: beacon %d, avg %d\n",
+	       priv->RSSI[TYPE_BEACON][TYPE_NOAVG],
+	       priv->RSSI[TYPE_BEACON][TYPE_AVG]);
+
+	lbs_deb_leave(LBS_DEB_CMD);
+	return 0;
+}
+
+static int lbs_ret_802_11_eeprom_access(struct lbs_private *priv,
+				  struct cmd_ds_command *resp)
+{
+	struct lbs_ioctl_regrdwr *pbuf;
+	pbuf = (struct lbs_ioctl_regrdwr *) priv->prdeeprom;
+
+	lbs_deb_enter_args(LBS_DEB_CMD, "len %d",
+	       le16_to_cpu(resp->params.rdeeprom.bytecount));
+	if (pbuf->NOB < le16_to_cpu(resp->params.rdeeprom.bytecount)) {
+		pbuf->NOB = 0;
+		lbs_deb_cmd("EEPROM read length too big\n");
+		return -1;
+	}
+	pbuf->NOB = le16_to_cpu(resp->params.rdeeprom.bytecount);
+	if (pbuf->NOB > 0) {
+
+		memcpy(&pbuf->value, (u8 *) & resp->params.rdeeprom.value,
+		       le16_to_cpu(resp->params.rdeeprom.bytecount));
+		lbs_deb_hex(LBS_DEB_CMD, "EEPROM", (char *)&pbuf->value,
+			le16_to_cpu(resp->params.rdeeprom.bytecount));
+	}
+	lbs_deb_leave(LBS_DEB_CMD);
+	return 0;
+}
+
+static int lbs_ret_get_log(struct lbs_private *priv,
+			    struct cmd_ds_command *resp)
+{
+	struct cmd_ds_802_11_get_log *logmessage = &resp->params.glog;
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	/* Stored little-endian */
+	memcpy(&priv->logmsg, logmessage, sizeof(struct cmd_ds_802_11_get_log));
+
+	lbs_deb_leave(LBS_DEB_CMD);
+	return 0;
+}
+
+static int lbs_ret_802_11_enable_rsn(struct lbs_private *priv,
+                                          struct cmd_ds_command *resp)
+{
+	struct cmd_ds_802_11_enable_rsn *enable_rsn = &resp->params.enbrsn;
+	u32 * pdata_buf = priv->cur_cmd->pdata_buf;
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	if (enable_rsn->action == cpu_to_le16(CMD_ACT_GET)) {
+		if (pdata_buf)
+			*pdata_buf = (u32) le16_to_cpu(enable_rsn->enable);
+	}
+
+	lbs_deb_leave(LBS_DEB_CMD);
+	return 0;
+}
+
+static int lbs_ret_802_11_bcn_ctrl(struct lbs_private * priv,
+					struct cmd_ds_command *resp)
+{
+	struct cmd_ds_802_11_beacon_control *bcn_ctrl =
+	    &resp->params.bcn_ctrl;
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	if (bcn_ctrl->action == CMD_ACT_GET) {
+		priv->beacon_enable = (u8) le16_to_cpu(bcn_ctrl->beacon_enable);
+		priv->beacon_period = le16_to_cpu(bcn_ctrl->beacon_period);
+	}
+
+	lbs_deb_enter(LBS_DEB_CMD);
+	return 0;
+}
+
+static int lbs_ret_802_11_subscribe_event(struct lbs_private *priv,
+	struct cmd_ds_command *resp)
+{
+	struct cmd_ds_802_11_subscribe_event *cmd_event =
+		&resp->params.subscribe_event;
+	struct cmd_ds_802_11_subscribe_event *dst_event =
+		priv->cur_cmd->pdata_buf;
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	if (dst_event->action == cpu_to_le16(CMD_ACT_GET)) {
+		dst_event->events = cmd_event->events;
+		memcpy(dst_event->tlv, cmd_event->tlv, sizeof(dst_event->tlv));
+	}
+
+	lbs_deb_leave(LBS_DEB_CMD);
+	return 0;
+}
+
+static inline int handle_cmd_response(struct lbs_private *priv,
+				      unsigned long dummy,
+				      struct cmd_header *cmd_response)
+{
+	struct cmd_ds_command *resp = (struct cmd_ds_command *) cmd_response;
+	int ret = 0;
+	unsigned long flags;
+	uint16_t respcmd = le16_to_cpu(resp->command);
+
+	lbs_deb_enter(LBS_DEB_HOST);
+
+	switch (respcmd) {
+	case CMD_RET(CMD_MAC_REG_ACCESS):
+	case CMD_RET(CMD_BBP_REG_ACCESS):
+	case CMD_RET(CMD_RF_REG_ACCESS):
+		ret = lbs_ret_reg_access(priv, respcmd, resp);
+		break;
+
+	case CMD_RET(CMD_802_11_SCAN):
+		ret = lbs_ret_80211_scan(priv, resp);
+		break;
+
+	case CMD_RET(CMD_802_11_GET_LOG):
+		ret = lbs_ret_get_log(priv, resp);
+		break;
+
+	case CMD_RET_802_11_ASSOCIATE:
+	case CMD_RET(CMD_802_11_ASSOCIATE):
+	case CMD_RET(CMD_802_11_REASSOCIATE):
+		ret = lbs_ret_80211_associate(priv, resp);
+		break;
+
+	case CMD_RET(CMD_802_11_DISASSOCIATE):
+	case CMD_RET(CMD_802_11_DEAUTHENTICATE):
+		ret = lbs_ret_80211_disassociate(priv, resp);
+		break;
+
+	case CMD_RET(CMD_802_11_AD_HOC_START):
+	case CMD_RET(CMD_802_11_AD_HOC_JOIN):
+		ret = lbs_ret_80211_ad_hoc_start(priv, resp);
+		break;
+
+	case CMD_RET(CMD_802_11_GET_STAT):
+		ret = lbs_ret_802_11_stat(priv, resp);
+		break;
+
+	case CMD_RET(CMD_802_11_SNMP_MIB):
+		ret = lbs_ret_802_11_snmp_mib(priv, resp);
+		break;
+
+	case CMD_RET(CMD_802_11_RF_TX_POWER):
+		ret = lbs_ret_802_11_rf_tx_power(priv, resp);
+		break;
+
+	case CMD_RET(CMD_802_11_SET_AFC):
+	case CMD_RET(CMD_802_11_GET_AFC):
+		spin_lock_irqsave(&priv->driver_lock, flags);
+		memmove(priv->cur_cmd->pdata_buf, &resp->params.afc,
+			sizeof(struct cmd_ds_802_11_afc));
+		spin_unlock_irqrestore(&priv->driver_lock, flags);
+
+		break;
+
+	case CMD_RET(CMD_MAC_MULTICAST_ADR):
+	case CMD_RET(CMD_MAC_CONTROL):
+	case CMD_RET(CMD_802_11_SET_WEP):
+	case CMD_RET(CMD_802_11_RESET):
+	case CMD_RET(CMD_802_11_AUTHENTICATE):
+	case CMD_RET(CMD_802_11_RADIO_CONTROL):
+	case CMD_RET(CMD_802_11_BEACON_STOP):
+		break;
+
+	case CMD_RET(CMD_802_11_ENABLE_RSN):
+		ret = lbs_ret_802_11_enable_rsn(priv, resp);
+		break;
+
+	case CMD_RET(CMD_802_11_RATE_ADAPT_RATESET):
+		ret = lbs_ret_802_11_rate_adapt_rateset(priv, resp);
+		break;
+
+	case CMD_RET(CMD_802_11_RSSI):
+		ret = lbs_ret_802_11_rssi(priv, resp);
+		break;
+
+	case CMD_RET(CMD_802_11_MAC_ADDRESS):
+		ret = lbs_ret_802_11_mac_address(priv, resp);
+		break;
+
+	case CMD_RET(CMD_802_11_AD_HOC_STOP):
+		ret = lbs_ret_80211_ad_hoc_stop(priv, resp);
+		break;
+
+	case CMD_RET(CMD_802_11_KEY_MATERIAL):
+		ret = lbs_ret_802_11_key_material(priv, resp);
+		break;
+
+	case CMD_RET(CMD_802_11_EEPROM_ACCESS):
+		ret = lbs_ret_802_11_eeprom_access(priv, resp);
+		break;
+
+	case CMD_RET(CMD_802_11D_DOMAIN_INFO):
+		ret = lbs_ret_802_11d_domain_info(priv, resp);
+		break;
+
+	case CMD_RET(CMD_802_11_SLEEP_PARAMS):
+		ret = lbs_ret_802_11_sleep_params(priv, resp);
+		break;
+	case CMD_RET(CMD_802_11_INACTIVITY_TIMEOUT):
+		spin_lock_irqsave(&priv->driver_lock, flags);
+		*((u16 *) priv->cur_cmd->pdata_buf) =
+		    le16_to_cpu(resp->params.inactivity_timeout.timeout);
+		spin_unlock_irqrestore(&priv->driver_lock, flags);
+		break;
+
+	case CMD_RET(CMD_802_11_TPC_CFG):
+		spin_lock_irqsave(&priv->driver_lock, flags);
+		memmove(priv->cur_cmd->pdata_buf, &resp->params.tpccfg,
+			sizeof(struct cmd_ds_802_11_tpc_cfg));
+		spin_unlock_irqrestore(&priv->driver_lock, flags);
+		break;
+	case CMD_RET(CMD_802_11_LED_GPIO_CTRL):
+		spin_lock_irqsave(&priv->driver_lock, flags);
+		memmove(priv->cur_cmd->pdata_buf, &resp->params.ledgpio,
+			sizeof(struct cmd_ds_802_11_led_ctrl));
+		spin_unlock_irqrestore(&priv->driver_lock, flags);
+		break;
+	case CMD_RET(CMD_802_11_SUBSCRIBE_EVENT):
+		ret = lbs_ret_802_11_subscribe_event(priv, resp);
+		break;
+
+	case CMD_RET(CMD_802_11_PWR_CFG):
+		spin_lock_irqsave(&priv->driver_lock, flags);
+		memmove(priv->cur_cmd->pdata_buf, &resp->params.pwrcfg,
+			sizeof(struct cmd_ds_802_11_pwr_cfg));
+		spin_unlock_irqrestore(&priv->driver_lock, flags);
+
+		break;
+
+	case CMD_RET(CMD_GET_TSF):
+		spin_lock_irqsave(&priv->driver_lock, flags);
+		memcpy(priv->cur_cmd->pdata_buf,
+		       &resp->params.gettsf.tsfvalue, sizeof(u64));
+		spin_unlock_irqrestore(&priv->driver_lock, flags);
+		break;
+	case CMD_RET(CMD_BT_ACCESS):
+		spin_lock_irqsave(&priv->driver_lock, flags);
+		if (priv->cur_cmd->pdata_buf)
+			memcpy(priv->cur_cmd->pdata_buf,
+			       &resp->params.bt.addr1, 2 * ETH_ALEN);
+		spin_unlock_irqrestore(&priv->driver_lock, flags);
+		break;
+	case CMD_RET(CMD_FWT_ACCESS):
+		spin_lock_irqsave(&priv->driver_lock, flags);
+		if (priv->cur_cmd->pdata_buf)
+			memcpy(priv->cur_cmd->pdata_buf, &resp->params.fwt,
+			       sizeof(resp->params.fwt));
+		spin_unlock_irqrestore(&priv->driver_lock, flags);
+		break;
+	case CMD_RET(CMD_802_11_BEACON_CTRL):
+		ret = lbs_ret_802_11_bcn_ctrl(priv, resp);
+		break;
+
+	default:
+		lbs_deb_host("CMD_RESP: unknown cmd response 0x%04x\n",
+			    resp->command);
+		break;
+	}
+	lbs_deb_leave(LBS_DEB_HOST);
+	return ret;
+}
+
+int lbs_process_rx_command(struct lbs_private *priv)
+{
+	u16 respcmd;
+	struct cmd_header *resp;
+	int ret = 0;
+	ulong flags;
+	u16 result;
+
+	lbs_deb_enter(LBS_DEB_HOST);
+
+	/* Now we got response from FW, cancel the command timer */
+	del_timer(&priv->command_timer);
+
+	mutex_lock(&priv->lock);
+	spin_lock_irqsave(&priv->driver_lock, flags);
+
+	if (!priv->cur_cmd) {
+		lbs_deb_host("CMD_RESP: cur_cmd is NULL\n");
+		ret = -1;
+		spin_unlock_irqrestore(&priv->driver_lock, flags);
+		goto done;
+	}
+	resp = priv->cur_cmd->cmdbuf;
+
+	respcmd = le16_to_cpu(resp->command);
+	result = le16_to_cpu(resp->result);
+
+	lbs_deb_host("CMD_RESP: response 0x%04x, size %d, jiffies %lu\n",
+		respcmd, priv->upld_len, jiffies);
+	lbs_deb_hex(LBS_DEB_HOST, "CMD_RESP", (void *) resp, priv->upld_len);
+
+	if (!(respcmd & 0x8000)) {
+		lbs_deb_host("invalid response!\n");
+		priv->cur_cmd_retcode = -1;
+		__lbs_cleanup_and_insert_cmd(priv, priv->cur_cmd);
+		priv->cur_cmd = NULL;
+		spin_unlock_irqrestore(&priv->driver_lock, flags);
+		ret = -1;
+		goto done;
+	}
+
+	/* Store the response code to cur_cmd_retcode. */
+	priv->cur_cmd_retcode = result;
+
+	if (respcmd == CMD_RET(CMD_802_11_PS_MODE)) {
+		struct cmd_ds_802_11_ps_mode *psmode = (void *) resp;
+		u16 action = le16_to_cpu(psmode->action);
+
+		lbs_deb_host(
+		       "CMD_RESP: PS_MODE cmd reply result 0x%x, action 0x%x\n",
+		       result, action);
+
+		if (result) {
+			lbs_deb_host("CMD_RESP: PS command failed with 0x%x\n",
+				    result);
+			/*
+			 * We should not re-try enter-ps command in
+			 * ad-hoc mode. It takes place in
+			 * lbs_execute_next_command().
+			 */
+			if (priv->mode == IW_MODE_ADHOC &&
+			    action == CMD_SUBCMD_ENTER_PS)
+				priv->psmode = LBS802_11POWERMODECAM;
+		} else if (action == CMD_SUBCMD_ENTER_PS) {
+			priv->needtowakeup = 0;
+			priv->psstate = PS_STATE_AWAKE;
+
+			lbs_deb_host("CMD_RESP: ENTER_PS command response\n");
+			if (priv->connect_status != LBS_CONNECTED) {
+				/*
+				 * When Deauth Event received before Enter_PS command
+				 * response, We need to wake up the firmware.
+				 */
+				lbs_deb_host(
+				       "disconnected, invoking lbs_ps_wakeup\n");
+
+				spin_unlock_irqrestore(&priv->driver_lock, flags);
+				mutex_unlock(&priv->lock);
+				lbs_ps_wakeup(priv, 0);
+				mutex_lock(&priv->lock);
+				spin_lock_irqsave(&priv->driver_lock, flags);
+			}
+		} else if (action == CMD_SUBCMD_EXIT_PS) {
+			priv->needtowakeup = 0;
+			priv->psstate = PS_STATE_FULL_POWER;
+			lbs_deb_host("CMD_RESP: EXIT_PS command response\n");
+		} else {
+			lbs_deb_host("CMD_RESP: PS action 0x%X\n", action);
+		}
+
+		__lbs_cleanup_and_insert_cmd(priv, priv->cur_cmd);
+		priv->cur_cmd = NULL;
+		spin_unlock_irqrestore(&priv->driver_lock, flags);
+
+		ret = 0;
+		goto done;
+	}
+
+	/* If the command is not successful, cleanup and return failure */
+	if ((result != 0 || !(respcmd & 0x8000))) {
+		lbs_deb_host("CMD_RESP: error 0x%04x in command reply 0x%04x\n",
+		       result, respcmd);
+		/*
+		 * Handling errors here
+		 */
+		switch (respcmd) {
+		case CMD_RET(CMD_GET_HW_SPEC):
+		case CMD_RET(CMD_802_11_RESET):
+			lbs_deb_host("CMD_RESP: reset failed\n");
+			break;
+
+		}
+
+		__lbs_cleanup_and_insert_cmd(priv, priv->cur_cmd);
+		priv->cur_cmd = NULL;
+		spin_unlock_irqrestore(&priv->driver_lock, flags);
+
+		ret = -1;
+		goto done;
+	}
+
+	spin_unlock_irqrestore(&priv->driver_lock, flags);
+
+	if (priv->cur_cmd && priv->cur_cmd->callback) {
+		ret = priv->cur_cmd->callback(priv, priv->cur_cmd->callback_arg,
+				resp);
+	} else
+		ret = handle_cmd_response(priv, 0, resp);
+
+	spin_lock_irqsave(&priv->driver_lock, flags);
+
+	if (priv->cur_cmd) {
+		/* Clean up and Put current command back to cmdfreeq */
+		__lbs_cleanup_and_insert_cmd(priv, priv->cur_cmd);
+		priv->cur_cmd = NULL;
+	}
+	spin_unlock_irqrestore(&priv->driver_lock, flags);
+
+done:
+	mutex_unlock(&priv->lock);
+	lbs_deb_leave_args(LBS_DEB_HOST, "ret %d", ret);
+	return ret;
+}
+
+int lbs_process_event(struct lbs_private *priv)
+{
+	int ret = 0;
+	u32 eventcause;
+
+	lbs_deb_enter(LBS_DEB_CMD);
+
+	spin_lock_irq(&priv->driver_lock);
+	eventcause = priv->eventcause >> SBI_EVENT_CAUSE_SHIFT;
+	spin_unlock_irq(&priv->driver_lock);
+
+	lbs_deb_cmd("event cause %d\n", eventcause);
+
+	switch (eventcause) {
+	case MACREG_INT_CODE_LINK_SENSED:
+		lbs_deb_cmd("EVENT: MACREG_INT_CODE_LINK_SENSED\n");
+		break;
+
+	case MACREG_INT_CODE_DEAUTHENTICATED:
+		lbs_deb_cmd("EVENT: deauthenticated\n");
+		lbs_mac_event_disconnected(priv);
+		break;
+
+	case MACREG_INT_CODE_DISASSOCIATED:
+		lbs_deb_cmd("EVENT: disassociated\n");
+		lbs_mac_event_disconnected(priv);
+		break;
+
+	case MACREG_INT_CODE_LINK_LOST_NO_SCAN:
+		lbs_deb_cmd("EVENT: link lost\n");
+		lbs_mac_event_disconnected(priv);
+		break;
+
+	case MACREG_INT_CODE_PS_SLEEP:
+		lbs_deb_cmd("EVENT: sleep\n");
+
+		/* handle unexpected PS SLEEP event */
+		if (priv->psstate == PS_STATE_FULL_POWER) {
+			lbs_deb_cmd(
+			       "EVENT: in FULL POWER mode, ignoreing PS_SLEEP\n");
+			break;
+		}
+		priv->psstate = PS_STATE_PRE_SLEEP;
+
+		lbs_ps_confirm_sleep(priv, (u16) priv->psmode);
+
+		break;
+
+	case MACREG_INT_CODE_PS_AWAKE:
+		lbs_deb_cmd("EVENT: awake\n");
+
+		/* handle unexpected PS AWAKE event */
+		if (priv->psstate == PS_STATE_FULL_POWER) {
+			lbs_deb_cmd(
+			       "EVENT: In FULL POWER mode - ignore PS AWAKE\n");
+			break;
+		}
+
+		priv->psstate = PS_STATE_AWAKE;
+
+		if (priv->needtowakeup) {
+			/*
+			 * wait for the command processing to finish
+			 * before resuming sending
+			 * priv->needtowakeup will be set to FALSE
+			 * in lbs_ps_wakeup()
+			 */
+			lbs_deb_cmd("waking up ...\n");
+			lbs_ps_wakeup(priv, 0);
+		}
+		break;
+
+	case MACREG_INT_CODE_MIC_ERR_UNICAST:
+		lbs_deb_cmd("EVENT: UNICAST MIC ERROR\n");
+		handle_mic_failureevent(priv, MACREG_INT_CODE_MIC_ERR_UNICAST);
+		break;
+
+	case MACREG_INT_CODE_MIC_ERR_MULTICAST:
+		lbs_deb_cmd("EVENT: MULTICAST MIC ERROR\n");
+		handle_mic_failureevent(priv, MACREG_INT_CODE_MIC_ERR_MULTICAST);
+		break;
+	case MACREG_INT_CODE_MIB_CHANGED:
+	case MACREG_INT_CODE_INIT_DONE:
+		break;
+
+	case MACREG_INT_CODE_ADHOC_BCN_LOST:
+		lbs_deb_cmd("EVENT: ADHOC beacon lost\n");
+		break;
+
+	case MACREG_INT_CODE_RSSI_LOW:
+		lbs_pr_alert("EVENT: rssi low\n");
+		break;
+	case MACREG_INT_CODE_SNR_LOW:
+		lbs_pr_alert("EVENT: snr low\n");
+		break;
+	case MACREG_INT_CODE_MAX_FAIL:
+		lbs_pr_alert("EVENT: max fail\n");
+		break;
+	case MACREG_INT_CODE_RSSI_HIGH:
+		lbs_pr_alert("EVENT: rssi high\n");
+		break;
+	case MACREG_INT_CODE_SNR_HIGH:
+		lbs_pr_alert("EVENT: snr high\n");
+		break;
+
+	case MACREG_INT_CODE_MESH_AUTO_STARTED:
+		/* Ignore spurious autostart events if autostart is disabled */
+		if (!priv->mesh_autostart_enabled) {
+			lbs_pr_info("EVENT: MESH_AUTO_STARTED (ignoring)\n");
+			break;
+		}
+		lbs_pr_info("EVENT: MESH_AUTO_STARTED\n");
+		priv->mesh_connect_status = LBS_CONNECTED;
+		if (priv->mesh_open == 1) {
+			netif_wake_queue(priv->mesh_dev);
+			netif_carrier_on(priv->mesh_dev);
+		}
+		priv->mode = IW_MODE_ADHOC;
+		schedule_work(&priv->sync_channel);
+		break;
+
+	default:
+		lbs_pr_alert("EVENT: unknown event id %d\n", eventcause);
+		break;
+	}
+
+	spin_lock_irq(&priv->driver_lock);
+	priv->eventcause = 0;
+	spin_unlock_irq(&priv->driver_lock);
+
+	lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
+	return ret;
+}

+ 17 - 0
package/libertas/src/compat.h

@@ -0,0 +1,17 @@
+#ifndef __COMPAT_H
+#define __COMPAT_H
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+#define MAC_FMT "%02x:%02x:%02x:%02x:%02x:%02x"
+#define DECLARE_MAC_BUF(var) char var[18] __maybe_unused
+
+static inline char *print_mac(char *buf, const u8 *addr)
+{
+	sprintf(buf, MAC_FMT,
+		addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+	return buf;
+}
+
+#endif

+ 1163 - 0
package/libertas/src/debugfs.c

@@ -0,0 +1,1163 @@
+#include <linux/module.h>
+#include <linux/dcache.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <net/iw_handler.h>
+
+#include "dev.h"
+#include "decl.h"
+#include "host.h"
+#include "debugfs.h"
+
+static struct dentry *lbs_dir;
+static char *szStates[] = {
+	"Connected",
+	"Disconnected"
+};
+
+#ifdef PROC_DEBUG
+static void lbs_debug_init(struct lbs_private *priv, struct net_device *dev);
+#endif
+
+static int open_file_generic(struct inode *inode, struct file *file)
+{
+	file->private_data = inode->i_private;
+	return 0;
+}
+
+static ssize_t write_file_dummy(struct file *file, const char __user *buf,
+                                size_t count, loff_t *ppos)
+{
+        return -EINVAL;
+}
+
+static const size_t len = PAGE_SIZE;
+
+static ssize_t lbs_dev_info(struct file *file, char __user *userbuf,
+				  size_t count, loff_t *ppos)
+{
+	struct lbs_private *priv = file->private_data;
+	size_t pos = 0;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+	ssize_t res;
+
+	pos += snprintf(buf+pos, len-pos, "state = %s\n",
+				szStates[priv->connect_status]);
+	pos += snprintf(buf+pos, len-pos, "region_code = %02x\n",
+				(u32) priv->regioncode);
+
+	res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+
+	free_page(addr);
+	return res;
+}
+
+
+static ssize_t lbs_getscantable(struct file *file, char __user *userbuf,
+				  size_t count, loff_t *ppos)
+{
+	struct lbs_private *priv = file->private_data;
+	size_t pos = 0;
+	int numscansdone = 0, res;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+	DECLARE_MAC_BUF(mac);
+	struct bss_descriptor * iter_bss;
+
+	pos += snprintf(buf+pos, len-pos,
+		"# | ch  | rssi |       bssid       |   cap    | Qual | SSID \n");
+
+	mutex_lock(&priv->lock);
+	list_for_each_entry (iter_bss, &priv->network_list, list) {
+		u16 ibss = (iter_bss->capability & WLAN_CAPABILITY_IBSS);
+		u16 privacy = (iter_bss->capability & WLAN_CAPABILITY_PRIVACY);
+		u16 spectrum_mgmt = (iter_bss->capability & WLAN_CAPABILITY_SPECTRUM_MGMT);
+
+		pos += snprintf(buf+pos, len-pos,
+			"%02u| %03d | %04ld | %s |",
+			numscansdone, iter_bss->channel, iter_bss->rssi,
+			print_mac(mac, iter_bss->bssid));
+		pos += snprintf(buf+pos, len-pos, " %04x-", iter_bss->capability);
+		pos += snprintf(buf+pos, len-pos, "%c%c%c |",
+				ibss ? 'A' : 'I', privacy ? 'P' : ' ',
+				spectrum_mgmt ? 'S' : ' ');
+		pos += snprintf(buf+pos, len-pos, " %04d |", SCAN_RSSI(iter_bss->rssi));
+		pos += snprintf(buf+pos, len-pos, " %s\n",
+		                escape_essid(iter_bss->ssid, iter_bss->ssid_len));
+
+		numscansdone++;
+	}
+	mutex_unlock(&priv->lock);
+
+	res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+
+	free_page(addr);
+	return res;
+}
+
+static ssize_t lbs_sleepparams_write(struct file *file,
+				const char __user *user_buf, size_t count,
+				loff_t *ppos)
+{
+	struct lbs_private *priv = file->private_data;
+	ssize_t buf_size, res;
+	int p1, p2, p3, p4, p5, p6;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	buf_size = min(count, len - 1);
+	if (copy_from_user(buf, user_buf, buf_size)) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+	res = sscanf(buf, "%d %d %d %d %d %d", &p1, &p2, &p3, &p4, &p5, &p6);
+	if (res != 6) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+	priv->sp.sp_error = p1;
+	priv->sp.sp_offset = p2;
+	priv->sp.sp_stabletime = p3;
+	priv->sp.sp_calcontrol = p4;
+	priv->sp.sp_extsleepclk = p5;
+	priv->sp.sp_reserved = p6;
+
+	res = lbs_prepare_and_send_command(priv,
+				CMD_802_11_SLEEP_PARAMS,
+				CMD_ACT_SET,
+				CMD_OPTION_WAITFORRSP, 0, NULL);
+
+	if (!res)
+		res = count;
+	else
+		res = -EINVAL;
+
+out_unlock:
+	free_page(addr);
+	return res;
+}
+
+static ssize_t lbs_sleepparams_read(struct file *file, char __user *userbuf,
+				  size_t count, loff_t *ppos)
+{
+	struct lbs_private *priv = file->private_data;
+	ssize_t res;
+	size_t pos = 0;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	res = lbs_prepare_and_send_command(priv,
+				CMD_802_11_SLEEP_PARAMS,
+				CMD_ACT_GET,
+				CMD_OPTION_WAITFORRSP, 0, NULL);
+	if (res) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+
+	pos += snprintf(buf, len, "%d %d %d %d %d %d\n", priv->sp.sp_error,
+			priv->sp.sp_offset, priv->sp.sp_stabletime,
+			priv->sp.sp_calcontrol, priv->sp.sp_extsleepclk,
+			priv->sp.sp_reserved);
+
+	res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+
+out_unlock:
+	free_page(addr);
+	return res;
+}
+
+static ssize_t lbs_extscan(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;
+	union iwreq_data wrqu;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	buf_size = min(count, len - 1);
+	if (copy_from_user(buf, userbuf, buf_size)) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+
+	lbs_send_specific_ssid_scan(priv, buf, strlen(buf)-1, 0);
+
+	memset(&wrqu, 0, sizeof(union iwreq_data));
+	wireless_send_event(priv->dev, SIOCGIWSCAN, &wrqu, NULL);
+
+out_unlock:
+	free_page(addr);
+	return count;
+}
+
+static void lbs_parse_bssid(char *buf, size_t count,
+	struct lbs_ioctl_user_scan_cfg *scan_cfg)
+{
+	char *hold;
+	unsigned int mac[ETH_ALEN];
+
+	hold = strstr(buf, "bssid=");
+	if (!hold)
+		return;
+	hold += 6;
+	sscanf(hold, MAC_FMT, mac, mac+1, mac+2, mac+3, mac+4, mac+5);
+	memcpy(scan_cfg->bssid, mac, ETH_ALEN);
+}
+
+static void lbs_parse_ssid(char *buf, size_t count,
+	struct lbs_ioctl_user_scan_cfg *scan_cfg)
+{
+	char *hold, *end;
+	ssize_t size;
+
+	hold = strstr(buf, "ssid=");
+	if (!hold)
+		return;
+	hold += 5;
+	end = strchr(hold, ' ');
+	if (!end)
+		end = buf + count - 1;
+
+	size = min((size_t)IW_ESSID_MAX_SIZE, (size_t) (end - hold));
+	strncpy(scan_cfg->ssid, hold, size);
+
+	return;
+}
+
+static int lbs_parse_clear(char *buf, size_t count, const char *tag)
+{
+	char *hold;
+	int val;
+
+	hold = strstr(buf, tag);
+	if (!hold)
+		return 0;
+	hold += strlen(tag);
+	sscanf(hold, "%d", &val);
+
+	if (val != 0)
+		val = 1;
+
+	return val;
+}
+
+static int lbs_parse_dur(char *buf, size_t count,
+	struct lbs_ioctl_user_scan_cfg *scan_cfg)
+{
+	char *hold;
+	int val;
+
+	hold = strstr(buf, "dur=");
+	if (!hold)
+		return 0;
+	hold += 4;
+	sscanf(hold, "%d", &val);
+
+	return val;
+}
+
+static void lbs_parse_type(char *buf, size_t count,
+	struct lbs_ioctl_user_scan_cfg *scan_cfg)
+{
+	char *hold;
+	int val;
+
+	hold = strstr(buf, "type=");
+	if (!hold)
+		return;
+	hold += 5;
+	sscanf(hold, "%d", &val);
+
+	/* type=1,2 or 3 */
+	if (val < 1 || val > 3)
+		return;
+
+	scan_cfg->bsstype = val;
+
+	return;
+}
+
+static ssize_t lbs_setuserscan(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;
+	struct lbs_ioctl_user_scan_cfg *scan_cfg;
+	union iwreq_data wrqu;
+	int dur;
+	char *buf = (char *)get_zeroed_page(GFP_KERNEL);
+
+	if (!buf)
+		return -ENOMEM;
+		
+	buf_size = min(count, len - 1);
+	if (copy_from_user(buf, userbuf, buf_size)) {
+		res = -EFAULT;
+		goto out_buf;
+	}
+
+	scan_cfg = kzalloc(sizeof(struct lbs_ioctl_user_scan_cfg), GFP_KERNEL);
+	if (!scan_cfg) {
+		res = -ENOMEM;
+		goto out_buf;
+	}
+	res = count;
+
+	scan_cfg->bsstype = LBS_SCAN_BSS_TYPE_ANY;
+
+	dur = lbs_parse_dur(buf, count, scan_cfg);
+	lbs_parse_bssid(buf, count, scan_cfg);
+	scan_cfg->clear_bssid = lbs_parse_clear(buf, count, "clear_bssid=");
+	lbs_parse_ssid(buf, count, scan_cfg);
+	scan_cfg->clear_ssid = lbs_parse_clear(buf, count, "clear_ssid=");
+	lbs_parse_type(buf, count, scan_cfg);
+
+	lbs_scan_networks(priv, scan_cfg, 1);
+	wait_event_interruptible(priv->cmd_pending,
+				 priv->surpriseremoved || !priv->last_scanned_channel);
+
+	if (priv->surpriseremoved)
+		goto out_scan_cfg;
+
+	memset(&wrqu, 0x00, sizeof(union iwreq_data));
+	wireless_send_event(priv->dev, SIOCGIWSCAN, &wrqu, NULL);
+
+ out_scan_cfg:
+	kfree(scan_cfg);
+ out_buf:
+	free_page((unsigned long)buf);
+	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
+ * firmware. Here's an example:
+ *	04 01 02 00 00 00 05 01 02 00 00 00 06 01 02 00
+ *	00 00 07 01 02 00 3c 00 00 00 00 00 00 00 03 03
+ *	00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
+ *
+ * The 04 01 is the TLV type (here TLV_TYPE_RSSI_LOW), 02 00 is the length,
+ * 00 00 are the data bytes of this TLV. For this TLV, their meaning is
+ * defined in mrvlietypes_thresholds
+ *
+ * This function searches in this TLV data chunk for a given TLV type
+ * and returns a pointer to the first data byte of the TLV, or to NULL
+ * if the TLV hasn't been found.
+ */
+static void *lbs_tlv_find(u16 tlv_type, const u8 *tlv, u16 size)
+{
+	__le16 le_type = cpu_to_le16(tlv_type);
+	ssize_t pos = 0;
+	struct mrvlietypesheader *tlv_h;
+	while (pos < size) {
+		u16 length;
+		tlv_h = (struct mrvlietypesheader *) tlv;
+		if (tlv_h->type == le_type)
+			return tlv_h;
+		if (tlv_h->len == 0)
+			return NULL;
+		length = le16_to_cpu(tlv_h->len) +
+			sizeof(struct mrvlietypesheader);
+		pos += length;
+		tlv += length;
+	}
+	return NULL;
+}
+
+
+/*
+ * This just gets the bitmap of currently subscribed events. Used when
+ * adding an additonal event subscription.
+ */
+static u16 lbs_get_events_bitmap(struct lbs_private *priv)
+{
+	ssize_t res;
+
+	struct cmd_ds_802_11_subscribe_event *events = kzalloc(
+		sizeof(struct cmd_ds_802_11_subscribe_event),
+		GFP_KERNEL);
+
+	res = lbs_prepare_and_send_command(priv,
+			CMD_802_11_SUBSCRIBE_EVENT, CMD_ACT_GET,
+			CMD_OPTION_WAITFORRSP, 0, events);
+
+	if (res) {
+		kfree(events);
+		return 0;
+	}
+	return le16_to_cpu(events->events);
+}
+
+
+static ssize_t lbs_threshold_read(
+	u16 tlv_type, u16 event_mask,
+	struct file *file, char __user *userbuf,
+	size_t count, loff_t *ppos)
+{
+	struct lbs_private *priv = file->private_data;
+	ssize_t res = 0;
+	size_t pos = 0;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+	u8 value;
+	u8 freq;
+	int events = 0;
+
+	struct cmd_ds_802_11_subscribe_event *subscribed = kzalloc(
+		sizeof(struct cmd_ds_802_11_subscribe_event),
+		GFP_KERNEL);
+	struct mrvlietypes_thresholds *got;
+
+	res = lbs_prepare_and_send_command(priv,
+			CMD_802_11_SUBSCRIBE_EVENT, CMD_ACT_GET,
+			CMD_OPTION_WAITFORRSP, 0, subscribed);
+	if (res) {
+		kfree(subscribed);
+		return res;
+	}
+
+	got = lbs_tlv_find(tlv_type, subscribed->tlv, sizeof(subscribed->tlv));
+	if (got) {
+		value = got->value;
+		freq  = got->freq;
+		events = le16_to_cpu(subscribed->events);
+	}
+	kfree(subscribed);
+
+	if (got)
+		pos += snprintf(buf, len, "%d %d %d\n", value, freq,
+			!!(events & event_mask));
+
+	res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+
+	free_page(addr);
+	return res;
+}
+
+
+static ssize_t lbs_threshold_write(
+	u16 tlv_type, u16 event_mask,
+	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 value, freq, curr_mask, new_mask;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+	struct cmd_ds_802_11_subscribe_event *events;
+
+	buf_size = min(count, len - 1);
+	if (copy_from_user(buf, userbuf, buf_size)) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+	res = sscanf(buf, "%d %d %d", &value, &freq, &new_mask);
+	if (res != 3) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+	curr_mask = lbs_get_events_bitmap(priv);
+
+	if (new_mask)
+		new_mask = curr_mask | event_mask;
+	else
+		new_mask = curr_mask & ~event_mask;
+
+	/* Now everything is set and we can send stuff down to the firmware */
+	events = kzalloc(
+		sizeof(struct cmd_ds_802_11_subscribe_event),
+		GFP_KERNEL);
+	if (events) {
+		struct mrvlietypes_thresholds *tlv =
+			(struct mrvlietypes_thresholds *) events->tlv;
+		events->action = cpu_to_le16(CMD_ACT_SET);
+		events->events = cpu_to_le16(new_mask);
+		tlv->header.type = cpu_to_le16(tlv_type);
+		tlv->header.len = cpu_to_le16(
+			sizeof(struct mrvlietypes_thresholds) -
+			sizeof(struct mrvlietypesheader));
+		tlv->value = value;
+		if (tlv_type != TLV_TYPE_BCNMISS)
+			tlv->freq = freq;
+		lbs_prepare_and_send_command(priv,
+			CMD_802_11_SUBSCRIBE_EVENT, CMD_ACT_SET,
+			CMD_OPTION_WAITFORRSP, 0, events);
+		kfree(events);
+	}
+
+	res = count;
+out_unlock:
+	free_page(addr);
+	return res;
+}
+
+
+static ssize_t lbs_lowrssi_read(
+	struct file *file, char __user *userbuf,
+	size_t count, loff_t *ppos)
+{
+	return lbs_threshold_read(TLV_TYPE_RSSI_LOW, CMD_SUBSCRIBE_RSSI_LOW,
+		file, userbuf, count, ppos);
+}
+
+
+static ssize_t lbs_lowrssi_write(
+	struct file *file, const char __user *userbuf,
+	size_t count, loff_t *ppos)
+{
+	return lbs_threshold_write(TLV_TYPE_RSSI_LOW, CMD_SUBSCRIBE_RSSI_LOW,
+		file, userbuf, count, ppos);
+}
+
+
+static ssize_t lbs_lowsnr_read(
+	struct file *file, char __user *userbuf,
+	size_t count, loff_t *ppos)
+{
+	return lbs_threshold_read(TLV_TYPE_SNR_LOW, CMD_SUBSCRIBE_SNR_LOW,
+		file, userbuf, count, ppos);
+}
+
+
+static ssize_t lbs_lowsnr_write(
+	struct file *file, const char __user *userbuf,
+	size_t count, loff_t *ppos)
+{
+	return lbs_threshold_write(TLV_TYPE_SNR_LOW, CMD_SUBSCRIBE_SNR_LOW,
+		file, userbuf, count, ppos);
+}
+
+
+static ssize_t lbs_failcount_read(
+	struct file *file, char __user *userbuf,
+	size_t count, loff_t *ppos)
+{
+	return lbs_threshold_read(TLV_TYPE_FAILCOUNT, CMD_SUBSCRIBE_FAILCOUNT,
+		file, userbuf, count, ppos);
+}
+
+
+static ssize_t lbs_failcount_write(
+	struct file *file, const char __user *userbuf,
+	size_t count, loff_t *ppos)
+{
+	return lbs_threshold_write(TLV_TYPE_FAILCOUNT, CMD_SUBSCRIBE_FAILCOUNT,
+		file, userbuf, count, ppos);
+}
+
+
+static ssize_t lbs_highrssi_read(
+	struct file *file, char __user *userbuf,
+	size_t count, loff_t *ppos)
+{
+	return lbs_threshold_read(TLV_TYPE_RSSI_HIGH, CMD_SUBSCRIBE_RSSI_HIGH,
+		file, userbuf, count, ppos);
+}
+
+
+static ssize_t lbs_highrssi_write(
+	struct file *file, const char __user *userbuf,
+	size_t count, loff_t *ppos)
+{
+	return lbs_threshold_write(TLV_TYPE_RSSI_HIGH, CMD_SUBSCRIBE_RSSI_HIGH,
+		file, userbuf, count, ppos);
+}
+
+
+static ssize_t lbs_highsnr_read(
+	struct file *file, char __user *userbuf,
+	size_t count, loff_t *ppos)
+{
+	return lbs_threshold_read(TLV_TYPE_SNR_HIGH, CMD_SUBSCRIBE_SNR_HIGH,
+		file, userbuf, count, ppos);
+}
+
+
+static ssize_t lbs_highsnr_write(
+	struct file *file, const char __user *userbuf,
+	size_t count, loff_t *ppos)
+{
+	return lbs_threshold_write(TLV_TYPE_SNR_HIGH, CMD_SUBSCRIBE_SNR_HIGH,
+		file, userbuf, count, ppos);
+}
+
+static ssize_t lbs_bcnmiss_read(
+	struct file *file, char __user *userbuf,
+	size_t count, loff_t *ppos)
+{
+	return lbs_threshold_read(TLV_TYPE_BCNMISS, CMD_SUBSCRIBE_BCNMISS,
+		file, userbuf, count, ppos);
+}
+
+
+static ssize_t lbs_bcnmiss_write(
+	struct file *file, const char __user *userbuf,
+	size_t count, loff_t *ppos)
+{
+	return lbs_threshold_write(TLV_TYPE_BCNMISS, CMD_SUBSCRIBE_BCNMISS,
+		file, userbuf, count, ppos);
+}
+
+
+
+
+
+
+
+
+static ssize_t lbs_rdmac_read(struct file *file, char __user *userbuf,
+				  size_t count, loff_t *ppos)
+{
+	struct lbs_private *priv = file->private_data;
+	struct lbs_offset_value offval;
+	ssize_t pos = 0;
+	int ret;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	offval.offset = priv->mac_offset;
+	offval.value = 0;
+
+	ret = lbs_prepare_and_send_command(priv,
+				CMD_MAC_REG_ACCESS, 0,
+				CMD_OPTION_WAITFORRSP, 0, &offval);
+	mdelay(10);
+	pos += snprintf(buf+pos, len-pos, "MAC[0x%x] = 0x%08x\n",
+				priv->mac_offset, priv->offsetvalue.value);
+
+	ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+	free_page(addr);
+	return ret;
+}
+
+static ssize_t lbs_rdmac_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;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	buf_size = min(count, len - 1);
+	if (copy_from_user(buf, userbuf, buf_size)) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+	priv->mac_offset = simple_strtoul((char *)buf, NULL, 16);
+	res = count;
+out_unlock:
+	free_page(addr);
+	return res;
+}
+
+static ssize_t lbs_wrmac_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;
+	u32 offset, value;
+	struct lbs_offset_value offval;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	buf_size = min(count, len - 1);
+	if (copy_from_user(buf, userbuf, buf_size)) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+	res = sscanf(buf, "%x %x", &offset, &value);
+	if (res != 2) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+
+	offval.offset = offset;
+	offval.value = value;
+	res = lbs_prepare_and_send_command(priv,
+				CMD_MAC_REG_ACCESS, 1,
+				CMD_OPTION_WAITFORRSP, 0, &offval);
+	mdelay(10);
+
+	res = count;
+out_unlock:
+	free_page(addr);
+	return res;
+}
+
+static ssize_t lbs_rdbbp_read(struct file *file, char __user *userbuf,
+				  size_t count, loff_t *ppos)
+{
+	struct lbs_private *priv = file->private_data;
+	struct lbs_offset_value offval;
+	ssize_t pos = 0;
+	int ret;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	offval.offset = priv->bbp_offset;
+	offval.value = 0;
+
+	ret = lbs_prepare_and_send_command(priv,
+				CMD_BBP_REG_ACCESS, 0,
+				CMD_OPTION_WAITFORRSP, 0, &offval);
+	mdelay(10);
+	pos += snprintf(buf+pos, len-pos, "BBP[0x%x] = 0x%08x\n",
+				priv->bbp_offset, priv->offsetvalue.value);
+
+	ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+	free_page(addr);
+
+	return ret;
+}
+
+static ssize_t lbs_rdbbp_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;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	buf_size = min(count, len - 1);
+	if (copy_from_user(buf, userbuf, buf_size)) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+	priv->bbp_offset = simple_strtoul((char *)buf, NULL, 16);
+	res = count;
+out_unlock:
+	free_page(addr);
+	return res;
+}
+
+static ssize_t lbs_wrbbp_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;
+	u32 offset, value;
+	struct lbs_offset_value offval;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	buf_size = min(count, len - 1);
+	if (copy_from_user(buf, userbuf, buf_size)) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+	res = sscanf(buf, "%x %x", &offset, &value);
+	if (res != 2) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+
+	offval.offset = offset;
+	offval.value = value;
+	res = lbs_prepare_and_send_command(priv,
+				CMD_BBP_REG_ACCESS, 1,
+				CMD_OPTION_WAITFORRSP, 0, &offval);
+	mdelay(10);
+
+	res = count;
+out_unlock:
+	free_page(addr);
+	return res;
+}
+
+static ssize_t lbs_rdrf_read(struct file *file, char __user *userbuf,
+				  size_t count, loff_t *ppos)
+{
+	struct lbs_private *priv = file->private_data;
+	struct lbs_offset_value offval;
+	ssize_t pos = 0;
+	int ret;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	offval.offset = priv->rf_offset;
+	offval.value = 0;
+
+	ret = lbs_prepare_and_send_command(priv,
+				CMD_RF_REG_ACCESS, 0,
+				CMD_OPTION_WAITFORRSP, 0, &offval);
+	mdelay(10);
+	pos += snprintf(buf+pos, len-pos, "RF[0x%x] = 0x%08x\n",
+				priv->rf_offset, priv->offsetvalue.value);
+
+	ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+	free_page(addr);
+
+	return ret;
+}
+
+static ssize_t lbs_rdrf_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;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	buf_size = min(count, len - 1);
+	if (copy_from_user(buf, userbuf, buf_size)) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+	priv->rf_offset = simple_strtoul((char *)buf, NULL, 16);
+	res = count;
+out_unlock:
+	free_page(addr);
+	return res;
+}
+
+static ssize_t lbs_wrrf_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;
+	u32 offset, value;
+	struct lbs_offset_value offval;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	buf_size = min(count, len - 1);
+	if (copy_from_user(buf, userbuf, buf_size)) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+	res = sscanf(buf, "%x %x", &offset, &value);
+	if (res != 2) {
+		res = -EFAULT;
+		goto out_unlock;
+	}
+
+	offval.offset = offset;
+	offval.value = value;
+	res = lbs_prepare_and_send_command(priv,
+				CMD_RF_REG_ACCESS, 1,
+				CMD_OPTION_WAITFORRSP, 0, &offval);
+	mdelay(10);
+
+	res = count;
+out_unlock:
+	free_page(addr);
+	return res;
+}
+
+#define FOPS(fread, fwrite) { \
+	.owner = THIS_MODULE, \
+	.open = open_file_generic, \
+	.read = (fread), \
+	.write = (fwrite), \
+}
+
+struct lbs_debugfs_files {
+	char *name;
+	int perm;
+	struct file_operations fops;
+};
+
+static struct lbs_debugfs_files debugfs_files[] = {
+	{ "info", 0444, FOPS(lbs_dev_info, write_file_dummy), },
+	{ "getscantable", 0444, FOPS(lbs_getscantable,
+					write_file_dummy), },
+	{ "sleepparams", 0644, FOPS(lbs_sleepparams_read,
+				lbs_sleepparams_write), },
+	{ "extscan", 0600, FOPS(NULL, lbs_extscan), },
+	{ "setuserscan", 0600, FOPS(NULL, lbs_setuserscan), },
+};
+
+static struct lbs_debugfs_files debugfs_events_files[] = {
+	{"low_rssi", 0644, FOPS(lbs_lowrssi_read,
+				lbs_lowrssi_write), },
+	{"low_snr", 0644, FOPS(lbs_lowsnr_read,
+				lbs_lowsnr_write), },
+	{"failure_count", 0644, FOPS(lbs_failcount_read,
+				lbs_failcount_write), },
+	{"beacon_missed", 0644, FOPS(lbs_bcnmiss_read,
+				lbs_bcnmiss_write), },
+	{"high_rssi", 0644, FOPS(lbs_highrssi_read,
+				lbs_highrssi_write), },
+	{"high_snr", 0644, FOPS(lbs_highsnr_read,
+				lbs_highsnr_write), },
+};
+
+static struct lbs_debugfs_files debugfs_regs_files[] = {
+	{"rdmac", 0644, FOPS(lbs_rdmac_read, lbs_rdmac_write), },
+	{"wrmac", 0600, FOPS(NULL, lbs_wrmac_write), },
+	{"rdbbp", 0644, FOPS(lbs_rdbbp_read, lbs_rdbbp_write), },
+	{"wrbbp", 0600, FOPS(NULL, lbs_wrbbp_write), },
+	{"rdrf", 0644, FOPS(lbs_rdrf_read, lbs_rdrf_write), },
+	{"wrrf", 0600, FOPS(NULL, lbs_wrrf_write), },
+};
+
+void lbs_debugfs_init(void)
+{
+	if (!lbs_dir)
+		lbs_dir = debugfs_create_dir("lbs_wireless", NULL);
+
+	return;
+}
+
+void lbs_debugfs_remove(void)
+{
+	if (lbs_dir)
+		 debugfs_remove(lbs_dir);
+	return;
+}
+
+void lbs_debugfs_init_one(struct lbs_private *priv, struct net_device *dev)
+{
+	int i;
+	struct lbs_debugfs_files *files;
+	if (!lbs_dir)
+		goto exit;
+
+	priv->debugfs_dir = debugfs_create_dir(dev->name, lbs_dir);
+	if (!priv->debugfs_dir)
+		goto exit;
+
+	for (i=0; i<ARRAY_SIZE(debugfs_files); i++) {
+		files = &debugfs_files[i];
+		priv->debugfs_files[i] = debugfs_create_file(files->name,
+							     files->perm,
+							     priv->debugfs_dir,
+							     priv,
+							     &files->fops);
+	}
+
+	priv->events_dir = debugfs_create_dir("subscribed_events", priv->debugfs_dir);
+	if (!priv->events_dir)
+		goto exit;
+
+	for (i=0; i<ARRAY_SIZE(debugfs_events_files); i++) {
+		files = &debugfs_events_files[i];
+		priv->debugfs_events_files[i] = debugfs_create_file(files->name,
+							     files->perm,
+							     priv->events_dir,
+							     priv,
+							     &files->fops);
+	}
+
+	priv->regs_dir = debugfs_create_dir("registers", priv->debugfs_dir);
+	if (!priv->regs_dir)
+		goto exit;
+
+	for (i=0; i<ARRAY_SIZE(debugfs_regs_files); i++) {
+		files = &debugfs_regs_files[i];
+		priv->debugfs_regs_files[i] = debugfs_create_file(files->name,
+							     files->perm,
+							     priv->regs_dir,
+							     priv,
+							     &files->fops);
+	}
+
+#ifdef PROC_DEBUG
+	lbs_debug_init(priv, dev);
+#endif
+exit:
+	return;
+}
+
+void lbs_debugfs_remove_one(struct lbs_private *priv)
+{
+	int i;
+
+	for(i=0; i<ARRAY_SIZE(debugfs_regs_files); i++)
+		debugfs_remove(priv->debugfs_regs_files[i]);
+
+	debugfs_remove(priv->regs_dir);
+
+	for(i=0; i<ARRAY_SIZE(debugfs_events_files); i++)
+		debugfs_remove(priv->debugfs_events_files[i]);
+
+	debugfs_remove(priv->events_dir);
+#ifdef PROC_DEBUG
+	debugfs_remove(priv->debugfs_debug);
+#endif
+	for(i=0; i<ARRAY_SIZE(debugfs_files); i++)
+		debugfs_remove(priv->debugfs_files[i]);
+	debugfs_remove(priv->debugfs_dir);
+}
+
+
+
+/* debug entry */
+
+#ifdef PROC_DEBUG
+
+#define item_size(n)	(FIELD_SIZEOF(struct lbs_private, n))
+#define item_addr(n)	(offsetof(struct lbs_private, n))
+
+
+struct debug_data {
+	char name[32];
+	u32 size;
+	size_t addr;
+};
+
+/* To debug any member of struct lbs_private, simply add one line here.
+ */
+static struct debug_data items[] = {
+	{"intcounter", item_size(intcounter), item_addr(intcounter)},
+	{"psmode", item_size(psmode), item_addr(psmode)},
+	{"psstate", item_size(psstate), item_addr(psstate)},
+};
+
+static int num_of_items = ARRAY_SIZE(items);
+
+/**
+ *  @brief proc read function
+ *
+ *  @param page	   pointer to buffer
+ *  @param s       read data starting position
+ *  @param off     offset
+ *  @param cnt     counter
+ *  @param eof     end of file flag
+ *  @param data    data to output
+ *  @return 	   number of output data
+ */
+static ssize_t lbs_debugfs_read(struct file *file, char __user *userbuf,
+			size_t count, loff_t *ppos)
+{
+	int val = 0;
+	size_t pos = 0;
+	ssize_t res;
+	char *p;
+	int i;
+	struct debug_data *d;
+	unsigned long addr = get_zeroed_page(GFP_KERNEL);
+	char *buf = (char *)addr;
+
+	p = buf;
+
+	d = (struct debug_data *)file->private_data;
+
+	for (i = 0; i < num_of_items; i++) {
+		if (d[i].size == 1)
+			val = *((u8 *) d[i].addr);
+		else if (d[i].size == 2)
+			val = *((u16 *) d[i].addr);
+		else if (d[i].size == 4)
+			val = *((u32 *) d[i].addr);
+		else if (d[i].size == 8)
+			val = *((u64 *) d[i].addr);
+
+		pos += sprintf(p + pos, "%s=%d\n", d[i].name, val);
+	}
+
+	res = simple_read_from_buffer(userbuf, count, ppos, p, pos);
+
+	free_page(addr);
+	return res;
+}
+
+/**
+ *  @brief proc write function
+ *
+ *  @param f	   file pointer
+ *  @param buf     pointer to data buffer
+ *  @param cnt     data number to write
+ *  @param data    data to write
+ *  @return 	   number of data
+ */
+static ssize_t lbs_debugfs_write(struct file *f, const char __user *buf,
+			    size_t cnt, loff_t *ppos)
+{
+	int r, i;
+	char *pdata;
+	char *p;
+	char *p0;
+	char *p1;
+	char *p2;
+	struct debug_data *d = (struct debug_data *)f->private_data;
+
+	pdata = kmalloc(cnt, GFP_KERNEL);
+	if (pdata == NULL)
+		return 0;
+
+	if (copy_from_user(pdata, buf, cnt)) {
+		lbs_deb_debugfs("Copy from user failed\n");
+		kfree(pdata);
+		return 0;
+	}
+
+	p0 = pdata;
+	for (i = 0; i < num_of_items; i++) {
+		do {
+			p = strstr(p0, d[i].name);
+			if (p == NULL)
+				break;
+			p1 = strchr(p, '\n');
+			if (p1 == NULL)
+				break;
+			p0 = p1++;
+			p2 = strchr(p, '=');
+			if (!p2)
+				break;
+			p2++;
+			r = simple_strtoul(p2, NULL, 0);
+			if (d[i].size == 1)
+				*((u8 *) d[i].addr) = (u8) r;
+			else if (d[i].size == 2)
+				*((u16 *) d[i].addr) = (u16) r;
+			else if (d[i].size == 4)
+				*((u32 *) d[i].addr) = (u32) r;
+			else if (d[i].size == 8)
+				*((u64 *) d[i].addr) = (u64) r;
+			break;
+		} while (1);
+	}
+	kfree(pdata);
+
+	return (ssize_t)cnt;
+}
+
+static struct file_operations lbs_debug_fops = {
+	.owner = THIS_MODULE,
+	.open = open_file_generic,
+	.write = lbs_debugfs_write,
+	.read = lbs_debugfs_read,
+};
+
+/**
+ *  @brief create debug proc file
+ *
+ *  @param priv	   pointer struct lbs_private
+ *  @param dev     pointer net_device
+ *  @return 	   N/A
+ */
+static void lbs_debug_init(struct lbs_private *priv, struct net_device *dev)
+{
+	int i;
+
+	if (!priv->debugfs_dir)
+		return;
+
+	for (i = 0; i < num_of_items; i++)
+		items[i].addr += (size_t) priv;
+
+	priv->debugfs_debug = debugfs_create_file("debug", 0644,
+						  priv->debugfs_dir, &items[0],
+						  &lbs_debug_fops);
+}
+#endif

+ 10 - 0
package/libertas/src/debugfs.h

@@ -0,0 +1,10 @@
+#ifndef _LBS_DEBUGFS_H_
+#define _LBS_DEBUGFS_H_
+
+void lbs_debugfs_init(void);
+void lbs_debugfs_remove(void);
+
+void lbs_debugfs_init_one(struct lbs_private *priv, struct net_device *dev);
+void lbs_debugfs_remove_one(struct lbs_private *priv);
+
+#endif

+ 80 - 0
package/libertas/src/decl.h

@@ -0,0 +1,80 @@
+/**
+  *  This file contains declaration referring to
+  *  functions defined in other source files
+  */
+
+#ifndef _LBS_DECL_H_
+#define _LBS_DECL_H_
+
+#include <linux/device.h>
+
+#include "defs.h"
+
+/** Function Prototype Declaration */
+struct lbs_private;
+struct sk_buff;
+struct net_device;
+struct cmd_ctrl_node;
+struct cmd_ds_command;
+
+int lbs_set_mac_packet_filter(struct lbs_private *priv);
+
+void lbs_send_tx_feedback(struct lbs_private *priv);
+
+int lbs_free_cmd_buffer(struct lbs_private *priv);
+
+int lbs_prepare_and_send_command(struct lbs_private *priv,
+	u16 cmd_no,
+	u16 cmd_action,
+	u16 wait_option, u32 cmd_oid, void *pdata_buf);
+
+void lbs_queue_cmd(struct lbs_private *priv,
+	struct cmd_ctrl_node *cmdnode,
+	u8 addtail);
+
+int lbs_allocate_cmd_buffer(struct lbs_private *priv);
+int lbs_execute_next_command(struct lbs_private *priv);
+int lbs_process_event(struct lbs_private *priv);
+void lbs_interrupt(struct lbs_private *priv);
+int lbs_set_radio_control(struct lbs_private *priv);
+u32 lbs_fw_index_to_data_rate(u8 index);
+u8 lbs_data_rate_to_fw_index(u32 rate);
+void lbs_get_fwversion(struct lbs_private *priv,
+	char *fwversion,
+	int maxlen);
+
+/** The proc fs interface */
+int lbs_process_rx_command(struct lbs_private *priv);
+void __lbs_cleanup_and_insert_cmd(struct lbs_private *priv,
+	struct cmd_ctrl_node *ptempcmd);
+
+int lbs_hard_start_xmit(struct sk_buff *skb, struct net_device *dev);
+int lbs_set_regiontable(struct lbs_private *priv, u8 region, u8 band);
+
+int lbs_process_rxed_packet(struct lbs_private *priv, struct sk_buff *);
+
+void lbs_ps_sleep(struct lbs_private *priv, int wait_option);
+void lbs_ps_confirm_sleep(struct lbs_private *priv, u16 psmode);
+void lbs_ps_wakeup(struct lbs_private *priv, int wait_option);
+
+struct chan_freq_power *lbs_find_cfp_by_band_and_channel(
+	struct lbs_private *priv,
+	u8 band,
+	u16 channel);
+
+void lbs_mac_event_disconnected(struct lbs_private *priv);
+
+void lbs_send_iwevcustom_event(struct lbs_private *priv, s8 *str);
+
+/* main.c */
+struct chan_freq_power *lbs_get_region_cfp_table(u8 region,
+	u8 band,
+	int *cfp_no);
+struct lbs_private *lbs_add_card(void *card, struct device *dmdev);
+int lbs_remove_card(struct lbs_private *priv);
+int lbs_start_card(struct lbs_private *priv);
+int lbs_stop_card(struct lbs_private *priv);
+int lbs_reset_device(struct lbs_private *priv);
+void lbs_host_to_card_done(struct lbs_private *priv);
+
+#endif

+ 379 - 0
package/libertas/src/defs.h

@@ -0,0 +1,379 @@
+/**
+  * This header file contains global constant/enum definitions,
+  * global variable declaration.
+  */
+#ifndef _LBS_DEFS_H_
+#define _LBS_DEFS_H_
+
+#include <linux/spinlock.h>
+
+#ifdef CONFIG_LIBERTAS_DEBUG
+#define DEBUG
+#define PROC_DEBUG
+#endif
+
+#ifndef DRV_NAME
+#define DRV_NAME "libertas"
+#endif
+
+
+#define LBS_DEB_ENTER	0x00000001
+#define LBS_DEB_LEAVE	0x00000002
+#define LBS_DEB_MAIN	0x00000004
+#define LBS_DEB_NET	0x00000008
+#define LBS_DEB_MESH	0x00000010
+#define LBS_DEB_WEXT	0x00000020
+#define LBS_DEB_IOCTL	0x00000040
+#define LBS_DEB_SCAN	0x00000080
+#define LBS_DEB_ASSOC	0x00000100
+#define LBS_DEB_JOIN	0x00000200
+#define LBS_DEB_11D	0x00000400
+#define LBS_DEB_DEBUGFS	0x00000800
+#define LBS_DEB_ETHTOOL	0x00001000
+#define LBS_DEB_HOST	0x00002000
+#define LBS_DEB_CMD	0x00004000
+#define LBS_DEB_RX	0x00008000
+#define LBS_DEB_TX	0x00010000
+#define LBS_DEB_USB	0x00020000
+#define LBS_DEB_CS	0x00040000
+#define LBS_DEB_FW	0x00080000
+#define LBS_DEB_THREAD	0x00100000
+#define LBS_DEB_HEX	0x00200000
+#define LBS_DEB_SDIO	0x00400000
+
+extern unsigned int lbs_debug;
+
+#ifdef DEBUG
+#define LBS_DEB_LL(grp, grpnam, fmt, args...) \
+do { if ((lbs_debug & (grp)) == (grp)) \
+  printk(KERN_DEBUG DRV_NAME grpnam "%s: " fmt, \
+         in_interrupt() ? " (INT)" : "", ## args); } while (0)
+#else
+#define LBS_DEB_LL(grp, grpnam, fmt, args...) do {} while (0)
+#endif
+
+#define lbs_deb_enter(grp) \
+  LBS_DEB_LL(grp | LBS_DEB_ENTER, " enter", "%s():%d\n", __FUNCTION__, __LINE__);
+#define lbs_deb_enter_args(grp, fmt, args...) \
+  LBS_DEB_LL(grp | LBS_DEB_ENTER, " enter", "%s(" fmt "):%d\n", __FUNCTION__, ## args, __LINE__);
+#define lbs_deb_leave(grp) \
+  LBS_DEB_LL(grp | LBS_DEB_LEAVE, " leave", "%s():%d\n", __FUNCTION__, __LINE__);
+#define lbs_deb_leave_args(grp, fmt, args...) \
+  LBS_DEB_LL(grp | LBS_DEB_LEAVE, " leave", "%s():%d, " fmt "\n", \
+  __FUNCTION__, __LINE__, ##args);
+#define lbs_deb_main(fmt, args...)      LBS_DEB_LL(LBS_DEB_MAIN, " main", fmt, ##args)
+#define lbs_deb_net(fmt, args...)       LBS_DEB_LL(LBS_DEB_NET, " net", fmt, ##args)
+#define lbs_deb_mesh(fmt, args...)      LBS_DEB_LL(LBS_DEB_MESH, " mesh", fmt, ##args)
+#define lbs_deb_wext(fmt, args...)      LBS_DEB_LL(LBS_DEB_WEXT, " wext", fmt, ##args)
+#define lbs_deb_ioctl(fmt, args...)     LBS_DEB_LL(LBS_DEB_IOCTL, " ioctl", fmt, ##args)
+#define lbs_deb_scan(fmt, args...)      LBS_DEB_LL(LBS_DEB_SCAN, " scan", fmt, ##args)
+#define lbs_deb_assoc(fmt, args...)     LBS_DEB_LL(LBS_DEB_ASSOC, " assoc", fmt, ##args)
+#define lbs_deb_join(fmt, args...)      LBS_DEB_LL(LBS_DEB_JOIN, " join", fmt, ##args)
+#define lbs_deb_11d(fmt, args...)       LBS_DEB_LL(LBS_DEB_11D, " 11d", fmt, ##args)
+#define lbs_deb_debugfs(fmt, args...)   LBS_DEB_LL(LBS_DEB_DEBUGFS, " debugfs", fmt, ##args)
+#define lbs_deb_ethtool(fmt, args...)   LBS_DEB_LL(LBS_DEB_ETHTOOL, " ethtool", fmt, ##args)
+#define lbs_deb_host(fmt, args...)      LBS_DEB_LL(LBS_DEB_HOST, " host", fmt, ##args)
+#define lbs_deb_cmd(fmt, args...)       LBS_DEB_LL(LBS_DEB_CMD, " cmd", fmt, ##args)
+#define lbs_deb_rx(fmt, args...)        LBS_DEB_LL(LBS_DEB_RX, " rx", fmt, ##args)
+#define lbs_deb_tx(fmt, args...)        LBS_DEB_LL(LBS_DEB_TX, " tx", fmt, ##args)
+#define lbs_deb_fw(fmt, args...)        LBS_DEB_LL(LBS_DEB_FW, " fw", fmt, ##args)
+#define lbs_deb_usb(fmt, args...)       LBS_DEB_LL(LBS_DEB_USB, " usb", fmt, ##args)
+#define lbs_deb_usbd(dev, fmt, args...) LBS_DEB_LL(LBS_DEB_USB, " usbd", "%s:" fmt, (dev)->bus_id, ##args)
+#define lbs_deb_cs(fmt, args...)        LBS_DEB_LL(LBS_DEB_CS, " cs", fmt, ##args)
+#define lbs_deb_thread(fmt, args...)    LBS_DEB_LL(LBS_DEB_THREAD, " thread", fmt, ##args)
+#define lbs_deb_sdio(fmt, args...)      LBS_DEB_LL(LBS_DEB_SDIO, " thread", fmt, ##args)
+
+#define lbs_pr_info(format, args...) \
+	printk(KERN_INFO DRV_NAME": " format, ## args)
+#define lbs_pr_err(format, args...) \
+	printk(KERN_ERR DRV_NAME": " format, ## args)
+#define lbs_pr_alert(format, args...) \
+	printk(KERN_ALERT DRV_NAME": " format, ## args)
+
+#ifdef DEBUG
+static inline void lbs_deb_hex(unsigned int grp, const char *prompt, u8 *buf, int len)
+{
+	int i = 0;
+
+	if (len &&
+	    (lbs_debug & LBS_DEB_HEX) &&
+	    (lbs_debug & grp))
+	{
+		for (i = 1; i <= len; i++) {
+			if ((i & 0xf) == 1) {
+				if (i != 1)
+					printk("\n");
+				printk(DRV_NAME " %s: ", prompt);
+			}
+			printk("%02x ", (u8) * buf);
+			buf++;
+		}
+		printk("\n");
+	}
+}
+#else
+#define lbs_deb_hex(grp,prompt,buf,len)	do {} while (0)
+#endif
+
+
+
+/** Buffer Constants */
+
+/*	The size of SQ memory PPA, DPA are 8 DWORDs, that keep the physical
+*	addresses of TxPD buffers. Station has only 8 TxPD available, Whereas
+*	driver has more local TxPDs. Each TxPD on the host memory is associated
+*	with a Tx control node. The driver maintains 8 RxPD descriptors for
+*	station firmware to store Rx packet information.
+*
+*	Current version of MAC has a 32x6 multicast address buffer.
+*
+*	802.11b can have up to  14 channels, the driver keeps the
+*	BSSID(MAC address) of each APs or Ad hoc stations it has sensed.
+*/
+
+#define MRVDRV_MAX_MULTICAST_LIST_SIZE	32
+#define LBS_NUM_CMD_BUFFERS             10
+#define LBS_CMD_BUFFER_SIZE             (2 * 1024)
+#define MRVDRV_MAX_CHANNEL_SIZE		14
+#define MRVDRV_ASSOCIATION_TIME_OUT	255
+#define MRVDRV_SNAP_HEADER_LEN          8
+
+#define	LBS_UPLD_SIZE			2312
+#define DEV_NAME_LEN			32
+
+/** Misc constants */
+/* This section defines 802.11 specific contants */
+
+#define MRVDRV_MAX_BSS_DESCRIPTS		16
+#define MRVDRV_MAX_REGION_CODE			6
+
+#define MRVDRV_IGNORE_MULTIPLE_DTIM		0xfffe
+#define MRVDRV_MIN_MULTIPLE_DTIM		1
+#define MRVDRV_MAX_MULTIPLE_DTIM		5
+#define MRVDRV_DEFAULT_MULTIPLE_DTIM		1
+
+#define MRVDRV_DEFAULT_LISTEN_INTERVAL		10
+
+#define	MRVDRV_CHANNELS_PER_SCAN		4
+#define	MRVDRV_MAX_CHANNELS_PER_SCAN		14
+
+#define MRVDRV_MIN_BEACON_INTERVAL		20
+#define MRVDRV_MAX_BEACON_INTERVAL		1000
+#define MRVDRV_BEACON_INTERVAL			100
+
+#define MARVELL_MESH_IE_LENGTH		9
+
+/** INT status Bit Definition*/
+#define MRVDRV_TX_DNLD_RDY		0x0001
+#define MRVDRV_RX_UPLD_RDY		0x0002
+#define MRVDRV_CMD_DNLD_RDY		0x0004
+#define MRVDRV_CMD_UPLD_RDY		0x0008
+#define MRVDRV_CARDEVENT		0x0010
+
+#define SBI_EVENT_CAUSE_SHIFT		3
+
+/** TxPD status */
+
+/*	Station firmware use TxPD status field to report final Tx transmit
+*	result, Bit masks are used to present combined situations.
+*/
+
+#define MRVDRV_TxPD_POWER_MGMT_NULL_PACKET 0x01
+#define MRVDRV_TxPD_POWER_MGMT_LAST_PACKET 0x08
+
+/** Tx mesh flag */
+/* Currently we are using normal WDS flag as mesh flag.
+ * TODO: change to proper mesh flag when MAC understands it.
+ */
+#define TxPD_CONTROL_WDS_FRAME (1<<17)
+#define TxPD_MESH_FRAME TxPD_CONTROL_WDS_FRAME
+
+/** RxPD status */
+
+#define MRVDRV_RXPD_STATUS_OK                0x0001
+
+/** RxPD status - Received packet types */
+/** Rx mesh flag */
+/* Currently we are using normal WDS flag as mesh flag.
+ * TODO: change to proper mesh flag when MAC understands it.
+ */
+#define RxPD_CONTROL_WDS_FRAME (0x40)
+#define RxPD_MESH_FRAME RxPD_CONTROL_WDS_FRAME
+
+/** RSSI-related defines */
+/*	RSSI constants are used to implement 802.11 RSSI threshold
+*	indication. if the Rx packet signal got too weak for 5 consecutive
+*	times, miniport driver (driver) will report this event to wrapper
+*/
+
+#define MRVDRV_NF_DEFAULT_SCAN_VALUE		(-96)
+
+/** RTS/FRAG related defines */
+#define MRVDRV_RTS_MIN_VALUE		0
+#define MRVDRV_RTS_MAX_VALUE		2347
+#define MRVDRV_FRAG_MIN_VALUE		256
+#define MRVDRV_FRAG_MAX_VALUE		2346
+
+/* This is for firmware specific length */
+#define EXTRA_LEN	36
+
+#define MRVDRV_ETH_TX_PACKET_BUFFER_SIZE \
+	(ETH_FRAME_LEN + sizeof(struct txpd) + EXTRA_LEN)
+
+#define MRVDRV_ETH_RX_PACKET_BUFFER_SIZE \
+	(ETH_FRAME_LEN + sizeof(struct rxpd) \
+	 + MRVDRV_SNAP_HEADER_LEN + EXTRA_LEN)
+
+#define	CMD_F_HOSTCMD		(1 << 0)
+#define FW_CAPINFO_WPA  	(1 << 0)
+
+#define KEY_LEN_WPA_AES			16
+#define KEY_LEN_WPA_TKIP		32
+#define KEY_LEN_WEP_104			13
+#define KEY_LEN_WEP_40			5
+
+#define RF_ANTENNA_1		0x1
+#define RF_ANTENNA_2		0x2
+#define RF_ANTENNA_AUTO		0xFFFF
+
+#define	BAND_B			(0x01)
+#define	BAND_G			(0x02)
+#define ALL_802_11_BANDS	(BAND_B | BAND_G)
+
+/** MACRO DEFINITIONS */
+#define CAL_NF(NF)			((s32)(-(s32)(NF)))
+#define CAL_RSSI(SNR, NF) 		((s32)((s32)(SNR) + CAL_NF(NF)))
+#define SCAN_RSSI(RSSI)			(0x100 - ((u8)(RSSI)))
+
+#define DEFAULT_BCN_AVG_FACTOR		8
+#define DEFAULT_DATA_AVG_FACTOR		8
+#define AVG_SCALE			100
+#define CAL_AVG_SNR_NF(AVG, SNRNF, N)         \
+                        (((AVG) == 0) ? ((u16)(SNRNF) * AVG_SCALE) : \
+                        ((((int)(AVG) * (N -1)) + ((u16)(SNRNF) * \
+                        AVG_SCALE))  / N))
+
+#define MAX_RATES			14
+
+#define	MAX_LEDS			8
+
+/** Global Variable Declaration */
+extern const char lbs_driver_version[];
+extern u16 lbs_region_code_to_index[MRVDRV_MAX_REGION_CODE];
+
+extern u8 lbs_bg_rates[MAX_RATES];
+
+/** ENUM definition*/
+/** SNRNF_TYPE */
+enum SNRNF_TYPE {
+	TYPE_BEACON = 0,
+	TYPE_RXPD,
+	MAX_TYPE_B
+};
+
+/** SNRNF_DATA*/
+enum SNRNF_DATA {
+	TYPE_NOAVG = 0,
+	TYPE_AVG,
+	MAX_TYPE_AVG
+};
+
+/** LBS_802_11_POWER_MODE */
+enum LBS_802_11_POWER_MODE {
+	LBS802_11POWERMODECAM,
+	LBS802_11POWERMODEMAX_PSP,
+	LBS802_11POWERMODEFAST_PSP,
+	/*not a real mode, defined as an upper bound */
+	LBS802_11POWEMODEMAX
+};
+
+/** PS_STATE */
+enum PS_STATE {
+	PS_STATE_FULL_POWER,
+	PS_STATE_AWAKE,
+	PS_STATE_PRE_SLEEP,
+	PS_STATE_SLEEP
+};
+
+/** DNLD_STATE */
+enum DNLD_STATE {
+	DNLD_RES_RECEIVED,
+	DNLD_DATA_SENT,
+	DNLD_CMD_SENT
+};
+
+/** LBS_MEDIA_STATE */
+enum LBS_MEDIA_STATE {
+	LBS_CONNECTED,
+	LBS_DISCONNECTED
+};
+
+/** LBS_802_11_PRIVACY_FILTER */
+enum LBS_802_11_PRIVACY_FILTER {
+	LBS802_11PRIVFILTERACCEPTALL,
+	LBS802_11PRIVFILTER8021XWEP
+};
+
+/** mv_ms_type */
+enum mv_ms_type {
+	MVMS_DAT = 0,
+	MVMS_CMD = 1,
+	MVMS_TXDONE = 2,
+	MVMS_EVENT
+};
+
+/** SNMP_MIB_INDEX_e */
+enum SNMP_MIB_INDEX_e {
+	DESIRED_BSSTYPE_I = 0,
+	OP_RATESET_I,
+	BCNPERIOD_I,
+	DTIMPERIOD_I,
+	ASSOCRSP_TIMEOUT_I,
+	RTSTHRESH_I,
+	SHORT_RETRYLIM_I,
+	LONG_RETRYLIM_I,
+	FRAGTHRESH_I,
+	DOT11D_I,
+	DOT11H_I,
+	MANUFID_I,
+	PRODID_I,
+	MANUF_OUI_I,
+	MANUF_NAME_I,
+	MANUF_PRODNAME_I,
+	MANUF_PRODVER_I,
+};
+
+/** KEY_TYPE_ID */
+enum KEY_TYPE_ID {
+	KEY_TYPE_ID_WEP = 0,
+	KEY_TYPE_ID_TKIP,
+	KEY_TYPE_ID_AES
+};
+
+/** KEY_INFO_WPA (applies to both TKIP and AES/CCMP) */
+enum KEY_INFO_WPA {
+	KEY_INFO_WPA_MCAST = 0x01,
+	KEY_INFO_WPA_UNICAST = 0x02,
+	KEY_INFO_WPA_ENABLED = 0x04
+};
+
+/** SNMP_MIB_VALUE_e */
+enum SNMP_MIB_VALUE_e {
+	SNMP_MIB_VALUE_INFRA = 1,
+	SNMP_MIB_VALUE_ADHOC
+};
+
+/* Default values for fwt commands. */
+#define FWT_DEFAULT_METRIC 0
+#define FWT_DEFAULT_DIR 1
+/* Default Rate, 11Mbps */
+#define FWT_DEFAULT_RATE 3
+#define FWT_DEFAULT_SSN 0xffffffff
+#define FWT_DEFAULT_DSN 0
+#define FWT_DEFAULT_HOPCOUNT 0
+#define FWT_DEFAULT_TTL 0
+#define FWT_DEFAULT_EXPIRATION 0
+#define FWT_DEFAULT_SLEEPMODE 0
+#define FWT_DEFAULT_SNR 0
+
+#endif

+ 365 - 0
package/libertas/src/dev.h

@@ -0,0 +1,365 @@
+/**
+  * This file contains definitions and data structures specific
+  * to Marvell 802.11 NIC. It contains the Device Information
+  * structure struct lbs_private..
+  */
+#ifndef _LBS_DEV_H_
+#define _LBS_DEV_H_
+
+#include <linux/netdevice.h>
+#include <linux/wireless.h>
+#include <linux/ethtool.h>
+#include <linux/debugfs.h>
+
+#include "defs.h"
+#include "scan.h"
+
+extern struct ethtool_ops lbs_ethtool_ops;
+
+#define	MAX_BSSID_PER_CHANNEL		16
+
+#define NR_TX_QUEUE			3
+
+/* For the extended Scan */
+#define MAX_EXTENDED_SCAN_BSSID_LIST    MAX_BSSID_PER_CHANNEL * \
+						MRVDRV_MAX_CHANNEL_SIZE + 1
+
+#define	MAX_REGION_CHANNEL_NUM	2
+
+/** Chan-freq-TxPower mapping table*/
+struct chan_freq_power {
+	/** channel Number		*/
+	u16 channel;
+	/** frequency of this channel	*/
+	u32 freq;
+	/** Max allowed Tx power level	*/
+	u16 maxtxpower;
+	/** TRUE:channel unsupported;  FLASE:supported*/
+	u8 unsupported;
+};
+
+/** region-band mapping table*/
+struct region_channel {
+	/** TRUE if this entry is valid		     */
+	u8 valid;
+	/** region code for US, Japan ...	     */
+	u8 region;
+	/** band B/G/A, used for BAND_CONFIG cmd	     */
+	u8 band;
+	/** Actual No. of elements in the array below */
+	u8 nrcfp;
+	/** chan-freq-txpower mapping table*/
+	struct chan_freq_power *CFP;
+};
+
+struct lbs_802_11_security {
+	u8 WPAenabled;
+	u8 WPA2enabled;
+	u8 wep_enabled;
+	u8 auth_mode;
+};
+
+/** Current Basic Service Set State Structure */
+struct current_bss_params {
+	/** bssid */
+	u8 bssid[ETH_ALEN];
+	/** ssid */
+	u8 ssid[IW_ESSID_MAX_SIZE + 1];
+	u8 ssid_len;
+
+	/** band */
+	u8 band;
+	/** channel */
+	u8 channel;
+	/** zero-terminated array of supported data rates */
+	u8 rates[MAX_RATES + 1];
+};
+
+/** sleep_params */
+struct sleep_params {
+	u16 sp_error;
+	u16 sp_offset;
+	u16 sp_stabletime;
+	u8 sp_calcontrol;
+	u8 sp_extsleepclk;
+	u16 sp_reserved;
+};
+
+/* Mesh statistics */
+struct lbs_mesh_stats {
+	u32	fwd_bcast_cnt;		/* Fwd: Broadcast counter */
+	u32	fwd_unicast_cnt;	/* Fwd: Unicast counter */
+	u32	fwd_drop_ttl;		/* Fwd: TTL zero */
+	u32	fwd_drop_rbt;		/* Fwd: Recently Broadcasted */
+	u32	fwd_drop_noroute; 	/* Fwd: No route to Destination */
+	u32	fwd_drop_nobuf;		/* Fwd: Run out of internal buffers */
+	u32	drop_blind;		/* Rx:  Dropped by blinding table */
+	u32	tx_failed_cnt;		/* Tx:  Failed transmissions */
+};
+
+/** Private structure for the MV device */
+struct lbs_private {
+	int mesh_open;
+	int infra_open;
+	int mesh_autostart_enabled;
+	__le16 boot2_version;
+
+	char name[DEV_NAME_LEN];
+
+	void *card;
+	struct net_device *dev;
+
+	struct net_device_stats stats;
+	struct net_device *mesh_dev; /* Virtual device */
+	struct net_device *rtap_net_dev;
+
+	struct iw_statistics wstats;
+	struct lbs_mesh_stats mstats;
+	struct dentry *debugfs_dir;
+	struct dentry *debugfs_debug;
+	struct dentry *debugfs_files[6];
+
+	struct dentry *events_dir;
+	struct dentry *debugfs_events_files[6];
+
+	struct dentry *regs_dir;
+	struct dentry *debugfs_regs_files[6];
+
+	u32 mac_offset;
+	u32 bbp_offset;
+	u32 rf_offset;
+
+	/** Upload length */
+	u32 upld_len;
+	/* Upload buffer */
+	u8 upld_buf[LBS_UPLD_SIZE];
+	/* Download sent:
+	   bit0 1/0=data_sent/data_tx_done,
+	   bit1 1/0=cmd_sent/cmd_tx_done,
+	   all other bits reserved 0 */
+	u8 dnld_sent;
+
+	/** thread to service interrupts */
+	struct task_struct *main_thread;
+	wait_queue_head_t waitq;
+	struct workqueue_struct *work_thread;
+
+	struct delayed_work scan_work;
+	struct delayed_work assoc_work;
+	struct work_struct sync_channel;
+
+	/** Hardware access */
+	int (*hw_host_to_card) (struct lbs_private *priv, u8 type, u8 *payload, u16 nb);
+	int (*hw_get_int_status) (struct lbs_private *priv, u8 *);
+	int (*hw_read_event_cause) (struct lbs_private *);
+
+	/* was struct lbs_adapter from here... */
+
+	/** Wlan adapter data structure*/
+	/** STATUS variables */
+	u8 fwreleasenumber[4];
+	u32 fwcapinfo;
+	/* protected with big lock */
+
+	struct mutex lock;
+
+	/* TX packet ready to be sent... */
+	int tx_pending_len;		/* -1 while building packet */
+
+	u8 tx_pending_buf[LBS_UPLD_SIZE];
+	/* protected by hard_start_xmit serialization */
+
+	/** command-related variables */
+	u16 seqnum;
+	/* protected by big lock */
+
+	struct cmd_ctrl_node *cmd_array;
+	/** Current command */
+	struct cmd_ctrl_node *cur_cmd;
+	int cur_cmd_retcode;
+	/** command Queues */
+	/** Free command buffers */
+	struct list_head cmdfreeq;
+	/** Pending command buffers */
+	struct list_head cmdpendingq;
+
+	wait_queue_head_t cmd_pending;
+	/* command related variables protected by priv->driver_lock */
+
+	/** Async and Sync Event variables */
+	u32 intcounter;
+	u32 eventcause;
+	u8 nodename[16];	/* nickname */
+
+	/** spin locks */
+	spinlock_t driver_lock;
+
+	/** Timers */
+	struct timer_list command_timer;
+
+	u8 hisregcpy;
+
+	/** current ssid/bssid related parameters*/
+	struct current_bss_params curbssparams;
+	u8 mesh_ssid[IW_ESSID_MAX_SIZE + 1];
+	u8 mesh_ssid_len;
+
+	/* IW_MODE_* */
+	u8 mode;
+
+	/* Scan results list */
+	struct list_head network_list;
+	struct list_head network_free_list;
+	struct bss_descriptor *networks;
+
+	u16 beacon_period;
+	u8 beacon_enable;
+	u8 adhoccreate;
+
+	/** capability Info used in Association, start, join */
+	u16 capability;
+
+	/** MAC address information */
+	u8 current_addr[ETH_ALEN];
+	u8 multicastlist[MRVDRV_MAX_MULTICAST_LIST_SIZE][ETH_ALEN];
+	u32 nr_of_multicastmacaddr;
+
+	/** 802.11 statistics */
+//	struct cmd_DS_802_11_GET_STAT wlan802_11Stat;
+
+	u16 enablehwauto;
+	u16 ratebitmap;
+
+	u32 fragthsd;
+	u32 rtsthsd;
+
+	u8 txretrycount;
+
+	/** Tx-related variables (for single packet tx) */
+	struct sk_buff *currenttxskb;
+
+	/** NIC Operation characteristics */
+	u16 currentpacketfilter;
+	u32 connect_status;
+	u32 mesh_connect_status;
+	u16 regioncode;
+	u16 txpowerlevel;
+
+	/** POWER MANAGEMENT AND PnP SUPPORT */
+	u8 surpriseremoved;
+
+	u16 psmode;		/* Wlan802_11PowermodeCAM=disable
+				   Wlan802_11PowermodeMAX_PSP=enable */
+	u32 psstate;
+	u8 needtowakeup;
+
+	struct PS_CMD_ConfirmSleep lbs_ps_confirm_sleep;
+
+	struct assoc_request * pending_assoc_req;
+	struct assoc_request * in_progress_assoc_req;
+
+	/** Encryption parameter */
+	struct lbs_802_11_security secinfo;
+
+	/** WEP keys */
+	struct enc_key wep_keys[4];
+	u16 wep_tx_keyidx;
+
+	/** WPA keys */
+	struct enc_key wpa_mcast_key;
+	struct enc_key wpa_unicast_key;
+
+	/** WPA Information Elements*/
+	u8 wpa_ie[MAX_WPA_IE_LEN];
+	u8 wpa_ie_len;
+
+	/** Requested Signal Strength*/
+	u16 SNR[MAX_TYPE_B][MAX_TYPE_AVG];
+	u16 NF[MAX_TYPE_B][MAX_TYPE_AVG];
+	u8 RSSI[MAX_TYPE_B][MAX_TYPE_AVG];
+	u8 rawSNR[DEFAULT_DATA_AVG_FACTOR];
+	u8 rawNF[DEFAULT_DATA_AVG_FACTOR];
+	u16 nextSNRNF;
+	u16 numSNRNF;
+
+	u8 radioon;
+	u32 preamble;
+
+	/** data rate stuff */
+	u8 cur_rate;
+	u8 auto_rate;
+
+	/** sleep_params */
+	struct sleep_params sp;
+
+	/** RF calibration data */
+
+#define	MAX_REGION_CHANNEL_NUM	2
+	/** region channel data */
+	struct region_channel region_channel[MAX_REGION_CHANNEL_NUM];
+
+	struct region_channel universal_channel[MAX_REGION_CHANNEL_NUM];
+
+	/** 11D and Domain Regulatory Data */
+	struct lbs_802_11d_domain_reg domainreg;
+	struct parsed_region_chan_11d parsed_region_chan;
+
+	/** FSM variable for 11d support */
+	u32 enable11d;
+
+	/**	MISCELLANEOUS */
+	u8 *prdeeprom;
+	struct lbs_offset_value offsetvalue;
+
+	struct cmd_ds_802_11_get_log logmsg;
+
+	u32 monitormode;
+	int last_scanned_channel;
+	u8 fw_ready;
+};
+
+/** Association request
+ *
+ * Encapsulates all the options that describe a specific assocation request
+ * or configuration of the wireless card's radio, mode, and security settings.
+ */
+struct assoc_request {
+#define ASSOC_FLAG_SSID			1
+#define ASSOC_FLAG_CHANNEL		2
+#define ASSOC_FLAG_BAND			3
+#define ASSOC_FLAG_MODE			4
+#define ASSOC_FLAG_BSSID		5
+#define ASSOC_FLAG_WEP_KEYS		6
+#define ASSOC_FLAG_WEP_TX_KEYIDX	7
+#define ASSOC_FLAG_WPA_MCAST_KEY	8
+#define ASSOC_FLAG_WPA_UCAST_KEY	9
+#define ASSOC_FLAG_SECINFO		10
+#define ASSOC_FLAG_WPA_IE		11
+	unsigned long flags;
+
+	u8 ssid[IW_ESSID_MAX_SIZE + 1];
+	u8 ssid_len;
+	u8 channel;
+	u8 band;
+	u8 mode;
+	u8 bssid[ETH_ALEN];
+
+	/** WEP keys */
+	struct enc_key wep_keys[4];
+	u16 wep_tx_keyidx;
+
+	/** WPA keys */
+	struct enc_key wpa_mcast_key;
+	struct enc_key wpa_unicast_key;
+
+	struct lbs_802_11_security secinfo;
+
+	/** WPA Information Elements*/
+	u8 wpa_ie[MAX_WPA_IE_LEN];
+	u8 wpa_ie_len;
+
+	/* BSS to associate with for infrastructure of Ad-Hoc join */
+	struct bss_descriptor bss;
+};
+
+#endif

+ 172 - 0
package/libertas/src/ethtool.c

@@ -0,0 +1,172 @@
+#include <linux/netdevice.h>
+#include <linux/ethtool.h>
+#include <linux/delay.h>
+
+#include "host.h"
+#include "decl.h"
+#include "defs.h"
+#include "dev.h"
+#include "join.h"
+#include "wext.h"
+static const char * mesh_stat_strings[]= {
+			"drop_duplicate_bcast",
+			"drop_ttl_zero",
+			"drop_no_fwd_route",
+			"drop_no_buffers",
+			"fwded_unicast_cnt",
+			"fwded_bcast_cnt",
+			"drop_blind_table",
+			"tx_failed_cnt"
+};
+
+static void lbs_ethtool_get_drvinfo(struct net_device *dev,
+					 struct ethtool_drvinfo *info)
+{
+	struct lbs_private *priv = (struct lbs_private *) dev->priv;
+	char fwver[32];
+
+	lbs_get_fwversion(priv, fwver, sizeof(fwver) - 1);
+
+	strcpy(info->driver, "libertas");
+	strcpy(info->version, lbs_driver_version);
+	strcpy(info->fw_version, fwver);
+}
+
+/* All 8388 parts have 16KiB EEPROM size at the time of writing.
+ * In case that changes this needs fixing.
+ */
+#define LBS_EEPROM_LEN 16384
+
+static int lbs_ethtool_get_eeprom_len(struct net_device *dev)
+{
+	return LBS_EEPROM_LEN;
+}
+
+static int lbs_ethtool_get_eeprom(struct net_device *dev,
+                                  struct ethtool_eeprom *eeprom, u8 * bytes)
+{
+	struct lbs_private *priv = (struct lbs_private *) dev->priv;
+	struct lbs_ioctl_regrdwr regctrl;
+	char *ptr;
+	int ret;
+
+	regctrl.action = 0;
+	regctrl.offset = eeprom->offset;
+	regctrl.NOB = eeprom->len;
+
+	if (eeprom->offset + eeprom->len > LBS_EEPROM_LEN)
+		return -EINVAL;
+
+//      mutex_lock(&priv->mutex);
+
+	priv->prdeeprom = kmalloc(eeprom->len+sizeof(regctrl), GFP_KERNEL);
+	if (!priv->prdeeprom)
+		return -ENOMEM;
+	memcpy(priv->prdeeprom, &regctrl, sizeof(regctrl));
+
+	/* +14 is for action, offset, and NOB in
+	 * response */
+	lbs_deb_ethtool("action:%d offset: %x NOB: %02x\n",
+	       regctrl.action, regctrl.offset, regctrl.NOB);
+
+	ret = lbs_prepare_and_send_command(priv,
+				    CMD_802_11_EEPROM_ACCESS,
+				    regctrl.action,
+				    CMD_OPTION_WAITFORRSP, 0,
+				    &regctrl);
+
+	if (ret) {
+		if (priv->prdeeprom)
+			kfree(priv->prdeeprom);
+		goto done;
+	}
+
+	mdelay(10);
+
+	ptr = (char *)priv->prdeeprom;
+
+	/* skip the command header, but include the "value" u32 variable */
+	ptr = ptr + sizeof(struct lbs_ioctl_regrdwr) - 4;
+
+	/*
+	 * Return the result back to the user
+	 */
+	memcpy(bytes, ptr, eeprom->len);
+
+	if (priv->prdeeprom)
+		kfree(priv->prdeeprom);
+//	mutex_unlock(&priv->mutex);
+
+	ret = 0;
+
+done:
+	lbs_deb_enter_args(LBS_DEB_ETHTOOL, "ret %d", ret);
+        return ret;
+}
+
+static void lbs_ethtool_get_stats(struct net_device * dev,
+				struct ethtool_stats * stats, u64 * data)
+{
+	struct lbs_private *priv = dev->priv;
+	struct cmd_ds_mesh_access mesh_access;
+	int ret;
+
+	lbs_deb_enter(LBS_DEB_ETHTOOL);
+
+	/* Get Mesh Statistics */
+	ret = lbs_prepare_and_send_command(priv,
+			CMD_MESH_ACCESS, CMD_ACT_MESH_GET_STATS,
+			CMD_OPTION_WAITFORRSP, 0, &mesh_access);
+
+	if (ret)
+		return;
+
+	priv->mstats.fwd_drop_rbt = le32_to_cpu(mesh_access.data[0]);
+	priv->mstats.fwd_drop_ttl = le32_to_cpu(mesh_access.data[1]);
+	priv->mstats.fwd_drop_noroute = le32_to_cpu(mesh_access.data[2]);
+	priv->mstats.fwd_drop_nobuf = le32_to_cpu(mesh_access.data[3]);
+	priv->mstats.fwd_unicast_cnt = le32_to_cpu(mesh_access.data[4]);
+	priv->mstats.fwd_bcast_cnt = le32_to_cpu(mesh_access.data[5]);
+	priv->mstats.drop_blind = le32_to_cpu(mesh_access.data[6]);
+	priv->mstats.tx_failed_cnt = le32_to_cpu(mesh_access.data[7]);
+
+	data[0] = priv->mstats.fwd_drop_rbt;
+	data[1] = priv->mstats.fwd_drop_ttl;
+	data[2] = priv->mstats.fwd_drop_noroute;
+	data[3] = priv->mstats.fwd_drop_nobuf;
+	data[4] = priv->mstats.fwd_unicast_cnt;
+	data[5] = priv->mstats.fwd_bcast_cnt;
+	data[6] = priv->mstats.drop_blind;
+	data[7] = priv->mstats.tx_failed_cnt;
+
+	lbs_deb_enter(LBS_DEB_ETHTOOL);
+}
+
+static void lbs_ethtool_get_strings(struct net_device *dev,
+					  u32 stringset,
+					  u8 * s)
+{
+	int i;
+
+	lbs_deb_enter(LBS_DEB_ETHTOOL);
+
+	switch (stringset) {
+        case ETH_SS_STATS:
+		for (i=0; i < MESH_STATS_NUM; i++) {
+			memcpy(s + i * ETH_GSTRING_LEN,
+					mesh_stat_strings[i],
+					ETH_GSTRING_LEN);
+		}
+		break;
+        }
+	lbs_deb_enter(LBS_DEB_ETHTOOL);
+}
+
+struct ethtool_ops lbs_ethtool_ops = {
+	.get_drvinfo = lbs_ethtool_get_drvinfo,
+	.get_eeprom =  lbs_ethtool_get_eeprom,
+	.get_eeprom_len = lbs_ethtool_get_eeprom_len,
+	.get_ethtool_stats = lbs_ethtool_get_stats,
+	.get_strings = lbs_ethtool_get_strings,
+};
+

+ 286 - 0
package/libertas/src/host.h

@@ -0,0 +1,286 @@
+/**
+  * This file contains definitions of WLAN commands.
+  */
+
+#ifndef _LBS_HOST_H_
+#define _LBS_HOST_H_
+
+/** PUBLIC DEFINITIONS */
+#define DEFAULT_AD_HOC_CHANNEL			6
+#define	DEFAULT_AD_HOC_CHANNEL_A		36
+
+/** IEEE 802.11 oids */
+#define OID_802_11_SSID				0x00008002
+#define OID_802_11_INFRASTRUCTURE_MODE		0x00008008
+#define OID_802_11_FRAGMENTATION_THRESHOLD	0x00008009
+#define OID_802_11_RTS_THRESHOLD		0x0000800A
+#define OID_802_11_TX_ANTENNA_SELECTED		0x0000800D
+#define OID_802_11_SUPPORTED_RATES		0x0000800E
+#define OID_802_11_STATISTICS			0x00008012
+#define OID_802_11_TX_RETRYCOUNT		0x0000801D
+#define OID_802_11D_ENABLE			0x00008020
+
+#define CMD_OPTION_WAITFORRSP			0x0002
+
+/** Host command IDs */
+
+/* Return command are almost always the same as the host command, but with
+ * bit 15 set high.  There are a few exceptions, though...
+ */
+#define CMD_RET(cmd)			(0x8000 | cmd)
+
+/* Return command convention exceptions: */
+#define CMD_RET_802_11_ASSOCIATE		0x8012
+
+/* Command codes */
+#define CMD_CODE_DNLD				0x0002
+#define CMD_GET_HW_SPEC				0x0003
+#define	CMD_EEPROM_UPDATE			0x0004
+#define CMD_802_11_RESET			0x0005
+#define	CMD_802_11_SCAN				0x0006
+#define CMD_802_11_GET_LOG			0x000b
+#define CMD_MAC_MULTICAST_ADR			0x0010
+#define CMD_802_11_AUTHENTICATE			0x0011
+#define CMD_802_11_EEPROM_ACCESS		0x0059
+#define CMD_802_11_ASSOCIATE			0x0050
+#define CMD_802_11_SET_WEP			0x0013
+#define CMD_802_11_GET_STAT			0x0014
+#define CMD_802_3_GET_STAT			0x0015
+#define CMD_802_11_SNMP_MIB			0x0016
+#define CMD_MAC_REG_MAP				0x0017
+#define CMD_BBP_REG_MAP				0x0018
+#define CMD_MAC_REG_ACCESS			0x0019
+#define CMD_BBP_REG_ACCESS			0x001a
+#define CMD_RF_REG_ACCESS			0x001b
+#define CMD_802_11_RADIO_CONTROL		0x001c
+#define CMD_802_11_RF_CHANNEL			0x001d
+#define CMD_802_11_RF_TX_POWER			0x001e
+#define CMD_802_11_RSSI				0x001f
+#define CMD_802_11_RF_ANTENNA			0x0020
+#define CMD_802_11_PS_MODE			0x0021
+#define CMD_802_11_DATA_RATE			0x0022
+#define CMD_RF_REG_MAP				0x0023
+#define CMD_802_11_DEAUTHENTICATE		0x0024
+#define CMD_802_11_REASSOCIATE			0x0025
+#define CMD_802_11_DISASSOCIATE			0x0026
+#define CMD_MAC_CONTROL				0x0028
+#define CMD_802_11_AD_HOC_START			0x002b
+#define CMD_802_11_AD_HOC_JOIN			0x002c
+#define CMD_802_11_QUERY_TKIP_REPLY_CNTRS	0x002e
+#define CMD_802_11_ENABLE_RSN			0x002f
+#define CMD_802_11_PAIRWISE_TSC			0x0036
+#define CMD_802_11_GROUP_TSC			0x0037
+#define CMD_802_11_SET_AFC			0x003c
+#define CMD_802_11_GET_AFC			0x003d
+#define CMD_802_11_AD_HOC_STOP			0x0040
+#define CMD_802_11_BEACON_STOP			0x0049
+#define CMD_802_11_MAC_ADDRESS			0x004d
+#define CMD_802_11_LED_GPIO_CTRL		0x004e
+#define CMD_802_11_EEPROM_ACCESS		0x0059
+#define CMD_802_11_BAND_CONFIG			0x0058
+#define CMD_802_11D_DOMAIN_INFO			0x005b
+#define CMD_802_11_KEY_MATERIAL			0x005e
+#define CMD_802_11_SLEEP_PARAMS			0x0066
+#define CMD_802_11_INACTIVITY_TIMEOUT		0x0067
+#define CMD_802_11_TPC_CFG			0x0072
+#define CMD_802_11_PWR_CFG			0x0073
+#define CMD_802_11_SUBSCRIBE_EVENT		0x0075
+#define CMD_802_11_RATE_ADAPT_RATESET		0x0076
+#define CMD_802_11_TX_RATE_QUERY		0x007f
+#define	CMD_GET_TSF				0x0080
+#define CMD_BT_ACCESS				0x0087
+#define CMD_FWT_ACCESS				0x0095
+#define CMD_802_11_MONITOR_MODE			0x0098
+#define CMD_MESH_ACCESS				0x009b
+#define CMD_MESH_CONFIG				0x00a3
+#define	CMD_SET_BOOT2_VER			0x00a5
+#define CMD_802_11_BEACON_CTRL			0x00b0
+
+/* For the IEEE Power Save */
+#define CMD_SUBCMD_ENTER_PS		0x0030
+#define CMD_SUBCMD_EXIT_PS		0x0031
+#define CMD_SUBCMD_SLEEP_CONFIRMED	0x0034
+#define CMD_SUBCMD_FULL_POWERDOWN	0x0035
+#define CMD_SUBCMD_FULL_POWERUP		0x0036
+
+#define CMD_ENABLE_RSN			0x0001
+#define CMD_DISABLE_RSN			0x0000
+
+#define CMD_ACT_GET			0x0000
+#define CMD_ACT_SET			0x0001
+#define CMD_ACT_GET_AES			0x0002
+#define CMD_ACT_SET_AES			0x0003
+#define CMD_ACT_REMOVE_AES		0x0004
+
+/* Define action or option for CMD_802_11_SET_WEP */
+#define CMD_ACT_ADD			0x0002
+#define CMD_ACT_REMOVE			0x0004
+#define CMD_ACT_USE_DEFAULT		0x0008
+
+#define CMD_TYPE_WEP_40_BIT		0x01
+#define CMD_TYPE_WEP_104_BIT		0x02
+
+#define CMD_NUM_OF_WEP_KEYS		4
+
+#define CMD_WEP_KEY_INDEX_MASK		0x3fff
+
+/* Define action or option for CMD_802_11_RESET */
+#define CMD_ACT_HALT			0x0003
+
+/* Define action or option for CMD_802_11_SCAN */
+#define CMD_BSS_TYPE_BSS		0x0001
+#define CMD_BSS_TYPE_IBSS		0x0002
+#define CMD_BSS_TYPE_ANY		0x0003
+
+/* Define action or option for CMD_802_11_SCAN */
+#define CMD_SCAN_TYPE_ACTIVE		0x0000
+#define CMD_SCAN_TYPE_PASSIVE		0x0001
+
+#define CMD_SCAN_RADIO_TYPE_BG		0
+
+#define	CMD_SCAN_PROBE_DELAY_TIME	0
+
+/* Define action or option for CMD_MAC_CONTROL */
+#define CMD_ACT_MAC_RX_ON			0x0001
+#define CMD_ACT_MAC_TX_ON			0x0002
+#define CMD_ACT_MAC_LOOPBACK_ON			0x0004
+#define CMD_ACT_MAC_WEP_ENABLE			0x0008
+#define CMD_ACT_MAC_INT_ENABLE			0x0010
+#define CMD_ACT_MAC_MULTICAST_ENABLE		0x0020
+#define CMD_ACT_MAC_BROADCAST_ENABLE		0x0040
+#define CMD_ACT_MAC_PROMISCUOUS_ENABLE		0x0080
+#define CMD_ACT_MAC_ALL_MULTICAST_ENABLE	0x0100
+#define CMD_ACT_MAC_STRICT_PROTECTION_ENABLE	0x0400
+
+/* Define action or option for CMD_802_11_RADIO_CONTROL */
+#define CMD_TYPE_AUTO_PREAMBLE		0x0001
+#define CMD_TYPE_SHORT_PREAMBLE		0x0002
+#define CMD_TYPE_LONG_PREAMBLE		0x0003
+
+/* Event flags for CMD_802_11_SUBSCRIBE_EVENT */
+#define CMD_SUBSCRIBE_RSSI_LOW		0x0001
+#define CMD_SUBSCRIBE_SNR_LOW		0x0002
+#define CMD_SUBSCRIBE_FAILCOUNT		0x0004
+#define CMD_SUBSCRIBE_BCNMISS		0x0008
+#define CMD_SUBSCRIBE_RSSI_HIGH		0x0010
+#define CMD_SUBSCRIBE_SNR_HIGH		0x0020
+
+#define TURN_ON_RF			0x01
+#define RADIO_ON			0x01
+#define RADIO_OFF			0x00
+
+#define SET_AUTO_PREAMBLE		0x05
+#define SET_SHORT_PREAMBLE		0x03
+#define SET_LONG_PREAMBLE		0x01
+
+/* Define action or option for CMD_802_11_RF_CHANNEL */
+#define CMD_OPT_802_11_RF_CHANNEL_GET	0x00
+#define CMD_OPT_802_11_RF_CHANNEL_SET	0x01
+
+/* Define action or option for CMD_802_11_RF_TX_POWER */
+#define CMD_ACT_TX_POWER_OPT_GET	0x0000
+#define CMD_ACT_TX_POWER_OPT_SET_HIGH	0x8007
+#define CMD_ACT_TX_POWER_OPT_SET_MID	0x8004
+#define CMD_ACT_TX_POWER_OPT_SET_LOW	0x8000
+
+#define CMD_ACT_TX_POWER_INDEX_HIGH	0x0007
+#define CMD_ACT_TX_POWER_INDEX_MID	0x0004
+#define CMD_ACT_TX_POWER_INDEX_LOW	0x0000
+
+/* Define action or option for CMD_802_11_DATA_RATE */
+#define CMD_ACT_SET_TX_AUTO		0x0000
+#define CMD_ACT_SET_TX_FIX_RATE		0x0001
+#define CMD_ACT_GET_TX_RATE		0x0002
+
+#define CMD_ACT_SET_RX			0x0001
+#define	CMD_ACT_SET_TX			0x0002
+#define CMD_ACT_SET_BOTH		0x0003
+#define	CMD_ACT_GET_RX			0x0004
+#define CMD_ACT_GET_TX			0x0008
+#define	CMD_ACT_GET_BOTH		0x000c
+
+/* Define action or option for CMD_802_11_PS_MODE */
+#define CMD_TYPE_CAM			0x0000
+#define	CMD_TYPE_MAX_PSP		0x0001
+#define CMD_TYPE_FAST_PSP		0x0002
+
+/* Define action or option for CMD_BT_ACCESS */
+enum cmd_bt_access_opts {
+	/* The bt commands start at 5 instead of 1 because the old dft commands
+	 * are mapped to 1-4.  These old commands are no longer maintained and
+	 * should not be called.
+	 */
+	CMD_ACT_BT_ACCESS_ADD = 5,
+	CMD_ACT_BT_ACCESS_DEL,
+	CMD_ACT_BT_ACCESS_LIST,
+	CMD_ACT_BT_ACCESS_RESET,
+	CMD_ACT_BT_ACCESS_SET_INVERT,
+	CMD_ACT_BT_ACCESS_GET_INVERT
+};
+
+/* Define action or option for CMD_FWT_ACCESS */
+enum cmd_fwt_access_opts {
+	CMD_ACT_FWT_ACCESS_ADD = 1,
+	CMD_ACT_FWT_ACCESS_DEL,
+	CMD_ACT_FWT_ACCESS_LOOKUP,
+	CMD_ACT_FWT_ACCESS_LIST,
+	CMD_ACT_FWT_ACCESS_LIST_ROUTE,
+	CMD_ACT_FWT_ACCESS_LIST_NEIGHBOR,
+	CMD_ACT_FWT_ACCESS_RESET,
+	CMD_ACT_FWT_ACCESS_CLEANUP,
+	CMD_ACT_FWT_ACCESS_TIME,
+};
+
+/* Define action or option for CMD_MESH_ACCESS */
+enum cmd_mesh_access_opts {
+	CMD_ACT_MESH_GET_TTL = 1,
+	CMD_ACT_MESH_SET_TTL,
+	CMD_ACT_MESH_GET_STATS,
+	CMD_ACT_MESH_GET_ANYCAST,
+	CMD_ACT_MESH_SET_ANYCAST,
+	CMD_ACT_MESH_SET_LINK_COSTS,
+	CMD_ACT_MESH_GET_LINK_COSTS,
+	CMD_ACT_MESH_SET_BCAST_RATE,
+	CMD_ACT_MESH_GET_BCAST_RATE,
+	CMD_ACT_MESH_SET_RREQ_DELAY,
+	CMD_ACT_MESH_GET_RREQ_DELAY,
+	CMD_ACT_MESH_SET_ROUTE_EXP,
+	CMD_ACT_MESH_GET_ROUTE_EXP,
+	CMD_ACT_MESH_SET_AUTOSTART_ENABLED,
+	CMD_ACT_MESH_GET_AUTOSTART_ENABLED,
+};
+
+/** Card Event definition */
+#define MACREG_INT_CODE_TX_PPA_FREE		0
+#define MACREG_INT_CODE_TX_DMA_DONE		1
+#define MACREG_INT_CODE_LINK_LOST_W_SCAN	2
+#define MACREG_INT_CODE_LINK_LOST_NO_SCAN	3
+#define MACREG_INT_CODE_LINK_SENSED		4
+#define MACREG_INT_CODE_CMD_FINISHED		5
+#define MACREG_INT_CODE_MIB_CHANGED		6
+#define MACREG_INT_CODE_INIT_DONE		7
+#define MACREG_INT_CODE_DEAUTHENTICATED		8
+#define MACREG_INT_CODE_DISASSOCIATED		9
+#define MACREG_INT_CODE_PS_AWAKE		10
+#define MACREG_INT_CODE_PS_SLEEP		11
+#define MACREG_INT_CODE_MIC_ERR_MULTICAST	13
+#define MACREG_INT_CODE_MIC_ERR_UNICAST		14
+#define MACREG_INT_CODE_WM_AWAKE		15
+#define MACREG_INT_CODE_DEEP_SLEEP_AWAKE	16
+#define MACREG_INT_CODE_ADHOC_BCN_LOST		17
+#define MACREG_INT_CODE_HOST_AWAKE		18
+#define MACREG_INT_CODE_STOP_TX			19
+#define MACREG_INT_CODE_START_TX		20
+#define MACREG_INT_CODE_CHANNEL_SWITCH		21
+#define MACREG_INT_CODE_MEASUREMENT_RDY		22
+#define MACREG_INT_CODE_WMM_CHANGE		23
+#define MACREG_INT_CODE_BG_SCAN_REPORT		24
+#define MACREG_INT_CODE_RSSI_LOW		25
+#define MACREG_INT_CODE_SNR_LOW			26
+#define MACREG_INT_CODE_MAX_FAIL		27
+#define MACREG_INT_CODE_RSSI_HIGH		28
+#define MACREG_INT_CODE_SNR_HIGH		29
+#define MACREG_INT_CODE_MESH_AUTO_STARTED	35
+#define MACREG_INT_CODE_FIRMWARE_READY		48
+
+#endif

+ 713 - 0
package/libertas/src/hostcmd.h

@@ -0,0 +1,713 @@
+/*
+ * This file contains the function prototypes, data structure
+ * and defines for all the host/station commands
+ */
+#ifndef _LBS_HOSTCMD_H
+#define _LBS_HOSTCMD_H
+
+#include <linux/wireless.h>
+#include "11d.h"
+#include "types.h"
+
+/* 802.11-related definitions */
+
+/* TxPD descriptor */
+struct txpd {
+	/* Current Tx packet status */
+	__le32 tx_status;
+	/* Tx control */
+	__le32 tx_control;
+	__le32 tx_packet_location;
+	/* Tx packet length */
+	__le16 tx_packet_length;
+	/* First 2 byte of destination MAC address */
+	u8 tx_dest_addr_high[2];
+	/* Last 4 byte of destination MAC address */
+	u8 tx_dest_addr_low[4];
+	/* Pkt Priority */
+	u8 priority;
+	/* Pkt Trasnit Power control */
+	u8 powermgmt;
+	/* Amount of time the packet has been queued in the driver (units = 2ms) */
+	u8 pktdelay_2ms;
+	/* reserved */
+	u8 reserved1;
+};
+
+/* RxPD Descriptor */
+struct rxpd {
+	/* Current Rx packet status */
+	__le16 status;
+
+	/* SNR */
+	u8 snr;
+
+	/* Tx control */
+	u8 rx_control;
+
+	/* Pkt length */
+	__le16 pkt_len;
+
+	/* Noise Floor */
+	u8 nf;
+
+	/* Rx Packet Rate */
+	u8 rx_rate;
+
+	/* Pkt addr */
+	__le32 pkt_ptr;
+
+	/* Next Rx RxPD addr */
+	__le32 next_rxpd_ptr;
+
+	/* Pkt Priority */
+	u8 priority;
+	u8 reserved[3];
+};
+
+struct cmd_header {
+	__le16 command;
+	__le16 size;
+	__le16 seqnum;
+	__le16 result;
+} __attribute__ ((packed));
+
+struct cmd_ctrl_node {
+	struct list_head list;
+	/* wait for finish or not */
+	u16 wait_option;
+	/* command response */
+	void *pdata_buf;
+	int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *);
+	unsigned long callback_arg;
+	/* command data */
+	struct cmd_header *cmdbuf;
+	/* wait queue */
+	u16 cmdwaitqwoken;
+	wait_queue_head_t cmdwait_q;
+};
+
+/* Generic structure to hold all key types. */
+struct enc_key {
+	u16 len;
+	u16 flags;  /* KEY_INFO_* from defs.h */
+	u16 type; /* KEY_TYPE_* from defs.h */
+	u8 key[32];
+};
+
+/* lbs_offset_value */
+struct lbs_offset_value {
+	u32 offset;
+	u32 value;
+};
+
+/* Define general data structure */
+/* cmd_DS_GEN */
+struct cmd_ds_gen {
+	__le16 command;
+	__le16 size;
+	__le16 seqnum;
+	__le16 result;
+	void *cmdresp[0];
+};
+
+#define S_DS_GEN sizeof(struct cmd_ds_gen)
+
+
+/*
+ * Define data structure for CMD_GET_HW_SPEC
+ * This structure defines the response for the GET_HW_SPEC command
+ */
+struct cmd_ds_get_hw_spec {
+	struct cmd_header hdr;
+
+	/* HW Interface version number */
+	__le16 hwifversion;
+	/* HW version number */
+	__le16 version;
+	/* Max number of TxPD FW can handle */
+	__le16 nr_txpd;
+	/* Max no of Multicast address */
+	__le16 nr_mcast_adr;
+	/* MAC address */
+	u8 permanentaddr[6];
+
+	/* region Code */
+	__le16 regioncode;
+
+	/* Number of antenna used */
+	__le16 nr_antenna;
+
+	/* FW release number, example 1,2,3,4 = 3.2.1p4 */
+	u8 fwreleasenumber[4];
+
+	/* Base Address of TxPD queue */
+	__le32 wcb_base;
+	/* Read Pointer of RxPd queue */
+	__le32 rxpd_rdptr;
+
+	/* Write Pointer of RxPd queue */
+	__le32 rxpd_wrptr;
+
+	/*FW/HW capability */
+	__le32 fwcapinfo;
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_reset {
+	__le16 action;
+};
+
+struct cmd_ds_802_11_subscribe_event {
+	__le16 action;
+	__le16 events;
+
+	/* A TLV to the CMD_802_11_SUBSCRIBE_EVENT command can contain a
+	 * number of TLVs. From the v5.1 manual, those TLVs would add up to
+	 * 40 bytes. However, future firmware might add additional TLVs, so I
+	 * bump this up a bit.
+	 */
+	u8 tlv[128];
+};
+
+/*
+ * This scan handle Country Information IE(802.11d compliant)
+ * Define data structure for CMD_802_11_SCAN
+ */
+struct cmd_ds_802_11_scan {
+	u8 bsstype;
+	u8 bssid[ETH_ALEN];
+	u8 tlvbuffer[1];
+#if 0
+	mrvlietypes_ssidparamset_t ssidParamSet;
+	mrvlietypes_chanlistparamset_t ChanListParamSet;
+	mrvlietypes_ratesparamset_t OpRateSet;
+#endif
+};
+
+struct cmd_ds_802_11_scan_rsp {
+	__le16 bssdescriptsize;
+	u8 nr_sets;
+	u8 bssdesc_and_tlvbuffer[1];
+};
+
+struct cmd_ds_802_11_get_log {
+	__le32 mcasttxframe;
+	__le32 failed;
+	__le32 retry;
+	__le32 multiretry;
+	__le32 framedup;
+	__le32 rtssuccess;
+	__le32 rtsfailure;
+	__le32 ackfailure;
+	__le32 rxfrag;
+	__le32 mcastrxframe;
+	__le32 fcserror;
+	__le32 txframe;
+	__le32 wepundecryptable;
+};
+
+struct cmd_ds_mac_control {
+	__le16 action;
+	__le16 reserved;
+};
+
+struct cmd_ds_mac_multicast_adr {
+	__le16 action;
+	__le16 nr_of_adrs;
+	u8 maclist[ETH_ALEN * MRVDRV_MAX_MULTICAST_LIST_SIZE];
+};
+
+struct cmd_ds_802_11_authenticate {
+	u8 macaddr[ETH_ALEN];
+	u8 authtype;
+	u8 reserved[10];
+};
+
+struct cmd_ds_802_11_deauthenticate {
+	u8 macaddr[6];
+	__le16 reasoncode;
+};
+
+struct cmd_ds_802_11_associate {
+	u8 peerstaaddr[6];
+	__le16 capability;
+	__le16 listeninterval;
+	__le16 bcnperiod;
+	u8 dtimperiod;
+
+#if 0
+	mrvlietypes_ssidparamset_t ssidParamSet;
+	mrvlietypes_phyparamset_t phyparamset;
+	mrvlietypes_ssparamset_t ssparamset;
+	mrvlietypes_ratesparamset_t ratesParamSet;
+#endif
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_disassociate {
+	u8 destmacaddr[6];
+	__le16 reasoncode;
+};
+
+struct cmd_ds_802_11_associate_rsp {
+	struct ieeetypes_assocrsp assocRsp;
+};
+
+struct cmd_ds_802_11_ad_hoc_result {
+	u8 pad[3];
+	u8 bssid[ETH_ALEN];
+};
+
+struct cmd_ds_802_11_set_wep {
+	/* ACT_ADD, ACT_REMOVE or ACT_ENABLE */
+	__le16 action;
+
+	/* key Index selected for Tx */
+	__le16 keyindex;
+
+	/* 40, 128bit or TXWEP */
+	u8 keytype[4];
+	u8 keymaterial[4][16];
+};
+
+struct cmd_ds_802_3_get_stat {
+	__le32 xmitok;
+	__le32 rcvok;
+	__le32 xmiterror;
+	__le32 rcverror;
+	__le32 rcvnobuffer;
+	__le32 rcvcrcerror;
+};
+
+struct cmd_ds_802_11_get_stat {
+	__le32 txfragmentcnt;
+	__le32 mcasttxframecnt;
+	__le32 failedcnt;
+	__le32 retrycnt;
+	__le32 Multipleretrycnt;
+	__le32 rtssuccesscnt;
+	__le32 rtsfailurecnt;
+	__le32 ackfailurecnt;
+	__le32 frameduplicatecnt;
+	__le32 rxfragmentcnt;
+	__le32 mcastrxframecnt;
+	__le32 fcserrorcnt;
+	__le32 bcasttxframecnt;
+	__le32 bcastrxframecnt;
+	__le32 txbeacon;
+	__le32 rxbeacon;
+	__le32 wepundecryptable;
+};
+
+struct cmd_ds_802_11_snmp_mib {
+	__le16 querytype;
+	__le16 oid;
+	__le16 bufsize;
+	u8 value[128];
+};
+
+struct cmd_ds_mac_reg_map {
+	__le16 buffersize;
+	u8 regmap[128];
+	__le16 reserved;
+};
+
+struct cmd_ds_bbp_reg_map {
+	__le16 buffersize;
+	u8 regmap[128];
+	__le16 reserved;
+};
+
+struct cmd_ds_rf_reg_map {
+	__le16 buffersize;
+	u8 regmap[64];
+	__le16 reserved;
+};
+
+struct cmd_ds_mac_reg_access {
+	__le16 action;
+	__le16 offset;
+	__le32 value;
+};
+
+struct cmd_ds_bbp_reg_access {
+	__le16 action;
+	__le16 offset;
+	u8 value;
+	u8 reserved[3];
+};
+
+struct cmd_ds_rf_reg_access {
+	__le16 action;
+	__le16 offset;
+	u8 value;
+	u8 reserved[3];
+};
+
+struct cmd_ds_802_11_radio_control {
+	__le16 action;
+	__le16 control;
+};
+
+struct cmd_ds_802_11_beacon_control {
+	__le16 action;
+	__le16 beacon_enable;
+	__le16 beacon_period;
+};
+
+struct cmd_ds_802_11_sleep_params {
+	/* ACT_GET/ACT_SET */
+	__le16 action;
+
+	/* Sleep clock error in ppm */
+	__le16 error;
+
+	/* Wakeup offset in usec */
+	__le16 offset;
+
+	/* Clock stabilization time in usec */
+	__le16 stabletime;
+
+	/* control periodic calibration */
+	u8 calcontrol;
+
+	/* control the use of external sleep clock */
+	u8 externalsleepclk;
+
+	/* reserved field, should be set to zero */
+	__le16 reserved;
+};
+
+struct cmd_ds_802_11_inactivity_timeout {
+	/* ACT_GET/ACT_SET */
+	__le16 action;
+
+	/* Inactivity timeout in msec */
+	__le16 timeout;
+};
+
+struct cmd_ds_802_11_rf_channel {
+	struct cmd_header hdr;
+
+	__le16 action;
+	__le16 channel;
+	__le16 rftype;      /* unused */
+	__le16 reserved;    /* unused */
+	u8 channellist[32]; /* unused */
+};
+
+struct cmd_ds_802_11_rssi {
+	/* weighting factor */
+	__le16 N;
+
+	__le16 reserved_0;
+	__le16 reserved_1;
+	__le16 reserved_2;
+};
+
+struct cmd_ds_802_11_rssi_rsp {
+	__le16 SNR;
+	__le16 noisefloor;
+	__le16 avgSNR;
+	__le16 avgnoisefloor;
+};
+
+struct cmd_ds_802_11_mac_address {
+	__le16 action;
+	u8 macadd[ETH_ALEN];
+};
+
+struct cmd_ds_802_11_rf_tx_power {
+	__le16 action;
+	__le16 currentlevel;
+};
+
+struct cmd_ds_802_11_rf_antenna {
+	__le16 action;
+
+	/* Number of antennas or 0xffff(diversity) */
+	__le16 antennamode;
+
+};
+
+struct cmd_ds_802_11_monitor_mode {
+	__le16 action;
+	__le16 mode;
+};
+
+struct cmd_ds_set_boot2_ver {
+	struct cmd_header hdr;
+
+	__le16 action;
+	__le16 version;
+};
+
+struct cmd_ds_802_11_ps_mode {
+	__le16 action;
+	__le16 nullpktinterval;
+	__le16 multipledtim;
+	__le16 reserved;
+	__le16 locallisteninterval;
+};
+
+struct PS_CMD_ConfirmSleep {
+	__le16 command;
+	__le16 size;
+	__le16 seqnum;
+	__le16 result;
+
+	__le16 action;
+	__le16 reserved1;
+	__le16 multipledtim;
+	__le16 reserved;
+	__le16 locallisteninterval;
+};
+
+struct cmd_ds_802_11_data_rate {
+	struct cmd_header hdr;
+
+	__le16 action;
+	__le16 reserved;
+	u8 rates[MAX_RATES];
+};
+
+struct cmd_ds_802_11_rate_adapt_rateset {
+	__le16 action;
+	__le16 enablehwauto;
+	__le16 bitmap;
+};
+
+struct cmd_ds_802_11_ad_hoc_start {
+	u8 ssid[IW_ESSID_MAX_SIZE];
+	u8 bsstype;
+	__le16 beaconperiod;
+	u8 dtimperiod;
+	union IEEEtypes_ssparamset ssparamset;
+	union ieeetypes_phyparamset phyparamset;
+	__le16 probedelay;
+	__le16 capability;
+	u8 rates[MAX_RATES];
+	u8 tlv_memory_size_pad[100];
+} __attribute__ ((packed));
+
+struct adhoc_bssdesc {
+	u8 bssid[6];
+	u8 ssid[32];
+	u8 type;
+	__le16 beaconperiod;
+	u8 dtimperiod;
+	__le64 timestamp;
+	__le64 localtime;
+	union ieeetypes_phyparamset phyparamset;
+	union IEEEtypes_ssparamset ssparamset;
+	__le16 capability;
+	u8 rates[MAX_RATES];
+
+	/* DO NOT ADD ANY FIELDS TO THIS STRUCTURE. It is used below in the
+	 * Adhoc join command and will cause a binary layout mismatch with
+	 * the firmware
+	 */
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_ad_hoc_join {
+	struct adhoc_bssdesc bss;
+	__le16 failtimeout;
+	__le16 probedelay;
+
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_enable_rsn {
+	__le16 action;
+	__le16 enable;
+} __attribute__ ((packed));
+
+struct MrvlIEtype_keyParamSet {
+	/* type ID */
+	__le16 type;
+
+	/* length of Payload */
+	__le16 length;
+
+	/* type of key: WEP=0, TKIP=1, AES=2 */
+	__le16 keytypeid;
+
+	/* key control Info specific to a keytypeid */
+	__le16 keyinfo;
+
+	/* length of key */
+	__le16 keylen;
+
+	/* key material of size keylen */
+	u8 key[32];
+};
+
+struct cmd_ds_802_11_key_material {
+	__le16 action;
+	struct MrvlIEtype_keyParamSet keyParamSet[2];
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_eeprom_access {
+	__le16 action;
+
+	/* multiple 4 */
+	__le16 offset;
+	__le16 bytecount;
+	u8 value;
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_tpc_cfg {
+	__le16 action;
+	u8 enable;
+	s8 P0;
+	s8 P1;
+	s8 P2;
+	u8 usesnr;
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_led_ctrl {
+	__le16 action;
+	__le16 numled;
+	u8 data[256];
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_pwr_cfg {
+	__le16 action;
+	u8 enable;
+	s8 PA_P0;
+	s8 PA_P1;
+	s8 PA_P2;
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_afc {
+	__le16 afc_auto;
+	union {
+		struct {
+			__le16 threshold;
+			__le16 period;
+		};
+		struct {
+			__le16 timing_offset; /* signed */
+			__le16 carrier_offset; /* signed */
+		};
+	};
+} __attribute__ ((packed));
+
+struct cmd_tx_rate_query {
+	__le16 txrate;
+} __attribute__ ((packed));
+
+struct cmd_ds_get_tsf {
+	__le64 tsfvalue;
+} __attribute__ ((packed));
+
+struct cmd_ds_bt_access {
+	__le16 action;
+	__le32 id;
+	u8 addr1[ETH_ALEN];
+	u8 addr2[ETH_ALEN];
+} __attribute__ ((packed));
+
+struct cmd_ds_fwt_access {
+	__le16 action;
+	__le32 id;
+	u8 valid;
+	u8 da[ETH_ALEN];
+	u8 dir;
+	u8 ra[ETH_ALEN];
+	__le32 ssn;
+	__le32 dsn;
+	__le32 metric;
+	u8 rate;
+	u8 hopcount;
+	u8 ttl;
+	__le32 expiration;
+	u8 sleepmode;
+	__le32 snr;
+	__le32 references;
+	u8 prec[ETH_ALEN];
+} __attribute__ ((packed));
+
+
+struct cmd_ds_mesh_config {
+	struct cmd_header hdr;
+
+        __le16 action;
+        __le16 channel;
+        __le16 type;
+        __le16 length;
+        u8 data[128];   /* last position reserved */
+} __attribute__ ((packed));
+
+
+struct cmd_ds_mesh_access {
+	struct cmd_header hdr;
+
+	__le16 action;
+	__le32 data[32];	/* last position reserved */
+} __attribute__ ((packed));
+
+/* Number of stats counters returned by the firmware */
+#define MESH_STATS_NUM 8
+
+struct cmd_ds_command {
+	/* command header */
+	__le16 command;
+	__le16 size;
+	__le16 seqnum;
+	__le16 result;
+
+	/* command Body */
+	union {
+		struct cmd_ds_802_11_ps_mode psmode;
+		struct cmd_ds_802_11_scan scan;
+		struct cmd_ds_802_11_scan_rsp scanresp;
+		struct cmd_ds_mac_control macctrl;
+		struct cmd_ds_802_11_associate associate;
+		struct cmd_ds_802_11_deauthenticate deauth;
+		struct cmd_ds_802_11_set_wep wep;
+		struct cmd_ds_802_11_ad_hoc_start ads;
+		struct cmd_ds_802_11_reset reset;
+		struct cmd_ds_802_11_ad_hoc_result result;
+		struct cmd_ds_802_11_get_log glog;
+		struct cmd_ds_802_11_authenticate auth;
+		struct cmd_ds_802_11_get_stat gstat;
+		struct cmd_ds_802_3_get_stat gstat_8023;
+		struct cmd_ds_802_11_snmp_mib smib;
+		struct cmd_ds_802_11_rf_tx_power txp;
+		struct cmd_ds_802_11_rf_antenna rant;
+		struct cmd_ds_802_11_monitor_mode monitor;
+		struct cmd_ds_802_11_rate_adapt_rateset rateset;
+		struct cmd_ds_mac_multicast_adr madr;
+		struct cmd_ds_802_11_ad_hoc_join adj;
+		struct cmd_ds_802_11_radio_control radio;
+		struct cmd_ds_802_11_rf_channel rfchannel;
+		struct cmd_ds_802_11_rssi rssi;
+		struct cmd_ds_802_11_rssi_rsp rssirsp;
+		struct cmd_ds_802_11_disassociate dassociate;
+		struct cmd_ds_802_11_mac_address macadd;
+		struct cmd_ds_802_11_enable_rsn enbrsn;
+		struct cmd_ds_802_11_key_material keymaterial;
+		struct cmd_ds_mac_reg_access macreg;
+		struct cmd_ds_bbp_reg_access bbpreg;
+		struct cmd_ds_rf_reg_access rfreg;
+		struct cmd_ds_802_11_eeprom_access rdeeprom;
+
+		struct cmd_ds_802_11d_domain_info domaininfo;
+		struct cmd_ds_802_11d_domain_info domaininforesp;
+
+		struct cmd_ds_802_11_sleep_params sleep_params;
+		struct cmd_ds_802_11_inactivity_timeout inactivity_timeout;
+		struct cmd_ds_802_11_tpc_cfg tpccfg;
+		struct cmd_ds_802_11_pwr_cfg pwrcfg;
+		struct cmd_ds_802_11_afc afc;
+		struct cmd_ds_802_11_led_ctrl ledgpio;
+
+		struct cmd_tx_rate_query txrate;
+		struct cmd_ds_bt_access bt;
+		struct cmd_ds_fwt_access fwt;
+		struct cmd_ds_get_tsf gettsf;
+		struct cmd_ds_802_11_subscribe_event subscribe_event;
+		struct cmd_ds_802_11_beacon_control bcn_ctrl;
+	} params;
+} __attribute__ ((packed));
+
+#endif

+ 973 - 0
package/libertas/src/if_cs.c

@@ -0,0 +1,973 @@
+/*
+
+  Driver for the Marvell 8385 based compact flash WLAN cards.
+
+  (C) 2007 by Holger Schurig <[email protected]>
+
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; see the file COPYING.  If not, write to
+  the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
+  Boston, MA 02110-1301, USA.
+
+*/
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <linux/netdevice.h>
+
+#include <pcmcia/cs_types.h>
+#include <pcmcia/cs.h>
+#include <pcmcia/cistpl.h>
+#include <pcmcia/ds.h>
+
+#include <linux/io.h>
+
+#define DRV_NAME "libertas_cs"
+
+#include "decl.h"
+#include "defs.h"
+#include "dev.h"
+
+
+/********************************************************************/
+/* Module stuff                                                     */
+/********************************************************************/
+
+MODULE_AUTHOR("Holger Schurig <[email protected]>");
+MODULE_DESCRIPTION("Driver for Marvell 83xx compact flash WLAN cards");
+MODULE_LICENSE("GPL");
+
+
+
+/********************************************************************/
+/* Data structures                                                  */
+/********************************************************************/
+
+struct if_cs_card {
+	struct pcmcia_device *p_dev;
+	struct lbs_private *priv;
+	void __iomem *iobase;
+};
+
+
+
+/********************************************************************/
+/* Hardware access                                                  */
+/********************************************************************/
+
+/* This define enables wrapper functions which allow you
+   to dump all register accesses. You normally won't this,
+   except for development */
+/* #define DEBUG_IO */
+
+#ifdef DEBUG_IO
+static int debug_output = 0;
+#else
+/* This way the compiler optimizes the printk's away */
+#define debug_output 0
+#endif
+
+static inline unsigned int if_cs_read8(struct if_cs_card *card, uint reg)
+{
+	unsigned int val = ioread8(card->iobase + reg);
+	if (debug_output)
+		printk(KERN_INFO "##inb %08x<%02x\n", reg, val);
+	return val;
+}
+static inline unsigned int if_cs_read16(struct if_cs_card *card, uint reg)
+{
+	unsigned int val = ioread16(card->iobase + reg);
+	if (debug_output)
+		printk(KERN_INFO "##inw %08x<%04x\n", reg, val);
+	return val;
+}
+static inline void if_cs_read16_rep(
+	struct if_cs_card *card,
+	uint reg,
+	void *buf,
+	unsigned long count)
+{
+	if (debug_output)
+		printk(KERN_INFO "##insw %08x<(0x%lx words)\n",
+			reg, count);
+	ioread16_rep(card->iobase + reg, buf, count);
+}
+
+static inline void if_cs_write8(struct if_cs_card *card, uint reg, u8 val)
+{
+	if (debug_output)
+		printk(KERN_INFO "##outb %08x>%02x\n", reg, val);
+	iowrite8(val, card->iobase + reg);
+}
+
+static inline void if_cs_write16(struct if_cs_card *card, uint reg, u16 val)
+{
+	if (debug_output)
+		printk(KERN_INFO "##outw %08x>%04x\n", reg, val);
+	iowrite16(val, card->iobase + reg);
+}
+
+static inline void if_cs_write16_rep(
+	struct if_cs_card *card,
+	uint reg,
+	void *buf,
+	unsigned long count)
+{
+	if (debug_output)
+		printk(KERN_INFO "##outsw %08x>(0x%lx words)\n",
+			reg, count);
+	iowrite16_rep(card->iobase + reg, buf, count);
+}
+
+
+/*
+ * I know that polling/delaying is frowned upon. However, this procedure
+ * with polling is needed while downloading the firmware. At this stage,
+ * the hardware does unfortunately not create any interrupts.
+ *
+ * Fortunately, this function is never used once the firmware is in
+ * the card. :-)
+ *
+ * As a reference, see the "Firmware Specification v5.1", page 18
+ * and 19. I did not follow their suggested timing to the word,
+ * but this works nice & fast anyway.
+ */
+static int if_cs_poll_while_fw_download(struct if_cs_card *card, uint addr, u8 reg)
+{
+	int i;
+
+	for (i = 0; i < 1000; i++) {
+		u8 val = if_cs_read8(card, addr);
+		if (val == reg)
+			return i;
+		udelay(500);
+	}
+	return -ETIME;
+}
+
+
+
+/* Host control registers and their bit definitions */
+
+#define IF_CS_H_STATUS			0x00000000
+#define IF_CS_H_STATUS_TX_OVER		0x0001
+#define IF_CS_H_STATUS_RX_OVER		0x0002
+#define IF_CS_H_STATUS_DNLD_OVER	0x0004
+
+#define IF_CS_H_INT_CAUSE		0x00000002
+#define IF_CS_H_IC_TX_OVER		0x0001
+#define IF_CS_H_IC_RX_OVER		0x0002
+#define IF_CS_H_IC_DNLD_OVER		0x0004
+#define IF_CS_H_IC_POWER_DOWN		0x0008
+#define IF_CS_H_IC_HOST_EVENT		0x0010
+#define IF_CS_H_IC_MASK			0x001f
+
+#define IF_CS_H_INT_MASK		0x00000004
+#define	IF_CS_H_IM_MASK			0x001f
+
+#define IF_CS_H_WRITE_LEN		0x00000014
+
+#define IF_CS_H_WRITE			0x00000016
+
+#define IF_CS_H_CMD_LEN			0x00000018
+
+#define IF_CS_H_CMD			0x0000001A
+
+#define IF_CS_C_READ_LEN		0x00000024
+
+#define IF_CS_H_READ			0x00000010
+
+/* Card control registers and their bit definitions */
+
+#define IF_CS_C_STATUS			0x00000020
+#define IF_CS_C_S_TX_DNLD_RDY		0x0001
+#define IF_CS_C_S_RX_UPLD_RDY		0x0002
+#define IF_CS_C_S_CMD_DNLD_RDY		0x0004
+#define IF_CS_C_S_CMD_UPLD_RDY		0x0008
+#define IF_CS_C_S_CARDEVENT		0x0010
+#define IF_CS_C_S_MASK			0x001f
+#define IF_CS_C_S_STATUS_MASK		0x7f00
+/* The following definitions should be the same as the MRVDRV_ ones */
+
+#if MRVDRV_CMD_DNLD_RDY != IF_CS_C_S_CMD_DNLD_RDY
+#error MRVDRV_CMD_DNLD_RDY and IF_CS_C_S_CMD_DNLD_RDY not in sync
+#endif
+#if MRVDRV_CMD_UPLD_RDY != IF_CS_C_S_CMD_UPLD_RDY
+#error MRVDRV_CMD_UPLD_RDY and IF_CS_C_S_CMD_UPLD_RDY not in sync
+#endif
+#if MRVDRV_CARDEVENT != IF_CS_C_S_CARDEVENT
+#error MRVDRV_CARDEVENT and IF_CS_C_S_CARDEVENT not in sync
+#endif
+
+#define IF_CS_C_INT_CAUSE		0x00000022
+#define	IF_CS_C_IC_MASK			0x001f
+
+#define IF_CS_C_SQ_READ_LOW		0x00000028
+#define IF_CS_C_SQ_HELPER_OK		0x10
+
+#define IF_CS_C_CMD_LEN			0x00000030
+
+#define IF_CS_C_CMD			0x00000012
+
+#define IF_CS_SCRATCH			0x0000003F
+
+
+
+/********************************************************************/
+/* Interrupts                                                       */
+/********************************************************************/
+
+static inline void if_cs_enable_ints(struct if_cs_card *card)
+{
+	lbs_deb_enter(LBS_DEB_CS);
+	if_cs_write16(card, IF_CS_H_INT_MASK, 0);
+}
+
+static inline void if_cs_disable_ints(struct if_cs_card *card)
+{
+	lbs_deb_enter(LBS_DEB_CS);
+	if_cs_write16(card, IF_CS_H_INT_MASK, IF_CS_H_IM_MASK);
+}
+
+static irqreturn_t if_cs_interrupt(int irq, void *data)
+{
+	struct if_cs_card *card = data;
+	u16 int_cause;
+
+	lbs_deb_enter(LBS_DEB_CS);
+
+	int_cause = if_cs_read16(card, IF_CS_C_INT_CAUSE);
+	if(int_cause == 0x0) {
+		/* Not for us */
+		return IRQ_NONE;
+
+	} else if (int_cause == 0xffff) {
+		/* Read in junk, the card has probably been removed */
+		card->priv->surpriseremoved = 1;
+
+	} else {
+		if (int_cause & IF_CS_H_IC_TX_OVER)
+			lbs_host_to_card_done(card->priv);
+
+		/* clear interrupt */
+		if_cs_write16(card, IF_CS_C_INT_CAUSE, int_cause & IF_CS_C_IC_MASK);
+	}
+	spin_lock(&card->priv->driver_lock);
+	lbs_interrupt(card->priv);
+	spin_unlock(&card->priv->driver_lock);
+
+	return IRQ_HANDLED;
+}
+
+
+
+
+/********************************************************************/
+/* I/O                                                              */
+/********************************************************************/
+
+/*
+ * Called from if_cs_host_to_card to send a command to the hardware
+ */
+static int if_cs_send_cmd(struct lbs_private *priv, u8 *buf, u16 nb)
+{
+	struct if_cs_card *card = (struct if_cs_card *)priv->card;
+	int ret = -1;
+	int loops = 0;
+
+	lbs_deb_enter(LBS_DEB_CS);
+
+	/* Is hardware ready? */
+	while (1) {
+		u16 val = if_cs_read16(card, IF_CS_C_STATUS);
+		if (val & IF_CS_C_S_CMD_DNLD_RDY)
+			break;
+		if (++loops > 100) {
+			lbs_pr_err("card not ready for commands\n");
+			goto done;
+		}
+		mdelay(1);
+	}
+
+	if_cs_write16(card, IF_CS_H_CMD_LEN, nb);
+
+	if_cs_write16_rep(card, IF_CS_H_CMD, buf, nb / 2);
+	/* Are we supposed to transfer an odd amount of bytes? */
+	if (nb & 1)
+		if_cs_write8(card, IF_CS_H_CMD, buf[nb-1]);
+
+	/* "Assert the download over interrupt command in the Host
+	 * status register" */
+	if_cs_write16(card, IF_CS_H_STATUS, IF_CS_H_STATUS_DNLD_OVER);
+
+	/* "Assert the download over interrupt command in the Card
+	 * interrupt case register" */
+	if_cs_write16(card, IF_CS_H_INT_CAUSE, IF_CS_H_IC_DNLD_OVER);
+	ret = 0;
+
+done:
+	lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret);
+	return ret;
+}
+
+
+/*
+ * Called from if_cs_host_to_card to send a data to the hardware
+ */
+static void if_cs_send_data(struct lbs_private *priv, u8 *buf, u16 nb)
+{
+	struct if_cs_card *card = (struct if_cs_card *)priv->card;
+
+	lbs_deb_enter(LBS_DEB_CS);
+
+	if_cs_write16(card, IF_CS_H_WRITE_LEN, nb);
+
+	/* write even number of bytes, then odd byte if necessary */
+	if_cs_write16_rep(card, IF_CS_H_WRITE, buf, nb / 2);
+	if (nb & 1)
+		if_cs_write8(card, IF_CS_H_WRITE, buf[nb-1]);
+
+	if_cs_write16(card, IF_CS_H_STATUS, IF_CS_H_STATUS_TX_OVER);
+	if_cs_write16(card, IF_CS_H_INT_CAUSE, IF_CS_H_STATUS_TX_OVER);
+
+	lbs_deb_leave(LBS_DEB_CS);
+}
+
+
+/*
+ * Get the command result out of the card.
+ */
+static int if_cs_receive_cmdres(struct lbs_private *priv, u8 *data, u32 *len)
+{
+	int ret = -1;
+	u16 val;
+
+	lbs_deb_enter(LBS_DEB_CS);
+
+	/* is hardware ready? */
+	val = if_cs_read16(priv->card, IF_CS_C_STATUS);
+	if ((val & IF_CS_C_S_CMD_UPLD_RDY) == 0) {
+		lbs_pr_err("card not ready for CMD\n");
+		goto out;
+	}
+
+	*len = if_cs_read16(priv->card, IF_CS_C_CMD_LEN);
+	if ((*len == 0) || (*len > LBS_CMD_BUFFER_SIZE)) {
+		lbs_pr_err("card cmd buffer has invalid # of bytes (%d)\n", *len);
+		goto out;
+	}
+
+	/* read even number of bytes, then odd byte if necessary */
+	if_cs_read16_rep(priv->card, IF_CS_C_CMD, data, *len/sizeof(u16));
+	if (*len & 1)
+		data[*len-1] = if_cs_read8(priv->card, IF_CS_C_CMD);
+
+	/* This is a workaround for a firmware that reports too much
+	 * bytes */
+	*len -= 8;
+	ret = 0;
+out:
+	lbs_deb_leave_args(LBS_DEB_CS, "ret %d, len %d", ret, *len);
+	return ret;
+}
+
+
+static struct sk_buff *if_cs_receive_data(struct lbs_private *priv)
+{
+	struct sk_buff *skb = NULL;
+	u16 len;
+	u8 *data;
+
+	lbs_deb_enter(LBS_DEB_CS);
+
+	len = if_cs_read16(priv->card, IF_CS_C_READ_LEN);
+	if (len == 0 || len > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) {
+		lbs_pr_err("card data buffer has invalid # of bytes (%d)\n", len);
+		priv->stats.rx_dropped++;
+		printk(KERN_INFO "##HS %s:%d TODO\n", __FUNCTION__, __LINE__);
+		goto dat_err;
+	}
+
+	//TODO: skb = dev_alloc_skb(len+ETH_FRAME_LEN+MRVDRV_SNAP_HEADER_LEN+EXTRA_LEN);
+	skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + 2);
+	if (!skb)
+		goto out;
+	skb_put(skb, len);
+	skb_reserve(skb, 2);/* 16 byte align */
+	data = skb->data;
+
+	/* read even number of bytes, then odd byte if necessary */
+	if_cs_read16_rep(priv->card, IF_CS_H_READ, data, len/sizeof(u16));
+	if (len & 1)
+		data[len-1] = if_cs_read8(priv->card, IF_CS_H_READ);
+
+dat_err:
+	if_cs_write16(priv->card, IF_CS_H_STATUS, IF_CS_H_STATUS_RX_OVER);
+	if_cs_write16(priv->card, IF_CS_H_INT_CAUSE, IF_CS_H_IC_RX_OVER);
+
+out:
+	lbs_deb_leave_args(LBS_DEB_CS, "ret %p", skb);
+	return skb;
+}
+
+
+
+/********************************************************************/
+/* Firmware                                                         */
+/********************************************************************/
+
+/*
+ * Tries to program the helper firmware.
+ *
+ * Return 0 on success
+ */
+static int if_cs_prog_helper(struct if_cs_card *card)
+{
+	int ret = 0;
+	int sent = 0;
+	u8  scratch;
+	const struct firmware *fw;
+
+	lbs_deb_enter(LBS_DEB_CS);
+
+	scratch = if_cs_read8(card, IF_CS_SCRATCH);
+
+	/* "If the value is 0x5a, the firmware is already
+	 * downloaded successfully"
+	 */
+	if (scratch == 0x5a)
+		goto done;
+
+	/* "If the value is != 00, it is invalid value of register */
+	if (scratch != 0x00) {
+		ret = -ENODEV;
+		goto done;
+	}
+
+	/* TODO: make firmware file configurable */
+	ret = request_firmware(&fw, "libertas_cs_helper.fw",
+		&handle_to_dev(card->p_dev));
+	if (ret) {
+		lbs_pr_err("can't load helper firmware\n");
+		ret = -ENODEV;
+		goto done;
+	}
+	lbs_deb_cs("helper size %td\n", fw->size);
+
+	/* "Set the 5 bytes of the helper image to 0" */
+	/* Not needed, this contains an ARM branch instruction */
+
+	for (;;) {
+		/* "the number of bytes to send is 256" */
+		int count = 256;
+		int remain = fw->size - sent;
+
+		if (remain < count)
+			count = remain;
+		/* printk(KERN_INFO "//HS %d loading %d of %d bytes\n",
+			__LINE__, sent, fw->size); */
+
+		/* "write the number of bytes to be sent to the I/O Command
+		 * write length register" */
+		if_cs_write16(card, IF_CS_H_CMD_LEN, count);
+
+		/* "write this to I/O Command port register as 16 bit writes */
+		if (count)
+			if_cs_write16_rep(card, IF_CS_H_CMD,
+				&fw->data[sent],
+				count >> 1);
+
+		/* "Assert the download over interrupt command in the Host
+		 * status register" */
+		if_cs_write8(card, IF_CS_H_STATUS, IF_CS_H_STATUS_DNLD_OVER);
+
+		/* "Assert the download over interrupt command in the Card
+		 * interrupt case register" */
+		if_cs_write16(card, IF_CS_H_INT_CAUSE, IF_CS_H_IC_DNLD_OVER);
+
+		/* "The host polls the Card Status register ... for 50 ms before
+		   declaring a failure */
+		ret = if_cs_poll_while_fw_download(card, IF_CS_C_STATUS,
+			IF_CS_C_S_CMD_DNLD_RDY);
+		if (ret < 0) {
+			lbs_pr_err("can't download helper at 0x%x, ret %d\n",
+				sent, ret);
+			goto done;
+		}
+
+		if (count == 0)
+			break;
+
+		sent += count;
+	}
+
+	release_firmware(fw);
+	ret = 0;
+
+done:
+	lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret);
+	return ret;
+}
+
+
+static int if_cs_prog_real(struct if_cs_card *card)
+{
+	const struct firmware *fw;
+	int ret = 0;
+	int retry = 0;
+	int len = 0;
+	int sent;
+
+	lbs_deb_enter(LBS_DEB_CS);
+
+	/* TODO: make firmware file configurable */
+	ret = request_firmware(&fw, "libertas_cs.fw",
+		&handle_to_dev(card->p_dev));
+	if (ret) {
+		lbs_pr_err("can't load firmware\n");
+		ret = -ENODEV;
+		goto done;
+	}
+	lbs_deb_cs("fw size %td\n", fw->size);
+
+	ret = if_cs_poll_while_fw_download(card, IF_CS_C_SQ_READ_LOW, IF_CS_C_SQ_HELPER_OK);
+	if (ret < 0) {
+		int i;
+		lbs_pr_err("helper firmware doesn't answer\n");
+		for (i = 0; i < 0x50; i += 2)
+			printk(KERN_INFO "## HS %02x: %04x\n",
+				i, if_cs_read16(card, i));
+		goto err_release;
+	}
+
+	for (sent = 0; sent < fw->size; sent += len) {
+		len = if_cs_read16(card, IF_CS_C_SQ_READ_LOW);
+		/* printk(KERN_INFO "//HS %d loading %d of %d bytes\n",
+			__LINE__, sent, fw->size); */
+		if (len & 1) {
+			retry++;
+			lbs_pr_info("odd, need to retry this firmware block\n");
+		} else {
+			retry = 0;
+		}
+
+		if (retry > 20) {
+			lbs_pr_err("could not download firmware\n");
+			ret = -ENODEV;
+			goto err_release;
+		}
+		if (retry) {
+			sent -= len;
+		}
+
+
+		if_cs_write16(card, IF_CS_H_CMD_LEN, len);
+
+		if_cs_write16_rep(card, IF_CS_H_CMD,
+			&fw->data[sent],
+			(len+1) >> 1);
+		if_cs_write8(card, IF_CS_H_STATUS, IF_CS_H_STATUS_DNLD_OVER);
+		if_cs_write16(card, IF_CS_H_INT_CAUSE, IF_CS_H_IC_DNLD_OVER);
+
+		ret = if_cs_poll_while_fw_download(card, IF_CS_C_STATUS,
+			IF_CS_C_S_CMD_DNLD_RDY);
+		if (ret < 0) {
+			lbs_pr_err("can't download firmware at 0x%x\n", sent);
+			goto err_release;
+		}
+	}
+
+	ret = if_cs_poll_while_fw_download(card, IF_CS_SCRATCH, 0x5a);
+	if (ret < 0) {
+		lbs_pr_err("firmware download failed\n");
+		goto err_release;
+	}
+
+	ret = 0;
+	goto done;
+
+
+err_release:
+	release_firmware(fw);
+
+done:
+	lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret);
+	return ret;
+}
+
+
+
+/********************************************************************/
+/* Callback functions for libertas.ko                               */
+/********************************************************************/
+
+/* Send commands or data packets to the card */
+static int if_cs_host_to_card(struct lbs_private *priv,
+	u8 type,
+	u8 *buf,
+	u16 nb)
+{
+	int ret = -1;
+
+	lbs_deb_enter_args(LBS_DEB_CS, "type %d, bytes %d", type, nb);
+
+	switch (type) {
+	case MVMS_DAT:
+		priv->dnld_sent = DNLD_DATA_SENT;
+		if_cs_send_data(priv, buf, nb);
+		ret = 0;
+		break;
+	case MVMS_CMD:
+		priv->dnld_sent = DNLD_CMD_SENT;
+		ret = if_cs_send_cmd(priv, buf, nb);
+		break;
+	default:
+		lbs_pr_err("%s: unsupported type %d\n", __FUNCTION__, type);
+	}
+
+	lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret);
+	return ret;
+}
+
+
+static int if_cs_get_int_status(struct lbs_private *priv, u8 *ireg)
+{
+	struct if_cs_card *card = (struct if_cs_card *)priv->card;
+	int ret = 0;
+	u16 int_cause;
+	u8 *cmdbuf;
+	*ireg = 0;
+
+	lbs_deb_enter(LBS_DEB_CS);
+
+	if (priv->surpriseremoved)
+		goto out;
+
+	int_cause = if_cs_read16(card, IF_CS_C_INT_CAUSE) & IF_CS_C_IC_MASK;
+	if_cs_write16(card, IF_CS_C_INT_CAUSE, int_cause);
+
+	*ireg = if_cs_read16(card, IF_CS_C_STATUS) & IF_CS_C_S_MASK;
+
+	if (!*ireg)
+		goto sbi_get_int_status_exit;
+
+sbi_get_int_status_exit:
+
+	/* is there a data packet for us? */
+	if (*ireg & IF_CS_C_S_RX_UPLD_RDY) {
+		struct sk_buff *skb = if_cs_receive_data(priv);
+		lbs_process_rxed_packet(priv, skb);
+		*ireg &= ~IF_CS_C_S_RX_UPLD_RDY;
+	}
+
+	if (*ireg & IF_CS_C_S_TX_DNLD_RDY) {
+		priv->dnld_sent = DNLD_RES_RECEIVED;
+	}
+
+	/* Card has a command result for us */
+	if (*ireg & IF_CS_C_S_CMD_UPLD_RDY) {
+		spin_lock(&priv->driver_lock);
+		if (!priv->cur_cmd) {
+			cmdbuf = priv->upld_buf;
+			priv->hisregcpy &= ~IF_CS_C_S_RX_UPLD_RDY;
+		} else {
+			cmdbuf = (u8 *) priv->cur_cmd->cmdbuf;
+		}
+
+		ret = if_cs_receive_cmdres(priv, cmdbuf, &priv->upld_len);
+		spin_unlock(&priv->driver_lock);
+		if (ret < 0)
+			lbs_pr_err("could not receive cmd from card\n");
+	}
+
+out:
+	lbs_deb_leave_args(LBS_DEB_CS, "ret %d, ireg 0x%x, hisregcpy 0x%x", ret, *ireg, priv->hisregcpy);
+	return ret;
+}
+
+
+static int if_cs_read_event_cause(struct lbs_private *priv)
+{
+	lbs_deb_enter(LBS_DEB_CS);
+
+	priv->eventcause = (if_cs_read16(priv->card, IF_CS_C_STATUS) & IF_CS_C_S_STATUS_MASK) >> 5;
+	if_cs_write16(priv->card, IF_CS_H_INT_CAUSE, IF_CS_H_IC_HOST_EVENT);
+
+	return 0;
+}
+
+
+
+/********************************************************************/
+/* Card Services                                                    */
+/********************************************************************/
+
+/*
+ * After a card is removed, if_cs_release() will unregister the
+ * device, and release the PCMCIA configuration.  If the device is
+ * still open, this will be postponed until it is closed.
+ */
+static void if_cs_release(struct pcmcia_device *p_dev)
+{
+	struct if_cs_card *card = p_dev->priv;
+
+	lbs_deb_enter(LBS_DEB_CS);
+
+	pcmcia_disable_device(p_dev);
+	free_irq(p_dev->irq.AssignedIRQ, card);
+	if (card->iobase)
+		ioport_unmap(card->iobase);
+
+	lbs_deb_leave(LBS_DEB_CS);
+}
+
+
+/*
+ * This creates an "instance" of the driver, allocating local data
+ * structures for one device.  The device is registered with Card
+ * Services.
+ *
+ * The dev_link structure is initialized, but we don't actually
+ * configure the card at this point -- we wait until we receive a card
+ * insertion event.
+ */
+static int if_cs_probe(struct pcmcia_device *p_dev)
+{
+	int ret = -ENOMEM;
+	struct lbs_private *priv;
+	struct if_cs_card *card;
+	/* CIS parsing */
+	tuple_t tuple;
+	cisparse_t parse;
+	cistpl_cftable_entry_t *cfg = &parse.cftable_entry;
+	cistpl_io_t *io = &cfg->io;
+	u_char buf[64];
+
+	lbs_deb_enter(LBS_DEB_CS);
+
+	card = kzalloc(sizeof(struct if_cs_card), GFP_KERNEL);
+	if (!card) {
+		lbs_pr_err("error in kzalloc\n");
+		goto out;
+	}
+	card->p_dev = p_dev;
+	p_dev->priv = card;
+
+	p_dev->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
+	p_dev->irq.Handler = NULL;
+	p_dev->irq.IRQInfo1 = IRQ_INFO2_VALID | IRQ_LEVEL_ID;
+
+	p_dev->conf.Attributes = 0;
+	p_dev->conf.IntType = INT_MEMORY_AND_IO;
+
+	tuple.Attributes = 0;
+	tuple.TupleData = buf;
+	tuple.TupleDataMax = sizeof(buf);
+	tuple.TupleOffset = 0;
+
+	tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
+	if ((ret = pcmcia_get_first_tuple(p_dev, &tuple)) != 0 ||
+	    (ret = pcmcia_get_tuple_data(p_dev, &tuple)) != 0 ||
+	    (ret = pcmcia_parse_tuple(p_dev, &tuple, &parse)) != 0)
+	{
+		lbs_pr_err("error in pcmcia_get_first_tuple etc\n");
+		goto out1;
+	}
+
+	p_dev->conf.ConfigIndex = cfg->index;
+
+	/* Do we need to allocate an interrupt? */
+	if (cfg->irq.IRQInfo1) {
+		p_dev->conf.Attributes |= CONF_ENABLE_IRQ;
+	}
+
+	/* IO window settings */
+	if (cfg->io.nwin != 1) {
+		lbs_pr_err("wrong CIS (check number of IO windows)\n");
+		ret = -ENODEV;
+		goto out1;
+	}
+	p_dev->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
+	p_dev->io.BasePort1 = io->win[0].base;
+	p_dev->io.NumPorts1 = io->win[0].len;
+
+	/* This reserves IO space but doesn't actually enable it */
+	ret = pcmcia_request_io(p_dev, &p_dev->io);
+	if (ret) {
+		lbs_pr_err("error in pcmcia_request_io\n");
+		goto out1;
+	}
+
+	/*
+	 * Allocate an interrupt line.  Note that this does not assign
+	 * a handler to the interrupt, unless the 'Handler' member of
+	 * the irq structure is initialized.
+	 */
+	if (p_dev->conf.Attributes & CONF_ENABLE_IRQ) {
+		ret = pcmcia_request_irq(p_dev, &p_dev->irq);
+		if (ret) {
+			lbs_pr_err("error in pcmcia_request_irq\n");
+			goto out1;
+		}
+	}
+
+	/* Initialize io access */
+	card->iobase = ioport_map(p_dev->io.BasePort1, p_dev->io.NumPorts1);
+	if (!card->iobase) {
+		lbs_pr_err("error in ioport_map\n");
+		ret = -EIO;
+		goto out1;
+	}
+
+	/*
+	 * This actually configures the PCMCIA socket -- setting up
+	 * the I/O windows and the interrupt mapping, and putting the
+	 * card and host interface into "Memory and IO" mode.
+	 */
+	ret = pcmcia_request_configuration(p_dev, &p_dev->conf);
+	if (ret) {
+		lbs_pr_err("error in pcmcia_request_configuration\n");
+		goto out2;
+	}
+
+	/* Finally, report what we've done */
+	lbs_deb_cs("irq %d, io 0x%04x-0x%04x\n",
+	       p_dev->irq.AssignedIRQ, p_dev->io.BasePort1,
+	       p_dev->io.BasePort1 + p_dev->io.NumPorts1 - 1);
+
+
+	/* Load the firmware early, before calling into libertas.ko */
+	ret = if_cs_prog_helper(card);
+	if (ret == 0)
+		ret = if_cs_prog_real(card);
+	if (ret)
+		goto out2;
+
+	/* Make this card known to the libertas driver */
+	priv = lbs_add_card(card, &p_dev->dev);
+	if (!priv) {
+		ret = -ENOMEM;
+		goto out2;
+	}
+
+	/* Store pointers to our call-back functions */
+	card->priv = priv;
+	priv->card = card;
+	priv->hw_host_to_card     = if_cs_host_to_card;
+	priv->hw_get_int_status   = if_cs_get_int_status;
+	priv->hw_read_event_cause = if_cs_read_event_cause;
+
+	priv->fw_ready = 1;
+
+	/* Now actually get the IRQ */
+	ret = request_irq(p_dev->irq.AssignedIRQ, if_cs_interrupt,
+		IRQF_SHARED, DRV_NAME, card);
+	if (ret) {
+		lbs_pr_err("error in request_irq\n");
+		goto out3;
+	}
+
+	/* Clear any interrupt cause that happend while sending
+	 * firmware/initializing card */
+	if_cs_write16(card, IF_CS_C_INT_CAUSE, IF_CS_C_IC_MASK);
+	if_cs_enable_ints(card);
+
+	/* And finally bring the card up */
+	if (lbs_start_card(priv) != 0) {
+		lbs_pr_err("could not activate card\n");
+		goto out3;
+	}
+
+	ret = 0;
+	goto out;
+
+out3:
+	lbs_remove_card(priv);
+out2:
+	ioport_unmap(card->iobase);
+out1:
+	pcmcia_disable_device(p_dev);
+out:
+	lbs_deb_leave_args(LBS_DEB_CS, "ret %d", ret);
+	return ret;
+}
+
+
+/*
+ * This deletes a driver "instance".  The device is de-registered with
+ * Card Services.  If it has been released, all local data structures
+ * are freed.  Otherwise, the structures will be freed when the device
+ * is released.
+ */
+static void if_cs_detach(struct pcmcia_device *p_dev)
+{
+	struct if_cs_card *card = p_dev->priv;
+
+	lbs_deb_enter(LBS_DEB_CS);
+
+	lbs_stop_card(card->priv);
+	lbs_remove_card(card->priv);
+	if_cs_disable_ints(card);
+	if_cs_release(p_dev);
+	kfree(card);
+
+	lbs_deb_leave(LBS_DEB_CS);
+}
+
+
+
+/********************************************************************/
+/* Module initialization                                            */
+/********************************************************************/
+
+static struct pcmcia_device_id if_cs_ids[] = {
+	PCMCIA_DEVICE_MANF_CARD(0x02df, 0x8103),
+	PCMCIA_DEVICE_NULL,
+};
+MODULE_DEVICE_TABLE(pcmcia, if_cs_ids);
+
+
+static struct pcmcia_driver lbs_driver = {
+	.owner		= THIS_MODULE,
+	.drv		= {
+		.name	= DRV_NAME,
+	},
+	.probe		= if_cs_probe,
+	.remove		= if_cs_detach,
+	.id_table       = if_cs_ids,
+};
+
+
+static int __init if_cs_init(void)
+{
+	int ret;
+
+	lbs_deb_enter(LBS_DEB_CS);
+	ret = pcmcia_register_driver(&lbs_driver);
+	lbs_deb_leave(LBS_DEB_CS);
+	return ret;
+}
+
+
+static void __exit if_cs_exit(void)
+{
+	lbs_deb_enter(LBS_DEB_CS);
+	pcmcia_unregister_driver(&lbs_driver);
+	lbs_deb_leave(LBS_DEB_CS);
+}
+
+
+module_init(if_cs_init);
+module_exit(if_cs_exit);

+ 1079 - 0
package/libertas/src/if_sdio.c

@@ -0,0 +1,1079 @@
+/*
+ *  linux/drivers/net/wireless/libertas/if_sdio.c
+ *
+ *  Copyright 2007 Pierre Ossman
+ *
+ * Inspired by if_cs.c, Copyright 2007 Holger Schurig
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This hardware has more or less no CMD53 support, so all registers
+ * must be accessed using sdio_readb()/sdio_writeb().
+ *
+ * Transfers must be in one transaction or the firmware goes bonkers.
+ * This means that the transfer must either be small enough to do a
+ * byte based transfer or it must be padded to a multiple of the
+ * current block size.
+ *
+ * As SDIO is still new to the kernel, it is unfortunately common with
+ * bugs in the host controllers related to that. One such bug is that 
+ * controllers cannot do transfers that aren't a multiple of 4 bytes.
+ * If you don't have time to fix the host controller driver, you can
+ * work around the problem by modifying if_sdio_host_to_card() and
+ * if_sdio_card_to_host() to pad the data.
+ */
+
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <linux/netdevice.h>
+#include <linux/delay.h>
+#include <linux/mmc/card.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio_ids.h>
+
+#include "host.h"
+#include "decl.h"
+#include "defs.h"
+#include "dev.h"
+#include "if_sdio.h"
+
+static char *lbs_helper_name = NULL;
+module_param_named(helper_name, lbs_helper_name, charp, 0644);
+
+static char *lbs_fw_name = NULL;
+module_param_named(fw_name, lbs_fw_name, charp, 0644);
+
+static const struct sdio_device_id if_sdio_ids[] = {
+	{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_LIBERTAS) },
+	{ /* end: all zeroes */						},
+};
+
+MODULE_DEVICE_TABLE(sdio, if_sdio_ids);
+
+struct if_sdio_model {
+	int model;
+	const char *helper;
+	const char *firmware;
+};
+
+static struct if_sdio_model if_sdio_models[] = {
+	{
+		/* 8385 */
+		.model = 0x04,
+		.helper = "sd8385_helper.bin",
+		.firmware = "sd8385.bin",
+	},
+	{
+		/* 8686 */
+		.model = 0x0B,
+		.helper = "sd8686_helper.bin",
+		.firmware = "sd8686.bin",
+	},
+};
+
+struct if_sdio_packet {
+	struct if_sdio_packet	*next;
+	u16			nb;
+	u8			buffer[0] __attribute__((aligned(4)));
+};
+
+struct if_sdio_card {
+	struct sdio_func	*func;
+	struct lbs_private	*priv;
+
+	int			model;
+	unsigned long		ioport;
+
+	const char		*helper;
+	const char		*firmware;
+
+	u8			buffer[65536];
+	u8			int_cause;
+	u32			event;
+
+	spinlock_t		lock;
+	struct if_sdio_packet	*packets;
+	struct work_struct	packet_worker;
+};
+
+/********************************************************************/
+/* I/O                                                              */
+/********************************************************************/
+
+static u16 if_sdio_read_scratch(struct if_sdio_card *card, int *err)
+{
+	int ret, reg;
+	u16 scratch;
+
+	if (card->model == 0x04)
+		reg = IF_SDIO_SCRATCH_OLD;
+	else
+		reg = IF_SDIO_SCRATCH;
+
+	scratch = sdio_readb(card->func, reg, &ret);
+	if (!ret)
+		scratch |= sdio_readb(card->func, reg + 1, &ret) << 8;
+
+	if (err)
+		*err = ret;
+
+	if (ret)
+		return 0xffff;
+
+	return scratch;
+}
+
+static int if_sdio_handle_cmd(struct if_sdio_card *card,
+		u8 *buffer, unsigned size)
+{
+	int ret;
+	unsigned long flags;
+
+	lbs_deb_enter(LBS_DEB_SDIO);
+
+	spin_lock_irqsave(&card->priv->driver_lock, flags);
+
+	if (!card->priv->cur_cmd) {
+		lbs_deb_sdio("discarding spurious response\n");
+		ret = 0;
+		goto out;
+	}
+
+	if (size > LBS_CMD_BUFFER_SIZE) {
+		lbs_deb_sdio("response packet too large (%d bytes)\n",
+			(int)size);
+		ret = -E2BIG;
+		goto out;
+	}
+
+	memcpy(card->priv->cur_cmd->cmdbuf, buffer, size);
+	card->priv->upld_len = size;
+
+	card->int_cause |= MRVDRV_CMD_UPLD_RDY;
+
+	lbs_interrupt(card->priv);
+
+	ret = 0;
+
+out:
+	spin_unlock_irqrestore(&card->priv->driver_lock, flags);
+
+	lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
+
+	return ret;
+}
+
+static int if_sdio_handle_data(struct if_sdio_card *card,
+		u8 *buffer, unsigned size)
+{
+	int ret;
+	struct sk_buff *skb;
+	char *data;
+
+	lbs_deb_enter(LBS_DEB_SDIO);
+
+	if (size > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE) {
+		lbs_deb_sdio("response packet too large (%d bytes)\n",
+			(int)size);
+		ret = -E2BIG;
+		goto out;
+	}
+
+	skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE + NET_IP_ALIGN);
+	if (!skb) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	skb_reserve(skb, NET_IP_ALIGN);
+
+	data = skb_put(skb, size);
+
+	memcpy(data, buffer, size);
+
+	lbs_process_rxed_packet(card->priv, skb);
+
+	ret = 0;
+
+out:
+	lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
+
+	return ret;
+}
+
+static int if_sdio_handle_event(struct if_sdio_card *card,
+		u8 *buffer, unsigned size)
+{
+	int ret;
+	unsigned long flags;
+	u32 event;
+
+	lbs_deb_enter(LBS_DEB_SDIO);
+
+	if (card->model == 0x04) {
+		event = sdio_readb(card->func, IF_SDIO_EVENT, &ret);
+		if (ret)
+			goto out;
+	} else {
+		if (size < 4) {
+			lbs_deb_sdio("event packet too small (%d bytes)\n",
+				(int)size);
+			ret = -EINVAL;
+			goto out;
+		}
+		event = buffer[3] << 24;
+		event |= buffer[2] << 16;
+		event |= buffer[1] << 8;
+		event |= buffer[0] << 0;
+		event <<= SBI_EVENT_CAUSE_SHIFT;
+	}
+
+	spin_lock_irqsave(&card->priv->driver_lock, flags);
+
+	card->event = event;
+	card->int_cause |= MRVDRV_CARDEVENT;
+
+	lbs_interrupt(card->priv);
+
+	spin_unlock_irqrestore(&card->priv->driver_lock, flags);
+
+	ret = 0;
+
+out:
+	lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
+
+	return ret;
+}
+
+static int if_sdio_card_to_host(struct if_sdio_card *card)
+{
+	int ret;
+	u8 status;
+	u16 size, type, chunk;
+	unsigned long timeout;
+
+	lbs_deb_enter(LBS_DEB_SDIO);
+
+	size = if_sdio_read_scratch(card, &ret);
+	if (ret)
+		goto out;
+
+	if (size < 4) {
+		lbs_deb_sdio("invalid packet size (%d bytes) from firmware\n",
+			(int)size);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	timeout = jiffies + HZ;
+	while (1) {
+		status = sdio_readb(card->func, IF_SDIO_STATUS, &ret);
+		if (ret)
+			goto out;
+		if (status & IF_SDIO_IO_RDY)
+			break;
+		if (time_after(jiffies, timeout)) {
+			ret = -ETIMEDOUT;
+			goto out;
+		}
+		mdelay(1);
+	}
+
+	/*
+	 * The transfer must be in one transaction or the firmware
+	 * goes suicidal.
+	 */
+	chunk = size;
+	if ((chunk > card->func->cur_blksize) || (chunk > 512)) {
+		chunk = (chunk + card->func->cur_blksize - 1) /
+			card->func->cur_blksize * card->func->cur_blksize;
+	}
+
+	ret = sdio_readsb(card->func, card->buffer, card->ioport, chunk);
+	if (ret)
+		goto out;
+
+	chunk = card->buffer[0] | (card->buffer[1] << 8);
+	type = card->buffer[2] | (card->buffer[3] << 8);
+
+	lbs_deb_sdio("packet of type %d and size %d bytes\n",
+		(int)type, (int)chunk);
+
+	if (chunk > size) {
+		lbs_deb_sdio("packet fragment (%d > %d)\n",
+			(int)chunk, (int)size);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (chunk < size) {
+		lbs_deb_sdio("packet fragment (%d < %d)\n",
+			(int)chunk, (int)size);
+	}
+
+	switch (type) {
+	case MVMS_CMD:
+		ret = if_sdio_handle_cmd(card, card->buffer + 4, chunk - 4);
+		if (ret)
+			goto out;
+		break;
+	case MVMS_DAT:
+		ret = if_sdio_handle_data(card, card->buffer + 4, chunk - 4);
+		if (ret)
+			goto out;
+		break;
+	case MVMS_EVENT:
+		ret = if_sdio_handle_event(card, card->buffer + 4, chunk - 4);
+		if (ret)
+			goto out;
+		break;
+	default:
+		lbs_deb_sdio("invalid type (%d) from firmware\n",
+				(int)type);
+		ret = -EINVAL;
+		goto out;
+	}
+
+out:
+	if (ret)
+		lbs_pr_err("problem fetching packet from firmware\n");
+
+	lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
+
+	return ret;
+}
+
+static void if_sdio_host_to_card_worker(struct work_struct *work)
+{
+	struct if_sdio_card *card;
+	struct if_sdio_packet *packet;
+	unsigned long timeout;
+	u8 status;
+	int ret;
+	unsigned long flags;
+
+	lbs_deb_enter(LBS_DEB_SDIO);
+
+	card = container_of(work, struct if_sdio_card, packet_worker);
+
+	while (1) {
+		spin_lock_irqsave(&card->lock, flags);
+		packet = card->packets;
+		if (packet)
+			card->packets = packet->next;
+		spin_unlock_irqrestore(&card->lock, flags);
+
+		if (!packet)
+			break;
+
+		sdio_claim_host(card->func);
+
+		timeout = jiffies + HZ;
+		while (1) {
+			status = sdio_readb(card->func, IF_SDIO_STATUS, &ret);
+			if (ret)
+				goto release;
+			if (status & IF_SDIO_IO_RDY)
+				break;
+			if (time_after(jiffies, timeout)) {
+				ret = -ETIMEDOUT;
+				goto release;
+			}
+			mdelay(1);
+		}
+
+		ret = sdio_writesb(card->func, card->ioport,
+				packet->buffer, packet->nb);
+		if (ret)
+			goto release;
+release:
+		sdio_release_host(card->func);
+
+		kfree(packet);
+	}
+
+	lbs_deb_leave(LBS_DEB_SDIO);
+}
+
+/********************************************************************/
+/* Firmware                                                         */
+/********************************************************************/
+
+static int if_sdio_prog_helper(struct if_sdio_card *card)
+{
+	int ret;
+	u8 status;
+	const struct firmware *fw;
+	unsigned long timeout;
+	u8 *chunk_buffer;
+	u32 chunk_size;
+	u8 *firmware;
+	size_t size;
+
+	lbs_deb_enter(LBS_DEB_SDIO);
+
+	ret = request_firmware(&fw, card->helper, &card->func->dev);
+	if (ret) {
+		lbs_pr_err("can't load helper firmware\n");
+		goto out;
+	}
+
+	chunk_buffer = kzalloc(64, GFP_KERNEL);
+	if (!chunk_buffer) {
+		ret = -ENOMEM;
+		goto release_fw;
+	}
+
+	sdio_claim_host(card->func);
+
+	ret = sdio_set_block_size(card->func, 32);
+	if (ret)
+		goto release;
+
+	firmware = fw->data;
+	size = fw->size;
+
+	while (size) {
+		timeout = jiffies + HZ;
+		while (1) {
+			status = sdio_readb(card->func, IF_SDIO_STATUS, &ret);
+			if (ret)
+				goto release;
+			if ((status & IF_SDIO_IO_RDY) &&
+					(status & IF_SDIO_DL_RDY))
+				break;
+			if (time_after(jiffies, timeout)) {
+				ret = -ETIMEDOUT;
+				goto release;
+			}
+			mdelay(1);
+		}
+
+		chunk_size = min(size, (size_t)60);
+
+		*((__le32*)chunk_buffer) = cpu_to_le32(chunk_size);
+		memcpy(chunk_buffer + 4, firmware, chunk_size);
+/*
+		lbs_deb_sdio("sending %d bytes chunk\n", chunk_size);
+*/
+		ret = sdio_writesb(card->func, card->ioport,
+				chunk_buffer, 64);
+		if (ret)
+			goto release;
+
+		firmware += chunk_size;
+		size -= chunk_size;
+	}
+
+	/* an empty block marks the end of the transfer */
+	memset(chunk_buffer, 0, 4);
+	ret = sdio_writesb(card->func, card->ioport, chunk_buffer, 64);
+	if (ret)
+		goto release;
+
+	lbs_deb_sdio("waiting for helper to boot...\n");
+
+	/* wait for the helper to boot by looking at the size register */
+	timeout = jiffies + HZ;
+	while (1) {
+		u16 req_size;
+
+		req_size = sdio_readb(card->func, IF_SDIO_RD_BASE, &ret);
+		if (ret)
+			goto release;
+
+		req_size |= sdio_readb(card->func, IF_SDIO_RD_BASE + 1, &ret) << 8;
+		if (ret)
+			goto release;
+
+		if (req_size != 0)
+			break;
+
+		if (time_after(jiffies, timeout)) {
+			ret = -ETIMEDOUT;
+			goto release;
+		}
+
+		msleep(10);
+	}
+
+	ret = 0;
+
+release:
+	sdio_set_block_size(card->func, 0);
+	sdio_release_host(card->func);
+	kfree(chunk_buffer);
+release_fw:
+	release_firmware(fw);
+
+out:
+	if (ret)
+		lbs_pr_err("failed to load helper firmware\n");
+
+	lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
+
+	return ret;
+}
+
+static int if_sdio_prog_real(struct if_sdio_card *card)
+{
+	int ret;
+	u8 status;
+	const struct firmware *fw;
+	unsigned long timeout;
+	u8 *chunk_buffer;
+	u32 chunk_size;
+	u8 *firmware;
+	size_t size, req_size;
+
+	lbs_deb_enter(LBS_DEB_SDIO);
+
+	ret = request_firmware(&fw, card->firmware, &card->func->dev);
+	if (ret) {
+		lbs_pr_err("can't load firmware\n");
+		goto out;
+	}
+
+	chunk_buffer = kzalloc(512, GFP_KERNEL);
+	if (!chunk_buffer) {
+		ret = -ENOMEM;
+		goto release_fw;
+	}
+
+	sdio_claim_host(card->func);
+
+	ret = sdio_set_block_size(card->func, 32);
+	if (ret)
+		goto release;
+
+	firmware = fw->data;
+	size = fw->size;
+
+	while (size) {
+		timeout = jiffies + HZ;
+		while (1) {
+			status = sdio_readb(card->func, IF_SDIO_STATUS, &ret);
+			if (ret)
+				goto release;
+			if ((status & IF_SDIO_IO_RDY) &&
+					(status & IF_SDIO_DL_RDY))
+				break;
+			if (time_after(jiffies, timeout)) {
+				ret = -ETIMEDOUT;
+				goto release;
+			}
+			mdelay(1);
+		}
+
+		req_size = sdio_readb(card->func, IF_SDIO_RD_BASE, &ret);
+		if (ret)
+			goto release;
+
+		req_size |= sdio_readb(card->func, IF_SDIO_RD_BASE + 1, &ret) << 8;
+		if (ret)
+			goto release;
+/*
+		lbs_deb_sdio("firmware wants %d bytes\n", (int)req_size);
+*/
+		if (req_size == 0) {
+			lbs_deb_sdio("firmware helper gave up early\n");
+			ret = -EIO;
+			goto release;
+		}
+
+		if (req_size & 0x01) {
+			lbs_deb_sdio("firmware helper signalled error\n");
+			ret = -EIO;
+			goto release;
+		}
+
+		if (req_size > size)
+			req_size = size;
+
+		while (req_size) {
+			chunk_size = min(req_size, (size_t)512);
+
+			memcpy(chunk_buffer, firmware, chunk_size);
+/*
+			lbs_deb_sdio("sending %d bytes (%d bytes) chunk\n",
+				chunk_size, (chunk_size + 31) / 32 * 32);
+*/
+			ret = sdio_writesb(card->func, card->ioport,
+				chunk_buffer, (chunk_size + 31) / 32 * 32);
+			if (ret)
+				goto release;
+
+			firmware += chunk_size;
+			size -= chunk_size;
+			req_size -= chunk_size;
+		}
+	}
+
+	ret = 0;
+
+	lbs_deb_sdio("waiting for firmware to boot...\n");
+
+	/* wait for the firmware to boot */
+	timeout = jiffies + HZ;
+	while (1) {
+		u16 scratch;
+
+		scratch = if_sdio_read_scratch(card, &ret);
+		if (ret)
+			goto release;
+
+		if (scratch == IF_SDIO_FIRMWARE_OK)
+			break;
+
+		if (time_after(jiffies, timeout)) {
+			ret = -ETIMEDOUT;
+			goto release;
+		}
+
+		msleep(10);
+	}
+
+	ret = 0;
+
+release:
+	sdio_set_block_size(card->func, 0);
+	sdio_release_host(card->func);
+	kfree(chunk_buffer);
+release_fw:
+	release_firmware(fw);
+
+out:
+	if (ret)
+		lbs_pr_err("failed to load firmware\n");
+
+	lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
+
+	return ret;
+}
+
+static int if_sdio_prog_firmware(struct if_sdio_card *card)
+{
+	int ret;
+	u16 scratch;
+
+	lbs_deb_enter(LBS_DEB_SDIO);
+
+	sdio_claim_host(card->func);
+	scratch = if_sdio_read_scratch(card, &ret);
+	sdio_release_host(card->func);
+
+	if (ret)
+		goto out;
+
+	if (scratch == IF_SDIO_FIRMWARE_OK) {
+		lbs_deb_sdio("firmware already loaded\n");
+		goto success;
+	}
+
+	ret = if_sdio_prog_helper(card);
+	if (ret)
+		goto out;
+
+	ret = if_sdio_prog_real(card);
+	if (ret)
+		goto out;
+
+success:
+	ret = 0;
+
+out:
+	lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
+
+	return ret;
+}
+
+/*******************************************************************/
+/* Libertas callbacks                                              */
+/*******************************************************************/
+
+static int if_sdio_host_to_card(struct lbs_private *priv,
+		u8 type, u8 *buf, u16 nb)
+{
+	int ret;
+	struct if_sdio_card *card;
+	struct if_sdio_packet *packet, *cur;
+	u16 size;
+	unsigned long flags;
+
+	lbs_deb_enter_args(LBS_DEB_SDIO, "type %d, bytes %d", type, nb);
+
+	card = priv->card;
+
+	if (nb > (65536 - sizeof(struct if_sdio_packet) - 4)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/*
+	 * The transfer must be in one transaction or the firmware
+	 * goes suicidal.
+	 */
+	size = nb + 4;
+	if ((size > card->func->cur_blksize) || (size > 512)) {
+		size = (size + card->func->cur_blksize - 1) /
+			card->func->cur_blksize * card->func->cur_blksize;
+	}
+
+	packet = kzalloc(sizeof(struct if_sdio_packet) + size,
+			GFP_ATOMIC);
+	if (!packet) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	packet->next = NULL;
+	packet->nb = size;
+
+	/*
+	 * SDIO specific header.
+	 */
+	packet->buffer[0] = (nb + 4) & 0xff;
+	packet->buffer[1] = ((nb + 4) >> 8) & 0xff;
+	packet->buffer[2] = type;
+	packet->buffer[3] = 0;
+
+	memcpy(packet->buffer + 4, buf, nb);
+
+	spin_lock_irqsave(&card->lock, flags);
+
+	if (!card->packets)
+		card->packets = packet;
+	else {
+		cur = card->packets;
+		while (cur->next)
+			cur = cur->next;
+		cur->next = packet;
+	}
+
+	switch (type) {
+	case MVMS_CMD:
+		priv->dnld_sent = DNLD_CMD_SENT;
+		break;
+	case MVMS_DAT:
+		priv->dnld_sent = DNLD_DATA_SENT;
+		break;
+	default:
+		lbs_deb_sdio("unknown packet type %d\n", (int)type);
+	}
+
+	spin_unlock_irqrestore(&card->lock, flags);
+
+	schedule_work(&card->packet_worker);
+
+	ret = 0;
+
+out:
+	lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
+
+	return ret;
+}
+
+static int if_sdio_get_int_status(struct lbs_private *priv, u8 *ireg)
+{
+	struct if_sdio_card *card;
+
+	lbs_deb_enter(LBS_DEB_SDIO);
+
+	card = priv->card;
+
+	*ireg = card->int_cause;
+	card->int_cause = 0;
+
+	lbs_deb_leave(LBS_DEB_SDIO);
+
+	return 0;
+}
+
+static int if_sdio_read_event_cause(struct lbs_private *priv)
+{
+	struct if_sdio_card *card;
+
+	lbs_deb_enter(LBS_DEB_SDIO);
+
+	card = priv->card;
+
+	priv->eventcause = card->event;
+
+	lbs_deb_leave(LBS_DEB_SDIO);
+
+	return 0;
+}
+
+/*******************************************************************/
+/* SDIO callbacks                                                  */
+/*******************************************************************/
+
+static void if_sdio_interrupt(struct sdio_func *func)
+{
+	int ret;
+	struct if_sdio_card *card;
+	u8 cause;
+
+	lbs_deb_enter(LBS_DEB_SDIO);
+
+	card = sdio_get_drvdata(func);
+
+	cause = sdio_readb(card->func, IF_SDIO_H_INT_STATUS, &ret);
+	if (ret)
+		goto out;
+
+	lbs_deb_sdio("interrupt: 0x%X\n", (unsigned)cause);
+
+	sdio_writeb(card->func, ~cause, IF_SDIO_H_INT_STATUS, &ret);
+	if (ret)
+		goto out;
+
+	/*
+	 * Ignore the define name, this really means the card has
+	 * successfully received the command.
+	 */
+	if (cause & IF_SDIO_H_INT_DNLD)
+		lbs_host_to_card_done(card->priv);
+
+
+	if (cause & IF_SDIO_H_INT_UPLD) {
+		ret = if_sdio_card_to_host(card);
+		if (ret)
+			goto out;
+	}
+
+	ret = 0;
+
+out:
+	lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
+}
+
+static int if_sdio_probe(struct sdio_func *func,
+		const struct sdio_device_id *id)
+{
+	struct if_sdio_card *card;
+	struct lbs_private *priv;
+	int ret, i;
+	unsigned int model;
+	struct if_sdio_packet *packet;
+
+	lbs_deb_enter(LBS_DEB_SDIO);
+
+	for (i = 0;i < func->card->num_info;i++) {
+		if (sscanf(func->card->info[i],
+				"802.11 SDIO ID: %x", &model) == 1)
+			break;
+		if (sscanf(func->card->info[i],
+				"ID: %x", &model) == 1)
+			break;
+	}
+
+	if (i == func->card->num_info) {
+		lbs_pr_err("unable to identify card model\n");
+		return -ENODEV;
+	}
+
+	card = kzalloc(sizeof(struct if_sdio_card), GFP_KERNEL);
+	if (!card)
+		return -ENOMEM;
+
+	card->func = func;
+	card->model = model;
+	spin_lock_init(&card->lock);
+	INIT_WORK(&card->packet_worker, if_sdio_host_to_card_worker);
+
+	for (i = 0;i < ARRAY_SIZE(if_sdio_models);i++) {
+		if (card->model == if_sdio_models[i].model)
+			break;
+	}
+
+	if (i == ARRAY_SIZE(if_sdio_models)) {
+		lbs_pr_err("unkown card model 0x%x\n", card->model);
+		ret = -ENODEV;
+		goto free;
+	}
+
+	card->helper = if_sdio_models[i].helper;
+	card->firmware = if_sdio_models[i].firmware;
+
+	if (lbs_helper_name) {
+		lbs_deb_sdio("overriding helper firmware: %s\n",
+			lbs_helper_name);
+		card->helper = lbs_helper_name;
+	}
+
+	if (lbs_fw_name) {
+		lbs_deb_sdio("overriding firmware: %s\n", lbs_fw_name);
+		card->firmware = lbs_fw_name;
+	}
+
+	sdio_claim_host(func);
+
+	ret = sdio_enable_func(func);
+	if (ret)
+		goto release;
+
+	ret = sdio_claim_irq(func, if_sdio_interrupt);
+	if (ret)
+		goto disable;
+
+	card->ioport = sdio_readb(func, IF_SDIO_IOPORT, &ret);
+	if (ret)
+		goto release_int;
+
+	card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 1, &ret) << 8;
+	if (ret)
+		goto release_int;
+
+	card->ioport |= sdio_readb(func, IF_SDIO_IOPORT + 2, &ret) << 16;
+	if (ret)
+		goto release_int;
+
+	sdio_release_host(func);
+
+	sdio_set_drvdata(func, card);
+
+	lbs_deb_sdio("class = 0x%X, vendor = 0x%X, "
+			"device = 0x%X, model = 0x%X, ioport = 0x%X\n",
+			func->class, func->vendor, func->device,
+			model, (unsigned)card->ioport);
+
+	ret = if_sdio_prog_firmware(card);
+	if (ret)
+		goto reclaim;
+
+	priv = lbs_add_card(card, &func->dev);
+	if (!priv) {
+		ret = -ENOMEM;
+		goto reclaim;
+	}
+
+	card->priv = priv;
+
+	priv->card = card;
+	priv->hw_host_to_card = if_sdio_host_to_card;
+	priv->hw_get_int_status = if_sdio_get_int_status;
+	priv->hw_read_event_cause = if_sdio_read_event_cause;
+
+	priv->fw_ready = 1;
+
+	/*
+	 * Enable interrupts now that everything is set up
+	 */
+	sdio_claim_host(func);
+	sdio_writeb(func, 0x0f, IF_SDIO_H_INT_MASK, &ret);
+	sdio_release_host(func);
+	if (ret)
+		goto reclaim;
+
+	ret = lbs_start_card(priv);
+	if (ret)
+		goto err_activate_card;
+
+out:
+	lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
+
+	return ret;
+
+err_activate_card:
+	flush_scheduled_work();
+	free_netdev(priv->dev);
+	kfree(priv);
+reclaim:
+	sdio_claim_host(func);
+release_int:
+	sdio_release_irq(func);
+disable:
+	sdio_disable_func(func);
+release:
+	sdio_release_host(func);
+free:
+	while (card->packets) {
+		packet = card->packets;
+		card->packets = card->packets->next;
+		kfree(packet);
+	}
+
+	kfree(card);
+
+	goto out;
+}
+
+static void if_sdio_remove(struct sdio_func *func)
+{
+	struct if_sdio_card *card;
+	struct if_sdio_packet *packet;
+
+	lbs_deb_enter(LBS_DEB_SDIO);
+
+	card = sdio_get_drvdata(func);
+
+	card->priv->surpriseremoved = 1;
+
+	lbs_deb_sdio("call remove card\n");
+	lbs_stop_card(card->priv);
+	lbs_remove_card(card->priv);
+
+	flush_scheduled_work();
+
+	sdio_claim_host(func);
+	sdio_release_irq(func);
+	sdio_disable_func(func);
+	sdio_release_host(func);
+
+	while (card->packets) {
+		packet = card->packets;
+		card->packets = card->packets->next;
+		kfree(packet);
+	}
+
+	kfree(card);
+
+	lbs_deb_leave(LBS_DEB_SDIO);
+}
+
+static struct sdio_driver if_sdio_driver = {
+	.name		= "libertas_sdio",
+	.id_table	= if_sdio_ids,
+	.probe		= if_sdio_probe,
+	.remove		= if_sdio_remove,
+};
+
+/*******************************************************************/
+/* Module functions                                                */
+/*******************************************************************/
+
+static int __init if_sdio_init_module(void)
+{
+	int ret = 0;
+
+	lbs_deb_enter(LBS_DEB_SDIO);
+
+	printk(KERN_INFO "libertas_sdio: Libertas SDIO driver\n");
+	printk(KERN_INFO "libertas_sdio: Copyright Pierre Ossman\n");
+
+	ret = sdio_register_driver(&if_sdio_driver);
+
+	lbs_deb_leave_args(LBS_DEB_SDIO, "ret %d", ret);
+
+	return ret;
+}
+
+static void __exit if_sdio_exit_module(void)
+{
+	lbs_deb_enter(LBS_DEB_SDIO);
+
+	sdio_unregister_driver(&if_sdio_driver);
+
+	lbs_deb_leave(LBS_DEB_SDIO);
+}
+
+module_init(if_sdio_init_module);
+module_exit(if_sdio_exit_module);
+
+MODULE_DESCRIPTION("Libertas SDIO WLAN Driver");
+MODULE_AUTHOR("Pierre Ossman");
+MODULE_LICENSE("GPL");

+ 45 - 0
package/libertas/src/if_sdio.h

@@ -0,0 +1,45 @@
+/*
+ *  linux/drivers/net/wireless/libertas/if_sdio.h
+ *
+ *  Copyright 2007 Pierre Ossman
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ */
+
+#ifndef _LBS_IF_SDIO_H
+#define _LBS_IF_SDIO_H
+
+#define IF_SDIO_IOPORT		0x00
+
+#define IF_SDIO_H_INT_MASK	0x04
+#define   IF_SDIO_H_INT_OFLOW	0x08
+#define   IF_SDIO_H_INT_UFLOW	0x04
+#define   IF_SDIO_H_INT_DNLD	0x02
+#define   IF_SDIO_H_INT_UPLD	0x01
+
+#define IF_SDIO_H_INT_STATUS	0x05
+#define IF_SDIO_H_INT_RSR	0x06
+#define IF_SDIO_H_INT_STATUS2	0x07
+
+#define IF_SDIO_RD_BASE		0x10
+
+#define IF_SDIO_STATUS		0x20
+#define   IF_SDIO_IO_RDY	0x08
+#define   IF_SDIO_CIS_RDY	0x04
+#define   IF_SDIO_UL_RDY	0x02
+#define   IF_SDIO_DL_RDY	0x01
+
+#define IF_SDIO_C_INT_MASK	0x24
+#define IF_SDIO_C_INT_STATUS	0x28
+#define IF_SDIO_C_INT_RSR	0x2C
+
+#define IF_SDIO_SCRATCH		0x34
+#define IF_SDIO_SCRATCH_OLD	0x80fe
+#define   IF_SDIO_FIRMWARE_OK	0xfedc
+
+#define IF_SDIO_EVENT           0x80fc
+
+#endif

+ 1043 - 0
package/libertas/src/if_usb.c

@@ -0,0 +1,1043 @@
+/**
+  * This file contains functions used in USB interface module.
+  */
+#include <linux/delay.h>
+#include <linux/moduleparam.h>
+#include <linux/firmware.h>
+#include <linux/netdevice.h>
+#include <linux/usb.h>
+
+#define DRV_NAME "usb8xxx"
+
+#include "host.h"
+#include "decl.h"
+#include "defs.h"
+#include "dev.h"
+#include "cmd.h"
+#include "if_usb.h"
+
+#define MESSAGE_HEADER_LEN	4
+
+static char *lbs_fw_name = "usb8388.bin";
+module_param_named(fw_name, lbs_fw_name, charp, 0644);
+
+static struct usb_device_id if_usb_table[] = {
+	/* Enter the device signature inside */
+	{ USB_DEVICE(0x1286, 0x2001) },
+	{ USB_DEVICE(0x05a3, 0x8388) },
+	{}	/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, if_usb_table);
+
+static void if_usb_receive(struct urb *urb);
+static void if_usb_receive_fwload(struct urb *urb);
+static int if_usb_prog_firmware(struct usb_card_rec *cardp);
+static int if_usb_host_to_card(struct lbs_private *priv,
+	u8 type,
+	u8 *payload,
+	u16 nb);
+static int if_usb_get_int_status(struct lbs_private *priv, u8 *);
+static int if_usb_read_event_cause(struct lbs_private *);
+static int usb_tx_block(struct usb_card_rec *cardp, u8 *payload, u16 nb);
+static void if_usb_free(struct usb_card_rec *cardp);
+static int if_usb_submit_rx_urb(struct usb_card_rec *cardp);
+static int if_usb_reset_device(struct usb_card_rec *cardp);
+
+/**
+ *  @brief  call back function to handle the status of the URB
+ *  @param urb 		pointer to urb structure
+ *  @return 	   	N/A
+ */
+static void if_usb_write_bulk_callback(struct urb *urb)
+{
+	struct usb_card_rec *cardp = (struct usb_card_rec *) urb->context;
+
+	/* handle the transmission complete validations */
+
+	if (urb->status == 0) {
+		struct lbs_private *priv = cardp->priv;
+
+		/*
+		lbs_deb_usbd(&urb->dev->dev, "URB status is successfull\n");
+		lbs_deb_usbd(&urb->dev->dev, "Actual length transmitted %d\n",
+		       urb->actual_length);
+		*/
+
+		/* Used for both firmware TX and regular TX.  priv isn't
+		 * valid at firmware load time.
+		 */
+		if (priv)
+			lbs_host_to_card_done(priv);
+	} else {
+		/* print the failure status number for debug */
+		lbs_pr_info("URB in failure status: %d\n", urb->status);
+	}
+
+	return;
+}
+
+/**
+ *  @brief  free tx/rx urb, skb and rx buffer
+ *  @param cardp	pointer usb_card_rec
+ *  @return 	   	N/A
+ */
+static void if_usb_free(struct usb_card_rec *cardp)
+{
+	lbs_deb_enter(LBS_DEB_USB);
+
+	/* Unlink tx & rx urb */
+	usb_kill_urb(cardp->tx_urb);
+	usb_kill_urb(cardp->rx_urb);
+
+	usb_free_urb(cardp->tx_urb);
+	cardp->tx_urb = NULL;
+
+	usb_free_urb(cardp->rx_urb);
+	cardp->rx_urb = NULL;
+
+	kfree(cardp->bulk_out_buffer);
+	cardp->bulk_out_buffer = NULL;
+
+	lbs_deb_leave(LBS_DEB_USB);
+}
+
+static void if_usb_set_boot2_ver(struct lbs_private *priv)
+{
+	struct cmd_ds_set_boot2_ver b2_cmd;
+
+	b2_cmd.action = 0;
+	b2_cmd.version = priv->boot2_version;
+
+	if (lbs_cmd(priv, CMD_SET_BOOT2_VER, b2_cmd, NULL, 0))
+		lbs_deb_usb("Setting boot2 version failed\n");
+}
+
+static void if_usb_fw_timeo(unsigned long priv)
+{
+	struct usb_card_rec *cardp = (void *)priv;
+
+	if (cardp->fwdnldover) {
+		lbs_deb_usb("Download complete, no event. Assuming success\n");
+	} else {
+		lbs_pr_err("Download timed out\n");
+		cardp->surprise_removed = 1;
+	}
+	wake_up(&cardp->fw_wq);
+}
+/**
+ *  @brief sets the configuration values
+ *  @param ifnum	interface number
+ *  @param id		pointer to usb_device_id
+ *  @return 	   	0 on success, error code on failure
+ */
+static int if_usb_probe(struct usb_interface *intf,
+			const struct usb_device_id *id)
+{
+	struct usb_device *udev;
+	struct usb_host_interface *iface_desc;
+	struct usb_endpoint_descriptor *endpoint;
+	struct lbs_private *priv;
+	struct usb_card_rec *cardp;
+	int i;
+
+	udev = interface_to_usbdev(intf);
+
+	cardp = kzalloc(sizeof(struct usb_card_rec), GFP_KERNEL);
+	if (!cardp) {
+		lbs_pr_err("Out of memory allocating private data.\n");
+		goto error;
+	}
+
+	setup_timer(&cardp->fw_timeout, if_usb_fw_timeo, (unsigned long)cardp);
+	init_waitqueue_head(&cardp->fw_wq);
+								     
+	cardp->udev = udev;
+	iface_desc = intf->cur_altsetting;
+
+	lbs_deb_usbd(&udev->dev, "bcdUSB = 0x%X bDeviceClass = 0x%X"
+	       " bDeviceSubClass = 0x%X, bDeviceProtocol = 0x%X\n",
+		     le16_to_cpu(udev->descriptor.bcdUSB),
+		     udev->descriptor.bDeviceClass,
+		     udev->descriptor.bDeviceSubClass,
+		     udev->descriptor.bDeviceProtocol);
+
+	for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+		endpoint = &iface_desc->endpoint[i].desc;
+		if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+		    && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+			USB_ENDPOINT_XFER_BULK)) {
+			/* we found a bulk in endpoint */
+			lbs_deb_usbd(&udev->dev, "Bulk in size is %d\n",
+				     le16_to_cpu(endpoint->wMaxPacketSize));
+			if (!(cardp->rx_urb = usb_alloc_urb(0, GFP_KERNEL))) {
+				lbs_deb_usbd(&udev->dev,
+				       "Rx URB allocation failed\n");
+				goto dealloc;
+			}
+			cardp->bulk_in_size =
+				le16_to_cpu(endpoint->wMaxPacketSize);
+			cardp->bulk_in_endpointAddr =
+			    (endpoint->
+			     bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+			lbs_deb_usbd(&udev->dev, "in_endpoint = %d\n",
+			       endpoint->bEndpointAddress);
+		}
+
+		if (((endpoint->
+		      bEndpointAddress & USB_ENDPOINT_DIR_MASK) ==
+		     USB_DIR_OUT)
+		    && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+			USB_ENDPOINT_XFER_BULK)) {
+			/* We found bulk out endpoint */
+			if (!(cardp->tx_urb = usb_alloc_urb(0, GFP_KERNEL))) {
+				lbs_deb_usbd(&udev->dev,
+				       "Tx URB allocation failed\n");
+				goto dealloc;
+			}
+
+			cardp->bulk_out_size =
+				le16_to_cpu(endpoint->wMaxPacketSize);
+			lbs_deb_usbd(&udev->dev,
+				     "Bulk out size is %d\n",
+				     le16_to_cpu(endpoint->wMaxPacketSize));
+			cardp->bulk_out_endpointAddr =
+			    endpoint->bEndpointAddress;
+			lbs_deb_usbd(&udev->dev, "out_endpoint = %d\n",
+				    endpoint->bEndpointAddress);
+			cardp->bulk_out_buffer =
+			    kmalloc(MRVDRV_ETH_TX_PACKET_BUFFER_SIZE,
+				    GFP_KERNEL);
+
+			if (!cardp->bulk_out_buffer) {
+				lbs_deb_usbd(&udev->dev,
+				       "Could not allocate buffer\n");
+				goto dealloc;
+			}
+		}
+	}
+
+	/* Upload firmware */
+	cardp->rinfo.cardp = cardp;
+	if (if_usb_prog_firmware(cardp)) {
+		lbs_deb_usbd(&udev->dev, "FW upload failed");
+		goto err_prog_firmware;
+	}
+
+	if (!(priv = lbs_add_card(cardp, &udev->dev)))
+		goto err_prog_firmware;
+
+	cardp->priv = priv;
+	cardp->priv->fw_ready = 1;
+
+	priv->hw_host_to_card = if_usb_host_to_card;
+	priv->hw_get_int_status = if_usb_get_int_status;
+	priv->hw_read_event_cause = if_usb_read_event_cause;
+	priv->boot2_version = udev->descriptor.bcdDevice;
+
+	if_usb_submit_rx_urb(cardp);
+
+	if (lbs_start_card(priv))
+		goto err_start_card;
+
+	if_usb_set_boot2_ver(priv);
+
+	usb_get_dev(udev);
+	usb_set_intfdata(intf, cardp);
+
+	return 0;
+
+err_start_card:
+	lbs_remove_card(priv);
+err_prog_firmware:
+	if_usb_reset_device(cardp);
+dealloc:
+	if_usb_free(cardp);
+
+error:
+	return -ENOMEM;
+}
+
+/**
+ *  @brief free resource and cleanup
+ *  @param intf		USB interface structure
+ *  @return 	   	N/A
+ */
+static void if_usb_disconnect(struct usb_interface *intf)
+{
+	struct usb_card_rec *cardp = usb_get_intfdata(intf);
+	struct lbs_private *priv = (struct lbs_private *) cardp->priv;
+
+	lbs_deb_enter(LBS_DEB_MAIN);
+
+	/* Update Surprise removed to TRUE */
+	cardp->surprise_removed = 1;
+
+	if (priv) {
+
+		priv->surpriseremoved = 1;
+		lbs_stop_card(priv);
+		lbs_remove_card(priv);
+	}
+
+	/* this is (apparently?) necessary for future usage of the device */
+	lbs_prepare_and_send_command(priv, CMD_802_11_RESET, CMD_ACT_HALT,
+			0, 0, NULL);
+
+	/* Unlink and free urb */
+	if_usb_free(cardp);
+
+	usb_set_intfdata(intf, NULL);
+	usb_put_dev(interface_to_usbdev(intf));
+
+	lbs_deb_leave(LBS_DEB_MAIN);
+}
+
+/**
+ *  @brief  This function download FW
+ *  @param priv		pointer to struct lbs_private
+ *  @return 	   	0
+ */
+static int if_usb_send_fw_pkt(struct usb_card_rec *cardp)
+{
+	struct FWData *fwdata;
+	struct fwheader *fwheader;
+	u8 *firmware = cardp->fw->data;
+
+	fwdata = kmalloc(sizeof(struct FWData), GFP_ATOMIC);
+
+	if (!fwdata)
+		return -1;
+
+	fwheader = &fwdata->fwheader;
+
+	if (!cardp->CRC_OK) {
+		cardp->totalbytes = cardp->fwlastblksent;
+		cardp->fwseqnum = cardp->lastseqnum - 1;
+	}
+
+	/*
+	lbs_deb_usbd(&cardp->udev->dev, "totalbytes = %d\n",
+		    cardp->totalbytes);
+	*/
+
+	memcpy(fwheader, &firmware[cardp->totalbytes],
+	       sizeof(struct fwheader));
+
+	cardp->fwlastblksent = cardp->totalbytes;
+	cardp->totalbytes += sizeof(struct fwheader);
+
+	/* lbs_deb_usbd(&cardp->udev->dev,"Copy Data\n"); */
+	memcpy(fwdata->data, &firmware[cardp->totalbytes],
+	       le32_to_cpu(fwdata->fwheader.datalength));
+
+	/*
+	lbs_deb_usbd(&cardp->udev->dev,
+		    "Data length = %d\n", le32_to_cpu(fwdata->fwheader.datalength));
+	*/
+
+	cardp->fwseqnum = cardp->fwseqnum + 1;
+
+	fwdata->seqnum = cpu_to_le32(cardp->fwseqnum);
+	cardp->lastseqnum = cardp->fwseqnum;
+	cardp->totalbytes += le32_to_cpu(fwdata->fwheader.datalength);
+
+	if (fwheader->dnldcmd == cpu_to_le32(FW_HAS_DATA_TO_RECV)) {
+		/*
+		lbs_deb_usbd(&cardp->udev->dev, "There are data to follow\n");
+		lbs_deb_usbd(&cardp->udev->dev,
+			    "seqnum = %d totalbytes = %d\n", cardp->fwseqnum,
+			    cardp->totalbytes);
+		*/
+		memcpy(cardp->bulk_out_buffer, fwheader, FW_DATA_XMIT_SIZE);
+		usb_tx_block(cardp, cardp->bulk_out_buffer, FW_DATA_XMIT_SIZE);
+
+	} else if (fwdata->fwheader.dnldcmd == cpu_to_le32(FW_HAS_LAST_BLOCK)) {
+		/*
+		lbs_deb_usbd(&cardp->udev->dev,
+			    "Host has finished FW downloading\n");
+		lbs_deb_usbd(&cardp->udev->dev,
+			    "Donwloading FW JUMP BLOCK\n");
+		*/
+		memcpy(cardp->bulk_out_buffer, fwheader, FW_DATA_XMIT_SIZE);
+		usb_tx_block(cardp, cardp->bulk_out_buffer, FW_DATA_XMIT_SIZE);
+		cardp->fwfinalblk = 1;
+	}
+
+	/*
+	lbs_deb_usbd(&cardp->udev->dev,
+		    "The firmware download is done size is %d\n",
+		    cardp->totalbytes);
+	*/
+
+	kfree(fwdata);
+
+	return 0;
+}
+
+static int if_usb_reset_device(struct usb_card_rec *cardp)
+{
+	struct cmd_ds_command *cmd = (void *)&cardp->bulk_out_buffer[4];
+	int ret;
+
+	lbs_deb_enter(LBS_DEB_USB);
+
+	*(__le32 *)cardp->bulk_out_buffer = cpu_to_le32(CMD_TYPE_REQUEST);
+
+	cmd->command = cpu_to_le16(CMD_802_11_RESET);
+	cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_reset) + S_DS_GEN);
+	cmd->result = cpu_to_le16(0);
+	cmd->seqnum = cpu_to_le16(0x5a5a);
+	cmd->params.reset.action = cpu_to_le16(CMD_ACT_HALT);
+	usb_tx_block(cardp, cardp->bulk_out_buffer, 4 + S_DS_GEN + sizeof(struct cmd_ds_802_11_reset));
+
+	msleep(100);
+	ret = usb_reset_device(cardp->udev);
+	msleep(100);
+
+	lbs_deb_leave_args(LBS_DEB_USB, "ret %d", ret);
+
+	return ret;
+}
+
+/**
+ *  @brief This function transfer the data to the device.
+ *  @param priv 	pointer to struct lbs_private
+ *  @param payload	pointer to payload data
+ *  @param nb		data length
+ *  @return 	   	0 or -1
+ */
+static int usb_tx_block(struct usb_card_rec *cardp, u8 * payload, u16 nb)
+{
+	int ret = -1;
+
+	/* check if device is removed */
+	if (cardp->surprise_removed) {
+		lbs_deb_usbd(&cardp->udev->dev, "Device removed\n");
+		goto tx_ret;
+	}
+
+	usb_fill_bulk_urb(cardp->tx_urb, cardp->udev,
+			  usb_sndbulkpipe(cardp->udev,
+					  cardp->bulk_out_endpointAddr),
+			  payload, nb, if_usb_write_bulk_callback, cardp);
+
+	cardp->tx_urb->transfer_flags |= URB_ZERO_PACKET;
+
+	if ((ret = usb_submit_urb(cardp->tx_urb, GFP_ATOMIC))) {
+		/*  transfer failed */
+		lbs_deb_usbd(&cardp->udev->dev, "usb_submit_urb failed: %d\n", ret);
+		ret = -1;
+	} else {
+		/* lbs_deb_usbd(&cardp->udev->dev, "usb_submit_urb success\n"); */
+		ret = 0;
+	}
+
+tx_ret:
+	return ret;
+}
+
+static int __if_usb_submit_rx_urb(struct usb_card_rec *cardp,
+				  void (*callbackfn)(struct urb *urb))
+{
+	struct sk_buff *skb;
+	struct read_cb_info *rinfo = &cardp->rinfo;
+	int ret = -1;
+
+	if (!(skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE))) {
+		lbs_pr_err("No free skb\n");
+		goto rx_ret;
+	}
+
+	rinfo->skb = skb;
+
+	/* Fill the receive configuration URB and initialise the Rx call back */
+	usb_fill_bulk_urb(cardp->rx_urb, cardp->udev,
+			  usb_rcvbulkpipe(cardp->udev,
+					  cardp->bulk_in_endpointAddr),
+			  (void *) (skb->tail + (size_t) IPFIELD_ALIGN_OFFSET),
+			  MRVDRV_ETH_RX_PACKET_BUFFER_SIZE, callbackfn,
+			  rinfo);
+
+	cardp->rx_urb->transfer_flags |= URB_ZERO_PACKET;
+
+	/* lbs_deb_usbd(&cardp->udev->dev, "Pointer for rx_urb %p\n", cardp->rx_urb); */
+	if ((ret = usb_submit_urb(cardp->rx_urb, GFP_ATOMIC))) {
+		/* handle failure conditions */
+		lbs_deb_usbd(&cardp->udev->dev, "Submit Rx URB failed: %d\n", ret);
+		kfree_skb(skb);
+		rinfo->skb = NULL;
+		ret = -1;
+	} else {
+		/* lbs_deb_usbd(&cardp->udev->dev, "Submit Rx URB success\n"); */
+		ret = 0;
+	}
+
+rx_ret:
+	return ret;
+}
+
+static int if_usb_submit_rx_urb_fwload(struct usb_card_rec *cardp)
+{
+	return __if_usb_submit_rx_urb(cardp, &if_usb_receive_fwload);
+}
+
+static int if_usb_submit_rx_urb(struct usb_card_rec *cardp)
+{
+	return __if_usb_submit_rx_urb(cardp, &if_usb_receive);
+}
+
+static void if_usb_receive_fwload(struct urb *urb)
+{
+	struct read_cb_info *rinfo = (struct read_cb_info *)urb->context;
+	struct sk_buff *skb = rinfo->skb;
+	struct usb_card_rec *cardp = (struct usb_card_rec *)rinfo->cardp;
+	struct fwsyncheader *syncfwheader;
+	struct bootcmdrespStr bootcmdresp;
+
+	if (urb->status) {
+		lbs_deb_usbd(&cardp->udev->dev,
+			    "URB status is failed during fw load\n");
+		kfree_skb(skb);
+		return;
+	}
+
+	if (cardp->fwdnldover) {
+		__le32 *tmp = (__le32 *)(skb->data + IPFIELD_ALIGN_OFFSET);
+
+		if (tmp[0] == cpu_to_le32(CMD_TYPE_INDICATION) &&
+		    tmp[1] == cpu_to_le32(MACREG_INT_CODE_FIRMWARE_READY)) {
+			lbs_pr_info("Firmware ready event received\n");
+			wake_up(&cardp->fw_wq);
+		} else {
+			lbs_deb_usb("Waiting for confirmation; got %x %x\n", le32_to_cpu(tmp[0]),
+				    le32_to_cpu(tmp[1]));
+			if_usb_submit_rx_urb_fwload(cardp);
+		}
+		kfree_skb(skb);
+		return;
+	}
+	if (cardp->bootcmdresp <= 0) {
+		memcpy (&bootcmdresp, skb->data + IPFIELD_ALIGN_OFFSET,
+			sizeof(bootcmdresp));
+		if (le16_to_cpu(cardp->udev->descriptor.bcdDevice) < 0x3106) {
+			kfree_skb(skb);
+			if_usb_submit_rx_urb_fwload(cardp);
+			cardp->bootcmdresp = 1;
+			lbs_deb_usbd(&cardp->udev->dev,
+				    "Received valid boot command response\n");
+			return;
+		}
+		if (bootcmdresp.u32magicnumber != cpu_to_le32(BOOT_CMD_MAGIC_NUMBER)) {
+			if (bootcmdresp.u32magicnumber == cpu_to_le32(CMD_TYPE_REQUEST) ||
+			    bootcmdresp.u32magicnumber == cpu_to_le32(CMD_TYPE_DATA) ||
+			    bootcmdresp.u32magicnumber == cpu_to_le32(CMD_TYPE_INDICATION)) {
+				if (!cardp->bootcmdresp)
+					lbs_pr_info("Firmware already seems alive; resetting\n");
+				cardp->bootcmdresp = -1;
+			} else {
+				lbs_pr_info("boot cmd response wrong magic number (0x%x)\n",
+					    le32_to_cpu(bootcmdresp.u32magicnumber));
+			}
+		} else if (bootcmdresp.u8cmd_tag != BOOT_CMD_FW_BY_USB) {
+			lbs_pr_info(
+				"boot cmd response cmd_tag error (%d)\n",
+				bootcmdresp.u8cmd_tag);
+		} else if (bootcmdresp.u8result != BOOT_CMD_RESP_OK) {
+			lbs_pr_info(
+				"boot cmd response result error (%d)\n",
+				bootcmdresp.u8result);
+		} else {
+			cardp->bootcmdresp = 1;
+			lbs_deb_usbd(&cardp->udev->dev,
+				    "Received valid boot command response\n");
+		}
+		kfree_skb(skb);
+		if_usb_submit_rx_urb_fwload(cardp);
+		return;
+	}
+
+	syncfwheader = kmalloc(sizeof(struct fwsyncheader), GFP_ATOMIC);
+	if (!syncfwheader) {
+		lbs_deb_usbd(&cardp->udev->dev, "Failure to allocate syncfwheader\n");
+		kfree_skb(skb);
+		return;
+	}
+
+	memcpy(syncfwheader, skb->data + IPFIELD_ALIGN_OFFSET,
+			sizeof(struct fwsyncheader));
+
+	if (!syncfwheader->cmd) {
+		/*
+		lbs_deb_usbd(&cardp->udev->dev,
+			    "FW received Blk with correct CRC\n");
+		lbs_deb_usbd(&cardp->udev->dev,
+			    "FW received Blk seqnum = %d\n",
+		       syncfwheader->seqnum);
+		*/
+		cardp->CRC_OK = 1;
+	} else {
+		lbs_deb_usbd(&cardp->udev->dev,
+			    "FW received Blk with CRC error\n");
+		cardp->CRC_OK = 0;
+	}
+
+	kfree_skb(skb);
+
+	/* reschedule timer for 200ms hence */
+	mod_timer(&cardp->fw_timeout, jiffies + (HZ/5));
+
+	if (cardp->fwfinalblk) {
+		cardp->fwdnldover = 1;
+		goto exit;
+	}
+
+	if_usb_send_fw_pkt(cardp);
+
+ exit:
+	if_usb_submit_rx_urb_fwload(cardp);
+
+	kfree(syncfwheader);
+
+	return;
+}
+
+#define MRVDRV_MIN_PKT_LEN	30
+
+static inline void process_cmdtypedata(int recvlength, struct sk_buff *skb,
+				       struct usb_card_rec *cardp,
+				       struct lbs_private *priv)
+{
+	if (recvlength > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE +
+	    MESSAGE_HEADER_LEN || recvlength < MRVDRV_MIN_PKT_LEN) {
+		lbs_deb_usbd(&cardp->udev->dev,
+			    "Packet length is Invalid\n");
+		kfree_skb(skb);
+		return;
+	}
+
+	skb_reserve(skb, IPFIELD_ALIGN_OFFSET);
+	skb_put(skb, recvlength);
+	skb_pull(skb, MESSAGE_HEADER_LEN);
+	lbs_process_rxed_packet(priv, skb);
+	priv->upld_len = (recvlength - MESSAGE_HEADER_LEN);
+}
+
+static inline void process_cmdrequest(int recvlength, u8 *recvbuff,
+				      struct sk_buff *skb,
+				      struct usb_card_rec *cardp,
+				      struct lbs_private *priv)
+{
+	u8 *cmdbuf;
+	if (recvlength > LBS_CMD_BUFFER_SIZE) {
+		lbs_deb_usbd(&cardp->udev->dev,
+			    "The receive buffer is too large\n");
+		kfree_skb(skb);
+		return;
+	}
+
+	if (!in_interrupt())
+		BUG();
+
+	spin_lock(&priv->driver_lock);
+	/* take care of cur_cmd = NULL case by reading the
+	 * data to clear the interrupt */
+	if (!priv->cur_cmd) {
+		cmdbuf = priv->upld_buf;
+		priv->hisregcpy &= ~MRVDRV_CMD_UPLD_RDY;
+	} else
+		cmdbuf = (u8 *) priv->cur_cmd->cmdbuf;
+
+	cardp->usb_int_cause |= MRVDRV_CMD_UPLD_RDY;
+	priv->upld_len = (recvlength - MESSAGE_HEADER_LEN);
+	memcpy(cmdbuf, recvbuff + MESSAGE_HEADER_LEN,
+	       priv->upld_len);
+
+	kfree_skb(skb);
+	lbs_interrupt(priv);
+	spin_unlock(&priv->driver_lock);
+
+	lbs_deb_usbd(&cardp->udev->dev,
+		    "Wake up main thread to handle cmd response\n");
+
+	return;
+}
+
+/**
+ *  @brief This function reads of the packet into the upload buff,
+ *  wake up the main thread and initialise the Rx callack.
+ *
+ *  @param urb		pointer to struct urb
+ *  @return 	   	N/A
+ */
+static void if_usb_receive(struct urb *urb)
+{
+	struct read_cb_info *rinfo = (struct read_cb_info *)urb->context;
+	struct sk_buff *skb = rinfo->skb;
+	struct usb_card_rec *cardp = (struct usb_card_rec *) rinfo->cardp;
+	struct lbs_private *priv = cardp->priv;
+
+	int recvlength = urb->actual_length;
+	u8 *recvbuff = NULL;
+	u32 recvtype = 0;
+
+	lbs_deb_enter(LBS_DEB_USB);
+
+	if (recvlength) {
+		__le32 tmp;
+
+		if (urb->status) {
+			lbs_deb_usbd(&cardp->udev->dev,
+				    "URB status is failed\n");
+			kfree_skb(skb);
+			goto setup_for_next;
+		}
+
+		recvbuff = skb->data + IPFIELD_ALIGN_OFFSET;
+		memcpy(&tmp, recvbuff, sizeof(u32));
+		recvtype = le32_to_cpu(tmp);
+		lbs_deb_usbd(&cardp->udev->dev,
+			    "Recv length = 0x%x, Recv type = 0x%X\n",
+			    recvlength, recvtype);
+	} else if (urb->status) {
+		kfree_skb(skb);
+		goto rx_exit;
+	}
+
+	switch (recvtype) {
+	case CMD_TYPE_DATA:
+		process_cmdtypedata(recvlength, skb, cardp, priv);
+		break;
+
+	case CMD_TYPE_REQUEST:
+		process_cmdrequest(recvlength, recvbuff, skb, cardp, priv);
+		break;
+
+	case CMD_TYPE_INDICATION:
+		/* Event cause handling */
+		spin_lock(&priv->driver_lock);
+		cardp->usb_event_cause = le32_to_cpu(*(__le32 *) (recvbuff + MESSAGE_HEADER_LEN));
+		lbs_deb_usbd(&cardp->udev->dev,"**EVENT** 0x%X\n",
+			    cardp->usb_event_cause);
+		if (cardp->usb_event_cause & 0xffff0000) {
+			lbs_send_tx_feedback(priv);
+			spin_unlock(&priv->driver_lock);
+			break;
+		}
+		cardp->usb_event_cause <<= 3;
+		cardp->usb_int_cause |= MRVDRV_CARDEVENT;
+		kfree_skb(skb);
+		lbs_interrupt(priv);
+		spin_unlock(&priv->driver_lock);
+		goto rx_exit;
+	default:
+		lbs_deb_usbd(&cardp->udev->dev, "Unknown command type 0x%X\n",
+		             recvtype);
+		kfree_skb(skb);
+		break;
+	}
+
+setup_for_next:
+	if_usb_submit_rx_urb(cardp);
+rx_exit:
+	lbs_deb_leave(LBS_DEB_USB);
+}
+
+/**
+ *  @brief This function downloads data to FW
+ *  @param priv		pointer to struct lbs_private structure
+ *  @param type		type of data
+ *  @param buf		pointer to data buffer
+ *  @param len		number of bytes
+ *  @return 	   	0 or -1
+ */
+static int if_usb_host_to_card(struct lbs_private *priv,
+	u8 type,
+	u8 *payload,
+	u16 nb)
+{
+	struct usb_card_rec *cardp = (struct usb_card_rec *)priv->card;
+
+	lbs_deb_usbd(&cardp->udev->dev,"*** type = %u\n", type);
+	lbs_deb_usbd(&cardp->udev->dev,"size after = %d\n", nb);
+
+	if (type == MVMS_CMD) {
+		__le32 tmp = cpu_to_le32(CMD_TYPE_REQUEST);
+		priv->dnld_sent = DNLD_CMD_SENT;
+		memcpy(cardp->bulk_out_buffer, (u8 *) & tmp,
+		       MESSAGE_HEADER_LEN);
+
+	} else {
+		__le32 tmp = cpu_to_le32(CMD_TYPE_DATA);
+		priv->dnld_sent = DNLD_DATA_SENT;
+		memcpy(cardp->bulk_out_buffer, (u8 *) & tmp,
+		       MESSAGE_HEADER_LEN);
+	}
+
+	memcpy((cardp->bulk_out_buffer + MESSAGE_HEADER_LEN), payload, nb);
+
+	return usb_tx_block(cardp, cardp->bulk_out_buffer,
+	                    nb + MESSAGE_HEADER_LEN);
+}
+
+/* called with priv->driver_lock held */
+static int if_usb_get_int_status(struct lbs_private *priv, u8 *ireg)
+{
+	struct usb_card_rec *cardp = priv->card;
+
+	*ireg = cardp->usb_int_cause;
+	cardp->usb_int_cause = 0;
+
+	lbs_deb_usbd(&cardp->udev->dev,"Int cause is 0x%X\n", *ireg);
+
+	return 0;
+}
+
+static int if_usb_read_event_cause(struct lbs_private *priv)
+{
+	struct usb_card_rec *cardp = priv->card;
+
+	priv->eventcause = cardp->usb_event_cause;
+	/* Re-submit rx urb here to avoid event lost issue */
+	if_usb_submit_rx_urb(cardp);
+	return 0;
+}
+
+/**
+ *  @brief This function issues Boot command to the Boot2 code
+ *  @param ivalue   1:Boot from FW by USB-Download
+ *                  2:Boot from FW in EEPROM
+ *  @return 	   	0
+ */
+static int if_usb_issue_boot_command(struct usb_card_rec *cardp, int ivalue)
+{
+	struct bootcmdstr sbootcmd;
+	int i;
+
+	/* Prepare command */
+	sbootcmd.u32magicnumber = cpu_to_le32(BOOT_CMD_MAGIC_NUMBER);
+	sbootcmd.u8cmd_tag = ivalue;
+	for (i=0; i<11; i++)
+		sbootcmd.au8dumy[i]=0x00;
+	memcpy(cardp->bulk_out_buffer, &sbootcmd, sizeof(struct bootcmdstr));
+
+	/* Issue command */
+	usb_tx_block(cardp, cardp->bulk_out_buffer, sizeof(struct bootcmdstr));
+
+	return 0;
+}
+
+
+/**
+ *  @brief This function checks the validity of Boot2/FW image.
+ *
+ *  @param data              pointer to image
+ *         len               image length
+ *  @return     0 or -1
+ */
+static int check_fwfile_format(u8 *data, u32 totlen)
+{
+	u32 bincmd, exit;
+	u32 blksize, offset, len;
+	int ret;
+
+	ret = 1;
+	exit = len = 0;
+
+	do {
+		struct fwheader *fwh = (void *)data;
+
+		bincmd = le32_to_cpu(fwh->dnldcmd);
+		blksize = le32_to_cpu(fwh->datalength);
+		switch (bincmd) {
+		case FW_HAS_DATA_TO_RECV:
+			offset = sizeof(struct fwheader) + blksize;
+			data += offset;
+			len += offset;
+			if (len >= totlen)
+				exit = 1;
+			break;
+		case FW_HAS_LAST_BLOCK:
+			exit = 1;
+			ret = 0;
+			break;
+		default:
+			exit = 1;
+			break;
+		}
+	} while (!exit);
+
+	if (ret)
+		lbs_pr_err("firmware file format check FAIL\n");
+	else
+		lbs_deb_fw("firmware file format check PASS\n");
+
+	return ret;
+}
+
+
+static int if_usb_prog_firmware(struct usb_card_rec *cardp)
+{
+	int i = 0;
+	static int reset_count = 10;
+	int ret = 0;
+
+	lbs_deb_enter(LBS_DEB_USB);
+
+	if ((ret = request_firmware(&cardp->fw, lbs_fw_name,
+				    &cardp->udev->dev)) < 0) {
+		lbs_pr_err("request_firmware() failed with %#x\n", ret);
+		lbs_pr_err("firmware %s not found\n", lbs_fw_name);
+		goto done;
+	}
+
+	if (check_fwfile_format(cardp->fw->data, cardp->fw->size))
+		goto release_fw;
+
+restart:
+	if (if_usb_submit_rx_urb_fwload(cardp) < 0) {
+		lbs_deb_usbd(&cardp->udev->dev, "URB submission is failed\n");
+		ret = -1;
+		goto release_fw;
+	}
+
+	cardp->bootcmdresp = 0;
+	do {
+		int j = 0;
+		i++;
+		/* Issue Boot command = 1, Boot from Download-FW */
+		if_usb_issue_boot_command(cardp, BOOT_CMD_FW_BY_USB);
+		/* wait for command response */
+		do {
+			j++;
+			msleep_interruptible(100);
+		} while (cardp->bootcmdresp == 0 && j < 10);
+	} while (cardp->bootcmdresp == 0 && i < 5);
+
+	if (cardp->bootcmdresp <= 0) {
+		if (--reset_count >= 0) {
+			if_usb_reset_device(cardp);
+			goto restart;
+		}
+		return -1;
+	}
+
+	i = 0;
+
+	cardp->totalbytes = 0;
+	cardp->fwlastblksent = 0;
+	cardp->CRC_OK = 1;
+	cardp->fwdnldover = 0;
+	cardp->fwseqnum = -1;
+	cardp->totalbytes = 0;
+	cardp->fwfinalblk = 0;
+
+	/* Send the first firmware packet... */
+	if_usb_send_fw_pkt(cardp);
+
+	/* ... and wait for the process to complete */
+	wait_event_interruptible(cardp->fw_wq, cardp->surprise_removed || cardp->fwdnldover);
+	
+	del_timer_sync(&cardp->fw_timeout);
+	usb_kill_urb(cardp->rx_urb);
+
+	if (!cardp->fwdnldover) {
+		lbs_pr_info("failed to load fw, resetting device!\n");
+		if (--reset_count >= 0) {
+			if_usb_reset_device(cardp);
+			goto restart;
+		}
+
+		lbs_pr_info("FW download failure, time = %d ms\n", i * 100);
+		ret = -1;
+		goto release_fw;
+	}
+
+release_fw:
+	release_firmware(cardp->fw);
+	cardp->fw = NULL;
+
+done:
+	lbs_deb_leave_args(LBS_DEB_USB, "ret %d", ret);
+	return ret;
+}
+
+
+#ifdef CONFIG_PM
+static int if_usb_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct usb_card_rec *cardp = usb_get_intfdata(intf);
+	struct lbs_private *priv = cardp->priv;
+
+	lbs_deb_enter(LBS_DEB_USB);
+
+	if (priv->psstate != PS_STATE_FULL_POWER)
+		return -1;
+
+	netif_device_detach(priv->dev);
+	netif_device_detach(priv->mesh_dev);
+
+	/* Unlink tx & rx urb */
+	usb_kill_urb(cardp->tx_urb);
+	usb_kill_urb(cardp->rx_urb);
+
+	lbs_deb_leave(LBS_DEB_USB);
+	return 0;
+}
+
+static int if_usb_resume(struct usb_interface *intf)
+{
+	struct usb_card_rec *cardp = usb_get_intfdata(intf);
+	struct lbs_private *priv = cardp->priv;
+
+	lbs_deb_enter(LBS_DEB_USB);
+
+	if_usb_submit_rx_urb(cardp);
+
+	netif_device_attach(priv->dev);
+	netif_device_attach(priv->mesh_dev);
+
+	lbs_deb_leave(LBS_DEB_USB);
+	return 0;
+}
+#else
+#define if_usb_suspend NULL
+#define if_usb_resume NULL
+#endif
+
+static struct usb_driver if_usb_driver = {
+	.name = DRV_NAME,
+	.probe = if_usb_probe,
+	.disconnect = if_usb_disconnect,
+	.id_table = if_usb_table,
+	.suspend = if_usb_suspend,
+	.resume = if_usb_resume,
+};
+
+static int __init if_usb_init_module(void)
+{
+	int ret = 0;
+
+	lbs_deb_enter(LBS_DEB_MAIN);
+
+	ret = usb_register(&if_usb_driver);
+
+	lbs_deb_leave_args(LBS_DEB_MAIN, "ret %d", ret);
+	return ret;
+}
+
+static void __exit if_usb_exit_module(void)
+{
+	lbs_deb_enter(LBS_DEB_MAIN);
+
+	usb_deregister(&if_usb_driver);
+
+	lbs_deb_leave(LBS_DEB_MAIN);
+}
+
+module_init(if_usb_init_module);
+module_exit(if_usb_exit_module);
+
+MODULE_DESCRIPTION("8388 USB WLAN Driver");
+MODULE_AUTHOR("Marvell International Ltd.");
+MODULE_LICENSE("GPL");

+ 108 - 0
package/libertas/src/if_usb.h

@@ -0,0 +1,108 @@
+#ifndef _LBS_IF_USB_H
+#define _LBS_IF_USB_H
+
+#include <linux/wait.h>
+#include <linux/timer.h>
+
+struct lbs_private;
+
+/**
+  * This file contains definition for USB interface.
+  */
+#define CMD_TYPE_REQUEST                0xF00DFACE
+#define CMD_TYPE_DATA                   0xBEADC0DE
+#define CMD_TYPE_INDICATION             0xBEEFFACE
+
+#define IPFIELD_ALIGN_OFFSET	2
+
+#define BOOT_CMD_FW_BY_USB     0x01
+#define BOOT_CMD_FW_IN_EEPROM  0x02
+#define BOOT_CMD_UPDATE_BOOT2  0x03
+#define BOOT_CMD_UPDATE_FW     0x04
+#define BOOT_CMD_MAGIC_NUMBER  0x4C56524D   /* M=>0x4D,R=>0x52,V=>0x56,L=>0x4C */
+
+struct bootcmdstr
+{
+	__le32 u32magicnumber;
+	u8  u8cmd_tag;
+	u8  au8dumy[11];
+};
+
+#define BOOT_CMD_RESP_OK     0x0001
+#define BOOT_CMD_RESP_FAIL   0x0000
+
+struct bootcmdrespStr
+{
+	__le32 u32magicnumber;
+	u8  u8cmd_tag;
+	u8  u8result;
+	u8  au8dumy[2];
+};
+
+/* read callback private data */
+struct read_cb_info {
+        struct usb_card_rec *cardp;
+        struct sk_buff *skb;
+};
+
+/** USB card description structure*/
+struct usb_card_rec {
+	struct usb_device *udev;
+	struct urb *rx_urb, *tx_urb;
+	struct lbs_private *priv;
+	struct read_cb_info rinfo;
+
+	int bulk_in_size;
+	u8 bulk_in_endpointAddr;
+
+	u8 *bulk_out_buffer;
+	int bulk_out_size;
+	u8 bulk_out_endpointAddr;
+
+	const struct firmware *fw;
+	struct timer_list fw_timeout;
+	wait_queue_head_t fw_wq;
+	u8 CRC_OK;
+	u32 fwseqnum;
+	u32 lastseqnum;
+	u32 totalbytes;
+	u32 fwlastblksent;
+	u8 fwdnldover;
+	u8 fwfinalblk;
+	u8 surprise_removed;
+
+	u32 usb_event_cause;
+	u8 usb_int_cause;
+
+	s8 bootcmdresp;
+};
+
+/** fwheader */
+struct fwheader {
+	__le32 dnldcmd;
+	__le32 baseaddr;
+	__le32 datalength;
+	__le32 CRC;
+};
+
+#define FW_MAX_DATA_BLK_SIZE	600
+/** FWData */
+struct FWData {
+	struct fwheader fwheader;
+	__le32 seqnum;
+	u8 data[FW_MAX_DATA_BLK_SIZE];
+};
+
+/** fwsyncheader */
+struct fwsyncheader {
+	__le32 cmd;
+	__le32 seqnum;
+};
+
+#define FW_HAS_DATA_TO_RECV		0x00000001
+#define FW_HAS_LAST_BLOCK		0x00000004
+
+#define FW_DATA_XMIT_SIZE \
+	sizeof(struct fwheader) + le32_to_cpu(fwdata->fwheader.datalength) + sizeof(u32)
+
+#endif

+ 894 - 0
package/libertas/src/join.c

@@ -0,0 +1,894 @@
+/**
+  *  Functions implementing wlan infrastructure and adhoc join routines,
+  *  IOCTL handlers as well as command preperation and response routines
+  *  for sending adhoc start, adhoc join, and association commands
+  *  to the firmware.
+  */
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/wireless.h>
+#include <linux/etherdevice.h>
+
+#include <net/iw_handler.h>
+
+#include "host.h"
+#include "decl.h"
+#include "join.h"
+#include "dev.h"
+#include "assoc.h"
+
+/* The firmware needs certain bits masked out of the beacon-derviced capability
+ * field when associating/joining to BSSs.
+ */
+#define CAPINFO_MASK	(~(0xda00))
+
+/**
+ *  @brief This function finds common rates between rate1 and card rates.
+ *
+ * It will fill common rates in rate1 as output if found.
+ *
+ * NOTE: Setting the MSB of the basic rates need to be taken
+ *   care, either before or after calling this function
+ *
+ *  @param priv     A pointer to struct lbs_private structure
+ *  @param rate1       the buffer which keeps input and output
+ *  @param rate1_size  the size of rate1 buffer; new size of buffer on return
+ *
+ *  @return            0 or -1
+ */
+static int get_common_rates(struct lbs_private *priv,
+	u8 *rates,
+	u16 *rates_size)
+{
+	u8 *card_rates = lbs_bg_rates;
+	size_t num_card_rates = sizeof(lbs_bg_rates);
+	int ret = 0, i, j;
+	u8 tmp[30];
+	size_t tmp_size = 0;
+
+	/* For each rate in card_rates that exists in rate1, copy to tmp */
+	for (i = 0; card_rates[i] && (i < num_card_rates); i++) {
+		for (j = 0; rates[j] && (j < *rates_size); j++) {
+			if (rates[j] == card_rates[i])
+				tmp[tmp_size++] = card_rates[i];
+		}
+	}
+
+	lbs_deb_hex(LBS_DEB_JOIN, "AP rates    ", rates, *rates_size);
+	lbs_deb_hex(LBS_DEB_JOIN, "card rates  ", card_rates, num_card_rates);
+	lbs_deb_hex(LBS_DEB_JOIN, "common rates", tmp, tmp_size);
+	lbs_deb_join("TX data rate 0x%02x\n", priv->cur_rate);
+
+	if (!priv->auto_rate) {
+		for (i = 0; i < tmp_size; i++) {
+			if (tmp[i] == priv->cur_rate)
+				goto done;
+		}
+		lbs_pr_alert("Previously set fixed data rate %#x isn't "
+		       "compatible with the network.\n", priv->cur_rate);
+		ret = -1;
+		goto done;
+	}
+	ret = 0;
+
+done:
+	memset(rates, 0, *rates_size);
+	*rates_size = min_t(int, tmp_size, *rates_size);
+	memcpy(rates, tmp, *rates_size);
+	return ret;
+}
+
+
+/**
+ *  @brief Sets the MSB on basic rates as the firmware requires
+ *
+ * Scan through an array and set the MSB for basic data rates.
+ *
+ *  @param rates     buffer of data rates
+ *  @param len       size of buffer
+ */
+static void lbs_set_basic_rate_flags(u8 *rates, size_t len)
+{
+	int i;
+
+	for (i = 0; i < len; i++) {
+		if (rates[i] == 0x02 || rates[i] == 0x04 ||
+		    rates[i] == 0x0b || rates[i] == 0x16)
+			rates[i] |= 0x80;
+	}
+}
+
+/**
+ *  @brief Unsets the MSB on basic rates
+ *
+ * Scan through an array and unset the MSB for basic data rates.
+ *
+ *  @param rates     buffer of data rates
+ *  @param len       size of buffer
+ */
+void lbs_unset_basic_rate_flags(u8 *rates, size_t len)
+{
+	int i;
+
+	for (i = 0; i < len; i++)
+		rates[i] &= 0x7f;
+}
+
+
+/**
+ *  @brief Associate to a specific BSS discovered in a scan
+ *
+ *  @param priv      A pointer to struct lbs_private structure
+ *  @param pbssdesc  Pointer to the BSS descriptor to associate with.
+ *
+ *  @return          0-success, otherwise fail
+ */
+int lbs_associate(struct lbs_private *priv, struct assoc_request *assoc_req)
+{
+	int ret;
+
+	lbs_deb_enter(LBS_DEB_ASSOC);
+
+	ret = lbs_prepare_and_send_command(priv, CMD_802_11_AUTHENTICATE,
+				    0, CMD_OPTION_WAITFORRSP,
+				    0, assoc_req->bss.bssid);
+
+	if (ret)
+		goto done;
+
+	/* set preamble to firmware */
+	if (   (priv->capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
+	    && (assoc_req->bss.capability & WLAN_CAPABILITY_SHORT_PREAMBLE))
+		priv->preamble = CMD_TYPE_SHORT_PREAMBLE;
+	else
+		priv->preamble = CMD_TYPE_LONG_PREAMBLE;
+
+	lbs_set_radio_control(priv);
+
+	ret = lbs_prepare_and_send_command(priv, CMD_802_11_ASSOCIATE,
+				    0, CMD_OPTION_WAITFORRSP, 0, assoc_req);
+
+done:
+	lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
+	return ret;
+}
+
+/**
+ *  @brief Start an Adhoc Network
+ *
+ *  @param priv         A pointer to struct lbs_private structure
+ *  @param adhocssid    The ssid of the Adhoc Network
+ *  @return             0--success, -1--fail
+ */
+int lbs_start_adhoc_network(struct lbs_private *priv,
+	struct assoc_request *assoc_req)
+{
+	int ret = 0;
+
+	priv->adhoccreate = 1;
+
+	if (priv->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) {
+		lbs_deb_join("AdhocStart: Short preamble\n");
+		priv->preamble = CMD_TYPE_SHORT_PREAMBLE;
+	} else {
+		lbs_deb_join("AdhocStart: Long preamble\n");
+		priv->preamble = CMD_TYPE_LONG_PREAMBLE;
+	}
+
+	lbs_set_radio_control(priv);
+
+	lbs_deb_join("AdhocStart: channel = %d\n", assoc_req->channel);
+	lbs_deb_join("AdhocStart: band = %d\n", assoc_req->band);
+
+	ret = lbs_prepare_and_send_command(priv, CMD_802_11_AD_HOC_START,
+				    0, CMD_OPTION_WAITFORRSP, 0, assoc_req);
+
+	return ret;
+}
+
+/**
+ *  @brief Join an adhoc network found in a previous scan
+ *
+ *  @param priv         A pointer to struct lbs_private structure
+ *  @param pbssdesc     Pointer to a BSS descriptor found in a previous scan
+ *                      to attempt to join
+ *
+ *  @return             0--success, -1--fail
+ */
+int lbs_join_adhoc_network(struct lbs_private *priv,
+	struct assoc_request *assoc_req)
+{
+	struct bss_descriptor * bss = &assoc_req->bss;
+	int ret = 0;
+
+	lbs_deb_join("%s: Current SSID '%s', ssid length %u\n",
+	             __func__,
+	             escape_essid(priv->curbssparams.ssid,
+	                          priv->curbssparams.ssid_len),
+	             priv->curbssparams.ssid_len);
+	lbs_deb_join("%s: requested ssid '%s', ssid length %u\n",
+	             __func__, escape_essid(bss->ssid, bss->ssid_len),
+	             bss->ssid_len);
+
+	/* check if the requested SSID is already joined */
+	if (   priv->curbssparams.ssid_len
+	    && !lbs_ssid_cmp(priv->curbssparams.ssid,
+	                          priv->curbssparams.ssid_len,
+	                          bss->ssid, bss->ssid_len)
+	    && (priv->mode == IW_MODE_ADHOC)
+	    && (priv->connect_status == LBS_CONNECTED)) {
+		union iwreq_data wrqu;
+
+		lbs_deb_join("ADHOC_J_CMD: New ad-hoc SSID is the same as "
+		             "current, not attempting to re-join");
+
+		/* Send the re-association event though, because the association
+		 * request really was successful, even if just a null-op.
+		 */
+		memset(&wrqu, 0, sizeof(wrqu));
+		memcpy(wrqu.ap_addr.sa_data, priv->curbssparams.bssid,
+		       ETH_ALEN);
+		wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+		wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
+		goto out;
+	}
+
+	/* Use shortpreamble only when both creator and card supports
+	   short preamble */
+	if (   !(bss->capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
+	    || !(priv->capability & WLAN_CAPABILITY_SHORT_PREAMBLE)) {
+		lbs_deb_join("AdhocJoin: Long preamble\n");
+		priv->preamble = CMD_TYPE_LONG_PREAMBLE;
+	} else {
+		lbs_deb_join("AdhocJoin: Short preamble\n");
+		priv->preamble = CMD_TYPE_SHORT_PREAMBLE;
+	}
+
+	lbs_set_radio_control(priv);
+
+	lbs_deb_join("AdhocJoin: channel = %d\n", assoc_req->channel);
+	lbs_deb_join("AdhocJoin: band = %c\n", assoc_req->band);
+
+	priv->adhoccreate = 0;
+
+	ret = lbs_prepare_and_send_command(priv, CMD_802_11_AD_HOC_JOIN,
+				    0, CMD_OPTION_WAITFORRSP,
+				    OID_802_11_SSID, assoc_req);
+
+out:
+	return ret;
+}
+
+int lbs_stop_adhoc_network(struct lbs_private *priv)
+{
+	return lbs_prepare_and_send_command(priv, CMD_802_11_AD_HOC_STOP,
+				     0, CMD_OPTION_WAITFORRSP, 0, NULL);
+}
+
+/**
+ *  @brief Send Deauthentication Request
+ *
+ *  @param priv      A pointer to struct lbs_private structure
+ *  @return          0--success, -1--fail
+ */
+int lbs_send_deauthentication(struct lbs_private *priv)
+{
+	return lbs_prepare_and_send_command(priv, CMD_802_11_DEAUTHENTICATE,
+				     0, CMD_OPTION_WAITFORRSP, 0, NULL);
+}
+
+/**
+ *  @brief This function prepares command of authenticate.
+ *
+ *  @param priv      A pointer to struct lbs_private structure
+ *  @param cmd       A pointer to cmd_ds_command structure
+ *  @param pdata_buf Void cast of pointer to a BSSID to authenticate with
+ *
+ *  @return         0 or -1
+ */
+int lbs_cmd_80211_authenticate(struct lbs_private *priv,
+				 struct cmd_ds_command *cmd,
+				 void *pdata_buf)
+{
+	struct cmd_ds_802_11_authenticate *pauthenticate = &cmd->params.auth;
+	int ret = -1;
+	u8 *bssid = pdata_buf;
+	DECLARE_MAC_BUF(mac);
+
+	lbs_deb_enter(LBS_DEB_JOIN);
+
+	cmd->command = cpu_to_le16(CMD_802_11_AUTHENTICATE);
+	cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_authenticate)
+	                        + S_DS_GEN);
+
+	/* translate auth mode to 802.11 defined wire value */
+	switch (priv->secinfo.auth_mode) {
+	case IW_AUTH_ALG_OPEN_SYSTEM:
+		pauthenticate->authtype = 0x00;
+		break;
+	case IW_AUTH_ALG_SHARED_KEY:
+		pauthenticate->authtype = 0x01;
+		break;
+	case IW_AUTH_ALG_LEAP:
+		pauthenticate->authtype = 0x80;
+		break;
+	default:
+		lbs_deb_join("AUTH_CMD: invalid auth alg 0x%X\n",
+		             priv->secinfo.auth_mode);
+		goto out;
+	}
+
+	memcpy(pauthenticate->macaddr, bssid, ETH_ALEN);
+
+	lbs_deb_join("AUTH_CMD: BSSID %s, auth 0x%x\n",
+	             print_mac(mac, bssid), pauthenticate->authtype);
+	ret = 0;
+
+out:
+	lbs_deb_leave_args(LBS_DEB_JOIN, "ret %d", ret);
+	return ret;
+}
+
+int lbs_cmd_80211_deauthenticate(struct lbs_private *priv,
+				   struct cmd_ds_command *cmd)
+{
+	struct cmd_ds_802_11_deauthenticate *dauth = &cmd->params.deauth;
+
+	lbs_deb_enter(LBS_DEB_JOIN);
+
+	cmd->command = cpu_to_le16(CMD_802_11_DEAUTHENTICATE);
+	cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_deauthenticate) +
+			     S_DS_GEN);
+
+	/* set AP MAC address */
+	memmove(dauth->macaddr, priv->curbssparams.bssid, ETH_ALEN);
+
+	/* Reason code 3 = Station is leaving */
+#define REASON_CODE_STA_LEAVING 3
+	dauth->reasoncode = cpu_to_le16(REASON_CODE_STA_LEAVING);
+
+	lbs_deb_leave(LBS_DEB_JOIN);
+	return 0;
+}
+
+int lbs_cmd_80211_associate(struct lbs_private *priv,
+			      struct cmd_ds_command *cmd, void *pdata_buf)
+{
+	struct cmd_ds_802_11_associate *passo = &cmd->params.associate;
+	int ret = 0;
+	struct assoc_request * assoc_req = pdata_buf;
+	struct bss_descriptor * bss = &assoc_req->bss;
+	u8 *pos;
+	u16 tmpcap, tmplen;
+	struct mrvlietypes_ssidparamset *ssid;
+	struct mrvlietypes_phyparamset *phy;
+	struct mrvlietypes_ssparamset *ss;
+	struct mrvlietypes_ratesparamset *rates;
+	struct mrvlietypes_rsnparamset *rsn;
+
+	lbs_deb_enter(LBS_DEB_ASSOC);
+
+	pos = (u8 *) passo;
+
+	if (!priv) {
+		ret = -1;
+		goto done;
+	}
+
+	cmd->command = cpu_to_le16(CMD_802_11_ASSOCIATE);
+
+	memcpy(passo->peerstaaddr, bss->bssid, sizeof(passo->peerstaaddr));
+	pos += sizeof(passo->peerstaaddr);
+
+	/* set the listen interval */
+	passo->listeninterval = cpu_to_le16(MRVDRV_DEFAULT_LISTEN_INTERVAL);
+
+	pos += sizeof(passo->capability);
+	pos += sizeof(passo->listeninterval);
+	pos += sizeof(passo->bcnperiod);
+	pos += sizeof(passo->dtimperiod);
+
+	ssid = (struct mrvlietypes_ssidparamset *) pos;
+	ssid->header.type = cpu_to_le16(TLV_TYPE_SSID);
+	tmplen = bss->ssid_len;
+	ssid->header.len = cpu_to_le16(tmplen);
+	memcpy(ssid->ssid, bss->ssid, tmplen);
+	pos += sizeof(ssid->header) + tmplen;
+
+	phy = (struct mrvlietypes_phyparamset *) pos;
+	phy->header.type = cpu_to_le16(TLV_TYPE_PHY_DS);
+	tmplen = sizeof(phy->fh_ds.dsparamset);
+	phy->header.len = cpu_to_le16(tmplen);
+	memcpy(&phy->fh_ds.dsparamset,
+	       &bss->phyparamset.dsparamset.currentchan,
+	       tmplen);
+	pos += sizeof(phy->header) + tmplen;
+
+	ss = (struct mrvlietypes_ssparamset *) pos;
+	ss->header.type = cpu_to_le16(TLV_TYPE_CF);
+	tmplen = sizeof(ss->cf_ibss.cfparamset);
+	ss->header.len = cpu_to_le16(tmplen);
+	pos += sizeof(ss->header) + tmplen;
+
+	rates = (struct mrvlietypes_ratesparamset *) pos;
+	rates->header.type = cpu_to_le16(TLV_TYPE_RATES);
+	memcpy(&rates->rates, &bss->rates, MAX_RATES);
+	tmplen = MAX_RATES;
+	if (get_common_rates(priv, rates->rates, &tmplen)) {
+		ret = -1;
+		goto done;
+	}
+	pos += sizeof(rates->header) + tmplen;
+	rates->header.len = cpu_to_le16(tmplen);
+	lbs_deb_assoc("ASSOC_CMD: num rates %u\n", tmplen);
+
+	/* Copy the infra. association rates into Current BSS state structure */
+	memset(&priv->curbssparams.rates, 0, sizeof(priv->curbssparams.rates));
+	memcpy(&priv->curbssparams.rates, &rates->rates, tmplen);
+
+	/* Set MSB on basic rates as the firmware requires, but _after_
+	 * copying to current bss rates.
+	 */
+	lbs_set_basic_rate_flags(rates->rates, tmplen);
+
+	if (assoc_req->secinfo.WPAenabled || assoc_req->secinfo.WPA2enabled) {
+		rsn = (struct mrvlietypes_rsnparamset *) pos;
+		/* WPA_IE or WPA2_IE */
+		rsn->header.type = cpu_to_le16((u16) assoc_req->wpa_ie[0]);
+		tmplen = (u16) assoc_req->wpa_ie[1];
+		rsn->header.len = cpu_to_le16(tmplen);
+		memcpy(rsn->rsnie, &assoc_req->wpa_ie[2], tmplen);
+		lbs_deb_hex(LBS_DEB_JOIN, "ASSOC_CMD: RSN IE", (u8 *) rsn,
+			sizeof(rsn->header) + tmplen);
+		pos += sizeof(rsn->header) + tmplen;
+	}
+
+	/* update curbssparams */
+	priv->curbssparams.channel = bss->phyparamset.dsparamset.currentchan;
+
+	if (lbs_parse_dnld_countryinfo_11d(priv, bss)) {
+		ret = -1;
+		goto done;
+	}
+
+	cmd->size = cpu_to_le16((u16) (pos - (u8 *) passo) + S_DS_GEN);
+
+	/* set the capability info */
+	tmpcap = (bss->capability & CAPINFO_MASK);
+	if (bss->mode == IW_MODE_INFRA)
+		tmpcap |= WLAN_CAPABILITY_ESS;
+	passo->capability = cpu_to_le16(tmpcap);
+	lbs_deb_assoc("ASSOC_CMD: capability 0x%04x\n", tmpcap);
+
+done:
+	lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
+	return ret;
+}
+
+int lbs_cmd_80211_ad_hoc_start(struct lbs_private *priv,
+				 struct cmd_ds_command *cmd, void *pdata_buf)
+{
+	struct cmd_ds_802_11_ad_hoc_start *adhs = &cmd->params.ads;
+	int ret = 0;
+	int cmdappendsize = 0;
+	struct assoc_request * assoc_req = pdata_buf;
+	u16 tmpcap = 0;
+	size_t ratesize = 0;
+
+	lbs_deb_enter(LBS_DEB_JOIN);
+
+	if (!priv) {
+		ret = -1;
+		goto done;
+	}
+
+	cmd->command = cpu_to_le16(CMD_802_11_AD_HOC_START);
+
+	/*
+	 * Fill in the parameters for 2 data structures:
+	 *   1. cmd_ds_802_11_ad_hoc_start command
+	 *   2. priv->scantable[i]
+	 *
+	 * Driver will fill up SSID, bsstype,IBSS param, Physical Param,
+	 *   probe delay, and cap info.
+	 *
+	 * Firmware will fill up beacon period, DTIM, Basic rates
+	 *   and operational rates.
+	 */
+
+	memset(adhs->ssid, 0, IW_ESSID_MAX_SIZE);
+	memcpy(adhs->ssid, assoc_req->ssid, assoc_req->ssid_len);
+
+	lbs_deb_join("ADHOC_S_CMD: SSID '%s', ssid length %u\n",
+	             escape_essid(assoc_req->ssid, assoc_req->ssid_len),
+	             assoc_req->ssid_len);
+
+	/* set the BSS type */
+	adhs->bsstype = CMD_BSS_TYPE_IBSS;
+	priv->mode = IW_MODE_ADHOC;
+	if (priv->beacon_period == 0)
+		priv->beacon_period = MRVDRV_BEACON_INTERVAL;
+	adhs->beaconperiod = cpu_to_le16(priv->beacon_period);
+
+	/* set Physical param set */
+#define DS_PARA_IE_ID   3
+#define DS_PARA_IE_LEN  1
+
+	adhs->phyparamset.dsparamset.elementid = DS_PARA_IE_ID;
+	adhs->phyparamset.dsparamset.len = DS_PARA_IE_LEN;
+
+	WARN_ON(!assoc_req->channel);
+
+	lbs_deb_join("ADHOC_S_CMD: Creating ADHOC on channel %d\n",
+		     assoc_req->channel);
+
+	adhs->phyparamset.dsparamset.currentchan = assoc_req->channel;
+
+	/* set IBSS param set */
+#define IBSS_PARA_IE_ID   6
+#define IBSS_PARA_IE_LEN  2
+
+	adhs->ssparamset.ibssparamset.elementid = IBSS_PARA_IE_ID;
+	adhs->ssparamset.ibssparamset.len = IBSS_PARA_IE_LEN;
+	adhs->ssparamset.ibssparamset.atimwindow = 0;
+
+	/* set capability info */
+	tmpcap = WLAN_CAPABILITY_IBSS;
+	if (assoc_req->secinfo.wep_enabled) {
+		lbs_deb_join("ADHOC_S_CMD: WEP enabled, setting privacy on\n");
+		tmpcap |= WLAN_CAPABILITY_PRIVACY;
+	} else {
+		lbs_deb_join("ADHOC_S_CMD: WEP disabled, setting privacy off\n");
+	}
+	adhs->capability = cpu_to_le16(tmpcap);
+
+	/* probedelay */
+	adhs->probedelay = cpu_to_le16(CMD_SCAN_PROBE_DELAY_TIME);
+
+	memset(adhs->rates, 0, sizeof(adhs->rates));
+	ratesize = min(sizeof(adhs->rates), sizeof(lbs_bg_rates));
+	memcpy(adhs->rates, lbs_bg_rates, ratesize);
+
+	/* Copy the ad-hoc creating rates into Current BSS state structure */
+	memset(&priv->curbssparams.rates, 0, sizeof(priv->curbssparams.rates));
+	memcpy(&priv->curbssparams.rates, &adhs->rates, ratesize);
+
+	/* Set MSB on basic rates as the firmware requires, but _after_
+	 * copying to current bss rates.
+	 */
+	lbs_set_basic_rate_flags(adhs->rates, ratesize);
+
+	lbs_deb_join("ADHOC_S_CMD: rates=%02x %02x %02x %02x \n",
+	       adhs->rates[0], adhs->rates[1], adhs->rates[2], adhs->rates[3]);
+
+	lbs_deb_join("ADHOC_S_CMD: AD HOC Start command is ready\n");
+
+	if (lbs_create_dnld_countryinfo_11d(priv)) {
+		lbs_deb_join("ADHOC_S_CMD: dnld_countryinfo_11d failed\n");
+		ret = -1;
+		goto done;
+	}
+
+	cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_ad_hoc_start) +
+				S_DS_GEN + cmdappendsize);
+
+	ret = 0;
+done:
+	lbs_deb_leave_args(LBS_DEB_JOIN, "ret %d", ret);
+	return ret;
+}
+
+int lbs_cmd_80211_ad_hoc_stop(struct lbs_private *priv,
+				struct cmd_ds_command *cmd)
+{
+	cmd->command = cpu_to_le16(CMD_802_11_AD_HOC_STOP);
+	cmd->size = cpu_to_le16(S_DS_GEN);
+
+	return 0;
+}
+
+int lbs_cmd_80211_ad_hoc_join(struct lbs_private *priv,
+				struct cmd_ds_command *cmd, void *pdata_buf)
+{
+	struct cmd_ds_802_11_ad_hoc_join *join_cmd = &cmd->params.adj;
+	struct assoc_request * assoc_req = pdata_buf;
+	struct bss_descriptor *bss = &assoc_req->bss;
+	int cmdappendsize = 0;
+	int ret = 0;
+	u16 ratesize = 0;
+	DECLARE_MAC_BUF(mac);
+
+	lbs_deb_enter(LBS_DEB_JOIN);
+
+	cmd->command = cpu_to_le16(CMD_802_11_AD_HOC_JOIN);
+
+	join_cmd->bss.type = CMD_BSS_TYPE_IBSS;
+	join_cmd->bss.beaconperiod = cpu_to_le16(bss->beaconperiod);
+
+	memcpy(&join_cmd->bss.bssid, &bss->bssid, ETH_ALEN);
+	memcpy(&join_cmd->bss.ssid, &bss->ssid, bss->ssid_len);
+
+	memcpy(&join_cmd->bss.phyparamset, &bss->phyparamset,
+	       sizeof(union ieeetypes_phyparamset));
+
+	memcpy(&join_cmd->bss.ssparamset, &bss->ssparamset,
+	       sizeof(union IEEEtypes_ssparamset));
+
+	join_cmd->bss.capability = cpu_to_le16(bss->capability & CAPINFO_MASK);
+	lbs_deb_join("ADHOC_J_CMD: tmpcap=%4X CAPINFO_MASK=%4X\n",
+	       bss->capability, CAPINFO_MASK);
+
+	/* information on BSSID descriptor passed to FW */
+	lbs_deb_join(
+	       "ADHOC_J_CMD: BSSID = %s, SSID = '%s'\n",
+	       print_mac(mac, join_cmd->bss.bssid),
+	       join_cmd->bss.ssid);
+
+	/* failtimeout */
+	join_cmd->failtimeout = cpu_to_le16(MRVDRV_ASSOCIATION_TIME_OUT);
+
+	/* probedelay */
+	join_cmd->probedelay = cpu_to_le16(CMD_SCAN_PROBE_DELAY_TIME);
+
+	priv->curbssparams.channel = bss->channel;
+
+	/* Copy Data rates from the rates recorded in scan response */
+	memset(join_cmd->bss.rates, 0, sizeof(join_cmd->bss.rates));
+	ratesize = min_t(u16, sizeof(join_cmd->bss.rates), MAX_RATES);
+	memcpy(join_cmd->bss.rates, bss->rates, ratesize);
+	if (get_common_rates(priv, join_cmd->bss.rates, &ratesize)) {
+		lbs_deb_join("ADHOC_J_CMD: get_common_rates returns error.\n");
+		ret = -1;
+		goto done;
+	}
+
+	/* Copy the ad-hoc creating rates into Current BSS state structure */
+	memset(&priv->curbssparams.rates, 0, sizeof(priv->curbssparams.rates));
+	memcpy(&priv->curbssparams.rates, join_cmd->bss.rates, ratesize);
+
+	/* Set MSB on basic rates as the firmware requires, but _after_
+	 * copying to current bss rates.
+	 */
+	lbs_set_basic_rate_flags(join_cmd->bss.rates, ratesize);
+
+	join_cmd->bss.ssparamset.ibssparamset.atimwindow =
+	    cpu_to_le16(bss->atimwindow);
+
+	if (assoc_req->secinfo.wep_enabled) {
+		u16 tmp = le16_to_cpu(join_cmd->bss.capability);
+		tmp |= WLAN_CAPABILITY_PRIVACY;
+		join_cmd->bss.capability = cpu_to_le16(tmp);
+	}
+
+	if (priv->psmode == LBS802_11POWERMODEMAX_PSP) {
+		/* wake up first */
+		__le32 Localpsmode;
+
+		Localpsmode = cpu_to_le32(LBS802_11POWERMODECAM);
+		ret = lbs_prepare_and_send_command(priv,
+					    CMD_802_11_PS_MODE,
+					    CMD_ACT_SET,
+					    0, 0, &Localpsmode);
+
+		if (ret) {
+			ret = -1;
+			goto done;
+		}
+	}
+
+	if (lbs_parse_dnld_countryinfo_11d(priv, bss)) {
+		ret = -1;
+		goto done;
+	}
+
+	cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_ad_hoc_join) +
+				S_DS_GEN + cmdappendsize);
+
+done:
+	lbs_deb_leave_args(LBS_DEB_JOIN, "ret %d", ret);
+	return ret;
+}
+
+int lbs_ret_80211_associate(struct lbs_private *priv,
+			      struct cmd_ds_command *resp)
+{
+	int ret = 0;
+	union iwreq_data wrqu;
+	struct ieeetypes_assocrsp *passocrsp;
+	struct bss_descriptor * bss;
+	u16 status_code;
+
+	lbs_deb_enter(LBS_DEB_ASSOC);
+
+	if (!priv->in_progress_assoc_req) {
+		lbs_deb_assoc("ASSOC_RESP: no in-progress assoc request\n");
+		ret = -1;
+		goto done;
+	}
+	bss = &priv->in_progress_assoc_req->bss;
+
+	passocrsp = (struct ieeetypes_assocrsp *) & resp->params;
+
+	/*
+	 * Older FW versions map the IEEE 802.11 Status Code in the association
+	 * response to the following values returned in passocrsp->statuscode:
+	 *
+	 *    IEEE Status Code                Marvell Status Code
+	 *    0                       ->      0x0000 ASSOC_RESULT_SUCCESS
+	 *    13                      ->      0x0004 ASSOC_RESULT_AUTH_REFUSED
+	 *    14                      ->      0x0004 ASSOC_RESULT_AUTH_REFUSED
+	 *    15                      ->      0x0004 ASSOC_RESULT_AUTH_REFUSED
+	 *    16                      ->      0x0004 ASSOC_RESULT_AUTH_REFUSED
+	 *    others                  ->      0x0003 ASSOC_RESULT_REFUSED
+	 *
+	 * Other response codes:
+	 *    0x0001 -> ASSOC_RESULT_INVALID_PARAMETERS (unused)
+	 *    0x0002 -> ASSOC_RESULT_TIMEOUT (internal timer expired waiting for
+	 *                                    association response from the AP)
+	 */
+
+	status_code = le16_to_cpu(passocrsp->statuscode);
+	switch (status_code) {
+	case 0x00:
+		break;
+	case 0x01:
+		lbs_deb_assoc("ASSOC_RESP: invalid parameters\n");
+		break;
+	case 0x02:
+		lbs_deb_assoc("ASSOC_RESP: internal timer "
+			"expired while waiting for the AP\n");
+		break;
+	case 0x03:
+		lbs_deb_assoc("ASSOC_RESP: association "
+			"refused by AP\n");
+		break;
+	case 0x04:
+		lbs_deb_assoc("ASSOC_RESP: authentication "
+			"refused by AP\n");
+		break;
+	default:
+		lbs_deb_assoc("ASSOC_RESP: failure reason 0x%02x "
+			" unknown\n", status_code);
+		break;
+	}
+
+	if (status_code) {
+		lbs_mac_event_disconnected(priv);
+		ret = -1;
+		goto done;
+	}
+
+	lbs_deb_hex(LBS_DEB_ASSOC, "ASSOC_RESP", (void *)&resp->params,
+		le16_to_cpu(resp->size) - S_DS_GEN);
+
+	/* Send a Media Connected event, according to the Spec */
+	priv->connect_status = LBS_CONNECTED;
+
+	/* Update current SSID and BSSID */
+	memcpy(&priv->curbssparams.ssid, &bss->ssid, IW_ESSID_MAX_SIZE);
+	priv->curbssparams.ssid_len = bss->ssid_len;
+	memcpy(priv->curbssparams.bssid, bss->bssid, ETH_ALEN);
+
+	lbs_deb_assoc("ASSOC_RESP: currentpacketfilter is 0x%x\n",
+		priv->currentpacketfilter);
+
+	priv->SNR[TYPE_RXPD][TYPE_AVG] = 0;
+	priv->NF[TYPE_RXPD][TYPE_AVG] = 0;
+
+	memset(priv->rawSNR, 0x00, sizeof(priv->rawSNR));
+	memset(priv->rawNF, 0x00, sizeof(priv->rawNF));
+	priv->nextSNRNF = 0;
+	priv->numSNRNF = 0;
+
+	netif_carrier_on(priv->dev);
+	netif_wake_queue(priv->dev);
+
+
+	memcpy(wrqu.ap_addr.sa_data, priv->curbssparams.bssid, ETH_ALEN);
+	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+	wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
+
+done:
+	lbs_deb_leave_args(LBS_DEB_ASSOC, "ret %d", ret);
+	return ret;
+}
+
+int lbs_ret_80211_disassociate(struct lbs_private *priv,
+				 struct cmd_ds_command *resp)
+{
+	lbs_deb_enter(LBS_DEB_JOIN);
+
+	lbs_mac_event_disconnected(priv);
+
+	lbs_deb_leave(LBS_DEB_JOIN);
+	return 0;
+}
+
+int lbs_ret_80211_ad_hoc_start(struct lbs_private *priv,
+				 struct cmd_ds_command *resp)
+{
+	int ret = 0;
+	u16 command = le16_to_cpu(resp->command);
+	u16 result = le16_to_cpu(resp->result);
+	struct cmd_ds_802_11_ad_hoc_result *padhocresult;
+	union iwreq_data wrqu;
+	struct bss_descriptor *bss;
+	DECLARE_MAC_BUF(mac);
+
+	lbs_deb_enter(LBS_DEB_JOIN);
+
+	padhocresult = &resp->params.result;
+
+	lbs_deb_join("ADHOC_RESP: size = %d\n", le16_to_cpu(resp->size));
+	lbs_deb_join("ADHOC_RESP: command = %x\n", command);
+	lbs_deb_join("ADHOC_RESP: result = %x\n", result);
+
+	if (!priv->in_progress_assoc_req) {
+		lbs_deb_join("ADHOC_RESP: no in-progress association request\n");
+		ret = -1;
+		goto done;
+	}
+	bss = &priv->in_progress_assoc_req->bss;
+
+	/*
+	 * Join result code 0 --> SUCCESS
+	 */
+	if (result) {
+		lbs_deb_join("ADHOC_RESP: failed\n");
+		if (priv->connect_status == LBS_CONNECTED) {
+			lbs_mac_event_disconnected(priv);
+		}
+		ret = -1;
+		goto done;
+	}
+
+	/*
+	 * Now the join cmd should be successful
+	 * If BSSID has changed use SSID to compare instead of BSSID
+	 */
+	lbs_deb_join("ADHOC_RESP: associated to '%s'\n",
+	             escape_essid(bss->ssid, bss->ssid_len));
+
+	/* Send a Media Connected event, according to the Spec */
+	priv->connect_status = LBS_CONNECTED;
+
+	if (command == CMD_RET(CMD_802_11_AD_HOC_START)) {
+		/* Update the created network descriptor with the new BSSID */
+		memcpy(bss->bssid, padhocresult->bssid, ETH_ALEN);
+	}
+
+	/* Set the BSSID from the joined/started descriptor */
+	memcpy(&priv->curbssparams.bssid, bss->bssid, ETH_ALEN);
+
+	/* Set the new SSID to current SSID */
+	memcpy(&priv->curbssparams.ssid, &bss->ssid, IW_ESSID_MAX_SIZE);
+	priv->curbssparams.ssid_len = bss->ssid_len;
+
+	netif_carrier_on(priv->dev);
+	netif_wake_queue(priv->dev);
+
+	memset(&wrqu, 0, sizeof(wrqu));
+	memcpy(wrqu.ap_addr.sa_data, priv->curbssparams.bssid, ETH_ALEN);
+	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+	wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
+
+	lbs_deb_join("ADHOC_RESP: - Joined/Started Ad Hoc\n");
+	lbs_deb_join("ADHOC_RESP: channel = %d\n", priv->curbssparams.channel);
+	lbs_deb_join("ADHOC_RESP: BSSID = %s\n",
+		     print_mac(mac, padhocresult->bssid));
+
+done:
+	lbs_deb_leave_args(LBS_DEB_JOIN, "ret %d", ret);
+	return ret;
+}
+
+int lbs_ret_80211_ad_hoc_stop(struct lbs_private *priv,
+				struct cmd_ds_command *resp)
+{
+	lbs_deb_enter(LBS_DEB_JOIN);
+
+	lbs_mac_event_disconnected(priv);
+
+	lbs_deb_leave(LBS_DEB_JOIN);
+	return 0;
+}

+ 53 - 0
package/libertas/src/join.h

@@ -0,0 +1,53 @@
+/**
+  * Interface for the wlan infrastructure and adhoc join routines
+  *
+  * Driver interface functions and type declarations for the join module
+  *   implemented in join.c.  Process all start/join requests for
+  *   both adhoc and infrastructure networks
+  */
+#ifndef _LBS_JOIN_H
+#define _LBS_JOIN_H
+
+#include "defs.h"
+#include "dev.h"
+
+struct cmd_ds_command;
+int lbs_cmd_80211_authenticate(struct lbs_private *priv,
+					struct cmd_ds_command *cmd,
+					void *pdata_buf);
+int lbs_cmd_80211_ad_hoc_join(struct lbs_private *priv,
+				       struct cmd_ds_command *cmd,
+				       void *pdata_buf);
+int lbs_cmd_80211_ad_hoc_stop(struct lbs_private *priv,
+				       struct cmd_ds_command *cmd);
+int lbs_cmd_80211_ad_hoc_start(struct lbs_private *priv,
+					struct cmd_ds_command *cmd,
+					void *pdata_buf);
+int lbs_cmd_80211_deauthenticate(struct lbs_private *priv,
+					  struct cmd_ds_command *cmd);
+int lbs_cmd_80211_associate(struct lbs_private *priv,
+				     struct cmd_ds_command *cmd,
+				     void *pdata_buf);
+
+int lbs_ret_80211_ad_hoc_start(struct lbs_private *priv,
+					struct cmd_ds_command *resp);
+int lbs_ret_80211_ad_hoc_stop(struct lbs_private *priv,
+				       struct cmd_ds_command *resp);
+int lbs_ret_80211_disassociate(struct lbs_private *priv,
+					struct cmd_ds_command *resp);
+int lbs_ret_80211_associate(struct lbs_private *priv,
+				     struct cmd_ds_command *resp);
+
+int lbs_start_adhoc_network(struct lbs_private *priv,
+			     struct assoc_request * assoc_req);
+int lbs_join_adhoc_network(struct lbs_private *priv,
+				struct assoc_request * assoc_req);
+int lbs_stop_adhoc_network(struct lbs_private *priv);
+
+int lbs_send_deauthentication(struct lbs_private *priv);
+
+int lbs_associate(struct lbs_private *priv, struct assoc_request *assoc_req);
+
+void lbs_unset_basic_rate_flags(u8 *rates, size_t len);
+
+#endif

+ 1474 - 0
package/libertas/src/main.c

@@ -0,0 +1,1474 @@
+/**
+  * This file contains the major functions in WLAN
+  * driver. It includes init, exit, open, close and main
+  * thread etc..
+  */
+
+#include <linux/moduleparam.h>
+#include <linux/delay.h>
+#include <linux/freezer.h>
+#include <linux/etherdevice.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/kthread.h>
+
+#include <net/iw_handler.h>
+#include <net/ieee80211.h>
+
+#include "host.h"
+#include "decl.h"
+#include "dev.h"
+#include "wext.h"
+#include "debugfs.h"
+#include "assoc.h"
+#include "join.h"
+#include "cmd.h"
+
+#define DRIVER_RELEASE_VERSION "323.p0"
+const char lbs_driver_version[] = "COMM-USB8388-" DRIVER_RELEASE_VERSION
+#ifdef  DEBUG
+    "-dbg"
+#endif
+    "";
+
+
+/* Module parameters */
+unsigned int lbs_debug;
+EXPORT_SYMBOL_GPL(lbs_debug);
+module_param_named(libertas_debug, lbs_debug, int, 0644);
+
+
+#define LBS_TX_PWR_DEFAULT		20	/*100mW */
+#define LBS_TX_PWR_US_DEFAULT		20	/*100mW */
+#define LBS_TX_PWR_JP_DEFAULT		16	/*50mW */
+#define LBS_TX_PWR_FR_DEFAULT		20	/*100mW */
+#define LBS_TX_PWR_EMEA_DEFAULT	20	/*100mW */
+
+/* Format { channel, frequency (MHz), maxtxpower } */
+/* band: 'B/G', region: USA FCC/Canada IC */
+static struct chan_freq_power channel_freq_power_US_BG[] = {
+	{1, 2412, LBS_TX_PWR_US_DEFAULT},
+	{2, 2417, LBS_TX_PWR_US_DEFAULT},
+	{3, 2422, LBS_TX_PWR_US_DEFAULT},
+	{4, 2427, LBS_TX_PWR_US_DEFAULT},
+	{5, 2432, LBS_TX_PWR_US_DEFAULT},
+	{6, 2437, LBS_TX_PWR_US_DEFAULT},
+	{7, 2442, LBS_TX_PWR_US_DEFAULT},
+	{8, 2447, LBS_TX_PWR_US_DEFAULT},
+	{9, 2452, LBS_TX_PWR_US_DEFAULT},
+	{10, 2457, LBS_TX_PWR_US_DEFAULT},
+	{11, 2462, LBS_TX_PWR_US_DEFAULT}
+};
+
+/* band: 'B/G', region: Europe ETSI */
+static struct chan_freq_power channel_freq_power_EU_BG[] = {
+	{1, 2412, LBS_TX_PWR_EMEA_DEFAULT},
+	{2, 2417, LBS_TX_PWR_EMEA_DEFAULT},
+	{3, 2422, LBS_TX_PWR_EMEA_DEFAULT},
+	{4, 2427, LBS_TX_PWR_EMEA_DEFAULT},
+	{5, 2432, LBS_TX_PWR_EMEA_DEFAULT},
+	{6, 2437, LBS_TX_PWR_EMEA_DEFAULT},
+	{7, 2442, LBS_TX_PWR_EMEA_DEFAULT},
+	{8, 2447, LBS_TX_PWR_EMEA_DEFAULT},
+	{9, 2452, LBS_TX_PWR_EMEA_DEFAULT},
+	{10, 2457, LBS_TX_PWR_EMEA_DEFAULT},
+	{11, 2462, LBS_TX_PWR_EMEA_DEFAULT},
+	{12, 2467, LBS_TX_PWR_EMEA_DEFAULT},
+	{13, 2472, LBS_TX_PWR_EMEA_DEFAULT}
+};
+
+/* band: 'B/G', region: Spain */
+static struct chan_freq_power channel_freq_power_SPN_BG[] = {
+	{10, 2457, LBS_TX_PWR_DEFAULT},
+	{11, 2462, LBS_TX_PWR_DEFAULT}
+};
+
+/* band: 'B/G', region: France */
+static struct chan_freq_power channel_freq_power_FR_BG[] = {
+	{10, 2457, LBS_TX_PWR_FR_DEFAULT},
+	{11, 2462, LBS_TX_PWR_FR_DEFAULT},
+	{12, 2467, LBS_TX_PWR_FR_DEFAULT},
+	{13, 2472, LBS_TX_PWR_FR_DEFAULT}
+};
+
+/* band: 'B/G', region: Japan */
+static struct chan_freq_power channel_freq_power_JPN_BG[] = {
+	{1, 2412, LBS_TX_PWR_JP_DEFAULT},
+	{2, 2417, LBS_TX_PWR_JP_DEFAULT},
+	{3, 2422, LBS_TX_PWR_JP_DEFAULT},
+	{4, 2427, LBS_TX_PWR_JP_DEFAULT},
+	{5, 2432, LBS_TX_PWR_JP_DEFAULT},
+	{6, 2437, LBS_TX_PWR_JP_DEFAULT},
+	{7, 2442, LBS_TX_PWR_JP_DEFAULT},
+	{8, 2447, LBS_TX_PWR_JP_DEFAULT},
+	{9, 2452, LBS_TX_PWR_JP_DEFAULT},
+	{10, 2457, LBS_TX_PWR_JP_DEFAULT},
+	{11, 2462, LBS_TX_PWR_JP_DEFAULT},
+	{12, 2467, LBS_TX_PWR_JP_DEFAULT},
+	{13, 2472, LBS_TX_PWR_JP_DEFAULT},
+	{14, 2484, LBS_TX_PWR_JP_DEFAULT}
+};
+
+/**
+ * the structure for channel, frequency and power
+ */
+struct region_cfp_table {
+	u8 region;
+	struct chan_freq_power *cfp_BG;
+	int cfp_no_BG;
+};
+
+/**
+ * the structure for the mapping between region and CFP
+ */
+static struct region_cfp_table region_cfp_table[] = {
+	{0x10,			/*US FCC */
+	 channel_freq_power_US_BG,
+	 ARRAY_SIZE(channel_freq_power_US_BG),
+	 }
+	,
+	{0x20,			/*CANADA IC */
+	 channel_freq_power_US_BG,
+	 ARRAY_SIZE(channel_freq_power_US_BG),
+	 }
+	,
+	{0x30, /*EU*/ channel_freq_power_EU_BG,
+	 ARRAY_SIZE(channel_freq_power_EU_BG),
+	 }
+	,
+	{0x31, /*SPAIN*/ channel_freq_power_SPN_BG,
+	 ARRAY_SIZE(channel_freq_power_SPN_BG),
+	 }
+	,
+	{0x32, /*FRANCE*/ channel_freq_power_FR_BG,
+	 ARRAY_SIZE(channel_freq_power_FR_BG),
+	 }
+	,
+	{0x40, /*JAPAN*/ channel_freq_power_JPN_BG,
+	 ARRAY_SIZE(channel_freq_power_JPN_BG),
+	 }
+	,
+/*Add new region here */
+};
+
+/**
+ * the table to keep region code
+ */
+u16 lbs_region_code_to_index[MRVDRV_MAX_REGION_CODE] =
+    { 0x10, 0x20, 0x30, 0x31, 0x32, 0x40 };
+
+/**
+ * 802.11b/g supported bitrates (in 500Kb/s units)
+ */
+u8 lbs_bg_rates[MAX_RATES] =
+    { 0x02, 0x04, 0x0b, 0x16, 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c,
+0x00, 0x00 };
+
+/**
+ * FW rate table.  FW refers to rates by their index in this table, not by the
+ * rate value itself.  Values of 0x00 are
+ * reserved positions.
+ */
+static u8 fw_data_rates[MAX_RATES] =
+    { 0x02, 0x04, 0x0B, 0x16, 0x00, 0x0C, 0x12,
+      0x18, 0x24, 0x30, 0x48, 0x60, 0x6C, 0x00
+};
+
+/**
+ *  @brief use index to get the data rate
+ *
+ *  @param idx                The index of data rate
+ *  @return 	   		data rate or 0
+ */
+u32 lbs_fw_index_to_data_rate(u8 idx)
+{
+	if (idx >= sizeof(fw_data_rates))
+		idx = 0;
+	return fw_data_rates[idx];
+}
+
+/**
+ *  @brief use rate to get the index
+ *
+ *  @param rate                 data rate
+ *  @return 	   		index or 0
+ */
+u8 lbs_data_rate_to_fw_index(u32 rate)
+{
+	u8 i;
+
+	if (!rate)
+		return 0;
+
+	for (i = 0; i < sizeof(fw_data_rates); i++) {
+		if (rate == fw_data_rates[i])
+			return i;
+	}
+	return 0;
+}
+
+/**
+ * Attributes exported through sysfs
+ */
+
+/**
+ * @brief Get function for sysfs attribute anycast_mask
+ */
+static ssize_t lbs_anycast_get(struct device *dev,
+		struct device_attribute *attr, char * buf)
+{
+	struct lbs_private *priv = to_net_dev(dev)->priv;
+	struct cmd_ds_mesh_access mesh_access;
+	int ret;
+
+	memset(&mesh_access, 0, sizeof(mesh_access));
+
+	ret = lbs_mesh_access(priv, CMD_ACT_MESH_GET_ANYCAST, &mesh_access);
+	if (ret)
+		return ret;
+
+	return snprintf(buf, 12, "0x%X\n", le32_to_cpu(mesh_access.data[0]));
+}
+
+/**
+ * @brief Set function for sysfs attribute anycast_mask
+ */
+static ssize_t lbs_anycast_set(struct device *dev,
+		struct device_attribute *attr, const char * buf, size_t count)
+{
+	struct lbs_private *priv = to_net_dev(dev)->priv;
+	struct cmd_ds_mesh_access mesh_access;
+	uint32_t datum;
+	int ret;
+
+	memset(&mesh_access, 0, sizeof(mesh_access));
+	sscanf(buf, "%x", &datum);
+	mesh_access.data[0] = cpu_to_le32(datum);
+
+	ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_ANYCAST, &mesh_access);
+	if (ret)
+		return ret;
+
+	return strlen(buf);
+}
+
+static int lbs_add_rtap(struct lbs_private *priv);
+static void lbs_remove_rtap(struct lbs_private *priv);
+static int lbs_add_mesh(struct lbs_private *priv);
+static void lbs_remove_mesh(struct lbs_private *priv);
+  
+
+/**
+ * Get function for sysfs attribute rtap
+ */
+static ssize_t lbs_rtap_get(struct device *dev,
+		struct device_attribute *attr, char * buf)
+{
+	struct lbs_private *priv = to_net_dev(dev)->priv;
+	return snprintf(buf, 5, "0x%X\n", priv->monitormode);
+}
+
+/**
+ *  Set function for sysfs attribute rtap
+ */
+static ssize_t lbs_rtap_set(struct device *dev,
+		struct device_attribute *attr, const char * buf, size_t count)
+{
+	int monitor_mode;
+	struct lbs_private *priv = to_net_dev(dev)->priv;
+
+	sscanf(buf, "%x", &monitor_mode);
+	if (monitor_mode != LBS_MONITOR_OFF) {
+		if(priv->monitormode == monitor_mode)
+			return strlen(buf);
+		if (priv->monitormode == LBS_MONITOR_OFF) {
+			if (priv->infra_open || priv->mesh_open)
+				return -EBUSY;
+			if (priv->mode == IW_MODE_INFRA)
+				lbs_send_deauthentication(priv);
+			else if (priv->mode == IW_MODE_ADHOC)
+				lbs_stop_adhoc_network(priv);
+			lbs_add_rtap(priv);
+		}
+		priv->monitormode = monitor_mode;
+	}
+
+	else {
+		if (priv->monitormode == LBS_MONITOR_OFF)
+			return strlen(buf);
+		priv->monitormode = LBS_MONITOR_OFF;
+		lbs_remove_rtap(priv);
+
+		if (priv->currenttxskb) {
+			dev_kfree_skb_any(priv->currenttxskb);
+			priv->currenttxskb = NULL;
+		}
+
+		/* Wake queues, command thread, etc. */
+		lbs_host_to_card_done(priv);
+	}
+
+	lbs_prepare_and_send_command(priv,
+			CMD_802_11_MONITOR_MODE, CMD_ACT_SET,
+			CMD_OPTION_WAITFORRSP, 0, &priv->monitormode);
+	return strlen(buf);
+}
+
+/**
+ * lbs_rtap attribute to be exported per ethX interface
+ * through sysfs (/sys/class/net/ethX/lbs_rtap)
+ */
+static DEVICE_ATTR(lbs_rtap, 0644, lbs_rtap_get, lbs_rtap_set );
+
+/**
+ * Get function for sysfs attribute mesh
+ */
+static ssize_t lbs_mesh_get(struct device *dev,
+		struct device_attribute *attr, char * buf)
+{
+	struct lbs_private *priv = to_net_dev(dev)->priv;
+	return snprintf(buf, 5, "0x%X\n", !!priv->mesh_dev);
+}
+
+/**
+ *  Set function for sysfs attribute mesh
+ */
+static ssize_t lbs_mesh_set(struct device *dev,
+		struct device_attribute *attr, const char * buf, size_t count)
+{
+	struct lbs_private *priv = to_net_dev(dev)->priv;
+	int enable;
+	int ret;
+
+	sscanf(buf, "%x", &enable);
+	enable = !!enable;
+	if (enable == !!priv->mesh_dev)
+		return count;
+
+	ret = lbs_mesh_config(priv, enable);
+	if (ret)
+		return ret;
+		
+	if (enable)
+		lbs_add_mesh(priv);
+	else
+		lbs_remove_mesh(priv);
+
+	return count;
+}
+
+/**
+ * lbs_mesh attribute to be exported per ethX interface
+ * through sysfs (/sys/class/net/ethX/lbs_mesh)
+ */
+static DEVICE_ATTR(lbs_mesh, 0644, lbs_mesh_get, lbs_mesh_set);
+
+/**
+ * anycast_mask attribute to be exported per mshX interface
+ * through sysfs (/sys/class/net/mshX/anycast_mask)
+ */
+static DEVICE_ATTR(anycast_mask, 0644, lbs_anycast_get, lbs_anycast_set);
+
+static struct attribute *lbs_mesh_sysfs_entries[] = {
+	&dev_attr_anycast_mask.attr,
+	NULL,
+};
+
+static struct attribute_group lbs_mesh_attr_group = {
+	.attrs = lbs_mesh_sysfs_entries,
+};
+
+/**
+ *  @brief This function opens the ethX or mshX interface
+ *
+ *  @param dev     A pointer to net_device structure
+ *  @return 	   0 or -EBUSY if monitor mode active
+ */
+static int lbs_dev_open(struct net_device *dev)
+{
+	struct lbs_private *priv = (struct lbs_private *) dev->priv ;
+	int ret = 0;
+
+	spin_lock_irq(&priv->driver_lock);
+
+	if (priv->monitormode != LBS_MONITOR_OFF) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	if (dev == priv->mesh_dev) {
+		priv->mesh_open = 1;
+		priv->mesh_connect_status = LBS_CONNECTED;
+		netif_carrier_on(dev);
+	} else {
+		priv->infra_open = 1;
+		
+		if (priv->connect_status == LBS_CONNECTED)
+			netif_carrier_on(dev);
+		else
+			netif_carrier_off(dev);
+	}
+
+	if (!priv->tx_pending_len)
+		netif_wake_queue(dev);
+ out:
+
+	spin_unlock_irq(&priv->driver_lock);
+	return ret;
+}
+
+/**
+ *  @brief This function closes the mshX interface
+ *
+ *  @param dev     A pointer to net_device structure
+ *  @return 	   0
+ */
+static int lbs_mesh_stop(struct net_device *dev)
+{
+	struct lbs_private *priv = (struct lbs_private *) (dev->priv);
+
+	spin_lock_irq(&priv->driver_lock);
+
+	priv->mesh_open = 0;
+	priv->mesh_connect_status = LBS_DISCONNECTED;
+
+	netif_stop_queue(dev);
+	netif_carrier_off(dev);
+	
+	spin_unlock_irq(&priv->driver_lock);
+	return 0;
+}
+
+/**
+ *  @brief This function closes the ethX interface
+ *
+ *  @param dev     A pointer to net_device structure
+ *  @return 	   0
+ */
+static int lbs_eth_stop(struct net_device *dev)
+{
+	struct lbs_private *priv = (struct lbs_private *) dev->priv;
+
+	spin_lock_irq(&priv->driver_lock);
+
+	priv->infra_open = 0;
+
+	netif_stop_queue(dev);
+	
+	spin_unlock_irq(&priv->driver_lock);
+	return 0;
+}
+
+static void lbs_tx_timeout(struct net_device *dev)
+{
+	struct lbs_private *priv = (struct lbs_private *) dev->priv;
+
+	lbs_deb_enter(LBS_DEB_TX);
+
+	lbs_pr_err("tx watch dog timeout\n");
+
+	dev->trans_start = jiffies;
+
+	if (priv->currenttxskb) {
+		priv->eventcause = 0x01000000;
+		lbs_send_tx_feedback(priv);
+	}
+	/* XX: Shouldn't we also call into the hw-specific driver
+	   to kick it somehow? */
+	lbs_host_to_card_done(priv);
+
+	lbs_deb_leave(LBS_DEB_TX);
+}
+
+void lbs_host_to_card_done(struct lbs_private *priv)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv->driver_lock, flags);
+
+	priv->dnld_sent = DNLD_RES_RECEIVED;
+
+	/* Wake main thread if commands are pending */
+	if (!priv->cur_cmd)
+		wake_up_interruptible(&priv->waitq);
+
+	/* Don't wake netif queues if we're in monitor mode and
+	   a TX packet is already pending, or if there are commands
+	   queued to be sent. */
+	if (!priv->currenttxskb && list_empty(&priv->cmdpendingq)) {
+		if (priv->dev && priv->connect_status == LBS_CONNECTED)
+			netif_wake_queue(priv->dev);
+
+		if (priv->mesh_dev && priv->mesh_connect_status == LBS_CONNECTED)
+			netif_wake_queue(priv->mesh_dev);
+	}
+	spin_unlock_irqrestore(&priv->driver_lock, flags);
+}
+EXPORT_SYMBOL_GPL(lbs_host_to_card_done);
+
+/**
+ *  @brief This function returns the network statistics
+ *
+ *  @param dev     A pointer to struct lbs_private structure
+ *  @return 	   A pointer to net_device_stats structure
+ */
+static struct net_device_stats *lbs_get_stats(struct net_device *dev)
+{
+	struct lbs_private *priv = (struct lbs_private *) dev->priv;
+
+	return &priv->stats;
+}
+
+static int lbs_set_mac_address(struct net_device *dev, void *addr)
+{
+	int ret = 0;
+	struct lbs_private *priv = (struct lbs_private *) dev->priv;
+	struct sockaddr *phwaddr = addr;
+
+	lbs_deb_enter(LBS_DEB_NET);
+
+	/* In case it was called from the mesh device */
+	dev = priv->dev ;
+
+	memset(priv->current_addr, 0, ETH_ALEN);
+
+	/* dev->dev_addr is 8 bytes */
+	lbs_deb_hex(LBS_DEB_NET, "dev->dev_addr", dev->dev_addr, ETH_ALEN);
+
+	lbs_deb_hex(LBS_DEB_NET, "addr", phwaddr->sa_data, ETH_ALEN);
+	memcpy(priv->current_addr, phwaddr->sa_data, ETH_ALEN);
+
+	ret = lbs_prepare_and_send_command(priv, CMD_802_11_MAC_ADDRESS,
+				    CMD_ACT_SET,
+				    CMD_OPTION_WAITFORRSP, 0, NULL);
+
+	if (ret) {
+		lbs_deb_net("set MAC address failed\n");
+		ret = -1;
+		goto done;
+	}
+
+	lbs_deb_hex(LBS_DEB_NET, "priv->macaddr", priv->current_addr, ETH_ALEN);
+	memcpy(dev->dev_addr, priv->current_addr, ETH_ALEN);
+	if (priv->mesh_dev)
+		memcpy(priv->mesh_dev->dev_addr, priv->current_addr, ETH_ALEN);
+
+done:
+	lbs_deb_leave_args(LBS_DEB_NET, "ret %d", ret);
+	return ret;
+}
+
+static int lbs_copy_multicast_address(struct lbs_private *priv,
+				     struct net_device *dev)
+{
+	int i = 0;
+	struct dev_mc_list *mcptr = dev->mc_list;
+
+	for (i = 0; i < dev->mc_count; i++) {
+		memcpy(&priv->multicastlist[i], mcptr->dmi_addr, ETH_ALEN);
+		mcptr = mcptr->next;
+	}
+
+	return i;
+
+}
+
+static void lbs_set_multicast_list(struct net_device *dev)
+{
+	struct lbs_private *priv = dev->priv;
+	int oldpacketfilter;
+	DECLARE_MAC_BUF(mac);
+
+	lbs_deb_enter(LBS_DEB_NET);
+
+	oldpacketfilter = priv->currentpacketfilter;
+
+	if (dev->flags & IFF_PROMISC) {
+		lbs_deb_net("enable promiscuous mode\n");
+		priv->currentpacketfilter |=
+		    CMD_ACT_MAC_PROMISCUOUS_ENABLE;
+		priv->currentpacketfilter &=
+		    ~(CMD_ACT_MAC_ALL_MULTICAST_ENABLE |
+		      CMD_ACT_MAC_MULTICAST_ENABLE);
+	} else {
+		/* Multicast */
+		priv->currentpacketfilter &=
+		    ~CMD_ACT_MAC_PROMISCUOUS_ENABLE;
+
+		if (dev->flags & IFF_ALLMULTI || dev->mc_count >
+		    MRVDRV_MAX_MULTICAST_LIST_SIZE) {
+			lbs_deb_net( "enabling all multicast\n");
+			priv->currentpacketfilter |=
+			    CMD_ACT_MAC_ALL_MULTICAST_ENABLE;
+			priv->currentpacketfilter &=
+			    ~CMD_ACT_MAC_MULTICAST_ENABLE;
+		} else {
+			priv->currentpacketfilter &=
+			    ~CMD_ACT_MAC_ALL_MULTICAST_ENABLE;
+
+			if (!dev->mc_count) {
+				lbs_deb_net("no multicast addresses, "
+				       "disabling multicast\n");
+				priv->currentpacketfilter &=
+				    ~CMD_ACT_MAC_MULTICAST_ENABLE;
+			} else {
+				int i;
+
+				priv->currentpacketfilter |=
+				    CMD_ACT_MAC_MULTICAST_ENABLE;
+
+				priv->nr_of_multicastmacaddr =
+				    lbs_copy_multicast_address(priv, dev);
+
+				lbs_deb_net("multicast addresses: %d\n",
+				       dev->mc_count);
+
+				for (i = 0; i < dev->mc_count; i++) {
+					lbs_deb_net("Multicast address %d:%s\n",
+					       i, print_mac(mac,
+					       priv->multicastlist[i]));
+				}
+				/* send multicast addresses to firmware */
+				lbs_prepare_and_send_command(priv,
+						      CMD_MAC_MULTICAST_ADR,
+						      CMD_ACT_SET, 0, 0,
+						      NULL);
+			}
+		}
+	}
+
+	if (priv->currentpacketfilter != oldpacketfilter) {
+		lbs_set_mac_packet_filter(priv);
+	}
+
+	lbs_deb_leave(LBS_DEB_NET);
+}
+
+/**
+ *  @brief This function handles the major jobs in the LBS driver.
+ *  It handles all events generated by firmware, RX data received
+ *  from firmware and TX data sent from kernel.
+ *
+ *  @param data    A pointer to lbs_thread structure
+ *  @return 	   0
+ */
+static int lbs_thread(void *data)
+{
+	struct net_device *dev = data;
+	struct lbs_private *priv = dev->priv;
+	wait_queue_t wait;
+	u8 ireg = 0;
+
+	lbs_deb_enter(LBS_DEB_THREAD);
+
+	init_waitqueue_entry(&wait, current);
+
+	set_freezable();
+
+	for (;;) {
+		int shouldsleep;
+
+		lbs_deb_thread( "main-thread 111: intcounter=%d currenttxskb=%p dnld_sent=%d\n",
+				priv->intcounter, priv->currenttxskb, priv->dnld_sent);
+
+		add_wait_queue(&priv->waitq, &wait);
+		set_current_state(TASK_INTERRUPTIBLE);
+		spin_lock_irq(&priv->driver_lock);
+
+		if (priv->surpriseremoved)
+			shouldsleep = 0;	/* Bye */
+		else if (priv->psstate == PS_STATE_SLEEP)
+			shouldsleep = 1;	/* Sleep mode. Nothing we can do till it wakes */
+		else if (priv->intcounter)
+			shouldsleep = 0;	/* Interrupt pending. Deal with it now */
+		else if (!priv->fw_ready)
+			shouldsleep = 1;	/* Firmware not ready. We're waiting for it */
+		else if (priv->dnld_sent)
+			shouldsleep = 1;	/* Something is en route to the device already */
+		else if (priv->tx_pending_len > 0)
+			shouldsleep = 0;	/* We've a packet to send */
+		else if (priv->cur_cmd)
+			shouldsleep = 1;	/* Can't send a command; one already running */
+		else if (!list_empty(&priv->cmdpendingq))
+			shouldsleep = 0;	/* We have a command to send */
+		else
+			shouldsleep = 1;	/* No command */
+
+		if (shouldsleep) {
+			lbs_deb_thread("main-thread sleeping... Conn=%d IntC=%d PS_mode=%d PS_State=%d\n",
+				       priv->connect_status, priv->intcounter,
+				       priv->psmode, priv->psstate);
+			spin_unlock_irq(&priv->driver_lock);
+			schedule();
+		} else
+			spin_unlock_irq(&priv->driver_lock);
+
+		lbs_deb_thread("main-thread 222 (waking up): intcounter=%d currenttxskb=%p dnld_sent=%d\n",
+			       priv->intcounter, priv->currenttxskb, priv->dnld_sent);
+
+		set_current_state(TASK_RUNNING);
+		remove_wait_queue(&priv->waitq, &wait);
+		try_to_freeze();
+
+		lbs_deb_thread("main-thread 333: intcounter=%d currenttxskb=%p dnld_sent=%d\n",
+			       priv->intcounter, priv->currenttxskb, priv->dnld_sent);
+
+		if (kthread_should_stop() || priv->surpriseremoved) {
+			lbs_deb_thread("main-thread: break from main thread: surpriseremoved=0x%x\n",
+				       priv->surpriseremoved);
+			break;
+		}
+
+
+		spin_lock_irq(&priv->driver_lock);
+
+		if (priv->intcounter) {
+			u8 int_status;
+
+			priv->intcounter = 0;
+			int_status = priv->hw_get_int_status(priv, &ireg);
+
+			if (int_status) {
+				lbs_deb_thread("main-thread: reading HOST_INT_STATUS_REG failed\n");
+				spin_unlock_irq(&priv->driver_lock);
+				continue;
+			}
+			priv->hisregcpy |= ireg;
+		}
+
+		lbs_deb_thread("main-thread 444: intcounter=%d currenttxskb=%p dnld_sent=%d\n",
+			       priv->intcounter, priv->currenttxskb, priv->dnld_sent);
+
+		/* command response? */
+		if (priv->hisregcpy & MRVDRV_CMD_UPLD_RDY) {
+			lbs_deb_thread("main-thread: cmd response ready\n");
+
+			priv->hisregcpy &= ~MRVDRV_CMD_UPLD_RDY;
+			spin_unlock_irq(&priv->driver_lock);
+			lbs_process_rx_command(priv);
+			spin_lock_irq(&priv->driver_lock);
+		}
+
+		/* Any Card Event */
+		if (priv->hisregcpy & MRVDRV_CARDEVENT) {
+			lbs_deb_thread("main-thread: Card Event Activity\n");
+
+			priv->hisregcpy &= ~MRVDRV_CARDEVENT;
+
+			if (priv->hw_read_event_cause(priv)) {
+				lbs_pr_alert("main-thread: hw_read_event_cause failed\n");
+				spin_unlock_irq(&priv->driver_lock);
+				continue;
+			}
+			spin_unlock_irq(&priv->driver_lock);
+			lbs_process_event(priv);
+		} else
+			spin_unlock_irq(&priv->driver_lock);
+
+		if (!priv->fw_ready)
+			continue;
+
+		/* Check if we need to confirm Sleep Request received previously */
+		if (priv->psstate == PS_STATE_PRE_SLEEP &&
+		    !priv->dnld_sent && !priv->cur_cmd) {
+			if (priv->connect_status == LBS_CONNECTED) {
+				lbs_deb_thread("main_thread: PRE_SLEEP--intcounter=%d currenttxskb=%p dnld_sent=%d cur_cmd=%p, confirm now\n",
+					       priv->intcounter, priv->currenttxskb, priv->dnld_sent, priv->cur_cmd);
+
+				lbs_ps_confirm_sleep(priv, (u16) priv->psmode);
+			} else {
+				/* workaround for firmware sending
+				 * deauth/linkloss event immediately
+				 * after sleep request; remove this
+				 * after firmware fixes it
+				 */
+				priv->psstate = PS_STATE_AWAKE;
+				lbs_pr_alert("main-thread: ignore PS_SleepConfirm in non-connected state\n");
+			}
+		}
+
+		/* The PS state is changed during processing of Sleep Request
+		 * event above
+		 */
+		if ((priv->psstate == PS_STATE_SLEEP) ||
+		    (priv->psstate == PS_STATE_PRE_SLEEP))
+			continue;
+
+		/* Execute the next command */
+		if (!priv->dnld_sent && !priv->cur_cmd)
+			lbs_execute_next_command(priv);
+
+		/* Wake-up command waiters which can't sleep in
+		 * lbs_prepare_and_send_command
+		 */
+		if (!list_empty(&priv->cmdpendingq))
+			wake_up_all(&priv->cmd_pending);
+
+		spin_lock_irq(&priv->driver_lock);
+		if (!priv->dnld_sent && priv->tx_pending_len > 0) {
+			int ret = priv->hw_host_to_card(priv, MVMS_DAT,
+							priv->tx_pending_buf,
+							priv->tx_pending_len);
+			if (ret) {
+				lbs_deb_tx("host_to_card failed %d\n", ret);
+				priv->dnld_sent = DNLD_RES_RECEIVED;
+			}
+			priv->tx_pending_len = 0;
+			if (!priv->currenttxskb) {
+				/* We can wake the queues immediately if we aren't
+				   waiting for TX feedback */
+				if (priv->connect_status == LBS_CONNECTED)
+					netif_wake_queue(priv->dev);
+				if (priv->mesh_dev &&
+				    priv->mesh_connect_status == LBS_CONNECTED)
+					netif_wake_queue(priv->mesh_dev);
+			}
+		}
+		spin_unlock_irq(&priv->driver_lock);
+	}
+
+	del_timer(&priv->command_timer);
+	wake_up_all(&priv->cmd_pending);
+
+	lbs_deb_leave(LBS_DEB_THREAD);
+	return 0;
+}
+
+/**
+ *  @brief This function downloads firmware image, gets
+ *  HW spec from firmware and set basic parameters to
+ *  firmware.
+ *
+ *  @param priv    A pointer to struct lbs_private structure
+ *  @return 	   0 or -1
+ */
+static int lbs_setup_firmware(struct lbs_private *priv)
+{
+	int ret = -1;
+
+	lbs_deb_enter(LBS_DEB_FW);
+
+	/*
+	 * Read MAC address from HW
+	 */
+	memset(priv->current_addr, 0xff, ETH_ALEN);
+	ret = lbs_update_hw_spec(priv);
+	if (ret) {
+		ret = -1;
+		goto done;
+	}
+
+	lbs_set_mac_packet_filter(priv);
+
+	ret = lbs_get_data_rate(priv);
+	if (ret < 0) {
+		ret = -1;
+		goto done;
+	}
+
+	ret = 0;
+done:
+	lbs_deb_leave_args(LBS_DEB_FW, "ret %d", ret);
+	return ret;
+}
+
+/**
+ *  This function handles the timeout of command sending.
+ *  It will re-send the same command again.
+ */
+static void command_timer_fn(unsigned long data)
+{
+	struct lbs_private *priv = (struct lbs_private *)data;
+	struct cmd_ctrl_node *node;
+	unsigned long flags;
+
+	node = priv->cur_cmd;
+	if (node == NULL) {
+		lbs_deb_fw("ptempnode empty\n");
+		return;
+	}
+
+	if (!node->cmdbuf) {
+		lbs_deb_fw("cmd is NULL\n");
+		return;
+	}
+
+	lbs_deb_fw("command_timer_fn fired, cmd %x\n", node->cmdbuf->command);
+
+	if (!priv->fw_ready)
+		return;
+
+	spin_lock_irqsave(&priv->driver_lock, flags);
+	priv->cur_cmd = NULL;
+	spin_unlock_irqrestore(&priv->driver_lock, flags);
+
+	lbs_deb_fw("re-sending same command because of timeout\n");
+	lbs_queue_cmd(priv, node, 0);
+
+	wake_up_interruptible(&priv->waitq);
+
+	return;
+}
+
+static int lbs_init_adapter(struct lbs_private *priv)
+{
+	size_t bufsize;
+	int i, ret = 0;
+
+	/* Allocate buffer to store the BSSID list */
+	bufsize = MAX_NETWORK_COUNT * sizeof(struct bss_descriptor);
+	priv->networks = kzalloc(bufsize, GFP_KERNEL);
+	if (!priv->networks) {
+		lbs_pr_err("Out of memory allocating beacons\n");
+		ret = -1;
+		goto out;
+	}
+
+	/* Initialize scan result lists */
+	INIT_LIST_HEAD(&priv->network_free_list);
+	INIT_LIST_HEAD(&priv->network_list);
+	for (i = 0; i < MAX_NETWORK_COUNT; i++) {
+		list_add_tail(&priv->networks[i].list,
+			      &priv->network_free_list);
+	}
+
+	priv->lbs_ps_confirm_sleep.seqnum = cpu_to_le16(++priv->seqnum);
+	priv->lbs_ps_confirm_sleep.command =
+	    cpu_to_le16(CMD_802_11_PS_MODE);
+	priv->lbs_ps_confirm_sleep.size =
+	    cpu_to_le16(sizeof(struct PS_CMD_ConfirmSleep));
+	priv->lbs_ps_confirm_sleep.action =
+	    cpu_to_le16(CMD_SUBCMD_SLEEP_CONFIRMED);
+
+	memset(priv->current_addr, 0xff, ETH_ALEN);
+
+	priv->connect_status = LBS_DISCONNECTED;
+	priv->mesh_connect_status = LBS_DISCONNECTED;
+	priv->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
+	priv->mode = IW_MODE_INFRA;
+	priv->curbssparams.channel = DEFAULT_AD_HOC_CHANNEL;
+	priv->currentpacketfilter = CMD_ACT_MAC_RX_ON | CMD_ACT_MAC_TX_ON;
+	priv->radioon = RADIO_ON;
+	priv->auto_rate = 1;
+	priv->capability = WLAN_CAPABILITY_SHORT_PREAMBLE;
+	priv->psmode = LBS802_11POWERMODECAM;
+	priv->psstate = PS_STATE_FULL_POWER;
+
+	mutex_init(&priv->lock);
+
+	setup_timer(&priv->command_timer, command_timer_fn,
+	            (unsigned long)priv);
+
+	INIT_LIST_HEAD(&priv->cmdfreeq);
+	INIT_LIST_HEAD(&priv->cmdpendingq);
+
+	spin_lock_init(&priv->driver_lock);
+	init_waitqueue_head(&priv->cmd_pending);
+
+	/* Allocate the command buffers */
+	if (lbs_allocate_cmd_buffer(priv)) {
+		lbs_pr_err("Out of memory allocating command buffers\n");
+		ret = -1;
+	}
+
+out:
+	return ret;
+}
+
+static void lbs_free_adapter(struct lbs_private *priv)
+{
+	lbs_deb_fw("free command buffer\n");
+	lbs_free_cmd_buffer(priv);
+
+	lbs_deb_fw("free command_timer\n");
+	del_timer(&priv->command_timer);
+
+	lbs_deb_fw("free scan results table\n");
+	kfree(priv->networks);
+	priv->networks = NULL;
+}
+
+/**
+ * @brief This function adds the card. it will probe the
+ * card, allocate the lbs_priv and initialize the device.
+ *
+ *  @param card    A pointer to card
+ *  @return 	   A pointer to struct lbs_private structure
+ */
+struct lbs_private *lbs_add_card(void *card, struct device *dmdev)
+{
+	struct net_device *dev = NULL;
+	struct lbs_private *priv = NULL;
+
+	lbs_deb_enter(LBS_DEB_NET);
+
+	/* Allocate an Ethernet device and register it */
+	dev = alloc_etherdev(sizeof(struct lbs_private));
+	if (!dev) {
+		lbs_pr_err("init ethX device failed\n");
+		goto done;
+	}
+	priv = dev->priv;
+
+	if (lbs_init_adapter(priv)) {
+		lbs_pr_err("failed to initialize adapter structure.\n");
+		goto err_init_adapter;
+	}
+
+	priv->dev = dev;
+	priv->card = card;
+	priv->mesh_open = 0;
+	priv->infra_open = 0;
+
+	/* Setup the OS Interface to our functions */
+	dev->open = lbs_dev_open;
+	dev->hard_start_xmit = lbs_hard_start_xmit;
+	dev->stop = lbs_eth_stop;
+	dev->set_mac_address = lbs_set_mac_address;
+	dev->tx_timeout = lbs_tx_timeout;
+	dev->get_stats = lbs_get_stats;
+	dev->watchdog_timeo = 5 * HZ;
+	dev->ethtool_ops = &lbs_ethtool_ops;
+#ifdef	WIRELESS_EXT
+	dev->wireless_handlers = (struct iw_handler_def *)&lbs_handler_def;
+#endif
+	dev->flags |= IFF_BROADCAST | IFF_MULTICAST;
+	dev->set_multicast_list = lbs_set_multicast_list;
+
+	SET_NETDEV_DEV(dev, dmdev);
+
+	priv->rtap_net_dev = NULL;
+
+	lbs_deb_thread("Starting main thread...\n");
+	init_waitqueue_head(&priv->waitq);
+	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;
+	}
+
+	priv->work_thread = create_singlethread_workqueue("lbs_worker");
+	INIT_DELAYED_WORK(&priv->assoc_work, lbs_association_worker);
+	INIT_DELAYED_WORK(&priv->scan_work, lbs_scan_worker);
+	INIT_WORK(&priv->sync_channel, lbs_sync_channel);
+
+	sprintf(priv->mesh_ssid, "mesh");
+	priv->mesh_ssid_len = 4;
+
+	goto done;
+
+err_init_adapter:
+	lbs_free_adapter(priv);
+	free_netdev(dev);
+	priv = NULL;
+
+done:
+	lbs_deb_leave_args(LBS_DEB_NET, "priv %p", priv);
+	return priv;
+}
+EXPORT_SYMBOL_GPL(lbs_add_card);
+
+
+int lbs_remove_card(struct lbs_private *priv)
+{
+	struct net_device *dev = priv->dev;
+	union iwreq_data wrqu;
+
+	lbs_deb_enter(LBS_DEB_MAIN);
+
+	lbs_remove_mesh(priv);
+	lbs_remove_rtap(priv);
+
+	dev = priv->dev;
+
+	cancel_delayed_work(&priv->scan_work);
+	cancel_delayed_work(&priv->assoc_work);
+	destroy_workqueue(priv->work_thread);
+
+	if (priv->psmode == LBS802_11POWERMODEMAX_PSP) {
+		priv->psmode = LBS802_11POWERMODECAM;
+		lbs_ps_wakeup(priv, CMD_OPTION_WAITFORRSP);
+	}
+
+	memset(wrqu.ap_addr.sa_data, 0xaa, ETH_ALEN);
+	wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+	wireless_send_event(priv->dev, SIOCGIWAP, &wrqu, NULL);
+
+	/* Stop the thread servicing the interrupts */
+	priv->surpriseremoved = 1;
+	kthread_stop(priv->main_thread);
+
+	lbs_free_adapter(priv);
+
+	priv->dev = NULL;
+	free_netdev(dev);
+
+	lbs_deb_leave(LBS_DEB_MAIN);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(lbs_remove_card);
+
+
+int lbs_start_card(struct lbs_private *priv)
+{
+	struct net_device *dev = priv->dev;
+	int ret = -1;
+
+	lbs_deb_enter(LBS_DEB_MAIN);
+
+	/* poke the firmware */
+	ret = lbs_setup_firmware(priv);
+	if (ret)
+		goto done;
+
+	/* init 802.11d */
+	lbs_init_11d(priv);
+
+	if (register_netdev(dev)) {
+		lbs_pr_err("cannot register ethX device\n");
+		goto done;
+	}
+	if (device_create_file(&dev->dev, &dev_attr_lbs_rtap))
+		lbs_pr_err("cannot register lbs_rtap attribute\n");
+	if (device_create_file(&dev->dev, &dev_attr_lbs_mesh))
+		lbs_pr_err("cannot register lbs_mesh attribute\n");
+
+	lbs_debugfs_init_one(priv, dev);
+
+	lbs_pr_info("%s: Marvell WLAN 802.11 adapter\n", dev->name);
+
+	ret = 0;
+
+done:
+	lbs_deb_leave_args(LBS_DEB_MAIN, "ret %d", ret);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(lbs_start_card);
+
+
+int lbs_stop_card(struct lbs_private *priv)
+{
+	struct net_device *dev = priv->dev;
+	int ret = -1;
+	struct cmd_ctrl_node *cmdnode;
+	unsigned long flags;
+
+	lbs_deb_enter(LBS_DEB_MAIN);
+
+	netif_stop_queue(priv->dev);
+	netif_carrier_off(priv->dev);
+
+	lbs_debugfs_remove_one(priv);
+	device_remove_file(&dev->dev, &dev_attr_lbs_rtap);
+	device_remove_file(&dev->dev, &dev_attr_lbs_mesh);
+
+	/* Flush pending command nodes */
+	spin_lock_irqsave(&priv->driver_lock, flags);
+	list_for_each_entry(cmdnode, &priv->cmdpendingq, list) {
+		cmdnode->cmdwaitqwoken = 1;
+		wake_up_interruptible(&cmdnode->cmdwait_q);
+	}
+	spin_unlock_irqrestore(&priv->driver_lock, flags);
+
+	unregister_netdev(dev);
+
+	lbs_deb_leave_args(LBS_DEB_MAIN, "ret %d", ret);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(lbs_stop_card);
+
+
+/**
+ * @brief This function adds mshX interface
+ *
+ *  @param priv    A pointer to the struct lbs_private structure
+ *  @return 	   0 if successful, -X otherwise
+ */
+static int lbs_add_mesh(struct lbs_private *priv)
+{
+	struct net_device *mesh_dev = NULL;
+	int ret = 0;
+
+	lbs_deb_enter(LBS_DEB_MESH);
+
+	/* Allocate a virtual mesh device */
+	if (!(mesh_dev = alloc_netdev(0, "msh%d", ether_setup))) {
+		lbs_deb_mesh("init mshX device failed\n");
+		ret = -ENOMEM;
+		goto done;
+	}
+	mesh_dev->priv = priv;
+	priv->mesh_dev = mesh_dev;
+
+	mesh_dev->open = lbs_dev_open;
+	mesh_dev->hard_start_xmit = lbs_hard_start_xmit;
+	mesh_dev->stop = lbs_mesh_stop;
+	mesh_dev->get_stats = lbs_get_stats;
+	mesh_dev->set_mac_address = lbs_set_mac_address;
+	mesh_dev->ethtool_ops = &lbs_ethtool_ops;
+	memcpy(mesh_dev->dev_addr, priv->dev->dev_addr,
+			sizeof(priv->dev->dev_addr));
+
+	SET_NETDEV_DEV(priv->mesh_dev, priv->dev->dev.parent);
+
+#ifdef	WIRELESS_EXT
+	mesh_dev->wireless_handlers = (struct iw_handler_def *)&mesh_handler_def;
+#endif
+	/* Register virtual mesh interface */
+	ret = register_netdev(mesh_dev);
+	if (ret) {
+		lbs_pr_err("cannot register mshX virtual interface\n");
+		goto err_free;
+	}
+
+	ret = sysfs_create_group(&(mesh_dev->dev.kobj), &lbs_mesh_attr_group);
+	if (ret)
+		goto err_unregister;
+
+	/* Everything successful */
+	ret = 0;
+	goto done;
+
+err_unregister:
+	unregister_netdev(mesh_dev);
+
+err_free:
+	free_netdev(mesh_dev);
+
+done:
+	lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(lbs_add_mesh);
+
+
+static void lbs_remove_mesh(struct lbs_private *priv)
+{
+	struct net_device *mesh_dev;
+
+	lbs_deb_enter(LBS_DEB_MAIN);
+
+	if (!priv)
+		goto out;
+
+	mesh_dev = priv->mesh_dev;
+	if (!mesh_dev)
+		goto out;
+
+	netif_stop_queue(mesh_dev);
+	netif_carrier_off(priv->mesh_dev);
+
+	sysfs_remove_group(&(mesh_dev->dev.kobj), &lbs_mesh_attr_group);
+	unregister_netdev(mesh_dev);
+
+	priv->mesh_dev = NULL;
+	free_netdev(mesh_dev);
+
+out:
+	lbs_deb_leave(LBS_DEB_MAIN);
+}
+EXPORT_SYMBOL_GPL(lbs_remove_mesh);
+
+/**
+ *  @brief This function finds the CFP in
+ *  region_cfp_table based on region and band parameter.
+ *
+ *  @param region  The region code
+ *  @param band	   The band
+ *  @param cfp_no  A pointer to CFP number
+ *  @return 	   A pointer to CFP
+ */
+struct chan_freq_power *lbs_get_region_cfp_table(u8 region, u8 band, int *cfp_no)
+{
+	int i, end;
+
+	lbs_deb_enter(LBS_DEB_MAIN);
+
+	end = ARRAY_SIZE(region_cfp_table);
+
+	for (i = 0; i < end ; i++) {
+		lbs_deb_main("region_cfp_table[i].region=%d\n",
+			region_cfp_table[i].region);
+		if (region_cfp_table[i].region == region) {
+			*cfp_no = region_cfp_table[i].cfp_no_BG;
+			lbs_deb_leave(LBS_DEB_MAIN);
+			return region_cfp_table[i].cfp_BG;
+		}
+	}
+
+	lbs_deb_leave_args(LBS_DEB_MAIN, "ret NULL");
+	return NULL;
+}
+
+int lbs_set_regiontable(struct lbs_private *priv, u8 region, u8 band)
+{
+	int ret = 0;
+	int i = 0;
+
+	struct chan_freq_power *cfp;
+	int cfp_no;
+
+	lbs_deb_enter(LBS_DEB_MAIN);
+
+	memset(priv->region_channel, 0, sizeof(priv->region_channel));
+
+	{
+		cfp = lbs_get_region_cfp_table(region, band, &cfp_no);
+		if (cfp != NULL) {
+			priv->region_channel[i].nrcfp = cfp_no;
+			priv->region_channel[i].CFP = cfp;
+		} else {
+			lbs_deb_main("wrong region code %#x in band B/G\n",
+			       region);
+			ret = -1;
+			goto out;
+		}
+		priv->region_channel[i].valid = 1;
+		priv->region_channel[i].region = region;
+		priv->region_channel[i].band = band;
+		i++;
+	}
+out:
+	lbs_deb_leave_args(LBS_DEB_MAIN, "ret %d", ret);
+	return ret;
+}
+
+/**
+ *  @brief This function handles the interrupt. it will change PS
+ *  state if applicable. it will wake up main_thread to handle
+ *  the interrupt event as well.
+ *
+ *  @param dev     A pointer to net_device structure
+ *  @return 	   n/a
+ */
+void lbs_interrupt(struct lbs_private *priv)
+{
+	lbs_deb_enter(LBS_DEB_THREAD);
+
+	lbs_deb_thread("lbs_interrupt: intcounter=%d\n", priv->intcounter);
+
+	if (!spin_trylock(&priv->driver_lock)) {
+		spin_unlock(&priv->driver_lock);
+		printk(KERN_CRIT "%s called without driver_lock held\n", __func__);
+		WARN_ON(1);
+	}
+
+	priv->intcounter++;
+
+	if (priv->psstate == PS_STATE_SLEEP)
+		priv->psstate = PS_STATE_AWAKE;
+
+	wake_up_interruptible(&priv->waitq);
+
+	lbs_deb_leave(LBS_DEB_THREAD);
+}
+EXPORT_SYMBOL_GPL(lbs_interrupt);
+
+int lbs_reset_device(struct lbs_private *priv)
+{
+	int ret;
+
+	lbs_deb_enter(LBS_DEB_MAIN);
+	ret = lbs_prepare_and_send_command(priv, CMD_802_11_RESET,
+				    CMD_ACT_HALT, 0, 0, NULL);
+	msleep_interruptible(10);
+
+	lbs_deb_leave_args(LBS_DEB_MAIN, "ret %d", ret);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(lbs_reset_device);
+
+static int __init lbs_init_module(void)
+{
+	lbs_deb_enter(LBS_DEB_MAIN);
+	lbs_debugfs_init();
+	lbs_deb_leave(LBS_DEB_MAIN);
+	return 0;
+}
+
+static void __exit lbs_exit_module(void)
+{
+	lbs_deb_enter(LBS_DEB_MAIN);
+
+	lbs_debugfs_remove();
+
+	lbs_deb_leave(LBS_DEB_MAIN);
+}
+
+/*
+ * rtap interface support fuctions
+ */
+
+static int lbs_rtap_open(struct net_device *dev)
+{
+	/* Yes, _stop_ the queue. Because we don't support injection */
+        netif_carrier_off(dev);
+        netif_stop_queue(dev);
+        return 0;
+}
+
+static int lbs_rtap_stop(struct net_device *dev)
+{
+        return 0;
+}
+
+static int lbs_rtap_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+        netif_stop_queue(dev);
+        return NETDEV_TX_BUSY;
+}
+
+static struct net_device_stats *lbs_rtap_get_stats(struct net_device *dev)
+{
+	struct lbs_private *priv = dev->priv;
+	return &priv->stats;
+}
+
+
+static void lbs_remove_rtap(struct lbs_private *priv)
+{
+	if (priv->rtap_net_dev == NULL)
+		return;
+	unregister_netdev(priv->rtap_net_dev);
+	free_netdev(priv->rtap_net_dev);
+	priv->rtap_net_dev = NULL;
+}
+
+static int lbs_add_rtap(struct lbs_private *priv)
+{
+	int rc = 0;
+	struct net_device *rtap_dev;
+
+	if (priv->rtap_net_dev)
+		return -EPERM;
+
+	rtap_dev = alloc_netdev(0, "rtap%d", ether_setup);
+	if (rtap_dev == NULL)
+		return -ENOMEM;
+
+	memcpy(rtap_dev->dev_addr, priv->current_addr, ETH_ALEN);
+	rtap_dev->type = ARPHRD_IEEE80211_RADIOTAP;
+	rtap_dev->open = lbs_rtap_open;
+	rtap_dev->stop = lbs_rtap_stop;
+	rtap_dev->get_stats = lbs_rtap_get_stats;
+	rtap_dev->hard_start_xmit = lbs_rtap_hard_start_xmit;
+	rtap_dev->set_multicast_list = lbs_set_multicast_list;
+	rtap_dev->priv = priv;
+
+	rc = register_netdev(rtap_dev);
+	if (rc) {
+		free_netdev(rtap_dev);
+		return rc;
+	}
+	priv->rtap_net_dev = rtap_dev;
+
+	return 0;
+}
+
+
+module_init(lbs_init_module);
+module_exit(lbs_exit_module);
+
+MODULE_DESCRIPTION("Libertas WLAN Driver Library");
+MODULE_AUTHOR("Marvell International Ltd.");
+MODULE_LICENSE("GPL");

+ 57 - 0
package/libertas/src/radiotap.h

@@ -0,0 +1,57 @@
+#include <net/ieee80211_radiotap.h>
+
+struct tx_radiotap_hdr {
+	struct ieee80211_radiotap_header hdr;
+	u8 rate;
+	u8 txpower;
+	u8 rts_retries;
+	u8 data_retries;
+#if 0
+	u8 pad[IEEE80211_RADIOTAP_HDRLEN - 12];
+#endif
+} __attribute__ ((packed));
+
+#define TX_RADIOTAP_PRESENT (				\
+	(1 << IEEE80211_RADIOTAP_RATE) |		\
+	(1 << IEEE80211_RADIOTAP_DBM_TX_POWER) |	\
+	(1 << IEEE80211_RADIOTAP_RTS_RETRIES) |		\
+	(1 << IEEE80211_RADIOTAP_DATA_RETRIES)  |	\
+	0)
+
+#define IEEE80211_FC_VERSION_MASK    0x0003
+#define IEEE80211_FC_TYPE_MASK       0x000c
+#define IEEE80211_FC_TYPE_MGT        0x0000
+#define IEEE80211_FC_TYPE_CTL        0x0004
+#define IEEE80211_FC_TYPE_DATA       0x0008
+#define IEEE80211_FC_SUBTYPE_MASK    0x00f0
+#define IEEE80211_FC_TOFROMDS_MASK   0x0300
+#define IEEE80211_FC_TODS_MASK       0x0100
+#define IEEE80211_FC_FROMDS_MASK     0x0200
+#define IEEE80211_FC_NODS            0x0000
+#define IEEE80211_FC_TODS            0x0100
+#define IEEE80211_FC_FROMDS          0x0200
+#define IEEE80211_FC_DSTODS          0x0300
+
+struct rx_radiotap_hdr {
+	struct ieee80211_radiotap_header hdr;
+	u8 flags;
+	u8 rate;
+	u16 chan_freq;
+	u16 chan_flags;
+	u8 antenna;
+	u8 antsignal;
+	u16 rx_flags;
+#if 0
+	u8 pad[IEEE80211_RADIOTAP_HDRLEN - 18];
+#endif
+} __attribute__ ((packed));
+
+#define RX_RADIOTAP_PRESENT (			\
+	(1 << IEEE80211_RADIOTAP_FLAGS) |	\
+	(1 << IEEE80211_RADIOTAP_RATE) |	\
+	(1 << IEEE80211_RADIOTAP_CHANNEL) |	\
+	(1 << IEEE80211_RADIOTAP_ANTENNA) |	\
+	(1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL) |\
+	(1 << IEEE80211_RADIOTAP_RX_FLAGS) |	\
+	0)
+

+ 400 - 0
package/libertas/src/rx.c

@@ -0,0 +1,400 @@
+/**
+  * This file contains the handling of RX in wlan driver.
+  */
+#include <linux/etherdevice.h>
+#include <linux/types.h>
+
+#include "hostcmd.h"
+#include "radiotap.h"
+#include "decl.h"
+#include "dev.h"
+#include "wext.h"
+
+struct eth803hdr {
+	u8 dest_addr[6];
+	u8 src_addr[6];
+	u16 h803_len;
+} __attribute__ ((packed));
+
+struct rfc1042hdr {
+	u8 llc_dsap;
+	u8 llc_ssap;
+	u8 llc_ctrl;
+	u8 snap_oui[3];
+	u16 snap_type;
+} __attribute__ ((packed));
+
+struct rxpackethdr {
+	struct rxpd rx_pd;
+	struct eth803hdr eth803_hdr;
+	struct rfc1042hdr rfc1042_hdr;
+} __attribute__ ((packed));
+
+struct rx80211packethdr {
+	struct rxpd rx_pd;
+	void *eth80211_hdr;
+} __attribute__ ((packed));
+
+static int process_rxed_802_11_packet(struct lbs_private *priv,
+	struct sk_buff *skb);
+
+/**
+ *  @brief This function computes the avgSNR .
+ *
+ *  @param priv    A pointer to struct lbs_private structure
+ *  @return 	   avgSNR
+ */
+static u8 lbs_getavgsnr(struct lbs_private *priv)
+{
+	u8 i;
+	u16 temp = 0;
+	if (priv->numSNRNF == 0)
+		return 0;
+	for (i = 0; i < priv->numSNRNF; i++)
+		temp += priv->rawSNR[i];
+	return (u8) (temp / priv->numSNRNF);
+
+}
+
+/**
+ *  @brief This function computes the AvgNF
+ *
+ *  @param priv    A pointer to struct lbs_private structure
+ *  @return 	   AvgNF
+ */
+static u8 lbs_getavgnf(struct lbs_private *priv)
+{
+	u8 i;
+	u16 temp = 0;
+	if (priv->numSNRNF == 0)
+		return 0;
+	for (i = 0; i < priv->numSNRNF; i++)
+		temp += priv->rawNF[i];
+	return (u8) (temp / priv->numSNRNF);
+
+}
+
+/**
+ *  @brief This function save the raw SNR/NF to our internel buffer
+ *
+ *  @param priv    A pointer to struct lbs_private structure
+ *  @param prxpd   A pointer to rxpd structure of received packet
+ *  @return 	   n/a
+ */
+static void lbs_save_rawSNRNF(struct lbs_private *priv, struct rxpd *p_rx_pd)
+{
+	if (priv->numSNRNF < DEFAULT_DATA_AVG_FACTOR)
+		priv->numSNRNF++;
+	priv->rawSNR[priv->nextSNRNF] = p_rx_pd->snr;
+	priv->rawNF[priv->nextSNRNF] = p_rx_pd->nf;
+	priv->nextSNRNF++;
+	if (priv->nextSNRNF >= DEFAULT_DATA_AVG_FACTOR)
+		priv->nextSNRNF = 0;
+	return;
+}
+
+/**
+ *  @brief This function computes the RSSI in received packet.
+ *
+ *  @param priv    A pointer to struct lbs_private structure
+ *  @param prxpd   A pointer to rxpd structure of received packet
+ *  @return 	   n/a
+ */
+static void lbs_compute_rssi(struct lbs_private *priv, struct rxpd *p_rx_pd)
+{
+
+	lbs_deb_enter(LBS_DEB_RX);
+
+	lbs_deb_rx("rxpd: SNR %d, NF %d\n", p_rx_pd->snr, p_rx_pd->nf);
+	lbs_deb_rx("before computing SNR: SNR-avg = %d, NF-avg = %d\n",
+	       priv->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE,
+	       priv->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE);
+
+	priv->SNR[TYPE_RXPD][TYPE_NOAVG] = p_rx_pd->snr;
+	priv->NF[TYPE_RXPD][TYPE_NOAVG] = p_rx_pd->nf;
+	lbs_save_rawSNRNF(priv, p_rx_pd);
+
+	priv->SNR[TYPE_RXPD][TYPE_AVG] = lbs_getavgsnr(priv) * AVG_SCALE;
+	priv->NF[TYPE_RXPD][TYPE_AVG] = lbs_getavgnf(priv) * AVG_SCALE;
+	lbs_deb_rx("after computing SNR: SNR-avg = %d, NF-avg = %d\n",
+	       priv->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE,
+	       priv->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE);
+
+	priv->RSSI[TYPE_RXPD][TYPE_NOAVG] =
+	    CAL_RSSI(priv->SNR[TYPE_RXPD][TYPE_NOAVG],
+		     priv->NF[TYPE_RXPD][TYPE_NOAVG]);
+
+	priv->RSSI[TYPE_RXPD][TYPE_AVG] =
+	    CAL_RSSI(priv->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE,
+		     priv->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE);
+
+	lbs_deb_leave(LBS_DEB_RX);
+}
+
+/**
+ *  @brief This function processes received packet and forwards it
+ *  to kernel/upper layer
+ *
+ *  @param priv    A pointer to struct lbs_private
+ *  @param skb     A pointer to skb which includes the received packet
+ *  @return 	   0 or -1
+ */
+int lbs_process_rxed_packet(struct lbs_private *priv, struct sk_buff *skb)
+{
+	int ret = 0;
+	struct net_device *dev = priv->dev;
+	struct rxpackethdr *p_rx_pkt;
+	struct rxpd *p_rx_pd;
+
+	int hdrchop;
+	struct ethhdr *p_ethhdr;
+
+	const u8 rfc1042_eth_hdr[] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
+
+	lbs_deb_enter(LBS_DEB_RX);
+
+	skb->ip_summed = CHECKSUM_NONE;
+
+	if (priv->monitormode != LBS_MONITOR_OFF)
+		return process_rxed_802_11_packet(priv, skb);
+
+	p_rx_pkt = (struct rxpackethdr *) skb->data;
+	p_rx_pd = &p_rx_pkt->rx_pd;
+	if (priv->mesh_dev && (p_rx_pd->rx_control & RxPD_MESH_FRAME))
+		dev = priv->mesh_dev;
+
+	lbs_deb_hex(LBS_DEB_RX, "RX Data: Before chop rxpd", skb->data,
+		 min_t(unsigned int, skb->len, 100));
+
+	if (skb->len < (ETH_HLEN + 8 + sizeof(struct rxpd))) {
+		lbs_deb_rx("rx err: frame received with bad length\n");
+		priv->stats.rx_length_errors++;
+		ret = 0;
+		goto done;
+	}
+
+	/*
+	 * Check rxpd status and update 802.3 stat,
+	 */
+	if (!(p_rx_pd->status & cpu_to_le16(MRVDRV_RXPD_STATUS_OK))) {
+		lbs_deb_rx("rx err: frame received with bad status\n");
+		lbs_pr_alert("rxpd not ok\n");
+		priv->stats.rx_errors++;
+		ret = 0;
+		goto done;
+	}
+
+	lbs_deb_rx("rx data: skb->len-sizeof(RxPd) = %d-%zd = %zd\n",
+	       skb->len, sizeof(struct rxpd), skb->len - sizeof(struct rxpd));
+
+	lbs_deb_hex(LBS_DEB_RX, "RX Data: Dest", p_rx_pkt->eth803_hdr.dest_addr,
+		sizeof(p_rx_pkt->eth803_hdr.dest_addr));
+	lbs_deb_hex(LBS_DEB_RX, "RX Data: Src", p_rx_pkt->eth803_hdr.src_addr,
+		sizeof(p_rx_pkt->eth803_hdr.src_addr));
+
+	if (memcmp(&p_rx_pkt->rfc1042_hdr,
+		   rfc1042_eth_hdr, sizeof(rfc1042_eth_hdr)) == 0) {
+		/*
+		 *  Replace the 803 header and rfc1042 header (llc/snap) with an
+		 *    EthernetII header, keep the src/dst and snap_type (ethertype)
+		 *
+		 *  The firmware only passes up SNAP frames converting
+		 *    all RX Data from 802.11 to 802.2/LLC/SNAP frames.
+		 *
+		 *  To create the Ethernet II, just move the src, dst address right
+		 *    before the snap_type.
+		 */
+		p_ethhdr = (struct ethhdr *)
+		    ((u8 *) & p_rx_pkt->eth803_hdr
+		     + sizeof(p_rx_pkt->eth803_hdr) + sizeof(p_rx_pkt->rfc1042_hdr)
+		     - sizeof(p_rx_pkt->eth803_hdr.dest_addr)
+		     - sizeof(p_rx_pkt->eth803_hdr.src_addr)
+		     - sizeof(p_rx_pkt->rfc1042_hdr.snap_type));
+
+		memcpy(p_ethhdr->h_source, p_rx_pkt->eth803_hdr.src_addr,
+		       sizeof(p_ethhdr->h_source));
+		memcpy(p_ethhdr->h_dest, p_rx_pkt->eth803_hdr.dest_addr,
+		       sizeof(p_ethhdr->h_dest));
+
+		/* Chop off the rxpd + the excess memory from the 802.2/llc/snap header
+		 *   that was removed
+		 */
+		hdrchop = (u8 *) p_ethhdr - (u8 *) p_rx_pkt;
+	} else {
+		lbs_deb_hex(LBS_DEB_RX, "RX Data: LLC/SNAP",
+			(u8 *) & p_rx_pkt->rfc1042_hdr,
+			sizeof(p_rx_pkt->rfc1042_hdr));
+
+		/* Chop off the rxpd */
+		hdrchop = (u8 *) & p_rx_pkt->eth803_hdr - (u8 *) p_rx_pkt;
+	}
+
+	/* Chop off the leading header bytes so the skb points to the start of
+	 *   either the reconstructed EthII frame or the 802.2/llc/snap frame
+	 */
+	skb_pull(skb, hdrchop);
+
+	/* Take the data rate from the rxpd structure
+	 * only if the rate is auto
+	 */
+	if (priv->auto_rate)
+		priv->cur_rate = lbs_fw_index_to_data_rate(p_rx_pd->rx_rate);
+
+	lbs_compute_rssi(priv, p_rx_pd);
+
+	lbs_deb_rx("rx data: size of actual packet %d\n", skb->len);
+	priv->stats.rx_bytes += skb->len;
+	priv->stats.rx_packets++;
+
+	skb->protocol = eth_type_trans(skb, dev);
+	netif_rx(skb);
+
+	ret = 0;
+done:
+	lbs_deb_leave_args(LBS_DEB_RX, "ret %d", ret);
+	return ret;
+}
+EXPORT_SYMBOL_GPL(lbs_process_rxed_packet);
+
+/**
+ *  @brief This function converts Tx/Rx rates from the Marvell WLAN format
+ *  (see Table 2 in Section 3.1) to IEEE80211_RADIOTAP_RATE units (500 Kb/s)
+ *
+ *  @param rate    Input rate
+ *  @return 	   Output Rate (0 if invalid)
+ */
+static u8 convert_mv_rate_to_radiotap(u8 rate)
+{
+	switch (rate) {
+	case 0:		/*   1 Mbps */
+		return 2;
+	case 1:		/*   2 Mbps */
+		return 4;
+	case 2:		/* 5.5 Mbps */
+		return 11;
+	case 3:		/*  11 Mbps */
+		return 22;
+	/* case 4: reserved */
+	case 5:		/*   6 Mbps */
+		return 12;
+	case 6:		/*   9 Mbps */
+		return 18;
+	case 7:		/*  12 Mbps */
+		return 24;
+	case 8:		/*  18 Mbps */
+		return 36;
+	case 9:		/*  24 Mbps */
+		return 48;
+	case 10:		/*  36 Mbps */
+		return 72;
+	case 11:		/*  48 Mbps */
+		return 96;
+	case 12:		/*  54 Mbps */
+		return 108;
+	}
+	lbs_pr_alert("Invalid Marvell WLAN rate %i\n", rate);
+	return 0;
+}
+
+/**
+ *  @brief This function processes a received 802.11 packet and forwards it
+ *  to kernel/upper layer
+ *
+ *  @param priv    A pointer to struct lbs_private
+ *  @param skb     A pointer to skb which includes the received packet
+ *  @return 	   0 or -1
+ */
+static int process_rxed_802_11_packet(struct lbs_private *priv,
+	struct sk_buff *skb)
+{
+	int ret = 0;
+
+	struct rx80211packethdr *p_rx_pkt;
+	struct rxpd *prxpd;
+	struct rx_radiotap_hdr radiotap_hdr;
+	struct rx_radiotap_hdr *pradiotap_hdr;
+
+	lbs_deb_enter(LBS_DEB_RX);
+
+	p_rx_pkt = (struct rx80211packethdr *) skb->data;
+	prxpd = &p_rx_pkt->rx_pd;
+
+	// lbs_deb_hex(LBS_DEB_RX, "RX Data: Before chop rxpd", skb->data, min(skb->len, 100));
+
+	if (skb->len < (ETH_HLEN + 8 + sizeof(struct rxpd))) {
+		lbs_deb_rx("rx err: frame received with bad length\n");
+		priv->stats.rx_length_errors++;
+		ret = -EINVAL;
+		kfree(skb);
+		goto done;
+	}
+
+	/*
+	 * Check rxpd status and update 802.3 stat,
+	 */
+	if (!(prxpd->status & cpu_to_le16(MRVDRV_RXPD_STATUS_OK))) {
+		//lbs_deb_rx("rx err: frame received with bad status\n");
+		priv->stats.rx_errors++;
+	}
+
+	lbs_deb_rx("rx data: skb->len-sizeof(RxPd) = %d-%zd = %zd\n",
+	       skb->len, sizeof(struct rxpd), skb->len - sizeof(struct rxpd));
+
+	/* create the exported radio header */
+
+	/* radiotap header */
+	radiotap_hdr.hdr.it_version = 0;
+	/* XXX must check this value for pad */
+	radiotap_hdr.hdr.it_pad = 0;
+	radiotap_hdr.hdr.it_len = cpu_to_le16 (sizeof(struct rx_radiotap_hdr));
+	radiotap_hdr.hdr.it_present = cpu_to_le32 (RX_RADIOTAP_PRESENT);
+	/* unknown values */
+	radiotap_hdr.flags = 0;
+	radiotap_hdr.chan_freq = 0;
+	radiotap_hdr.chan_flags = 0;
+	radiotap_hdr.antenna = 0;
+	/* known values */
+	radiotap_hdr.rate = convert_mv_rate_to_radiotap(prxpd->rx_rate);
+	/* XXX must check no carryout */
+	radiotap_hdr.antsignal = prxpd->snr + prxpd->nf;
+	radiotap_hdr.rx_flags = 0;
+	if (!(prxpd->status & cpu_to_le16(MRVDRV_RXPD_STATUS_OK)))
+		radiotap_hdr.rx_flags |= IEEE80211_RADIOTAP_F_RX_BADFCS;
+	//memset(radiotap_hdr.pad, 0x11, IEEE80211_RADIOTAP_HDRLEN - 18);
+
+	/* chop the rxpd */
+	skb_pull(skb, sizeof(struct rxpd));
+
+	/* add space for the new radio header */
+	if ((skb_headroom(skb) < sizeof(struct rx_radiotap_hdr)) &&
+	    pskb_expand_head(skb, sizeof(struct rx_radiotap_hdr), 0, GFP_ATOMIC)) {
+		lbs_pr_alert("%s: couldn't pskb_expand_head\n", __func__);
+		ret = -ENOMEM;
+		kfree_skb(skb);
+		goto done;
+	}
+
+	pradiotap_hdr = (void *)skb_push(skb, sizeof(struct rx_radiotap_hdr));
+	memcpy(pradiotap_hdr, &radiotap_hdr, sizeof(struct rx_radiotap_hdr));
+
+	/* Take the data rate from the rxpd structure
+	 * only if the rate is auto
+	 */
+	if (priv->auto_rate)
+		priv->cur_rate = lbs_fw_index_to_data_rate(prxpd->rx_rate);
+
+	lbs_compute_rssi(priv, prxpd);
+
+	lbs_deb_rx("rx data: size of actual packet %d\n", skb->len);
+	priv->stats.rx_bytes += skb->len;
+	priv->stats.rx_packets++;
+
+	skb->protocol = eth_type_trans(skb, priv->rtap_net_dev);
+	netif_rx(skb);
+
+	ret = 0;
+
+done:
+	lbs_deb_leave_args(LBS_DEB_RX, "ret %d", ret);
+	return ret;
+}

+ 1643 - 0
package/libertas/src/scan.c

@@ -0,0 +1,1643 @@
+/**
+  * Functions implementing wlan scan IOCTL and firmware command APIs
+  *
+  * IOCTL handlers as well as command preperation and response routines
+  *  for sending scan commands to the firmware.
+  */
+#include <linux/ctype.h>
+#include <linux/if.h>
+#include <linux/netdevice.h>
+#include <linux/wireless.h>
+#include <linux/etherdevice.h>
+
+#include <net/ieee80211.h>
+#include <net/iw_handler.h>
+
+#include <asm/unaligned.h>
+
+#include "host.h"
+#include "decl.h"
+#include "dev.h"
+#include "scan.h"
+#include "join.h"
+
+//! Approximate amount of data needed to pass a scan result back to iwlist
+#define MAX_SCAN_CELL_SIZE  (IW_EV_ADDR_LEN             \
+                             + IW_ESSID_MAX_SIZE        \
+                             + IW_EV_UINT_LEN           \
+                             + IW_EV_FREQ_LEN           \
+                             + IW_EV_QUAL_LEN           \
+                             + IW_ESSID_MAX_SIZE        \
+                             + IW_EV_PARAM_LEN          \
+                             + 40)	/* 40 for WPAIE */
+
+//! Memory needed to store a max sized channel List TLV for a firmware scan
+#define CHAN_TLV_MAX_SIZE  (sizeof(struct mrvlietypesheader)    \
+                            + (MRVDRV_MAX_CHANNELS_PER_SCAN     \
+                               * sizeof(struct chanscanparamset)))
+
+//! Memory needed to store a max number/size SSID TLV for a firmware scan
+#define SSID_TLV_MAX_SIZE  (1 * sizeof(struct mrvlietypes_ssidparamset))
+
+//! Maximum memory needed for a lbs_scan_cmd_config with all TLVs at max
+#define MAX_SCAN_CFG_ALLOC (sizeof(struct lbs_scan_cmd_config)  \
+                            + CHAN_TLV_MAX_SIZE                 \
+                            + SSID_TLV_MAX_SIZE)
+
+//! The maximum number of channels the firmware can scan per command
+#define MRVDRV_MAX_CHANNELS_PER_SCAN   14
+
+/**
+ * @brief Number of channels to scan per firmware scan command issuance.
+ *
+ *  Number restricted to prevent hitting the limit on the amount of scan data
+ *  returned in a single firmware scan command.
+ */
+#define MRVDRV_CHANNELS_PER_SCAN_CMD   4
+
+//! Scan time specified in the channel TLV for each channel for passive scans
+#define MRVDRV_PASSIVE_SCAN_CHAN_TIME  100
+
+//! Scan time specified in the channel TLV for each channel for active scans
+#define MRVDRV_ACTIVE_SCAN_CHAN_TIME   100
+
+static const u8 zeromac[ETH_ALEN] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+static const u8 bcastmac[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+
+
+
+
+/*********************************************************************/
+/*                                                                   */
+/*  Misc helper functions                                            */
+/*                                                                   */
+/*********************************************************************/
+
+static inline void clear_bss_descriptor (struct bss_descriptor * bss)
+{
+	/* Don't blow away ->list, just BSS data */
+	memset(bss, 0, offsetof(struct bss_descriptor, list));
+}
+
+/**
+ *  @brief Compare two SSIDs
+ *
+ *  @param ssid1    A pointer to ssid to compare
+ *  @param ssid2    A pointer to ssid to compare
+ *
+ *  @return         0: ssid is same, otherwise is different
+ */
+int lbs_ssid_cmp(u8 *ssid1, u8 ssid1_len, u8 *ssid2, u8 ssid2_len)
+{
+	if (ssid1_len != ssid2_len)
+		return -1;
+
+	return memcmp(ssid1, ssid2, ssid1_len);
+}
+
+static inline int match_bss_no_security(struct lbs_802_11_security *secinfo,
+			struct bss_descriptor * match_bss)
+{
+	if (   !secinfo->wep_enabled
+	    && !secinfo->WPAenabled
+	    && !secinfo->WPA2enabled
+	    && match_bss->wpa_ie[0] != MFIE_TYPE_GENERIC
+	    && match_bss->rsn_ie[0] != MFIE_TYPE_RSN
+	    && !(match_bss->capability & WLAN_CAPABILITY_PRIVACY)) {
+		return 1;
+	}
+	return 0;
+}
+
+static inline int match_bss_static_wep(struct lbs_802_11_security *secinfo,
+			struct bss_descriptor * match_bss)
+{
+	if ( secinfo->wep_enabled
+	   && !secinfo->WPAenabled
+	   && !secinfo->WPA2enabled
+	   && (match_bss->capability & WLAN_CAPABILITY_PRIVACY)) {
+		return 1;
+	}
+	return 0;
+}
+
+static inline int match_bss_wpa(struct lbs_802_11_security *secinfo,
+			struct bss_descriptor * match_bss)
+{
+	if (  !secinfo->wep_enabled
+	   && secinfo->WPAenabled
+	   && (match_bss->wpa_ie[0] == MFIE_TYPE_GENERIC)
+	   /* privacy bit may NOT be set in some APs like LinkSys WRT54G
+	      && (match_bss->capability & WLAN_CAPABILITY_PRIVACY)) {
+	    */
+	   ) {
+		return 1;
+	}
+	return 0;
+}
+
+static inline int match_bss_wpa2(struct lbs_802_11_security *secinfo,
+			struct bss_descriptor * match_bss)
+{
+	if (  !secinfo->wep_enabled
+	   && secinfo->WPA2enabled
+	   && (match_bss->rsn_ie[0] == MFIE_TYPE_RSN)
+	   /* privacy bit may NOT be set in some APs like LinkSys WRT54G
+	      && (match_bss->capability & WLAN_CAPABILITY_PRIVACY)) {
+	    */
+	   ) {
+		return 1;
+	}
+	return 0;
+}
+
+static inline int match_bss_dynamic_wep(struct lbs_802_11_security *secinfo,
+			struct bss_descriptor * match_bss)
+{
+	if (  !secinfo->wep_enabled
+	   && !secinfo->WPAenabled
+	   && !secinfo->WPA2enabled
+	   && (match_bss->wpa_ie[0] != MFIE_TYPE_GENERIC)
+	   && (match_bss->rsn_ie[0] != MFIE_TYPE_RSN)
+	   && (match_bss->capability & WLAN_CAPABILITY_PRIVACY)) {
+		return 1;
+	}
+	return 0;
+}
+
+static inline int is_same_network(struct bss_descriptor *src,
+				  struct bss_descriptor *dst)
+{
+	/* A network is only a duplicate if the channel, BSSID, and ESSID
+	 * all match.  We treat all <hidden> with the same BSSID and channel
+	 * as one network */
+	return ((src->ssid_len == dst->ssid_len) &&
+		(src->channel == dst->channel) &&
+		!compare_ether_addr(src->bssid, dst->bssid) &&
+		!memcmp(src->ssid, dst->ssid, src->ssid_len));
+}
+
+/**
+ *  @brief Check if a scanned network compatible with the driver settings
+ *
+ *   WEP     WPA     WPA2    ad-hoc  encrypt                      Network
+ * enabled enabled  enabled   AES     mode   privacy  WPA  WPA2  Compatible
+ *    0       0        0       0      NONE      0      0    0   yes No security
+ *    1       0        0       0      NONE      1      0    0   yes Static WEP
+ *    0       1        0       0       x        1x     1    x   yes WPA
+ *    0       0        1       0       x        1x     x    1   yes WPA2
+ *    0       0        0       1      NONE      1      0    0   yes Ad-hoc AES
+ *    0       0        0       0     !=NONE     1      0    0   yes Dynamic WEP
+ *
+ *
+ *  @param priv A pointer to struct lbs_private
+ *  @param index   Index in scantable to check against current driver settings
+ *  @param mode    Network mode: Infrastructure or IBSS
+ *
+ *  @return        Index in scantable, or error code if negative
+ */
+static int is_network_compatible(struct lbs_private *priv,
+		struct bss_descriptor * bss, u8 mode)
+{
+	int matched = 0;
+
+	lbs_deb_enter(LBS_DEB_SCAN);
+
+	if (bss->mode != mode)
+		goto done;
+
+	if ((matched = match_bss_no_security(&priv->secinfo, bss))) {
+		goto done;
+	} else if ((matched = match_bss_static_wep(&priv->secinfo, bss))) {
+		goto done;
+	} else if ((matched = match_bss_wpa(&priv->secinfo, bss))) {
+		lbs_deb_scan(
+		       "is_network_compatible() WPA: wpa_ie 0x%x "
+		       "wpa2_ie 0x%x WEP %s WPA %s WPA2 %s "
+		       "privacy 0x%x\n", bss->wpa_ie[0], bss->rsn_ie[0],
+		       priv->secinfo.wep_enabled ? "e" : "d",
+		       priv->secinfo.WPAenabled ? "e" : "d",
+		       priv->secinfo.WPA2enabled ? "e" : "d",
+		       (bss->capability & WLAN_CAPABILITY_PRIVACY));
+		goto done;
+	} else if ((matched = match_bss_wpa2(&priv->secinfo, bss))) {
+		lbs_deb_scan(
+		       "is_network_compatible() WPA2: wpa_ie 0x%x "
+		       "wpa2_ie 0x%x WEP %s WPA %s WPA2 %s "
+		       "privacy 0x%x\n", bss->wpa_ie[0], bss->rsn_ie[0],
+		       priv->secinfo.wep_enabled ? "e" : "d",
+		       priv->secinfo.WPAenabled ? "e" : "d",
+		       priv->secinfo.WPA2enabled ? "e" : "d",
+		       (bss->capability & WLAN_CAPABILITY_PRIVACY));
+		goto done;
+	} else if ((matched = match_bss_dynamic_wep(&priv->secinfo, bss))) {
+		lbs_deb_scan(
+		       "is_network_compatible() dynamic WEP: "
+		       "wpa_ie 0x%x wpa2_ie 0x%x privacy 0x%x\n",
+		       bss->wpa_ie[0], bss->rsn_ie[0],
+		       (bss->capability & WLAN_CAPABILITY_PRIVACY));
+		goto done;
+	}
+
+	/* bss security settings don't match those configured on card */
+	lbs_deb_scan(
+	       "is_network_compatible() FAILED: wpa_ie 0x%x "
+	       "wpa2_ie 0x%x WEP %s WPA %s WPA2 %s privacy 0x%x\n",
+	       bss->wpa_ie[0], bss->rsn_ie[0],
+	       priv->secinfo.wep_enabled ? "e" : "d",
+	       priv->secinfo.WPAenabled ? "e" : "d",
+	       priv->secinfo.WPA2enabled ? "e" : "d",
+	       (bss->capability & WLAN_CAPABILITY_PRIVACY));
+
+done:
+	lbs_deb_leave_args(LBS_DEB_SCAN, "matched: %d", matched);
+	return matched;
+}
+
+
+
+
+/*********************************************************************/
+/*                                                                   */
+/*  Main scanning support                                            */
+/*                                                                   */
+/*********************************************************************/
+
+void lbs_scan_worker(struct work_struct *work)
+{
+	struct lbs_private *priv =
+		container_of(work, struct lbs_private, scan_work.work);
+
+	lbs_deb_enter(LBS_DEB_SCAN);
+	lbs_scan_networks(priv, NULL, 0);
+	lbs_deb_leave(LBS_DEB_SCAN);
+}
+
+
+/**
+ *  @brief Create a channel list for the driver to scan based on region info
+ *
+ *  Only used from lbs_scan_setup_scan_config()
+ *
+ *  Use the driver region/band information to construct a comprehensive list
+ *    of channels to scan.  This routine is used for any scan that is not
+ *    provided a specific channel list to scan.
+ *
+ *  @param priv          A pointer to struct lbs_private structure
+ *  @param scanchanlist  Output parameter: resulting channel list to scan
+ *  @param filteredscan  Flag indicating whether or not a BSSID or SSID filter
+ *                       is being sent in the command to firmware.  Used to
+ *                       increase the number of channels sent in a scan
+ *                       command and to disable the firmware channel scan
+ *                       filter.
+ *
+ *  @return              void
+ */
+static int lbs_scan_create_channel_list(struct lbs_private *priv,
+					  struct chanscanparamset * scanchanlist,
+					  u8 filteredscan)
+{
+
+	struct region_channel *scanregion;
+	struct chan_freq_power *cfp;
+	int rgnidx;
+	int chanidx;
+	int nextchan;
+	u8 scantype;
+
+	chanidx = 0;
+
+	/* Set the default scan type to the user specified type, will later
+	 *   be changed to passive on a per channel basis if restricted by
+	 *   regulatory requirements (11d or 11h)
+	 */
+	scantype = CMD_SCAN_TYPE_ACTIVE;
+
+	for (rgnidx = 0; rgnidx < ARRAY_SIZE(priv->region_channel); rgnidx++) {
+		if (priv->enable11d &&
+		    (priv->connect_status != LBS_CONNECTED) &&
+		    (priv->mesh_connect_status != LBS_CONNECTED)) {
+			/* Scan all the supported chan for the first scan */
+			if (!priv->universal_channel[rgnidx].valid)
+				continue;
+			scanregion = &priv->universal_channel[rgnidx];
+
+			/* clear the parsed_region_chan for the first scan */
+			memset(&priv->parsed_region_chan, 0x00,
+			       sizeof(priv->parsed_region_chan));
+		} else {
+			if (!priv->region_channel[rgnidx].valid)
+				continue;
+			scanregion = &priv->region_channel[rgnidx];
+		}
+
+		for (nextchan = 0;
+		     nextchan < scanregion->nrcfp; nextchan++, chanidx++) {
+
+			cfp = scanregion->CFP + nextchan;
+
+			if (priv->enable11d) {
+				scantype =
+				    lbs_get_scan_type_11d(cfp->channel,
+							   &priv->
+							   parsed_region_chan);
+			}
+
+			switch (scanregion->band) {
+			case BAND_B:
+			case BAND_G:
+			default:
+				scanchanlist[chanidx].radiotype =
+				    CMD_SCAN_RADIO_TYPE_BG;
+				break;
+			}
+
+			if (scantype == CMD_SCAN_TYPE_PASSIVE) {
+				scanchanlist[chanidx].maxscantime =
+				    cpu_to_le16(MRVDRV_PASSIVE_SCAN_CHAN_TIME);
+				scanchanlist[chanidx].chanscanmode.passivescan =
+				    1;
+			} else {
+				scanchanlist[chanidx].maxscantime =
+				    cpu_to_le16(MRVDRV_ACTIVE_SCAN_CHAN_TIME);
+				scanchanlist[chanidx].chanscanmode.passivescan =
+				    0;
+			}
+
+			scanchanlist[chanidx].channumber = cfp->channel;
+
+			if (filteredscan) {
+				scanchanlist[chanidx].chanscanmode.
+				    disablechanfilt = 1;
+			}
+		}
+	}
+	return chanidx;
+}
+
+
+/*
+ * Add SSID TLV of the form:
+ *
+ * TLV-ID SSID     00 00
+ * length          06 00
+ * ssid            4d 4e 54 45 53 54
+ */
+static int lbs_scan_add_ssid_tlv(u8 *tlv,
+	const struct lbs_ioctl_user_scan_cfg *user_cfg)
+{
+	struct mrvlietypes_ssidparamset *ssid_tlv =
+		(struct mrvlietypes_ssidparamset *)tlv;
+	ssid_tlv->header.type = cpu_to_le16(TLV_TYPE_SSID);
+	ssid_tlv->header.len = cpu_to_le16(user_cfg->ssid_len);
+	memcpy(ssid_tlv->ssid, user_cfg->ssid, user_cfg->ssid_len);
+	return sizeof(ssid_tlv->header) + user_cfg->ssid_len;
+}
+
+
+/*
+ * Add CHANLIST TLV of the form
+ *
+ * TLV-ID CHANLIST 01 01
+ * length          5b 00
+ * channel 1       00 01 00 00 00 64 00
+ *   radio type    00
+ *   channel          01
+ *   scan type           00
+ *   min scan time          00 00
+ *   max scan time                64 00
+ * channel 2       00 02 00 00 00 64 00
+ * channel 3       00 03 00 00 00 64 00
+ * channel 4       00 04 00 00 00 64 00
+ * channel 5       00 05 00 00 00 64 00
+ * channel 6       00 06 00 00 00 64 00
+ * channel 7       00 07 00 00 00 64 00
+ * channel 8       00 08 00 00 00 64 00
+ * channel 9       00 09 00 00 00 64 00
+ * channel 10      00 0a 00 00 00 64 00
+ * channel 11      00 0b 00 00 00 64 00
+ * channel 12      00 0c 00 00 00 64 00
+ * channel 13      00 0d 00 00 00 64 00
+ *
+ */
+static int lbs_scan_add_chanlist_tlv(u8 *tlv,
+	struct chanscanparamset *chan_list,
+	int chan_count)
+{
+	size_t size = sizeof(struct chanscanparamset) * chan_count;
+	struct mrvlietypes_chanlistparamset *chan_tlv =
+		(struct mrvlietypes_chanlistparamset *) tlv;
+
+	chan_tlv->header.type = cpu_to_le16(TLV_TYPE_CHANLIST);
+	memcpy(chan_tlv->chanscanparam, chan_list, size);
+	chan_tlv->header.len = cpu_to_le16(size);
+	return sizeof(chan_tlv->header) + size;
+}
+
+
+/*
+ * Add RATES TLV of the form
+ *
+ * TLV-ID RATES    01 00
+ * length          0e 00
+ * rates           82 84 8b 96 0c 12 18 24 30 48 60 6c
+ *
+ * The rates are in lbs_bg_rates[], but for the 802.11b
+ * rates the high bit isn't set.
+ */
+static int lbs_scan_add_rates_tlv(u8 *tlv)
+{
+	int i;
+	struct mrvlietypes_ratesparamset *rate_tlv =
+		(struct mrvlietypes_ratesparamset *) tlv;
+
+	rate_tlv->header.type = cpu_to_le16(TLV_TYPE_RATES);
+	tlv += sizeof(rate_tlv->header);
+	for (i = 0; i < MAX_RATES; i++) {
+		*tlv = lbs_bg_rates[i];
+		if (*tlv == 0)
+			break;
+		/* This code makes sure that the 802.11b rates (1 MBit/s, 2
+		   MBit/s, 5.5 MBit/s and 11 MBit/s get's the high bit set.
+		   Note that the values are MBit/s * 2, to mark them as
+		   basic rates so that the firmware likes it better */
+		if (*tlv == 0x02 || *tlv == 0x04 ||
+		    *tlv == 0x0b || *tlv == 0x16)
+			*tlv |= 0x80;
+		tlv++;
+	}
+	rate_tlv->header.len = cpu_to_le16(i);
+	return sizeof(rate_tlv->header) + i;
+}
+
+
+/*
+ * Generate the CMD_802_11_SCAN command with the proper tlv
+ * for a bunch of channels.
+ */
+static int lbs_do_scan(struct lbs_private *priv,
+	u8 bsstype,
+	struct chanscanparamset *chan_list,
+	int chan_count,
+	const struct lbs_ioctl_user_scan_cfg *user_cfg)
+{
+	int ret = -ENOMEM;
+	struct lbs_scan_cmd_config *scan_cmd;
+	u8 *tlv;    /* pointer into our current, growing TLV storage area */
+
+	lbs_deb_enter_args(LBS_DEB_SCAN, "bsstype %d, chanlist[].chan %d, "
+		"chan_count %d",
+		bsstype, chan_list[0].channumber, chan_count);
+
+	/* create the fixed part for scan command */
+	scan_cmd = kzalloc(MAX_SCAN_CFG_ALLOC, GFP_KERNEL);
+	if (scan_cmd == NULL)
+		goto out;
+	tlv = scan_cmd->tlvbuffer;
+	if (user_cfg)
+		memcpy(scan_cmd->bssid, user_cfg->bssid, ETH_ALEN);
+	scan_cmd->bsstype = bsstype;
+
+	/* add TLVs */
+	if (user_cfg && user_cfg->ssid_len)
+		tlv += lbs_scan_add_ssid_tlv(tlv, user_cfg);
+	if (chan_list && chan_count)
+		tlv += lbs_scan_add_chanlist_tlv(tlv, chan_list, chan_count);
+	tlv += lbs_scan_add_rates_tlv(tlv);
+
+	/* This is the final data we are about to send */
+	scan_cmd->tlvbufferlen = tlv - scan_cmd->tlvbuffer;
+	lbs_deb_hex(LBS_DEB_SCAN, "SCAN_CMD", (void *)scan_cmd, 1+6);
+	lbs_deb_hex(LBS_DEB_SCAN, "SCAN_TLV", scan_cmd->tlvbuffer,
+		scan_cmd->tlvbufferlen);
+
+	ret = lbs_prepare_and_send_command(priv, CMD_802_11_SCAN, 0,
+		CMD_OPTION_WAITFORRSP, 0, scan_cmd);
+out:
+	kfree(scan_cmd);
+	lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret);
+	return ret;
+}
+
+
+/**
+ *  @brief Internal function used to start a scan based on an input config
+ *
+ *  Also used from debugfs
+ *
+ *  Use the input user scan configuration information when provided in
+ *    order to send the appropriate scan commands to firmware to populate or
+ *    update the internal driver scan table
+ *
+ *  @param priv          A pointer to struct lbs_private structure
+ *  @param puserscanin   Pointer to the input configuration for the requested
+ *                       scan.
+ *
+ *  @return              0 or < 0 if error
+ */
+int lbs_scan_networks(struct lbs_private *priv,
+	const struct lbs_ioctl_user_scan_cfg *user_cfg,
+                       int full_scan)
+{
+	int ret = -ENOMEM;
+	struct chanscanparamset *chan_list;
+	struct chanscanparamset *curr_chans;
+	int chan_count;
+	u8 bsstype = CMD_BSS_TYPE_ANY;
+	int numchannels = MRVDRV_CHANNELS_PER_SCAN_CMD;
+	int filteredscan = 0;
+	union iwreq_data wrqu;
+#ifdef CONFIG_LIBERTAS_DEBUG
+	struct bss_descriptor *iter;
+	int i = 0;
+	DECLARE_MAC_BUF(mac);
+#endif
+
+	lbs_deb_enter_args(LBS_DEB_SCAN, "full_scan %d",
+		full_scan);
+
+	/* Cancel any partial outstanding partial scans if this scan
+	 * is a full scan.
+	 */
+	if (full_scan && delayed_work_pending(&priv->scan_work))
+		cancel_delayed_work(&priv->scan_work);
+
+	/* Determine same scan parameters */
+	if (user_cfg) {
+		if (user_cfg->bsstype)
+			bsstype = user_cfg->bsstype;
+		if (compare_ether_addr(user_cfg->bssid, &zeromac[0]) != 0) {
+			numchannels = MRVDRV_MAX_CHANNELS_PER_SCAN;
+			filteredscan = 1;
+		}
+	}
+	lbs_deb_scan("numchannels %d, bsstype %d, "
+		"filteredscan %d\n",
+		numchannels, bsstype, filteredscan);
+
+	/* Create list of channels to scan */
+	chan_list = kzalloc(sizeof(struct chanscanparamset) *
+				LBS_IOCTL_USER_SCAN_CHAN_MAX, GFP_KERNEL);
+	if (!chan_list) {
+		lbs_pr_alert("SCAN: chan_list empty\n");
+		goto out;
+	}
+
+	/* We want to scan all channels */
+	chan_count = lbs_scan_create_channel_list(priv, chan_list,
+		filteredscan);
+
+	netif_stop_queue(priv->dev);
+	netif_carrier_off(priv->dev);
+	if (priv->mesh_dev) {
+			netif_stop_queue(priv->mesh_dev);
+			netif_carrier_off(priv->mesh_dev);
+	}
+
+	/* Prepare to continue an interrupted scan */
+	lbs_deb_scan("chan_count %d, last_scanned_channel %d\n",
+		chan_count, priv->last_scanned_channel);
+	curr_chans = chan_list;
+	/* advance channel list by already-scanned-channels */
+	if (priv->last_scanned_channel > 0) {
+		curr_chans += priv->last_scanned_channel;
+		chan_count -= priv->last_scanned_channel;
+	}
+
+	/* Send scan command(s)
+	 * numchannels contains the number of channels we should maximally scan
+	 * chan_count is the total number of channels to scan
+	 */
+
+	while (chan_count) {
+		int to_scan = min(numchannels, chan_count);
+		lbs_deb_scan("scanning %d of %d channels\n",
+			to_scan, chan_count);
+		ret = lbs_do_scan(priv, bsstype, curr_chans,
+			to_scan, user_cfg);
+		if (ret) {
+			lbs_pr_err("SCAN_CMD failed\n");
+			goto out2;
+		}
+		curr_chans += to_scan;
+		chan_count -= to_scan;
+
+		/* somehow schedule the next part of the scan */
+		if (chan_count &&
+		    !full_scan &&
+		    !priv->surpriseremoved) {
+			/* -1 marks just that we're currently scanning */
+			if (priv->last_scanned_channel < 0)
+				priv->last_scanned_channel = to_scan;
+			else
+				priv->last_scanned_channel += to_scan;
+			cancel_delayed_work(&priv->scan_work);
+			queue_delayed_work(priv->work_thread, &priv->scan_work,
+				msecs_to_jiffies(300));
+			/* skip over GIWSCAN event */
+			goto out;
+		}
+
+	}
+	memset(&wrqu, 0, sizeof(union iwreq_data));
+	wireless_send_event(priv->dev, SIOCGIWSCAN, &wrqu, NULL);
+
+#ifdef CONFIG_LIBERTAS_DEBUG
+	/* Dump the scan table */
+	mutex_lock(&priv->lock);
+	lbs_deb_scan("scan table:\n");
+	list_for_each_entry(iter, &priv->network_list, list)
+		lbs_deb_scan("%02d: BSSID %s, RSSI %d, SSID '%s'\n",
+		       i++, print_mac(mac, iter->bssid), (s32) iter->rssi,
+		       escape_essid(iter->ssid, iter->ssid_len));
+	mutex_unlock(&priv->lock);
+#endif
+
+out2:
+	priv->last_scanned_channel = 0;
+
+out:
+	if (priv->connect_status == LBS_CONNECTED) {
+		netif_carrier_on(priv->dev);
+		netif_wake_queue(priv->dev);
+	}
+	if (priv->mesh_dev && (priv->mesh_connect_status == LBS_CONNECTED)) {
+		netif_carrier_on(priv->mesh_dev);
+		netif_wake_queue(priv->mesh_dev);
+	}
+	kfree(chan_list);
+
+	lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret);
+	return ret;
+}
+
+
+
+
+/*********************************************************************/
+/*                                                                   */
+/*  Result interpretation                                            */
+/*                                                                   */
+/*********************************************************************/
+
+/**
+ *  @brief Interpret a BSS scan response returned from the firmware
+ *
+ *  Parse the various fixed fields and IEs passed back for a a BSS probe
+ *  response or beacon from the scan command.  Record information as needed
+ *  in the scan table struct bss_descriptor for that entry.
+ *
+ *  @param bss  Output parameter: Pointer to the BSS Entry
+ *
+ *  @return             0 or -1
+ */
+static int lbs_process_bss(struct bss_descriptor *bss,
+				u8 ** pbeaconinfo, int *bytesleft)
+{
+	struct ieeetypes_fhparamset *pFH;
+	struct ieeetypes_dsparamset *pDS;
+	struct ieeetypes_cfparamset *pCF;
+	struct ieeetypes_ibssparamset *pibss;
+	DECLARE_MAC_BUF(mac);
+	struct ieeetypes_countryinfoset *pcountryinfo;
+	u8 *pos, *end, *p;
+	u8 n_ex_rates = 0, got_basic_rates = 0, n_basic_rates = 0;
+	u16 beaconsize = 0;
+	int ret;
+
+	lbs_deb_enter(LBS_DEB_SCAN);
+
+	if (*bytesleft >= sizeof(beaconsize)) {
+		/* Extract & convert beacon size from the command buffer */
+		beaconsize = le16_to_cpu(get_unaligned((__le16 *)*pbeaconinfo));
+		*bytesleft -= sizeof(beaconsize);
+		*pbeaconinfo += sizeof(beaconsize);
+	}
+
+	if (beaconsize == 0 || beaconsize > *bytesleft) {
+		*pbeaconinfo += *bytesleft;
+		*bytesleft = 0;
+		ret = -1;
+		goto done;
+	}
+
+	/* Initialize the current working beacon pointer for this BSS iteration */
+	pos = *pbeaconinfo;
+	end = pos + beaconsize;
+
+	/* Advance the return beacon pointer past the current beacon */
+	*pbeaconinfo += beaconsize;
+	*bytesleft -= beaconsize;
+
+	memcpy(bss->bssid, pos, ETH_ALEN);
+	lbs_deb_scan("process_bss: BSSID %s\n", print_mac(mac, bss->bssid));
+	pos += ETH_ALEN;
+
+	if ((end - pos) < 12) {
+		lbs_deb_scan("process_bss: Not enough bytes left\n");
+		ret = -1;
+		goto done;
+	}
+
+	/*
+	 * next 4 fields are RSSI, time stamp, beacon interval,
+	 *   and capability information
+	 */
+
+	/* RSSI is 1 byte long */
+	bss->rssi = *pos;
+	lbs_deb_scan("process_bss: RSSI %d\n", *pos);
+	pos++;
+
+	/* time stamp is 8 bytes long */
+	pos += 8;
+
+	/* beacon interval is 2 bytes long */
+	bss->beaconperiod = le16_to_cpup((void *) pos);
+	pos += 2;
+
+	/* capability information is 2 bytes long */
+	bss->capability = le16_to_cpup((void *) pos);
+	lbs_deb_scan("process_bss: capabilities 0x%04x\n", bss->capability);
+	pos += 2;
+
+	if (bss->capability & WLAN_CAPABILITY_PRIVACY)
+		lbs_deb_scan("process_bss: WEP enabled\n");
+	if (bss->capability & WLAN_CAPABILITY_IBSS)
+		bss->mode = IW_MODE_ADHOC;
+	else
+		bss->mode = IW_MODE_INFRA;
+
+	/* rest of the current buffer are IE's */
+	lbs_deb_scan("process_bss: IE len %zd\n", end - pos);
+	lbs_deb_hex(LBS_DEB_SCAN, "process_bss: IE info", pos, end - pos);
+
+	/* process variable IE */
+	while (pos <= end - 2) {
+		struct ieee80211_info_element * elem =
+			(struct ieee80211_info_element *) pos;
+
+		if (pos + elem->len > end) {
+			lbs_deb_scan("process_bss: error in processing IE, "
+			       "bytes left < IE length\n");
+			break;
+		}
+
+		switch (elem->id) {
+		case MFIE_TYPE_SSID:
+			bss->ssid_len = elem->len;
+			memcpy(bss->ssid, elem->data, elem->len);
+			lbs_deb_scan("got SSID IE: '%s', len %u\n",
+			             escape_essid(bss->ssid, bss->ssid_len),
+			             bss->ssid_len);
+			break;
+
+		case MFIE_TYPE_RATES:
+			n_basic_rates = min_t(u8, MAX_RATES, elem->len);
+			memcpy(bss->rates, elem->data, n_basic_rates);
+			got_basic_rates = 1;
+			lbs_deb_scan("got RATES IE\n");
+			break;
+
+		case MFIE_TYPE_FH_SET:
+			pFH = (struct ieeetypes_fhparamset *) pos;
+			memmove(&bss->phyparamset.fhparamset, pFH,
+				sizeof(struct ieeetypes_fhparamset));
+			lbs_deb_scan("got FH IE\n");
+			break;
+
+		case MFIE_TYPE_DS_SET:
+			pDS = (struct ieeetypes_dsparamset *) pos;
+			bss->channel = pDS->currentchan;
+			memcpy(&bss->phyparamset.dsparamset, pDS,
+			       sizeof(struct ieeetypes_dsparamset));
+			lbs_deb_scan("got DS IE, channel %d\n", bss->channel);
+			break;
+
+		case MFIE_TYPE_CF_SET:
+			pCF = (struct ieeetypes_cfparamset *) pos;
+			memcpy(&bss->ssparamset.cfparamset, pCF,
+			       sizeof(struct ieeetypes_cfparamset));
+			lbs_deb_scan("got CF IE\n");
+			break;
+
+		case MFIE_TYPE_IBSS_SET:
+			pibss = (struct ieeetypes_ibssparamset *) pos;
+			bss->atimwindow = le16_to_cpu(pibss->atimwindow);
+			memmove(&bss->ssparamset.ibssparamset, pibss,
+				sizeof(struct ieeetypes_ibssparamset));
+			lbs_deb_scan("got IBSS IE\n");
+			break;
+
+		case MFIE_TYPE_COUNTRY:
+			pcountryinfo = (struct ieeetypes_countryinfoset *) pos;
+			lbs_deb_scan("got COUNTRY IE\n");
+			if (pcountryinfo->len < sizeof(pcountryinfo->countrycode)
+			    || pcountryinfo->len > 254) {
+				lbs_deb_scan("process_bss: 11D- Err "
+				       "CountryInfo len %d, min %zd, max 254\n",
+				       pcountryinfo->len,
+				       sizeof(pcountryinfo->countrycode));
+				ret = -1;
+				goto done;
+			}
+
+			memcpy(&bss->countryinfo,
+			       pcountryinfo, pcountryinfo->len + 2);
+			lbs_deb_hex(LBS_DEB_SCAN, "process_bss: 11d countryinfo",
+				(u8 *) pcountryinfo,
+				(u32) (pcountryinfo->len + 2));
+			break;
+
+		case MFIE_TYPE_RATES_EX:
+			/* only process extended supported rate if data rate is
+			 * already found. Data rate IE should come before
+			 * extended supported rate IE
+			 */
+			lbs_deb_scan("got RATESEX IE\n");
+			if (!got_basic_rates) {
+				lbs_deb_scan("... but ignoring it\n");
+				break;
+			}
+
+			n_ex_rates = elem->len;
+			if (n_basic_rates + n_ex_rates > MAX_RATES)
+				n_ex_rates = MAX_RATES - n_basic_rates;
+
+			p = bss->rates + n_basic_rates;
+			memcpy(p, elem->data, n_ex_rates);
+			break;
+
+		case MFIE_TYPE_GENERIC:
+			if (elem->len >= 4 &&
+			    elem->data[0] == 0x00 &&
+			    elem->data[1] == 0x50 &&
+			    elem->data[2] == 0xf2 &&
+			    elem->data[3] == 0x01) {
+				bss->wpa_ie_len = min(elem->len + 2,
+				                      MAX_WPA_IE_LEN);
+				memcpy(bss->wpa_ie, elem, bss->wpa_ie_len);
+				lbs_deb_scan("got WPA IE\n");
+				lbs_deb_hex(LBS_DEB_SCAN, "WPA IE", bss->wpa_ie,
+				            elem->len);
+			} else if (elem->len >= MARVELL_MESH_IE_LENGTH &&
+			    elem->data[0] == 0x00 &&
+			    elem->data[1] == 0x50 &&
+			    elem->data[2] == 0x43 &&
+			    elem->data[3] == 0x04) {
+				lbs_deb_scan("got mesh IE\n");
+				bss->mesh = 1;
+			} else {
+				lbs_deb_scan("got generiec IE: "
+					"%02x:%02x:%02x:%02x, len %d\n",
+					elem->data[0], elem->data[1],
+					elem->data[2], elem->data[3],
+					elem->len);
+			}
+			break;
+
+		case MFIE_TYPE_RSN:
+			lbs_deb_scan("got RSN IE\n");
+			bss->rsn_ie_len = min(elem->len + 2, MAX_WPA_IE_LEN);
+			memcpy(bss->rsn_ie, elem, bss->rsn_ie_len);
+			lbs_deb_hex(LBS_DEB_SCAN, "process_bss: RSN_IE",
+				bss->rsn_ie, elem->len);
+			break;
+
+		default:
+			lbs_deb_scan("got IE 0x%04x, len %d\n",
+				elem->id, elem->len);
+			break;
+		}
+
+		pos += elem->len + 2;
+	}
+
+	/* Timestamp */
+	bss->last_scanned = jiffies;
+	lbs_unset_basic_rate_flags(bss->rates, sizeof(bss->rates));
+
+	ret = 0;
+
+done:
+	lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret);
+	return ret;
+}
+
+/**
+ *  @brief This function finds a specific compatible BSSID in the scan list
+ *
+ *  Used in association code
+ *
+ *  @param priv  A pointer to struct lbs_private
+ *  @param bssid    BSSID to find in the scan list
+ *  @param mode     Network mode: Infrastructure or IBSS
+ *
+ *  @return         index in BSSID list, or error return code (< 0)
+ */
+struct bss_descriptor *lbs_find_bssid_in_list(struct lbs_private *priv,
+		u8 * bssid, u8 mode)
+{
+	struct bss_descriptor * iter_bss;
+	struct bss_descriptor * found_bss = NULL;
+
+	lbs_deb_enter(LBS_DEB_SCAN);
+
+	if (!bssid)
+		goto out;
+
+	lbs_deb_hex(LBS_DEB_SCAN, "looking for",
+		bssid, ETH_ALEN);
+
+	/* Look through the scan table for a compatible match.  The loop will
+	 *   continue past a matched bssid that is not compatible in case there
+	 *   is an AP with multiple SSIDs assigned to the same BSSID
+	 */
+	mutex_lock(&priv->lock);
+	list_for_each_entry (iter_bss, &priv->network_list, list) {
+		if (compare_ether_addr(iter_bss->bssid, bssid))
+			continue; /* bssid doesn't match */
+		switch (mode) {
+		case IW_MODE_INFRA:
+		case IW_MODE_ADHOC:
+			if (!is_network_compatible(priv, iter_bss, mode))
+				break;
+			found_bss = iter_bss;
+			break;
+		default:
+			found_bss = iter_bss;
+			break;
+		}
+	}
+	mutex_unlock(&priv->lock);
+
+out:
+	lbs_deb_leave_args(LBS_DEB_SCAN, "found_bss %p", found_bss);
+	return found_bss;
+}
+
+/**
+ *  @brief This function finds ssid in ssid list.
+ *
+ *  Used in association code
+ *
+ *  @param priv  A pointer to struct lbs_private
+ *  @param ssid     SSID to find in the list
+ *  @param bssid    BSSID to qualify the SSID selection (if provided)
+ *  @param mode     Network mode: Infrastructure or IBSS
+ *
+ *  @return         index in BSSID list
+ */
+struct bss_descriptor *lbs_find_ssid_in_list(struct lbs_private *priv,
+		   u8 *ssid, u8 ssid_len, u8 * bssid, u8 mode,
+		   int channel)
+{
+	u8 bestrssi = 0;
+	struct bss_descriptor * iter_bss = NULL;
+	struct bss_descriptor * found_bss = NULL;
+	struct bss_descriptor * tmp_oldest = NULL;
+
+	lbs_deb_enter(LBS_DEB_SCAN);
+
+	mutex_lock(&priv->lock);
+
+	list_for_each_entry (iter_bss, &priv->network_list, list) {
+		if (   !tmp_oldest
+		    || (iter_bss->last_scanned < tmp_oldest->last_scanned))
+			tmp_oldest = iter_bss;
+
+		if (lbs_ssid_cmp(iter_bss->ssid, iter_bss->ssid_len,
+		                      ssid, ssid_len) != 0)
+			continue; /* ssid doesn't match */
+		if (bssid && compare_ether_addr(iter_bss->bssid, bssid) != 0)
+			continue; /* bssid doesn't match */
+		if ((channel > 0) && (iter_bss->channel != channel))
+			continue; /* channel doesn't match */
+
+		switch (mode) {
+		case IW_MODE_INFRA:
+		case IW_MODE_ADHOC:
+			if (!is_network_compatible(priv, iter_bss, mode))
+				break;
+
+			if (bssid) {
+				/* Found requested BSSID */
+				found_bss = iter_bss;
+				goto out;
+			}
+
+			if (SCAN_RSSI(iter_bss->rssi) > bestrssi) {
+				bestrssi = SCAN_RSSI(iter_bss->rssi);
+				found_bss = iter_bss;
+			}
+			break;
+		case IW_MODE_AUTO:
+		default:
+			if (SCAN_RSSI(iter_bss->rssi) > bestrssi) {
+				bestrssi = SCAN_RSSI(iter_bss->rssi);
+				found_bss = iter_bss;
+			}
+			break;
+		}
+	}
+
+out:
+	mutex_unlock(&priv->lock);
+	lbs_deb_leave_args(LBS_DEB_SCAN, "found_bss %p", found_bss);
+	return found_bss;
+}
+
+/**
+ *  @brief This function finds the best SSID in the Scan List
+ *
+ *  Search the scan table for the best SSID that also matches the current
+ *   adapter network preference (infrastructure or adhoc)
+ *
+ *  @param priv  A pointer to struct lbs_private
+ *
+ *  @return         index in BSSID list
+ */
+static struct bss_descriptor *lbs_find_best_ssid_in_list(
+	struct lbs_private *priv,
+	u8 mode)
+{
+	u8 bestrssi = 0;
+	struct bss_descriptor * iter_bss;
+	struct bss_descriptor * best_bss = NULL;
+
+	lbs_deb_enter(LBS_DEB_SCAN);
+
+	mutex_lock(&priv->lock);
+
+	list_for_each_entry (iter_bss, &priv->network_list, list) {
+		switch (mode) {
+		case IW_MODE_INFRA:
+		case IW_MODE_ADHOC:
+			if (!is_network_compatible(priv, iter_bss, mode))
+				break;
+			if (SCAN_RSSI(iter_bss->rssi) <= bestrssi)
+				break;
+			bestrssi = SCAN_RSSI(iter_bss->rssi);
+			best_bss = iter_bss;
+			break;
+		case IW_MODE_AUTO:
+		default:
+			if (SCAN_RSSI(iter_bss->rssi) <= bestrssi)
+				break;
+			bestrssi = SCAN_RSSI(iter_bss->rssi);
+			best_bss = iter_bss;
+			break;
+		}
+	}
+
+	mutex_unlock(&priv->lock);
+	lbs_deb_leave_args(LBS_DEB_SCAN, "best_bss %p", best_bss);
+	return best_bss;
+}
+
+/**
+ *  @brief Find the AP with specific ssid in the scan list
+ *
+ *  Used from association worker.
+ *
+ *  @param priv         A pointer to struct lbs_private structure
+ *  @param pSSID        A pointer to AP's ssid
+ *
+ *  @return             0--success, otherwise--fail
+ */
+int lbs_find_best_network_ssid(struct lbs_private *priv,
+		u8 *out_ssid, u8 *out_ssid_len, u8 preferred_mode, u8 *out_mode)
+{
+	int ret = -1;
+	struct bss_descriptor * found;
+
+	lbs_deb_enter(LBS_DEB_SCAN);
+
+	lbs_scan_networks(priv, NULL, 1);
+	if (priv->surpriseremoved)
+		goto out;
+
+	found = lbs_find_best_ssid_in_list(priv, preferred_mode);
+	if (found && (found->ssid_len > 0)) {
+		memcpy(out_ssid, &found->ssid, IW_ESSID_MAX_SIZE);
+		*out_ssid_len = found->ssid_len;
+		*out_mode = found->mode;
+		ret = 0;
+	}
+
+out:
+	lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret);
+	return ret;
+}
+
+
+/**
+ *  @brief Send a scan command for all available channels filtered on a spec
+ *
+ *  Used in association code and from debugfs
+ *
+ *  @param priv             A pointer to struct lbs_private structure
+ *  @param ssid             A pointer to the SSID to scan for
+ *  @param ssid_len         Length of the SSID
+ *  @param clear_ssid       Should existing scan results with this SSID
+ *                          be cleared?
+ *
+ *  @return                0-success, otherwise fail
+ */
+int lbs_send_specific_ssid_scan(struct lbs_private *priv,
+			u8 *ssid, u8 ssid_len, u8 clear_ssid)
+{
+	struct lbs_ioctl_user_scan_cfg scancfg;
+	int ret = 0;
+
+	lbs_deb_enter_args(LBS_DEB_SCAN, "SSID '%s', clear %d",
+		escape_essid(ssid, ssid_len), clear_ssid);
+
+	if (!ssid_len)
+		goto out;
+
+	memset(&scancfg, 0x00, sizeof(scancfg));
+	memcpy(scancfg.ssid, ssid, ssid_len);
+	scancfg.ssid_len = ssid_len;
+	scancfg.clear_ssid = clear_ssid;
+
+	lbs_scan_networks(priv, &scancfg, 1);
+	if (priv->surpriseremoved) {
+		ret = -1;
+		goto out;
+	}
+
+out:
+	lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret);
+	return ret;
+}
+
+
+
+
+/*********************************************************************/
+/*                                                                   */
+/*  Support for Wireless Extensions                                  */
+/*                                                                   */
+/*********************************************************************/
+
+
+#define MAX_CUSTOM_LEN 64
+
+static inline char *lbs_translate_scan(struct lbs_private *priv,
+					char *start, char *stop,
+					struct bss_descriptor *bss)
+{
+	struct chan_freq_power *cfp;
+	char *current_val;	/* For rates */
+	struct iw_event iwe;	/* Temporary buffer */
+	int j;
+#define PERFECT_RSSI ((u8)50)
+#define WORST_RSSI   ((u8)0)
+#define RSSI_DIFF    ((u8)(PERFECT_RSSI - WORST_RSSI))
+	u8 rssi;
+
+	lbs_deb_enter(LBS_DEB_SCAN);
+
+	cfp = lbs_find_cfp_by_band_and_channel(priv, 0, bss->channel);
+	if (!cfp) {
+		lbs_deb_scan("Invalid channel number %d\n", bss->channel);
+		start = NULL;
+		goto out;
+	}
+
+	/* First entry *MUST* be the BSSID */
+	iwe.cmd = SIOCGIWAP;
+	iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+	memcpy(iwe.u.ap_addr.sa_data, &bss->bssid, ETH_ALEN);
+	start = iwe_stream_add_event(start, stop, &iwe, IW_EV_ADDR_LEN);
+
+	/* SSID */
+	iwe.cmd = SIOCGIWESSID;
+	iwe.u.data.flags = 1;
+	iwe.u.data.length = min((u32) bss->ssid_len, (u32) IW_ESSID_MAX_SIZE);
+	start = iwe_stream_add_point(start, stop, &iwe, bss->ssid);
+
+	/* Mode */
+	iwe.cmd = SIOCGIWMODE;
+	iwe.u.mode = bss->mode;
+	start = iwe_stream_add_event(start, stop, &iwe, IW_EV_UINT_LEN);
+
+	/* Frequency */
+	iwe.cmd = SIOCGIWFREQ;
+	iwe.u.freq.m = (long)cfp->freq * 100000;
+	iwe.u.freq.e = 1;
+	start = iwe_stream_add_event(start, stop, &iwe, IW_EV_FREQ_LEN);
+
+	/* Add quality statistics */
+	iwe.cmd = IWEVQUAL;
+	iwe.u.qual.updated = IW_QUAL_ALL_UPDATED;
+	iwe.u.qual.level = SCAN_RSSI(bss->rssi);
+
+	rssi = iwe.u.qual.level - MRVDRV_NF_DEFAULT_SCAN_VALUE;
+	iwe.u.qual.qual =
+	    (100 * RSSI_DIFF * RSSI_DIFF - (PERFECT_RSSI - rssi) *
+	     (15 * (RSSI_DIFF) + 62 * (PERFECT_RSSI - rssi))) /
+	    (RSSI_DIFF * RSSI_DIFF);
+	if (iwe.u.qual.qual > 100)
+		iwe.u.qual.qual = 100;
+
+	if (priv->NF[TYPE_BEACON][TYPE_NOAVG] == 0) {
+		iwe.u.qual.noise = MRVDRV_NF_DEFAULT_SCAN_VALUE;
+	} else {
+		iwe.u.qual.noise =
+		    CAL_NF(priv->NF[TYPE_BEACON][TYPE_NOAVG]);
+	}
+
+	/* Locally created ad-hoc BSSs won't have beacons if this is the
+	 * only station in the adhoc network; so get signal strength
+	 * from receive statistics.
+	 */
+	if ((priv->mode == IW_MODE_ADHOC)
+	    && priv->adhoccreate
+	    && !lbs_ssid_cmp(priv->curbssparams.ssid,
+	                          priv->curbssparams.ssid_len,
+	                          bss->ssid, bss->ssid_len)) {
+		int snr, nf;
+		snr = priv->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE;
+		nf = priv->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE;
+		iwe.u.qual.level = CAL_RSSI(snr, nf);
+	}
+	start = iwe_stream_add_event(start, stop, &iwe, IW_EV_QUAL_LEN);
+
+	/* Add encryption capability */
+	iwe.cmd = SIOCGIWENCODE;
+	if (bss->capability & WLAN_CAPABILITY_PRIVACY) {
+		iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+	} else {
+		iwe.u.data.flags = IW_ENCODE_DISABLED;
+	}
+	iwe.u.data.length = 0;
+	start = iwe_stream_add_point(start, stop, &iwe, bss->ssid);
+
+	current_val = start + IW_EV_LCP_LEN;
+
+	iwe.cmd = SIOCGIWRATE;
+	iwe.u.bitrate.fixed = 0;
+	iwe.u.bitrate.disabled = 0;
+	iwe.u.bitrate.value = 0;
+
+	for (j = 0; bss->rates[j] && (j < sizeof(bss->rates)); j++) {
+		/* Bit rate given in 500 kb/s units */
+		iwe.u.bitrate.value = bss->rates[j] * 500000;
+		current_val = iwe_stream_add_value(start, current_val,
+					 stop, &iwe, IW_EV_PARAM_LEN);
+	}
+	if ((bss->mode == IW_MODE_ADHOC)
+	    && !lbs_ssid_cmp(priv->curbssparams.ssid,
+	                          priv->curbssparams.ssid_len,
+	                          bss->ssid, bss->ssid_len)
+	    && priv->adhoccreate) {
+		iwe.u.bitrate.value = 22 * 500000;
+		current_val = iwe_stream_add_value(start, current_val,
+					 stop, &iwe, IW_EV_PARAM_LEN);
+	}
+	/* Check if we added any event */
+	if((current_val - start) > IW_EV_LCP_LEN)
+		start = current_val;
+
+	memset(&iwe, 0, sizeof(iwe));
+	if (bss->wpa_ie_len) {
+		char buf[MAX_WPA_IE_LEN];
+		memcpy(buf, bss->wpa_ie, bss->wpa_ie_len);
+		iwe.cmd = IWEVGENIE;
+		iwe.u.data.length = bss->wpa_ie_len;
+		start = iwe_stream_add_point(start, stop, &iwe, buf);
+	}
+
+	memset(&iwe, 0, sizeof(iwe));
+	if (bss->rsn_ie_len) {
+		char buf[MAX_WPA_IE_LEN];
+		memcpy(buf, bss->rsn_ie, bss->rsn_ie_len);
+		iwe.cmd = IWEVGENIE;
+		iwe.u.data.length = bss->rsn_ie_len;
+		start = iwe_stream_add_point(start, stop, &iwe, buf);
+	}
+
+	if (bss->mesh) {
+		char custom[MAX_CUSTOM_LEN];
+		char *p = custom;
+
+		iwe.cmd = IWEVCUSTOM;
+		p += snprintf(p, MAX_CUSTOM_LEN - (p - custom),
+		              "mesh-type: olpc");
+		iwe.u.data.length = p - custom;
+		if (iwe.u.data.length)
+			start = iwe_stream_add_point(start, stop, &iwe, custom);
+	}
+
+out:
+	lbs_deb_leave_args(LBS_DEB_SCAN, "start %p", start);
+	return start;
+}
+
+
+/**
+ *  @brief Handle Scan Network ioctl
+ *
+ *  @param dev          A pointer to net_device structure
+ *  @param info         A pointer to iw_request_info structure
+ *  @param vwrq         A pointer to iw_param structure
+ *  @param extra        A pointer to extra data buf
+ *
+ *  @return             0 --success, otherwise fail
+ */
+int lbs_set_scan(struct net_device *dev, struct iw_request_info *info,
+		  struct iw_param *wrqu, char *extra)
+{
+	struct lbs_private *priv = dev->priv;
+
+	lbs_deb_enter(LBS_DEB_SCAN);
+
+	if (!netif_running(dev))
+		return -ENETDOWN;
+
+	/* mac80211 does this:
+	struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+	if (sdata->type != IEEE80211_IF_TYPE_xxx)
+		return -EOPNOTSUPP;
+
+	if (wrqu->data.length == sizeof(struct iw_scan_req) &&
+	    wrqu->data.flags & IW_SCAN_THIS_ESSID) {
+		req = (struct iw_scan_req *)extra;
+			ssid = req->essid;
+		ssid_len = req->essid_len;
+	}
+	*/
+
+	if (!delayed_work_pending(&priv->scan_work))
+		queue_delayed_work(priv->work_thread, &priv->scan_work,
+			msecs_to_jiffies(50));
+	/* set marker that currently a scan is taking place */
+	priv->last_scanned_channel = -1;
+
+	if (priv->surpriseremoved)
+		return -EIO;
+
+	lbs_deb_leave(LBS_DEB_SCAN);
+	return 0;
+}
+
+
+/**
+ *  @brief  Handle Retrieve scan table ioctl
+ *
+ *  @param dev          A pointer to net_device structure
+ *  @param info         A pointer to iw_request_info structure
+ *  @param dwrq         A pointer to iw_point structure
+ *  @param extra        A pointer to extra data buf
+ *
+ *  @return             0 --success, otherwise fail
+ */
+int lbs_get_scan(struct net_device *dev, struct iw_request_info *info,
+		  struct iw_point *dwrq, char *extra)
+{
+#define SCAN_ITEM_SIZE 128
+	struct lbs_private *priv = dev->priv;
+	int err = 0;
+	char *ev = extra;
+	char *stop = ev + dwrq->length;
+	struct bss_descriptor * iter_bss;
+	struct bss_descriptor * safe;
+
+	lbs_deb_enter(LBS_DEB_SCAN);
+
+	/* iwlist should wait until the current scan is finished */
+	if (priv->last_scanned_channel)
+		return -EAGAIN;
+
+	/* Update RSSI if current BSS is a locally created ad-hoc BSS */
+	if ((priv->mode == IW_MODE_ADHOC) && priv->adhoccreate) {
+		lbs_prepare_and_send_command(priv, CMD_802_11_RSSI, 0,
+					CMD_OPTION_WAITFORRSP, 0, NULL);
+	}
+
+	mutex_lock(&priv->lock);
+	list_for_each_entry_safe (iter_bss, safe, &priv->network_list, list) {
+		char * next_ev;
+		unsigned long stale_time;
+
+		if (stop - ev < SCAN_ITEM_SIZE) {
+			err = -E2BIG;
+			break;
+		}
+
+		/* For mesh device, list only mesh networks */
+		if (dev == priv->mesh_dev && !iter_bss->mesh)
+			continue;
+
+		/* Prune old an old scan result */
+		stale_time = iter_bss->last_scanned + DEFAULT_MAX_SCAN_AGE;
+		if (time_after(jiffies, stale_time)) {
+			list_move_tail (&iter_bss->list,
+			                &priv->network_free_list);
+			clear_bss_descriptor(iter_bss);
+			continue;
+		}
+
+		/* Translate to WE format this entry */
+		next_ev = lbs_translate_scan(priv, ev, stop, iter_bss);
+		if (next_ev == NULL)
+			continue;
+		ev = next_ev;
+	}
+	mutex_unlock(&priv->lock);
+
+	dwrq->length = (ev - extra);
+	dwrq->flags = 0;
+
+	lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", err);
+	return err;
+}
+
+
+
+
+/*********************************************************************/
+/*                                                                   */
+/*  Command execution                                                */
+/*                                                                   */
+/*********************************************************************/
+
+
+/**
+ *  @brief Prepare a scan command to be sent to the firmware
+ *
+ *  Called via lbs_prepare_and_send_command(priv, CMD_802_11_SCAN, ...)
+ *  from cmd.c
+ *
+ *  Sends a fixed lenght data part (specifying the BSS type and BSSID filters)
+ *  as well as a variable number/length of TLVs to the firmware.
+ *
+ *  @param priv       A pointer to struct lbs_private structure
+ *  @param cmd        A pointer to cmd_ds_command structure to be sent to
+ *                    firmware with the cmd_DS_801_11_SCAN structure
+ *  @param pdata_buf  Void pointer cast of a lbs_scan_cmd_config struct used
+ *                    to set the fields/TLVs for the command sent to firmware
+ *
+ *  @return           0 or -1
+ */
+int lbs_cmd_80211_scan(struct lbs_private *priv,
+	struct cmd_ds_command *cmd, void *pdata_buf)
+{
+	struct cmd_ds_802_11_scan *pscan = &cmd->params.scan;
+	struct lbs_scan_cmd_config *pscancfg = pdata_buf;
+
+	lbs_deb_enter(LBS_DEB_SCAN);
+
+	/* Set fixed field variables in scan command */
+	pscan->bsstype = pscancfg->bsstype;
+	memcpy(pscan->bssid, pscancfg->bssid, ETH_ALEN);
+	memcpy(pscan->tlvbuffer, pscancfg->tlvbuffer, pscancfg->tlvbufferlen);
+
+	/* size is equal to the sizeof(fixed portions) + the TLV len + header */
+	cmd->size = cpu_to_le16(sizeof(pscan->bsstype) + ETH_ALEN
+				+ pscancfg->tlvbufferlen + S_DS_GEN);
+
+	lbs_deb_leave(LBS_DEB_SCAN);
+	return 0;
+}
+
+/**
+ *  @brief This function handles the command response of scan
+ *
+ *  Called from handle_cmd_response() in cmdrespc.
+ *
+ *   The response buffer for the scan command has the following
+ *      memory layout:
+ *
+ *     .-----------------------------------------------------------.
+ *     |  header (4 * sizeof(u16)):  Standard command response hdr |
+ *     .-----------------------------------------------------------.
+ *     |  bufsize (u16) : sizeof the BSS Description data          |
+ *     .-----------------------------------------------------------.
+ *     |  NumOfSet (u8) : Number of BSS Descs returned             |
+ *     .-----------------------------------------------------------.
+ *     |  BSSDescription data (variable, size given in bufsize)    |
+ *     .-----------------------------------------------------------.
+ *     |  TLV data (variable, size calculated using header->size,  |
+ *     |            bufsize and sizeof the fixed fields above)     |
+ *     .-----------------------------------------------------------.
+ *
+ *  @param priv    A pointer to struct lbs_private structure
+ *  @param resp    A pointer to cmd_ds_command
+ *
+ *  @return        0 or -1
+ */
+int lbs_ret_80211_scan(struct lbs_private *priv, struct cmd_ds_command *resp)
+{
+	struct cmd_ds_802_11_scan_rsp *pscan;
+	struct bss_descriptor * iter_bss;
+	struct bss_descriptor * safe;
+	u8 *pbssinfo;
+	u16 scanrespsize;
+	int bytesleft;
+	int idx;
+	int tlvbufsize;
+	int ret;
+
+	lbs_deb_enter(LBS_DEB_SCAN);
+
+	/* Prune old entries from scan table */
+	list_for_each_entry_safe (iter_bss, safe, &priv->network_list, list) {
+		unsigned long stale_time = iter_bss->last_scanned + DEFAULT_MAX_SCAN_AGE;
+		if (time_before(jiffies, stale_time))
+			continue;
+		list_move_tail (&iter_bss->list, &priv->network_free_list);
+		clear_bss_descriptor(iter_bss);
+	}
+
+	pscan = &resp->params.scanresp;
+
+	if (pscan->nr_sets > MAX_NETWORK_COUNT) {
+		lbs_deb_scan(
+		       "SCAN_RESP: too many scan results (%d, max %d)!!\n",
+		       pscan->nr_sets, MAX_NETWORK_COUNT);
+		ret = -1;
+		goto done;
+	}
+
+	bytesleft = le16_to_cpu(pscan->bssdescriptsize);
+	lbs_deb_scan("SCAN_RESP: bssdescriptsize %d\n", bytesleft);
+
+	scanrespsize = le16_to_cpu(resp->size);
+	lbs_deb_scan("SCAN_RESP: scan results %d\n", pscan->nr_sets);
+
+	pbssinfo = pscan->bssdesc_and_tlvbuffer;
+
+	/* The size of the TLV buffer is equal to the entire command response
+	 *   size (scanrespsize) minus the fixed fields (sizeof()'s), the
+	 *   BSS Descriptions (bssdescriptsize as bytesLef) and the command
+	 *   response header (S_DS_GEN)
+	 */
+	tlvbufsize = scanrespsize - (bytesleft + sizeof(pscan->bssdescriptsize)
+				     + sizeof(pscan->nr_sets)
+				     + S_DS_GEN);
+
+	/*
+	 *  Process each scan response returned (pscan->nr_sets).  Save
+	 *    the information in the newbssentry and then insert into the
+	 *    driver scan table either as an update to an existing entry
+	 *    or as an addition at the end of the table
+	 */
+	for (idx = 0; idx < pscan->nr_sets && bytesleft; idx++) {
+		struct bss_descriptor new;
+		struct bss_descriptor * found = NULL;
+		struct bss_descriptor * oldest = NULL;
+		DECLARE_MAC_BUF(mac);
+
+		/* Process the data fields and IEs returned for this BSS */
+		memset(&new, 0, sizeof (struct bss_descriptor));
+		if (lbs_process_bss(&new, &pbssinfo, &bytesleft) != 0) {
+			/* error parsing the scan response, skipped */
+			lbs_deb_scan("SCAN_RESP: process_bss returned ERROR\n");
+			continue;
+		}
+
+		/* Try to find this bss in the scan table */
+		list_for_each_entry (iter_bss, &priv->network_list, list) {
+			if (is_same_network(iter_bss, &new)) {
+				found = iter_bss;
+				break;
+			}
+
+			if ((oldest == NULL) ||
+			    (iter_bss->last_scanned < oldest->last_scanned))
+				oldest = iter_bss;
+		}
+
+		if (found) {
+			/* found, clear it */
+			clear_bss_descriptor(found);
+		} else if (!list_empty(&priv->network_free_list)) {
+			/* Pull one from the free list */
+			found = list_entry(priv->network_free_list.next,
+					   struct bss_descriptor, list);
+			list_move_tail(&found->list, &priv->network_list);
+		} else if (oldest) {
+			/* If there are no more slots, expire the oldest */
+			found = oldest;
+			clear_bss_descriptor(found);
+			list_move_tail(&found->list, &priv->network_list);
+		} else {
+			continue;
+		}
+
+		lbs_deb_scan("SCAN_RESP: BSSID %s\n",
+			     print_mac(mac, new.bssid));
+
+		/* Copy the locally created newbssentry to the scan table */
+		memcpy(found, &new, offsetof(struct bss_descriptor, list));
+	}
+
+	ret = 0;
+
+done:
+	lbs_deb_leave_args(LBS_DEB_SCAN, "ret %d", ret);
+	return ret;
+}

+ 205 - 0
package/libertas/src/scan.h

@@ -0,0 +1,205 @@
+/**
+  * Interface for the wlan network scan routines
+  *
+  * Driver interface functions and type declarations for the scan module
+  * implemented in scan.c.
+  */
+#ifndef _LBS_SCAN_H
+#define _LBS_SCAN_H
+
+#include <net/ieee80211.h>
+#include "hostcmd.h"
+
+/**
+ *  @brief Maximum number of channels that can be sent in a setuserscan ioctl
+ *
+ *  @sa lbs_ioctl_user_scan_cfg
+ */
+#define LBS_IOCTL_USER_SCAN_CHAN_MAX  50
+
+//! Infrastructure BSS scan type in lbs_scan_cmd_config
+#define LBS_SCAN_BSS_TYPE_BSS         1
+
+//! Adhoc BSS scan type in lbs_scan_cmd_config
+#define LBS_SCAN_BSS_TYPE_IBSS        2
+
+//! Adhoc or Infrastructure BSS scan type in lbs_scan_cmd_config, no filter
+#define LBS_SCAN_BSS_TYPE_ANY         3
+
+/**
+ * @brief Structure used internally in the wlan driver to configure a scan.
+ *
+ * Sent to the command processing module to configure the firmware
+ *   scan command prepared by lbs_cmd_80211_scan.
+ *
+ * @sa lbs_scan_networks
+ *
+ */
+struct lbs_scan_cmd_config {
+    /**
+     *  @brief BSS type to be sent in the firmware command
+     *
+     *  Field can be used to restrict the types of networks returned in the
+     *    scan.  valid settings are:
+     *
+     *   - LBS_SCAN_BSS_TYPE_BSS  (infrastructure)
+     *   - LBS_SCAN_BSS_TYPE_IBSS (adhoc)
+     *   - LBS_SCAN_BSS_TYPE_ANY  (unrestricted, adhoc and infrastructure)
+     */
+	u8 bsstype;
+
+    /**
+     *  @brief Specific BSSID used to filter scan results in the firmware
+     */
+	u8 bssid[ETH_ALEN];
+
+    /**
+     *  @brief length of TLVs sent in command starting at tlvBuffer
+     */
+	int tlvbufferlen;
+
+    /**
+     *  @brief SSID TLV(s) and ChanList TLVs to be sent in the firmware command
+     *
+     *  @sa TLV_TYPE_CHANLIST, mrvlietypes_chanlistparamset_t
+     *  @sa TLV_TYPE_SSID, mrvlietypes_ssidparamset_t
+     */
+	u8 tlvbuffer[1];	//!< SSID TLV(s) and ChanList TLVs are stored here
+};
+
+/**
+ *  @brief IOCTL channel sub-structure sent in lbs_ioctl_user_scan_cfg
+ *
+ *  Multiple instances of this structure are included in the IOCTL command
+ *   to configure a instance of a scan on the specific channel.
+ */
+struct lbs_ioctl_user_scan_chan {
+	u8 channumber;		//!< channel Number to scan
+	u8 radiotype;		//!< Radio type: 'B/G' band = 0, 'A' band = 1
+	u8 scantype;		//!< Scan type: Active = 0, Passive = 1
+	u16 scantime;		//!< Scan duration in milliseconds; if 0 default used
+};
+
+/**
+ *  @brief IOCTL input structure to configure an immediate scan cmd to firmware
+ *
+ *  Used in the setuserscan (LBS_SET_USER_SCAN) private ioctl.  Specifies
+ *   a number of parameters to be used in general for the scan as well
+ *   as a channel list (lbs_ioctl_user_scan_chan) for each scan period
+ *   desired.
+ *
+ *  @sa lbs_set_user_scan_ioctl
+ */
+struct lbs_ioctl_user_scan_cfg {
+    /**
+     *  @brief BSS type to be sent in the firmware command
+     *
+     *  Field can be used to restrict the types of networks returned in the
+     *    scan.  valid settings are:
+     *
+     *   - LBS_SCAN_BSS_TYPE_BSS  (infrastructure)
+     *   - LBS_SCAN_BSS_TYPE_IBSS (adhoc)
+     *   - LBS_SCAN_BSS_TYPE_ANY  (unrestricted, adhoc and infrastructure)
+     */
+	u8 bsstype;
+
+	/**
+	 *  @brief BSSID filter sent in the firmware command to limit the results
+	 */
+	u8 bssid[ETH_ALEN];
+
+	/* Clear existing scan results matching this BSSID */
+	u8 clear_bssid;
+
+	/**
+	 *  @brief SSID filter sent in the firmware command to limit the results
+	 */
+	char ssid[IW_ESSID_MAX_SIZE];
+	u8 ssid_len;
+
+	/* Clear existing scan results matching this SSID */
+	u8 clear_ssid;
+};
+
+/**
+ *  @brief Structure used to store information for each beacon/probe response
+ */
+struct bss_descriptor {
+	u8 bssid[ETH_ALEN];
+
+	u8 ssid[IW_ESSID_MAX_SIZE + 1];
+	u8 ssid_len;
+
+	u16 capability;
+
+	/* receive signal strength in dBm */
+	long rssi;
+
+	u32 channel;
+
+	u16 beaconperiod;
+
+	u32 atimwindow;
+
+	/* IW_MODE_AUTO, IW_MODE_ADHOC, IW_MODE_INFRA */
+	u8 mode;
+
+	/* zero-terminated array of supported data rates */
+	u8 rates[MAX_RATES + 1];
+
+	unsigned long last_scanned;
+
+	union ieeetypes_phyparamset phyparamset;
+	union IEEEtypes_ssparamset ssparamset;
+
+	struct ieeetypes_countryinfofullset countryinfo;
+
+	u8 wpa_ie[MAX_WPA_IE_LEN];
+	size_t wpa_ie_len;
+	u8 rsn_ie[MAX_WPA_IE_LEN];
+	size_t rsn_ie_len;
+
+	u8 mesh;
+
+	struct list_head list;
+};
+
+int lbs_ssid_cmp(u8 *ssid1, u8 ssid1_len, u8 *ssid2, u8 ssid2_len);
+
+struct bss_descriptor *lbs_find_ssid_in_list(struct lbs_private *priv,
+		u8 *ssid, u8 ssid_len, u8 *bssid, u8 mode,
+		int channel);
+
+struct bss_descriptor *lbs_find_bssid_in_list(struct lbs_private *priv,
+	u8 *bssid, u8 mode);
+
+int lbs_find_best_network_ssid(struct lbs_private *priv, u8 *out_ssid,
+			u8 *out_ssid_len, u8 preferred_mode, u8 *out_mode);
+
+int lbs_send_specific_ssid_scan(struct lbs_private *priv, u8 *ssid,
+				u8 ssid_len, u8 clear_ssid);
+
+int lbs_cmd_80211_scan(struct lbs_private *priv,
+				struct cmd_ds_command *cmd,
+				void *pdata_buf);
+
+int lbs_ret_80211_scan(struct lbs_private *priv,
+				struct cmd_ds_command *resp);
+
+int lbs_scan_networks(struct lbs_private *priv,
+	const struct lbs_ioctl_user_scan_cfg *puserscanin,
+                int full_scan);
+
+struct ifreq;
+
+struct iw_point;
+struct iw_param;
+struct iw_request_info;
+int lbs_get_scan(struct net_device *dev, struct iw_request_info *info,
+			 struct iw_point *dwrq, char *extra);
+int lbs_set_scan(struct net_device *dev, struct iw_request_info *info,
+			 struct iw_param *vwrq, char *extra);
+
+void lbs_scan_worker(struct work_struct *work);
+
+#endif

+ 221 - 0
package/libertas/src/tx.c

@@ -0,0 +1,221 @@
+/**
+  * This file contains the handling of TX in wlan driver.
+  */
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include "hostcmd.h"
+#include "radiotap.h"
+#include "decl.h"
+#include "defs.h"
+#include "dev.h"
+#include "wext.h"
+
+/**
+ *  @brief This function converts Tx/Rx rates from IEEE80211_RADIOTAP_RATE
+ *  units (500 Kb/s) into Marvell WLAN format (see Table 8 in Section 3.2.1)
+ *
+ *  @param rate    Input rate
+ *  @return      Output Rate (0 if invalid)
+ */
+static u32 convert_radiotap_rate_to_mv(u8 rate)
+{
+	switch (rate) {
+	case 2:		/*   1 Mbps */
+		return 0 | (1 << 4);
+	case 4:		/*   2 Mbps */
+		return 1 | (1 << 4);
+	case 11:		/* 5.5 Mbps */
+		return 2 | (1 << 4);
+	case 22:		/*  11 Mbps */
+		return 3 | (1 << 4);
+	case 12:		/*   6 Mbps */
+		return 4 | (1 << 4);
+	case 18:		/*   9 Mbps */
+		return 5 | (1 << 4);
+	case 24:		/*  12 Mbps */
+		return 6 | (1 << 4);
+	case 36:		/*  18 Mbps */
+		return 7 | (1 << 4);
+	case 48:		/*  24 Mbps */
+		return 8 | (1 << 4);
+	case 72:		/*  36 Mbps */
+		return 9 | (1 << 4);
+	case 96:		/*  48 Mbps */
+		return 10 | (1 << 4);
+	case 108:		/*  54 Mbps */
+		return 11 | (1 << 4);
+	}
+	return 0;
+}
+
+/**
+ *  @brief This function checks the conditions and sends packet to IF
+ *  layer if everything is ok.
+ *
+ *  @param priv    A pointer to struct lbs_private structure
+ *  @param skb     A pointer to skb which includes TX packet
+ *  @return 	   0 or -1
+ */
+int lbs_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	unsigned long flags;
+	struct lbs_private *priv = dev->priv;
+	struct txpd *txpd;
+	char *p802x_hdr;
+	uint16_t pkt_len;
+	int ret;
+
+	lbs_deb_enter(LBS_DEB_TX);
+
+	ret = NETDEV_TX_OK;
+
+	/* We need to protect against the queues being restarted before
+	   we get round to stopping them */
+	spin_lock_irqsave(&priv->driver_lock, flags);
+
+	if (priv->surpriseremoved)
+		goto free;
+
+	if (!skb->len || (skb->len > MRVDRV_ETH_TX_PACKET_BUFFER_SIZE)) {
+		lbs_deb_tx("tx err: skb length %d 0 or > %zd\n",
+		       skb->len, MRVDRV_ETH_TX_PACKET_BUFFER_SIZE);
+		/* We'll never manage to send this one; drop it and return 'OK' */
+
+		priv->stats.tx_dropped++;
+		priv->stats.tx_errors++;
+		goto free;
+	}
+
+
+	netif_stop_queue(priv->dev);
+	if (priv->mesh_dev)
+		netif_stop_queue(priv->mesh_dev);
+
+	if (priv->tx_pending_len) {
+		/* This can happen if packets come in on the mesh and eth 
+		   device simultaneously -- there's no mutual exclusion on 
+		   hard_start_xmit() calls between devices. */
+		lbs_deb_tx("Packet on %s while busy\n", dev->name);
+		ret = NETDEV_TX_BUSY;
+		goto unlock;
+	}
+
+	priv->tx_pending_len = -1;
+	spin_unlock_irqrestore(&priv->driver_lock, flags);
+
+	lbs_deb_hex(LBS_DEB_TX, "TX Data", skb->data, min_t(unsigned int, skb->len, 100));
+
+	txpd = (void *)priv->tx_pending_buf;
+	memset(txpd, 0, sizeof(struct txpd));
+
+	p802x_hdr = skb->data;
+	pkt_len = skb->len;
+
+	if (dev == priv->rtap_net_dev) {
+		struct tx_radiotap_hdr *rtap_hdr = (void *)skb->data;
+
+		/* set txpd fields from the radiotap header */
+		txpd->tx_control = cpu_to_le32(convert_radiotap_rate_to_mv(rtap_hdr->rate));
+
+		/* skip the radiotap header */
+		p802x_hdr += sizeof(*rtap_hdr);
+		pkt_len -= sizeof(*rtap_hdr);
+
+		/* copy destination address from 802.11 header */
+		memcpy(txpd->tx_dest_addr_high, p802x_hdr + 4, ETH_ALEN);
+	} else {
+		/* copy destination address from 802.3 header */
+		memcpy(txpd->tx_dest_addr_high, p802x_hdr, ETH_ALEN);
+	}
+
+	txpd->tx_packet_length = cpu_to_le16(pkt_len);
+	txpd->tx_packet_location = cpu_to_le32(sizeof(struct txpd));
+
+	if (dev == priv->mesh_dev)
+		txpd->tx_control |= cpu_to_le32(TxPD_MESH_FRAME);
+
+	lbs_deb_hex(LBS_DEB_TX, "txpd", (u8 *) &txpd, sizeof(struct txpd));
+
+	lbs_deb_hex(LBS_DEB_TX, "Tx Data", (u8 *) p802x_hdr, le16_to_cpu(txpd->tx_packet_length));
+
+	memcpy(&txpd[1], p802x_hdr, le16_to_cpu(txpd->tx_packet_length));
+
+	spin_lock_irqsave(&priv->driver_lock, flags);
+	priv->tx_pending_len = pkt_len + sizeof(struct txpd);
+
+	lbs_deb_tx("%s lined up packet\n", __func__);
+
+	priv->stats.tx_packets++;
+	priv->stats.tx_bytes += skb->len;
+
+	dev->trans_start = jiffies;
+
+	if (priv->monitormode != LBS_MONITOR_OFF) {
+		/* Keep the skb to echo it back once Tx feedback is
+		   received from FW */
+		skb_orphan(skb);
+
+		/* Keep the skb around for when we get feedback */
+		priv->currenttxskb = skb;
+	} else {
+ free:
+		dev_kfree_skb_any(skb);
+	}
+ unlock:
+	spin_unlock_irqrestore(&priv->driver_lock, flags);
+	wake_up(&priv->waitq);
+
+	lbs_deb_leave_args(LBS_DEB_TX, "ret %d", ret);
+	return ret;
+}
+
+/**
+ *  @brief This function sends to the host the last transmitted packet,
+ *  filling the radiotap headers with transmission information.
+ *
+ *  @param priv     A pointer to struct lbs_private structure
+ *  @param status   A 32 bit value containing transmission status.
+ *
+ *  @returns void
+ */
+void lbs_send_tx_feedback(struct lbs_private *priv)
+{
+	struct tx_radiotap_hdr *radiotap_hdr;
+	u32 status = priv->eventcause;
+	int txfail;
+	int try_count;
+
+	if (priv->monitormode == LBS_MONITOR_OFF ||
+	    priv->currenttxskb == NULL)
+		return;
+
+	radiotap_hdr = (struct tx_radiotap_hdr *)priv->currenttxskb->data;
+
+	txfail = (status >> 24);
+
+#if 0
+	/* The version of roofnet that we've tested does not use this yet
+	 * But it may be used in the future.
+	 */
+	if (txfail)
+		radiotap_hdr->flags &= IEEE80211_RADIOTAP_F_TX_FAIL;
+#endif
+	try_count = (status >> 16) & 0xff;
+	radiotap_hdr->data_retries = (try_count) ?
+	    (1 + priv->txretrycount - try_count) : 0;
+
+
+	priv->currenttxskb->protocol = eth_type_trans(priv->currenttxskb,
+						      priv->rtap_net_dev);
+	netif_rx(priv->currenttxskb);
+
+	priv->currenttxskb = NULL;
+
+	if (priv->connect_status == LBS_CONNECTED)
+		netif_wake_queue(priv->dev);
+
+	if (priv->mesh_dev && (priv->mesh_connect_status == LBS_CONNECTED))
+		netif_wake_queue(priv->mesh_dev);
+}
+EXPORT_SYMBOL_GPL(lbs_send_tx_feedback);

+ 242 - 0
package/libertas/src/types.h

@@ -0,0 +1,242 @@
+/**
+  * This header file contains definition for global types
+  */
+#ifndef _LBS_TYPES_H_
+#define _LBS_TYPES_H_
+
+#include <linux/if_ether.h>
+#include <asm/byteorder.h>
+
+struct ieeetypes_cfparamset {
+	u8 elementid;
+	u8 len;
+	u8 cfpcnt;
+	u8 cfpperiod;
+	__le16 cfpmaxduration;
+	__le16 cfpdurationremaining;
+} __attribute__ ((packed));
+
+
+struct ieeetypes_ibssparamset {
+	u8 elementid;
+	u8 len;
+	__le16 atimwindow;
+} __attribute__ ((packed));
+
+union IEEEtypes_ssparamset {
+	struct ieeetypes_cfparamset cfparamset;
+	struct ieeetypes_ibssparamset ibssparamset;
+} __attribute__ ((packed));
+
+struct ieeetypes_fhparamset {
+	u8 elementid;
+	u8 len;
+	__le16 dwelltime;
+	u8 hopset;
+	u8 hoppattern;
+	u8 hopindex;
+} __attribute__ ((packed));
+
+struct ieeetypes_dsparamset {
+	u8 elementid;
+	u8 len;
+	u8 currentchan;
+} __attribute__ ((packed));
+
+union ieeetypes_phyparamset {
+	struct ieeetypes_fhparamset fhparamset;
+	struct ieeetypes_dsparamset dsparamset;
+} __attribute__ ((packed));
+
+struct ieeetypes_assocrsp {
+	__le16 capability;
+	__le16 statuscode;
+	__le16 aid;
+	u8 iebuffer[1];
+} __attribute__ ((packed));
+
+/** TLV  type ID definition */
+#define PROPRIETARY_TLV_BASE_ID		0x0100
+
+/* Terminating TLV type */
+#define MRVL_TERMINATE_TLV_ID		0xffff
+
+#define TLV_TYPE_SSID				0x0000
+#define TLV_TYPE_RATES				0x0001
+#define TLV_TYPE_PHY_FH				0x0002
+#define TLV_TYPE_PHY_DS				0x0003
+#define TLV_TYPE_CF				    0x0004
+#define TLV_TYPE_IBSS				0x0006
+
+#define TLV_TYPE_DOMAIN				0x0007
+
+#define TLV_TYPE_POWER_CAPABILITY	0x0021
+
+#define TLV_TYPE_KEY_MATERIAL       (PROPRIETARY_TLV_BASE_ID + 0)
+#define TLV_TYPE_CHANLIST           (PROPRIETARY_TLV_BASE_ID + 1)
+#define TLV_TYPE_NUMPROBES          (PROPRIETARY_TLV_BASE_ID + 2)
+#define TLV_TYPE_RSSI_LOW           (PROPRIETARY_TLV_BASE_ID + 4)
+#define TLV_TYPE_SNR_LOW            (PROPRIETARY_TLV_BASE_ID + 5)
+#define TLV_TYPE_FAILCOUNT          (PROPRIETARY_TLV_BASE_ID + 6)
+#define TLV_TYPE_BCNMISS            (PROPRIETARY_TLV_BASE_ID + 7)
+#define TLV_TYPE_LED_GPIO           (PROPRIETARY_TLV_BASE_ID + 8)
+#define TLV_TYPE_LEDBEHAVIOR        (PROPRIETARY_TLV_BASE_ID + 9)
+#define TLV_TYPE_PASSTHROUGH        (PROPRIETARY_TLV_BASE_ID + 10)
+#define TLV_TYPE_REASSOCAP          (PROPRIETARY_TLV_BASE_ID + 11)
+#define TLV_TYPE_POWER_TBL_2_4GHZ   (PROPRIETARY_TLV_BASE_ID + 12)
+#define TLV_TYPE_POWER_TBL_5GHZ     (PROPRIETARY_TLV_BASE_ID + 13)
+#define TLV_TYPE_BCASTPROBE	    (PROPRIETARY_TLV_BASE_ID + 14)
+#define TLV_TYPE_NUMSSID_PROBE	    (PROPRIETARY_TLV_BASE_ID + 15)
+#define TLV_TYPE_WMMQSTATUS   	    (PROPRIETARY_TLV_BASE_ID + 16)
+#define TLV_TYPE_CRYPTO_DATA	    (PROPRIETARY_TLV_BASE_ID + 17)
+#define TLV_TYPE_WILDCARDSSID	    (PROPRIETARY_TLV_BASE_ID + 18)
+#define TLV_TYPE_TSFTIMESTAMP	    (PROPRIETARY_TLV_BASE_ID + 19)
+#define TLV_TYPE_RSSI_HIGH          (PROPRIETARY_TLV_BASE_ID + 22)
+#define TLV_TYPE_SNR_HIGH           (PROPRIETARY_TLV_BASE_ID + 23)
+
+/** TLV related data structures*/
+struct mrvlietypesheader {
+	__le16 type;
+	__le16 len;
+} __attribute__ ((packed));
+
+struct mrvlietypes_data {
+	struct mrvlietypesheader header;
+	u8 Data[1];
+} __attribute__ ((packed));
+
+struct mrvlietypes_ratesparamset {
+	struct mrvlietypesheader header;
+	u8 rates[1];
+} __attribute__ ((packed));
+
+struct mrvlietypes_ssidparamset {
+	struct mrvlietypesheader header;
+	u8 ssid[1];
+} __attribute__ ((packed));
+
+struct mrvlietypes_wildcardssidparamset {
+	struct mrvlietypesheader header;
+	u8 MaxSsidlength;
+	u8 ssid[1];
+} __attribute__ ((packed));
+
+struct chanscanmode {
+#ifdef __BIG_ENDIAN_BITFIELD
+	u8 reserved_2_7:6;
+	u8 disablechanfilt:1;
+	u8 passivescan:1;
+#else
+	u8 passivescan:1;
+	u8 disablechanfilt:1;
+	u8 reserved_2_7:6;
+#endif
+} __attribute__ ((packed));
+
+struct chanscanparamset {
+	u8 radiotype;
+	u8 channumber;
+	struct chanscanmode chanscanmode;
+	__le16 minscantime;
+	__le16 maxscantime;
+} __attribute__ ((packed));
+
+struct mrvlietypes_chanlistparamset {
+	struct mrvlietypesheader header;
+	struct chanscanparamset chanscanparam[1];
+} __attribute__ ((packed));
+
+struct cfparamset {
+	u8 cfpcnt;
+	u8 cfpperiod;
+	__le16 cfpmaxduration;
+	__le16 cfpdurationremaining;
+} __attribute__ ((packed));
+
+struct ibssparamset {
+	__le16 atimwindow;
+} __attribute__ ((packed));
+
+struct mrvlietypes_ssparamset {
+	struct mrvlietypesheader header;
+	union {
+		struct cfparamset cfparamset[1];
+		struct ibssparamset ibssparamset[1];
+	} cf_ibss;
+} __attribute__ ((packed));
+
+struct fhparamset {
+	__le16 dwelltime;
+	u8 hopset;
+	u8 hoppattern;
+	u8 hopindex;
+} __attribute__ ((packed));
+
+struct dsparamset {
+	u8 currentchan;
+} __attribute__ ((packed));
+
+struct mrvlietypes_phyparamset {
+	struct mrvlietypesheader header;
+	union {
+		struct fhparamset fhparamset[1];
+		struct dsparamset dsparamset[1];
+	} fh_ds;
+} __attribute__ ((packed));
+
+struct mrvlietypes_rsnparamset {
+	struct mrvlietypesheader header;
+	u8 rsnie[1];
+} __attribute__ ((packed));
+
+struct mrvlietypes_tsftimestamp {
+	struct mrvlietypesheader header;
+	__le64 tsftable[1];
+} __attribute__ ((packed));
+
+/**  Local Power capability */
+struct mrvlietypes_powercapability {
+	struct mrvlietypesheader header;
+	s8 minpower;
+	s8 maxpower;
+} __attribute__ ((packed));
+
+/* used in CMD_802_11_SUBSCRIBE_EVENT for SNR, RSSI and Failure */
+struct mrvlietypes_thresholds {
+	struct mrvlietypesheader header;
+	u8 value;
+	u8 freq;
+} __attribute__ ((packed));
+
+struct mrvlietypes_beaconsmissed {
+	struct mrvlietypesheader header;
+	u8 beaconmissed;
+	u8 reserved;
+} __attribute__ ((packed));
+
+struct mrvlietypes_numprobes {
+	struct mrvlietypesheader header;
+	__le16 numprobes;
+} __attribute__ ((packed));
+
+struct mrvlietypes_bcastprobe {
+	struct mrvlietypesheader header;
+	__le16 bcastprobe;
+} __attribute__ ((packed));
+
+struct mrvlietypes_numssidprobe {
+	struct mrvlietypesheader header;
+	__le16 numssidprobe;
+} __attribute__ ((packed));
+
+struct led_pin {
+	u8 led;
+	u8 pin;
+} __attribute__ ((packed));
+
+struct mrvlietypes_ledgpio {
+	struct mrvlietypesheader header;
+	struct led_pin ledpin[1];
+} __attribute__ ((packed));
+
+#endif

+ 2213 - 0
package/libertas/src/wext.c

@@ -0,0 +1,2213 @@
+/**
+  * This file contains ioctl functions
+  */
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/if.h>
+#include <linux/if_arp.h>
+#include <linux/wireless.h>
+#include <linux/bitops.h>
+
+#include <net/ieee80211.h>
+#include <net/iw_handler.h>
+
+#include "host.h"
+#include "radiotap.h"
+#include "decl.h"
+#include "defs.h"
+#include "dev.h"
+#include "join.h"
+#include "wext.h"
+#include "assoc.h"
+#include "cmd.h"
+
+
+static inline void lbs_postpone_association_work(struct lbs_private *priv)
+{
+	if (priv->surpriseremoved)
+		return;
+	cancel_delayed_work(&priv->assoc_work);
+	queue_delayed_work(priv->work_thread, &priv->assoc_work, HZ / 2);
+}
+
+static inline void lbs_cancel_association_work(struct lbs_private *priv)
+{
+	cancel_delayed_work(&priv->assoc_work);
+	kfree(priv->pending_assoc_req);
+	priv->pending_assoc_req = NULL;
+}
+
+
+/**
+ *  @brief Find the channel frequency power info with specific channel
+ *
+ *  @param priv 	A pointer to struct lbs_private structure
+ *  @param band		it can be BAND_A, BAND_G or BAND_B
+ *  @param channel      the channel for looking
+ *  @return 	   	A pointer to struct chan_freq_power structure or NULL if not find.
+ */
+struct chan_freq_power *lbs_find_cfp_by_band_and_channel(
+	struct lbs_private *priv,
+	u8 band,
+	u16 channel)
+{
+	struct chan_freq_power *cfp = NULL;
+	struct region_channel *rc;
+	int i, j;
+
+	for (j = 0; !cfp && (j < ARRAY_SIZE(priv->region_channel)); j++) {
+		rc = &priv->region_channel[j];
+
+		if (priv->enable11d)
+			rc = &priv->universal_channel[j];
+		if (!rc->valid || !rc->CFP)
+			continue;
+		if (rc->band != band)
+			continue;
+		for (i = 0; i < rc->nrcfp; i++) {
+			if (rc->CFP[i].channel == channel) {
+				cfp = &rc->CFP[i];
+				break;
+			}
+		}
+	}
+
+	if (!cfp && channel)
+		lbs_deb_wext("lbs_find_cfp_by_band_and_channel: can't find "
+		       "cfp by band %d / channel %d\n", band, channel);
+
+	return cfp;
+}
+
+/**
+ *  @brief Find the channel frequency power info with specific frequency
+ *
+ *  @param priv 	A pointer to struct lbs_private structure
+ *  @param band		it can be BAND_A, BAND_G or BAND_B
+ *  @param freq	        the frequency for looking
+ *  @return 	   	A pointer to struct chan_freq_power structure or NULL if not find.
+ */
+static struct chan_freq_power *find_cfp_by_band_and_freq(
+	struct lbs_private *priv,
+	u8 band,
+	u32 freq)
+{
+	struct chan_freq_power *cfp = NULL;
+	struct region_channel *rc;
+	int i, j;
+
+	for (j = 0; !cfp && (j < ARRAY_SIZE(priv->region_channel)); j++) {
+		rc = &priv->region_channel[j];
+
+		if (priv->enable11d)
+			rc = &priv->universal_channel[j];
+		if (!rc->valid || !rc->CFP)
+			continue;
+		if (rc->band != band)
+			continue;
+		for (i = 0; i < rc->nrcfp; i++) {
+			if (rc->CFP[i].freq == freq) {
+				cfp = &rc->CFP[i];
+				break;
+			}
+		}
+	}
+
+	if (!cfp && freq)
+		lbs_deb_wext("find_cfp_by_band_and_freql: can't find cfp by "
+		       "band %d / freq %d\n", band, freq);
+
+	return cfp;
+}
+
+
+/**
+ *  @brief Set Radio On/OFF
+ *
+ *  @param priv                 A pointer to struct lbs_private structure
+ *  @option 			Radio Option
+ *  @return 	   		0 --success, otherwise fail
+ */
+static int lbs_radio_ioctl(struct lbs_private *priv, u8 option)
+{
+	int ret = 0;
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	if (priv->radioon != option) {
+		lbs_deb_wext("switching radio %s\n", option ? "on" : "off");
+		priv->radioon = option;
+
+		ret = lbs_prepare_and_send_command(priv,
+					    CMD_802_11_RADIO_CONTROL,
+					    CMD_ACT_SET,
+					    CMD_OPTION_WAITFORRSP, 0, NULL);
+	}
+
+	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+	return ret;
+}
+
+/**
+ *  @brief Copy active data rates based on adapter mode and status
+ *
+ *  @param priv              A pointer to struct lbs_private structure
+ *  @param rate		        The buf to return the active rates
+ */
+static void copy_active_data_rates(struct lbs_private *priv, u8 *rates)
+{
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	if ((priv->connect_status != LBS_CONNECTED) &&
+		(priv->mesh_connect_status != LBS_CONNECTED))
+		memcpy(rates, lbs_bg_rates, MAX_RATES);
+	else
+		memcpy(rates, priv->curbssparams.rates, MAX_RATES);
+
+	lbs_deb_leave(LBS_DEB_WEXT);
+}
+
+static int lbs_get_name(struct net_device *dev, struct iw_request_info *info,
+			 char *cwrq, char *extra)
+{
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	/* We could add support for 802.11n here as needed. Jean II */
+	snprintf(cwrq, IFNAMSIZ, "IEEE 802.11b/g");
+
+	lbs_deb_leave(LBS_DEB_WEXT);
+	return 0;
+}
+
+static int lbs_get_freq(struct net_device *dev, struct iw_request_info *info,
+			 struct iw_freq *fwrq, char *extra)
+{
+	struct lbs_private *priv = dev->priv;
+	struct chan_freq_power *cfp;
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	cfp = lbs_find_cfp_by_band_and_channel(priv, 0,
+					   priv->curbssparams.channel);
+
+	if (!cfp) {
+		if (priv->curbssparams.channel)
+			lbs_deb_wext("invalid channel %d\n",
+			       priv->curbssparams.channel);
+		return -EINVAL;
+	}
+
+	fwrq->m = (long)cfp->freq * 100000;
+	fwrq->e = 1;
+
+	lbs_deb_wext("freq %u\n", fwrq->m);
+	lbs_deb_leave(LBS_DEB_WEXT);
+	return 0;
+}
+
+static int lbs_get_wap(struct net_device *dev, struct iw_request_info *info,
+			struct sockaddr *awrq, char *extra)
+{
+	struct lbs_private *priv = dev->priv;
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	if (priv->connect_status == LBS_CONNECTED) {
+		memcpy(awrq->sa_data, priv->curbssparams.bssid, ETH_ALEN);
+	} else {
+		memset(awrq->sa_data, 0, ETH_ALEN);
+	}
+	awrq->sa_family = ARPHRD_ETHER;
+
+	lbs_deb_leave(LBS_DEB_WEXT);
+	return 0;
+}
+
+static int lbs_set_nick(struct net_device *dev, struct iw_request_info *info,
+			 struct iw_point *dwrq, char *extra)
+{
+	struct lbs_private *priv = dev->priv;
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	/*
+	 * Check the size of the string
+	 */
+
+	if (dwrq->length > 16) {
+		return -E2BIG;
+	}
+
+	mutex_lock(&priv->lock);
+	memset(priv->nodename, 0, sizeof(priv->nodename));
+	memcpy(priv->nodename, extra, dwrq->length);
+	mutex_unlock(&priv->lock);
+
+	lbs_deb_leave(LBS_DEB_WEXT);
+	return 0;
+}
+
+static int lbs_get_nick(struct net_device *dev, struct iw_request_info *info,
+			 struct iw_point *dwrq, char *extra)
+{
+	struct lbs_private *priv = dev->priv;
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	dwrq->length = strlen(priv->nodename);
+	memcpy(extra, priv->nodename, dwrq->length);
+	extra[dwrq->length] = '\0';
+
+	dwrq->flags = 1;	/* active */
+
+	lbs_deb_leave(LBS_DEB_WEXT);
+	return 0;
+}
+
+static int mesh_get_nick(struct net_device *dev, struct iw_request_info *info,
+			 struct iw_point *dwrq, char *extra)
+{
+	struct lbs_private *priv = dev->priv;
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	/* Use nickname to indicate that mesh is on */
+
+	if (priv->mesh_connect_status == LBS_CONNECTED) {
+		strncpy(extra, "Mesh", 12);
+		extra[12] = '\0';
+		dwrq->length = strlen(extra);
+	}
+
+	else {
+		extra[0] = '\0';
+		dwrq->length = 0;
+	}
+
+	lbs_deb_leave(LBS_DEB_WEXT);
+	return 0;
+}
+
+static int lbs_set_rts(struct net_device *dev, struct iw_request_info *info,
+			struct iw_param *vwrq, char *extra)
+{
+	int ret = 0;
+	struct lbs_private *priv = dev->priv;
+	u32 rthr = vwrq->value;
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	if (vwrq->disabled) {
+		priv->rtsthsd = rthr = MRVDRV_RTS_MAX_VALUE;
+	} else {
+		if (rthr < MRVDRV_RTS_MIN_VALUE || rthr > MRVDRV_RTS_MAX_VALUE)
+			return -EINVAL;
+		priv->rtsthsd = rthr;
+	}
+
+	ret = lbs_prepare_and_send_command(priv, CMD_802_11_SNMP_MIB,
+				    CMD_ACT_SET, CMD_OPTION_WAITFORRSP,
+				    OID_802_11_RTS_THRESHOLD, &rthr);
+
+	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+	return ret;
+}
+
+static int lbs_get_rts(struct net_device *dev, struct iw_request_info *info,
+			struct iw_param *vwrq, char *extra)
+{
+	int ret = 0;
+	struct lbs_private *priv = dev->priv;
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	priv->rtsthsd = 0;
+	ret = lbs_prepare_and_send_command(priv, CMD_802_11_SNMP_MIB,
+				    CMD_ACT_GET, CMD_OPTION_WAITFORRSP,
+				    OID_802_11_RTS_THRESHOLD, NULL);
+	if (ret)
+		goto out;
+
+	vwrq->value = priv->rtsthsd;
+	vwrq->disabled = ((vwrq->value < MRVDRV_RTS_MIN_VALUE)
+			  || (vwrq->value > MRVDRV_RTS_MAX_VALUE));
+	vwrq->fixed = 1;
+
+out:
+	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+	return ret;
+}
+
+static int lbs_set_frag(struct net_device *dev, struct iw_request_info *info,
+			 struct iw_param *vwrq, char *extra)
+{
+	int ret = 0;
+	u32 fthr = vwrq->value;
+	struct lbs_private *priv = dev->priv;
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	if (vwrq->disabled) {
+		priv->fragthsd = fthr = MRVDRV_FRAG_MAX_VALUE;
+	} else {
+		if (fthr < MRVDRV_FRAG_MIN_VALUE
+		    || fthr > MRVDRV_FRAG_MAX_VALUE)
+			return -EINVAL;
+		priv->fragthsd = fthr;
+	}
+
+	ret = lbs_prepare_and_send_command(priv, CMD_802_11_SNMP_MIB,
+				    CMD_ACT_SET, CMD_OPTION_WAITFORRSP,
+				    OID_802_11_FRAGMENTATION_THRESHOLD, &fthr);
+
+	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+	return ret;
+}
+
+static int lbs_get_frag(struct net_device *dev, struct iw_request_info *info,
+			 struct iw_param *vwrq, char *extra)
+{
+	int ret = 0;
+	struct lbs_private *priv = dev->priv;
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	priv->fragthsd = 0;
+	ret = lbs_prepare_and_send_command(priv,
+				    CMD_802_11_SNMP_MIB,
+				    CMD_ACT_GET, CMD_OPTION_WAITFORRSP,
+				    OID_802_11_FRAGMENTATION_THRESHOLD, NULL);
+	if (ret)
+		goto out;
+
+	vwrq->value = priv->fragthsd;
+	vwrq->disabled = ((vwrq->value < MRVDRV_FRAG_MIN_VALUE)
+			  || (vwrq->value > MRVDRV_FRAG_MAX_VALUE));
+	vwrq->fixed = 1;
+
+out:
+	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+	return ret;
+}
+
+static int lbs_get_mode(struct net_device *dev,
+			 struct iw_request_info *info, u32 * uwrq, char *extra)
+{
+	struct lbs_private *priv = dev->priv;
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	*uwrq = priv->mode;
+
+	lbs_deb_leave(LBS_DEB_WEXT);
+	return 0;
+}
+
+static int mesh_wlan_get_mode(struct net_device *dev,
+		              struct iw_request_info *info, u32 * uwrq,
+			      char *extra)
+{
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	*uwrq = IW_MODE_REPEAT ;
+
+	lbs_deb_leave(LBS_DEB_WEXT);
+	return 0;
+}
+
+static int lbs_get_txpow(struct net_device *dev,
+			  struct iw_request_info *info,
+			  struct iw_param *vwrq, char *extra)
+{
+	int ret = 0;
+	struct lbs_private *priv = dev->priv;
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	ret = lbs_prepare_and_send_command(priv,
+				    CMD_802_11_RF_TX_POWER,
+				    CMD_ACT_TX_POWER_OPT_GET,
+				    CMD_OPTION_WAITFORRSP, 0, NULL);
+
+	if (ret)
+		goto out;
+
+	lbs_deb_wext("tx power level %d dbm\n", priv->txpowerlevel);
+	vwrq->value = priv->txpowerlevel;
+	vwrq->fixed = 1;
+	if (priv->radioon) {
+		vwrq->disabled = 0;
+		vwrq->flags = IW_TXPOW_DBM;
+	} else {
+		vwrq->disabled = 1;
+	}
+
+out:
+	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+	return ret;
+}
+
+static int lbs_set_retry(struct net_device *dev, struct iw_request_info *info,
+			  struct iw_param *vwrq, char *extra)
+{
+	int ret = 0;
+	struct lbs_private *priv = dev->priv;
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	if (vwrq->flags == IW_RETRY_LIMIT) {
+		/* The MAC has a 4-bit Total_Tx_Count register
+		   Total_Tx_Count = 1 + Tx_Retry_Count */
+#define TX_RETRY_MIN 0
+#define TX_RETRY_MAX 14
+		if (vwrq->value < TX_RETRY_MIN || vwrq->value > TX_RETRY_MAX)
+			return -EINVAL;
+
+		/* Adding 1 to convert retry count to try count */
+		priv->txretrycount = vwrq->value + 1;
+
+		ret = lbs_prepare_and_send_command(priv, CMD_802_11_SNMP_MIB,
+					    CMD_ACT_SET,
+					    CMD_OPTION_WAITFORRSP,
+					    OID_802_11_TX_RETRYCOUNT, NULL);
+
+		if (ret)
+			goto out;
+	} else {
+		return -EOPNOTSUPP;
+	}
+
+out:
+	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+	return ret;
+}
+
+static int lbs_get_retry(struct net_device *dev, struct iw_request_info *info,
+			  struct iw_param *vwrq, char *extra)
+{
+	struct lbs_private *priv = dev->priv;
+	int ret = 0;
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	priv->txretrycount = 0;
+	ret = lbs_prepare_and_send_command(priv,
+				    CMD_802_11_SNMP_MIB,
+				    CMD_ACT_GET, CMD_OPTION_WAITFORRSP,
+				    OID_802_11_TX_RETRYCOUNT, NULL);
+	if (ret)
+		goto out;
+
+	vwrq->disabled = 0;
+	if (!vwrq->flags) {
+		vwrq->flags = IW_RETRY_LIMIT;
+		/* Subtract 1 to convert try count to retry count */
+		vwrq->value = priv->txretrycount - 1;
+	}
+
+out:
+	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+	return ret;
+}
+
+static inline void sort_channels(struct iw_freq *freq, int num)
+{
+	int i, j;
+	struct iw_freq temp;
+
+	for (i = 0; i < num; i++)
+		for (j = i + 1; j < num; j++)
+			if (freq[i].i > freq[j].i) {
+				temp.i = freq[i].i;
+				temp.m = freq[i].m;
+
+				freq[i].i = freq[j].i;
+				freq[i].m = freq[j].m;
+
+				freq[j].i = temp.i;
+				freq[j].m = temp.m;
+			}
+}
+
+/* data rate listing
+	MULTI_BANDS:
+		abg		a	b	b/g
+   Infra 	G(12)		A(8)	B(4)	G(12)
+   Adhoc 	A+B(12)		A(8)	B(4)	B(4)
+
+	non-MULTI_BANDS:
+					b	b/g
+   Infra 	     		    	B(4)	G(12)
+   Adhoc 	      		    	B(4)	B(4)
+ */
+/**
+ *  @brief Get Range Info
+ *
+ *  @param dev                  A pointer to net_device structure
+ *  @param info			A pointer to iw_request_info structure
+ *  @param vwrq 		A pointer to iw_param structure
+ *  @param extra		A pointer to extra data buf
+ *  @return 	   		0 --success, otherwise fail
+ */
+static int lbs_get_range(struct net_device *dev, struct iw_request_info *info,
+			  struct iw_point *dwrq, char *extra)
+{
+	int i, j;
+	struct lbs_private *priv = dev->priv;
+	struct iw_range *range = (struct iw_range *)extra;
+	struct chan_freq_power *cfp;
+	u8 rates[MAX_RATES + 1];
+
+	u8 flag = 0;
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	dwrq->length = sizeof(struct iw_range);
+	memset(range, 0, sizeof(struct iw_range));
+
+	range->min_nwid = 0;
+	range->max_nwid = 0;
+
+	memset(rates, 0, sizeof(rates));
+	copy_active_data_rates(priv, rates);
+	range->num_bitrates = strnlen(rates, IW_MAX_BITRATES);
+	for (i = 0; i < range->num_bitrates; i++)
+		range->bitrate[i] = rates[i] * 500000;
+	range->num_bitrates = i;
+	lbs_deb_wext("IW_MAX_BITRATES %d, num_bitrates %d\n", IW_MAX_BITRATES,
+	       range->num_bitrates);
+
+	range->num_frequency = 0;
+	if (priv->enable11d &&
+	    (priv->connect_status == LBS_CONNECTED ||
+	    priv->mesh_connect_status == LBS_CONNECTED)) {
+		u8 chan_no;
+		u8 band;
+
+		struct parsed_region_chan_11d *parsed_region_chan =
+		    &priv->parsed_region_chan;
+
+		if (parsed_region_chan == NULL) {
+			lbs_deb_wext("11d: parsed_region_chan is NULL\n");
+			goto out;
+		}
+		band = parsed_region_chan->band;
+		lbs_deb_wext("band %d, nr_char %d\n", band,
+		       parsed_region_chan->nr_chan);
+
+		for (i = 0; (range->num_frequency < IW_MAX_FREQUENCIES)
+		     && (i < parsed_region_chan->nr_chan); i++) {
+			chan_no = parsed_region_chan->chanpwr[i].chan;
+			lbs_deb_wext("chan_no %d\n", chan_no);
+			range->freq[range->num_frequency].i = (long)chan_no;
+			range->freq[range->num_frequency].m =
+			    (long)lbs_chan_2_freq(chan_no, band) * 100000;
+			range->freq[range->num_frequency].e = 1;
+			range->num_frequency++;
+		}
+		flag = 1;
+	}
+	if (!flag) {
+		for (j = 0; (range->num_frequency < IW_MAX_FREQUENCIES)
+		     && (j < ARRAY_SIZE(priv->region_channel)); j++) {
+			cfp = priv->region_channel[j].CFP;
+			for (i = 0; (range->num_frequency < IW_MAX_FREQUENCIES)
+			     && priv->region_channel[j].valid
+			     && cfp
+			     && (i < priv->region_channel[j].nrcfp); i++) {
+				range->freq[range->num_frequency].i =
+				    (long)cfp->channel;
+				range->freq[range->num_frequency].m =
+				    (long)cfp->freq * 100000;
+				range->freq[range->num_frequency].e = 1;
+				cfp++;
+				range->num_frequency++;
+			}
+		}
+	}
+
+	lbs_deb_wext("IW_MAX_FREQUENCIES %d, num_frequency %d\n",
+	       IW_MAX_FREQUENCIES, range->num_frequency);
+
+	range->num_channels = range->num_frequency;
+
+	sort_channels(&range->freq[0], range->num_frequency);
+
+	/*
+	 * Set an indication of the max TCP throughput in bit/s that we can
+	 * expect using this interface
+	 */
+	if (i > 2)
+		range->throughput = 5000 * 1000;
+	else
+		range->throughput = 1500 * 1000;
+
+	range->min_rts = MRVDRV_RTS_MIN_VALUE;
+	range->max_rts = MRVDRV_RTS_MAX_VALUE;
+	range->min_frag = MRVDRV_FRAG_MIN_VALUE;
+	range->max_frag = MRVDRV_FRAG_MAX_VALUE;
+
+	range->encoding_size[0] = 5;
+	range->encoding_size[1] = 13;
+	range->num_encoding_sizes = 2;
+	range->max_encoding_tokens = 4;
+
+	range->min_pmp = 1000000;
+	range->max_pmp = 120000000;
+	range->min_pmt = 1000;
+	range->max_pmt = 1000000;
+	range->pmp_flags = IW_POWER_PERIOD;
+	range->pmt_flags = IW_POWER_TIMEOUT;
+	range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R;
+
+	/*
+	 * Minimum version we recommend
+	 */
+	range->we_version_source = 15;
+
+	/*
+	 * Version we are compiled with
+	 */
+	range->we_version_compiled = WIRELESS_EXT;
+
+	range->retry_capa = IW_RETRY_LIMIT;
+	range->retry_flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
+
+	range->min_retry = TX_RETRY_MIN;
+	range->max_retry = TX_RETRY_MAX;
+
+	/*
+	 * Set the qual, level and noise range values
+	 */
+	range->max_qual.qual = 100;
+	range->max_qual.level = 0;
+	range->max_qual.noise = 0;
+	range->max_qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
+
+	range->avg_qual.qual = 70;
+	/* TODO: Find real 'good' to 'bad' threshold value for RSSI */
+	range->avg_qual.level = 0;
+	range->avg_qual.noise = 0;
+	range->avg_qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
+
+	range->sensitivity = 0;
+
+	/*
+	 * Setup the supported power level ranges
+	 */
+	memset(range->txpower, 0, sizeof(range->txpower));
+	range->txpower[0] = 5;
+	range->txpower[1] = 7;
+	range->txpower[2] = 9;
+	range->txpower[3] = 11;
+	range->txpower[4] = 13;
+	range->txpower[5] = 15;
+	range->txpower[6] = 17;
+	range->txpower[7] = 19;
+
+	range->num_txpower = 8;
+	range->txpower_capa = IW_TXPOW_DBM;
+	range->txpower_capa |= IW_TXPOW_RANGE;
+
+	range->event_capa[0] = (IW_EVENT_CAPA_K_0 |
+				IW_EVENT_CAPA_MASK(SIOCGIWAP) |
+				IW_EVENT_CAPA_MASK(SIOCGIWSCAN));
+	range->event_capa[1] = IW_EVENT_CAPA_K_1;
+
+	if (priv->fwcapinfo & FW_CAPINFO_WPA) {
+		range->enc_capa =   IW_ENC_CAPA_WPA
+		                  | IW_ENC_CAPA_WPA2
+		                  | IW_ENC_CAPA_CIPHER_TKIP
+		                  | IW_ENC_CAPA_CIPHER_CCMP;
+	}
+
+out:
+	lbs_deb_leave(LBS_DEB_WEXT);
+	return 0;
+}
+
+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->priv;
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	/* PS is currently supported only in Infrastructure mode
+	 * Remove this check if it is to be supported in IBSS mode also
+	 */
+
+	if (vwrq->disabled) {
+		priv->psmode = LBS802_11POWERMODECAM;
+		if (priv->psstate != PS_STATE_FULL_POWER) {
+			lbs_ps_wakeup(priv, CMD_OPTION_WAITFORRSP);
+		}
+
+		return 0;
+	}
+
+	if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
+		lbs_deb_wext(
+		       "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;
+	}
+
+	if (priv->psmode != LBS802_11POWERMODECAM) {
+		return 0;
+	}
+
+	priv->psmode = LBS802_11POWERMODEMAX_PSP;
+
+	if (priv->connect_status == LBS_CONNECTED) {
+		lbs_ps_sleep(priv, CMD_OPTION_WAITFORRSP);
+	}
+
+	lbs_deb_leave(LBS_DEB_WEXT);
+	return 0;
+}
+
+static int lbs_get_power(struct net_device *dev, struct iw_request_info *info,
+			  struct iw_param *vwrq, char *extra)
+{
+	struct lbs_private *priv = dev->priv;
+	int mode;
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	mode = priv->psmode;
+
+	if ((vwrq->disabled = (mode == LBS802_11POWERMODECAM))
+	    || priv->connect_status == LBS_DISCONNECTED)
+	{
+		goto out;
+	}
+
+	vwrq->value = 0;
+
+out:
+	lbs_deb_leave(LBS_DEB_WEXT);
+	return 0;
+}
+
+static struct iw_statistics *lbs_get_wireless_stats(struct net_device *dev)
+{
+	enum {
+		POOR = 30,
+		FAIR = 60,
+		GOOD = 80,
+		VERY_GOOD = 90,
+		EXCELLENT = 95,
+		PERFECT = 100
+	};
+	struct lbs_private *priv = dev->priv;
+	u32 rssi_qual;
+	u32 tx_qual;
+	u32 quality = 0;
+	int stats_valid = 0;
+	u8 rssi;
+	u32 tx_retries;
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	priv->wstats.status = priv->mode;
+
+	/* If we're not associated, all quality values are meaningless */
+	if ((priv->connect_status != LBS_CONNECTED) &&
+	    (priv->mesh_connect_status != LBS_CONNECTED))
+		goto out;
+
+	/* Quality by RSSI */
+	priv->wstats.qual.level =
+	    CAL_RSSI(priv->SNR[TYPE_BEACON][TYPE_NOAVG],
+	     priv->NF[TYPE_BEACON][TYPE_NOAVG]);
+
+	if (priv->NF[TYPE_BEACON][TYPE_NOAVG] == 0) {
+		priv->wstats.qual.noise = MRVDRV_NF_DEFAULT_SCAN_VALUE;
+	} else {
+		priv->wstats.qual.noise =
+		    CAL_NF(priv->NF[TYPE_BEACON][TYPE_NOAVG]);
+	}
+
+	lbs_deb_wext("signal level %#x\n", priv->wstats.qual.level);
+	lbs_deb_wext("noise %#x\n", priv->wstats.qual.noise);
+
+	rssi = priv->wstats.qual.level - priv->wstats.qual.noise;
+	if (rssi < 15)
+		rssi_qual = rssi * POOR / 10;
+	else if (rssi < 20)
+		rssi_qual = (rssi - 15) * (FAIR - POOR) / 5 + POOR;
+	else if (rssi < 30)
+		rssi_qual = (rssi - 20) * (GOOD - FAIR) / 5 + FAIR;
+	else if (rssi < 40)
+		rssi_qual = (rssi - 30) * (VERY_GOOD - GOOD) /
+		    10 + GOOD;
+	else
+		rssi_qual = (rssi - 40) * (PERFECT - VERY_GOOD) /
+		    10 + VERY_GOOD;
+	quality = rssi_qual;
+
+	/* Quality by TX errors */
+	priv->wstats.discard.retries = priv->stats.tx_errors;
+
+	tx_retries = le32_to_cpu(priv->logmsg.retry);
+
+	if (tx_retries > 75)
+		tx_qual = (90 - tx_retries) * POOR / 15;
+	else if (tx_retries > 70)
+		tx_qual = (75 - tx_retries) * (FAIR - POOR) / 5 + POOR;
+	else if (tx_retries > 65)
+		tx_qual = (70 - tx_retries) * (GOOD - FAIR) / 5 + FAIR;
+	else if (tx_retries > 50)
+		tx_qual = (65 - tx_retries) * (VERY_GOOD - GOOD) /
+		    15 + GOOD;
+	else
+		tx_qual = (50 - tx_retries) *
+		    (PERFECT - VERY_GOOD) / 50 + VERY_GOOD;
+	quality = min(quality, tx_qual);
+
+	priv->wstats.discard.code = le32_to_cpu(priv->logmsg.wepundecryptable);
+	priv->wstats.discard.fragment = le32_to_cpu(priv->logmsg.rxfrag);
+	priv->wstats.discard.retries = tx_retries;
+	priv->wstats.discard.misc = le32_to_cpu(priv->logmsg.ackfailure);
+
+	/* Calculate quality */
+	priv->wstats.qual.qual = min_t(u8, quality, 100);
+	priv->wstats.qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
+	stats_valid = 1;
+
+	/* update stats asynchronously for future calls */
+	lbs_prepare_and_send_command(priv, CMD_802_11_RSSI, 0,
+					0, 0, NULL);
+	lbs_prepare_and_send_command(priv, CMD_802_11_GET_LOG, 0,
+					0, 0, NULL);
+out:
+	if (!stats_valid) {
+		priv->wstats.miss.beacon = 0;
+		priv->wstats.discard.retries = 0;
+		priv->wstats.qual.qual = 0;
+		priv->wstats.qual.level = 0;
+		priv->wstats.qual.noise = 0;
+		priv->wstats.qual.updated = IW_QUAL_ALL_UPDATED;
+		priv->wstats.qual.updated |= IW_QUAL_NOISE_INVALID |
+		    IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID;
+	}
+
+	lbs_deb_leave(LBS_DEB_WEXT);
+	return &priv->wstats;
+
+
+}
+
+static int lbs_set_freq(struct net_device *dev, struct iw_request_info *info,
+		  struct iw_freq *fwrq, char *extra)
+{
+	int ret = -EINVAL;
+	struct lbs_private *priv = dev->priv;
+	struct chan_freq_power *cfp;
+	struct assoc_request * assoc_req;
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	mutex_lock(&priv->lock);
+	assoc_req = lbs_get_association_request(priv);
+	if (!assoc_req) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	/* If setting by frequency, convert to a channel */
+	if (fwrq->e == 1) {
+		long f = fwrq->m / 100000;
+
+		cfp = find_cfp_by_band_and_freq(priv, 0, f);
+		if (!cfp) {
+			lbs_deb_wext("invalid freq %ld\n", f);
+			goto out;
+		}
+
+		fwrq->e = 0;
+		fwrq->m = (int) cfp->channel;
+	}
+
+	/* Setting by channel number */
+	if (fwrq->m > 1000 || fwrq->e > 0) {
+		goto out;
+	}
+
+	cfp = lbs_find_cfp_by_band_and_channel(priv, 0, fwrq->m);
+	if (!cfp) {
+		goto out;
+	}
+
+	assoc_req->channel = fwrq->m;
+	ret = 0;
+
+out:
+	if (ret == 0) {
+		set_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags);
+		lbs_postpone_association_work(priv);
+	} else {
+		lbs_cancel_association_work(priv);
+	}
+	mutex_unlock(&priv->lock);
+
+	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+	return ret;
+}
+
+static int lbs_mesh_set_freq(struct net_device *dev,
+			     struct iw_request_info *info,
+			     struct iw_freq *fwrq, char *extra)
+{
+	struct lbs_private *priv = dev->priv;
+	struct chan_freq_power *cfp;
+	int ret = -EINVAL;
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	/* If setting by frequency, convert to a channel */
+	if (fwrq->e == 1) {
+		long f = fwrq->m / 100000;
+
+		cfp = find_cfp_by_band_and_freq(priv, 0, f);
+		if (!cfp) {
+			lbs_deb_wext("invalid freq %ld\n", f);
+			goto out;
+		}
+
+		fwrq->e = 0;
+		fwrq->m = (int) cfp->channel;
+	}
+
+	/* Setting by channel number */
+	if (fwrq->m > 1000 || fwrq->e > 0) {
+		goto out;
+	}
+
+	cfp = lbs_find_cfp_by_band_and_channel(priv, 0, fwrq->m);
+	if (!cfp) {
+		goto out;
+	}
+
+	if (fwrq->m != priv->curbssparams.channel) {
+		lbs_deb_wext("mesh channel change forces eth disconnect\n");
+		if (priv->mode == IW_MODE_INFRA)
+			lbs_send_deauthentication(priv);
+		else if (priv->mode == IW_MODE_ADHOC)
+			lbs_stop_adhoc_network(priv);
+	}
+	priv->curbssparams.channel = fwrq->m;
+	lbs_mesh_config(priv, 0);
+	lbs_mesh_config(priv, 1);
+	ret = 0;
+
+out:
+	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+	return ret;
+}
+
+static int lbs_set_rate(struct net_device *dev, struct iw_request_info *info,
+		  struct iw_param *vwrq, char *extra)
+{
+	struct lbs_private *priv = dev->priv;
+	u8 new_rate = 0;
+	int ret = -EINVAL;
+	u8 rates[MAX_RATES + 1];
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+	lbs_deb_wext("vwrq->value %d\n", vwrq->value);
+
+	/* Auto rate? */
+	if (vwrq->value == -1) {
+		priv->auto_rate = 1;
+		priv->cur_rate = 0;
+	} else {
+		if (vwrq->value % 100000)
+			goto out;
+
+		memset(rates, 0, sizeof(rates));
+		copy_active_data_rates(priv, rates);
+		new_rate = vwrq->value / 500000;
+		if (!memchr(rates, new_rate, sizeof(rates))) {
+			lbs_pr_alert("fixed data rate 0x%X out of range\n",
+				new_rate);
+			goto out;
+		}
+
+		priv->cur_rate = new_rate;
+		priv->auto_rate = 0;
+	}
+
+	ret = lbs_set_data_rate(priv, new_rate);
+
+out:
+	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+	return ret;
+}
+
+static int lbs_get_rate(struct net_device *dev, struct iw_request_info *info,
+		  struct iw_param *vwrq, char *extra)
+{
+	struct lbs_private *priv = dev->priv;
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	if (priv->connect_status == LBS_CONNECTED) {
+		vwrq->value = priv->cur_rate * 500000;
+
+		if (priv->auto_rate)
+			vwrq->fixed = 0;
+		else
+			vwrq->fixed = 1;
+
+	} else {
+		vwrq->fixed = 0;
+		vwrq->value = 0;
+	}
+
+	lbs_deb_leave(LBS_DEB_WEXT);
+	return 0;
+}
+
+static int lbs_set_mode(struct net_device *dev,
+		  struct iw_request_info *info, u32 * uwrq, char *extra)
+{
+	int ret = 0;
+	struct lbs_private *priv = dev->priv;
+	struct assoc_request * assoc_req;
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	if (   (*uwrq != IW_MODE_ADHOC)
+	    && (*uwrq != IW_MODE_INFRA)
+	    && (*uwrq != IW_MODE_AUTO)) {
+		lbs_deb_wext("Invalid mode: 0x%x\n", *uwrq);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	mutex_lock(&priv->lock);
+	assoc_req = lbs_get_association_request(priv);
+	if (!assoc_req) {
+		ret = -ENOMEM;
+		lbs_cancel_association_work(priv);
+	} else {
+		assoc_req->mode = *uwrq;
+		set_bit(ASSOC_FLAG_MODE, &assoc_req->flags);
+		lbs_postpone_association_work(priv);
+		lbs_deb_wext("Switching to mode: 0x%x\n", *uwrq);
+	}
+	mutex_unlock(&priv->lock);
+
+out:
+	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+	return ret;
+}
+
+
+/**
+ *  @brief Get Encryption key
+ *
+ *  @param dev                  A pointer to net_device structure
+ *  @param info			A pointer to iw_request_info structure
+ *  @param vwrq 		A pointer to iw_param structure
+ *  @param extra		A pointer to extra data buf
+ *  @return 	   		0 --success, otherwise fail
+ */
+static int lbs_get_encode(struct net_device *dev,
+			   struct iw_request_info *info,
+			   struct iw_point *dwrq, u8 * extra)
+{
+	struct lbs_private *priv = dev->priv;
+	int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	lbs_deb_wext("flags 0x%x, index %d, length %d, wep_tx_keyidx %d\n",
+	       dwrq->flags, index, dwrq->length, priv->wep_tx_keyidx);
+
+	dwrq->flags = 0;
+
+	/* Authentication method */
+	switch (priv->secinfo.auth_mode) {
+	case IW_AUTH_ALG_OPEN_SYSTEM:
+		dwrq->flags = IW_ENCODE_OPEN;
+		break;
+
+	case IW_AUTH_ALG_SHARED_KEY:
+	case IW_AUTH_ALG_LEAP:
+		dwrq->flags = IW_ENCODE_RESTRICTED;
+		break;
+	default:
+		dwrq->flags = IW_ENCODE_DISABLED | IW_ENCODE_OPEN;
+		break;
+	}
+
+	memset(extra, 0, 16);
+
+	mutex_lock(&priv->lock);
+
+	/* Default to returning current transmit key */
+	if (index < 0)
+		index = priv->wep_tx_keyidx;
+
+	if ((priv->wep_keys[index].len) && priv->secinfo.wep_enabled) {
+		memcpy(extra, priv->wep_keys[index].key,
+		       priv->wep_keys[index].len);
+		dwrq->length = priv->wep_keys[index].len;
+
+		dwrq->flags |= (index + 1);
+		/* Return WEP enabled */
+		dwrq->flags &= ~IW_ENCODE_DISABLED;
+	} else if ((priv->secinfo.WPAenabled)
+		   || (priv->secinfo.WPA2enabled)) {
+		/* return WPA enabled */
+		dwrq->flags &= ~IW_ENCODE_DISABLED;
+		dwrq->flags |= IW_ENCODE_NOKEY;
+	} else {
+		dwrq->flags |= IW_ENCODE_DISABLED;
+	}
+
+	mutex_unlock(&priv->lock);
+
+	lbs_deb_wext("key: %02x:%02x:%02x:%02x:%02x:%02x, keylen %d\n",
+	       extra[0], extra[1], extra[2],
+	       extra[3], extra[4], extra[5], dwrq->length);
+
+	lbs_deb_wext("return flags 0x%x\n", dwrq->flags);
+
+	lbs_deb_leave(LBS_DEB_WEXT);
+	return 0;
+}
+
+/**
+ *  @brief Set Encryption key (internal)
+ *
+ *  @param priv			A pointer to private card structure
+ *  @param key_material		A pointer to key material
+ *  @param key_length		length of key material
+ *  @param index		key index to set
+ *  @param set_tx_key		Force set TX key (1 = yes, 0 = no)
+ *  @return 	   		0 --success, otherwise fail
+ */
+static int lbs_set_wep_key(struct assoc_request *assoc_req,
+			    const char *key_material,
+			    u16 key_length,
+			    u16 index,
+			    int set_tx_key)
+{
+	int ret = 0;
+	struct enc_key *pkey;
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	/* Paranoid validation of key index */
+	if (index > 3) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* validate max key length */
+	if (key_length > KEY_LEN_WEP_104) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	pkey = &assoc_req->wep_keys[index];
+
+	if (key_length > 0) {
+		memset(pkey, 0, sizeof(struct enc_key));
+		pkey->type = KEY_TYPE_ID_WEP;
+
+		/* Standardize the key length */
+		pkey->len = (key_length > KEY_LEN_WEP_40) ?
+		                KEY_LEN_WEP_104 : KEY_LEN_WEP_40;
+		memcpy(pkey->key, key_material, key_length);
+	}
+
+	if (set_tx_key) {
+		/* Ensure the chosen key is valid */
+		if (!pkey->len) {
+			lbs_deb_wext("key not set, so cannot enable it\n");
+			ret = -EINVAL;
+			goto out;
+		}
+		assoc_req->wep_tx_keyidx = index;
+	}
+
+	assoc_req->secinfo.wep_enabled = 1;
+
+out:
+	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+	return ret;
+}
+
+static int validate_key_index(u16 def_index, u16 raw_index,
+			      u16 *out_index, u16 *is_default)
+{
+	if (!out_index || !is_default)
+		return -EINVAL;
+
+	/* Verify index if present, otherwise use default TX key index */
+	if (raw_index > 0) {
+		if (raw_index > 4)
+			return -EINVAL;
+		*out_index = raw_index - 1;
+	} else {
+		*out_index = def_index;
+		*is_default = 1;
+	}
+	return 0;
+}
+
+static void disable_wep(struct assoc_request *assoc_req)
+{
+	int i;
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	/* Set Open System auth mode */
+	assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
+
+	/* Clear WEP keys and mark WEP as disabled */
+	assoc_req->secinfo.wep_enabled = 0;
+	for (i = 0; i < 4; i++)
+		assoc_req->wep_keys[i].len = 0;
+
+	set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
+	set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags);
+
+	lbs_deb_leave(LBS_DEB_WEXT);
+}
+
+static void disable_wpa(struct assoc_request *assoc_req)
+{
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	memset(&assoc_req->wpa_mcast_key, 0, sizeof (struct enc_key));
+	assoc_req->wpa_mcast_key.flags = KEY_INFO_WPA_MCAST;
+	set_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags);
+
+	memset(&assoc_req->wpa_unicast_key, 0, sizeof (struct enc_key));
+	assoc_req->wpa_unicast_key.flags = KEY_INFO_WPA_UNICAST;
+	set_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags);
+
+	assoc_req->secinfo.WPAenabled = 0;
+	assoc_req->secinfo.WPA2enabled = 0;
+	set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
+
+	lbs_deb_leave(LBS_DEB_WEXT);
+}
+
+/**
+ *  @brief Set Encryption key
+ *
+ *  @param dev                  A pointer to net_device structure
+ *  @param info			A pointer to iw_request_info structure
+ *  @param vwrq 		A pointer to iw_param structure
+ *  @param extra		A pointer to extra data buf
+ *  @return 	   		0 --success, otherwise fail
+ */
+static int lbs_set_encode(struct net_device *dev,
+		    struct iw_request_info *info,
+		    struct iw_point *dwrq, char *extra)
+{
+	int ret = 0;
+	struct lbs_private *priv = dev->priv;
+	struct assoc_request * assoc_req;
+	u16 is_default = 0, index = 0, set_tx_key = 0;
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	mutex_lock(&priv->lock);
+	assoc_req = lbs_get_association_request(priv);
+	if (!assoc_req) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	if (dwrq->flags & IW_ENCODE_DISABLED) {
+		disable_wep (assoc_req);
+		disable_wpa (assoc_req);
+		goto out;
+	}
+
+	ret = validate_key_index(assoc_req->wep_tx_keyidx,
+	                         (dwrq->flags & IW_ENCODE_INDEX),
+	                         &index, &is_default);
+	if (ret) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	/* If WEP isn't enabled, or if there is no key data but a valid
+	 * index, set the TX key.
+	 */
+	if (!assoc_req->secinfo.wep_enabled || (dwrq->length == 0 && !is_default))
+		set_tx_key = 1;
+
+	ret = lbs_set_wep_key(assoc_req, extra, dwrq->length, index, set_tx_key);
+	if (ret)
+		goto out;
+
+	if (dwrq->length)
+		set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags);
+	if (set_tx_key)
+		set_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags);
+
+	if (dwrq->flags & IW_ENCODE_RESTRICTED) {
+		assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY;
+	} else if (dwrq->flags & IW_ENCODE_OPEN) {
+		assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
+	}
+
+out:
+	if (ret == 0) {
+		set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
+		lbs_postpone_association_work(priv);
+	} else {
+		lbs_cancel_association_work(priv);
+	}
+	mutex_unlock(&priv->lock);
+
+	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+	return ret;
+}
+
+/**
+ *  @brief Get Extended Encryption key (WPA/802.1x and WEP)
+ *
+ *  @param dev                  A pointer to net_device structure
+ *  @param info			A pointer to iw_request_info structure
+ *  @param vwrq 		A pointer to iw_param structure
+ *  @param extra		A pointer to extra data buf
+ *  @return 	   		0 on success, otherwise failure
+ */
+static int lbs_get_encodeext(struct net_device *dev,
+			      struct iw_request_info *info,
+			      struct iw_point *dwrq,
+			      char *extra)
+{
+	int ret = -EINVAL;
+	struct lbs_private *priv = dev->priv;
+	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
+	int index, max_key_len;
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	max_key_len = dwrq->length - sizeof(*ext);
+	if (max_key_len < 0)
+		goto out;
+
+	index = dwrq->flags & IW_ENCODE_INDEX;
+	if (index) {
+		if (index < 1 || index > 4)
+			goto out;
+		index--;
+	} else {
+		index = priv->wep_tx_keyidx;
+	}
+
+	if (!(ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) &&
+	    ext->alg != IW_ENCODE_ALG_WEP) {
+		if (index != 0 || priv->mode != IW_MODE_INFRA)
+			goto out;
+	}
+
+	dwrq->flags = index + 1;
+	memset(ext, 0, sizeof(*ext));
+
+	if (   !priv->secinfo.wep_enabled
+	    && !priv->secinfo.WPAenabled
+	    && !priv->secinfo.WPA2enabled) {
+		ext->alg = IW_ENCODE_ALG_NONE;
+		ext->key_len = 0;
+		dwrq->flags |= IW_ENCODE_DISABLED;
+	} else {
+		u8 *key = NULL;
+
+		if (   priv->secinfo.wep_enabled
+		    && !priv->secinfo.WPAenabled
+		    && !priv->secinfo.WPA2enabled) {
+			/* WEP */
+			ext->alg = IW_ENCODE_ALG_WEP;
+			ext->key_len = priv->wep_keys[index].len;
+			key = &priv->wep_keys[index].key[0];
+		} else if (   !priv->secinfo.wep_enabled
+		           && (priv->secinfo.WPAenabled ||
+		               priv->secinfo.WPA2enabled)) {
+			/* WPA */
+			struct enc_key * pkey = NULL;
+
+			if (   priv->wpa_mcast_key.len
+			    && (priv->wpa_mcast_key.flags & KEY_INFO_WPA_ENABLED))
+				pkey = &priv->wpa_mcast_key;
+			else if (   priv->wpa_unicast_key.len
+			         && (priv->wpa_unicast_key.flags & KEY_INFO_WPA_ENABLED))
+				pkey = &priv->wpa_unicast_key;
+
+			if (pkey) {
+				if (pkey->type == KEY_TYPE_ID_AES) {
+					ext->alg = IW_ENCODE_ALG_CCMP;
+				} else {
+					ext->alg = IW_ENCODE_ALG_TKIP;
+				}
+				ext->key_len = pkey->len;
+				key = &pkey->key[0];
+			} else {
+				ext->alg = IW_ENCODE_ALG_TKIP;
+				ext->key_len = 0;
+			}
+		} else {
+			goto out;
+		}
+
+		if (ext->key_len > max_key_len) {
+			ret = -E2BIG;
+			goto out;
+		}
+
+		if (ext->key_len)
+			memcpy(ext->key, key, ext->key_len);
+		else
+			dwrq->flags |= IW_ENCODE_NOKEY;
+		dwrq->flags |= IW_ENCODE_ENABLED;
+	}
+	ret = 0;
+
+out:
+	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+	return ret;
+}
+
+/**
+ *  @brief Set Encryption key Extended (WPA/802.1x and WEP)
+ *
+ *  @param dev                  A pointer to net_device structure
+ *  @param info			A pointer to iw_request_info structure
+ *  @param vwrq 		A pointer to iw_param structure
+ *  @param extra		A pointer to extra data buf
+ *  @return 	   		0 --success, otherwise fail
+ */
+static int lbs_set_encodeext(struct net_device *dev,
+			      struct iw_request_info *info,
+			      struct iw_point *dwrq,
+			      char *extra)
+{
+	int ret = 0;
+	struct lbs_private *priv = dev->priv;
+	struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
+	int alg = ext->alg;
+	struct assoc_request * assoc_req;
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	mutex_lock(&priv->lock);
+	assoc_req = lbs_get_association_request(priv);
+	if (!assoc_req) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	if ((alg == IW_ENCODE_ALG_NONE) || (dwrq->flags & IW_ENCODE_DISABLED)) {
+		disable_wep (assoc_req);
+		disable_wpa (assoc_req);
+	} else if (alg == IW_ENCODE_ALG_WEP) {
+		u16 is_default = 0, index, set_tx_key = 0;
+
+		ret = validate_key_index(assoc_req->wep_tx_keyidx,
+		                         (dwrq->flags & IW_ENCODE_INDEX),
+		                         &index, &is_default);
+		if (ret)
+			goto out;
+
+		/* If WEP isn't enabled, or if there is no key data but a valid
+		 * index, or if the set-TX-key flag was passed, set the TX key.
+		 */
+		if (   !assoc_req->secinfo.wep_enabled
+		    || (dwrq->length == 0 && !is_default)
+		    || (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY))
+			set_tx_key = 1;
+
+		/* Copy key to driver */
+		ret = lbs_set_wep_key(assoc_req, ext->key, ext->key_len, index,
+					set_tx_key);
+		if (ret)
+			goto out;
+
+		if (dwrq->flags & IW_ENCODE_RESTRICTED) {
+			assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY;
+		} else if (dwrq->flags & IW_ENCODE_OPEN) {
+			assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
+		}
+
+		/* Mark the various WEP bits as modified */
+		set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
+		if (dwrq->length)
+			set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags);
+		if (set_tx_key)
+			set_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags);
+	} else if ((alg == IW_ENCODE_ALG_TKIP) || (alg == IW_ENCODE_ALG_CCMP)) {
+		struct enc_key * pkey;
+
+		/* validate key length */
+		if (((alg == IW_ENCODE_ALG_TKIP)
+			&& (ext->key_len != KEY_LEN_WPA_TKIP))
+		    || ((alg == IW_ENCODE_ALG_CCMP)
+		        && (ext->key_len != KEY_LEN_WPA_AES))) {
+				lbs_deb_wext("invalid size %d for key of alg "
+				       "type %d\n",
+				       ext->key_len,
+				       alg);
+				ret = -EINVAL;
+				goto out;
+		}
+
+		if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
+			pkey = &assoc_req->wpa_mcast_key;
+			set_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags);
+		} else {
+			pkey = &assoc_req->wpa_unicast_key;
+			set_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags);
+		}
+
+		memset(pkey, 0, sizeof (struct enc_key));
+		memcpy(pkey->key, ext->key, ext->key_len);
+		pkey->len = ext->key_len;
+		if (pkey->len)
+			pkey->flags |= KEY_INFO_WPA_ENABLED;
+
+		/* Do this after zeroing key structure */
+		if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
+			pkey->flags |= KEY_INFO_WPA_MCAST;
+		} else {
+			pkey->flags |= KEY_INFO_WPA_UNICAST;
+		}
+
+		if (alg == IW_ENCODE_ALG_TKIP) {
+			pkey->type = KEY_TYPE_ID_TKIP;
+		} else if (alg == IW_ENCODE_ALG_CCMP) {
+			pkey->type = KEY_TYPE_ID_AES;
+		}
+
+		/* If WPA isn't enabled yet, do that now */
+		if (   assoc_req->secinfo.WPAenabled == 0
+		    && assoc_req->secinfo.WPA2enabled == 0) {
+			assoc_req->secinfo.WPAenabled = 1;
+			assoc_req->secinfo.WPA2enabled = 1;
+			set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
+		}
+
+		disable_wep (assoc_req);
+	}
+
+out:
+	if (ret == 0) {
+		lbs_postpone_association_work(priv);
+	} else {
+		lbs_cancel_association_work(priv);
+	}
+	mutex_unlock(&priv->lock);
+
+	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+	return ret;
+}
+
+
+static int lbs_set_genie(struct net_device *dev,
+			  struct iw_request_info *info,
+			  struct iw_point *dwrq,
+			  char *extra)
+{
+	struct lbs_private *priv = dev->priv;
+	int ret = 0;
+	struct assoc_request * assoc_req;
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	mutex_lock(&priv->lock);
+	assoc_req = lbs_get_association_request(priv);
+	if (!assoc_req) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	if (dwrq->length > MAX_WPA_IE_LEN ||
+	    (dwrq->length && extra == NULL)) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (dwrq->length) {
+		memcpy(&assoc_req->wpa_ie[0], extra, dwrq->length);
+		assoc_req->wpa_ie_len = dwrq->length;
+	} else {
+		memset(&assoc_req->wpa_ie[0], 0, sizeof(priv->wpa_ie));
+		assoc_req->wpa_ie_len = 0;
+	}
+
+out:
+	if (ret == 0) {
+		set_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags);
+		lbs_postpone_association_work(priv);
+	} else {
+		lbs_cancel_association_work(priv);
+	}
+	mutex_unlock(&priv->lock);
+
+	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+	return ret;
+}
+
+static int lbs_get_genie(struct net_device *dev,
+			  struct iw_request_info *info,
+			  struct iw_point *dwrq,
+			  char *extra)
+{
+	int ret = 0;
+	struct lbs_private *priv = dev->priv;
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	if (priv->wpa_ie_len == 0) {
+		dwrq->length = 0;
+		goto out;
+	}
+
+	if (dwrq->length < priv->wpa_ie_len) {
+		ret = -E2BIG;
+		goto out;
+	}
+
+	dwrq->length = priv->wpa_ie_len;
+	memcpy(extra, &priv->wpa_ie[0], priv->wpa_ie_len);
+
+out:
+	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+	return ret;
+}
+
+
+static int lbs_set_auth(struct net_device *dev,
+			 struct iw_request_info *info,
+			 struct iw_param *dwrq,
+			 char *extra)
+{
+	struct lbs_private *priv = dev->priv;
+	struct assoc_request * assoc_req;
+	int ret = 0;
+	int updated = 0;
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	mutex_lock(&priv->lock);
+	assoc_req = lbs_get_association_request(priv);
+	if (!assoc_req) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	switch (dwrq->flags & IW_AUTH_INDEX) {
+	case IW_AUTH_TKIP_COUNTERMEASURES:
+	case IW_AUTH_CIPHER_PAIRWISE:
+	case IW_AUTH_CIPHER_GROUP:
+	case IW_AUTH_KEY_MGMT:
+	case IW_AUTH_DROP_UNENCRYPTED:
+		/*
+		 * libertas does not use these parameters
+		 */
+		break;
+
+	case IW_AUTH_WPA_VERSION:
+		if (dwrq->value & IW_AUTH_WPA_VERSION_DISABLED) {
+			assoc_req->secinfo.WPAenabled = 0;
+			assoc_req->secinfo.WPA2enabled = 0;
+			disable_wpa (assoc_req);
+		}
+		if (dwrq->value & IW_AUTH_WPA_VERSION_WPA) {
+			assoc_req->secinfo.WPAenabled = 1;
+			assoc_req->secinfo.wep_enabled = 0;
+			assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
+		}
+		if (dwrq->value & IW_AUTH_WPA_VERSION_WPA2) {
+			assoc_req->secinfo.WPA2enabled = 1;
+			assoc_req->secinfo.wep_enabled = 0;
+			assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
+		}
+		updated = 1;
+		break;
+
+	case IW_AUTH_80211_AUTH_ALG:
+		if (dwrq->value & IW_AUTH_ALG_SHARED_KEY) {
+			assoc_req->secinfo.auth_mode = IW_AUTH_ALG_SHARED_KEY;
+		} else if (dwrq->value & IW_AUTH_ALG_OPEN_SYSTEM) {
+			assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
+		} else if (dwrq->value & IW_AUTH_ALG_LEAP) {
+			assoc_req->secinfo.auth_mode = IW_AUTH_ALG_LEAP;
+		} else {
+			ret = -EINVAL;
+		}
+		updated = 1;
+		break;
+
+	case IW_AUTH_WPA_ENABLED:
+		if (dwrq->value) {
+			if (!assoc_req->secinfo.WPAenabled &&
+			    !assoc_req->secinfo.WPA2enabled) {
+				assoc_req->secinfo.WPAenabled = 1;
+				assoc_req->secinfo.WPA2enabled = 1;
+				assoc_req->secinfo.wep_enabled = 0;
+				assoc_req->secinfo.auth_mode = IW_AUTH_ALG_OPEN_SYSTEM;
+			}
+		} else {
+			assoc_req->secinfo.WPAenabled = 0;
+			assoc_req->secinfo.WPA2enabled = 0;
+			disable_wpa (assoc_req);
+		}
+		updated = 1;
+		break;
+
+	default:
+		ret = -EOPNOTSUPP;
+		break;
+	}
+
+out:
+	if (ret == 0) {
+		if (updated)
+			set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
+		lbs_postpone_association_work(priv);
+	} else if (ret != -EOPNOTSUPP) {
+		lbs_cancel_association_work(priv);
+	}
+	mutex_unlock(&priv->lock);
+
+	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+	return ret;
+}
+
+static int lbs_get_auth(struct net_device *dev,
+			 struct iw_request_info *info,
+			 struct iw_param *dwrq,
+			 char *extra)
+{
+	int ret = 0;
+	struct lbs_private *priv = dev->priv;
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	switch (dwrq->flags & IW_AUTH_INDEX) {
+	case IW_AUTH_WPA_VERSION:
+		dwrq->value = 0;
+		if (priv->secinfo.WPAenabled)
+			dwrq->value |= IW_AUTH_WPA_VERSION_WPA;
+		if (priv->secinfo.WPA2enabled)
+			dwrq->value |= IW_AUTH_WPA_VERSION_WPA2;
+		if (!dwrq->value)
+			dwrq->value |= IW_AUTH_WPA_VERSION_DISABLED;
+		break;
+
+	case IW_AUTH_80211_AUTH_ALG:
+		dwrq->value = priv->secinfo.auth_mode;
+		break;
+
+	case IW_AUTH_WPA_ENABLED:
+		if (priv->secinfo.WPAenabled && priv->secinfo.WPA2enabled)
+			dwrq->value = 1;
+		break;
+
+	default:
+		ret = -EOPNOTSUPP;
+	}
+
+	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+	return ret;
+}
+
+
+static int lbs_set_txpow(struct net_device *dev, struct iw_request_info *info,
+		   struct iw_param *vwrq, char *extra)
+{
+	int ret = 0;
+	struct lbs_private *priv = dev->priv;
+
+	u16 dbm;
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	if (vwrq->disabled) {
+		lbs_radio_ioctl(priv, RADIO_OFF);
+		return 0;
+	}
+
+	priv->preamble = CMD_TYPE_AUTO_PREAMBLE;
+
+	lbs_radio_ioctl(priv, RADIO_ON);
+
+	/* Userspace check in iwrange if it should use dBm or mW,
+	 * therefore this should never happen... Jean II */
+	if ((vwrq->flags & IW_TXPOW_TYPE) == IW_TXPOW_MWATT) {
+		return -EOPNOTSUPP;
+	} else
+		dbm = (u16) vwrq->value;
+
+	/* auto tx power control */
+
+	if (vwrq->fixed == 0)
+		dbm = 0xffff;
+
+	lbs_deb_wext("txpower set %d dbm\n", dbm);
+
+	ret = lbs_prepare_and_send_command(priv,
+				    CMD_802_11_RF_TX_POWER,
+				    CMD_ACT_TX_POWER_OPT_SET_LOW,
+				    CMD_OPTION_WAITFORRSP, 0, (void *)&dbm);
+
+	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+	return ret;
+}
+
+static int lbs_get_essid(struct net_device *dev, struct iw_request_info *info,
+		   struct iw_point *dwrq, char *extra)
+{
+	struct lbs_private *priv = dev->priv;
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	/*
+	 * Note : if dwrq->flags != 0, we should get the relevant SSID from
+	 * the SSID list...
+	 */
+
+	/*
+	 * Get the current SSID
+	 */
+	if (priv->connect_status == LBS_CONNECTED) {
+		memcpy(extra, priv->curbssparams.ssid,
+		       priv->curbssparams.ssid_len);
+		extra[priv->curbssparams.ssid_len] = '\0';
+	} else {
+		memset(extra, 0, 32);
+		extra[priv->curbssparams.ssid_len] = '\0';
+	}
+	/*
+	 * If none, we may want to get the one that was set
+	 */
+
+	dwrq->length = priv->curbssparams.ssid_len;
+
+	dwrq->flags = 1;	/* active */
+
+	lbs_deb_leave(LBS_DEB_WEXT);
+	return 0;
+}
+
+static int lbs_set_essid(struct net_device *dev, struct iw_request_info *info,
+		   struct iw_point *dwrq, char *extra)
+{
+	struct lbs_private *priv = dev->priv;
+	int ret = 0;
+	u8 ssid[IW_ESSID_MAX_SIZE];
+	u8 ssid_len = 0;
+	struct assoc_request * assoc_req;
+	int in_ssid_len = dwrq->length;
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	/* Check the size of the string */
+	if (in_ssid_len > IW_ESSID_MAX_SIZE) {
+		ret = -E2BIG;
+		goto out;
+	}
+
+	memset(&ssid, 0, sizeof(ssid));
+
+	if (!dwrq->flags || !in_ssid_len) {
+		/* "any" SSID requested; leave SSID blank */
+	} else {
+		/* Specific SSID requested */
+		memcpy(&ssid, extra, in_ssid_len);
+		ssid_len = in_ssid_len;
+	}
+
+	if (!ssid_len) {
+		lbs_deb_wext("requested any SSID\n");
+	} else {
+		lbs_deb_wext("requested SSID '%s'\n",
+		             escape_essid(ssid, ssid_len));
+	}
+
+out:
+	mutex_lock(&priv->lock);
+	if (ret == 0) {
+		/* Get or create the current association request */
+		assoc_req = lbs_get_association_request(priv);
+		if (!assoc_req) {
+			ret = -ENOMEM;
+		} else {
+			/* Copy the SSID to the association request */
+			memcpy(&assoc_req->ssid, &ssid, IW_ESSID_MAX_SIZE);
+			assoc_req->ssid_len = ssid_len;
+			set_bit(ASSOC_FLAG_SSID, &assoc_req->flags);
+			lbs_postpone_association_work(priv);
+		}
+	}
+
+	/* Cancel the association request if there was an error */
+	if (ret != 0) {
+		lbs_cancel_association_work(priv);
+	}
+
+	mutex_unlock(&priv->lock);
+
+	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+	return ret;
+}
+
+static int lbs_mesh_get_essid(struct net_device *dev,
+			      struct iw_request_info *info,
+			      struct iw_point *dwrq, char *extra)
+{
+	struct lbs_private *priv = dev->priv;
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	memcpy(extra, priv->mesh_ssid, priv->mesh_ssid_len);
+
+	dwrq->length = priv->mesh_ssid_len;
+
+	dwrq->flags = 1;	/* active */
+
+	lbs_deb_leave(LBS_DEB_WEXT);
+	return 0;
+}
+
+static int lbs_mesh_set_essid(struct net_device *dev,
+			      struct iw_request_info *info,
+			      struct iw_point *dwrq, char *extra)
+{
+	struct lbs_private *priv = dev->priv;
+	int ret = 0;
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	/* Check the size of the string */
+	if (dwrq->length > IW_ESSID_MAX_SIZE) {
+		ret = -E2BIG;
+		goto out;
+	}
+
+	if (!dwrq->flags || !dwrq->length) {
+		ret = -EINVAL;
+		goto out;
+	} else {
+		/* Specific SSID requested */
+		memcpy(priv->mesh_ssid, extra, dwrq->length);
+		priv->mesh_ssid_len = dwrq->length;
+	}
+
+	lbs_mesh_config(priv, 1);
+ out:
+	lbs_deb_leave_args(LBS_DEB_WEXT, "ret %d", ret);
+	return ret;
+}
+
+/**
+ *  @brief Connect to the AP or Ad-hoc Network with specific bssid
+ *
+ *  @param dev          A pointer to net_device structure
+ *  @param info         A pointer to iw_request_info structure
+ *  @param awrq         A pointer to iw_param structure
+ *  @param extra        A pointer to extra data buf
+ *  @return             0 --success, otherwise fail
+ */
+static int lbs_set_wap(struct net_device *dev, struct iw_request_info *info,
+		 struct sockaddr *awrq, char *extra)
+{
+	struct lbs_private *priv = dev->priv;
+	struct assoc_request * assoc_req;
+	int ret = 0;
+	DECLARE_MAC_BUF(mac);
+
+	lbs_deb_enter(LBS_DEB_WEXT);
+
+	if (awrq->sa_family != ARPHRD_ETHER)
+		return -EINVAL;
+
+	lbs_deb_wext("ASSOC: WAP: sa_data %s\n", print_mac(mac, awrq->sa_data));
+
+	mutex_lock(&priv->lock);
+
+	/* Get or create the current association request */
+	assoc_req = lbs_get_association_request(priv);
+	if (!assoc_req) {
+		lbs_cancel_association_work(priv);
+		ret = -ENOMEM;
+	} else {
+		/* Copy the BSSID to the association request */
+		memcpy(&assoc_req->bssid, awrq->sa_data, ETH_ALEN);
+		set_bit(ASSOC_FLAG_BSSID, &assoc_req->flags);
+		lbs_postpone_association_work(priv);
+	}
+
+	mutex_unlock(&priv->lock);
+
+	return ret;
+}
+
+void lbs_get_fwversion(struct lbs_private *priv, char *fwversion, int maxlen)
+{
+	char fwver[32];
+
+	mutex_lock(&priv->lock);
+
+	if (priv->fwreleasenumber[3] == 0)
+		sprintf(fwver, "%u.%u.%u",
+			priv->fwreleasenumber[2],
+			priv->fwreleasenumber[1],
+			priv->fwreleasenumber[0]);
+	else
+		sprintf(fwver, "%u.%u.%u.p%u",
+			priv->fwreleasenumber[2],
+			priv->fwreleasenumber[1],
+			priv->fwreleasenumber[0],
+			priv->fwreleasenumber[3]);
+
+	mutex_unlock(&priv->lock);
+	snprintf(fwversion, maxlen, fwver);
+}
+
+
+/*
+ * iwconfig settable callbacks
+ */
+static const iw_handler lbs_handler[] = {
+	(iw_handler) NULL,	/* SIOCSIWCOMMIT */
+	(iw_handler) lbs_get_name,	/* SIOCGIWNAME */
+	(iw_handler) NULL,	/* SIOCSIWNWID */
+	(iw_handler) NULL,	/* SIOCGIWNWID */
+	(iw_handler) lbs_set_freq,	/* SIOCSIWFREQ */
+	(iw_handler) lbs_get_freq,	/* SIOCGIWFREQ */
+	(iw_handler) lbs_set_mode,	/* SIOCSIWMODE */
+	(iw_handler) lbs_get_mode,	/* SIOCGIWMODE */
+	(iw_handler) NULL,	/* SIOCSIWSENS */
+	(iw_handler) NULL,	/* SIOCGIWSENS */
+	(iw_handler) NULL,	/* SIOCSIWRANGE */
+	(iw_handler) lbs_get_range,	/* SIOCGIWRANGE */
+	(iw_handler) NULL,	/* SIOCSIWPRIV */
+	(iw_handler) NULL,	/* SIOCGIWPRIV */
+	(iw_handler) NULL,	/* SIOCSIWSTATS */
+	(iw_handler) NULL,	/* SIOCGIWSTATS */
+	iw_handler_set_spy,	/* SIOCSIWSPY */
+	iw_handler_get_spy,	/* SIOCGIWSPY */
+	iw_handler_set_thrspy,	/* SIOCSIWTHRSPY */
+	iw_handler_get_thrspy,	/* SIOCGIWTHRSPY */
+	(iw_handler) lbs_set_wap,	/* SIOCSIWAP */
+	(iw_handler) lbs_get_wap,	/* SIOCGIWAP */
+	(iw_handler) NULL,	/* SIOCSIWMLME */
+	(iw_handler) NULL,	/* SIOCGIWAPLIST - deprecated */
+	(iw_handler) lbs_set_scan,	/* SIOCSIWSCAN */
+	(iw_handler) lbs_get_scan,	/* SIOCGIWSCAN */
+	(iw_handler) lbs_set_essid,	/* SIOCSIWESSID */
+	(iw_handler) lbs_get_essid,	/* SIOCGIWESSID */
+	(iw_handler) lbs_set_nick,	/* SIOCSIWNICKN */
+	(iw_handler) lbs_get_nick,	/* SIOCGIWNICKN */
+	(iw_handler) NULL,	/* -- hole -- */
+	(iw_handler) NULL,	/* -- hole -- */
+	(iw_handler) lbs_set_rate,	/* SIOCSIWRATE */
+	(iw_handler) lbs_get_rate,	/* SIOCGIWRATE */
+	(iw_handler) lbs_set_rts,	/* SIOCSIWRTS */
+	(iw_handler) lbs_get_rts,	/* SIOCGIWRTS */
+	(iw_handler) lbs_set_frag,	/* SIOCSIWFRAG */
+	(iw_handler) lbs_get_frag,	/* SIOCGIWFRAG */
+	(iw_handler) lbs_set_txpow,	/* SIOCSIWTXPOW */
+	(iw_handler) lbs_get_txpow,	/* SIOCGIWTXPOW */
+	(iw_handler) lbs_set_retry,	/* SIOCSIWRETRY */
+	(iw_handler) lbs_get_retry,	/* SIOCGIWRETRY */
+	(iw_handler) lbs_set_encode,	/* SIOCSIWENCODE */
+	(iw_handler) lbs_get_encode,	/* SIOCGIWENCODE */
+	(iw_handler) lbs_set_power,	/* SIOCSIWPOWER */
+	(iw_handler) lbs_get_power,	/* SIOCGIWPOWER */
+	(iw_handler) NULL,	/* -- hole -- */
+	(iw_handler) NULL,	/* -- hole -- */
+	(iw_handler) lbs_set_genie,	/* SIOCSIWGENIE */
+	(iw_handler) lbs_get_genie,	/* SIOCGIWGENIE */
+	(iw_handler) lbs_set_auth,	/* SIOCSIWAUTH */
+	(iw_handler) lbs_get_auth,	/* SIOCGIWAUTH */
+	(iw_handler) lbs_set_encodeext,/* SIOCSIWENCODEEXT */
+	(iw_handler) lbs_get_encodeext,/* SIOCGIWENCODEEXT */
+	(iw_handler) NULL,		/* SIOCSIWPMKSA */
+};
+
+static const iw_handler mesh_wlan_handler[] = {
+	(iw_handler) NULL,	/* SIOCSIWCOMMIT */
+	(iw_handler) lbs_get_name,	/* SIOCGIWNAME */
+	(iw_handler) NULL,	/* SIOCSIWNWID */
+	(iw_handler) NULL,	/* SIOCGIWNWID */
+	(iw_handler) lbs_mesh_set_freq,	/* SIOCSIWFREQ */
+	(iw_handler) lbs_get_freq,	/* SIOCGIWFREQ */
+	(iw_handler) NULL,		/* SIOCSIWMODE */
+	(iw_handler) mesh_wlan_get_mode,	/* SIOCGIWMODE */
+	(iw_handler) NULL,	/* SIOCSIWSENS */
+	(iw_handler) NULL,	/* SIOCGIWSENS */
+	(iw_handler) NULL,	/* SIOCSIWRANGE */
+	(iw_handler) lbs_get_range,	/* SIOCGIWRANGE */
+	(iw_handler) NULL,	/* SIOCSIWPRIV */
+	(iw_handler) NULL,	/* SIOCGIWPRIV */
+	(iw_handler) NULL,	/* SIOCSIWSTATS */
+	(iw_handler) NULL,	/* SIOCGIWSTATS */
+	iw_handler_set_spy,	/* SIOCSIWSPY */
+	iw_handler_get_spy,	/* SIOCGIWSPY */
+	iw_handler_set_thrspy,	/* SIOCSIWTHRSPY */
+	iw_handler_get_thrspy,	/* SIOCGIWTHRSPY */
+	(iw_handler) NULL,	/* SIOCSIWAP */
+	(iw_handler) NULL,	/* SIOCGIWAP */
+	(iw_handler) NULL,	/* SIOCSIWMLME */
+	(iw_handler) NULL,	/* SIOCGIWAPLIST - deprecated */
+	(iw_handler) lbs_set_scan,	/* SIOCSIWSCAN */
+	(iw_handler) lbs_get_scan,	/* SIOCGIWSCAN */
+	(iw_handler) lbs_mesh_set_essid,/* SIOCSIWESSID */
+	(iw_handler) lbs_mesh_get_essid,/* SIOCGIWESSID */
+	(iw_handler) NULL,		/* SIOCSIWNICKN */
+	(iw_handler) mesh_get_nick,	/* SIOCGIWNICKN */
+	(iw_handler) NULL,	/* -- hole -- */
+	(iw_handler) NULL,	/* -- hole -- */
+	(iw_handler) lbs_set_rate,	/* SIOCSIWRATE */
+	(iw_handler) lbs_get_rate,	/* SIOCGIWRATE */
+	(iw_handler) lbs_set_rts,	/* SIOCSIWRTS */
+	(iw_handler) lbs_get_rts,	/* SIOCGIWRTS */
+	(iw_handler) lbs_set_frag,	/* SIOCSIWFRAG */
+	(iw_handler) lbs_get_frag,	/* SIOCGIWFRAG */
+	(iw_handler) lbs_set_txpow,	/* SIOCSIWTXPOW */
+	(iw_handler) lbs_get_txpow,	/* SIOCGIWTXPOW */
+	(iw_handler) lbs_set_retry,	/* SIOCSIWRETRY */
+	(iw_handler) lbs_get_retry,	/* SIOCGIWRETRY */
+	(iw_handler) lbs_set_encode,	/* SIOCSIWENCODE */
+	(iw_handler) lbs_get_encode,	/* SIOCGIWENCODE */
+	(iw_handler) lbs_set_power,	/* SIOCSIWPOWER */
+	(iw_handler) lbs_get_power,	/* SIOCGIWPOWER */
+	(iw_handler) NULL,	/* -- hole -- */
+	(iw_handler) NULL,	/* -- hole -- */
+	(iw_handler) lbs_set_genie,	/* SIOCSIWGENIE */
+	(iw_handler) lbs_get_genie,	/* SIOCGIWGENIE */
+	(iw_handler) lbs_set_auth,	/* SIOCSIWAUTH */
+	(iw_handler) lbs_get_auth,	/* SIOCGIWAUTH */
+	(iw_handler) lbs_set_encodeext,/* SIOCSIWENCODEEXT */
+	(iw_handler) lbs_get_encodeext,/* SIOCGIWENCODEEXT */
+	(iw_handler) NULL,		/* SIOCSIWPMKSA */
+};
+struct iw_handler_def lbs_handler_def = {
+	.num_standard	= ARRAY_SIZE(lbs_handler),
+	.standard	= (iw_handler *) lbs_handler,
+	.get_wireless_stats = lbs_get_wireless_stats,
+};
+
+struct iw_handler_def mesh_handler_def = {
+	.num_standard	= ARRAY_SIZE(mesh_wlan_handler),
+	.standard	= (iw_handler *) mesh_wlan_handler,
+	.get_wireless_stats = lbs_get_wireless_stats,
+};

+ 23 - 0
package/libertas/src/wext.h

@@ -0,0 +1,23 @@
+/**
+  * This file contains definition for IOCTL call.
+  */
+#ifndef	_LBS_WEXT_H_
+#define	_LBS_WEXT_H_
+
+/** lbs_ioctl_regrdwr */
+struct lbs_ioctl_regrdwr {
+	/** Which register to access */
+	u16 whichreg;
+	/** Read or Write */
+	u16 action;
+	u32 offset;
+	u16 NOB;
+	u32 value;
+};
+
+#define LBS_MONITOR_OFF			0
+
+extern struct iw_handler_def lbs_handler_def;
+extern struct iw_handler_def mesh_handler_def;
+
+#endif

+ 11 - 0
target/linux/olpc/base-files/etc/config/network

@@ -0,0 +1,11 @@
+# Copyright (C) 2006 OpenWrt.org
+
+config interface loopback
+        option ifname   lo
+        option proto    static
+        option ipaddr   127.0.0.1
+        option netmask  255.0.0.0
+
+config interface wlan
+        option ifname   eth0
+        option proto    dhcp

+ 5 - 11
target/linux/olpc/config-2.6.23

@@ -25,19 +25,14 @@ CONFIG_BATTERY_OLPC=y
 # CONFIG_BINFMT_AOUT is not set
 # CONFIG_BINFMT_AOUT is not set
 CONFIG_BINFMT_MISC=y
 CONFIG_BINFMT_MISC=y
 CONFIG_BITREVERSE=y
 CONFIG_BITREVERSE=y
-CONFIG_BLK_DEV_CRYPTOLOOP=y
 CONFIG_BLK_DEV_GENERIC=m
 CONFIG_BLK_DEV_GENERIC=m
 CONFIG_BLK_DEV_IDE=m
 CONFIG_BLK_DEV_IDE=m
 CONFIG_BLK_DEV_IDEDISK=m
 CONFIG_BLK_DEV_IDEDISK=m
 CONFIG_BLK_DEV_IDEDMA=y
 CONFIG_BLK_DEV_IDEDMA=y
 CONFIG_BLK_DEV_IDEDMA_PCI=y
 CONFIG_BLK_DEV_IDEDMA_PCI=y
 CONFIG_BLK_DEV_IDEPCI=y
 CONFIG_BLK_DEV_IDEPCI=y
-CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_LOOP is not set
 # CONFIG_BLK_DEV_NBD is not set
 # CONFIG_BLK_DEV_NBD is not set
-CONFIG_BLK_DEV_RAM=y
-CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024
-CONFIG_BLK_DEV_RAM_COUNT=16
-CONFIG_BLK_DEV_RAM_SIZE=4096
 CONFIG_BLK_DEV_SC1200=m
 CONFIG_BLK_DEV_SC1200=m
 CONFIG_BLK_DEV_SD=y
 CONFIG_BLK_DEV_SD=y
 CONFIG_BLK_DEV_SR=y
 CONFIG_BLK_DEV_SR=y
@@ -57,22 +52,21 @@ CONFIG_COMPAT_VDSO=y
 # CONFIG_CRC_ITU_T is not set
 # CONFIG_CRC_ITU_T is not set
 # CONFIG_CRYPTO_AES is not set
 # CONFIG_CRYPTO_AES is not set
 # CONFIG_CRYPTO_AES_586 is not set
 # CONFIG_CRYPTO_AES_586 is not set
-CONFIG_CRYPTO_ALGAPI=y
 # CONFIG_CRYPTO_ANUBIS is not set
 # CONFIG_CRYPTO_ANUBIS is not set
 # CONFIG_CRYPTO_ARC4 is not set
 # CONFIG_CRYPTO_ARC4 is not set
-CONFIG_CRYPTO_BLKCIPHER=y
 # CONFIG_CRYPTO_BLOWFISH is not set
 # CONFIG_CRYPTO_BLOWFISH is not set
 # CONFIG_CRYPTO_CAMELLIA is not set
 # CONFIG_CRYPTO_CAMELLIA is not set
 # CONFIG_CRYPTO_CAST5 is not set
 # CONFIG_CRYPTO_CAST5 is not set
 # CONFIG_CRYPTO_CAST6 is not set
 # CONFIG_CRYPTO_CAST6 is not set
-CONFIG_CRYPTO_CBC=y
+# CONFIG_CRYPTO_CBC is not set
 # CONFIG_CRYPTO_CRC32C is not set
 # CONFIG_CRYPTO_CRC32C is not set
 # CONFIG_CRYPTO_DEFLATE is not set
 # CONFIG_CRYPTO_DEFLATE is not set
+# CONFIG_CRYPTO_DES is not set
 # CONFIG_CRYPTO_ECB is not set
 # CONFIG_CRYPTO_ECB is not set
 # CONFIG_CRYPTO_HMAC is not set
 # CONFIG_CRYPTO_HMAC is not set
 # CONFIG_CRYPTO_HW is not set
 # CONFIG_CRYPTO_HW is not set
 # CONFIG_CRYPTO_KHAZAD is not set
 # CONFIG_CRYPTO_KHAZAD is not set
-CONFIG_CRYPTO_MANAGER=y
+# CONFIG_CRYPTO_MANAGER is not set
 # CONFIG_CRYPTO_MD4 is not set
 # CONFIG_CRYPTO_MD4 is not set
 # CONFIG_CRYPTO_MD5 is not set
 # CONFIG_CRYPTO_MD5 is not set
 # CONFIG_CRYPTO_MICHAEL_MIC is not set
 # CONFIG_CRYPTO_MICHAEL_MIC is not set
@@ -82,6 +76,7 @@ CONFIG_CRYPTO_MANAGER=y
 # CONFIG_CRYPTO_SHA256 is not set
 # CONFIG_CRYPTO_SHA256 is not set
 # CONFIG_CRYPTO_SHA512 is not set
 # CONFIG_CRYPTO_SHA512 is not set
 # CONFIG_CRYPTO_TEA is not set
 # CONFIG_CRYPTO_TEA is not set
+# CONFIG_CRYPTO_TEST is not set
 # CONFIG_CRYPTO_TGR192 is not set
 # CONFIG_CRYPTO_TGR192 is not set
 # CONFIG_CRYPTO_TWOFISH is not set
 # CONFIG_CRYPTO_TWOFISH is not set
 # CONFIG_CRYPTO_TWOFISH_586 is not set
 # CONFIG_CRYPTO_TWOFISH_586 is not set
@@ -242,7 +237,6 @@ CONFIG_INSTRUMENTATION=y
 # CONFIG_IP_NF_MATCH_AH is not set
 # CONFIG_IP_NF_MATCH_AH is not set
 # CONFIG_IP_NF_MATCH_ECN is not set
 # CONFIG_IP_NF_MATCH_ECN is not set
 # CONFIG_IP_NF_MATCH_IPP2P is not set
 # CONFIG_IP_NF_MATCH_IPP2P is not set
-# CONFIG_IP_NF_MATCH_LAYER7 is not set
 # CONFIG_IP_NF_MATCH_OWNER is not set
 # CONFIG_IP_NF_MATCH_OWNER is not set
 # CONFIG_IP_NF_MATCH_RECENT is not set
 # CONFIG_IP_NF_MATCH_RECENT is not set
 # CONFIG_IP_NF_MATCH_TIME is not set
 # CONFIG_IP_NF_MATCH_TIME is not set

+ 1 - 1
target/linux/olpc/image/olpc.fth

@@ -1,4 +1,4 @@
 \ Boot script
 \ Boot script
 " u:\boot\vmlinuz" to boot-device
 " u:\boot\vmlinuz" to boot-device
-" block2mtd.block2mtd=/dev/sda2,65536,rootfs root=/dev/mtdblock1 rootfstype=squashfs init=/etc/preinit rootdelay=5 noinitrd console=tty0" to boot-file
+" block2mtd.block2mtd=/dev/sda2,65536,rootfs root=/dev/mtdblock1 rootfstype=squashfs init=/etc/preinit rootdelay=5 noinitrd console=tty0 quiet" to boot-file
 boot
 boot