* adding network config for olpc
authorJens Muecke <jens@nons.de>
Sat, 15 Dec 2007 19:59:21 +0000 (19:59 +0000)
committerJens Muecke <jens@nons.de>
Sat, 15 Dec 2007 19:59:21 +0000 (19:59 +0000)
* adding libertas
* config issue
* quiet mode for bootloader

SVN-Revision: 9768

41 files changed:
package/libertas/Makefile [new file with mode: 0644]
package/libertas/files/LICENSE [new file with mode: 0644]
package/libertas/src/11d.c [new file with mode: 0644]
package/libertas/src/11d.h [new file with mode: 0644]
package/libertas/src/:0 [new file with mode: 0644]
package/libertas/src/LICENSE [new file with mode: 0644]
package/libertas/src/Makefile [new file with mode: 0644]
package/libertas/src/README [new file with mode: 0644]
package/libertas/src/assoc.c [new file with mode: 0644]
package/libertas/src/assoc.h [new file with mode: 0644]
package/libertas/src/cmd.c [new file with mode: 0644]
package/libertas/src/cmd.h [new file with mode: 0644]
package/libertas/src/cmdresp.c [new file with mode: 0644]
package/libertas/src/compat.h [new file with mode: 0644]
package/libertas/src/debugfs.c [new file with mode: 0644]
package/libertas/src/debugfs.h [new file with mode: 0644]
package/libertas/src/decl.h [new file with mode: 0644]
package/libertas/src/defs.h [new file with mode: 0644]
package/libertas/src/dev.h [new file with mode: 0644]
package/libertas/src/ethtool.c [new file with mode: 0644]
package/libertas/src/host.h [new file with mode: 0644]
package/libertas/src/hostcmd.h [new file with mode: 0644]
package/libertas/src/if_cs.c [new file with mode: 0644]
package/libertas/src/if_sdio.c [new file with mode: 0644]
package/libertas/src/if_sdio.h [new file with mode: 0644]
package/libertas/src/if_usb.c [new file with mode: 0644]
package/libertas/src/if_usb.h [new file with mode: 0644]
package/libertas/src/join.c [new file with mode: 0644]
package/libertas/src/join.h [new file with mode: 0644]
package/libertas/src/main.c [new file with mode: 0644]
package/libertas/src/radiotap.h [new file with mode: 0644]
package/libertas/src/rx.c [new file with mode: 0644]
package/libertas/src/scan.c [new file with mode: 0644]
package/libertas/src/scan.h [new file with mode: 0644]
package/libertas/src/tx.c [new file with mode: 0644]
package/libertas/src/types.h [new file with mode: 0644]
package/libertas/src/wext.c [new file with mode: 0644]
package/libertas/src/wext.h [new file with mode: 0644]
target/linux/olpc/base-files/etc/config/network [new file with mode: 0644]
target/linux/olpc/config-2.6.23
target/linux/olpc/image/olpc.fth

diff --git a/package/libertas/Makefile b/package/libertas/Makefile
new file mode 100644 (file)
index 0000000..bf8cbbc
--- /dev/null
@@ -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))
diff --git a/package/libertas/files/LICENSE b/package/libertas/files/LICENSE
new file mode 100644 (file)
index 0000000..7b6e8a9
--- /dev/null
@@ -0,0 +1,33 @@
+Copyright (c) 2006, One Laptop per Child and Marvell Corporation.\r
+All rights reserved.\r
+\r
+Redistribution.  Redistribution and use in binary form, without \r
+modification, are permitted provided that the following conditions are \r
+met:\r
+\r
+* Redistributions must reproduce the above copyright notice and the \r
+  following disclaimer in the documentation and/or other materials \r
+  provided with the distribution.\r
+* Neither the name of Marvell Corporation nor the names of its suppliers \r
+  may be used to endorse or promote products derived from this software \r
+  without specific prior written permission.
+* No reverse engineering, decompilation, or disassembly of this software \r
+  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.\r
+\r
+DISCLAIMER.  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND \r
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, \r
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND \r
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE \r
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, \r
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, \r
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS \r
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND \r
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR \r
+TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE \r
+USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH \r
+DAMAGE.\r
diff --git a/package/libertas/src/11d.c b/package/libertas/src/11d.c
new file mode 100644 (file)
index 0000000..5e10ce0
--- /dev/null
@@ -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;
+}
diff --git a/package/libertas/src/11d.h b/package/libertas/src/11d.h
new file mode 100644 (file)
index 0000000..811eea2
--- /dev/null
@@ -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
diff --git a/package/libertas/src/:0 b/package/libertas/src/:0
new file mode 100644 (file)
index 0000000..ff68859
--- /dev/null
@@ -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))
diff --git a/package/libertas/src/LICENSE b/package/libertas/src/LICENSE
new file mode 100644 (file)
index 0000000..8862742
--- /dev/null
@@ -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.
+
diff --git a/package/libertas/src/Makefile b/package/libertas/src/Makefile
new file mode 100644 (file)
index 0000000..0e27876
--- /dev/null
@@ -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
diff --git a/package/libertas/src/README b/package/libertas/src/README
new file mode 100644 (file)
index 0000000..d860fc3
--- /dev/null
@@ -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.
+
+==============================================================================
diff --git a/package/libertas/src/assoc.c b/package/libertas/src/assoc.c
new file mode 100644 (file)
index 0000000..7b672fe
--- /dev/null
@@ -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;
+}
diff --git a/package/libertas/src/assoc.h b/package/libertas/src/assoc.h
new file mode 100644 (file)
index 0000000..08372bb
--- /dev/null
@@ -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 */
diff --git a/package/libertas/src/cmd.c b/package/libertas/src/cmd.c
new file mode 100644 (file)
index 0000000..01d2349
--- /dev/null
@@ -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);
+
+
diff --git a/package/libertas/src/cmd.h b/package/libertas/src/cmd.h
new file mode 100644 (file)
index 0000000..80714b5
--- /dev/null
@@ -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 */
diff --git a/package/libertas/src/cmdresp.c b/package/libertas/src/cmdresp.c
new file mode 100644 (file)
index 0000000..bf9941e
--- /dev/null
@@ -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;
+}
diff --git a/package/libertas/src/compat.h b/package/libertas/src/compat.h
new file mode 100644 (file)
index 0000000..26c6363
--- /dev/null
@@ -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
diff --git a/package/libertas/src/debugfs.c b/package/libertas/src/debugfs.c
new file mode 100644 (file)
index 0000000..f4858bd
--- /dev/null
@@ -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
diff --git a/package/libertas/src/debugfs.h b/package/libertas/src/debugfs.h
new file mode 100644 (file)
index 0000000..f2b9c7f
--- /dev/null
@@ -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
diff --git a/package/libertas/src/decl.h b/package/libertas/src/decl.h
new file mode 100644 (file)
index 0000000..9b0ef16
--- /dev/null
@@ -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
diff --git a/package/libertas/src/defs.h b/package/libertas/src/defs.h
new file mode 100644 (file)
index 0000000..9b98ae7
--- /dev/null
@@ -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
diff --git a/package/libertas/src/dev.h b/package/libertas/src/dev.h
new file mode 100644 (file)
index 0000000..86b45a4
--- /dev/null
@@ -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
diff --git a/package/libertas/src/ethtool.c b/package/libertas/src/ethtool.c
new file mode 100644 (file)
index 0000000..98757f1
--- /dev/null
@@ -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,
+};
+
diff --git a/package/libertas/src/host.h b/package/libertas/src/host.h
new file mode 100644 (file)
index 0000000..64178cf
--- /dev/null
@@ -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
diff --git a/package/libertas/src/hostcmd.h b/package/libertas/src/hostcmd.h
new file mode 100644 (file)
index 0000000..aab5d64
--- /dev/null
@@ -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
diff --git a/package/libertas/src/if_cs.c b/package/libertas/src/if_cs.c
new file mode 100644 (file)
index 0000000..5814363
--- /dev/null
@@ -0,0 +1,973 @@
+/*
+
+  Driver for the Marvell 8385 based compact flash WLAN cards.
+
+  (C) 2007 by Holger Schurig <hs4233@mail.mn-solutions.de>
+
+  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 <hs4233@mail.mn-solutions.de>");
+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);