]> err.no Git - linux-2.6/commitdiff
[PATCH] Marvell Libertas 8388 802.11b/g USB driver
authorMarcelo Tosatti <marcelo@kvack.org>
Sat, 10 Feb 2007 14:25:27 +0000 (12:25 -0200)
committerJeff Garzik <jeff@garzik.org>
Sat, 28 Apr 2007 15:00:54 +0000 (11:00 -0400)
Add the Marvell Libertas 8388 802.11 USB driver.

Signed-off-by: Marcelo Tosatti <marcelo@kvack.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
40 files changed:
drivers/net/wireless/Kconfig
drivers/net/wireless/Makefile
drivers/net/wireless/libertas/11d.c [new file with mode: 0644]
drivers/net/wireless/libertas/11d.h [new file with mode: 0644]
drivers/net/wireless/libertas/LICENSE [new file with mode: 0644]
drivers/net/wireless/libertas/Makefile [new file with mode: 0644]
drivers/net/wireless/libertas/README [new file with mode: 0644]
drivers/net/wireless/libertas/assoc.c [new file with mode: 0644]
drivers/net/wireless/libertas/assoc.h [new file with mode: 0644]
drivers/net/wireless/libertas/cmd.c [new file with mode: 0644]
drivers/net/wireless/libertas/cmdresp.c [new file with mode: 0644]
drivers/net/wireless/libertas/debugfs.c [new file with mode: 0644]
drivers/net/wireless/libertas/debugfs.h [new file with mode: 0644]
drivers/net/wireless/libertas/decl.h [new file with mode: 0644]
drivers/net/wireless/libertas/defs.h [new file with mode: 0644]
drivers/net/wireless/libertas/dev.h [new file with mode: 0644]
drivers/net/wireless/libertas/ethtool.c [new file with mode: 0644]
drivers/net/wireless/libertas/fw.c [new file with mode: 0644]
drivers/net/wireless/libertas/fw.h [new file with mode: 0644]
drivers/net/wireless/libertas/host.h [new file with mode: 0644]
drivers/net/wireless/libertas/hostcmd.h [new file with mode: 0644]
drivers/net/wireless/libertas/if_bootcmd.c [new file with mode: 0644]
drivers/net/wireless/libertas/if_usb.c [new file with mode: 0644]
drivers/net/wireless/libertas/if_usb.h [new file with mode: 0644]
drivers/net/wireless/libertas/ioctl.c [new file with mode: 0644]
drivers/net/wireless/libertas/join.c [new file with mode: 0644]
drivers/net/wireless/libertas/join.h [new file with mode: 0644]
drivers/net/wireless/libertas/main.c [new file with mode: 0644]
drivers/net/wireless/libertas/radiotap.h [new file with mode: 0644]
drivers/net/wireless/libertas/rx.c [new file with mode: 0644]
drivers/net/wireless/libertas/sbi.h [new file with mode: 0644]
drivers/net/wireless/libertas/scan.c [new file with mode: 0644]
drivers/net/wireless/libertas/scan.h [new file with mode: 0644]
drivers/net/wireless/libertas/thread.h [new file with mode: 0644]
drivers/net/wireless/libertas/tx.c [new file with mode: 0644]
drivers/net/wireless/libertas/types.h [new file with mode: 0644]
drivers/net/wireless/libertas/version.h [new file with mode: 0644]
drivers/net/wireless/libertas/wext.c [new file with mode: 0644]
drivers/net/wireless/libertas/wext.h [new file with mode: 0644]
include/net/ieee80211_radiotap.h

index 4426841b2be68e0be492dfa4ea5613307d9bac0a..c4b3dc291a5a11898c95c38917b892cbb835e499 100644 (file)
@@ -265,6 +265,19 @@ config IPW2200_DEBUG
 
          If you are not sure, say N here.
 
+config LIBERTAS_USB
+       tristate "Marvell Libertas 8388 802.11a/b/g cards"
+       depends on NET_RADIO && USB
+       select FW_LOADER
+       ---help---
+         A driver for Marvell Libertas 8388 USB devices.
+
+config LIBERTAS_USB_DEBUG
+       bool "Enable full debugging output in the Libertas USB module."
+       depends on LIBERTAS_USB
+       ---help---
+         Debugging support.
+
 config AIRO
        tristate "Cisco/Aironet 34X/35X/4500/4800 ISA and PCI cards"
        depends on ISA_DMA_API && WLAN_80211 && (PCI || BROKEN)
index c613af17a159b62269c7460d24066bceae15e7a5..d2124602263bf8f193d575428f1fc3b11825cece 100644 (file)
@@ -43,3 +43,4 @@ obj-$(CONFIG_PCMCIA_RAYCS)    += ray_cs.o
 obj-$(CONFIG_PCMCIA_WL3501)    += wl3501_cs.o
 
 obj-$(CONFIG_USB_ZD1201)       += zd1201.o
+obj-$(CONFIG_LIBERTAS_USB)     += libertas/
diff --git a/drivers/net/wireless/libertas/11d.c b/drivers/net/wireless/libertas/11d.c
new file mode 100644 (file)
index 0000000..e0ecc4d
--- /dev/null
@@ -0,0 +1,754 @@
+/**
+  * 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 wlan_region_2_code(u8 * region)
+{
+       u8 i;
+       u8 size = sizeof(region_code_mapping)/
+                 sizeof(struct region_code_mapping);
+
+       for (i = 0; region[i] && i < COUNTRY_CODE_LEN; i++)
+               region[i] = toupper(region[i]);
+
+       for (i = 0; i < size; 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 *wlan_code_2_region(u8 code)
+{
+       u8 i;
+       u8 size = sizeof(region_code_mapping)
+                 / sizeof(struct region_code_mapping);
+       for (i = 0; i < size; 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 wlan_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 = sizeof(channel_freq_power_UN_BG) /
+           sizeof(struct chan_freq_power);
+
+       for (i = 0; i < cfp_no; i++) {
+               if ((cfp + i)->channel == firstchan) {
+                       lbs_pr_debug(1, "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 wlan_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_dbg_hex("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_pr_debug(1, "11D: Found Chan:%d\n", chan);
+                       return 1;
+               }
+       }
+
+       lbs_pr_debug(1, "11D: Not Find Chan:%d\n", chan);
+       return 0;
+}
+
+u32 libertas_chan_2_freq(u8 chan, u8 band)
+{
+       struct chan_freq_power *cf;
+       u16 cnt;
+       u16 i;
+       u32 freq = 0;
+
+       cf = channel_freq_power_UN_BG;
+       cnt =
+           sizeof(channel_freq_power_UN_BG) /
+           sizeof(struct chan_freq_power);
+
+       for (i = 0; i < cnt; 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 wlan_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_pr_debug(1, "11D:nrchan=%d\n", nr_chan);
+       lbs_dbg_hex("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_pr_debug(1, "nr_subband=%x\n", domaininfo->nr_subband);
+       lbs_dbg_hex("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 wlan_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_pr_debug(1, "11D: region_chan is NULL\n");
+               return;
+       }
+
+       cfp = region_chan->CFP;
+       if (cfp == NULL) {
+               lbs_pr_debug(1, "11D: cfp equal NULL \n");
+               return;
+       }
+
+       parsed_region_chan->band = region_chan->band;
+       parsed_region_chan->region = region_chan->region;
+       memcpy(parsed_region_chan->countrycode,
+              wlan_code_2_region(region_chan->region), COUNTRY_CODE_LEN);
+
+       lbs_pr_debug(1, "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_pr_debug(1, "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_pr_debug(1, "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 wlan_region_chan_supported_11d(u8 region, u8 band, u8 chan)
+{
+       struct chan_freq_power *cfp;
+       int cfp_no;
+       u8 idx;
+
+       ENTER();
+
+       cfp = libertas_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) {
+                               return 0;
+                       } else {
+                               return 1;
+                       }
+               }
+       }
+
+       /*chan is not in the region table */
+       LEAVE();
+       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                    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;
+
+       ENTER();
+
+       /*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_dbg_hex("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 */
+               LEAVE();
+               return 0;
+       }
+
+       /*Step1: check region_code */
+       parsed_region_chan->region = region =
+           wlan_region_2_code(countryinfo->countrycode);
+
+       lbs_pr_debug(1, "regioncode=%x\n", (u8) parsed_region_chan->region);
+       lbs_dbg_hex("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_pr_debug(1, "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 (!wlan_get_chan_11d(band, firstchan, i, &curchan)) {
+                               /* Chan is not found in UN table */
+                               lbs_pr_debug(1, "chan is not supported: %d \n", i);
+                               break;
+                       }
+
+                       lastchan = curchan;
+
+                       if (wlan_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_pr_debug(1,
+                                      "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_pr_debug(1, "nrchan=%x\n", parsed_region_chan->nr_chan);
+       lbs_dbg_hex("11D:parsed_region_chan:", (u8 *) parsed_region_chan,
+               2 + COUNTRY_CODE_LEN + sizeof(struct parsed_region_chan_11d) * idx);
+
+       LEAVE();
+       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 libertas_get_scan_type_11d(u8 chan,
+                         struct parsed_region_chan_11d * parsed_region_chan)
+{
+       u8 scan_type = cmd_scan_type_passive;
+
+       ENTER();
+
+       if (wlan_channel_known_11d(chan, parsed_region_chan)) {
+               lbs_pr_debug(1, "11D: Found and do Active Scan\n");
+               scan_type = cmd_scan_type_active;
+       } else {
+               lbs_pr_debug(1, "11D: Not Find and do Passive Scan\n");
+       }
+
+       LEAVE();
+       return scan_type;
+
+}
+
+void libertas_init_11d(wlan_private * priv)
+{
+       priv->adapter->enable11d = 0;
+       memset(&(priv->adapter->parsed_region_chan), 0,
+              sizeof(struct parsed_region_chan_11d));
+       return;
+}
+
+static int wlan_enable_11d(wlan_private * priv, u8 flag)
+{
+       int ret;
+
+       priv->adapter->enable11d = flag;
+
+       /* send cmd to FW to enable/disable 11D function in FW */
+       ret = libertas_prepare_and_send_command(priv,
+                                   cmd_802_11_snmp_mib,
+                                   cmd_act_set,
+                                   cmd_option_waitforrsp,
+                                   OID_802_11D_ENABLE,
+                                   &priv->adapter->enable11d);
+       if (ret)
+               lbs_pr_debug(1, "11D: Fail to enable 11D \n");
+
+       return 0;
+}
+
+/**
+ *  @brief This function sets DOMAIN INFO to FW
+ *  @param priv       pointer to wlan_private
+ *  @return          0; -1
+*/
+static int set_domain_info_11d(wlan_private * priv)
+{
+       int ret;
+
+       if (!priv->adapter->enable11d) {
+               lbs_pr_debug(1, "11D: dnld domain Info with 11d disabled\n");
+               return 0;
+       }
+
+       ret = libertas_prepare_and_send_command(priv, cmd_802_11d_domain_info,
+                                   cmd_act_set,
+                                   cmd_option_waitforrsp, 0, NULL);
+       if (ret)
+               lbs_pr_debug(1, "11D: Fail to dnld domain Info\n");
+
+       return ret;
+}
+
+/**
+ *  @brief This function setups scan channels
+ *  @param priv       pointer to wlan_private
+ *  @param band       band
+ *  @return          0
+*/
+int libertas_set_universaltable(wlan_private * priv, u8 band)
+{
+       wlan_adapter *adapter = priv->adapter;
+       u16 size = sizeof(struct chan_freq_power);
+       u16 i = 0;
+
+       memset(adapter->universal_channel, 0,
+              sizeof(adapter->universal_channel));
+
+       adapter->universal_channel[i].nrcfp =
+           sizeof(channel_freq_power_UN_BG) / size;
+       lbs_pr_debug(1, "11D: BG-band nrcfp=%d\n",
+              adapter->universal_channel[i].nrcfp);
+
+       adapter->universal_channel[i].CFP = channel_freq_power_UN_BG;
+       adapter->universal_channel[i].valid = 1;
+       adapter->universal_channel[i].region = UNIVERSAL_REGION_CODE;
+       adapter->universal_channel[i].band = band;
+       i++;
+
+       return 0;
+}
+
+/**
+ *  @brief This function implements command CMD_802_11D_DOMAIN_INFO
+ *  @param priv       pointer to wlan_private
+ *  @param cmd        pointer to cmd buffer
+ *  @param cmdno      cmd ID
+ *  @param cmdOption  cmd action
+ *  @return          0
+*/
+int libertas_cmd_802_11d_domain_info(wlan_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;
+       wlan_adapter *adapter = priv->adapter;
+       u8 nr_subband = adapter->domainreg.nr_subband;
+
+       ENTER();
+
+       lbs_pr_debug(1, "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_dbg_hex("11D: 802_11D_DOMAIN_INFO:", (u8 *) cmd,
+                       (int)(cmd->size));
+               LEAVE();
+               return 0;
+       }
+
+       domain->header.type = cpu_to_le16(TLV_TYPE_DOMAIN);
+       memcpy(domain->countrycode, adapter->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, adapter->domainreg.subband,
+                      nr_subband * sizeof(struct ieeetypes_subbandset));
+
+               cmd->size = cpu_to_le16(sizeof(pdomaininfo->action) +
+                                            domain->header.len +
+                                            sizeof(struct mrvlietypesheader) +
+                                            S_DS_GEN);
+       } else {
+               cmd->size =
+                   cpu_to_le16(sizeof(pdomaininfo->action) + S_DS_GEN);
+       }
+
+       lbs_dbg_hex("11D:802_11D_DOMAIN_INFO:", (u8 *) cmd, (int)(cmd->size));
+
+       LEAVE();
+
+       return 0;
+}
+
+/**
+ *  @brief This function implements private cmd: enable/disable 11D
+ *  @param priv    pointer to wlan_private
+ *  @param wrq     pointer to user data
+ *  @return       0 or -1
+ */
+int libertas_cmd_enable_11d(wlan_private * priv, struct iwreq *wrq)
+{
+       int data = 0;
+       int *val;
+
+       ENTER();
+       data = SUBCMD_DATA(wrq);
+
+       lbs_pr_debug(1, "enable 11D: %s\n",
+              (data == 1) ? "enable" : "Disable");
+
+       wlan_enable_11d(priv, data);
+       val = (int *)wrq->u.name;
+       *val = priv->adapter->enable11d;
+
+       LEAVE();
+       return 0;
+}
+
+/**
+ *  @brief This function parses countryinfo from AP and download country info to FW
+ *  @param priv    pointer to wlan_private
+ *  @param resp    pointer to command response buffer
+ *  @return       0; -1
+ */
+int libertas_ret_802_11d_domain_info(wlan_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;
+
+       ENTER();
+
+       lbs_dbg_hex("11D DOMAIN Info Rsp Data:", (u8 *) resp,
+               (int)le16_to_cpu(resp->size));
+
+       nr_subband = (domain->header.len - 3) / sizeof(struct ieeetypes_subbandset);
+       /* countrycode 3 bytes */
+
+       lbs_pr_debug(1, "11D Domain Info Resp: nr_subband=%d\n", nr_subband);
+
+       if (nr_subband > MRVDRV_MAX_SUBBAND_802_11D) {
+               lbs_pr_debug(1, "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_pr_debug(1, "Invalid action:%d\n", domaininfo->action);
+               ret = -1;
+               break;
+       }
+
+       LEAVE();
+       return ret;
+}
+
+/**
+ *  @brief This function parses countryinfo from AP and download country info to FW
+ *  @param priv    pointer to wlan_private
+ *  @return       0; -1
+ */
+int libertas_parse_dnld_countryinfo_11d(wlan_private * priv)
+{
+       int ret;
+       wlan_adapter *adapter = priv->adapter;
+
+       ENTER();
+       if (priv->adapter->enable11d) {
+               memset(&adapter->parsed_region_chan, 0,
+                      sizeof(struct parsed_region_chan_11d));
+               ret = parse_domain_info_11d(&adapter->pattemptedbssdesc->
+                                              countryinfo, 0,
+                                              &adapter->parsed_region_chan);
+
+               if (ret == -1) {
+                       lbs_pr_debug(1, "11D: Err Parse domain_info from AP..\n");
+                       LEAVE();
+                       return ret;
+               }
+
+               memset(&adapter->domainreg, 0,
+                      sizeof(struct wlan_802_11d_domain_reg));
+               generate_domain_info_11d(&adapter->parsed_region_chan,
+                                     &adapter->domainreg);
+
+               ret = set_domain_info_11d(priv);
+
+               if (ret) {
+                       lbs_pr_debug(1, "11D: Err set domainInfo to FW\n");
+                       LEAVE();
+                       return ret;
+               }
+       }
+       LEAVE();
+       return 0;
+}
+
+/**
+ *  @brief This function generates 11D info from user specified regioncode and download to FW
+ *  @param priv    pointer to wlan_private
+ *  @return       0; -1
+ */
+int libertas_create_dnld_countryinfo_11d(wlan_private * priv)
+{
+       int ret;
+       wlan_adapter *adapter = priv->adapter;
+       struct region_channel *region_chan;
+       u8 j;
+
+       ENTER();
+       lbs_pr_debug(1, "11D:curbssparams.band[%d]\n", adapter->curbssparams.band);
+
+       if (priv->adapter->enable11d) {
+               /* update parsed_region_chan_11; dnld domaininf to FW */
+
+               for (j = 0; j < sizeof(adapter->region_channel) /
+                    sizeof(adapter->region_channel[0]); j++) {
+                       region_chan = &adapter->region_channel[j];
+
+                       lbs_pr_debug(1, "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 != adapter->curbssparams.band)
+                               continue;
+                       break;
+               }
+
+               if (j >= sizeof(adapter->region_channel) /
+                   sizeof(adapter->region_channel[0])) {
+                       lbs_pr_debug(1, "11D:region_chan not found. band[%d]\n",
+                              adapter->curbssparams.band);
+                       LEAVE();
+                       return -1;
+               }
+
+               memset(&adapter->parsed_region_chan, 0,
+                      sizeof(struct parsed_region_chan_11d));
+               wlan_generate_parsed_region_chan_11d(region_chan,
+                                                    &adapter->
+                                                    parsed_region_chan);
+
+               memset(&adapter->domainreg, 0,
+                      sizeof(struct wlan_802_11d_domain_reg));
+               generate_domain_info_11d(&adapter->parsed_region_chan,
+                                        &adapter->domainreg);
+
+               ret = set_domain_info_11d(priv);
+
+               if (ret) {
+                       lbs_pr_debug(1, "11D: Err set domainInfo to FW\n");
+                       LEAVE();
+                       return ret;
+               }
+
+       }
+
+       LEAVE();
+       return 0;
+}
diff --git a/drivers/net/wireless/libertas/11d.h b/drivers/net/wireless/libertas/11d.h
new file mode 100644 (file)
index 0000000..db2ebea
--- /dev/null
@@ -0,0 +1,105 @@
+/**
+  * This header file contains data structures and
+  * function declarations of 802.11d
+  */
+#ifndef _WLAN_11D_
+#define _WLAN_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 {
+       u16 action;
+       struct mrvlietypes_domainparamset domain;
+} __attribute__ ((packed));
+
+/** domain regulatory information */
+struct wlan_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;
+};
+
+u8 libertas_get_scan_type_11d(u8 chan,
+                         struct parsed_region_chan_11d *parsed_region_chan);
+
+u32 libertas_chan_2_freq(u8 chan, u8 band);
+
+enum state_11d libertas_get_state_11d(wlan_private * priv);
+
+void libertas_init_11d(wlan_private * priv);
+
+int libertas_set_universaltable(wlan_private * priv, u8 band);
+
+int libertas_cmd_802_11d_domain_info(wlan_private * priv,
+                                struct cmd_ds_command *cmd, u16 cmdno,
+                                u16 cmdOption);
+
+int libertas_cmd_enable_11d(wlan_private * priv, struct iwreq *wrq);
+
+int libertas_ret_802_11d_domain_info(wlan_private * priv,
+                                struct cmd_ds_command *resp);
+
+int libertas_parse_dnld_countryinfo_11d(wlan_private * priv);
+
+int libertas_create_dnld_countryinfo_11d(wlan_private * priv);
+
+#endif                         /* _WLAN_11D_ */
diff --git a/drivers/net/wireless/libertas/LICENSE b/drivers/net/wireless/libertas/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/drivers/net/wireless/libertas/Makefile b/drivers/net/wireless/libertas/Makefile
new file mode 100644 (file)
index 0000000..19c9350
--- /dev/null
@@ -0,0 +1,21 @@
+# EXTRA_CFLAGS += -Wpacked
+
+usb8xxx-objs := main.o fw.o wext.o \
+               rx.o tx.o cmd.o           \
+               cmdresp.o scan.o          \
+               join.o 11d.o              \
+               ioctl.o debugfs.o         \
+               ethtool.o assoc.o
+
+ifeq ($(CONFIG_LIBERTAS_USB_DEBUG), y)
+EXTRA_CFLAGS += -DDEBUG -DPROC_DEBUG
+endif
+
+
+# This is needed to support the newer boot2 bootloader (v >= 3104)
+EXTRA_CFLAGS += -DSUPPORT_BOOT_COMMAND
+usb8xxx-objs += if_bootcmd.o
+usb8xxx-objs += if_usb.o
+
+obj-$(CONFIG_LIBERTAS_USB) += usb8xxx.o
+
diff --git a/drivers/net/wireless/libertas/README b/drivers/net/wireless/libertas/README
new file mode 100644 (file)
index 0000000..688da4c
--- /dev/null
@@ -0,0 +1,1044 @@
+================================================================================
+                       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]
+
+=====================
+IWPRIV COMMAND
+=====================
+
+NAME
+       This manual describes the usage of private commands used in Marvell WLAN
+       Linux Driver. All the commands available in Wlanconfig will not be available
+       in the iwpriv.
+
+SYNOPSIS
+       iwpriv <ethX> <command> [sub-command] ...
+
+       iwpriv ethX version
+       iwpriv ethX scantype [sub-command]
+       iwpriv ethX getSNR <n>
+       iwpriv ethX getNF <n>
+       iwpriv ethX getRSSI <n>
+       iwpriv ethX setrxant <n>
+       iwpriv ethX getrxant
+       iwpriv ethX settxant <n>
+       iwpriv ethX gettxant
+       iwpriv ethX authalgs <n>
+       iwpriv ethX pre-TBTT <n>
+       iwpriv ethX 8021xauthalgs <n>
+       iwpriv ethX encryptionmode <n>
+       iwpriv ethX setregioncode <n>
+       iwpriv ethX getregioncode
+       iwpriv ethX setbcnavg <n>
+       iwpriv ethX getbcnavg
+       iwpriv ethX setdataavg <n>
+       iwpriv ethX setlisteninter <n>
+       iwpriv ethX getlisteninter
+       iwpriv ethX setmultipledtim <n>
+       iwpriv ethX getmultipledtim
+       iwpriv ethX atimwindow <n>
+       iwpriv ethX deauth
+       iwpriv ethX adhocstop
+       iwpriv ethX radioon
+       iwpriv ethX radiooff
+       iwpriv ethX reasso-on
+       iwpriv ethX reasso-off
+       iwpriv ethX scanmode  [sub-command]
+       iwpriv ethX setwpaie <n>
+       iwpriv ethX wlanidle-off
+       iwpriv ethX wlanidle-on
+       iwpriv ethX getcis
+       iwpriv ethX getlog
+       iwpriv ethX getadhocstatus
+       iwpriv ethX adhocgrate <n>
+
+Version 4 Command:
+       iwpriv ethX inactvityto <n>
+       iwpriv ethX sleeppd <n>
+       iwpriv ethX enable11d <n>
+       iwpriv ethX tpccfg <n>
+       iwpriv ethX powercfg <n>
+       iwpriv ethX setafc <n>
+       iwpriv ethX getafc
+
+Version 5 Command:
+       iwpriv ethX ledgpio <n>
+       iwpriv ethX scanprobes <n>
+       iwpriv ethX lolisteninter <n>
+       iwpriv ethX rateadapt <n> <m>
+       iwpriv ethX txcontrol <n>
+       iwpriv ethX psnullinterval <n>
+       iwpriv ethX prescan <n>
+       iwpriv ethX getrxinfo
+       iwpriv ethX gettxrate
+       iwpriv ethX beaconinterval
+
+BT Commands:
+       The blinding table (BT) contains a list of mac addresses that should be
+       ignored by the firmware.  It is primarily used for debugging and
+       testing networks.  It can be edited and inspected with the following
+       commands:
+
+       iwpriv ethX bt_reset
+       iwpriv ethX bt_add <mac_address>
+       iwpriv ethX bt_del <mac_address>
+       iwpriv ethX bt_list <id>
+
+FWT Commands:
+       The forwarding table (FWT) is a feature used to manage mesh network
+       routing in the firmware.  The FWT is essentially a routing table that
+       associates a destination mac address (da) with a next hop receiver
+       address (ra).  The FWT can be inspected and edited with the following
+       iwpriv commands, which are described in greater detail below.
+       Eventually, the table will be automatically maintained by a custom
+       routing protocol.
+
+       NOTE: FWT commands replace the previous DFT commands.  What were the DFT
+       commands?, you might ask.  They were an earlier API to the firmware that
+       implemented a simple MAC-layer forwarding mechanism.  In the unlikely
+       event that you were using these commands, you must migrate to the new
+       FWT commands which can be used to achieve the same functionality.
+
+       iwpriv ethX fwt_add [parameters]
+       iwpriv ethX fwt_del [parameters]
+       iwpriv ethX fwt_lookup [parameters]
+       iwpriv ethX fwt_list [parameters]
+       iwpriv ethX fwt_list_route [parameters]
+       iwpriv ethX fwt_list_neigh [parameters]
+       iwpriv ethX fwt_reset [parameters]
+       iwpriv ethX fwt_cleanup
+       iwpriv ethX fwt_time
+
+MESH Commands:
+
+       The MESH commands are used to configure various features of the mesh
+       routing protocol.  The following commands are supported:
+
+       iwpriv ethX mesh_get_ttl
+       iwpriv ethX mesh_set_ttl ttl
+
+DESCRIPTION
+       Those commands are used to send additional commands to the Marvell WLAN
+       card via the Linux device driver.
+
+       The ethX parameter specifies the network device that is to be used to
+               perform this command on. it could be eth0, eth1 etc.
+
+version
+       This is used to get the current version of the driver and the firmware.
+
+scantype
+       This command is used to set the scan type to be used by the driver in
+       the scan command. This setting will not be used while performing a scan
+       for a specific SSID, as it is always done with scan type being active.
+
+       where the sub-commands are: -
+                       active  -- to set the scan type to active
+                       passive -- to set the scan type to passive
+                       get     -- to get the scan type set in the driver
+
+getSNR
+       This command gets the average and non average value of Signal to Noise
+       Ratio of Beacon and Data.
+
+       where value is:-
+                       0       -- Beacon non-average.
+                       1       -- Beacon average.
+                       2       -- Data non-average.
+                       3       -- Data average.
+
+       If no value is given, all four values are returned in the order mentioned
+       above.
+
+       Note: This command is available only when STA is connected.
+
+getRSSI
+       This command gets the average and non average value os Receive Signal
+       Strength of Beacon and Data.
+
+       where value is:-
+                       0       -- Beacon non-average.
+                       1       -- Beacon average.
+                       2       -- Data non-average.
+                       3       -- Data average.
+
+       Note: This command is available only when STA is connected.
+
+getNF
+       This command gets the average and non average value of Noise Floor of
+       Beacon and Data.
+
+       where value is:-
+                       0       -- Beacon non-average.
+                       1       -- Beacon average.
+                       2       -- Data non-average.
+                       3       -- Data average.
+
+       Note: This command is available only when STA is connected.
+
+setrxant
+       This command is used to set the mode for Rx antenna.
+
+       The options that can be sent are:-
+                       1       -- Antenna 1.
+                       2       -- Antenna 2.
+                       0xFFFF  -- Diversity.
+
+       Usage:
+               iwpriv ethX setrxant 0x01: select Antenna 1.
+
+getrxant
+       This command is used to get the mode for Rx antenna.
+
+
+settxant
+       This command is used to set the mode for Tx antenna.
+               The options that can be sent are:-
+                       1       -- Antenna 1.
+                       2       -- Antenna 2.
+                       0xFFFF  -- Diversity.
+       Usage:
+               iwpriv ethX settxant 0x01: select Antenna 1.
+
+gettxant
+       This command is used to get the mode for Tx antenna.
+
+authalgs
+       This command is used by the WPA supplicant to set the authentication
+       algorithms in the station.
+
+8021xauthalgs
+       This command is used by the WPA supplicant to set the 8021.x authentication algorithm type
+       station.
+
+       where values can be:-
+                       1       -- None
+                       2       -- LEAP
+                       4       -- TLS
+                       8       -- TTLs
+                       16      -- MD5
+
+
+encryptionmode
+       This command is used by the WPA supplicant to set the encryption algorithm.
+
+       where values can be:-
+                       0       -- NONE
+                       1       -- WEP40
+                       2       -- TKIP
+                       3       -- CCMP
+                       4       -- WEP104
+
+pre-TBTT
+       This command is used to set pre-TBTT time period where value is in microseconds.
+
+setregioncode
+       This command is used to set the region code in the station.
+       where value is 'region code' for various regions like
+       USA FCC, Canada IC, Spain, France, Europe ETSI, Japan ...
+
+       Usage:
+               iwpriv ethX setregioncode 0x10: set region code to USA (0x10).
+
+getregioncode
+       This command is used to get the region code information set in the
+       station.
+
+setbcnavg
+       Set the weighting factor for calculating RSSI.
+
+getbcnavg
+       Get weighting factor for calculating RSSI.
+
+setdataavg
+       Set the weighting factor for calculating SNR.
+
+setlisteninter
+       This command is used to set the listen interval in the
+       station.
+
+       where the value ranges between 1 - 255
+
+getlisteninter
+       This command is used to get the listen interval value set in the
+       station.
+
+setmultipledtim
+       This command is used to set the multiple dtim value in the
+       station.
+               where the value is 1,2,3,4,5,0xfffe
+               0xfffe means the firmware will use listen interval in association
+               command for waking up
+
+getmultipledtim
+       This command is used to get the multiple dtim value set in the station.
+
+atimwindow
+       This command is used to set the atim value in the
+       station.
+
+       where the value ranges between 0 - 50
+
+deauth
+       This command is used to send the de-authentication to the AP with which
+       the station is associated. This command is valid only when
+       station is in Infrastructure mode.
+
+       Note: This command is available only when STA is connected.
+
+adhocstop
+       This command is used to stop beacon transmission from the station and
+       go into idle state in ad-hoc mode.
+
+       Note: This command is available only when STA is connected.
+
+radioon
+       This command is used to turn on the RF antenna.
+
+radiooff
+       This command is sued to turn off the RF antenna.
+
+scanmode
+       This command is used to set the station to scan for either IBSS
+       networks or BSS networks or both BSS and IBSS networks. This
+       command can be used with sub commands,
+
+       where the value for
+                       bss     -- Scan All the BSS networks.
+                       ibss    -- Scan All the IBSS networks.
+                       any     -- Scan both BSS and IBSS networks.
+
+
+
+setwpaie
+       This command is used by WPA supplicant to send the WPA-IE to the driver.
+
+wlanidle-off
+       This command is used to get into idle state.
+
+       Note: This command is available only when STA is connected.
+
+wlanidle-on
+       This command is used to get off the idle state.
+
+       Note: This command is available only when STA is connected.
+
+
+getlog
+       This command is used to get the 802.11 statistics available in the
+               station.
+
+       Note: This command is available only when STA is connected.
+
+getadhocstatus
+       This command is used to get the ad-hoc Network Status.
+
+       The various status codes are:
+               AdhocStarted
+               AdhocJoined
+               AdhocIdle
+               InfraMode
+               AutoUnknownMode
+
+       Note: This command is available only when STA is connected.
+
+adhocgrate
+       This command is used to enable(1) g_rate, Disable(0) g_rate
+       and request(2) the status which g_rate is disabled/enabled,
+       for Ad-hoc creator.
+
+       where value is:-
+               0       -- Disabled
+               1       -- Enabled
+               2       -- Get
+
+ledgpio
+       This command is used to set/get LEDs.
+
+       iwpriv ethX ledgpio <LEDs>
+               will set the corresponding LED for the GPIO Line.
+
+       iwpriv ethX ledgpio
+               will give u which LEDs are Enabled.
+
+       Usage:
+               iwpriv eth1 ledgpio 1 0 2 1 3 4
+                       will enable
+                       LED 1 -> GPIO 0
+                       LED 2 -> GPIO 1
+                       LED 3 -> GPIO 4
+
+               iwpriv eth1 ledgpio
+                       shows LED information in the format as mentioned above.
+
+       Note: LED0 is invalid
+       Note: Maximum Number of LEDs are 16.
+
+inactivityto
+       This command is used by the host to set/get the inactivity timeout value,
+       which specifies when WLAN device is put to sleep.
+
+       Usage:
+               iwpriv ethX inactivityto [<timeout>]
+
+       where the parameter are:
+               timeout: timeout value in milliseconds.
+
+       Example:
+               iwpriv eth1 inactivityto
+                       "get the timeout value"
+
+               iwpriv eth1 inactivityto X
+                       "set timeout value to X ms"
+
+
+sleeppd
+       This command is used to configure the sleep period of the WLAN device.
+
+       Usage:
+               iwpriv ethX sleeppd [<sleep period>]
+
+       where the parameter are:
+               Period: sleep period in milliseconds. Range 10~60.
+
+       Example:
+               iwpriv eth1 sleeppd 10
+                       "set period as 10 ms"
+               iwpriv eth1 sleeppd
+                       "get the sleep period configuration"
+
+enable11d
+       This command is used to control 11d
+       where value is:-
+               1       -- Enabled
+               0       -- Disabled
+               2       -- Get
+
+
+
+
+tpccfg
+       Enables or disables automatic transmit power control.
+
+       The first parameter turns this feature on (1) or off (0).  When turning
+       on, the user must also supply four more parameters in the following
+       order:
+               -UseSNR (Use SNR (in addition to PER) for TPC algorithm),
+               -P0 (P0 power level for TPC),
+               -P1 (P1 power level for TPC),
+               -P2 (P2 power level for TPC).
+
+       Usage:
+               iwpriv ethX tpccfg: Get current configuration
+               iwpriv ethX tpccfg 0: disable auto TPC
+               iwpriv ethX tpccfg 0x01 0x00 0x05 0x0a 0x0d: enable auto TPC; do not use SNR;
+                                                            P0=0x05; P1=0x0a; P2=0x0d;
+               iwpriv ethX tpccfg 0x01 0x01 0x05 0x0a 0x0d: enable auto TPC; use SNR;
+                                                            P0=0x05; P1=0x0a; P2=0x0d.
+
+powercfg
+       Enables or disables power adaptation.
+
+       The first parameter turns this feature on (1) or off (0).  When turning
+       on, the user must also supply three more parameters in the following
+       order:
+               -P0 (P0 power level for Power Adaptation),
+               -P1 (P1 power level for Power Adaptation),
+               -P2 (P2 power level for Power Adaptation).
+
+       Usage:
+               iwpriv ethX powercfg: Get current configuration
+               iwpriv ethX powercfg 0: disable power adaptation
+               iwpriv ethX powercfg 1 0x0d 0x0f 0x12: enable power adaptation;
+                                                      P0=0x0d; P1=0x0f; P2=0x12.
+
+getafc
+       This command returns automatic frequency control parameters.  It returns
+       three integers:
+               -P0: automatic is on (1), or off (0),
+               -P1: current timing offset in PPM (part per million), and
+               -P2: current frequency offset in PPM.
+
+setafc
+       Set automatic frequency control options.
+
+       The first parameter turns automatic on (1) or off (0).
+       The user must supply two more parameters in either case, in the following
+  order:
+
+  When auto is on:
+
+               -P0 (automatic adjustment frequency threshold in PPM),
+               -P1 (automatic adjustment period in beacon period),
+
+  When auto is off:
+
+               -P0 (manual adjustment timing offset in PPM), and
+               -P1 (manual adjustment frequency offset in PPM).
+
+       Usage:
+               iwpriv ethX setafc 0 10 10: manual adjustment, both timing and frequcncy
+    offset are 10 PPM.
+
+               iwpriv ethX setafc 1 10 10 enable afc, automatic adjustment,
+    frequency threshold 10 PPM, for every 10 beacon periods.
+
+
+
+scanprobes
+       This command sets number of probe requests per channel.
+
+       Usage:
+               iwpriv ethX scanprobes 3 (set scan probes to 3)
+               iwpriv ethX scanprobes   (get scan probes)
+
+lolisteninter
+       This command sets the value of listen interval.
+
+       Usage:
+       iwpriv ethX lolisteninter 234 (set the lolisteninter to 234)
+       iwpriv ethX lolisteninter     (get the lolisteninter value)
+
+rateadapt
+       This command sets the data rates bitmap.
+       Where <n>
+               0: Disable auto rate adapt
+               1: Enable auto rate adapt
+
+             <m>
+                data rate bitmap
+                       Bit     Data rate
+                       0       1 Mbps
+                       1       2 Mbps
+                       2       5.5 Mbps
+                       3       11 Mbps
+                       4       Reserved
+                       5       6 Mbps
+                       6       9 Mbps
+                       7       12 Mbps
+                       8       18 Mbps
+                       9       24 Mbps
+                       10      36 Mbps
+                       11      48 Mbps
+                       12      54 Mbps
+                       12-15   Reserved
+
+       Usage:
+       iwpriv ethX rateadapt
+                       read the currect data rate setting
+       iwpriv ethX rateadapt 1 0x07
+                       enable auto data rate adapt and
+                       data rates are 1Mbps, 2Mbsp and 5.5Mbps
+
+
+txcontrol
+       This command is used to set the Tx rate, ack policy, and retry limit on a per packet basis.
+
+       Where value <n> is:
+           if bit[4] == 1:
+               bit[3:0]        -- 0   1   2   3   4   5   6   7   8   9   10   11   12   13-16
+               Data Rate(Mbps) -- 1   2   5.5 11  Rsv 6   9   12  18  24  36   48   54   Rsv
+
+           bit[12:8]
+               if bit[12] == 1, bit[11:8] specifies the Tx retry limit.
+
+           bit[14:13] specifies per packet ack policy:
+               bit[14:13]
+                    1  0       use immediate ack policy for this packet
+                    1  1       use no ack policy for this packet
+                    0  x       use the per-packet ack policy setting
+
+       Usage:
+       iwpriv ethX txcontrol 0x7513
+                       Use no-ack policy, 5 retires for Tx, 11Mbps rate
+
+
+
+psnullinterval
+       This command is used to set/request NULL package interval for Power Save
+       under infrastructure mode.
+
+       where value is:-
+               -1      -- Disabled
+               n>0     -- Set interval as n (seconds)
+
+prescan
+       This command is used to enable (1)/disable(0) auto prescan before assoicate to the ap
+
+       where value is:-
+               0       -- Disabled
+               1       -- Enabled
+               2       -- Get
+
+getrxinfo
+       This command gets non average value of Signal to Noise Ratio of Data and rate index.
+
+       The following table shows RateIndex and Rate
+
+                    RateIndex  Data rate
+                       0       1 Mbps
+                       1       2 Mbps
+                       2       5.5 Mbps
+                       3       11 Mbps
+                       4       Reserved
+                       5       6 Mbps
+                       6       9 Mbps
+                       7       12 Mbps
+                       8       18 Mbps
+                       9       24 Mbps
+                       10      36 Mbps
+                       11      48 Mbps
+                       12      54 Mbps
+                       13-15   Reserved
+
+gettxrate
+       This command gets current Tx rate index of the first packet associated with Rate Adaptation.
+
+       The following table shows RateIndex and Rate
+
+                    RateIndex  Data rate
+                       0       1 Mbps
+                       1       2 Mbps
+                       2       5.5 Mbps
+                       3       11 Mbps
+                       4       Reserved
+                       5       6 Mbps
+                       6       9 Mbps
+                       7       12 Mbps
+                       8       18 Mbps
+                       9       24 Mbps
+                       10      36 Mbps
+                       11      48 Mbps
+                       12      54 Mbps
+                       13-15   Reserved
+
+bcninterval
+       This command is used to sets beacon interval in adhoc mode when an argument is given, and gets current adhoc
+       beacon interval when no argument is given. The valid beacon interval is between 20 - 1000,
+       default beacon interval is 100.
+
+       Usage:
+               iwpriv ethX bcninterval 100  (set adhoc beacon interval to 100)
+               iwpriv ethX bcninterval      (get adhoc beacon interval)
+
+fwt_add
+       This command is used to insert an entry into the FWT table. The list of
+       parameters must follow the following structure:
+
+       iwpriv ethX fwt_add da ra [metric dir ssn dsn hopcount ttl expiration sleepmode snr]
+
+       The parameters between brackets are optional, but they must appear in
+       the order specified.  For example, if you want to specify the metric,
+       you must also specify the dir, ssn, and dsn but you need not specify the
+       hopcount, expiration, sleepmode, or snr.  Any unspecified parameters
+       will be assigned the defaults specified below.
+
+       The different parameters are:-
+               da              -- DA MAC address in the form 00:11:22:33:44:55
+               ra              -- RA MAC address in the form 00:11:22:33:44:55
+               metric          -- route metric (cost: smaller-metric routes are
+                                  preferred, default is 0)
+               dir             -- direction (1 for direct, 0 for reverse,
+                                  default is 1)
+               ssn             -- Source Sequence Number (time at the RA for
+                                  reverse routes.  Default is 0)
+               dsn             -- Destination Sequence Number (time at the DA
+                                  for direct routes.  Default is 0)
+               hopcount        -- hop count (currently unused, default is 0)
+               ttl             -- TTL (Only used in reverse entries)
+               expiration      -- entry expiration (in ticks, where a tick is
+                                  1024us, or ~ 1ms. Use 0 for an indefinite
+                                  entry, default is 0)
+               sleepmode       -- RA's sleep mode (currently unused, default is
+                                  0)
+               snr             -- SNR in the link to RA (currently unused,
+                                  default is 0)
+
+       The command does not return anything.
+
+fwt_del
+       This command is used to remove an entry to the FWT table. The list of
+       parameters must follow the following structure:
+
+               iwpriv ethX fwt_del da ra [dir]
+
+       where the different parameters are:-
+               da              -- DA MAC address (in the form "00:11:22:33:44:55")
+               ra              -- RA MAC address (in the form "00:11:22:33:44:55")
+               dir             -- direction (1 for direct, 0 for reverse,
+                                  default is 1)
+
+       The command does not return anything.
+
+fwt_lookup
+       This command is used to get the best route in the FWT table to a given
+       host. The only parameter is the MAC address of the host that is being
+       looked for.
+
+               iwpriv ethX fwt_lookup da
+
+       where:-
+               da              -- DA MAC address (in the form "00:11:22:33:44:55")
+
+       The command returns an output string identical to the one returned by
+       fwt_list described below.
+
+
+fwt_list
+       This command is used to list a route from the FWT table. The only
+       parameter is the index into the table. If you want to list all the
+       routes in a table, start with index=0, and keep listing until you get a
+       "(null)" string.  Note that the indicies may change as the fwt is
+       updated.  It is expected that most users will not use fwt_list directly,
+       but that a utility similar to the traditional route command will be used
+       to invoke fwt_list over and over.
+
+               iwpriv ethX fwt_list index
+
+       The output is a string of the following form:
+
+               da ra metric dir ssn dsn hopcount ttl expiration sleepmode snr
+
+       where the different fields are:-
+               da              -- DA MAC address (in the form "00:11:22:33:44:55")
+               ra              -- RA MAC address (in the form "00:11:22:33:44:55")
+               metric          -- route metric (cost: smaller-metric routes are preferred)
+               dir             -- direction (1 for direct, 0 for reverse)
+               ssn             -- Source Sequence Number (time at the RA for reverse routes)
+               dsn             -- Destination Sequence Number (time at the DA for direct routes)
+               hopcount        -- hop count (currently unused)
+               ttl             -- TTL (only used in reverse entries)
+               expiration      -- entry expiration (in ticks, where a tick is 1024us, or ~ 1ms. Use 0 for an indefinite entry)
+               sleepmode       -- RA's sleep mode (currently unused)
+               snr             -- SNR in the link to RA (currently unused)
+
+fwt_list_route
+       This command is used to list a route from the FWT table. The only
+       parameter is the route ID. If you want to list all the routes in a
+       table, start with rid=0, and keep incrementing rid until you get a
+       "(null)" string. This function is similar to fwt_list. The only
+       difference is the output format.  Also note that this command is meant
+       for debugging.  It is expected that users will use fwt_lookup and
+       fwt_list.  One important reason for this is that the route id may change
+       as the route table is altered.
+
+               iwpriv ethX fwt_list_route rid
+
+       The output is a string of the following form:
+
+               da metric dir nid ssn dsn hopcount ttl expiration
+
+       where the different fields are:-
+               da              -- DA MAC address (in the form "00:11:22:33:44:55")
+               metric          -- route metric (cost: smaller-metric routes are preferred)
+               dir             -- direction (1 for direct, 0 for reverse)
+               nid             -- Next-hop (neighbor) host ID (nid)
+               ssn             -- Source Sequence Number (time at the RA for reverse routes)
+               dsn             -- Destination Sequence Number (time at the DA for direct routes)
+               hopcount        -- hop count (currently unused)
+               ttl             -- TTL count (only used in reverse entries)
+               expiration      -- entry expiration (in ticks, where a tick is 1024us, or ~ 1ms. Use 0 for an indefinite entry)
+
+fwt_list_neigh
+       This command is used to list a neighbor from the FWT table. The only
+       parameter is the neighbor ID. If you want to list all the neighbors in a
+       table, start with nid=0, and keep incrementing nid until you get a
+       "(null)" string.  Note that the nid from a fwt_list_route command can be
+       used as an input to this command.  Also note that this command is meant
+       mostly for debugging.  It is expected that users will use fwt_lookup.
+       One important reason for this is that the neighbor id may change as the
+       neighbor table is altered.
+
+               iwpriv ethX fwt_list_neigh nid
+
+       The output is a string of the following form:
+
+               ra sleepmode snr references
+
+       where the different fields are:-
+               ra              -- RA MAC address (in the form "00:11:22:33:44:55")
+               sleepmode       -- RA's sleep mode (currently unused)
+               snr             -- SNR in the link to RA (currently unused)
+               references      -- RA's reference counter
+
+fwt_reset
+       This command is used to reset the FWT table, getting rid of all the
+       entries. There are no input parameters.
+
+               iwpriv ethX fwt_reset
+
+       The command does not return anything.
+
+fwt_cleanup
+       This command is used to perform user-based garbage recollection. The
+       FWT table is checked, and all the entries that are expired or invalid
+       are cleaned. Note that this is exported to the driver for debugging
+       purposes, as garbage collection is also fired by the firmware when in
+       space problems. There are no input parameters.
+
+               iwpriv ethX fwt_cleanup
+
+       The command does returns the number of invalid/expired routes deleted.
+
+fwt_time
+       This command returns a card's internal time representation.  It is this
+       time that is used to represent the expiration times of FWT entries.  The
+       number is not consistent from card to card; it is simply a timer count.
+       The fwt_time command is used to inspect the timer so that expiration
+       times reported by fwt_list can be properly interpreted.
+
+               iwpriv ethX fwt_time
+
+mesh_get_ttl
+
+       The mesh ttl is the number of hops a mesh packet can traverse before it
+       is dropped.  This parameter is used to prevent infinite loops in the
+       mesh network.  The value returned by this function is the ttl assigned
+       to all mesh packets.  Currently there is no way to control the ttl on a
+       per packet or per socket basis.
+
+       iwpriv ethX mesh_get_ttl
+
+mesh_set_ttl ttl
+
+       Set the ttl.  The argument must be between 0 and 255.
+
+       iwpriv ethX mesh_set_ttl <ttl>
+
+=========================
+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]:
+
+      chan=[chan#][band][mode] where band is [a,b,g] and mode is
+                               blank for active or 'p' for passive
+      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
+      probes=[#]               number of probe requests to send on each chan
+      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 the chan token is absent, a full channel scan will be completed by
+      the driver.  If the dur or probes 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 an active scan on channels 1, 6, and 11 in the 'g' band:
+            echo "chan=1g,6g,11g" > setuserscan
+
+    2) Perform a passive scan on channel 11 for 20 ms:
+            echo "chan=11gp dur=20" > setuserscan
+
+    3) Perform an active scan on channels 1, 6, and 11; and a passive scan on
+       channel 36 in the 'a' band:
+
+            echo "chan=1g,6g,11g,36ap" > setuserscan
+
+    4) Perform an active scan on channel 6 and 36 for a specific SSID:
+            echo "chan=6g,36a ssid="TestAP"" > setuserscan
+
+    5) 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
+
+    6) Scan channel 6, for all infrastructure networks, sending two probe
+       requests.  Keep the previous scan table intact. Update any duplicate
+       BSSID/SSID matches with the new scan data:
+            echo "chan=6g type=1 probes=2 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/drivers/net/wireless/libertas/assoc.c b/drivers/net/wireless/libertas/assoc.c
new file mode 100644 (file)
index 0000000..b55c7f5
--- /dev/null
@@ -0,0 +1,588 @@
+/* Copyright (C) 2006, Red Hat, Inc. */
+
+#include <linux/bitops.h>
+#include <net/ieee80211.h>
+
+#include "assoc.h"
+#include "join.h"
+#include "decl.h"
+#include "hostcmd.h"
+#include "host.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(wlan_private *priv,
+                              struct assoc_request * assoc_req)
+{
+       wlan_adapter *adapter = priv->adapter;
+       int ret = 0;
+       int i;
+
+       ENTER();
+
+       lbs_pr_debug(1, "New SSID requested: %s\n", assoc_req->ssid.ssid);
+       if (assoc_req->mode == wlan802_11infrastructure) {
+               if (adapter->prescan) {
+                       libertas_send_specific_SSID_scan(priv, &assoc_req->ssid, 1);
+               }
+
+               i = libertas_find_SSID_in_list(adapter, &assoc_req->ssid,
+                               NULL, wlan802_11infrastructure);
+               if (i >= 0) {
+                       lbs_pr_debug(1,
+                              "SSID found in scan list ... associating...\n");
+
+                       ret = wlan_associate(priv, &adapter->scantable[i]);
+                       if (ret == 0) {
+                               memcpy(&assoc_req->bssid,
+                                      &adapter->scantable[i].macaddress,
+                                      ETH_ALEN);
+                       }
+               } else {
+                       lbs_pr_debug(1, "SSID '%s' not found; cannot associate\n",
+                               assoc_req->ssid.ssid);
+               }
+       } else if (assoc_req->mode == wlan802_11ibss) {
+               /* Scan for the network, do not save previous results.  Stale
+                *   scan data will cause us to join a non-existant adhoc network
+                */
+               libertas_send_specific_SSID_scan(priv, &assoc_req->ssid, 0);
+
+               /* Search for the requested SSID in the scan table */
+               i = libertas_find_SSID_in_list(adapter, &assoc_req->ssid, NULL,
+                               wlan802_11ibss);
+               if (i >= 0) {
+                       lbs_pr_debug(1, "SSID found at %d in List, so join\n", ret);
+                       libertas_join_adhoc_network(priv, &adapter->scantable[i]);
+               } else {
+                       /* else send START command */
+                       lbs_pr_debug(1, "SSID not found in list, so creating adhoc"
+                               " with SSID '%s'\n", assoc_req->ssid.ssid);
+                       libertas_start_adhoc_network(priv, &assoc_req->ssid);
+               }
+               memcpy(&assoc_req->bssid, &adapter->current_addr, ETH_ALEN);
+       }
+
+       LEAVE();
+       return ret;
+}
+
+
+static int assoc_helper_bssid(wlan_private *priv,
+                              struct assoc_request * assoc_req)
+{
+       wlan_adapter *adapter = priv->adapter;
+       int i, ret = 0;
+
+       ENTER();
+
+       lbs_pr_debug(1, "ASSOC: WAP: BSSID = " MAC_FMT "\n",
+               MAC_ARG(assoc_req->bssid));
+
+       /* Search for index position in list for requested MAC */
+       i = libertas_find_BSSID_in_list(adapter, assoc_req->bssid,
+                           assoc_req->mode);
+       if (i < 0) {
+               lbs_pr_debug(1, "ASSOC: WAP: BSSID " MAC_FMT " not found, "
+                       "cannot associate.\n", MAC_ARG(assoc_req->bssid));
+               goto out;
+       }
+
+       if (assoc_req->mode == wlan802_11infrastructure) {
+               ret = wlan_associate(priv, &adapter->scantable[i]);
+               lbs_pr_debug(1, "ASSOC: return from wlan_associate(bssd) was %d\n", ret);
+       } else if (assoc_req->mode == wlan802_11ibss) {
+               libertas_join_adhoc_network(priv, &adapter->scantable[i]);
+       }
+       memcpy(&assoc_req->ssid, &adapter->scantable[i].ssid,
+               sizeof(struct WLAN_802_11_SSID));
+
+out:
+       LEAVE();
+       return ret;
+}
+
+
+static int assoc_helper_associate(wlan_private *priv,
+                                  struct assoc_request * assoc_req)
+{
+       int ret = 0, done = 0;
+
+       /* If we're given and 'any' BSSID, try associating based on SSID */
+
+       if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) {
+               if (memcmp(bssid_any, assoc_req->bssid, ETH_ALEN)
+                   && memcmp(bssid_off, assoc_req->bssid, ETH_ALEN)) {
+                       ret = assoc_helper_bssid(priv, assoc_req);
+                       done = 1;
+                       if (ret) {
+                               lbs_pr_debug(1, "ASSOC: bssid: ret = %d\n", ret);
+                       }
+               }
+       }
+
+       if (!done && test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
+               ret = assoc_helper_essid(priv, assoc_req);
+               if (ret) {
+                       lbs_pr_debug(1, "ASSOC: bssid: ret = %d\n", ret);
+               }
+       }
+
+       return ret;
+}
+
+
+static int assoc_helper_mode(wlan_private *priv,
+                             struct assoc_request * assoc_req)
+{
+       wlan_adapter *adapter = priv->adapter;
+       int ret = 0;
+
+       ENTER();
+
+       if (assoc_req->mode == adapter->inframode) {
+               LEAVE();
+               return 0;
+       }
+
+       if (assoc_req->mode == wlan802_11infrastructure) {
+               if (adapter->psstate != PS_STATE_FULL_POWER)
+                       libertas_ps_wakeup(priv, cmd_option_waitforrsp);
+               adapter->psmode = wlan802_11powermodecam;
+       }
+
+       adapter->inframode = assoc_req->mode;
+       ret = libertas_prepare_and_send_command(priv,
+                                   cmd_802_11_snmp_mib,
+                                   0, cmd_option_waitforrsp,
+                                   OID_802_11_INFRASTRUCTURE_MODE,
+                                   (void *) assoc_req->mode);
+
+       LEAVE();
+       return ret;
+}
+
+
+static int assoc_helper_wep_keys(wlan_private *priv,
+                                 struct assoc_request * assoc_req)
+{
+       wlan_adapter *adapter = priv->adapter;
+       int i;
+       int ret = 0;
+
+       ENTER();
+
+       /* 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 = libertas_prepare_and_send_command(priv,
+                                           cmd_802_11_set_wep,
+                                           cmd_act_add,
+                                           cmd_option_waitforrsp,
+                                           0, assoc_req);
+       } else {
+               ret = libertas_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.WEPstatus == wlan802_11WEPenabled)
+               adapter->currentpacketfilter |= cmd_act_mac_wep_enable;
+       else
+               adapter->currentpacketfilter &= ~cmd_act_mac_wep_enable;
+       ret = libertas_set_mac_packet_filter(priv);
+       if (ret)
+               goto out;
+
+       mutex_lock(&adapter->lock);
+
+       /* Copy WEP keys into adapter wep key fields */
+       for (i = 0; i < 4; i++) {
+               memcpy(&adapter->wep_keys[i], &assoc_req->wep_keys[i],
+                       sizeof(struct WLAN_802_11_KEY));
+       }
+       adapter->wep_tx_keyidx = assoc_req->wep_tx_keyidx;
+
+       mutex_unlock(&adapter->lock);
+
+out:
+       LEAVE();
+       return ret;
+}
+
+static int assoc_helper_secinfo(wlan_private *priv,
+                                struct assoc_request * assoc_req)
+{
+       wlan_adapter *adapter = priv->adapter;
+       int ret = 0;
+
+       ENTER();
+
+       memcpy(&adapter->secinfo, &assoc_req->secinfo,
+               sizeof(struct wlan_802_11_security));
+
+       ret = libertas_set_mac_packet_filter(priv);
+
+       LEAVE();
+       return ret;
+}
+
+
+static int assoc_helper_wpa_keys(wlan_private *priv,
+                                 struct assoc_request * assoc_req)
+{
+       int ret = 0;
+
+       ENTER();
+
+       /* enable/Disable RSN */
+       ret = libertas_prepare_and_send_command(priv,
+                                   cmd_802_11_enable_rsn,
+                                   cmd_act_set,
+                                   cmd_option_waitforrsp,
+                                   0, assoc_req);
+       if (ret)
+               goto out;
+
+       ret = libertas_prepare_and_send_command(priv,
+                                   cmd_802_11_key_material,
+                                   cmd_act_set,
+                                   cmd_option_waitforrsp,
+                                   0, assoc_req);
+
+out:
+       LEAVE();
+       return ret;
+}
+
+
+static int assoc_helper_wpa_ie(wlan_private *priv,
+                               struct assoc_request * assoc_req)
+{
+       wlan_adapter *adapter = priv->adapter;
+       int ret = 0;
+
+       ENTER();
+
+       if (assoc_req->secinfo.WPAenabled || assoc_req->secinfo.WPA2enabled) {
+               memcpy(&adapter->wpa_ie, &assoc_req->wpa_ie, assoc_req->wpa_ie_len);
+               adapter->wpa_ie_len = assoc_req->wpa_ie_len;
+       } else {
+               memset(&adapter->wpa_ie, 0, MAX_WPA_IE_LEN);
+               adapter->wpa_ie_len = 0;
+       }
+
+       LEAVE();
+       return ret;
+}
+
+
+static int should_deauth_infrastructure(wlan_adapter *adapter,
+                                        struct assoc_request * assoc_req)
+{
+       if (adapter->connect_status != libertas_connected)
+               return 0;
+
+       if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
+               lbs_pr_debug(1, "Deauthenticating due to new SSID in "
+                       " configuration request.\n");
+               return 1;
+       }
+
+       if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) {
+               if (adapter->secinfo.authmode !=
+                   assoc_req->secinfo.authmode) {
+                       lbs_pr_debug(1, "Deauthenticating due to updated security "
+                               "info in configuration request.\n");
+                       return 1;
+               }
+       }
+
+       if (test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) {
+               lbs_pr_debug(1, "Deauthenticating due to new BSSID in "
+                       " configuration request.\n");
+               return 1;
+       }
+
+       /* FIXME: deal with 'auto' mode somehow */
+       if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) {
+               if (assoc_req->mode != wlan802_11infrastructure)
+                       return 1;
+       }
+
+       return 0;
+}
+
+
+static int should_stop_adhoc(wlan_adapter *adapter,
+                             struct assoc_request * assoc_req)
+{
+       if (adapter->connect_status != libertas_connected)
+               return 0;
+
+       if (adapter->curbssparams.ssid.ssidlength != assoc_req->ssid.ssidlength)
+               return 1;
+       if (memcmp(adapter->curbssparams.ssid.ssid, assoc_req->ssid.ssid,
+                       sizeof(struct WLAN_802_11_SSID)))
+               return 1;
+
+       /* FIXME: deal with 'auto' mode somehow */
+       if (test_bit(ASSOC_FLAG_MODE, &assoc_req->flags)) {
+               if (assoc_req->mode != wlan802_11ibss)
+                       return 1;
+       }
+
+       return 0;
+}
+
+
+void wlan_association_worker(struct work_struct *work)
+{
+       wlan_private *priv = container_of(work, wlan_private, assoc_work.work);
+       wlan_adapter *adapter = priv->adapter;
+       struct assoc_request * assoc_req = NULL;
+       int ret = 0;
+       int find_any_ssid = 0;
+
+       ENTER();
+
+       mutex_lock(&adapter->lock);
+       assoc_req = adapter->assoc_req;
+       adapter->assoc_req = NULL;
+       mutex_unlock(&adapter->lock);
+
+       if (!assoc_req) {
+               LEAVE();
+               return;
+       }
+
+       lbs_pr_debug(1, "ASSOC: starting new association request: flags = 0x%lX\n",
+               assoc_req->flags);
+
+       /* If 'any' SSID was specified, find an SSID to associate with */
+       if (test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)
+           && !assoc_req->ssid.ssidlength)
+               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 (memcmp(&assoc_req->bssid, bssid_any, ETH_ALEN)
+                   && memcmp(&assoc_req->bssid, bssid_off, ETH_ALEN))
+                       find_any_ssid = 0;
+       }
+
+       if (find_any_ssid) {
+               enum WLAN_802_11_NETWORK_INFRASTRUCTURE new_mode;
+
+               ret = libertas_find_best_network_SSID(priv, &assoc_req->ssid,
+                               assoc_req->mode, &new_mode);
+               if (ret) {
+                       lbs_pr_debug(1, "Could not find best network\n");
+                       ret = -ENETUNREACH;
+                       goto out;
+               }
+
+               /* Ensure we switch to the mode of the AP */
+               if (assoc_req->mode == wlan802_11autounknown) {
+                       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 (adapter->inframode == wlan802_11infrastructure) {
+               if (should_deauth_infrastructure(adapter, assoc_req)) {
+                       ret = libertas_send_deauthentication(priv);
+                       if (ret) {
+                               lbs_pr_debug(1, "Deauthentication due to new "
+                                       "configuration request failed: %d\n",
+                                       ret);
+                       }
+               }
+       } else if (adapter->inframode == wlan802_11ibss) {
+               if (should_stop_adhoc(adapter, assoc_req)) {
+                       ret = libertas_stop_adhoc_network(priv);
+                       if (ret) {
+                               lbs_pr_debug(1, "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) {
+lbs_pr_debug(1, "ASSOC(:%d) mode: ret = %d\n", __LINE__, 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) {
+lbs_pr_debug(1, "ASSOC(:%d) wep_keys: ret = %d\n", __LINE__, ret);
+                       goto out;
+               }
+       }
+
+       if (test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) {
+               ret = assoc_helper_secinfo(priv, assoc_req);
+               if (ret) {
+lbs_pr_debug(1, "ASSOC(:%d) secinfo: ret = %d\n", __LINE__, ret);
+                       goto out;
+               }
+       }
+
+       if (test_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags)) {
+               ret = assoc_helper_wpa_ie(priv, assoc_req);
+               if (ret) {
+lbs_pr_debug(1, "ASSOC(:%d) wpa_ie: ret = %d\n", __LINE__, 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) {
+lbs_pr_debug(1, "ASSOC(:%d) wpa_keys: ret = %d\n", __LINE__, 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_pr_debug(1, "ASSOC: association attempt unsuccessful: %d\n",
+                               ret);
+                       success = 0;
+               }
+
+               if (adapter->connect_status != libertas_connected) {
+                       lbs_pr_debug(1, "ASSOC: assoication attempt unsuccessful, "
+                               "not connected.\n");
+                       success = 0;
+               }
+
+               if (success) {
+                       lbs_pr_debug(1, "ASSOC: association attempt successful. "
+                               "Associated to '%s' (" MAC_FMT ")\n",
+                               assoc_req->ssid.ssid, MAC_ARG(assoc_req->bssid));
+                       libertas_prepare_and_send_command(priv,
+                               cmd_802_11_rssi,
+                               0, cmd_option_waitforrsp, 0, NULL);
+
+                       libertas_prepare_and_send_command(priv,
+                               cmd_802_11_get_log,
+                               0, cmd_option_waitforrsp, 0, NULL);
+               } else {
+
+                       ret = -1;
+               }
+       }
+
+out:
+       if (ret) {
+               lbs_pr_debug(1, "ASSOC: reconfiguration attempt unsuccessful: %d\n",
+                       ret);
+       }
+       kfree(assoc_req);
+       LEAVE();
+}
+
+
+/*
+ * Caller MUST hold any necessary locks
+ */
+struct assoc_request * wlan_get_association_request(wlan_adapter *adapter)
+{
+       struct assoc_request * assoc_req;
+
+       if (!adapter->assoc_req) {
+               adapter->assoc_req = kzalloc(sizeof(struct assoc_request), GFP_KERNEL);
+               if (!adapter->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 = adapter->assoc_req;
+       if (!test_bit(ASSOC_FLAG_SSID, &assoc_req->flags)) {
+               memcpy(&assoc_req->ssid, adapter->curbssparams.ssid.ssid,
+                       adapter->curbssparams.ssid.ssidlength);
+       }
+
+       if (!test_bit(ASSOC_FLAG_CHANNEL, &assoc_req->flags))
+               assoc_req->channel = adapter->curbssparams.channel;
+
+       if (!test_bit(ASSOC_FLAG_MODE, &assoc_req->flags))
+               assoc_req->mode = adapter->inframode;
+
+       if (!test_bit(ASSOC_FLAG_BSSID, &assoc_req->flags)) {
+               memcpy(&assoc_req->bssid, adapter->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], &adapter->wep_keys[i],
+                               sizeof(struct WLAN_802_11_KEY));
+               }
+       }
+
+       if (!test_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags))
+               assoc_req->wep_tx_keyidx = adapter->wep_tx_keyidx;
+
+       if (!test_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags)) {
+               memcpy(&assoc_req->wpa_mcast_key, &adapter->wpa_mcast_key,
+                       sizeof(struct WLAN_802_11_KEY));
+       }
+
+       if (!test_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags)) {
+               memcpy(&assoc_req->wpa_unicast_key, &adapter->wpa_unicast_key,
+                       sizeof(struct WLAN_802_11_KEY));
+       }
+
+       if (!test_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags)) {
+               memcpy(&assoc_req->secinfo, &adapter->secinfo,
+                       sizeof(struct wlan_802_11_security));
+       }
+
+       if (!test_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags)) {
+               memcpy(&assoc_req->wpa_ie, &adapter->wpa_ie,
+                       MAX_WPA_IE_LEN);
+               assoc_req->wpa_ie_len = adapter->wpa_ie_len;
+       }
+
+       return assoc_req;
+}
+
+
diff --git a/drivers/net/wireless/libertas/assoc.h b/drivers/net/wireless/libertas/assoc.h
new file mode 100644 (file)
index 0000000..2ffd82d
--- /dev/null
@@ -0,0 +1,30 @@
+/* Copyright (C) 2006, Red Hat, Inc. */
+
+#ifndef _WLAN_ASSOC_H_
+#define _WLAN_ASSOC_H_
+
+#include "dev.h"
+
+void wlan_association_worker(struct work_struct *work);
+
+struct assoc_request * wlan_get_association_request(wlan_adapter *adapter);
+
+#define ASSOC_DELAY (HZ / 2)
+static inline void wlan_postpone_association_work(wlan_private *priv)
+{
+       if (priv->adapter->surpriseremoved)
+               return;
+       cancel_delayed_work(&priv->assoc_work);
+       queue_delayed_work(priv->assoc_thread, &priv->assoc_work, ASSOC_DELAY);
+}
+
+static inline void wlan_cancel_association_work(wlan_private *priv)
+{
+       cancel_delayed_work(&priv->assoc_work);
+       if (priv->adapter->assoc_req) {
+               kfree(priv->adapter->assoc_req);
+               priv->adapter->assoc_req = NULL;
+       }
+}
+
+#endif /* _WLAN_ASSOC_H */
diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c
new file mode 100644 (file)
index 0000000..bfdac58
--- /dev/null
@@ -0,0 +1,1958 @@
+/**
+  * 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 "sbi.h"
+#include "decl.h"
+#include "defs.h"
+#include "dev.h"
+#include "join.h"
+#include "wext.h"
+
+static void cleanup_cmdnode(struct cmd_ctrl_node *ptempnode);
+
+static u16 commands_allowed_in_ps[] = {
+       cmd_802_11_rssi,
+};
+
+/**
+ *  @brief This function checks if the commans is allowed
+ *  in PS mode not.
+ *
+ *  @param command the command ID
+ *  @return       TRUE or FALSE
+ */
+static u8 is_command_allowed_in_ps(u16 command)
+{
+       int count = sizeof(commands_allowed_in_ps)
+           / sizeof(commands_allowed_in_ps[0]);
+       int i;
+
+       for (i = 0; i < count; i++) {
+               if (command == cpu_to_le16(commands_allowed_in_ps[i]))
+                       return 1;
+       }
+
+       return 0;
+}
+
+static int wlan_cmd_hw_spec(wlan_private * priv, struct cmd_ds_command *cmd)
+{
+       struct cmd_ds_get_hw_spec *hwspec = &cmd->params.hwspec;
+
+       ENTER();
+
+       cmd->command = cpu_to_le16(cmd_get_hw_spec);
+       cmd->size =
+           cpu_to_le16(sizeof(struct cmd_ds_get_hw_spec) + S_DS_GEN);
+       memcpy(hwspec->permanentaddr, priv->adapter->current_addr, ETH_ALEN);
+
+       LEAVE();
+       return 0;
+}
+
+static int wlan_cmd_802_11_ps_mode(wlan_private * priv,
+                                  struct cmd_ds_command *cmd,
+                                  u16 cmd_action)
+{
+       struct cmd_ds_802_11_ps_mode *psm = &cmd->params.psmode;
+       u16 action = cmd_action;
+       wlan_adapter *adapter = priv->adapter;
+
+       ENTER();
+
+       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 (action) {
+       case cmd_subcmd_enter_ps:
+               lbs_pr_debug(1, "PS command:" "SubCode- Enter PS\n");
+               lbs_pr_debug(1, "locallisteninterval = %d\n",
+                      adapter->locallisteninterval);
+
+               psm->locallisteninterval =
+                   cpu_to_le16(adapter->locallisteninterval);
+               psm->nullpktinterval =
+                   cpu_to_le16(adapter->nullpktinterval);
+               psm->multipledtim =
+                   cpu_to_le16(priv->adapter->multipledtim);
+               break;
+
+       case cmd_subcmd_exit_ps:
+               lbs_pr_debug(1, "PS command:" "SubCode- Exit PS\n");
+               break;
+
+       case cmd_subcmd_sleep_confirmed:
+               lbs_pr_debug(1, "PS command: SubCode- sleep confirm\n");
+               break;
+
+       default:
+               break;
+       }
+
+       LEAVE();
+       return 0;
+}
+
+static int wlan_cmd_802_11_inactivity_timeout(wlan_private * priv,
+                                             struct cmd_ds_command *cmd,
+                                             u16 cmd_action, void *pdata_buf)
+{
+       u16 *timeout = pdata_buf;
+
+       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;
+
+       return 0;
+}
+
+static int wlan_cmd_802_11_sleep_params(wlan_private * priv,
+                                       struct cmd_ds_command *cmd,
+                                       u16 cmd_action)
+{
+       wlan_adapter *adapter = priv->adapter;
+       struct cmd_ds_802_11_sleep_params *sp = &cmd->params.sleep_params;
+
+       ENTER();
+
+       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(&adapter->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(adapter->sp.sp_error);
+               sp->offset = cpu_to_le16(adapter->sp.sp_offset);
+               sp->stabletime = cpu_to_le16(adapter->sp.sp_stabletime);
+               sp->calcontrol = (u8) adapter->sp.sp_calcontrol;
+               sp->externalsleepclk = (u8) adapter->sp.sp_extsleepclk;
+               sp->reserved = cpu_to_le16(adapter->sp.sp_reserved);
+       }
+
+       LEAVE();
+       return 0;
+}
+
+static int wlan_cmd_802_11_set_wep(wlan_private * priv,
+                                   struct cmd_ds_command *cmd,
+                                   u32 cmd_act,
+                                   void * pdata_buf)
+{
+       struct cmd_ds_802_11_set_wep *wep = &cmd->params.wep;
+       wlan_adapter *adapter = priv->adapter;
+       int ret = 0;
+       struct assoc_request * assoc_req = pdata_buf;
+
+       ENTER();
+
+       cmd->command = cpu_to_le16(cmd_802_11_set_wep);
+       cmd->size = cpu_to_le16((sizeof(struct cmd_ds_802_11_set_wep))
+                                    + S_DS_GEN);
+
+       if (cmd_act == cmd_act_add) {
+               int i;
+
+               if (!assoc_req) {
+                       lbs_pr_debug(1, "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));
+
+               lbs_pr_debug(1, "Tx key Index: %u\n", wep->keyindex);
+
+               /* Copy key types and material to host command structure */
+               for (i = 0; i < 4; i++) {
+                       struct WLAN_802_11_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);
+                               break;
+                       case KEY_LEN_WEP_104:
+                               wep->keytype[i] = cmd_type_wep_104_bit;
+                               memmove(&wep->keymaterial[i], pkey->key,
+                                       pkey->len);
+                               break;
+                       case 0:
+                               break;
+                       default:
+                               lbs_pr_debug(1, "Invalid WEP key %d length of %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)
+                                                (adapter->wep_tx_keyidx &
+                                                (u32)cmd_WEP_KEY_INDEX_MASK));
+       }
+
+       ret = 0;
+
+done:
+       LEAVE();
+       return ret;
+}
+
+static int wlan_cmd_802_11_enable_rsn(wlan_private * priv,
+                                     struct cmd_ds_command *cmd,
+                                     u16 cmd_action)
+{
+       struct cmd_ds_802_11_enable_rsn *penableRSN = &cmd->params.enbrsn;
+       wlan_adapter *adapter = priv->adapter;
+
+       cmd->command = cpu_to_le16(cmd_802_11_enable_rsn);
+       cmd->size =
+           cpu_to_le16(sizeof(struct cmd_ds_802_11_enable_rsn) +
+                            S_DS_GEN);
+       penableRSN->action = cpu_to_le16(cmd_action);
+       if (adapter->secinfo.WPAenabled || adapter->secinfo.WPA2enabled) {
+               penableRSN->enable = cpu_to_le16(cmd_enable_rsn);
+       } else {
+               penableRSN->enable = cpu_to_le16(cmd_disable_rsn);
+       }
+
+       return 0;
+}
+
+
+static void set_one_wpa_key(struct MrvlIEtype_keyParamSet * pkeyparamset,
+                            struct WLAN_802_11_KEY * pkey)
+{
+       pkeyparamset->keytypeid = cpu_to_le16(pkey->type);
+
+       if (pkey->flags & KEY_INFO_WPA_ENABLED) {
+               pkeyparamset->keyinfo = cpu_to_le16(KEY_INFO_WPA_ENABLED);
+       } else {
+               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);
+       } else 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->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));
+}
+
+static int wlan_cmd_802_11_key_material(wlan_private * priv,
+                                       struct cmd_ds_command *cmd,
+                                       u16 cmd_action,
+                                       u32 cmd_oid, void *pdata_buf)
+{
+       wlan_adapter *adapter = priv->adapter;
+       struct cmd_ds_802_11_key_material *pkeymaterial =
+           &cmd->params.keymaterial;
+       int ret = 0;
+       int index = 0;
+
+       ENTER();
+
+       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 (adapter->wpa_unicast_key.len) {
+               set_one_wpa_key(&pkeymaterial->keyParamSet[index],
+                               &adapter->wpa_unicast_key);
+               index++;
+       }
+
+       if (adapter->wpa_mcast_key.len) {
+               set_one_wpa_key(&pkeymaterial->keyParamSet[index],
+                               &adapter->wpa_mcast_key);
+               index++;
+       }
+
+       cmd->size = cpu_to_le16(  S_DS_GEN
+                                    + sizeof (pkeymaterial->action)
+                                    + index * sizeof(struct MrvlIEtype_keyParamSet));
+
+       ret = 0;
+
+done:
+       LEAVE();
+       return ret;
+}
+
+static int wlan_cmd_802_11_reset(wlan_private * priv,
+                                struct cmd_ds_command *cmd, int cmd_action)
+{
+       struct cmd_ds_802_11_reset *reset = &cmd->params.reset;
+
+       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);
+
+       return 0;
+}
+
+static int wlan_cmd_802_11_get_log(wlan_private * priv,
+                                  struct cmd_ds_command *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);
+
+       return 0;
+}
+
+static int wlan_cmd_802_11_get_stat(wlan_private * priv,
+                                   struct cmd_ds_command *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);
+
+       return 0;
+}
+
+static int wlan_cmd_802_11_snmp_mib(wlan_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;
+       wlan_adapter *adapter = priv->adapter;
+       u8 ucTemp;
+
+       ENTER();
+
+       lbs_pr_debug(1, "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(struct cmd_ds_802_11_snmp_mib) +
+                            S_DS_GEN);
+
+       switch (cmd_oid) {
+       case OID_802_11_INFRASTRUCTURE_MODE:
+       {
+               enum WLAN_802_11_NETWORK_INFRASTRUCTURE mode =
+                       (enum WLAN_802_11_NETWORK_INFRASTRUCTURE) pdata_buf;
+               pSNMPMIB->querytype = cpu_to_le16(cmd_act_set);
+               pSNMPMIB->oid = cpu_to_le16((u16) desired_bsstype_i);
+               pSNMPMIB->bufsize = sizeof(u8);
+               if (mode == wlan802_11infrastructure)
+                       ucTemp = SNMP_MIB_VALUE_INFRA;
+               else
+                       ucTemp = SNMP_MIB_VALUE_ADHOC;
+
+               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 = cmd_act_set;
+                               pSNMPMIB->bufsize = sizeof(u16);
+                               ulTemp = *(u32 *)pdata_buf;
+                               *((unsigned short *)(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);
+                               *((unsigned short *)(pSNMPMIB->value)) =
+                                   cpu_to_le16((u16) ulTemp);
+
+                       }
+
+                       break;
+               }
+
+       case OID_802_11_RTS_THRESHOLD:
+               {
+
+                       u32 ulTemp;
+                       pSNMPMIB->oid = le16_to_cpu((u16) 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);
+                               *(unsigned short *)(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));
+                       *((unsigned short *)(pSNMPMIB->value)) =
+                           cpu_to_le16((u16) adapter->txretrycount);
+               }
+
+               break;
+       default:
+               break;
+       }
+
+       lbs_pr_debug(1,
+              "SNMP_CMD: command=0x%x, size=0x%x, seqnum=0x%x, result=0x%x\n",
+              cmd->command, cmd->size, cmd->seqnum, cmd->result);
+
+       lbs_pr_debug(1,
+              "SNMP_CMD: action=0x%x, oid=0x%x, oidsize=0x%x, value=0x%x\n",
+              pSNMPMIB->querytype, pSNMPMIB->oid, pSNMPMIB->bufsize,
+              *(u16 *) pSNMPMIB->value);
+
+       LEAVE();
+       return 0;
+}
+
+static int wlan_cmd_802_11_radio_control(wlan_private * priv,
+                                        struct cmd_ds_command *cmd,
+                                        int cmd_action)
+{
+       wlan_adapter *adapter = priv->adapter;
+       struct cmd_ds_802_11_radio_control *pradiocontrol =
+           &cmd->params.radio;
+
+       ENTER();
+
+       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 (adapter->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 (adapter->radioon)
+               pradiocontrol->control |= cpu_to_le16(TURN_ON_RF);
+       else
+               pradiocontrol->control &= cpu_to_le16(~TURN_ON_RF);
+
+       LEAVE();
+       return 0;
+}
+
+static int wlan_cmd_802_11_rf_tx_power(wlan_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;
+
+       ENTER();
+
+       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 = cmd_action;
+
+       lbs_pr_debug(1, "RF_TX_POWER_CMD: size:%d cmd:0x%x Act:%d\n", cmd->size,
+              cmd->command, 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;
+       }
+       LEAVE();
+       return 0;
+}
+
+static int wlan_cmd_802_11_rf_antenna(wlan_private * priv,
+                                     struct cmd_ds_command *cmd,
+                                     u16 cmd_action, void *pdata_buf)
+{
+       struct cmd_ds_802_11_rf_antenna *rant = &cmd->params.rant;
+
+       cmd->command = cpu_to_le16(cmd_802_11_rf_antenna);
+       cmd->size =
+           cpu_to_le16(sizeof(struct cmd_ds_802_11_rf_antenna) +
+                            S_DS_GEN);
+
+       rant->action = cpu_to_le16(cmd_action);
+       if ((cmd_action == cmd_act_set_rx) ||
+           (cmd_action == cmd_act_set_tx)) {
+               rant->antennamode =
+                   cpu_to_le16((u16) (*(u32 *) pdata_buf));
+       }
+
+       return 0;
+}
+
+static int wlan_cmd_802_11_rate_adapt_rateset(wlan_private * priv,
+                                             struct cmd_ds_command *cmd,
+                                             u16 cmd_action)
+{
+       struct cmd_ds_802_11_rate_adapt_rateset
+       *rateadapt = &cmd->params.rateset;
+       wlan_adapter *adapter = priv->adapter;
+
+       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);
+
+       ENTER();
+
+       rateadapt->action = cmd_action;
+       rateadapt->enablehwauto = adapter->enablehwauto;
+       rateadapt->bitmap = adapter->ratebitmap;
+
+       LEAVE();
+       return 0;
+}
+
+static int wlan_cmd_802_11_data_rate(wlan_private * priv,
+                                    struct cmd_ds_command *cmd,
+                                    u16 cmd_action)
+{
+       struct cmd_ds_802_11_data_rate *pdatarate = &cmd->params.drate;
+       wlan_adapter *adapter = priv->adapter;
+       u16 action = cmd_action;
+
+       ENTER();
+
+       cmd->size =
+           cpu_to_le16(sizeof(struct cmd_ds_802_11_data_rate) +
+                            S_DS_GEN);
+
+       cmd->command = cpu_to_le16(cmd_802_11_data_rate);
+
+       memset(pdatarate, 0, sizeof(struct cmd_ds_802_11_data_rate));
+
+       pdatarate->action = cpu_to_le16(cmd_action);
+
+       if (action == cmd_act_set_tx_fix_rate) {
+               pdatarate->datarate[0] = libertas_data_rate_to_index(adapter->datarate);
+               lbs_pr_debug(1, "Setting FW for fixed rate 0x%02X\n",
+                      adapter->datarate);
+       } else if (action == cmd_act_set_tx_auto) {
+               lbs_pr_debug(1, "Setting FW for AUTO rate\n");
+       }
+
+       LEAVE();
+       return 0;
+}
+
+static int wlan_cmd_mac_multicast_adr(wlan_private * priv,
+                                     struct cmd_ds_command *cmd,
+                                     u16 cmd_action)
+{
+       struct cmd_ds_mac_multicast_adr *pMCastAdr = &cmd->params.madr;
+       wlan_adapter *adapter = priv->adapter;
+
+       cmd->size =
+           cpu_to_le16(sizeof(struct cmd_ds_mac_multicast_adr) +
+                            S_DS_GEN);
+       cmd->command = cpu_to_le16(cmd_mac_multicast_adr);
+
+       pMCastAdr->action = cpu_to_le16(cmd_action);
+       pMCastAdr->nr_of_adrs =
+           cpu_to_le16((u16) adapter->nr_of_multicastmacaddr);
+       memcpy(pMCastAdr->maclist, adapter->multicastlist,
+              adapter->nr_of_multicastmacaddr * ETH_ALEN);
+
+       return 0;
+}
+
+static int wlan_cmd_802_11_rf_channel(wlan_private * priv,
+                                     struct cmd_ds_command *cmd,
+                                     int option, void *pdata_buf)
+{
+       struct cmd_ds_802_11_rf_channel *rfchan = &cmd->params.rfchannel;
+
+       cmd->command = cpu_to_le16(cmd_802_11_rf_channel);
+       cmd->size = cpu_to_le16(sizeof(struct cmd_ds_802_11_rf_channel)
+                                    + S_DS_GEN);
+
+       if (option == cmd_opt_802_11_rf_channel_set) {
+               rfchan->currentchannel = cpu_to_le16(*((u16 *) pdata_buf));
+       }
+
+       rfchan->action = cpu_to_le16(option);
+
+       return 0;
+}
+
+static int wlan_cmd_802_11_rssi(wlan_private * priv,
+                               struct cmd_ds_command *cmd)
+{
+       wlan_adapter *adapter = priv->adapter;
+
+       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 = priv->adapter->bcn_avg_factor;
+
+       /* reset Beacon SNR/NF/RSSI values */
+       adapter->SNR[TYPE_BEACON][TYPE_NOAVG] = 0;
+       adapter->SNR[TYPE_BEACON][TYPE_AVG] = 0;
+       adapter->NF[TYPE_BEACON][TYPE_NOAVG] = 0;
+       adapter->NF[TYPE_BEACON][TYPE_AVG] = 0;
+       adapter->RSSI[TYPE_BEACON][TYPE_NOAVG] = 0;
+       adapter->RSSI[TYPE_BEACON][TYPE_AVG] = 0;
+
+       return 0;
+}
+
+static int wlan_cmd_reg_access(wlan_private * priv,
+                              struct cmd_ds_command *cmdptr,
+                              u8 cmd_action, void *pdata_buf)
+{
+       struct wlan_offset_value *offval;
+
+       ENTER();
+
+       offval = (struct wlan_offset_value *)pdata_buf;
+
+       switch (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;
+       }
+
+       LEAVE();
+       return 0;
+}
+
+static int wlan_cmd_802_11_mac_address(wlan_private * priv,
+                                      struct cmd_ds_command *cmd,
+                                      u16 cmd_action)
+{
+       wlan_adapter *adapter = priv->adapter;
+
+       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,
+                      adapter->current_addr, ETH_ALEN);
+               lbs_dbg_hex("SET_CMD: MAC ADDRESS-", adapter->current_addr, 6);
+       }
+
+       return 0;
+}
+
+static int wlan_cmd_802_11_eeprom_access(wlan_private * priv,
+                                        struct cmd_ds_command *cmd,
+                                        int cmd_action, void *pdata_buf)
+{
+       struct wlan_ioctl_regrdwr *ea = pdata_buf;
+
+       ENTER();
+
+       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;
+
+       return 0;
+}
+
+static int wlan_cmd_bt_access(wlan_private * priv,
+                              struct cmd_ds_command *cmd,
+                              u16 cmd_action, void *pdata_buf)
+{
+       struct cmd_ds_bt_access *bt_access = &cmd->params.bt;
+       lbs_pr_debug(1, "BT CMD(%d)\n", 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_dbg_hex("BT_ADD: blinded mac address-", bt_access->addr1, 6);
+               break;
+       case cmd_act_bt_access_del:
+               memcpy(bt_access->addr1, pdata_buf, 1 * ETH_ALEN);
+               lbs_dbg_hex("BT_DEL: blinded mac address-", 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;
+       default:
+               break;
+       }
+       return 0;
+}
+
+static int wlan_cmd_fwt_access(wlan_private * priv,
+                              struct cmd_ds_command *cmd,
+                              u16 cmd_action, void *pdata_buf)
+{
+       struct cmd_ds_fwt_access *fwt_access = &cmd->params.fwt;
+       lbs_pr_debug(1, "FWT CMD(%d)\n", 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);
+
+       return 0;
+}
+
+static int wlan_cmd_mesh_access(wlan_private * priv,
+                               struct cmd_ds_command *cmd,
+                               u16 cmd_action, void *pdata_buf)
+{
+       struct cmd_ds_mesh_access *mesh_access = &cmd->params.mesh;
+       lbs_pr_debug(1, "FWT CMD(%d)\n", cmd_action);
+
+       cmd->command = cpu_to_le16(cmd_mesh_access);
+       cmd->size = cpu_to_le16(sizeof(struct cmd_ds_mesh_access)
+                                    + S_DS_GEN);
+       cmd->result = 0;
+
+       if (pdata_buf)
+               memcpy(mesh_access, pdata_buf, sizeof(*mesh_access));
+       else
+               memset(mesh_access, 0, sizeof(*mesh_access));
+
+       mesh_access->action = cpu_to_le16(cmd_action);
+
+       return 0;
+}
+
+void libertas_queue_cmd(wlan_adapter * adapter, struct cmd_ctrl_node *cmdnode, u8 addtail)
+{
+       unsigned long flags;
+       struct cmd_ds_command *cmdptr;
+
+       ENTER();
+
+       if (!cmdnode) {
+               lbs_pr_debug(1, "QUEUE_CMD: cmdnode is NULL\n");
+               goto done;
+       }
+
+       cmdptr = (struct cmd_ds_command *)cmdnode->bufvirtualaddr;
+       if (!cmdptr) {
+               lbs_pr_debug(1, "QUEUE_CMD: cmdptr is NULL\n");
+               goto done;
+       }
+
+       /* Exit_PS command needs to be queued in the header always. */
+       if (cmdptr->command == cmd_802_11_ps_mode) {
+               struct cmd_ds_802_11_ps_mode *psm = &cmdptr->params.psmode;
+               if (psm->action == cmd_subcmd_exit_ps) {
+                       if (adapter->psstate != PS_STATE_FULL_POWER)
+                               addtail = 0;
+               }
+       }
+
+       spin_lock_irqsave(&adapter->driver_lock, flags);
+
+       if (addtail)
+               list_add_tail((struct list_head *)cmdnode,
+                             &adapter->cmdpendingq);
+       else
+               list_add((struct list_head *)cmdnode, &adapter->cmdpendingq);
+
+       spin_unlock_irqrestore(&adapter->driver_lock, flags);
+
+       lbs_pr_debug(1, "QUEUE_CMD: Inserted node=0x%x, cmd=0x%x in cmdpendingq\n",
+              (u32) cmdnode,
+              ((struct cmd_ds_gen*)cmdnode->bufvirtualaddr)->command);
+
+done:
+       LEAVE();
+       return;
+}
+
+/*
+ * TODO: Fix the issue when DownloadcommandToStation is being called the
+ * second time when the command timesout. 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(wlan_private * priv,
+                                   struct cmd_ctrl_node *cmdnode)
+{
+       unsigned long flags;
+       struct cmd_ds_command *cmdptr;
+       wlan_adapter *adapter = priv->adapter;
+       int ret = 0;
+       u16 cmdsize;
+       u16 command;
+
+       ENTER();
+
+       if (!adapter || !cmdnode) {
+               lbs_pr_debug(1, "DNLD_CMD: adapter = %#x, cmdnode = %#x\n",
+                      (int)adapter, (int)cmdnode);
+               if (cmdnode) {
+                       spin_lock_irqsave(&adapter->driver_lock, flags);
+                       __libertas_cleanup_and_insert_cmd(priv, cmdnode);
+                       spin_unlock_irqrestore(&adapter->driver_lock, flags);
+               }
+               ret = -1;
+               goto done;
+       }
+
+       cmdptr = (struct cmd_ds_command *)cmdnode->bufvirtualaddr;
+
+
+       spin_lock_irqsave(&adapter->driver_lock, flags);
+       if (!cmdptr || !cmdptr->size) {
+               lbs_pr_debug(1, "DNLD_CMD: cmdptr is Null or cmd size is Zero, "
+                      "Not sending\n");
+               __libertas_cleanup_and_insert_cmd(priv, cmdnode);
+               spin_unlock_irqrestore(&adapter->driver_lock, flags);
+               ret = -1;
+               goto done;
+       }
+
+       adapter->cur_cmd = cmdnode;
+       adapter->cur_cmd_retcode = 0;
+       spin_unlock_irqrestore(&adapter->driver_lock, flags);
+       lbs_pr_debug(1, "DNLD_CMD:: Before download, size of cmd = %d\n",
+              cmdptr->size);
+
+       cmdsize = cmdptr->size;
+
+       command = cpu_to_le16(cmdptr->command);
+
+       cmdnode->cmdwaitqwoken = 0;
+       cmdsize = cpu_to_le16(cmdsize);
+
+       ret = libertas_sbi_host_to_card(priv, MVMS_CMD, (u8 *) cmdptr, cmdsize);
+
+       if (ret != 0) {
+               lbs_pr_debug(1, "DNLD_CMD: Host to Card failed\n");
+               spin_lock_irqsave(&adapter->driver_lock, flags);
+               __libertas_cleanup_and_insert_cmd(priv, adapter->cur_cmd);
+               adapter->cur_cmd = NULL;
+               spin_unlock_irqrestore(&adapter->driver_lock, flags);
+               ret = -1;
+               goto done;
+       }
+
+       lbs_pr_debug(1, "DNLD_CMD: Sent command 0x%x @ %lu\n", command, jiffies);
+       lbs_dbg_hex("DNLD_CMD: command", cmdnode->bufvirtualaddr, cmdsize);
+
+       /* Setup the timer after transmit command */
+       if (command == cmd_802_11_scan
+           || command == cmd_802_11_authenticate
+           || command == cmd_802_11_associate)
+               mod_timer(&adapter->command_timer, jiffies + (10*HZ));
+       else
+               mod_timer(&adapter->command_timer, jiffies + (5*HZ));
+
+       ret = 0;
+
+      done:
+       LEAVE();
+       return ret;
+}
+
+static int wlan_cmd_mac_control(wlan_private * priv,
+                               struct cmd_ds_command *cmd)
+{
+       struct cmd_ds_mac_control *mac = &cmd->params.macctrl;
+
+       ENTER();
+
+       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->adapter->currentpacketfilter);
+
+       lbs_pr_debug(1, "wlan_cmd_mac_control(): action=0x%X size=%d\n",
+              mac->action, cmd->size);
+
+       LEAVE();
+       return 0;
+}
+
+/**
+ *  This function inserts command node to cmdfreeq
+ *  after cleans it. Requires adapter->driver_lock held.
+ */
+void __libertas_cleanup_and_insert_cmd(wlan_private * priv, struct cmd_ctrl_node *ptempcmd)
+{
+       wlan_adapter *adapter = priv->adapter;
+
+       if (!ptempcmd)
+               goto done;
+
+       cleanup_cmdnode(ptempcmd);
+       list_add_tail((struct list_head *)ptempcmd, &adapter->cmdfreeq);
+done:
+       return;
+}
+
+void libertas_cleanup_and_insert_cmd(wlan_private * priv, struct cmd_ctrl_node *ptempcmd)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->adapter->driver_lock, flags);
+       __libertas_cleanup_and_insert_cmd(priv, ptempcmd);
+       spin_unlock_irqrestore(&priv->adapter->driver_lock, flags);
+}
+
+int libertas_set_radio_control(wlan_private * priv)
+{
+       int ret = 0;
+
+       ENTER();
+
+       ret = libertas_prepare_and_send_command(priv,
+                                   cmd_802_11_radio_control,
+                                   cmd_act_set,
+                                   cmd_option_waitforrsp, 0, NULL);
+
+       lbs_pr_debug(1, "RADIO_SET: on or off: 0x%X, preamble = 0x%X\n",
+              priv->adapter->radioon, priv->adapter->preamble);
+
+       LEAVE();
+       return ret;
+}
+
+int libertas_set_mac_packet_filter(wlan_private * priv)
+{
+       int ret = 0;
+
+       ENTER();
+
+       lbs_pr_debug(1, "libertas_set_mac_packet_filter value = %x\n",
+              priv->adapter->currentpacketfilter);
+
+       /* Send MAC control command to station */
+       ret = libertas_prepare_and_send_command(priv,
+                                   cmd_mac_control, 0, 0, 0, NULL);
+
+       LEAVE();
+       return ret;
+}
+
+/**
+ *  @brief This function prepare the command before send to firmware.
+ *
+ *  @param priv                A pointer to wlan_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 libertas_prepare_and_send_command(wlan_private * priv,
+                         u16 cmd_no,
+                         u16 cmd_action,
+                         u16 wait_option, u32 cmd_oid, void *pdata_buf)
+{
+       int ret = 0;
+       wlan_adapter *adapter = priv->adapter;
+       struct cmd_ctrl_node *cmdnode;
+       struct cmd_ds_command *cmdptr;
+       unsigned long flags;
+
+       ENTER();
+
+       if (!adapter) {
+               lbs_pr_debug(1, "PREP_CMD: adapter is Null\n");
+               ret = -1;
+               goto done;
+       }
+
+       if (adapter->surpriseremoved) {
+               lbs_pr_debug(1, "PREP_CMD: Card is Removed\n");
+               ret = -1;
+               goto done;
+       }
+
+       cmdnode = libertas_get_free_cmd_ctrl_node(priv);
+
+       if (cmdnode == NULL) {
+               lbs_pr_debug(1, "PREP_CMD: No free cmdnode\n");
+
+               /* Wake up main thread to execute next command */
+               wake_up_interruptible(&priv->mainthread.waitq);
+               ret = -1;
+               goto done;
+       }
+
+       libertas_set_cmd_ctrl_node(priv, cmdnode, cmd_oid, wait_option, pdata_buf);
+
+       cmdptr = (struct cmd_ds_command *)cmdnode->bufvirtualaddr;
+
+       lbs_pr_debug(1, "PREP_CMD: Val of cmd ptr =0x%x, command=0x%X\n",
+              (u32) cmdptr, cmd_no);
+
+       if (!cmdptr) {
+               lbs_pr_debug(1, "PREP_CMD: bufvirtualaddr of cmdnode is NULL\n");
+               libertas_cleanup_and_insert_cmd(priv, cmdnode);
+               ret = -1;
+               goto done;
+       }
+
+       /* Set sequence number, command and INT option */
+       adapter->seqnum++;
+       cmdptr->seqnum = cpu_to_le16(adapter->seqnum);
+
+       cmdptr->command = cmd_no;
+       cmdptr->result = 0;
+
+       switch (cmd_no) {
+       case cmd_get_hw_spec:
+               ret = wlan_cmd_hw_spec(priv, cmdptr);
+               break;
+       case cmd_802_11_ps_mode:
+               ret = wlan_cmd_802_11_ps_mode(priv, cmdptr, cmd_action);
+               break;
+
+       case cmd_802_11_scan:
+               ret = libertas_cmd_80211_scan(priv, cmdptr, pdata_buf);
+               break;
+
+       case cmd_mac_control:
+               ret = wlan_cmd_mac_control(priv, cmdptr);
+               break;
+
+       case cmd_802_11_associate:
+       case cmd_802_11_reassociate:
+               ret = libertas_cmd_80211_associate(priv, cmdptr, pdata_buf);
+               break;
+
+       case cmd_802_11_deauthenticate:
+               ret = libertas_cmd_80211_deauthenticate(priv, cmdptr);
+               break;
+
+       case cmd_802_11_set_wep:
+               ret = wlan_cmd_802_11_set_wep(priv, cmdptr, cmd_action, pdata_buf);
+               break;
+
+       case cmd_802_11_ad_hoc_start:
+               ret = libertas_cmd_80211_ad_hoc_start(priv, cmdptr, pdata_buf);
+               break;
+       case cmd_code_dnld:
+               break;
+
+       case cmd_802_11_reset:
+               ret = wlan_cmd_802_11_reset(priv, cmdptr, cmd_action);
+               break;
+
+       case cmd_802_11_get_log:
+               ret = wlan_cmd_802_11_get_log(priv, cmdptr);
+               break;
+
+       case cmd_802_11_authenticate:
+               ret = libertas_cmd_80211_authenticate(priv, cmdptr, pdata_buf);
+               break;
+
+       case cmd_802_11_get_stat:
+               ret = wlan_cmd_802_11_get_stat(priv, cmdptr);
+               break;
+
+       case cmd_802_11_snmp_mib:
+               ret = wlan_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 = wlan_cmd_reg_access(priv, cmdptr, cmd_action, pdata_buf);
+               break;
+
+       case cmd_802_11_rf_channel:
+               ret = wlan_cmd_802_11_rf_channel(priv, cmdptr,
+                                                cmd_action, pdata_buf);
+               break;
+
+       case cmd_802_11_rf_tx_power:
+               ret = wlan_cmd_802_11_rf_tx_power(priv, cmdptr,
+                                                 cmd_action, pdata_buf);
+               break;
+
+       case cmd_802_11_radio_control:
+               ret = wlan_cmd_802_11_radio_control(priv, cmdptr, cmd_action);
+               break;
+
+       case cmd_802_11_rf_antenna:
+               ret = wlan_cmd_802_11_rf_antenna(priv, cmdptr,
+                                                cmd_action, pdata_buf);
+               break;
+
+       case cmd_802_11_data_rate:
+               ret = wlan_cmd_802_11_data_rate(priv, cmdptr, cmd_action);
+               break;
+       case cmd_802_11_rate_adapt_rateset:
+               ret = wlan_cmd_802_11_rate_adapt_rateset(priv,
+                                                        cmdptr, cmd_action);
+               break;
+
+       case cmd_mac_multicast_adr:
+               ret = wlan_cmd_mac_multicast_adr(priv, cmdptr, cmd_action);
+               break;
+
+       case cmd_802_11_ad_hoc_join:
+               ret = libertas_cmd_80211_ad_hoc_join(priv, cmdptr, pdata_buf);
+               break;
+
+       case cmd_802_11_rssi:
+               ret = wlan_cmd_802_11_rssi(priv, cmdptr);
+               break;
+
+       case cmd_802_11_ad_hoc_stop:
+               ret = libertas_cmd_80211_ad_hoc_stop(priv, cmdptr);
+               break;
+
+       case cmd_802_11_enable_rsn:
+               ret = wlan_cmd_802_11_enable_rsn(priv, cmdptr, cmd_action);
+               break;
+
+       case cmd_802_11_key_material:
+               ret = wlan_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 = wlan_cmd_802_11_mac_address(priv, cmdptr, cmd_action);
+               break;
+
+       case cmd_802_11_eeprom_access:
+               ret = wlan_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 = libertas_cmd_802_11d_domain_info(priv, cmdptr,
+                                                  cmd_no, cmd_action);
+               break;
+
+       case cmd_802_11_sleep_params:
+               ret = wlan_cmd_802_11_sleep_params(priv, cmdptr, cmd_action);
+               break;
+       case cmd_802_11_inactivity_timeout:
+               ret = wlan_cmd_802_11_inactivity_timeout(priv, cmdptr,
+                                                        cmd_action, pdata_buf);
+               libertas_set_cmd_ctrl_node(priv, cmdnode, 0, 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(gpio->header.len + S_DS_GEN +
+                                            ACTION_NUMLED_TLVTYPE_LEN_FIELDS_LEN);
+                       gpio->header.len = cpu_to_le16(gpio->header.len);
+
+                       ret = 0;
+                       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 = wlan_cmd_bt_access(priv, cmdptr, cmd_action, pdata_buf);
+               break;
+
+       case cmd_fwt_access:
+               ret = wlan_cmd_fwt_access(priv, cmdptr, cmd_action, pdata_buf);
+               break;
+
+       case cmd_mesh_access:
+               ret = wlan_cmd_mesh_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_tx_rate_query:
+               cmdptr->command =
+                   cpu_to_le16(cmd_802_11_tx_rate_query);
+               cmdptr->size =
+                   cpu_to_le16(sizeof(struct cmd_tx_rate_query) +
+                                    S_DS_GEN);
+               adapter->txrate = 0;
+               ret = 0;
+               break;
+       default:
+               lbs_pr_debug(1, "PREP_CMD: unknown command- %#x\n", cmd_no);
+               ret = -1;
+               break;
+       }
+
+       /* return error, since the command preparation failed */
+       if (ret != 0) {
+               lbs_pr_debug(1, "PREP_CMD: command preparation failed\n");
+               libertas_cleanup_and_insert_cmd(priv, cmdnode);
+               ret = -1;
+               goto done;
+       }
+
+       cmdnode->cmdwaitqwoken = 0;
+
+       libertas_queue_cmd(adapter, cmdnode, 1);
+       adapter->nr_cmd_pending++;
+       wake_up_interruptible(&priv->mainthread.waitq);
+
+       if (wait_option & cmd_option_waitforrsp) {
+               lbs_pr_debug(1, "PREP_CMD: Wait for CMD response\n");
+               might_sleep();
+               wait_event_interruptible(cmdnode->cmdwait_q,
+                                        cmdnode->cmdwaitqwoken);
+       }
+
+       spin_lock_irqsave(&adapter->driver_lock, flags);
+       if (adapter->cur_cmd_retcode) {
+               lbs_pr_debug(1, "PREP_CMD: command failed with return code=%d\n",
+                      adapter->cur_cmd_retcode);
+               adapter->cur_cmd_retcode = 0;
+               ret = -1;
+       }
+       spin_unlock_irqrestore(&adapter->driver_lock, flags);
+
+done:
+       LEAVE();
+       return ret;
+}
+
+/**
+ *  @brief This function allocates the command buffer and link
+ *  it to command free queue.
+ *
+ *  @param priv                A pointer to wlan_private structure
+ *  @return            0 or -1
+ */
+int libertas_allocate_cmd_buffer(wlan_private * priv)
+{
+       int ret = 0;
+       u32 ulbufsize;
+       u32 i;
+       struct cmd_ctrl_node *tempcmd_array;
+       u8 *ptempvirtualaddr;
+       wlan_adapter *adapter = priv->adapter;
+
+       ENTER();
+
+       /* Allocate and initialize cmdCtrlNode */
+       ulbufsize = sizeof(struct cmd_ctrl_node) * MRVDRV_NUM_OF_CMD_BUFFER;
+
+       if (!(tempcmd_array = kmalloc(ulbufsize, GFP_KERNEL))) {
+               lbs_pr_debug(1,
+                      "ALLOC_CMD_BUF: failed to allocate tempcmd_array\n");
+               ret = -1;
+               goto done;
+       }
+
+       adapter->cmd_array = tempcmd_array;
+       memset(adapter->cmd_array, 0, ulbufsize);
+
+       /* Allocate and initialize command buffers */
+       ulbufsize = MRVDRV_SIZE_OF_CMD_BUFFER;
+       for (i = 0; i < MRVDRV_NUM_OF_CMD_BUFFER; i++) {
+               if (!(ptempvirtualaddr = kmalloc(ulbufsize, GFP_KERNEL))) {
+                       lbs_pr_debug(1,
+                              "ALLOC_CMD_BUF: ptempvirtualaddr: out of memory\n");
+                       ret = -1;
+                       goto done;
+               }
+
+               memset(ptempvirtualaddr, 0, ulbufsize);
+
+               /* Update command buffer virtual */
+               tempcmd_array[i].bufvirtualaddr = ptempvirtualaddr;
+       }
+
+       for (i = 0; i < MRVDRV_NUM_OF_CMD_BUFFER; i++) {
+               init_waitqueue_head(&tempcmd_array[i].cmdwait_q);
+               libertas_cleanup_and_insert_cmd(priv, &tempcmd_array[i]);
+       }
+
+       ret = 0;
+      done:
+       LEAVE();
+       return ret;
+}
+
+/**
+ *  @brief This function frees the command buffer.
+ *
+ *  @param priv                A pointer to wlan_private structure
+ *  @return            0 or -1
+ */
+int libertas_free_cmd_buffer(wlan_private * priv)
+{
+       u32 ulbufsize;
+       unsigned int i;
+       struct cmd_ctrl_node *tempcmd_array;
+       wlan_adapter *adapter = priv->adapter;
+
+       ENTER();
+
+       /* need to check if cmd array is allocated or not */
+       if (adapter->cmd_array == NULL) {
+               lbs_pr_debug(1, "FREE_CMD_BUF: cmd_array is Null\n");
+               goto done;
+       }
+
+       tempcmd_array = adapter->cmd_array;
+
+       /* Release shared memory buffers */
+       ulbufsize = MRVDRV_SIZE_OF_CMD_BUFFER;
+       for (i = 0; i < MRVDRV_NUM_OF_CMD_BUFFER; i++) {
+               if (tempcmd_array[i].bufvirtualaddr) {
+                       lbs_pr_debug(1, "Free all the array\n");
+                       kfree(tempcmd_array[i].bufvirtualaddr);
+                       tempcmd_array[i].bufvirtualaddr = NULL;
+               }
+       }
+
+       /* Release cmd_ctrl_node */
+       if (adapter->cmd_array) {
+               lbs_pr_debug(1, "Free cmd_array\n");
+               kfree(adapter->cmd_array);
+               adapter->cmd_array = NULL;
+       }
+
+done:
+       LEAVE();
+       return 0;
+}
+
+/**
+ *  @brief This function gets a free command node if available in
+ *  command free queue.
+ *
+ *  @param priv                A pointer to wlan_private structure
+ *  @return cmd_ctrl_node A pointer to cmd_ctrl_node structure or NULL
+ */
+struct cmd_ctrl_node *libertas_get_free_cmd_ctrl_node(wlan_private * priv)
+{
+       struct cmd_ctrl_node *tempnode;
+       wlan_adapter *adapter = priv->adapter;
+       unsigned long flags;
+
+       if (!adapter)
+               return NULL;
+
+       spin_lock_irqsave(&adapter->driver_lock, flags);
+
+       if (!list_empty(&adapter->cmdfreeq)) {
+               tempnode = (struct cmd_ctrl_node *)adapter->cmdfreeq.next;
+               list_del((struct list_head *)tempnode);
+       } else {
+               lbs_pr_debug(1, "GET_CMD_NODE: cmd_ctrl_node is not available\n");
+               tempnode = NULL;
+       }
+
+       spin_unlock_irqrestore(&adapter->driver_lock, flags);
+
+       if (tempnode) {
+               lbs_pr_debug(3, "GET_CMD_NODE: cmdCtrlNode available\n");
+               lbs_pr_debug(3, "GET_CMD_NODE: cmdCtrlNode Address = %p\n",
+                      tempnode);
+               cleanup_cmdnode(tempnode);
+       }
+
+       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 *ptempnode)
+{
+       if (!ptempnode)
+               return;
+       ptempnode->cmdwaitqwoken = 1;
+       wake_up_interruptible(&ptempnode->cmdwait_q);
+       ptempnode->status = 0;
+       ptempnode->cmd_oid = (u32) 0;
+       ptempnode->wait_option = 0;
+       ptempnode->pdata_buf = NULL;
+
+       if (ptempnode->bufvirtualaddr != NULL)
+               memset(ptempnode->bufvirtualaddr, 0, MRVDRV_SIZE_OF_CMD_BUFFER);
+       return;
+}
+
+/**
+ *  @brief This function initializes the command node.
+ *
+ *  @param priv                A pointer to wlan_private structure
+ *  @param ptempnode   A pointer to cmd_ctrl_node structure
+ *  @param cmd_oid     cmd oid: treated as sub command
+ *  @param wait_option wait option: wait response or not
+ *  @param pdata_buf   A pointer to informaion buffer
+ *  @return            0 or -1
+ */
+void libertas_set_cmd_ctrl_node(wlan_private * priv,
+                   struct cmd_ctrl_node *ptempnode,
+                   u32 cmd_oid, u16 wait_option, void *pdata_buf)
+{
+       ENTER();
+
+       if (!ptempnode)
+               return;
+
+       ptempnode->cmd_oid = cmd_oid;
+       ptempnode->wait_option = wait_option;
+       ptempnode->pdata_buf = pdata_buf;
+
+       LEAVE();
+}
+
+/**
+ *  @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 wlan_private structure
+ *  @return       0 or -1
+ */
+int libertas_execute_next_command(wlan_private * priv)
+{
+       wlan_adapter *adapter = priv->adapter;
+       struct cmd_ctrl_node *cmdnode = NULL;
+       struct cmd_ds_command *cmdptr;
+       unsigned long flags;
+       int ret = 0;
+
+       lbs_pr_debug(1, "libertas_execute_next_command\n");
+
+       spin_lock_irqsave(&adapter->driver_lock, flags);
+
+       if (adapter->cur_cmd) {
+               lbs_pr_alert( "EXEC_NEXT_CMD: there is command in processing!\n");
+               spin_unlock_irqrestore(&adapter->driver_lock, flags);
+               ret = -1;
+               goto done;
+       }
+
+       if (!list_empty(&adapter->cmdpendingq)) {
+               cmdnode = (struct cmd_ctrl_node *)
+                   adapter->cmdpendingq.next;
+       }
+
+       spin_unlock_irqrestore(&adapter->driver_lock, flags);
+
+       if (cmdnode) {
+               lbs_pr_debug(1,
+                      "EXEC_NEXT_CMD: Got next command from cmdpendingq\n");
+               cmdptr = (struct cmd_ds_command *)cmdnode->bufvirtualaddr;
+
+               if (is_command_allowed_in_ps(cmdptr->command)) {
+                       if ((adapter->psstate == PS_STATE_SLEEP)
+                           || (adapter->psstate == PS_STATE_PRE_SLEEP)
+                           ) {
+                               lbs_pr_debug(1,
+                                      "EXEC_NEXT_CMD: Cannot send cmd 0x%x in psstate %d\n",
+                                      cmdptr->command, adapter->psstate);
+                               ret = -1;
+                               goto done;
+                       }
+                       lbs_pr_debug(1, "EXEC_NEXT_CMD: OK to send command "
+                              "0x%x in psstate %d\n",
+                              cmdptr->command, adapter->psstate);
+               } else if (adapter->psstate != PS_STATE_FULL_POWER) {
+                       /*
+                        * 1. Non-PS command:
+                        * Queue it. set needtowakeup to TRUE if current state
+                        * is SLEEP, otherwise call libertas_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 (cmdptr->command !=
+                           cpu_to_le16(cmd_802_11_ps_mode)) {
+                               /*  Prepare to send Exit PS,
+                                *  this non PS command will be sent later */
+                               if ((adapter->psstate == PS_STATE_SLEEP)
+                                   || (adapter->psstate == PS_STATE_PRE_SLEEP)
+                                   ) {
+                                       /* w/ new scheme, it will not reach here.
+                                          since it is blocked in main_thread. */
+                                       adapter->needtowakeup = 1;
+                               } else
+                                       libertas_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 =
+                                   &cmdptr->params.psmode;
+
+                               lbs_pr_debug(1,
+                                      "EXEC_NEXT_CMD: PS cmd- action=0x%x\n",
+                                      psm->action);
+                               if (psm->action !=
+                                   cpu_to_le16(cmd_subcmd_exit_ps)) {
+                                       lbs_pr_debug(1,
+                                              "EXEC_NEXT_CMD: Ignore Enter PS cmd\n");
+                                       list_del((struct list_head *)cmdnode);
+                                       libertas_cleanup_and_insert_cmd(priv, cmdnode);
+
+                                       ret = 0;
+                                       goto done;
+                               }
+
+                               if ((adapter->psstate == PS_STATE_SLEEP)
+                                   || (adapter->psstate == PS_STATE_PRE_SLEEP)
+                                   ) {
+                                       lbs_pr_debug(1,
+                                              "EXEC_NEXT_CMD: Ignore ExitPS cmd in sleep\n");
+                                       list_del((struct list_head *)cmdnode);
+                                       libertas_cleanup_and_insert_cmd(priv, cmdnode);
+                                       adapter->needtowakeup = 1;
+
+                                       ret = 0;
+                                       goto done;
+                               }
+
+                               lbs_pr_debug(1,
+                                      "EXEC_NEXT_CMD: Sending Exit_PS down...\n");
+                       }
+               }
+               list_del((struct list_head *)cmdnode);
+               lbs_pr_debug(1, "EXEC_NEXT_CMD: Sending 0x%04X command\n",
+                      cmdptr->command);
+               DownloadcommandToStation(priv, cmdnode);
+       } else {
+               /*
+                * check if in power save mode, if yes, put the device back
+                * to PS mode
+                */
+               if ((adapter->psmode != wlan802_11powermodecam) &&
+                   (adapter->psstate == PS_STATE_FULL_POWER) &&
+                   (adapter->connect_status == libertas_connected)) {
+                       if (adapter->secinfo.WPAenabled
+                           || adapter->secinfo.WPA2enabled) {
+                               /* check for valid WPA group keys */
+                               if (adapter->wpa_mcast_key.len
+                                   || adapter->wpa_unicast_key.len) {
+                                       lbs_pr_debug(1,
+                                              "EXEC_NEXT_CMD: WPA enabled and GTK_SET"
+                                              " go back to PS_SLEEP");
+                                       libertas_ps_sleep(priv, 0);
+                               }
+                       } else {
+                               lbs_pr_debug(1,
+                                      "EXEC_NEXT_CMD: command PendQ is empty,"
+                                      " go back to PS_SLEEP");
+                               libertas_ps_sleep(priv, 0);
+                       }
+               }
+       }
+
+       ret = 0;
+done:
+       return ret;
+}
+
+void libertas_send_iwevcustom_event(wlan_private * priv, s8 * str)
+{
+       union iwreq_data iwrq;
+       u8 buf[50];
+
+       ENTER();
+
+       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_pr_debug(1, "Event Indication string = %s\n",
+              (char *)buf);
+       lbs_pr_debug(1, "Event Indication String length = %d\n", iwrq.data.length);
+
+       lbs_pr_debug(1, "Sending wireless event IWEVCUSTOM for %s\n", str);
+       wireless_send_event(priv->wlan_dev.netdev, IWEVCUSTOM, &iwrq, buf);
+
+       LEAVE();
+       return;
+}
+
+static int sendconfirmsleep(wlan_private * priv, u8 * cmdptr, u16 size)
+{
+       unsigned long flags;
+       wlan_adapter *adapter = priv->adapter;
+       int ret = 0;
+
+       ENTER();
+
+       lbs_pr_debug(1, "SEND_SLEEPC_CMD: Before download, size of cmd = %d\n",
+              size);
+
+       lbs_dbg_hex("SEND_SLEEPC_CMD: Sleep confirm command", cmdptr, size);
+
+       ret = libertas_sbi_host_to_card(priv, MVMS_CMD, cmdptr, size);
+       priv->wlan_dev.dnld_sent = DNLD_RES_RECEIVED;
+
+       spin_lock_irqsave(&adapter->driver_lock, flags);
+       if (adapter->intcounter || adapter->currenttxskb)
+               lbs_pr_debug(1, "SEND_SLEEPC_CMD: intcounter=%d currenttxskb=%p\n",
+                      adapter->intcounter, adapter->currenttxskb);
+       spin_unlock_irqrestore(&adapter->driver_lock, flags);
+
+       if (ret) {
+               lbs_pr_alert(
+                      "SEND_SLEEPC_CMD: Host to Card failed for Confirm Sleep\n");
+       } else {
+               spin_lock_irqsave(&adapter->driver_lock, flags);
+               if (!adapter->intcounter) {
+                       adapter->psstate = PS_STATE_SLEEP;
+               } else {
+                       lbs_pr_debug(1, "SEND_SLEEPC_CMD: After sent,IntC=%d\n",
+                              adapter->intcounter);
+               }
+               spin_unlock_irqrestore(&adapter->driver_lock, flags);
+
+               lbs_pr_debug(1, "SEND_SLEEPC_CMD: Sent Confirm Sleep command\n");
+               lbs_pr_debug(1, "+");
+       }
+
+       LEAVE();
+       return ret;
+}
+
+void libertas_ps_sleep(wlan_private * priv, int wait_option)
+{
+
+       ENTER();
+
+       /*
+        * PS is currently supported only in Infrastructure mode
+        * Remove this check if it is to be supported in IBSS mode also
+        */
+
+       libertas_prepare_and_send_command(priv, cmd_802_11_ps_mode,
+                             cmd_subcmd_enter_ps, wait_option, 0, NULL);
+
+       LEAVE();
+       return;
+}
+
+/**
+ *  @brief This function sends Eixt_PS command to firmware.
+ *
+ *  @param priv        A pointer to wlan_private structure
+ *  @param wait_option wait response or not
+ *  @return            n/a
+ */
+void libertas_ps_wakeup(wlan_private * priv, int wait_option)
+{
+       enum WLAN_802_11_POWER_MODE Localpsmode;
+
+       ENTER();
+
+       Localpsmode = wlan802_11powermodecam;
+
+       lbs_pr_debug(1, "Exit_PS: Localpsmode = %d\n", Localpsmode);
+
+       libertas_prepare_and_send_command(priv, cmd_802_11_ps_mode,
+                             cmd_subcmd_exit_ps,
+                             wait_option, 0, &Localpsmode);
+
+       LEAVE();
+       return;
+}
+
+/**
+ *  @brief This function checks condition and prepares to
+ *  send sleep confirm command to firmware if ok.
+ *
+ *  @param priv        A pointer to wlan_private structure
+ *  @param psmode      Power Saving mode
+ *  @return            n/a
+ */
+void libertas_ps_confirm_sleep(wlan_private * priv, u16 psmode)
+{
+       unsigned long flags =0;
+       wlan_adapter *adapter = priv->adapter;
+       u8 allowed = 1;
+
+       ENTER();
+
+       if (priv->wlan_dev.dnld_sent) {
+               allowed = 0;
+               lbs_pr_debug(1, "D");
+       }
+
+       spin_lock_irqsave(&adapter->driver_lock, flags);
+       if (adapter->cur_cmd) {
+               allowed = 0;
+               lbs_pr_debug(1, "C");
+       }
+       if (adapter->intcounter > 0) {
+               allowed = 0;
+               lbs_pr_debug(1, "I%d", adapter->intcounter);
+       }
+       spin_unlock_irqrestore(&adapter->driver_lock, flags);
+
+       if (allowed) {
+               lbs_pr_debug(1, "Sending libertas_ps_confirm_sleep\n");
+               sendconfirmsleep(priv, (u8 *) & adapter->libertas_ps_confirm_sleep,
+                                sizeof(struct PS_CMD_ConfirmSleep));
+       } else {
+               lbs_pr_debug(1, "Sleep Confirm has been delayed\n");
+       }
+
+       LEAVE();
+}
diff --git a/drivers/net/wireless/libertas/cmdresp.c b/drivers/net/wireless/libertas/cmdresp.c
new file mode 100644 (file)
index 0000000..cdb012c
--- /dev/null
@@ -0,0 +1,1031 @@
+/**
+  * 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 "sbi.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 wlan_private structure
+ *  @return       n/a
+ */
+void libertas_mac_event_disconnected(wlan_private * priv)
+{
+       wlan_adapter *adapter = priv->adapter;
+       union iwreq_data wrqu;
+
+       if (adapter->connect_status != libertas_connected)
+               return;
+
+       lbs_pr_debug(1, "Handles disconnect event.\n");
+
+       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->wlan_dev.netdev, SIOCGIWAP, &wrqu, NULL);
+
+       /* Free Tx and Rx packets */
+       kfree_skb(priv->adapter->currenttxskb);
+       priv->adapter->currenttxskb = NULL;
+
+       /* report disconnect to upper layer */
+       netif_stop_queue(priv->wlan_dev.netdev);
+       netif_carrier_off(priv->wlan_dev.netdev);
+
+       /* reset SNR/NF/RSSI values */
+       memset(adapter->SNR, 0x00, sizeof(adapter->SNR));
+       memset(adapter->NF, 0x00, sizeof(adapter->NF));
+       memset(adapter->RSSI, 0x00, sizeof(adapter->RSSI));
+       memset(adapter->rawSNR, 0x00, sizeof(adapter->rawSNR));
+       memset(adapter->rawNF, 0x00, sizeof(adapter->rawNF));
+       adapter->nextSNRNF = 0;
+       adapter->numSNRNF = 0;
+       adapter->rxpd_rate = 0;
+       lbs_pr_debug(1, "Current SSID=%s, ssid length=%u\n",
+              adapter->curbssparams.ssid.ssid,
+              adapter->curbssparams.ssid.ssidlength);
+       lbs_pr_debug(1, "Previous SSID=%s, ssid length=%u\n",
+              adapter->previousssid.ssid, adapter->previousssid.ssidlength);
+
+       /* reset internal flags */
+       adapter->secinfo.WPAenabled = 0;
+       adapter->secinfo.WPA2enabled = 0;
+       adapter->wpa_ie_len = 0;
+       adapter->secinfo.auth1xalg = WLAN_1X_AUTH_ALG_NONE;
+       adapter->secinfo.Encryptionmode = CIPHER_NONE;
+
+       adapter->connect_status = libertas_disconnected;
+
+       /*
+        * memorize the previous SSID and BSSID
+        * it could be used for re-assoc
+        */
+       memcpy(&adapter->previousssid,
+              &adapter->curbssparams.ssid, sizeof(struct WLAN_802_11_SSID));
+       memcpy(adapter->previousbssid,
+              adapter->curbssparams.bssid, ETH_ALEN);
+
+       /* need to erase the current SSID and BSSID info */
+       adapter->pattemptedbssdesc = NULL;
+       memset(&adapter->curbssparams, 0, sizeof(adapter->curbssparams));
+
+       if (adapter->psstate != PS_STATE_FULL_POWER) {
+               /* make firmware to exit PS mode */
+               lbs_pr_debug(1, "Disconnected, so exit PS mode.\n");
+               libertas_ps_wakeup(priv, 0);
+       }
+}
+
+/**
+ *  @brief This function handles MIC failure event.
+ *
+ *  @param priv    A pointer to wlan_private structure
+ *  @para  event   the event id
+ *  @return       n/a
+ */
+static void handle_mic_failureevent(wlan_private * priv, u32 event)
+{
+       char buf[50];
+
+       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 ");
+       }
+
+       libertas_send_iwevcustom_event(priv, buf);
+}
+
+static int wlan_ret_reg_access(wlan_private * priv,
+                              u16 type, struct cmd_ds_command *resp)
+{
+       wlan_adapter *adapter = priv->adapter;
+
+       ENTER();
+
+       switch (type) {
+       case cmd_ret_mac_reg_access:
+               {
+                       struct cmd_ds_mac_reg_access *reg;
+
+                       reg =
+                           (struct cmd_ds_mac_reg_access *)&resp->params.
+                           macreg;
+
+                       adapter->offsetvalue.offset = reg->offset;
+                       adapter->offsetvalue.value = reg->value;
+                       break;
+               }
+
+       case cmd_ret_bbp_reg_access:
+               {
+                       struct cmd_ds_bbp_reg_access *reg;
+                       reg =
+                           (struct cmd_ds_bbp_reg_access *)&resp->params.
+                           bbpreg;
+
+                       adapter->offsetvalue.offset = reg->offset;
+                       adapter->offsetvalue.value = reg->value;
+                       break;
+               }
+
+       case cmd_ret_rf_reg_access:
+               {
+                       struct cmd_ds_rf_reg_access *reg;
+                       reg =
+                           (struct cmd_ds_rf_reg_access *)&resp->params.
+                           rfreg;
+
+                       adapter->offsetvalue.offset = reg->offset;
+                       adapter->offsetvalue.value = reg->value;
+                       break;
+               }
+
+       default:
+               LEAVE();
+               return -1;
+       }
+
+       LEAVE();
+       return 0;
+}
+
+static int wlan_ret_get_hw_spec(wlan_private * priv,
+                               struct cmd_ds_command *resp)
+{
+       u32 i;
+       struct cmd_ds_get_hw_spec *hwspec = &resp->params.hwspec;
+       wlan_adapter *adapter = priv->adapter;
+       int ret = 0;
+
+       ENTER();
+
+       adapter->fwcapinfo = le32_to_cpu(hwspec->fwcapinfo);
+
+       adapter->fwreleasenumber = hwspec->fwreleasenumber;
+
+       lbs_pr_debug(1, "GET_HW_SPEC: FWReleaseVersion- 0x%X\n",
+              adapter->fwreleasenumber);
+       lbs_pr_debug(1, "GET_HW_SPEC: Permanent addr- %2x:%2x:%2x:%2x:%2x:%2x\n",
+              hwspec->permanentaddr[0], hwspec->permanentaddr[1],
+              hwspec->permanentaddr[2], hwspec->permanentaddr[3],
+              hwspec->permanentaddr[4], hwspec->permanentaddr[5]);
+       lbs_pr_debug(1, "GET_HW_SPEC: hwifversion=0x%X  version=0x%X\n",
+              hwspec->hwifversion, hwspec->version);
+
+       adapter->regioncode = le16_to_cpu(hwspec->regioncode);
+
+       for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) {
+               /* use the region code to search for the index */
+               if (adapter->regioncode == libertas_region_code_to_index[i]) {
+                       adapter->regiontableindex = (u16) i;
+                       break;
+               }
+       }
+
+       /* if it's unidentified region code, use the default (USA) */
+       if (i >= MRVDRV_MAX_REGION_CODE) {
+               adapter->regioncode = 0x10;
+               adapter->regiontableindex = 0;
+               lbs_pr_info(
+                      "unidentified region code, use the default (USA)\n");
+       }
+
+       if (adapter->current_addr[0] == 0xff) {
+               memmove(adapter->current_addr, hwspec->permanentaddr,
+                       ETH_ALEN);
+       }
+
+       memcpy(priv->wlan_dev.netdev->dev_addr, adapter->current_addr, ETH_ALEN);
+       memcpy(priv->mesh_dev->dev_addr, adapter->current_addr, ETH_ALEN);
+
+       if (libertas_set_regiontable(priv, adapter->regioncode, 0)) {
+               ret = -1;
+               goto done;
+       }
+
+       if (libertas_set_universaltable(priv, 0)) {
+               ret = -1;
+               goto done;
+       }
+
+      done:
+       LEAVE();
+       return ret;
+}
+
+static int wlan_ret_802_11_sleep_params(wlan_private * priv,
+                                       struct cmd_ds_command *resp)
+{
+       struct cmd_ds_802_11_sleep_params *sp = &resp->params.sleep_params;
+       wlan_adapter *adapter = priv->adapter;
+
+       ENTER();
+
+       lbs_pr_debug(1, "error=%x offset=%x stabletime=%x calcontrol=%x\n"
+              " extsleepclk=%x\n", sp->error, sp->offset,
+              sp->stabletime, sp->calcontrol, sp->externalsleepclk);
+       adapter->sp.sp_error = le16_to_cpu(sp->error);
+       adapter->sp.sp_offset = le16_to_cpu(sp->offset);
+       adapter->sp.sp_stabletime = le16_to_cpu(sp->stabletime);
+       adapter->sp.sp_calcontrol = le16_to_cpu(sp->calcontrol);
+       adapter->sp.sp_extsleepclk = le16_to_cpu(sp->externalsleepclk);
+       adapter->sp.sp_reserved = le16_to_cpu(sp->reserved);
+
+       LEAVE();
+       return 0;
+}
+
+static int wlan_ret_802_11_stat(wlan_private * priv,
+                               struct cmd_ds_command *resp)
+{
+/*     currently adapter->wlan802_11Stat is unused
+
+       struct cmd_ds_802_11_get_stat *p11Stat = &resp->params.gstat;
+       wlan_adapter *adapter = priv->adapter;
+
+       // TODO Convert it to Big endian befor copy
+       memcpy(&adapter->wlan802_11Stat,
+              p11Stat, sizeof(struct cmd_ds_802_11_get_stat));
+*/
+       return 0;
+}
+
+static int wlan_ret_802_11_snmp_mib(wlan_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);
+
+       ENTER();
+
+       lbs_pr_debug(1, "SNMP_RESP: value of the oid = %x, querytype=%x\n", oid,
+              querytype);
+       lbs_pr_debug(1, "SNMP_RESP: Buf size  = %x\n",
+              le16_to_cpu(smib->bufsize));
+
+       if (querytype == cmd_act_get) {
+               switch (oid) {
+               case fragthresh_i:
+                       priv->adapter->fragthsd =
+                           le16_to_cpu(*
+                                            ((unsigned short *)(smib->value)));
+                       lbs_pr_debug(1, "SNMP_RESP: fragthsd =%u\n",
+                              priv->adapter->fragthsd);
+                       break;
+               case rtsthresh_i:
+                       priv->adapter->rtsthsd =
+                           le16_to_cpu(*
+                                            ((unsigned short *)(smib->value)));
+                       lbs_pr_debug(1, "SNMP_RESP: rtsthsd =%u\n",
+                              priv->adapter->rtsthsd);
+                       break;
+               case short_retrylim_i:
+                       priv->adapter->txretrycount =
+                           le16_to_cpu(*
+                                            ((unsigned short *)(smib->value)));
+                       lbs_pr_debug(1, "SNMP_RESP: txretrycount =%u\n",
+                              priv->adapter->rtsthsd);
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       LEAVE();
+       return 0;
+}
+
+static int wlan_ret_802_11_key_material(wlan_private * priv,
+                                       struct cmd_ds_command *resp)
+{
+       struct cmd_ds_802_11_key_material *pkeymaterial =
+           &resp->params.keymaterial;
+       wlan_adapter *adapter = priv->adapter;
+       u16 action = le16_to_cpu(pkeymaterial->action);
+
+       ENTER();
+
+       /* 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 WLAN_802_11_KEY * pkey;
+                       u16 key_info = le16_to_cpu(pkeyparamset->keyinfo);
+                       u16 param_set_len = le16_to_cpu(pkeyparamset->length);
+                       u8 * end;
+                       u16 key_len = le16_to_cpu(pkeyparamset->keylen);
+
+                       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_info & KEY_INFO_WPA_UNICAST)
+                               pkey = &adapter->wpa_unicast_key;
+                       else if (key_info & KEY_INFO_WPA_MCAST)
+                               pkey = &adapter->wpa_mcast_key;
+                       else
+                               break;
+
+                       /* Copy returned key into driver */
+                       memset(pkey, 0, sizeof(struct WLAN_802_11_KEY));
+                       if (key_len > sizeof(pkey->key))
+                               break;
+                       pkey->type = le16_to_cpu(pkeyparamset->keytypeid);
+                       pkey->flags = le16_to_cpu(pkeyparamset->keyinfo);
+                       pkey->len = le16_to_cpu(pkeyparamset->keylen);
+                       memcpy(pkey->key, pkeyparamset->key, pkey->len);
+
+                       buf_ptr = end + 1;
+               }
+       }
+
+       LEAVE();
+       return 0;
+}
+
+static int wlan_ret_802_11_mac_address(wlan_private * priv,
+                                      struct cmd_ds_command *resp)
+{
+       struct cmd_ds_802_11_mac_address *macadd = &resp->params.macadd;
+       wlan_adapter *adapter = priv->adapter;
+
+       ENTER();
+
+       memcpy(adapter->current_addr, macadd->macadd, ETH_ALEN);
+
+       LEAVE();
+       return 0;
+}
+
+static int wlan_ret_802_11_rf_tx_power(wlan_private * priv,
+                                      struct cmd_ds_command *resp)
+{
+       struct cmd_ds_802_11_rf_tx_power *rtp = &resp->params.txp;
+       wlan_adapter *adapter = priv->adapter;
+
+       ENTER();
+
+       adapter->txpowerlevel = le16_to_cpu(rtp->currentlevel);
+
+       lbs_pr_debug(1, "Current TxPower Level = %d\n", adapter->txpowerlevel);
+
+       LEAVE();
+       return 0;
+}
+
+static int wlan_ret_802_11_rf_antenna(wlan_private * priv,
+                                     struct cmd_ds_command *resp)
+{
+       struct cmd_ds_802_11_rf_antenna *pAntenna = &resp->params.rant;
+       wlan_adapter *adapter = priv->adapter;
+       u16 action = le16_to_cpu(pAntenna->action);
+
+       if (action == cmd_act_get_rx)
+               adapter->rxantennamode =
+                   le16_to_cpu(pAntenna->antennamode);
+
+       if (action == cmd_act_get_tx)
+               adapter->txantennamode =
+                   le16_to_cpu(pAntenna->antennamode);
+
+       lbs_pr_debug(1, "RF_ANT_RESP: action = 0x%x, mode = 0x%04x\n",
+              action, le16_to_cpu(pAntenna->antennamode));
+
+       return 0;
+}
+
+static int wlan_ret_802_11_rate_adapt_rateset(wlan_private * priv,
+                                             struct cmd_ds_command *resp)
+{
+       struct cmd_ds_802_11_rate_adapt_rateset *rates =
+           &resp->params.rateset;
+       wlan_adapter *adapter = priv->adapter;
+
+       ENTER();
+
+       if (rates->action == cmd_act_get) {
+               adapter->enablehwauto = rates->enablehwauto;
+               adapter->ratebitmap = rates->bitmap;
+       }
+
+       LEAVE();
+
+       return 0;
+}
+
+static int wlan_ret_802_11_data_rate(wlan_private * priv,
+                                    struct cmd_ds_command *resp)
+{
+       struct cmd_ds_802_11_data_rate *pdatarate = &resp->params.drate;
+       wlan_adapter *adapter = priv->adapter;
+       u8 dot11datarate;
+
+       ENTER();
+
+       lbs_dbg_hex("DATA_RATE_RESP: data_rate- ",
+               (u8 *) pdatarate, sizeof(struct cmd_ds_802_11_data_rate));
+
+       dot11datarate = pdatarate->datarate[0];
+       if (pdatarate->action == cmd_act_get_tx_rate) {
+               memcpy(adapter->libertas_supported_rates, pdatarate->datarate,
+                      sizeof(adapter->libertas_supported_rates));
+       }
+       adapter->datarate = libertas_index_to_data_rate(dot11datarate);
+
+       LEAVE();
+       return 0;
+}
+
+static int wlan_ret_802_11_rf_channel(wlan_private * priv,
+                                     struct cmd_ds_command *resp)
+{
+       struct cmd_ds_802_11_rf_channel *rfchannel =
+           &resp->params.rfchannel;
+       wlan_adapter *adapter = priv->adapter;
+       u16 action = le16_to_cpu(rfchannel->action);
+       u16 newchannel = le16_to_cpu(rfchannel->currentchannel);
+
+       ENTER();
+
+       if (action == cmd_opt_802_11_rf_channel_get
+           && adapter->curbssparams.channel != newchannel) {
+               lbs_pr_debug(1, "channel Switch: %d to %d\n",
+                      adapter->curbssparams.channel, newchannel);
+
+               /* Update the channel again */
+               adapter->curbssparams.channel = newchannel;
+       }
+
+       LEAVE();
+       return 0;
+}
+
+static int wlan_ret_802_11_rssi(wlan_private * priv,
+                               struct cmd_ds_command *resp)
+{
+       struct cmd_ds_802_11_rssi_rsp *rssirsp = &resp->params.rssirsp;
+       wlan_adapter *adapter = priv->adapter;
+
+       /* store the non average value */
+       adapter->SNR[TYPE_BEACON][TYPE_NOAVG] = le16_to_cpu(rssirsp->SNR);
+       adapter->NF[TYPE_BEACON][TYPE_NOAVG] =
+           le16_to_cpu(rssirsp->noisefloor);
+
+       adapter->SNR[TYPE_BEACON][TYPE_AVG] = le16_to_cpu(rssirsp->avgSNR);
+       adapter->NF[TYPE_BEACON][TYPE_AVG] =
+           le16_to_cpu(rssirsp->avgnoisefloor);
+
+       adapter->RSSI[TYPE_BEACON][TYPE_NOAVG] =
+           CAL_RSSI(adapter->SNR[TYPE_BEACON][TYPE_NOAVG],
+                    adapter->NF[TYPE_BEACON][TYPE_NOAVG]);
+
+       adapter->RSSI[TYPE_BEACON][TYPE_AVG] =
+           CAL_RSSI(adapter->SNR[TYPE_BEACON][TYPE_AVG] / AVG_SCALE,
+                    adapter->NF[TYPE_BEACON][TYPE_AVG] / AVG_SCALE);
+
+       lbs_pr_debug(1, "Beacon RSSI value = 0x%x\n",
+              adapter->RSSI[TYPE_BEACON][TYPE_AVG]);
+
+       return 0;
+}
+
+static int wlan_ret_802_11_eeprom_access(wlan_private * priv,
+                                 struct cmd_ds_command *resp)
+{
+       wlan_adapter *adapter = priv->adapter;
+       struct wlan_ioctl_regrdwr *pbuf;
+       pbuf = (struct wlan_ioctl_regrdwr *) adapter->prdeeprom;
+
+       lbs_pr_debug(1, "eeprom read len=%x\n",
+              le16_to_cpu(resp->params.rdeeprom.bytecount));
+       if (pbuf->NOB < le16_to_cpu(resp->params.rdeeprom.bytecount)) {
+               pbuf->NOB = 0;
+               lbs_pr_debug(1, "eeprom read return length is 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_dbg_hex("adapter", (char *)&pbuf->value,
+                       le16_to_cpu(resp->params.rdeeprom.bytecount));
+       }
+       return 0;
+}
+
+static int wlan_ret_get_log(wlan_private * priv,
+                           struct cmd_ds_command *resp)
+{
+       struct cmd_ds_802_11_get_log *logmessage =
+           (struct cmd_ds_802_11_get_log *)&resp->params.glog;
+       wlan_adapter *adapter = priv->adapter;
+
+       ENTER();
+
+       /* TODO Convert it to Big Endian before copy */
+       memcpy(&adapter->logmsg, logmessage,
+              sizeof(struct cmd_ds_802_11_get_log));
+
+       LEAVE();
+       return 0;
+}
+
+static inline int handle_cmd_response(u16 respcmd,
+                                     struct cmd_ds_command *resp,
+                                     wlan_private *priv)
+{
+       int ret = 0;
+       unsigned long flags;
+       wlan_adapter *adapter = priv->adapter;
+
+       switch (respcmd) {
+       case cmd_ret_mac_reg_access:
+       case cmd_ret_bbp_reg_access:
+       case cmd_ret_rf_reg_access:
+               ret = wlan_ret_reg_access(priv, respcmd, resp);
+               break;
+
+       case cmd_ret_hw_spec_info:
+               ret = wlan_ret_get_hw_spec(priv, resp);
+               break;
+
+       case cmd_ret_802_11_scan:
+               ret = libertas_ret_80211_scan(priv, resp);
+               break;
+
+       case cmd_ret_802_11_get_log:
+               ret = wlan_ret_get_log(priv, resp);
+               break;
+
+       case cmd_ret_802_11_associate:
+       case cmd_ret_802_11_reassociate:
+               ret = libertas_ret_80211_associate(priv, resp);
+               break;
+
+       case cmd_ret_802_11_disassociate:
+       case cmd_ret_802_11_deauthenticate:
+               ret = libertas_ret_80211_disassociate(priv, resp);
+               break;
+
+       case cmd_ret_802_11_ad_hoc_start:
+       case cmd_ret_802_11_ad_hoc_join:
+               ret = libertas_ret_80211_ad_hoc_start(priv, resp);
+               break;
+
+       case cmd_ret_802_11_stat:
+               ret = wlan_ret_802_11_stat(priv, resp);
+               break;
+
+       case cmd_ret_802_11_snmp_mib:
+               ret = wlan_ret_802_11_snmp_mib(priv, resp);
+               break;
+
+       case cmd_ret_802_11_rf_tx_power:
+               ret = wlan_ret_802_11_rf_tx_power(priv, resp);
+               break;
+
+       case cmd_ret_802_11_set_afc:
+       case cmd_ret_802_11_get_afc:
+               spin_lock_irqsave(&adapter->driver_lock, flags);
+               memmove(adapter->cur_cmd->pdata_buf,
+                       &resp->params.afc,
+                       sizeof(struct cmd_ds_802_11_afc));
+               spin_unlock_irqrestore(&adapter->driver_lock, flags);
+
+               break;
+       case cmd_ret_802_11_rf_antenna:
+               ret = wlan_ret_802_11_rf_antenna(priv, resp);
+               break;
+
+       case cmd_ret_mac_multicast_adr:
+       case cmd_ret_mac_control:
+       case cmd_ret_802_11_set_wep:
+       case cmd_ret_802_11_reset:
+       case cmd_ret_802_11_authenticate:
+       case cmd_ret_802_11_radio_control:
+       case cmd_ret_802_11_beacon_stop:
+       case cmd_ret_802_11_enable_rsn:
+               break;
+
+       case cmd_ret_802_11_data_rate:
+               ret = wlan_ret_802_11_data_rate(priv, resp);
+               break;
+       case cmd_ret_802_11_rate_adapt_rateset:
+               ret = wlan_ret_802_11_rate_adapt_rateset(priv, resp);
+               break;
+       case cmd_ret_802_11_rf_channel:
+               ret = wlan_ret_802_11_rf_channel(priv, resp);
+               break;
+
+       case cmd_ret_802_11_rssi:
+               ret = wlan_ret_802_11_rssi(priv, resp);
+               break;
+
+       case cmd_ret_802_11_mac_address:
+               ret = wlan_ret_802_11_mac_address(priv, resp);
+               break;
+
+       case cmd_ret_802_11_ad_hoc_stop:
+               ret = libertas_ret_80211_ad_hoc_stop(priv, resp);
+               break;
+
+       case cmd_ret_802_11_key_material:
+               lbs_pr_debug(1, "CMD_RESP: KEY_MATERIAL command response\n");
+               ret = wlan_ret_802_11_key_material(priv, resp);
+               break;
+
+       case cmd_ret_802_11_eeprom_access:
+               ret = wlan_ret_802_11_eeprom_access(priv, resp);
+               break;
+
+       case cmd_ret_802_11d_domain_info:
+               ret = libertas_ret_802_11d_domain_info(priv, resp);
+               break;
+
+       case cmd_ret_802_11_sleep_params:
+               ret = wlan_ret_802_11_sleep_params(priv, resp);
+               break;
+       case cmd_ret_802_11_inactivity_timeout:
+               spin_lock_irqsave(&adapter->driver_lock, flags);
+               *((u16 *) adapter->cur_cmd->pdata_buf) =
+                   le16_to_cpu(resp->params.inactivity_timeout.timeout);
+               spin_unlock_irqrestore(&adapter->driver_lock, flags);
+               break;
+
+       case cmd_ret_802_11_tpc_cfg:
+               spin_lock_irqsave(&adapter->driver_lock, flags);
+               memmove(adapter->cur_cmd->pdata_buf,
+                       &resp->params.tpccfg,
+                       sizeof(struct cmd_ds_802_11_tpc_cfg));
+               spin_unlock_irqrestore(&adapter->driver_lock, flags);
+               break;
+       case cmd_ret_802_11_led_gpio_ctrl:
+               spin_lock_irqsave(&adapter->driver_lock, flags);
+               memmove(adapter->cur_cmd->pdata_buf,
+                       &resp->params.ledgpio,
+                       sizeof(struct cmd_ds_802_11_led_ctrl));
+               spin_unlock_irqrestore(&adapter->driver_lock, flags);
+               break;
+       case cmd_ret_802_11_pwr_cfg:
+               spin_lock_irqsave(&adapter->driver_lock, flags);
+               memmove(adapter->cur_cmd->pdata_buf,
+                       &resp->params.pwrcfg,
+                       sizeof(struct cmd_ds_802_11_pwr_cfg));
+               spin_unlock_irqrestore(&adapter->driver_lock, flags);
+
+               break;
+
+       case cmd_ret_get_tsf:
+               spin_lock_irqsave(&adapter->driver_lock, flags);
+               memcpy(priv->adapter->cur_cmd->pdata_buf,
+                      &resp->params.gettsf.tsfvalue, sizeof(u64));
+               spin_unlock_irqrestore(&adapter->driver_lock, flags);
+               break;
+       case cmd_ret_bt_access:
+               spin_lock_irqsave(&adapter->driver_lock, flags);
+               if (adapter->cur_cmd->pdata_buf)
+                       memcpy(adapter->cur_cmd->pdata_buf,
+                              &resp->params.bt.addr1, 2 * ETH_ALEN);
+               spin_unlock_irqrestore(&adapter->driver_lock, flags);
+               break;
+       case cmd_ret_fwt_access:
+               spin_lock_irqsave(&adapter->driver_lock, flags);
+               if (adapter->cur_cmd->pdata_buf)
+                       memcpy(adapter->cur_cmd->pdata_buf,
+                              &resp->params.fwt,
+                               sizeof(resp->params.fwt));
+               spin_unlock_irqrestore(&adapter->driver_lock, flags);
+               break;
+       case cmd_ret_mesh_access:
+               if (adapter->cur_cmd->pdata_buf)
+                       memcpy(adapter->cur_cmd->pdata_buf,
+                              &resp->params.mesh,
+                              sizeof(resp->params.mesh));
+               break;
+       case cmd_rte_802_11_tx_rate_query:
+               priv->adapter->txrate = resp->params.txrate.txrate;
+               break;
+       default:
+               lbs_pr_debug(1, "CMD_RESP: Unknown command response %#x\n",
+                      resp->command);
+               break;
+       }
+       return ret;
+}
+
+int libertas_process_rx_command(wlan_private * priv)
+{
+       u16 respcmd;
+       struct cmd_ds_command *resp;
+       wlan_adapter *adapter = priv->adapter;
+       int ret = 0;
+       ulong flags;
+       u16 result;
+
+       ENTER();
+
+       lbs_pr_debug(1, "CMD_RESP: @ %lu\n", jiffies);
+
+       /* Now we got response from FW, cancel the command timer */
+       del_timer(&adapter->command_timer);
+
+       mutex_lock(&adapter->lock);
+       spin_lock_irqsave(&adapter->driver_lock, flags);
+
+       if (!adapter->cur_cmd) {
+               lbs_pr_debug(1, "CMD_RESP: NULL cur_cmd=%p\n", adapter->cur_cmd);
+               ret = -1;
+               spin_unlock_irqrestore(&adapter->driver_lock, flags);
+               goto done;
+       }
+       resp = (struct cmd_ds_command *)(adapter->cur_cmd->bufvirtualaddr);
+
+       lbs_dbg_hex("CMD_RESP:", adapter->cur_cmd->bufvirtualaddr,
+               priv->wlan_dev.upld_len);
+
+       respcmd = le16_to_cpu(resp->command);
+
+       result = le16_to_cpu(resp->result);
+
+       lbs_pr_debug(1, "CMD_RESP: %x result: %d length: %d\n", respcmd,
+              result, priv->wlan_dev.upld_len);
+
+       if (!(respcmd & 0x8000)) {
+               lbs_pr_debug(1, "Invalid response to command!");
+               adapter->cur_cmd_retcode = -1;
+               __libertas_cleanup_and_insert_cmd(priv, adapter->cur_cmd);
+               adapter->nr_cmd_pending--;
+               adapter->cur_cmd = NULL;
+               spin_unlock_irqrestore(&adapter->driver_lock, flags);
+               ret = -1;
+               goto done;
+       }
+
+       /* Store the response code to cur_cmd_retcode. */
+       adapter->cur_cmd_retcode = le16_to_cpu(resp->result);
+
+       if (respcmd == cmd_ret_802_11_ps_mode) {
+               struct cmd_ds_802_11_ps_mode *psmode;
+
+               psmode = &resp->params.psmode;
+               lbs_pr_debug(1,
+                      "CMD_RESP: PS_MODE cmd reply result=%#x action=0x%X\n",
+                      resp->result, psmode->action);
+               psmode->action = cpu_to_le16(psmode->action);
+
+               if (result) {
+                       lbs_pr_debug(1, "CMD_RESP: PS command failed- %#x \n",
+                              resp->result);
+                       if (adapter->inframode == wlan802_11ibss) {
+                               /*
+                                * We should not re-try enter-ps command in
+                                * ad-hoc mode. It takes place in
+                                * libertas_execute_next_command().
+                                */
+                               if (psmode->action == cmd_subcmd_enter_ps)
+                                       adapter->psmode =
+                                           wlan802_11powermodecam;
+                       }
+               } else if (psmode->action == cmd_subcmd_enter_ps) {
+                       adapter->needtowakeup = 0;
+                       adapter->psstate = PS_STATE_AWAKE;
+
+                       lbs_pr_debug(1, "CMD_RESP: Enter_PS command response\n");
+                       if (adapter->connect_status != libertas_connected) {
+                               /*
+                                * When Deauth Event received before Enter_PS command
+                                * response, We need to wake up the firmware.
+                                */
+                               lbs_pr_debug(1,
+                                      "Disconnected, Going to invoke libertas_ps_wakeup\n");
+
+                               mutex_unlock(&adapter->lock);
+                               spin_unlock_irqrestore(&adapter->driver_lock, flags);
+                               libertas_ps_wakeup(priv, 0);
+                               mutex_lock(&adapter->lock);
+                               spin_lock_irqsave(&adapter->driver_lock, flags);
+                       }
+               } else if (psmode->action == cmd_subcmd_exit_ps) {
+                       adapter->needtowakeup = 0;
+                       adapter->psstate = PS_STATE_FULL_POWER;
+                       lbs_pr_debug(1, "CMD_RESP: Exit_PS command response\n");
+               } else {
+                       lbs_pr_debug(1, "CMD_RESP: PS- action=0x%X\n",
+                              psmode->action);
+               }
+
+               __libertas_cleanup_and_insert_cmd(priv, adapter->cur_cmd);
+               adapter->nr_cmd_pending--;
+               adapter->cur_cmd = NULL;
+               spin_unlock_irqrestore(&adapter->driver_lock, flags);
+
+               ret = 0;
+               goto done;
+       }
+
+       if (adapter->cur_cmd->cmdflags & CMD_F_HOSTCMD) {
+               /* Copy the response back to response buffer */
+               memcpy(adapter->cur_cmd->pdata_buf, resp, resp->size);
+
+               adapter->cur_cmd->cmdflags &= ~CMD_F_HOSTCMD;
+       }
+
+       /* If the command is not successful, cleanup and return failure */
+       if ((result != 0 || !(respcmd & 0x8000))) {
+               lbs_pr_debug(1, "CMD_RESP: command reply %#x result=%#x\n",
+                      resp->command, resp->result);
+               /*
+                * Handling errors here
+                */
+               switch (respcmd) {
+               case cmd_ret_hw_spec_info:
+               case cmd_ret_802_11_reset:
+                       lbs_pr_debug(1, "CMD_RESP: Reset command failed\n");
+                       break;
+
+               }
+
+               __libertas_cleanup_and_insert_cmd(priv, adapter->cur_cmd);
+               adapter->nr_cmd_pending--;
+               adapter->cur_cmd = NULL;
+               spin_unlock_irqrestore(&adapter->driver_lock, flags);
+
+               ret = -1;
+               goto done;
+       }
+
+       spin_unlock_irqrestore(&adapter->driver_lock, flags);
+
+       ret = handle_cmd_response(respcmd, resp, priv);
+
+       spin_lock_irqsave(&adapter->driver_lock, flags);
+       if (adapter->cur_cmd) {
+               /* Clean up and Put current command back to cmdfreeq */
+               __libertas_cleanup_and_insert_cmd(priv, adapter->cur_cmd);
+               adapter->nr_cmd_pending--;
+               WARN_ON(adapter->nr_cmd_pending > 128);
+               adapter->cur_cmd = NULL;
+       }
+       spin_unlock_irqrestore(&adapter->driver_lock, flags);
+
+done:
+       mutex_unlock(&adapter->lock);
+       LEAVE();
+       return ret;
+}
+
+int libertas_process_event(wlan_private * priv)
+{
+       int ret = 0;
+       wlan_adapter *adapter = priv->adapter;
+       u32 eventcause;
+
+       spin_lock_irq(&adapter->driver_lock);
+       eventcause = adapter->eventcause;
+       spin_unlock_irq(&adapter->driver_lock);
+
+       ENTER();
+
+       lbs_pr_debug(1, "EVENT Cause %x\n", eventcause);
+
+       switch (eventcause >> SBI_EVENT_CAUSE_SHIFT) {
+       case MACREG_INT_CODE_LINK_SENSED:
+               lbs_pr_debug(1, "EVENT: MACREG_INT_CODE_LINK_SENSED\n");
+               break;
+
+       case MACREG_INT_CODE_DEAUTHENTICATED:
+               lbs_pr_debug(1, "EVENT: Deauthenticated\n");
+               libertas_mac_event_disconnected(priv);
+               break;
+
+       case MACREG_INT_CODE_DISASSOCIATED:
+               lbs_pr_debug(1, "EVENT: Disassociated\n");
+               libertas_mac_event_disconnected(priv);
+               break;
+
+       case MACREG_INT_CODE_LINK_LOSE_NO_SCAN:
+               lbs_pr_debug(1, "EVENT: Link lost\n");
+               libertas_mac_event_disconnected(priv);
+               break;
+
+       case MACREG_INT_CODE_PS_SLEEP:
+               lbs_pr_debug(1, "EVENT: SLEEP\n");
+               lbs_pr_debug(1, "_");
+
+               /* handle unexpected PS SLEEP event */
+               if (adapter->psstate == PS_STATE_FULL_POWER) {
+                       lbs_pr_debug(1,
+                              "EVENT: In FULL POWER mode - ignore PS SLEEP\n");
+                       break;
+               }
+               adapter->psstate = PS_STATE_PRE_SLEEP;
+
+               libertas_ps_confirm_sleep(priv, (u16) adapter->psmode);
+
+               break;
+
+       case MACREG_INT_CODE_PS_AWAKE:
+               lbs_pr_debug(1, "EVENT: AWAKE \n");
+               lbs_pr_debug(1, "|");
+
+               /* handle unexpected PS AWAKE event */
+               if (adapter->psstate == PS_STATE_FULL_POWER) {
+                       lbs_pr_debug(1,
+                              "EVENT: In FULL POWER mode - ignore PS AWAKE\n");
+                       break;
+               }
+
+               adapter->psstate = PS_STATE_AWAKE;
+
+               if (adapter->needtowakeup) {
+                       /*
+                        * wait for the command processing to finish
+                        * before resuming sending
+                        * adapter->needtowakeup will be set to FALSE
+                        * in libertas_ps_wakeup()
+                        */
+                       lbs_pr_debug(1, "Waking up...\n");
+                       libertas_ps_wakeup(priv, 0);
+               }
+               break;
+
+       case MACREG_INT_CODE_MIC_ERR_UNICAST:
+               lbs_pr_debug(1, "EVENT: UNICAST MIC ERROR\n");
+               handle_mic_failureevent(priv, MACREG_INT_CODE_MIC_ERR_UNICAST);
+               break;
+
+       case MACREG_INT_CODE_MIC_ERR_MULTICAST:
+               lbs_pr_debug(1, "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_pr_debug(1, "EVENT: HWAC - ADHOC BCN 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;
+
+       default:
+               lbs_pr_alert( "EVENT: unknown event id: %#x\n",
+                      eventcause >> SBI_EVENT_CAUSE_SHIFT);
+               break;
+       }
+
+       spin_lock_irq(&adapter->driver_lock);
+       adapter->eventcause = 0;
+       spin_unlock_irq(&adapter->driver_lock);
+       LEAVE();
+       return ret;
+}
diff --git a/drivers/net/wireless/libertas/debugfs.c b/drivers/net/wireless/libertas/debugfs.c
new file mode 100644 (file)
index 0000000..3ad1e03
--- /dev/null
@@ -0,0 +1,1968 @@
+#include <linux/module.h>
+#include <linux/dcache.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/mm.h>
+#include <net/iw_handler.h>
+#include "dev.h"
+#include "decl.h"
+#include "host.h"
+
+static struct dentry *libertas_dir = NULL;
+static char *szStates[] = {
+       "Connected",
+       "Disconnected"
+};
+
+void libertas_debug_init(wlan_private * priv, struct net_device *dev);
+
+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 libertas_dev_info(struct file *file, char __user *userbuf,
+                                 size_t count, loff_t *ppos)
+{
+       wlan_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->adapter->connect_status]);
+       pos += snprintf(buf+pos, len-pos, "region_code = %02x\n",
+                               (u32) priv->adapter->regioncode);
+
+       res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+
+       free_page(addr);
+       return res;
+}
+
+
+static ssize_t libertas_getscantable(struct file *file, char __user *userbuf,
+                                 size_t count, loff_t *ppos)
+{
+       wlan_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;
+
+       pos += snprintf(buf+pos, len-pos,
+                       "---------------------------------------");
+       pos += snprintf(buf+pos, len-pos,
+                       "---------------------------------------\n");
+       pos += snprintf(buf+pos, len-pos,
+               "# | ch  | ss  |       bssid       |   cap    |    TSF   | Qual | SSID \n");
+       pos += snprintf(buf+pos, len-pos,
+               "---------------------------------------");
+       pos += snprintf(buf+pos, len-pos,
+               "---------------------------------------\n");
+
+       while (numscansdone < priv->adapter->numinscantable) {
+               struct bss_descriptor *pbssinfo;
+               u16 cap;
+
+               pbssinfo = &priv->adapter->scantable[numscansdone];
+               memcpy(&cap, &pbssinfo->cap, sizeof(cap));
+               pos += snprintf(buf+pos, len-pos,
+                       "%02u| %03d | %03ld | %02x:%02x:%02x:%02x:%02x:%02x |",
+                       numscansdone, pbssinfo->channel, pbssinfo->rssi,
+                       pbssinfo->macaddress[0], pbssinfo->macaddress[1],
+                       pbssinfo->macaddress[2], pbssinfo->macaddress[3],
+                       pbssinfo->macaddress[4], pbssinfo->macaddress[5]);
+               pos += snprintf(buf+pos, len-pos, " %04x-", cap);
+               pos += snprintf(buf+pos, len-pos, "%c%c%c |",
+                               pbssinfo->cap.ibss ? 'A' : 'I',
+                               pbssinfo->cap.privacy ? 'P' : ' ',
+                               pbssinfo->cap.spectrummgmt ? 'S' : ' ');
+               pos += snprintf(buf+pos, len-pos, " %08llx |", pbssinfo->networktsf);
+               pos += snprintf(buf+pos, len-pos, " %d |",
+                       SCAN_RSSI(priv->adapter->scantable[numscansdone].rssi));
+
+               pos += snprintf(buf+pos, len-pos, " %s\n", pbssinfo->ssid.ssid);
+
+               numscansdone++;
+       }
+
+       res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+
+       free_page(addr);
+       return res;
+}
+
+static ssize_t libertas_sleepparams_write(struct file *file,
+                               const char __user *user_buf, size_t count,
+                               loff_t *ppos)
+{
+       wlan_private *priv = file->private_data;
+       ssize_t buf_size, res;
+       int p1, p2, p3, p4, p5, p6;
+       struct sleep_params sp;
+       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;
+       }
+       sp.sp_error = p1;
+       sp.sp_offset = p2;
+       sp.sp_stabletime = p3;
+       sp.sp_calcontrol = p4;
+       sp.sp_extsleepclk = p5;
+       sp.sp_reserved = p6;
+
+       memcpy(&priv->adapter->sp, &sp, sizeof(struct sleep_params));
+
+        res = libertas_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 libertas_sleepparams_read(struct file *file, char __user *userbuf,
+                                 size_t count, loff_t *ppos)
+{
+       wlan_private *priv = file->private_data;
+       wlan_adapter *adapter = priv->adapter;
+       ssize_t res;
+       size_t pos = 0;
+       unsigned long addr = get_zeroed_page(GFP_KERNEL);
+       char *buf = (char *)addr;
+
+        res = libertas_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", adapter->sp.sp_error,
+                       adapter->sp.sp_offset, adapter->sp.sp_stabletime,
+                       adapter->sp.sp_calcontrol, adapter->sp.sp_extsleepclk,
+                       adapter->sp.sp_reserved);
+
+       res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+
+out_unlock:
+       free_page(addr);
+       return res;
+}
+
+static ssize_t libertas_extscan(struct file *file, const char __user *userbuf,
+                                 size_t count, loff_t *ppos)
+{
+       wlan_private *priv = file->private_data;
+       ssize_t res, buf_size;
+       struct WLAN_802_11_SSID extscan_ssid;
+       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;
+       }
+
+       memcpy(&extscan_ssid.ssid, buf, strlen(buf)-1);
+       extscan_ssid.ssidlength = strlen(buf)-1;
+
+       libertas_send_specific_SSID_scan(priv, &extscan_ssid, 1);
+
+       memset(&wrqu, 0, sizeof(union iwreq_data));
+       wireless_send_event(priv->wlan_dev.netdev, SIOCGIWSCAN, &wrqu, NULL);
+
+out_unlock:
+       free_page(addr);
+       return count;
+}
+
+static int libertas_parse_chan(char *buf, size_t count,
+                       struct wlan_ioctl_user_scan_cfg *scan_cfg, int dur)
+{
+       char *start, *end, *hold, *str;
+       int i = 0;
+
+       start = strstr(buf, "chan=");
+       if (!start)
+               return -EINVAL;
+       start += 5;
+       end = strstr(start, " ");
+       if (!end)
+               end = buf + count;
+       hold = kzalloc((end - start)+1, GFP_KERNEL);
+       if (!hold)
+               return -ENOMEM;
+       strncpy(hold, start, end - start);
+       hold[(end-start)+1] = '\0';
+       while(hold && (str = strsep(&hold, ","))) {
+               int chan;
+               char band, passive = 0;
+               sscanf(str, "%d%c%c", &chan, &band, &passive);
+               scan_cfg->chanlist[i].channumber = chan;
+               scan_cfg->chanlist[i].scantype = passive ? 1 : 0;
+               if (band == 'b' || band == 'g')
+                       scan_cfg->chanlist[i].radiotype = 0;
+               else if (band == 'a')
+                       scan_cfg->chanlist[i].radiotype = 1;
+
+               scan_cfg->chanlist[i].scantime = dur;
+               i++;
+       }
+
+       kfree(hold);
+       return i;
+}
+
+static void libertas_parse_bssid(char *buf, size_t count,
+                        struct wlan_ioctl_user_scan_cfg *scan_cfg)
+{
+       char *hold;
+       unsigned int mac[ETH_ALEN];
+       int i;
+
+       hold = strstr(buf, "bssid=");
+       if (!hold)
+               return;
+       hold += 6;
+       sscanf(hold, "%2x:%2x:%2x:%2x:%2x:%2x", mac, mac+1, mac+2, mac+3,
+                       mac+4, mac+5);
+       for(i=0;i<ETH_ALEN;i++)
+               scan_cfg->specificBSSID[i] = mac[i];
+}
+
+static void libertas_parse_ssid(char *buf, size_t count,
+                        struct wlan_ioctl_user_scan_cfg *scan_cfg)
+{
+       char *hold, *end;
+       ssize_t size;
+
+       hold = strstr(buf, "ssid=");
+       if (!hold)
+               return;
+       hold += 5;
+       end = strstr(hold, " ");
+       if (!end)
+               end = buf + count - 1;
+
+       size = min(IW_ESSID_MAX_SIZE, end - hold);
+       strncpy(scan_cfg->specificSSID, hold, size);
+
+       return;
+}
+
+static void libertas_parse_keep(char *buf, size_t count,
+                        struct wlan_ioctl_user_scan_cfg *scan_cfg)
+{
+       char *hold;
+       int val;
+
+       hold = strstr(buf, "keep=");
+       if (!hold)
+               return;
+       hold += 5;
+       sscanf(hold, "%d", &val);
+
+       if (val != 0)
+               val = 1;
+
+       scan_cfg->keeppreviousscan = val;
+       return;
+}
+
+static int libertas_parse_dur(char *buf, size_t count,
+                        struct wlan_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 libertas_parse_probes(char *buf, size_t count,
+                        struct wlan_ioctl_user_scan_cfg *scan_cfg)
+{
+       char *hold;
+       int val;
+
+       hold = strstr(buf, "probes=");
+       if (!hold)
+               return;
+       hold += 7;
+       sscanf(hold, "%d", &val);
+
+       scan_cfg->numprobes = val;
+
+       return;
+}
+
+static void libertas_parse_type(char *buf, size_t count,
+                        struct wlan_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 libertas_setuserscan(struct file *file,
+                                   const char __user *userbuf,
+                                   size_t count, loff_t *ppos)
+{
+       wlan_private *priv = file->private_data;
+       ssize_t res, buf_size;
+       struct wlan_ioctl_user_scan_cfg *scan_cfg;
+       union iwreq_data wrqu;
+       int dur;
+       unsigned long addr = get_zeroed_page(GFP_KERNEL);
+       char *buf = (char *)addr;
+
+       scan_cfg = kzalloc(sizeof(struct wlan_ioctl_user_scan_cfg), GFP_KERNEL);
+       if (!scan_cfg)
+               return -ENOMEM;
+
+       buf_size = min(count, len - 1);
+       if (copy_from_user(buf, userbuf, buf_size)) {
+               res = -EFAULT;
+               goto out_unlock;
+       }
+
+       scan_cfg->bsstype = WLAN_SCAN_BSS_TYPE_ANY;
+
+       dur = libertas_parse_dur(buf, count, scan_cfg);
+       libertas_parse_chan(buf, count, scan_cfg, dur);
+       libertas_parse_bssid(buf, count, scan_cfg);
+       libertas_parse_ssid(buf, count, scan_cfg);
+       libertas_parse_keep(buf, count, scan_cfg);
+       libertas_parse_probes(buf, count, scan_cfg);
+       libertas_parse_type(buf, count, scan_cfg);
+
+       wlan_scan_networks(priv, scan_cfg);
+       wait_event_interruptible(priv->adapter->cmd_pending,
+                                !priv->adapter->nr_cmd_pending);
+
+       memset(&wrqu, 0x00, sizeof(union iwreq_data));
+       wireless_send_event(priv->wlan_dev.netdev, SIOCGIWSCAN, &wrqu, NULL);
+
+out_unlock:
+       free_page(addr);
+       kfree(scan_cfg);
+       return count;
+}
+
+static int libertas_event_initcmd(wlan_private *priv, void **response_buf,
+                       struct cmd_ctrl_node **cmdnode,
+                       struct cmd_ds_command **cmd)
+{
+       u16 wait_option = cmd_option_waitforrsp;
+
+       if (!(*cmdnode = libertas_get_free_cmd_ctrl_node(priv))) {
+               lbs_pr_debug(1, "failed libertas_get_free_cmd_ctrl_node\n");
+               return -ENOMEM;
+       }
+       if (!(*response_buf = kmalloc(3000, GFP_KERNEL))) {
+               lbs_pr_debug(1, "failed to allocate response buffer!\n");
+               return -ENOMEM;
+       }
+       libertas_set_cmd_ctrl_node(priv, *cmdnode, 0, wait_option, NULL);
+       init_waitqueue_head(&(*cmdnode)->cmdwait_q);
+       (*cmdnode)->pdata_buf = *response_buf;
+       (*cmdnode)->cmdflags |= CMD_F_HOSTCMD;
+       (*cmdnode)->cmdwaitqwoken = 0;
+       *cmd = (struct cmd_ds_command *)(*cmdnode)->bufvirtualaddr;
+       (*cmd)->command = cmd_802_11_subscribe_event;
+       (*cmd)->seqnum = ++priv->adapter->seqnum;
+       (*cmd)->result = 0;
+       return 0;
+}
+
+static ssize_t libertas_lowrssi_read(struct file *file, char __user *userbuf,
+                                 size_t count, loff_t *ppos)
+{
+       wlan_private *priv = file->private_data;
+       wlan_adapter *adapter = priv->adapter;
+       struct cmd_ctrl_node *pcmdnode;
+       struct cmd_ds_command *pcmdptr;
+       struct cmd_ds_802_11_subscribe_event *event;
+       void *response_buf;
+       int res, cmd_len;
+       ssize_t pos = 0;
+       unsigned long addr = get_zeroed_page(GFP_KERNEL);
+       char *buf = (char *)addr;
+
+       res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr);
+       if (res < 0) {
+               free_page(addr);
+               return res;
+       }
+
+       event = &pcmdptr->params.subscribe_event;
+       event->action = cmd_act_get;
+       pcmdptr->size =
+       cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN);
+       libertas_queue_cmd(adapter, pcmdnode, 1);
+       wake_up_interruptible(&priv->mainthread.waitq);
+
+       /* Sleep until response is generated by FW */
+       wait_event_interruptible(pcmdnode->cmdwait_q,
+                               pcmdnode->cmdwaitqwoken);
+
+       pcmdptr = response_buf;
+       if (pcmdptr->result) {
+               lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__,
+                       pcmdptr->result);
+               kfree(response_buf);
+               free_page(addr);
+               return 0;
+       }
+
+       if (pcmdptr->command != cmd_ret_802_11_subscribe_event) {
+               lbs_pr_err("command response incorrect!\n");
+               kfree(response_buf);
+               free_page(addr);
+               return 0;
+       }
+
+       cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event);
+       event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN);
+       while (cmd_len < pcmdptr->size) {
+               struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len);
+               switch(header->type) {
+               struct mrvlietypes_rssithreshold  *Lowrssi;
+               case TLV_TYPE_RSSI_LOW:
+               Lowrssi = (struct mrvlietypes_rssithreshold *)(response_buf + cmd_len);
+               pos += snprintf(buf+pos, len-pos, "%d %d %d\n",
+                               Lowrssi->rssivalue,
+                               Lowrssi->rssifreq,
+                               (event->events & 0x0001)?1:0);
+               default:
+                       cmd_len += sizeof(struct mrvlietypes_snrthreshold);
+                       break;
+               }
+       }
+
+       kfree(response_buf);
+       res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+       free_page(addr);
+       return res;
+}
+
+static u16 libertas_get_events_bitmap(wlan_private *priv)
+{
+       wlan_adapter *adapter = priv->adapter;
+       struct cmd_ctrl_node *pcmdnode;
+       struct cmd_ds_command *pcmdptr;
+       struct cmd_ds_802_11_subscribe_event *event;
+       void *response_buf;
+       int res;
+       u16 event_bitmap;
+
+       res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr);
+       if (res < 0)
+               return res;
+
+       event = &pcmdptr->params.subscribe_event;
+       event->action = cmd_act_get;
+       pcmdptr->size =
+       cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN);
+       libertas_queue_cmd(adapter, pcmdnode, 1);
+       wake_up_interruptible(&priv->mainthread.waitq);
+
+       /* Sleep until response is generated by FW */
+       wait_event_interruptible(pcmdnode->cmdwait_q,
+                               pcmdnode->cmdwaitqwoken);
+
+       pcmdptr = response_buf;
+
+       if (pcmdptr->result) {
+               lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__,
+                       pcmdptr->result);
+               kfree(response_buf);
+               return 0;
+       }
+
+       if (pcmdptr->command != cmd_ret_802_11_subscribe_event) {
+               lbs_pr_err("command response incorrect!\n");
+               kfree(response_buf);
+               return 0;
+       }
+
+       event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN);
+       event_bitmap = event->events;
+       kfree(response_buf);
+       return event_bitmap;
+}
+
+static ssize_t libertas_lowrssi_write(struct file *file,
+                                   const char __user *userbuf,
+                                   size_t count, loff_t *ppos)
+{
+       wlan_private *priv = file->private_data;
+       wlan_adapter *adapter = priv->adapter;
+       ssize_t res, buf_size;
+       int value, freq, subscribed, cmd_len;
+       struct cmd_ctrl_node *pcmdnode;
+       struct cmd_ds_command *pcmdptr;
+       struct cmd_ds_802_11_subscribe_event *event;
+       struct mrvlietypes_rssithreshold *rssi_threshold;
+       void *response_buf;
+       u16 event_bitmap;
+       u8 *ptr;
+       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, "%d %d %d", &value, &freq, &subscribed);
+       if (res != 3) {
+               res = -EFAULT;
+               goto out_unlock;
+       }
+
+       event_bitmap = libertas_get_events_bitmap(priv);
+
+       res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr);
+       if (res < 0)
+               goto out_unlock;
+
+       event = &pcmdptr->params.subscribe_event;
+       event->action = cmd_act_set;
+       pcmdptr->size = cpu_to_le16(S_DS_GEN +
+               sizeof(struct cmd_ds_802_11_subscribe_event) +
+               sizeof(struct mrvlietypes_rssithreshold));
+
+       cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event);
+       ptr = (u8*) pcmdptr+cmd_len;
+       rssi_threshold = (struct mrvlietypes_rssithreshold *)(ptr);
+       rssi_threshold->header.type = cpu_to_le16(0x0104);
+       rssi_threshold->header.len = 2;
+       rssi_threshold->rssivalue = cpu_to_le16(value);
+       rssi_threshold->rssifreq = cpu_to_le16(freq);
+       event_bitmap |= subscribed ? 0x0001 : 0x0;
+       event->events = event_bitmap;
+
+       libertas_queue_cmd(adapter, pcmdnode, 1);
+       wake_up_interruptible(&priv->mainthread.waitq);
+
+       /* Sleep until response is generated by FW */
+       wait_event_interruptible(pcmdnode->cmdwait_q,
+                               pcmdnode->cmdwaitqwoken);
+
+       pcmdptr = response_buf;
+
+       if (pcmdptr->result) {
+               lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__,
+                       pcmdptr->result);
+               kfree(response_buf);
+               free_page(addr);
+               return 0;
+       }
+
+       if (pcmdptr->command != cmd_ret_802_11_subscribe_event) {
+               lbs_pr_err("command response incorrect!\n");
+               kfree(response_buf);
+               free_page(addr);
+               return 0;
+       }
+
+       res = count;
+out_unlock:
+       free_page(addr);
+       return res;
+}
+
+static ssize_t libertas_lowsnr_read(struct file *file, char __user *userbuf,
+                                 size_t count, loff_t *ppos)
+{
+       wlan_private *priv = file->private_data;
+       wlan_adapter *adapter = priv->adapter;
+       struct cmd_ctrl_node *pcmdnode;
+       struct cmd_ds_command *pcmdptr;
+       struct cmd_ds_802_11_subscribe_event *event;
+       void *response_buf;
+       int res, cmd_len;
+       ssize_t pos = 0;
+       unsigned long addr = get_zeroed_page(GFP_KERNEL);
+       char *buf = (char *)addr;
+
+       res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr);
+       if (res < 0) {
+               free_page(addr);
+               return res;
+       }
+
+       event = &pcmdptr->params.subscribe_event;
+       event->action = cmd_act_get;
+       pcmdptr->size =
+       cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN);
+       libertas_queue_cmd(adapter, pcmdnode, 1);
+       wake_up_interruptible(&priv->mainthread.waitq);
+
+       /* Sleep until response is generated by FW */
+       wait_event_interruptible(pcmdnode->cmdwait_q,
+                               pcmdnode->cmdwaitqwoken);
+
+       pcmdptr = response_buf;
+
+       if (pcmdptr->result) {
+               lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__,
+                       pcmdptr->result);
+               kfree(response_buf);
+               free_page(addr);
+               return 0;
+       }
+
+       if (pcmdptr->command != cmd_ret_802_11_subscribe_event) {
+               lbs_pr_err("command response incorrect!\n");
+               kfree(response_buf);
+               free_page(addr);
+               return 0;
+       }
+
+       cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event);
+       event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN);
+       while (cmd_len < pcmdptr->size) {
+               struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len);
+               switch(header->type) {
+               struct mrvlietypes_snrthreshold *LowSnr;
+               case TLV_TYPE_SNR_LOW:
+               LowSnr = (struct mrvlietypes_snrthreshold *)(response_buf + cmd_len);
+               pos += snprintf(buf+pos, len-pos, "%d %d %d\n",
+                               LowSnr->snrvalue,
+                               LowSnr->snrfreq,
+                               (event->events & 0x0002)?1:0);
+               default:
+                       cmd_len += sizeof(struct mrvlietypes_snrthreshold);
+                       break;
+               }
+       }
+
+       kfree(response_buf);
+
+       res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+       free_page(addr);
+       return res;
+}
+
+static ssize_t libertas_lowsnr_write(struct file *file,
+                                   const char __user *userbuf,
+                                   size_t count, loff_t *ppos)
+{
+       wlan_private *priv = file->private_data;
+       wlan_adapter *adapter = priv->adapter;
+       ssize_t res, buf_size;
+       int value, freq, subscribed, cmd_len;
+       struct cmd_ctrl_node *pcmdnode;
+       struct cmd_ds_command *pcmdptr;
+       struct cmd_ds_802_11_subscribe_event *event;
+       struct mrvlietypes_snrthreshold *snr_threshold;
+       void *response_buf;
+       u16 event_bitmap;
+       u8 *ptr;
+       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, "%d %d %d", &value, &freq, &subscribed);
+       if (res != 3) {
+               res = -EFAULT;
+               goto out_unlock;
+       }
+
+       event_bitmap = libertas_get_events_bitmap(priv);
+
+       res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr);
+       if (res < 0)
+               goto out_unlock;
+
+       event = &pcmdptr->params.subscribe_event;
+       event->action = cmd_act_set;
+       pcmdptr->size = cpu_to_le16(S_DS_GEN +
+               sizeof(struct cmd_ds_802_11_subscribe_event) +
+               sizeof(struct mrvlietypes_snrthreshold));
+       cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event);
+       ptr = (u8*) pcmdptr+cmd_len;
+       snr_threshold = (struct mrvlietypes_snrthreshold *)(ptr);
+       snr_threshold->header.type = cpu_to_le16(TLV_TYPE_SNR_LOW);
+       snr_threshold->header.len = 2;
+       snr_threshold->snrvalue = cpu_to_le16(value);
+       snr_threshold->snrfreq = cpu_to_le16(freq);
+       event_bitmap |= subscribed ? 0x0002 : 0x0;
+       event->events = event_bitmap;
+
+       libertas_queue_cmd(adapter, pcmdnode, 1);
+       wake_up_interruptible(&priv->mainthread.waitq);
+
+       /* Sleep until response is generated by FW */
+       wait_event_interruptible(pcmdnode->cmdwait_q,
+                               pcmdnode->cmdwaitqwoken);
+
+       pcmdptr = response_buf;
+
+       if (pcmdptr->result) {
+               lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__,
+                       pcmdptr->result);
+               kfree(response_buf);
+               free_page(addr);
+               return 0;
+       }
+
+       if (pcmdptr->command != cmd_ret_802_11_subscribe_event) {
+               lbs_pr_err("command response incorrect!\n");
+               kfree(response_buf);
+               free_page(addr);
+               return 0;
+       }
+
+       res = count;
+
+out_unlock:
+       free_page(addr);
+       return res;
+}
+
+static ssize_t libertas_failcount_read(struct file *file, char __user *userbuf,
+                                 size_t count, loff_t *ppos)
+{
+       wlan_private *priv = file->private_data;
+       wlan_adapter *adapter = priv->adapter;
+       struct cmd_ctrl_node *pcmdnode;
+       struct cmd_ds_command *pcmdptr;
+       struct cmd_ds_802_11_subscribe_event *event;
+       void *response_buf;
+       int res, cmd_len;
+       ssize_t pos = 0;
+       unsigned long addr = get_zeroed_page(GFP_KERNEL);
+       char *buf = (char *)addr;
+
+       res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr);
+       if (res < 0) {
+               free_page(addr);
+               return res;
+       }
+
+       event = &pcmdptr->params.subscribe_event;
+       event->action = cmd_act_get;
+       pcmdptr->size =
+       cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN);
+       libertas_queue_cmd(adapter, pcmdnode, 1);
+       wake_up_interruptible(&priv->mainthread.waitq);
+
+       /* Sleep until response is generated by FW */
+       wait_event_interruptible(pcmdnode->cmdwait_q,
+                               pcmdnode->cmdwaitqwoken);
+
+       pcmdptr = response_buf;
+
+       if (pcmdptr->result) {
+               lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__,
+                       pcmdptr->result);
+               kfree(response_buf);
+               free_page(addr);
+               return 0;
+       }
+
+       if (pcmdptr->command != cmd_ret_802_11_subscribe_event) {
+               lbs_pr_err("command response incorrect!\n");
+               kfree(response_buf);
+               free_page(addr);
+               return 0;
+       }
+
+       cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event);
+       event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN);
+       while (cmd_len < pcmdptr->size) {
+               struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len);
+               switch(header->type) {
+               struct mrvlietypes_failurecount *failcount;
+               case TLV_TYPE_FAILCOUNT:
+               failcount = (struct mrvlietypes_failurecount *)(response_buf + cmd_len);
+               pos += snprintf(buf+pos, len-pos, "%d %d %d\n",
+                               failcount->failvalue,
+                               failcount->Failfreq,
+                               (event->events & 0x0004)?1:0);
+               default:
+                       cmd_len += sizeof(struct mrvlietypes_failurecount);
+                       break;
+               }
+       }
+
+       kfree(response_buf);
+       res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+       free_page(addr);
+       return res;
+}
+
+static ssize_t libertas_failcount_write(struct file *file,
+                                   const char __user *userbuf,
+                                   size_t count, loff_t *ppos)
+{
+       wlan_private *priv = file->private_data;
+       wlan_adapter *adapter = priv->adapter;
+       ssize_t res, buf_size;
+       int value, freq, subscribed, cmd_len;
+       struct cmd_ctrl_node *pcmdnode;
+       struct cmd_ds_command *pcmdptr;
+       struct cmd_ds_802_11_subscribe_event *event;
+       struct mrvlietypes_failurecount *failcount;
+       void *response_buf;
+       u16 event_bitmap;
+       u8 *ptr;
+       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, "%d %d %d", &value, &freq, &subscribed);
+       if (res != 3) {
+               res = -EFAULT;
+               goto out_unlock;
+       }
+
+       event_bitmap = libertas_get_events_bitmap(priv);
+
+       res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr);
+       if (res < 0)
+               goto out_unlock;
+
+       event = &pcmdptr->params.subscribe_event;
+       event->action = cmd_act_set;
+       pcmdptr->size = cpu_to_le16(S_DS_GEN +
+               sizeof(struct cmd_ds_802_11_subscribe_event) +
+               sizeof(struct mrvlietypes_failurecount));
+       cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event);
+       ptr = (u8*) pcmdptr+cmd_len;
+       failcount = (struct mrvlietypes_failurecount *)(ptr);
+       failcount->header.type = cpu_to_le16(TLV_TYPE_FAILCOUNT);
+       failcount->header.len = 2;
+       failcount->failvalue = cpu_to_le16(value);
+       failcount->Failfreq = cpu_to_le16(freq);
+       event_bitmap |= subscribed ? 0x0004 : 0x0;
+       event->events = event_bitmap;
+
+       libertas_queue_cmd(adapter, pcmdnode, 1);
+       wake_up_interruptible(&priv->mainthread.waitq);
+
+       /* Sleep until response is generated by FW */
+       wait_event_interruptible(pcmdnode->cmdwait_q,
+                               pcmdnode->cmdwaitqwoken);
+
+       pcmdptr = (struct cmd_ds_command *)response_buf;
+
+       if (pcmdptr->result) {
+               lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__,
+                       pcmdptr->result);
+               kfree(response_buf);
+               free_page(addr);
+               return 0;
+       }
+
+       if (pcmdptr->command != cmd_ret_802_11_subscribe_event) {
+               lbs_pr_err("command response incorrect!\n");
+               kfree(response_buf);
+               free_page(addr);
+               return 0;
+       }
+
+       res = count;
+out_unlock:
+       free_page(addr);
+       return res;
+}
+
+static ssize_t libertas_bcnmiss_read(struct file *file, char __user *userbuf,
+                                 size_t count, loff_t *ppos)
+{
+       wlan_private *priv = file->private_data;
+       wlan_adapter *adapter = priv->adapter;
+       struct cmd_ctrl_node *pcmdnode;
+       struct cmd_ds_command *pcmdptr;
+       struct cmd_ds_802_11_subscribe_event *event;
+       void *response_buf;
+       int res, cmd_len;
+       ssize_t pos = 0;
+       unsigned long addr = get_zeroed_page(GFP_KERNEL);
+       char *buf = (char *)addr;
+
+       res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr);
+       if (res < 0) {
+               free_page(addr);
+               return res;
+       }
+
+       event = &pcmdptr->params.subscribe_event;
+       event->action = cmd_act_get;
+       pcmdptr->size =
+       cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN);
+       libertas_queue_cmd(adapter, pcmdnode, 1);
+       wake_up_interruptible(&priv->mainthread.waitq);
+
+       /* Sleep until response is generated by FW */
+       wait_event_interruptible(pcmdnode->cmdwait_q,
+                               pcmdnode->cmdwaitqwoken);
+
+       pcmdptr = response_buf;
+
+       if (pcmdptr->result) {
+               lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__,
+                       pcmdptr->result);
+               free_page(addr);
+               kfree(response_buf);
+               return 0;
+       }
+
+       if (pcmdptr->command != cmd_ret_802_11_subscribe_event) {
+               lbs_pr_err("command response incorrect!\n");
+               free_page(addr);
+               kfree(response_buf);
+               return 0;
+       }
+
+       cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event);
+       event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN);
+       while (cmd_len < pcmdptr->size) {
+               struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len);
+               switch(header->type) {
+               struct mrvlietypes_beaconsmissed *bcnmiss;
+               case TLV_TYPE_BCNMISS:
+               bcnmiss = (struct mrvlietypes_beaconsmissed *)(response_buf + cmd_len);
+               pos += snprintf(buf+pos, len-pos, "%d N/A %d\n",
+                               bcnmiss->beaconmissed,
+                               (event->events & 0x0008)?1:0);
+               default:
+                       cmd_len += sizeof(struct mrvlietypes_beaconsmissed);
+                       break;
+               }
+       }
+
+       kfree(response_buf);
+
+       res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+       free_page(addr);
+       return res;
+}
+
+static ssize_t libertas_bcnmiss_write(struct file *file,
+                                   const char __user *userbuf,
+                                   size_t count, loff_t *ppos)
+{
+       wlan_private *priv = file->private_data;
+       wlan_adapter *adapter = priv->adapter;
+       ssize_t res, buf_size;
+       int value, freq, subscribed, cmd_len;
+       struct cmd_ctrl_node *pcmdnode;
+       struct cmd_ds_command *pcmdptr;
+       struct cmd_ds_802_11_subscribe_event *event;
+       struct mrvlietypes_beaconsmissed *bcnmiss;
+       void *response_buf;
+       u16 event_bitmap;
+       u8 *ptr;
+       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, "%d %d %d", &value, &freq, &subscribed);
+       if (res != 3) {
+               res = -EFAULT;
+               goto out_unlock;
+       }
+
+       event_bitmap = libertas_get_events_bitmap(priv);
+
+       res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr);
+       if (res < 0)
+               goto out_unlock;
+
+       event = &pcmdptr->params.subscribe_event;
+       event->action = cmd_act_set;
+       pcmdptr->size = cpu_to_le16(S_DS_GEN +
+               sizeof(struct cmd_ds_802_11_subscribe_event) +
+               sizeof(struct mrvlietypes_beaconsmissed));
+       cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event);
+       ptr = (u8*) pcmdptr+cmd_len;
+       bcnmiss = (struct mrvlietypes_beaconsmissed *)(ptr);
+       bcnmiss->header.type = cpu_to_le16(TLV_TYPE_BCNMISS);
+       bcnmiss->header.len = 2;
+       bcnmiss->beaconmissed = cpu_to_le16(value);
+       event_bitmap |= subscribed ? 0x0008 : 0x0;
+       event->events = event_bitmap;
+
+       libertas_queue_cmd(adapter, pcmdnode, 1);
+       wake_up_interruptible(&priv->mainthread.waitq);
+
+       /* Sleep until response is generated by FW */
+       wait_event_interruptible(pcmdnode->cmdwait_q,
+                               pcmdnode->cmdwaitqwoken);
+
+       pcmdptr = response_buf;
+
+       if (pcmdptr->result) {
+               lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__,
+                       pcmdptr->result);
+               kfree(response_buf);
+               free_page(addr);
+               return 0;
+       }
+
+       if (pcmdptr->command != cmd_ret_802_11_subscribe_event) {
+               lbs_pr_err("command response incorrect!\n");
+               free_page(addr);
+               kfree(response_buf);
+               return 0;
+       }
+
+       res = count;
+out_unlock:
+       free_page(addr);
+       return res;
+}
+
+static ssize_t libertas_highrssi_read(struct file *file, char __user *userbuf,
+                                 size_t count, loff_t *ppos)
+{
+       wlan_private *priv = file->private_data;
+       wlan_adapter *adapter = priv->adapter;
+       struct cmd_ctrl_node *pcmdnode;
+       struct cmd_ds_command *pcmdptr;
+       struct cmd_ds_802_11_subscribe_event *event;
+       void *response_buf;
+       int res, cmd_len;
+       ssize_t pos = 0;
+       unsigned long addr = get_zeroed_page(GFP_KERNEL);
+       char *buf = (char *)addr;
+
+       res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr);
+       if (res < 0) {
+               free_page(addr);
+               return res;
+       }
+
+       event = &pcmdptr->params.subscribe_event;
+       event->action = cmd_act_get;
+       pcmdptr->size =
+       cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN);
+       libertas_queue_cmd(adapter, pcmdnode, 1);
+       wake_up_interruptible(&priv->mainthread.waitq);
+
+       /* Sleep until response is generated by FW */
+       wait_event_interruptible(pcmdnode->cmdwait_q,
+                               pcmdnode->cmdwaitqwoken);
+
+       pcmdptr = response_buf;
+
+       if (pcmdptr->result) {
+               lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__,
+                       pcmdptr->result);
+               kfree(response_buf);
+               free_page(addr);
+               return 0;
+       }
+
+       if (pcmdptr->command != cmd_ret_802_11_subscribe_event) {
+               lbs_pr_err("command response incorrect!\n");
+               kfree(response_buf);
+               free_page(addr);
+               return 0;
+       }
+
+       cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event);
+       event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN);
+       while (cmd_len < pcmdptr->size) {
+               struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len);
+               switch(header->type) {
+               struct mrvlietypes_rssithreshold  *Highrssi;
+               case TLV_TYPE_RSSI_HIGH:
+               Highrssi = (struct mrvlietypes_rssithreshold *)(response_buf + cmd_len);
+               pos += snprintf(buf+pos, len-pos, "%d %d %d\n",
+                               Highrssi->rssivalue,
+                               Highrssi->rssifreq,
+                               (event->events & 0x0010)?1:0);
+               default:
+                       cmd_len += sizeof(struct mrvlietypes_snrthreshold);
+                       break;
+               }
+       }
+
+       kfree(response_buf);
+
+       res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+       free_page(addr);
+       return res;
+}
+
+static ssize_t libertas_highrssi_write(struct file *file,
+                                   const char __user *userbuf,
+                                   size_t count, loff_t *ppos)
+{
+       wlan_private *priv = file->private_data;
+       wlan_adapter *adapter = priv->adapter;
+       ssize_t res, buf_size;
+       int value, freq, subscribed, cmd_len;
+       struct cmd_ctrl_node *pcmdnode;
+       struct cmd_ds_command *pcmdptr;
+       struct cmd_ds_802_11_subscribe_event *event;
+       struct mrvlietypes_rssithreshold *rssi_threshold;
+       void *response_buf;
+       u16 event_bitmap;
+       u8 *ptr;
+       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, "%d %d %d", &value, &freq, &subscribed);
+       if (res != 3) {
+               res = -EFAULT;
+               goto out_unlock;
+       }
+
+       event_bitmap = libertas_get_events_bitmap(priv);
+
+       res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr);
+       if (res < 0)
+               goto out_unlock;
+
+       event = &pcmdptr->params.subscribe_event;
+       event->action = cmd_act_set;
+       pcmdptr->size = cpu_to_le16(S_DS_GEN +
+               sizeof(struct cmd_ds_802_11_subscribe_event) +
+               sizeof(struct mrvlietypes_rssithreshold));
+       cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event);
+       ptr = (u8*) pcmdptr+cmd_len;
+       rssi_threshold = (struct mrvlietypes_rssithreshold *)(ptr);
+       rssi_threshold->header.type = cpu_to_le16(TLV_TYPE_RSSI_HIGH);
+       rssi_threshold->header.len = 2;
+       rssi_threshold->rssivalue = cpu_to_le16(value);
+       rssi_threshold->rssifreq = cpu_to_le16(freq);
+       event_bitmap |= subscribed ? 0x0010 : 0x0;
+       event->events = event_bitmap;
+
+       libertas_queue_cmd(adapter, pcmdnode, 1);
+       wake_up_interruptible(&priv->mainthread.waitq);
+
+       /* Sleep until response is generated by FW */
+       wait_event_interruptible(pcmdnode->cmdwait_q,
+                               pcmdnode->cmdwaitqwoken);
+
+       pcmdptr = response_buf;
+
+       if (pcmdptr->result) {
+               lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__,
+                       pcmdptr->result);
+               kfree(response_buf);
+               return 0;
+       }
+
+       if (pcmdptr->command != cmd_ret_802_11_subscribe_event) {
+               lbs_pr_err("command response incorrect!\n");
+               kfree(response_buf);
+               return 0;
+       }
+
+       res = count;
+out_unlock:
+       free_page(addr);
+       return res;
+}
+
+static ssize_t libertas_highsnr_read(struct file *file, char __user *userbuf,
+                                 size_t count, loff_t *ppos)
+{
+       wlan_private *priv = file->private_data;
+       wlan_adapter *adapter = priv->adapter;
+       struct cmd_ctrl_node *pcmdnode;
+       struct cmd_ds_command *pcmdptr;
+       struct cmd_ds_802_11_subscribe_event *event;
+       void *response_buf;
+       int res, cmd_len;
+       ssize_t pos = 0;
+       unsigned long addr = get_zeroed_page(GFP_KERNEL);
+       char *buf = (char *)addr;
+
+       res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr);
+       if (res < 0) {
+               free_page(addr);
+               return res;
+       }
+
+       event = &pcmdptr->params.subscribe_event;
+       event->action = cmd_act_get;
+       pcmdptr->size =
+       cpu_to_le16(sizeof(struct cmd_ds_802_11_subscribe_event) + S_DS_GEN);
+       libertas_queue_cmd(adapter, pcmdnode, 1);
+       wake_up_interruptible(&priv->mainthread.waitq);
+
+       /* Sleep until response is generated by FW */
+       wait_event_interruptible(pcmdnode->cmdwait_q,
+                               pcmdnode->cmdwaitqwoken);
+
+       pcmdptr = response_buf;
+
+       if (pcmdptr->result) {
+               lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__,
+                       pcmdptr->result);
+               kfree(response_buf);
+               free_page(addr);
+               return 0;
+       }
+
+       if (pcmdptr->command != cmd_ret_802_11_subscribe_event) {
+               lbs_pr_err("command response incorrect!\n");
+               kfree(response_buf);
+               free_page(addr);
+               return 0;
+       }
+
+       cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event);
+       event = (struct cmd_ds_802_11_subscribe_event *)(response_buf + S_DS_GEN);
+       while (cmd_len < pcmdptr->size) {
+               struct mrvlietypesheader *header = (struct mrvlietypesheader *)(response_buf + cmd_len);
+               switch(header->type) {
+               struct mrvlietypes_snrthreshold *HighSnr;
+               case TLV_TYPE_SNR_HIGH:
+               HighSnr = (struct mrvlietypes_snrthreshold *)(response_buf + cmd_len);
+               pos += snprintf(buf+pos, len-pos, "%d %d %d\n",
+                               HighSnr->snrvalue,
+                               HighSnr->snrfreq,
+                               (event->events & 0x0020)?1:0);
+               default:
+                       cmd_len += sizeof(struct mrvlietypes_snrthreshold);
+                       break;
+               }
+       }
+
+       kfree(response_buf);
+
+       res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+       free_page(addr);
+       return res;
+}
+
+static ssize_t libertas_highsnr_write(struct file *file,
+                                   const char __user *userbuf,
+                                   size_t count, loff_t *ppos)
+{
+       wlan_private *priv = file->private_data;
+       wlan_adapter *adapter = priv->adapter;
+       ssize_t res, buf_size;
+       int value, freq, subscribed, cmd_len;
+       struct cmd_ctrl_node *pcmdnode;
+       struct cmd_ds_command *pcmdptr;
+       struct cmd_ds_802_11_subscribe_event *event;
+       struct mrvlietypes_snrthreshold *snr_threshold;
+       void *response_buf;
+       u16 event_bitmap;
+       u8 *ptr;
+       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, "%d %d %d", &value, &freq, &subscribed);
+       if (res != 3) {
+               res = -EFAULT;
+               goto out_unlock;
+       }
+
+       event_bitmap = libertas_get_events_bitmap(priv);
+
+       res = libertas_event_initcmd(priv, &response_buf, &pcmdnode, &pcmdptr);
+       if (res < 0)
+               goto out_unlock;
+
+       event = &pcmdptr->params.subscribe_event;
+       event->action = cmd_act_set;
+       pcmdptr->size = cpu_to_le16(S_DS_GEN +
+               sizeof(struct cmd_ds_802_11_subscribe_event) +
+               sizeof(struct mrvlietypes_snrthreshold));
+       cmd_len = S_DS_GEN + sizeof(struct cmd_ds_802_11_subscribe_event);
+       ptr = (u8*) pcmdptr+cmd_len;
+       snr_threshold = (struct mrvlietypes_snrthreshold *)(ptr);
+       snr_threshold->header.type = cpu_to_le16(TLV_TYPE_SNR_HIGH);
+       snr_threshold->header.len = 2;
+       snr_threshold->snrvalue = cpu_to_le16(value);
+       snr_threshold->snrfreq = cpu_to_le16(freq);
+       event_bitmap |= subscribed ? 0x0020 : 0x0;
+       event->events = event_bitmap;
+
+       libertas_queue_cmd(adapter, pcmdnode, 1);
+       wake_up_interruptible(&priv->mainthread.waitq);
+
+       /* Sleep until response is generated by FW */
+       wait_event_interruptible(pcmdnode->cmdwait_q,
+                               pcmdnode->cmdwaitqwoken);
+
+       pcmdptr = response_buf;
+
+       if (pcmdptr->result) {
+               lbs_pr_err("%s: fail, result=%d\n", __FUNCTION__,
+                       pcmdptr->result);
+               kfree(response_buf);
+               free_page(addr);
+               return 0;
+       }
+
+       if (pcmdptr->command != cmd_ret_802_11_subscribe_event) {
+               lbs_pr_err("command response incorrect!\n");
+               kfree(response_buf);
+               free_page(addr);
+               return 0;
+       }
+
+       res = count;
+out_unlock:
+       free_page(addr);
+       return res;
+}
+
+static ssize_t libertas_rdmac_read(struct file *file, char __user *userbuf,
+                                 size_t count, loff_t *ppos)
+{
+       wlan_private *priv = file->private_data;
+       wlan_adapter *adapter = priv->adapter;
+       struct wlan_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 = libertas_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, adapter->offsetvalue.value);
+
+       ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+       free_page(addr);
+       return ret;
+}
+
+static ssize_t libertas_rdmac_write(struct file *file,
+                                   const char __user *userbuf,
+                                   size_t count, loff_t *ppos)
+{
+       wlan_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 libertas_wrmac_write(struct file *file,
+                                   const char __user *userbuf,
+                                   size_t count, loff_t *ppos)
+{
+
+       wlan_private *priv = file->private_data;
+       ssize_t res, buf_size;
+       u32 offset, value;
+       struct wlan_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 = libertas_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 libertas_rdbbp_read(struct file *file, char __user *userbuf,
+                                 size_t count, loff_t *ppos)
+{
+       wlan_private *priv = file->private_data;
+       wlan_adapter *adapter = priv->adapter;
+       struct wlan_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 = libertas_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, adapter->offsetvalue.value);
+
+       ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+       free_page(addr);
+
+       return ret;
+}
+
+static ssize_t libertas_rdbbp_write(struct file *file,
+                                   const char __user *userbuf,
+                                   size_t count, loff_t *ppos)
+{
+       wlan_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 libertas_wrbbp_write(struct file *file,
+                                   const char __user *userbuf,
+                                   size_t count, loff_t *ppos)
+{
+
+       wlan_private *priv = file->private_data;
+       ssize_t res, buf_size;
+       u32 offset, value;
+       struct wlan_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 = libertas_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 libertas_rdrf_read(struct file *file, char __user *userbuf,
+                                 size_t count, loff_t *ppos)
+{
+       wlan_private *priv = file->private_data;
+       wlan_adapter *adapter = priv->adapter;
+       struct wlan_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 = libertas_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, adapter->offsetvalue.value);
+
+       ret = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+       free_page(addr);
+
+       return ret;
+}
+
+static ssize_t libertas_rdrf_write(struct file *file,
+                                   const char __user *userbuf,
+                                   size_t count, loff_t *ppos)
+{
+       wlan_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 libertas_wrrf_write(struct file *file,
+                                   const char __user *userbuf,
+                                   size_t count, loff_t *ppos)
+{
+
+       wlan_private *priv = file->private_data;
+       ssize_t res, buf_size;
+       u32 offset, value;
+       struct wlan_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 = libertas_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 libertas_debugfs_files {
+       char *name;
+       int perm;
+       struct file_operations fops;
+};
+
+struct libertas_debugfs_files debugfs_files[] = {
+       { "info", 0444, FOPS(libertas_dev_info, write_file_dummy), },
+       { "getscantable", 0444, FOPS(libertas_getscantable,
+                                       write_file_dummy), },
+       { "sleepparams", 0644, FOPS(libertas_sleepparams_read,
+                               libertas_sleepparams_write), },
+       { "extscan", 0600, FOPS(NULL, libertas_extscan), },
+       { "setuserscan", 0600, FOPS(NULL, libertas_setuserscan), },
+};
+
+struct libertas_debugfs_files debugfs_events_files[] = {
+       {"low_rssi", 0644, FOPS(libertas_lowrssi_read,
+                               libertas_lowrssi_write), },
+       {"low_snr", 0644, FOPS(libertas_lowsnr_read,
+                               libertas_lowsnr_write), },
+       {"failure_count", 0644, FOPS(libertas_failcount_read,
+                               libertas_failcount_write), },
+       {"beacon_missed", 0644, FOPS(libertas_bcnmiss_read,
+                               libertas_bcnmiss_write), },
+       {"high_rssi", 0644, FOPS(libertas_highrssi_read,
+                               libertas_highrssi_write), },
+       {"high_snr", 0644, FOPS(libertas_highsnr_read,
+                               libertas_highsnr_write), },
+};
+
+struct libertas_debugfs_files debugfs_regs_files[] = {
+       {"rdmac", 0644, FOPS(libertas_rdmac_read, libertas_rdmac_write), },
+       {"wrmac", 0600, FOPS(NULL, libertas_wrmac_write), },
+       {"rdbbp", 0644, FOPS(libertas_rdbbp_read, libertas_rdbbp_write), },
+       {"wrbbp", 0600, FOPS(NULL, libertas_wrbbp_write), },
+       {"rdrf", 0644, FOPS(libertas_rdrf_read, libertas_rdrf_write), },
+       {"wrrf", 0600, FOPS(NULL, libertas_wrrf_write), },
+};
+
+void libertas_debugfs_init(void)
+{
+       if (!libertas_dir)
+               libertas_dir = debugfs_create_dir("libertas_wireless", NULL);
+
+       return;
+}
+
+void libertas_debugfs_remove(void)
+{
+       if (libertas_dir)
+                debugfs_remove(libertas_dir);
+       return;
+}
+
+void libertas_debugfs_init_one(wlan_private *priv, struct net_device *dev)
+{
+       int i;
+       struct libertas_debugfs_files *files;
+       if (!libertas_dir)
+               goto exit;
+
+       priv->debugfs_dir = debugfs_create_dir(dev->name, libertas_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
+       libertas_debug_init(priv, dev);
+#endif
+exit:
+       return;
+}
+
+void libertas_debugfs_remove_one(wlan_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_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]);
+}
+
+/* debug entry */
+
+#define item_size(n) (sizeof ((wlan_adapter *)0)->n)
+#define item_addr(n) ((u32) &((wlan_adapter *)0)->n)
+
+struct debug_data {
+       char name[32];
+       u32 size;
+       u32 addr;
+};
+
+/* To debug any member of wlan_adapter, 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 = sizeof(items) / sizeof(items[0]);
+
+/**
+ *  @brief convert string to number
+ *
+ *  @param s              pointer to numbered string
+ *  @return       converted number from string s
+ */
+static int string_to_number(char *s)
+{
+       int r = 0;
+       int base = 0;
+
+       if ((strncmp(s, "0x", 2) == 0) || (strncmp(s, "0X", 2) == 0))
+               base = 16;
+       else
+               base = 10;
+
+       if (base == 16)
+               s += 2;
+
+       for (s = s; *s != 0; s++) {
+               if ((*s >= 48) && (*s <= 57))
+                       r = (r * base) + (*s - 48);
+               else if ((*s >= 65) && (*s <= 70))
+                       r = (r * base) + (*s - 55);
+               else if ((*s >= 97) && (*s <= 102))
+                       r = (r * base) + (*s - 87);
+               else
+                       break;
+       }
+
+       return r;
+}
+
+/**
+ *  @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 wlan_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);
+
+               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 int wlan_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 = (char *)kmalloc(cnt, GFP_KERNEL);
+       if (pdata == NULL)
+               return 0;
+
+       if (copy_from_user(pdata, buf, cnt)) {
+               lbs_pr_debug(1, "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 = string_to_number(p2);
+                       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;
+                       break;
+               } while (1);
+       }
+       kfree(pdata);
+
+       return cnt;
+}
+
+static struct file_operations libertas_debug_fops = {
+       .owner = THIS_MODULE,
+       .open = open_file_generic,
+       .write = wlan_debugfs_write,
+       .read = wlan_debugfs_read,
+};
+
+/**
+ *  @brief create debug proc file
+ *
+ *  @param priv           pointer wlan_private
+ *  @param dev     pointer net_device
+ *  @return       N/A
+ */
+void libertas_debug_init(wlan_private * priv, struct net_device *dev)
+{
+       int i;
+
+       if (!priv->debugfs_dir)
+               return;
+
+       for (i = 0; i < num_of_items; i++)
+               items[i].addr += (u32) priv->adapter;
+
+       priv->debugfs_debug = debugfs_create_file("debug", 0644,
+                                                 priv->debugfs_dir, &items[0],
+                                                 &libertas_debug_fops);
+}
+
+/**
+ *  @brief remove proc file
+ *
+ *  @param priv           pointer wlan_private
+ *  @return       N/A
+ */
+void libertas_debug_remove(wlan_private * priv)
+{
+       debugfs_remove(priv->debugfs_debug);
+}
diff --git a/drivers/net/wireless/libertas/debugfs.h b/drivers/net/wireless/libertas/debugfs.h
new file mode 100644 (file)
index 0000000..880a11b
--- /dev/null
@@ -0,0 +1,6 @@
+void libertas_debugfs_init(void);
+void libertas_debugfs_remove(void);
+
+void libertas_debugfs_init_one(wlan_private *priv, struct net_device *dev);
+void libertas_debugfs_remove_one(wlan_private *priv);
+
diff --git a/drivers/net/wireless/libertas/decl.h b/drivers/net/wireless/libertas/decl.h
new file mode 100644 (file)
index 0000000..606bdd0
--- /dev/null
@@ -0,0 +1,83 @@
+/**
+  *  This file contains declaration referring to
+  *  functions defined in other source files
+  */
+
+#ifndef _WLAN_DECL_H_
+#define _WLAN_DECL_H_
+
+#include "defs.h"
+
+/** Function Prototype Declaration */
+struct wlan_private;
+struct sk_buff;
+struct net_device;
+
+extern char *libertas_fw_name;
+
+void libertas_free_adapter(wlan_private * priv);
+int libertas_set_mac_packet_filter(wlan_private * priv);
+
+int libertas_send_null_packet(wlan_private * priv, u8 pwr_mgmt);
+void libertas_send_tx_feedback(wlan_private * priv);
+u8 libertas_check_last_packet_indication(wlan_private * priv);
+
+int libertas_free_cmd_buffer(wlan_private * priv);
+struct cmd_ctrl_node;
+struct cmd_ctrl_node *libertas_get_free_cmd_ctrl_node(wlan_private * priv);
+
+void libertas_set_cmd_ctrl_node(wlan_private * priv,
+                   struct cmd_ctrl_node *ptempnode,
+                   u32 cmd_oid, u16 wait_option, void *pdata_buf);
+
+int libertas_prepare_and_send_command(wlan_private * priv,
+                         u16 cmd_no,
+                         u16 cmd_action,
+                         u16 wait_option, u32 cmd_oid, void *pdata_buf);
+
+void libertas_queue_cmd(wlan_adapter * adapter, struct cmd_ctrl_node *cmdnode, u8 addtail);
+
+int libertas_allocate_cmd_buffer(wlan_private * priv);
+int libertas_execute_next_command(wlan_private * priv);
+int libertas_process_event(wlan_private * priv);
+void libertas_interrupt(struct net_device *);
+int libertas_set_radio_control(wlan_private * priv);
+u32 libertas_index_to_data_rate(u8 index);
+u8 libertas_data_rate_to_index(u32 rate);
+void libertas_get_fwversion(wlan_adapter * adapter, char *fwversion, int maxlen);
+
+int libertas_upload_rx_packet(wlan_private * priv, struct sk_buff *skb);
+
+/** The proc fs interface */
+int libertas_process_rx_command(wlan_private * priv);
+int libertas_process_tx(wlan_private * priv, struct sk_buff *skb);
+void libertas_cleanup_and_insert_cmd(wlan_private * priv,
+                                       struct cmd_ctrl_node *ptempcmd);
+void __libertas_cleanup_and_insert_cmd(wlan_private * priv,
+                                       struct cmd_ctrl_node *ptempcmd);
+
+int libertas_set_regiontable(wlan_private * priv, u8 region, u8 band);
+
+int libertas_process_rxed_packet(wlan_private * priv, struct sk_buff *);
+
+void libertas_ps_sleep(wlan_private * priv, int wait_option);
+void libertas_ps_confirm_sleep(wlan_private * priv, u16 psmode);
+void libertas_ps_wakeup(wlan_private * priv, int wait_option);
+
+void libertas_tx_runqueue(wlan_private *priv);
+
+extern struct chan_freq_power *libertas_find_cfp_by_band_and_channel(
+                               wlan_adapter * adapter, u8 band, u16 channel);
+
+extern void libertas_mac_event_disconnected(wlan_private * priv);
+
+void libertas_send_iwevcustom_event(wlan_private * priv, s8 * str);
+
+int reset_device(wlan_private *priv);
+/* main.c */
+extern struct chan_freq_power *libertas_get_region_cfp_table(u8 region, u8 band,
+                                                            int *cfp_no);
+wlan_private *wlan_add_card(void *card);
+int wlan_remove_card(void *card);
+
+#endif                         /* _WLAN_DECL_H_ */
diff --git a/drivers/net/wireless/libertas/defs.h b/drivers/net/wireless/libertas/defs.h
new file mode 100644 (file)
index 0000000..fb1478c
--- /dev/null
@@ -0,0 +1,369 @@
+/**
+  * This header file contains global constant/enum definitions,
+  * global variable declaration.
+  */
+#ifndef _WLAN_DEFS_H_
+#define _WLAN_DEFS_H_
+
+#include <linux/spinlock.h>
+
+extern unsigned int libertas_debug;
+
+#define DRV_NAME               "usb8xxx"
+
+#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
+#define lbs_pr_debug(level, format, args...) \
+       do { if (libertas_debug >= level) \
+       printk(KERN_INFO DRV_NAME": " format, ##args); } while (0)
+#define lbs_dev_dbg(level, device, format, args...) \
+        lbs_pr_debug(level, "%s: " format, \
+        (device)->bus_id , ## args)
+
+static inline void lbs_dbg_hex(char *prompt, u8 * buf, int len)
+{
+       int i = 0;
+
+       if (!libertas_debug)
+               return;
+
+       printk(KERN_DEBUG "%s: ", prompt);
+       for (i = 1; i <= len; i++) {
+               printk(KERN_DEBUG "%02x ", (u8) * buf);
+               buf++;
+       }
+       printk("\n");
+}
+#else
+#define lbs_pr_debug(level, format, args...)           do {} while (0)
+#define lbs_dev_dbg(level, device, format, args...)    do {} while (0)
+#define lbs_dbg_hex(x,y,z)                             do {} while (0)
+#endif
+
+#define        ENTER()                 lbs_pr_debug(1, "Enter: %s, %s:%i\n", \
+                                       __FUNCTION__, __FILE__, __LINE__)
+#define        LEAVE()                 lbs_pr_debug(1, "Leave: %s, %s:%i\n", \
+                                       __FUNCTION__, __FILE__, __LINE__)
+
+/** 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 MRVDRV_NUM_OF_CMD_BUFFER        10
+#define MRVDRV_SIZE_OF_CMD_BUFFER       (2 * 1024)
+#define MRVDRV_MAX_CHANNEL_SIZE                14
+#define MRVDRV_MAX_BSSID_LIST          64
+#define MRVDRV_ASSOCIATION_TIME_OUT    255
+#define MRVDRV_SNAP_HEADER_LEN          8
+
+#define        WLAN_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_DEBUG_RX_PATH           0x00000001
+#define MRVDRV_DEBUG_TX_PATH           0x00000002
+
+#define MRVDRV_MIN_BEACON_INTERVAL             20
+#define MRVDRV_MAX_BEACON_INTERVAL             1000
+#define MRVDRV_BEACON_INTERVAL                 100
+
+/** 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)
+
+/** WPA key LENGTH*/
+#define MRVL_MAX_KEY_WPA_KEY_LENGTH     32
+
+#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 B_SUPPORTED_RATES              8
+#define G_SUPPORTED_RATES              14
+
+#define        WLAN_SUPPORTED_RATES            14
+
+#define        MAX_LEDS                        8
+
+#define IS_MESH_FRAME(x) (x->cb[6])
+#define SET_MESH_FRAME(x) (x->cb[6]=1)
+#define UNSET_MESH_FRAME(x) (x->cb[6]=0)
+
+/** Global Variable Declaration */
+typedef struct _wlan_private wlan_private;
+typedef struct _wlan_adapter wlan_adapter;
+extern const char libertas_driver_version[];
+extern u16 libertas_region_code_to_index[MRVDRV_MAX_REGION_CODE];
+
+extern u8 libertas_wlan_data_rates[WLAN_SUPPORTED_RATES];
+
+extern u8 libertas_supported_rates[G_SUPPORTED_RATES];
+
+extern u8 libertas_adhoc_rates_g[G_SUPPORTED_RATES];
+
+extern u8 libertas_adhoc_rates_b[4];
+
+/** 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
+};
+
+/** WLAN_802_11_AUTH_ALG*/
+enum WLAN_802_11_AUTH_ALG {
+       AUTH_ALG_OPEN_SYSTEM = 1,
+       AUTH_ALG_SHARED_KEY = 2,
+       AUTH_ALG_NETWORK_EAP = 8,
+};
+
+/** WLAN_802_1X_AUTH_ALG */
+enum WLAN_802_1X_AUTH_ALG {
+       WLAN_1X_AUTH_ALG_NONE = 1,
+       WLAN_1X_AUTH_ALG_LEAP = 2,
+       WLAN_1X_AUTH_ALG_TLS = 4,
+       WLAN_1X_AUTH_ALG_TTLS = 8,
+       WLAN_1X_AUTH_ALG_MD5 = 16,
+};
+
+/** WLAN_802_11_ENCRYPTION_MODE */
+enum WLAN_802_11_ENCRYPTION_MODE {
+       CIPHER_NONE,
+       CIPHER_WEP40,
+       CIPHER_TKIP,
+       CIPHER_CCMP,
+       CIPHER_WEP104,
+};
+
+/** WLAN_802_11_POWER_MODE */
+enum WLAN_802_11_POWER_MODE {
+       wlan802_11powermodecam,
+       wlan802_11powermodemax_psp,
+       wlan802_11Powermodefast_psp,
+       /*not a real mode, defined as an upper bound */
+       wlan802_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
+};
+
+/** WLAN_MEDIA_STATE */
+enum WLAN_MEDIA_STATE {
+       libertas_connected,
+       libertas_disconnected
+};
+
+/** WLAN_802_11_PRIVACY_FILTER */
+enum WLAN_802_11_PRIVACY_FILTER {
+       wlan802_11privfilteracceptall,
+       wlan802_11privfilter8021xWEP
+};
+
+/** mv_ms_type */
+enum mv_ms_type {
+       MVMS_DAT = 0,
+       MVMS_CMD = 1,
+       MVMS_TXDONE = 2,
+       MVMS_EVENT
+};
+
+/** WLAN_802_11_NETWORK_INFRASTRUCTURE */
+enum WLAN_802_11_NETWORK_INFRASTRUCTURE {
+       wlan802_11ibss,
+       wlan802_11infrastructure,
+       wlan802_11autounknown,
+       /*defined as upper bound */
+       wlan802_11infrastructuremax
+};
+
+/** WLAN_802_11_AUTHENTICATION_MODE */
+enum WLAN_802_11_AUTHENTICATION_MODE {
+       wlan802_11authmodeopen = 0x00,
+       wlan802_11authmodeshared = 0x01,
+       wlan802_11authmodenetworkEAP = 0x80,
+};
+
+/** WLAN_802_11_WEP_STATUS */
+enum WLAN_802_11_WEP_STATUS {
+       wlan802_11WEPenabled,
+       wlan802_11WEPdisabled,
+};
+
+/** 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
+#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                         /* _WLAN_DEFS_H_ */
diff --git a/drivers/net/wireless/libertas/dev.h b/drivers/net/wireless/libertas/dev.h
new file mode 100644 (file)
index 0000000..b1f876f
--- /dev/null
@@ -0,0 +1,403 @@
+/**
+  * This file contains definitions and data structures specific
+  * to Marvell 802.11 NIC. It contains the Device Information
+  * structure wlan_adapter.
+  */
+#ifndef _WLAN_DEV_H_
+#define _WLAN_DEV_H_
+
+#include <linux/netdevice.h>
+#include <linux/wireless.h>
+#include <linux/ethtool.h>
+#include <linux/debugfs.h>
+
+#include "defs.h"
+#include "scan.h"
+#include "thread.h"
+
+extern struct ethtool_ops libertas_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 wlan_802_11_security {
+       u8 WPAenabled;
+       u8 WPA2enabled;
+       enum WLAN_802_11_WEP_STATUS WEPstatus;
+       enum WLAN_802_11_AUTHENTICATION_MODE authmode;
+       enum WLAN_802_1X_AUTH_ALG auth1xalg;
+       enum WLAN_802_11_ENCRYPTION_MODE Encryptionmode;
+};
+
+/** Current Basic Service Set State Structure */
+struct current_bss_params {
+       struct bss_descriptor bssdescriptor;
+       /** bssid */
+       u8 bssid[ETH_ALEN];
+       /** ssid */
+       struct WLAN_802_11_SSID ssid;
+
+       /** band */
+       u8 band;
+       /** channel */
+       u8 channel;
+       /** number of rates supported */
+       int numofrates;
+       /** supported rates*/
+       u8 datarates[WLAN_SUPPORTED_RATES];
+};
+
+/** sleep_params */
+struct sleep_params {
+       u16 sp_error;
+       u16 sp_offset;
+       u16 sp_stabletime;
+       u8 sp_calcontrol;
+       u8 sp_extsleepclk;
+       u16 sp_reserved;
+};
+
+/** Data structure for the Marvell WLAN device */
+typedef struct _wlan_dev {
+       /** device name */
+       char name[DEV_NAME_LEN];
+       /** card pointer */
+       void *card;
+       /** IO port */
+       u32 ioport;
+       /** Upload received */
+       u32 upld_rcv;
+       /** Upload type */
+       u32 upld_typ;
+       /** Upload length */
+       u32 upld_len;
+       /** netdev pointer */
+       struct net_device *netdev;
+       /* Upload buffer */
+       u8 upld_buf[WLAN_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;
+} wlan_dev_t, *pwlan_dev_t;
+
+/* Mesh statistics */
+struct wlan_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 */
+};
+
+/** Private structure for the MV device */
+struct _wlan_private {
+       int open;
+       int mesh_open;
+       int infra_open;
+
+       wlan_adapter *adapter;
+       wlan_dev_t wlan_dev;
+
+       struct net_device_stats stats;
+       struct net_device *mesh_dev ; /* Virtual device */
+
+       struct iw_statistics wstats;
+       struct wlan_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;
+
+       const struct firmware *firmware;
+       struct device *hotplug_device;
+
+       /** thread to service interrupts */
+       struct wlan_thread mainthread;
+
+       struct delayed_work assoc_work;
+       struct workqueue_struct *assoc_thread;
+};
+
+/** 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_MODE                        3
+#define ASSOC_FLAG_BSSID               4
+#define ASSOC_FLAG_WEP_KEYS            5
+#define ASSOC_FLAG_WEP_TX_KEYIDX       6
+#define ASSOC_FLAG_WPA_MCAST_KEY       7
+#define ASSOC_FLAG_WPA_UCAST_KEY       8
+#define ASSOC_FLAG_SECINFO             9
+#define ASSOC_FLAG_WPA_IE              10
+       unsigned long flags;
+
+       struct WLAN_802_11_SSID ssid;
+       u8 channel;
+       enum WLAN_802_11_NETWORK_INFRASTRUCTURE mode;
+       u8 bssid[ETH_ALEN];
+
+       /** WEP keys */
+       struct WLAN_802_11_KEY wep_keys[4];
+       u16 wep_tx_keyidx;
+
+       /** WPA keys */
+       struct WLAN_802_11_KEY wpa_mcast_key;
+       struct WLAN_802_11_KEY wpa_unicast_key;
+
+       struct wlan_802_11_security secinfo;
+
+       /** WPA Information Elements*/
+#define MAX_WPA_IE_LEN 64
+       u8 wpa_ie[MAX_WPA_IE_LEN];
+       u8 wpa_ie_len;
+};
+
+/** Wlan adapter data structure*/
+struct _wlan_adapter {
+       /** STATUS variables */
+       u32 fwreleasenumber;
+       u32 fwcapinfo;
+       /* protected with big lock */
+
+       struct mutex lock;
+
+       u8 tmptxbuf[WLAN_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;
+       u8 nr_cmd_pending;
+       /* command related variables protected by adapter->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;
+
+       /* TX queue used in PS mode */
+       spinlock_t txqueue_lock;
+       struct sk_buff *tx_queue_ps[NR_TX_QUEUE];
+       unsigned int tx_queue_idx;
+
+       u8 hisregcpy;
+
+       /** current ssid/bssid related parameters*/
+       struct current_bss_params curbssparams;
+
+       enum WLAN_802_11_NETWORK_INFRASTRUCTURE inframode;
+
+       struct bss_descriptor *pattemptedbssdesc;
+
+       struct WLAN_802_11_SSID previousssid;
+       u8 previousbssid[ETH_ALEN];
+
+       struct bss_descriptor *scantable;
+       u32 numinscantable;
+
+       u8 scantype;
+       u32 scanmode;
+
+       u16 beaconperiod;
+       u8 adhoccreate;
+
+       /** capability Info used in Association, start, join */
+       struct ieeetypes_capinfo capinfo;
+
+       /** 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;
+       /** control G rates */
+       u8 adhoc_grate_enabled;
+
+       u32 txantenna;
+       u32 rxantenna;
+
+       u8 adhocchannel;
+       u32 fragthsd;
+       u32 rtsthsd;
+
+       u32 datarate;
+       u8 is_datarate_auto;
+
+       u16 listeninterval;
+       u16 prescan;
+       u8 txretrycount;
+
+       /** Tx-related variables (for single packet tx) */
+       struct sk_buff *currenttxskb;
+       u16 TxLockFlag;
+
+       /** NIC Operation characteristics */
+       u16 currentpacketfilter;
+       u32 connect_status;
+       u16 regioncode;
+       u16 regiontableindex;
+       u16 txpowerlevel;
+
+       /** POWER MANAGEMENT AND PnP SUPPORT */
+       u8 surpriseremoved;
+       u16 atimwindow;
+
+       u16 psmode;             /* Wlan802_11PowermodeCAM=disable
+                                  Wlan802_11PowermodeMAX_PSP=enable */
+       u16 multipledtim;
+       u32 psstate;
+       u8 needtowakeup;
+
+       struct PS_CMD_ConfirmSleep libertas_ps_confirm_sleep;
+       u16 locallisteninterval;
+       u16 nullpktinterval;
+
+       struct assoc_request * assoc_req;
+
+       /** Encryption parameter */
+       struct wlan_802_11_security secinfo;
+
+       /** WEP keys */
+       struct WLAN_802_11_KEY wep_keys[4];
+       u16 wep_tx_keyidx;
+
+       /** WPA keys */
+       struct WLAN_802_11_KEY wpa_mcast_key;
+       struct WLAN_802_11_KEY wpa_unicast_key;
+
+       /** WPA Information Elements*/
+#define MAX_WPA_IE_LEN 64
+       u8 wpa_ie[MAX_WPA_IE_LEN];
+       u8 wpa_ie_len;
+
+       u16 rxantennamode;
+       u16 txantennamode;
+
+       /** Requested Signal Strength*/
+       u16 bcn_avg_factor;
+       u16 data_avg_factor;
+       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;
+       u16 rxpd_rate;
+
+       u8 radioon;
+       u32 preamble;
+
+       /** Multi bands Parameter*/
+       u8 libertas_supported_rates[G_SUPPORTED_RATES];
+
+       /** Blue Tooth Co-existence Arbitration */
+
+       /** 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 wlan_802_11d_domain_reg domainreg;
+       struct parsed_region_chan_11d parsed_region_chan;
+
+       /** FSM variable for 11d support */
+       u32 enable11d;
+
+       /**     MISCELLANEOUS */
+       u8 *prdeeprom;
+       struct wlan_offset_value offsetvalue;
+
+       struct cmd_ds_802_11_get_log logmsg;
+       u16 scanprobes;
+
+       u32 pkttxctrl;
+
+       u16 txrate;
+       u32 linkmode;
+       u32 radiomode;
+       u32 debugmode;
+       u8 fw_ready;
+};
+
+#endif                         /* _WLAN_DEV_H_ */
diff --git a/drivers/net/wireless/libertas/ethtool.c b/drivers/net/wireless/libertas/ethtool.c
new file mode 100644 (file)
index 0000000..0064de5
--- /dev/null
@@ -0,0 +1,184 @@
+
+#include <linux/netdevice.h>
+#include <linux/ethtool.h>
+#include <linux/delay.h>
+
+#include "host.h"
+#include "sbi.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"
+};
+
+static void libertas_ethtool_get_drvinfo(struct net_device *dev,
+                                        struct ethtool_drvinfo *info)
+{
+       wlan_private *priv = (wlan_private *) dev->priv;
+       char fwver[32];
+
+       libertas_get_fwversion(priv->adapter, fwver, sizeof(fwver) - 1);
+
+       strcpy(info->driver, "libertas");
+       strcpy(info->version, libertas_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 LIBERTAS_EEPROM_LEN 16384
+
+static int libertas_ethtool_get_eeprom_len(struct net_device *dev)
+{
+       return LIBERTAS_EEPROM_LEN;
+}
+
+static int libertas_ethtool_get_eeprom(struct net_device *dev,
+                                  struct ethtool_eeprom *eeprom, u8 * bytes)
+{
+       wlan_private *priv = (wlan_private *) dev->priv;
+       wlan_adapter *adapter = priv->adapter;
+       struct wlan_ioctl_regrdwr regctrl;
+       char *ptr;
+       int ret;
+
+       regctrl.action = 0;
+       regctrl.offset = eeprom->offset;
+       regctrl.NOB = eeprom->len;
+
+       if (eeprom->offset + eeprom->len > LIBERTAS_EEPROM_LEN)
+               return -EINVAL;
+
+//      mutex_lock(&priv->mutex);
+
+       adapter->prdeeprom =
+                   (char *)kmalloc(eeprom->len+sizeof(regctrl), GFP_KERNEL);
+       if (!adapter->prdeeprom)
+               return -ENOMEM;
+       memcpy(adapter->prdeeprom, &regctrl, sizeof(regctrl));
+
+       /* +14 is for action, offset, and NOB in
+        * response */
+       lbs_pr_debug(1, "action:%d offset: %x NOB: %02x\n",
+              regctrl.action, regctrl.offset, regctrl.NOB);
+
+       ret = libertas_prepare_and_send_command(priv,
+                                   cmd_802_11_eeprom_access,
+                                   regctrl.action,
+                                   cmd_option_waitforrsp, 0,
+                                   &regctrl);
+
+       if (ret) {
+               if (adapter->prdeeprom)
+                       kfree(adapter->prdeeprom);
+               LEAVE();
+                       return ret;
+       }
+
+       mdelay(10);
+
+       ptr = (char *)adapter->prdeeprom;
+
+       /* skip the command header, but include the "value" u32 variable */
+       ptr = ptr + sizeof(struct wlan_ioctl_regrdwr) - 4;
+
+       /*
+        * Return the result back to the user
+        */
+       memcpy(bytes, ptr, eeprom->len);
+
+       if (adapter->prdeeprom)
+               kfree(adapter->prdeeprom);
+//     mutex_unlock(&priv->mutex);
+
+        return 0;
+}
+
+static void libertas_ethtool_get_stats(struct net_device * dev,
+                               struct ethtool_stats * stats, u64 * data)
+{
+       wlan_private *priv = dev->priv;
+
+       ENTER();
+
+       stats->cmd = ETHTOOL_GSTATS;
+       BUG_ON(stats->n_stats != MESH_STATS_NUM);
+
+        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;
+
+       LEAVE();
+}
+
+static int libertas_ethtool_get_stats_count(struct net_device * dev)
+{
+       int ret;
+       wlan_private *priv = dev->priv;
+       struct cmd_ds_mesh_access mesh_access;
+
+       ENTER();
+       /* Get Mesh Statistics */
+       ret = libertas_prepare_and_send_command(priv,
+                       cmd_mesh_access, cmd_act_mesh_get_stats,
+                       cmd_option_waitforrsp, 0, &mesh_access);
+
+       if (ret) {
+               LEAVE();
+               return 0;
+       }
+
+        priv->mstats.fwd_drop_rbt = mesh_access.data[0];
+        priv->mstats.fwd_drop_ttl = mesh_access.data[1];
+        priv->mstats.fwd_drop_noroute = mesh_access.data[2];
+        priv->mstats.fwd_drop_nobuf = mesh_access.data[3];
+        priv->mstats.fwd_unicast_cnt = mesh_access.data[4];
+        priv->mstats.fwd_bcast_cnt = mesh_access.data[5];
+        priv->mstats.drop_blind = mesh_access.data[6];
+
+       LEAVE();
+       return MESH_STATS_NUM;
+}
+
+static void libertas_ethtool_get_strings (struct net_device * dev,
+                                         u32 stringset,
+                                         u8 * s)
+{
+       int i;
+
+       ENTER();
+       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;
+        }
+       LEAVE();
+}
+
+struct ethtool_ops libertas_ethtool_ops = {
+       .get_drvinfo = libertas_ethtool_get_drvinfo,
+       .get_eeprom =  libertas_ethtool_get_eeprom,
+       .get_eeprom_len = libertas_ethtool_get_eeprom_len,
+       .get_stats_count = libertas_ethtool_get_stats_count,
+       .get_ethtool_stats = libertas_ethtool_get_stats,
+       .get_strings = libertas_ethtool_get_strings,
+};
+
diff --git a/drivers/net/wireless/libertas/fw.c b/drivers/net/wireless/libertas/fw.c
new file mode 100644 (file)
index 0000000..b194a45
--- /dev/null
@@ -0,0 +1,361 @@
+/**
+  * This file contains the initialization for FW and HW
+  */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+
+#include <linux/vmalloc.h>
+#include <linux/firmware.h>
+#include <linux/version.h>
+
+#include "host.h"
+#include "sbi.h"
+#include "defs.h"
+#include "decl.h"
+#include "dev.h"
+#include "fw.h"
+#include "wext.h"
+#include "if_usb.h"
+
+char *libertas_fw_name = NULL;
+module_param_named(fw_name, libertas_fw_name, charp, 0644);
+
+unsigned int libertas_debug = 0;
+module_param(libertas_debug, int, 0);
+
+/**
+ *  @brief This function checks the validity of Boot2/FW image.
+ *
+ *  @param data              pointer to image
+ *         len               image length
+ *  @return     0 or -1
+ */
+static int check_fwfile_format(u8 *data, u32 totlen)
+{
+       u8  bincmd, exit;
+       u32 blksize, offset, len;
+       int ret;
+
+       ret = 1;
+       exit = len = 0;
+
+       do {
+               bincmd = *data;
+               blksize = *(u32*)(data + offsetof(struct fwheader, datalength));
+               switch (bincmd) {
+               case FW_HAS_DATA_TO_RECV:
+                       offset = sizeof(struct fwheader) + blksize;
+                       data += offset;
+                       len += offset;
+                       if (len >= totlen)
+                               exit = 1;
+                       break;
+               case FW_HAS_LAST_BLOCK:
+                       exit = 1;
+                       ret = 0;
+                       break;
+               default:
+                       exit = 1;
+                       break;
+               }
+       } while (!exit);
+
+       if (ret)
+               lbs_pr_err("bin file format check FAIL...\n");
+       else
+               lbs_pr_debug(1, "bin file format check PASS...\n");
+
+       return ret;
+}
+
+/**
+ *  @brief This function downloads firmware image, gets
+ *  HW spec from firmware and set basic parameters to
+ *  firmware.
+ *
+ *  @param priv    A pointer to wlan_private structure
+ *  @return       0 or -1
+ */
+static int wlan_setup_station_hw(wlan_private * priv)
+{
+       int ret = -1;
+       wlan_adapter *adapter = priv->adapter;
+
+       ENTER();
+
+       if ((ret = request_firmware(&priv->firmware, libertas_fw_name,
+                                   priv->hotplug_device)) < 0) {
+               lbs_pr_err("request_firmware() failed, error code = %#x\n",
+                      ret);
+               lbs_pr_err("%s not found in /lib/firmware\n", libertas_fw_name);
+               goto done;
+       }
+
+       if(check_fwfile_format(priv->firmware->data, priv->firmware->size)) {
+               release_firmware(priv->firmware);
+               goto done;
+       }
+
+       ret = libertas_sbi_prog_firmware(priv);
+
+       release_firmware(priv->firmware);
+
+       if (ret) {
+               lbs_pr_debug(1, "Bootloader in invalid state!\n");
+               ret = -1;
+               goto done;
+       }
+
+       /*
+        * Read MAC address from HW
+        */
+       memset(adapter->current_addr, 0xff, ETH_ALEN);
+
+       ret = libertas_prepare_and_send_command(priv, cmd_get_hw_spec,
+                                   0, cmd_option_waitforrsp, 0, NULL);
+
+       if (ret) {
+               ret = -1;
+               goto done;
+       }
+
+       libertas_set_mac_packet_filter(priv);
+
+       /* Get the supported Data rates */
+       ret = libertas_prepare_and_send_command(priv, cmd_802_11_data_rate,
+                                   cmd_act_get_tx_rate,
+                                   cmd_option_waitforrsp, 0, NULL);
+
+       if (ret) {
+               ret = -1;
+               goto done;
+       }
+
+       ret = 0;
+done:
+       LEAVE();
+
+       return (ret);
+}
+
+static int wlan_allocate_adapter(wlan_private * priv)
+{
+       u32 ulbufsize;
+       wlan_adapter *adapter = priv->adapter;
+
+       struct bss_descriptor *ptempscantable;
+
+       /* Allocate buffer to store the BSSID list */
+       ulbufsize = sizeof(struct bss_descriptor) * MRVDRV_MAX_BSSID_LIST;
+       if (!(ptempscantable = kmalloc(ulbufsize, GFP_KERNEL))) {
+               libertas_free_adapter(priv);
+               return -1;
+       }
+
+       adapter->scantable = ptempscantable;
+       memset(adapter->scantable, 0, ulbufsize);
+
+       /* Allocate the command buffers */
+       libertas_allocate_cmd_buffer(priv);
+
+       memset(&adapter->libertas_ps_confirm_sleep, 0, sizeof(struct PS_CMD_ConfirmSleep));
+       adapter->libertas_ps_confirm_sleep.seqnum = cpu_to_le16(++adapter->seqnum);
+       adapter->libertas_ps_confirm_sleep.command =
+           cpu_to_le16(cmd_802_11_ps_mode);
+       adapter->libertas_ps_confirm_sleep.size =
+           cpu_to_le16(sizeof(struct PS_CMD_ConfirmSleep));
+       adapter->libertas_ps_confirm_sleep.result = 0;
+       adapter->libertas_ps_confirm_sleep.action =
+           cpu_to_le16(cmd_subcmd_sleep_confirmed);
+
+       return 0;
+}
+
+static void wlan_init_adapter(wlan_private * priv)
+{
+       wlan_adapter *adapter = priv->adapter;
+       int i;
+
+       adapter->scanprobes = 0;
+
+       adapter->bcn_avg_factor = DEFAULT_BCN_AVG_FACTOR;
+       adapter->data_avg_factor = DEFAULT_DATA_AVG_FACTOR;
+
+       /* ATIM params */
+       adapter->atimwindow = 0;
+
+       adapter->connect_status = libertas_disconnected;
+       memset(adapter->current_addr, 0xff, ETH_ALEN);
+
+       /* scan type */
+       adapter->scantype = cmd_scan_type_active;
+
+       /* scan mode */
+       adapter->scanmode = cmd_bss_type_any;
+
+       /* 802.11 specific */
+       adapter->secinfo.WEPstatus = wlan802_11WEPdisabled;
+       for (i = 0; i < sizeof(adapter->wep_keys) / sizeof(adapter->wep_keys[0]);
+            i++)
+               memset(&adapter->wep_keys[i], 0, sizeof(struct WLAN_802_11_KEY));
+       adapter->wep_tx_keyidx = 0;
+       adapter->secinfo.WEPstatus = wlan802_11WEPdisabled;
+       adapter->secinfo.authmode = wlan802_11authmodeopen;
+       adapter->secinfo.auth1xalg = WLAN_1X_AUTH_ALG_NONE;
+       adapter->secinfo.Encryptionmode = CIPHER_NONE;
+       adapter->inframode = wlan802_11infrastructure;
+
+       adapter->assoc_req = NULL;
+
+       adapter->numinscantable = 0;
+       adapter->pattemptedbssdesc = NULL;
+       mutex_init(&adapter->lock);
+
+       adapter->prescan = 1;
+
+       memset(&adapter->curbssparams, 0, sizeof(adapter->curbssparams));
+
+       /* PnP and power profile */
+       adapter->surpriseremoved = 0;
+
+       adapter->currentpacketfilter =
+           cmd_act_mac_rx_on | cmd_act_mac_tx_on;
+
+       adapter->radioon = RADIO_ON;
+       adapter->txantenna = RF_ANTENNA_2;
+       adapter->rxantenna = RF_ANTENNA_AUTO;
+
+       adapter->is_datarate_auto = 1;
+       adapter->beaconperiod = MRVDRV_BEACON_INTERVAL;
+
+       // set default value of capinfo.
+#define SHORT_PREAMBLE_ALLOWED         1
+       memset(&adapter->capinfo, 0, sizeof(adapter->capinfo));
+       adapter->capinfo.shortpreamble = SHORT_PREAMBLE_ALLOWED;
+
+       adapter->adhocchannel = DEFAULT_AD_HOC_CHANNEL;
+
+       adapter->psmode = wlan802_11powermodecam;
+       adapter->multipledtim = MRVDRV_DEFAULT_MULTIPLE_DTIM;
+
+       adapter->listeninterval = MRVDRV_DEFAULT_LISTEN_INTERVAL;
+
+       adapter->psstate = PS_STATE_FULL_POWER;
+       adapter->needtowakeup = 0;
+       adapter->locallisteninterval = 0;       /* default value in firmware will be used */
+
+       adapter->datarate = 0;  // Initially indicate the rate as auto
+
+       adapter->adhoc_grate_enabled = 0;
+
+       adapter->intcounter = 0;
+
+       adapter->currenttxskb = NULL;
+       adapter->pkttxctrl = 0;
+
+       memset(&adapter->tx_queue_ps, 0, NR_TX_QUEUE*sizeof(struct sk_buff*));
+       adapter->tx_queue_idx = 0;
+       spin_lock_init(&adapter->txqueue_lock);
+
+       return;
+}
+
+static void command_timer_fn(unsigned long data);
+
+int libertas_init_fw(wlan_private * priv)
+{
+       int ret = -1;
+       wlan_adapter *adapter = priv->adapter;
+
+       ENTER();
+
+       /* Allocate adapter structure */
+       if ((ret = wlan_allocate_adapter(priv)) != 0)
+               goto done;
+
+       /* init adapter structure */
+       wlan_init_adapter(priv);
+
+       /* init timer etc. */
+       setup_timer(&adapter->command_timer, command_timer_fn,
+                       (unsigned long)priv);
+
+       /* download fimrware etc. */
+       if ((ret = wlan_setup_station_hw(priv)) != 0) {
+               del_timer_sync(&adapter->command_timer);
+               goto done;
+       }
+
+       /* init 802.11d */
+       libertas_init_11d(priv);
+
+       ret = 0;
+done:
+       LEAVE();
+       return ret;
+}
+
+void libertas_free_adapter(wlan_private * priv)
+{
+       wlan_adapter *adapter = priv->adapter;
+
+       if (!adapter) {
+               lbs_pr_debug(1, "Why double free adapter?:)\n");
+               return;
+       }
+
+       lbs_pr_debug(1, "Free command buffer\n");
+       libertas_free_cmd_buffer(priv);
+
+       lbs_pr_debug(1, "Free commandTimer\n");
+       del_timer(&adapter->command_timer);
+
+       lbs_pr_debug(1, "Free scantable\n");
+       if (adapter->scantable) {
+               kfree(adapter->scantable);
+               adapter->scantable = NULL;
+       }
+
+       lbs_pr_debug(1, "Free adapter\n");
+
+       /* Free the adapter object itself */
+       kfree(adapter);
+       priv->adapter = NULL;
+}
+
+/**
+ *  This function handles the timeout of command sending.
+ *  It will re-send the same command again.
+ */
+static void command_timer_fn(unsigned long data)
+{
+       wlan_private *priv = (wlan_private *)data;
+       wlan_adapter *adapter = priv->adapter;
+       struct cmd_ctrl_node *ptempnode;
+       struct cmd_ds_command *cmd;
+       unsigned long flags;
+
+       ptempnode = adapter->cur_cmd;
+       cmd = (struct cmd_ds_command *)ptempnode->bufvirtualaddr;
+
+       lbs_pr_info("command_timer_fn fired (%x)\n", cmd->command);
+
+       if (!adapter->fw_ready)
+               return;
+
+       if (ptempnode == NULL) {
+               lbs_pr_debug(1, "PTempnode Empty\n");
+               return;
+       }
+
+       spin_lock_irqsave(&adapter->driver_lock, flags);
+       adapter->cur_cmd = NULL;
+       spin_unlock_irqrestore(&adapter->driver_lock, flags);
+
+       lbs_pr_debug(1, "Re-sending same command as it timeout...!\n");
+       libertas_queue_cmd(adapter, ptempnode, 0);
+
+       wake_up_interruptible(&priv->mainthread.waitq);
+
+       return;
+}
diff --git a/drivers/net/wireless/libertas/fw.h b/drivers/net/wireless/libertas/fw.h
new file mode 100644 (file)
index 0000000..1f9ae26
--- /dev/null
@@ -0,0 +1,13 @@
+/**
+  * This header file contains FW interface related definitions.
+  */
+#ifndef _WLAN_FW_H_
+#define _WLAN_FW_H_
+
+#ifndef DEV_NAME_LEN
+#define DEV_NAME_LEN            32
+#endif
+
+int libertas_init_fw(wlan_private * priv);
+
+#endif                         /* _WLAN_FW_H_ */
diff --git a/drivers/net/wireless/libertas/host.h b/drivers/net/wireless/libertas/host.h
new file mode 100644 (file)
index 0000000..c0faaec
--- /dev/null
@@ -0,0 +1,338 @@
+/**
+  * This file contains definitions of WLAN commands.
+  */
+
+#ifndef _HOST_H_
+#define _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 ID */
+#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_key_material       0x005e
+
+#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_eeprom_access      0x0059
+
+#define cmd_802_11_band_config        0x0058
+
+#define cmd_802_11d_domain_info       0x005b
+
+#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_led_gpio_ctrl         0x004e
+
+#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_ret_bt_access                 0x8087
+
+#define cmd_fwt_access                0x0088
+#define cmd_ret_fwt_access                0x8088
+
+#define cmd_mesh_access               0x0090
+#define cmd_ret_mesh_access               0x8090
+
+/* 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
+
+/* command RET code, MSB is set to 1 */
+#define cmd_ret_hw_spec_info              0x8003
+#define cmd_ret_eeprom_update             0x8004
+#define cmd_ret_802_11_reset              0x8005
+#define cmd_ret_802_11_scan               0x8006
+#define cmd_ret_802_11_get_log            0x800b
+#define cmd_ret_mac_control               0x8028
+#define cmd_ret_mac_multicast_adr         0x8010
+#define cmd_ret_802_11_authenticate       0x8011
+#define cmd_ret_802_11_deauthenticate     0x8024
+#define cmd_ret_802_11_associate          0x8012
+#define cmd_ret_802_11_reassociate        0x8025
+#define cmd_ret_802_11_disassociate       0x8026
+#define cmd_ret_802_11_set_wep            0x8013
+#define cmd_ret_802_11_stat               0x8014
+#define cmd_ret_802_3_stat                0x8015
+#define cmd_ret_802_11_snmp_mib           0x8016
+#define cmd_ret_mac_reg_map               0x8017
+#define cmd_ret_bbp_reg_map               0x8018
+#define cmd_ret_rf_reg_map                0x8023
+#define cmd_ret_mac_reg_access            0x8019
+#define cmd_ret_bbp_reg_access            0x801a
+#define cmd_ret_rf_reg_access             0x801b
+#define cmd_ret_802_11_radio_control      0x801c
+#define cmd_ret_802_11_rf_channel         0x801d
+#define cmd_ret_802_11_rssi               0x801f
+#define cmd_ret_802_11_rf_tx_power        0x801e
+#define cmd_ret_802_11_rf_antenna         0x8020
+#define cmd_ret_802_11_ps_mode            0x8021
+#define cmd_ret_802_11_data_rate          0x8022
+
+#define cmd_ret_802_11_ad_hoc_start       0x802B
+#define cmd_ret_802_11_ad_hoc_join        0x802C
+
+#define cmd_ret_802_11_query_tkip_reply_cntrs  0x802e
+#define cmd_ret_802_11_enable_rsn              0x802f
+#define cmd_ret_802_11_pairwise_tsc       0x8036
+#define cmd_ret_802_11_group_tsc          0x8037
+#define cmd_ret_802_11_key_material       0x805e
+
+#define cmd_enable_rsn                    0x0001
+#define cmd_disable_rsn                   0x0000
+
+#define cmd_act_set                       0x0001
+#define cmd_act_get                       0x0000
+
+#define cmd_act_get_AES                   (cmd_act_get + 2)
+#define cmd_act_set_AES                   (cmd_act_set + 2)
+#define cmd_act_remove_aes                (cmd_act_set + 3)
+
+#define cmd_ret_802_11_set_afc            0x803c
+#define cmd_ret_802_11_get_afc            0x803d
+
+#define cmd_ret_802_11_ad_hoc_stop        0x8040
+
+#define cmd_ret_802_11_beacon_stop        0x8049
+
+#define cmd_ret_802_11_mac_address        0x804D
+#define cmd_ret_802_11_eeprom_access      0x8059
+
+#define cmd_ret_802_11_band_config        0x8058
+
+#define cmd_ret_802_11_sleep_params          0x8066
+
+#define cmd_ret_802_11_inactivity_timeout    0x8067
+
+#define cmd_ret_802_11d_domain_info      (0x8000 |                  \
+                                              cmd_802_11d_domain_info)
+
+#define cmd_ret_802_11_tpc_cfg        (cmd_802_11_tpc_cfg | 0x8000)
+#define cmd_ret_802_11_pwr_cfg        (cmd_802_11_pwr_cfg | 0x8000)
+
+#define cmd_ret_802_11_led_gpio_ctrl     0x804e
+
+#define cmd_ret_802_11_subscribe_event (cmd_802_11_subscribe_event | 0x8000)
+
+#define cmd_ret_802_11_rate_adapt_rateset      (cmd_802_11_rate_adapt_rateset | 0x8000)
+
+#define cmd_rte_802_11_tx_rate_query   (cmd_802_11_tx_rate_query | 0x8000)
+
+#define cmd_ret_get_tsf             0x8080
+
+/* 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                 0x0001
+#define cmd_type_wep_104_bit                0x0002
+
+#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
+
+#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
+};
+
+/* 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_mpp,
+       cmd_act_mesh_set_mpp,
+};
+
+/** Card Event definition */
+#define MACREG_INT_CODE_TX_PPA_FREE             0x00000000
+#define MACREG_INT_CODE_TX_DMA_DONE             0x00000001
+#define MACREG_INT_CODE_LINK_LOSE_W_SCAN        0x00000002
+#define MACREG_INT_CODE_LINK_LOSE_NO_SCAN       0x00000003
+#define MACREG_INT_CODE_LINK_SENSED             0x00000004
+#define MACREG_INT_CODE_CMD_FINISHED            0x00000005
+#define MACREG_INT_CODE_MIB_CHANGED             0x00000006
+#define MACREG_INT_CODE_INIT_DONE               0x00000007
+#define MACREG_INT_CODE_DEAUTHENTICATED         0x00000008
+#define MACREG_INT_CODE_DISASSOCIATED           0x00000009
+#define MACREG_INT_CODE_PS_AWAKE                0x0000000a
+#define MACREG_INT_CODE_PS_SLEEP                0x0000000b
+#define MACREG_INT_CODE_MIC_ERR_MULTICAST       0x0000000d
+#define MACREG_INT_CODE_MIC_ERR_UNICAST         0x0000000e
+#define MACREG_INT_CODE_WM_AWAKE                0x0000000f
+#define MACREG_INT_CODE_ADHOC_BCN_LOST          0x00000011
+#define MACREG_INT_CODE_RSSI_LOW               0x00000019
+#define MACREG_INT_CODE_SNR_LOW                        0x0000001a
+#define MACREG_INT_CODE_MAX_FAIL               0x0000001b
+#define MACREG_INT_CODE_RSSI_HIGH              0x0000001c
+#define MACREG_INT_CODE_SNR_HIGH               0x0000001d
+
+#endif                         /* _HOST_H_ */
diff --git a/drivers/net/wireless/libertas/hostcmd.h b/drivers/net/wireless/libertas/hostcmd.h
new file mode 100644 (file)
index 0000000..f239e5d
--- /dev/null
@@ -0,0 +1,693 @@
+/*
+ * This file contains the function prototypes, data structure
+ * and defines for all the host/station commands
+ */
+#ifndef __HOSTCMD__H
+#define __HOSTCMD__H
+
+#include <linux/wireless.h>
+#include "11d.h"
+#include "types.h"
+
+/* 802.11-related definitions */
+
+/* TxPD descriptor */
+struct txpd {
+       /* Current Tx packet status */
+       u32 tx_status;
+       /* Tx control */
+       u32 tx_control;
+       u32 tx_packet_location;
+       /* Tx packet length */
+       u16 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 */
+       u16 status;
+
+       /* SNR */
+       u8 snr;
+
+       /* Tx control */
+       u8 rx_control;
+
+       /* Pkt length */
+       u16 pkt_len;
+
+       /* Noise Floor */
+       u8 nf;
+
+       /* Rx Packet Rate */
+       u8 rx_rate;
+
+       /* Pkt addr */
+       u32 pkt_ptr;
+
+       /* Next Rx RxPD addr */
+       u32 next_rxpd_ptr;
+
+       /* Pkt Priority */
+       u8 priority;
+       u8 reserved[3];
+};
+
+struct cmd_ctrl_node {
+       /* CMD link list */
+       struct list_head list;
+       u32 status;
+       /* CMD ID */
+       u32 cmd_oid;
+       /*CMD wait option: wait for finish or no wait */
+       u16 wait_option;
+       /* command parameter */
+       void *pdata_buf;
+       /*command data */
+       u8 *bufvirtualaddr;
+       u16 cmdflags;
+       /* wait queue */
+       u16 cmdwaitqwoken;
+       wait_queue_head_t cmdwait_q;
+};
+
+/* WLAN_802_11_KEY
+ *
+ * Generic structure to hold all key types.  key type (WEP40, WEP104, TKIP, AES)
+ * is determined from the keylength field.
+ */
+struct WLAN_802_11_KEY {
+       u32 len;
+       u32 flags;  /* KEY_INFO_* from wlan_defs.h */
+       u8 key[MRVL_MAX_KEY_WPA_KEY_LENGTH];
+       u16 type; /* KEY_TYPE_* from wlan_defs.h */
+};
+
+struct IE_WPA {
+       u8 elementid;
+       u8 len;
+       u8 oui[4];
+       u16 version;
+};
+
+struct WLAN_802_11_SSID {
+       /* SSID length */
+       u32 ssidlength;
+
+       /* SSID information field */
+       u8 ssid[IW_ESSID_MAX_SIZE];
+};
+
+struct WPA_SUPPLICANT {
+       u8 wpa_ie[256];
+       u8 wpa_ie_len;
+};
+
+/* wlan_offset_value */
+struct wlan_offset_value {
+       u32 offset;
+       u32 value;
+};
+
+struct WLAN_802_11_FIXED_IEs {
+       u8 timestamp[8];
+       u16 beaconinterval;
+       u16 capabilities;
+};
+
+struct WLAN_802_11_VARIABLE_IEs {
+       u8 elementid;
+       u8 length;
+       u8 data[1];
+};
+
+/* Define general data structure */
+/* cmd_DS_GEN */
+struct cmd_ds_gen {
+       u16 command;
+       u16 size;
+       u16 seqnum;
+       u16 result;
+};
+
+#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 {
+       /* HW Interface version number */
+       u16 hwifversion;
+       /* HW version number */
+       u16 version;
+       /* Max number of TxPD FW can handle */
+       u16 nr_txpd;
+       /* Max no of Multicast address */
+       u16 nr_mcast_adr;
+       /* MAC address */
+       u8 permanentaddr[6];
+
+       /* region Code */
+       u16 regioncode;
+
+       /* Number of antenna used */
+       u16 nr_antenna;
+
+       /* FW release number, example 0x1234=1.2.3.4 */
+       u32 fwreleasenumber;
+
+       /* Base Address of TxPD queue */
+       u32 wcb_base;
+       /* Read Pointer of RxPd queue */
+       u32 rxpd_rdptr;
+
+       /* Write Pointer of RxPd queue */
+       u32 rxpd_wrptr;
+
+       /*FW/HW capability */
+       u32 fwcapinfo;
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_reset {
+       u16 action;
+};
+
+struct cmd_ds_802_11_subscribe_event {
+       u16 action;
+       u16 events;
+};
+
+/*
+ * 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 {
+       u16 bssdescriptsize;
+       u8 nr_sets;
+       u8 bssdesc_and_tlvbuffer[1];
+};
+
+struct cmd_ds_802_11_get_log {
+       u32 mcasttxframe;
+       u32 failed;
+       u32 retry;
+       u32 multiretry;
+       u32 framedup;
+       u32 rtssuccess;
+       u32 rtsfailure;
+       u32 ackfailure;
+       u32 rxfrag;
+       u32 mcastrxframe;
+       u32 fcserror;
+       u32 txframe;
+       u32 wepundecryptable;
+};
+
+struct cmd_ds_mac_control {
+       u16 action;
+       u16 reserved;
+};
+
+struct cmd_ds_mac_multicast_adr {
+       u16 action;
+       u16 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];
+       u16 reasoncode;
+};
+
+struct cmd_ds_802_11_associate {
+       u8 peerstaaddr[6];
+       struct ieeetypes_capinfo capinfo;
+       u16 listeninterval;
+       u16 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];
+       u16 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 */
+       u16 action;
+
+       /* key Index selected for Tx */
+       u16 keyindex;
+
+       /* 40, 128bit or TXWEP */
+       u8 keytype[4];
+       u8 keymaterial[4][16];
+};
+
+struct cmd_ds_802_3_get_stat {
+       u32 xmitok;
+       u32 rcvok;
+       u32 xmiterror;
+       u32 rcverror;
+       u32 rcvnobuffer;
+       u32 rcvcrcerror;
+};
+
+struct cmd_ds_802_11_get_stat {
+       u32 txfragmentcnt;
+       u32 mcasttxframecnt;
+       u32 failedcnt;
+       u32 retrycnt;
+       u32 Multipleretrycnt;
+       u32 rtssuccesscnt;
+       u32 rtsfailurecnt;
+       u32 ackfailurecnt;
+       u32 frameduplicatecnt;
+       u32 rxfragmentcnt;
+       u32 mcastrxframecnt;
+       u32 fcserrorcnt;
+       u32 bcasttxframecnt;
+       u32 bcastrxframecnt;
+       u32 txbeacon;
+       u32 rxbeacon;
+       u32 wepundecryptable;
+};
+
+struct cmd_ds_802_11_snmp_mib {
+       u16 querytype;
+       u16 oid;
+       u16 bufsize;
+       u8 value[128];
+};
+
+struct cmd_ds_mac_reg_map {
+       u16 buffersize;
+       u8 regmap[128];
+       u16 reserved;
+};
+
+struct cmd_ds_bbp_reg_map {
+       u16 buffersize;
+       u8 regmap[128];
+       u16 reserved;
+};
+
+struct cmd_ds_rf_reg_map {
+       u16 buffersize;
+       u8 regmap[64];
+       u16 reserved;
+};
+
+struct cmd_ds_mac_reg_access {
+       u16 action;
+       u16 offset;
+       u32 value;
+};
+
+struct cmd_ds_bbp_reg_access {
+       u16 action;
+       u16 offset;
+       u8 value;
+       u8 reserved[3];
+};
+
+struct cmd_ds_rf_reg_access {
+       u16 action;
+       u16 offset;
+       u8 value;
+       u8 reserved[3];
+};
+
+struct cmd_ds_802_11_radio_control {
+       u16 action;
+       u16 control;
+};
+
+struct cmd_ds_802_11_sleep_params {
+       /* ACT_GET/ACT_SET */
+       u16 action;
+
+       /* Sleep clock error in ppm */
+       u16 error;
+
+       /* Wakeup offset in usec */
+       u16 offset;
+
+       /* Clock stabilization time in usec */
+       u16 stabletime;
+
+       /* control periodic calibration */
+       u8 calcontrol;
+
+       /* control the use of external sleep clock */
+       u8 externalsleepclk;
+
+       /* reserved field, should be set to zero */
+       u16 reserved;
+};
+
+struct cmd_ds_802_11_inactivity_timeout {
+       /* ACT_GET/ACT_SET */
+       u16 action;
+
+       /* Inactivity timeout in msec */
+       u16 timeout;
+};
+
+struct cmd_ds_802_11_rf_channel {
+       u16 action;
+       u16 currentchannel;
+       u16 rftype;
+       u16 reserved;
+       u8 channellist[32];
+};
+
+struct cmd_ds_802_11_rssi {
+       /* weighting factor */
+       u16 N;
+
+       u16 reserved_0;
+       u16 reserved_1;
+       u16 reserved_2;
+};
+
+struct cmd_ds_802_11_rssi_rsp {
+       u16 SNR;
+       u16 noisefloor;
+       u16 avgSNR;
+       u16 avgnoisefloor;
+};
+
+struct cmd_ds_802_11_mac_address {
+       u16 action;
+       u8 macadd[ETH_ALEN];
+};
+
+struct cmd_ds_802_11_rf_tx_power {
+       u16 action;
+       u16 currentlevel;
+};
+
+struct cmd_ds_802_11_rf_antenna {
+       u16 action;
+
+       /* Number of antennas or 0xffff(diversity) */
+       u16 antennamode;
+
+};
+
+struct cmd_ds_802_11_ps_mode {
+       u16 action;
+       u16 nullpktinterval;
+       u16 multipledtim;
+       u16 reserved;
+       u16 locallisteninterval;
+};
+
+struct PS_CMD_ConfirmSleep {
+       u16 command;
+       u16 size;
+       u16 seqnum;
+       u16 result;
+
+       u16 action;
+       u16 reserved1;
+       u16 multipledtim;
+       u16 reserved;
+       u16 locallisteninterval;
+};
+
+struct cmd_ds_802_11_data_rate {
+       u16 action;
+       u16 reserverd;
+       u8 datarate[G_SUPPORTED_RATES];
+};
+
+struct cmd_ds_802_11_rate_adapt_rateset {
+       u16 action;
+       u16 enablehwauto;
+       u16 bitmap;
+};
+
+struct cmd_ds_802_11_ad_hoc_start {
+       u8 SSID[IW_ESSID_MAX_SIZE];
+       u8 bsstype;
+       u16 beaconperiod;
+       u8 dtimperiod;
+       union IEEEtypes_ssparamset ssparamset;
+       union ieeetypes_phyparamset phyparamset;
+       u16 probedelay;
+       struct ieeetypes_capinfo cap;
+       u8 datarate[G_SUPPORTED_RATES];
+       u8 tlv_memory_size_pad[100];
+} __attribute__ ((packed));
+
+struct adhoc_bssdesc {
+       u8 BSSID[6];
+       u8 SSID[32];
+       u8 bsstype;
+       u16 beaconperiod;
+       u8 dtimperiod;
+       u8 timestamp[8];
+       u8 localtime[8];
+       union ieeetypes_phyparamset phyparamset;
+       union IEEEtypes_ssparamset ssparamset;
+       struct ieeetypes_capinfo cap;
+       u8 datarates[G_SUPPORTED_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 bssdescriptor;
+       u16 failtimeout;
+       u16 probedelay;
+
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_enable_rsn {
+       u16 action;
+       u16 enable;
+};
+
+struct MrvlIEtype_keyParamSet {
+       /* type ID */
+       u16 type;
+
+       /* length of Payload */
+       u16 length;
+
+       /* type of key: WEP=0, TKIP=1, AES=2 */
+       u16 keytypeid;
+
+       /* key control Info specific to a keytypeid */
+       u16 keyinfo;
+
+       /* length of key */
+       u16 keylen;
+
+       /* key material of size keylen */
+       u8 key[32];
+};
+
+struct cmd_ds_802_11_key_material {
+       u16 action;
+       struct MrvlIEtype_keyParamSet keyParamSet[2];
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_eeprom_access {
+       u16 action;
+
+       /* multiple 4 */
+       u16 offset;
+       u16 bytecount;
+       u8 value;
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_tpc_cfg {
+       u16 action;
+       u8 enable;
+       s8 P0;
+       s8 P1;
+       s8 P2;
+       u8 usesnr;
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_led_ctrl {
+       u16 action;
+       u16 numled;
+       u8 data[256];
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_pwr_cfg {
+       u16 action;
+       u8 enable;
+       s8 PA_P0;
+       s8 PA_P1;
+       s8 PA_P2;
+} __attribute__ ((packed));
+
+struct cmd_ds_802_11_afc {
+       u16 afc_auto;
+       union {
+               struct {
+                       u16 threshold;
+                       u16 period;
+               };
+               struct {
+                       s16 timing_offset;
+                       s16 carrier_offset;
+               };
+       };
+} __attribute__ ((packed));
+
+struct cmd_tx_rate_query {
+       u16 txrate;
+} __attribute__ ((packed));
+
+struct cmd_ds_get_tsf {
+       __le64 tsfvalue;
+} __attribute__ ((packed));
+
+struct cmd_ds_bt_access {
+       u16 action;
+       u32 id;
+       u8 addr1[ETH_ALEN];
+       u8 addr2[ETH_ALEN];
+} __attribute__ ((packed));
+
+struct cmd_ds_fwt_access {
+       u16 action;
+       u32 id;
+       u8 da[ETH_ALEN];
+       u8 dir;
+       u8 ra[ETH_ALEN];
+       u32 ssn;
+       u32 dsn;
+       u32 metric;
+       u8 hopcount;
+       u8 ttl;
+       u32 expiration;
+       u8 sleepmode;
+       u32 snr;
+       u32 references;
+} __attribute__ ((packed));
+
+#define MESH_STATS_NUM 7
+struct cmd_ds_mesh_access {
+       u16 action;
+       u32 data[MESH_STATS_NUM + 1];   /* last position reserved */
+} __attribute__ ((packed));
+
+struct cmd_ds_command {
+       /* command header */
+       u16 command;
+       u16 size;
+       u16 seqnum;
+       u16 result;
+
+       /* command Body */
+       union {
+               struct cmd_ds_get_hw_spec hwspec;
+               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_data_rate drate;
+               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_mesh_access mesh;
+               struct cmd_ds_get_tsf gettsf;
+               struct cmd_ds_802_11_subscribe_event subscribe_event;
+       } params;
+} __attribute__ ((packed));
+
+#endif
diff --git a/drivers/net/wireless/libertas/if_bootcmd.c b/drivers/net/wireless/libertas/if_bootcmd.c
new file mode 100644 (file)
index 0000000..567000c
--- /dev/null
@@ -0,0 +1,38 @@
+/**
+  * This file contains functions used in USB Boot command
+  * and Boot2/FW update
+  */
+
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/netdevice.h>
+#include <linux/usb.h>
+
+#include "defs.h"
+#include "dev.h"
+#include "if_usb.h"
+
+/**
+ *  @brief This function issues Boot command to the Boot2 code
+ *  @param ivalue   1:Boot from FW by USB-Download
+ *                  2:Boot from FW in EEPROM
+ *  @return            0
+ */
+int if_usb_issue_boot_command(wlan_private *priv, int ivalue)
+{
+       struct usb_card_rec     *cardp = priv->wlan_dev.card;
+       struct bootcmdstr       sbootcmd;
+       int i;
+
+       /* Prepare command */
+       sbootcmd.u32magicnumber = BOOT_CMD_MAGIC_NUMBER;
+       sbootcmd.u8cmd_tag = ivalue;
+       for (i=0; i<11; i++)
+               sbootcmd.au8dumy[i]=0x00;
+       memcpy(cardp->bulk_out_buffer, &sbootcmd, sizeof(struct bootcmdstr));
+
+       /* Issue command */
+       usb_tx_block(priv, cardp->bulk_out_buffer, sizeof(struct bootcmdstr));
+
+       return 0;
+}
diff --git a/drivers/net/wireless/libertas/if_usb.c b/drivers/net/wireless/libertas/if_usb.c
new file mode 100644 (file)
index 0000000..695fb6a
--- /dev/null
@@ -0,0 +1,952 @@
+/**
+  * This file contains functions used in USB interface module.
+  */
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/netdevice.h>
+#include <linux/usb.h>
+
+#include "host.h"
+#include "sbi.h"
+#include "decl.h"
+#include "defs.h"
+#include "dev.h"
+#include "if_usb.h"
+
+#define MESSAGE_HEADER_LEN     4
+
+static const char usbdriver_name[] = "usb8xxx";
+
+static struct usb_device_id if_usb_table[] = {
+       /* Enter the device signature inside */
+       {
+               USB_DEVICE(USB8388_VID_1, USB8388_PID_1),
+       },
+       {
+               USB_DEVICE(USB8388_VID_2, USB8388_PID_2),
+       },
+       {}      /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, if_usb_table);
+
+static void if_usb_receive(struct urb *urb);
+static void if_usb_receive_fwload(struct urb *urb);
+
+/**
+ *  @brief  call back function to handle the status of the URB
+ *  @param urb                 pointer to urb structure
+ *  @return            N/A
+ */
+static void if_usb_write_bulk_callback(struct urb *urb)
+{
+       wlan_private *priv = (wlan_private *) (urb->context);
+       wlan_adapter *adapter = priv->adapter;
+       struct net_device *dev = priv->wlan_dev.netdev;
+
+       /* handle the transmission complete validations */
+
+       if (urb->status != 0) {
+               /* print the failure status number for debug */
+               lbs_pr_info("URB in failure status\n");
+       } else {
+               lbs_dev_dbg(2, &urb->dev->dev, "URB status is successfull\n");
+               lbs_dev_dbg(2, &urb->dev->dev, "Actual length transmitted %d\n",
+                      urb->actual_length);
+               priv->wlan_dev.dnld_sent = DNLD_RES_RECEIVED;
+               /* Wake main thread if commands are pending */
+               if (!adapter->cur_cmd)
+                       wake_up_interruptible(&priv->mainthread.waitq);
+               if ((adapter->connect_status == libertas_connected))
+                       netif_wake_queue(dev);
+       }
+
+       return;
+}
+
+/**
+ *  @brief  free tx/rx urb, skb and rx buffer
+ *  @param cardp       pointer usb_card_rec
+ *  @return            N/A
+ */
+void if_usb_free(struct usb_card_rec *cardp)
+{
+       ENTER();
+
+       /* Unlink tx & rx urb */
+       usb_kill_urb(cardp->tx_urb);
+       usb_kill_urb(cardp->rx_urb);
+
+       usb_free_urb(cardp->tx_urb);
+       cardp->tx_urb = NULL;
+
+       usb_free_urb(cardp->rx_urb);
+       cardp->rx_urb = NULL;
+
+       kfree(cardp->bulk_out_buffer);
+       cardp->bulk_out_buffer = NULL;
+
+       LEAVE();
+       return;
+}
+
+/**
+ *  @brief sets the configuration values
+ *  @param ifnum       interface number
+ *  @param id          pointer to usb_device_id
+ *  @return            0 on success, error code on failure
+ */
+static int if_usb_probe(struct usb_interface *intf,
+                       const struct usb_device_id *id)
+{
+       struct usb_device *udev;
+       struct usb_host_interface *iface_desc;
+       struct usb_endpoint_descriptor *endpoint;
+       wlan_private *pwlanpriv;
+       struct usb_card_rec *usb_cardp;
+       int i;
+
+       udev = interface_to_usbdev(intf);
+
+       usb_cardp = kzalloc(sizeof(struct usb_card_rec), GFP_KERNEL);
+       if (!usb_cardp) {
+               lbs_pr_err("Out of memory allocating private data.\n");
+               goto error;
+       }
+
+       usb_cardp->udev = udev;
+       iface_desc = intf->cur_altsetting;
+
+       lbs_dev_dbg(1, &udev->dev, "bcdUSB = 0x%X bDeviceClass = 0x%X"
+              " bDeviceSubClass = 0x%X, bDeviceProtocol = 0x%X\n",
+              udev->descriptor.bcdUSB,
+              udev->descriptor.bDeviceClass,
+              udev->descriptor.bDeviceSubClass,
+              udev->descriptor.bDeviceProtocol);
+
+       for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+               endpoint = &iface_desc->endpoint[i].desc;
+               if ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+                   && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+                       USB_ENDPOINT_XFER_BULK)) {
+                       /* we found a bulk in endpoint */
+                       lbs_dev_dbg(1, &udev->dev, "Bulk in size is %d\n",
+                              endpoint->wMaxPacketSize);
+                       if (!
+                           (usb_cardp->rx_urb =
+                            usb_alloc_urb(0, GFP_KERNEL))) {
+                               lbs_dev_dbg(1, &udev->dev,
+                                      "Rx URB allocation failed\n");
+                               goto dealloc;
+                       }
+                       usb_cardp->rx_urb_recall = 0;
+
+                       usb_cardp->bulk_in_size =
+                           endpoint->wMaxPacketSize;
+                       usb_cardp->bulk_in_endpointAddr =
+                           (endpoint->
+                            bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
+                       lbs_dev_dbg(1, &udev->dev, "in_endpoint = %d\n",
+                              endpoint->bEndpointAddress);
+               }
+
+               if (((endpoint->
+                     bEndpointAddress & USB_ENDPOINT_DIR_MASK) ==
+                    USB_DIR_OUT)
+                   && ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+                       USB_ENDPOINT_XFER_BULK)) {
+                       /* We found bulk out endpoint */
+                       if (!
+                           (usb_cardp->tx_urb =
+                            usb_alloc_urb(0, GFP_KERNEL))) {
+                               lbs_dev_dbg(1,&udev->dev,
+                                      "Tx URB allocation failed\n");
+                               goto dealloc;
+                       }
+
+                       usb_cardp->bulk_out_size =
+                           endpoint->wMaxPacketSize;
+                       lbs_dev_dbg(1, &udev->dev,
+                                   "Bulk out size is %d\n",
+                                   endpoint->wMaxPacketSize);
+                       usb_cardp->bulk_out_endpointAddr =
+                           endpoint->bEndpointAddress;
+                       lbs_dev_dbg(1, &udev->dev, "out_endpoint = %d\n",
+                                   endpoint->bEndpointAddress);
+                       usb_cardp->bulk_out_buffer =
+                           kmalloc(MRVDRV_ETH_TX_PACKET_BUFFER_SIZE,
+                                   GFP_KERNEL);
+
+                       if (!usb_cardp->bulk_out_buffer) {
+                               lbs_dev_dbg(1, &udev->dev,
+                                      "Could not allocate buffer\n");
+                               goto dealloc;
+                       }
+               }
+       }
+
+
+       /* At this point wlan_add_card() will be called.  Don't worry
+        * about keeping pwlanpriv around since it will be set on our
+        * usb device data in -> add() -> libertas_sbi_register_dev().
+        */
+       if (!(pwlanpriv = wlan_add_card(usb_cardp)))
+               goto dealloc;
+
+       usb_get_dev(udev);
+       usb_set_intfdata(intf, usb_cardp);
+
+       /*
+        * return card structure, which can be got back in the
+        * diconnect function as the ptr
+        * argument.
+        */
+       return 0;
+
+dealloc:
+       if_usb_free(usb_cardp);
+
+error:
+       return -ENOMEM;
+}
+
+/**
+ *  @brief free resource and cleanup
+ *  @param udev                pointer to usb_device
+ *  @param ptr         pointer to usb_cardp
+ *  @return            N/A
+ */
+static void if_usb_disconnect(struct usb_interface *intf)
+{
+       struct usb_card_rec *cardp = usb_get_intfdata(intf);
+       wlan_private *priv = (wlan_private *) cardp->priv;
+       wlan_adapter *adapter = NULL;
+
+       adapter = priv->adapter;
+
+       /*
+        * Update Surprise removed to TRUE
+        */
+       adapter->surpriseremoved = 1;
+
+       /* card is removed and we can call wlan_remove_card */
+       lbs_dev_dbg(1, &cardp->udev->dev, "call remove card\n");
+       wlan_remove_card(cardp);
+
+       /* Unlink and free urb */
+       if_usb_free(cardp);
+
+       usb_set_intfdata(intf, NULL);
+       usb_put_dev(interface_to_usbdev(intf));
+
+       return;
+}
+
+/**
+ *  @brief  This function download FW
+ *  @param priv                pointer to wlan_private
+ *  @return            0
+ */
+static int if_prog_firmware(wlan_private * priv)
+{
+       struct usb_card_rec *cardp = priv->wlan_dev.card;
+       struct FWData *fwdata;
+       struct fwheader *fwheader;
+       u8 *firmware = priv->firmware->data;
+
+       fwdata = kmalloc(sizeof(struct FWData), GFP_ATOMIC);
+
+       if (!fwdata)
+               return -1;
+
+       fwheader = &fwdata->fwheader;
+
+       if (!cardp->CRC_OK) {
+               cardp->totalbytes = cardp->fwlastblksent;
+               cardp->fwseqnum = cardp->lastseqnum - 1;
+       }
+
+       lbs_dev_dbg(2, &cardp->udev->dev, "totalbytes = %d\n",
+                   cardp->totalbytes);
+
+       memcpy(fwheader, &firmware[cardp->totalbytes],
+              sizeof(struct fwheader));
+
+       cardp->fwlastblksent = cardp->totalbytes;
+       cardp->totalbytes += sizeof(struct fwheader);
+
+       lbs_dev_dbg(2, &cardp->udev->dev,"Copy Data\n");
+       memcpy(fwdata->data, &firmware[cardp->totalbytes],
+              fwdata->fwheader.datalength);
+
+       lbs_dev_dbg(2, &cardp->udev->dev,
+                   "Data length = %d\n", fwdata->fwheader.datalength);
+
+       cardp->fwseqnum = cardp->fwseqnum + 1;
+
+       fwdata->seqnum = cardp->fwseqnum;
+       cardp->lastseqnum = fwdata->seqnum;
+       cardp->totalbytes += fwdata->fwheader.datalength;
+
+       if (fwheader->dnldcmd == FW_HAS_DATA_TO_RECV) {
+               lbs_dev_dbg(2, &cardp->udev->dev, "There is data to follow\n");
+               lbs_dev_dbg(2, &cardp->udev->dev,
+                           "seqnum = %d totalbytes = %d\n", cardp->fwseqnum,
+                           cardp->totalbytes);
+               memcpy(cardp->bulk_out_buffer, fwheader, FW_DATA_XMIT_SIZE);
+               usb_tx_block(priv, cardp->bulk_out_buffer, FW_DATA_XMIT_SIZE);
+
+       } else if (fwdata->fwheader.dnldcmd == FW_HAS_LAST_BLOCK) {
+               lbs_dev_dbg(2, &cardp->udev->dev,
+                           "Host has finished FW downloading\n");
+               lbs_dev_dbg(2, &cardp->udev->dev,
+                           "Donwloading FW JUMP BLOCK\n");
+               memcpy(cardp->bulk_out_buffer, fwheader, FW_DATA_XMIT_SIZE);
+               usb_tx_block(priv, cardp->bulk_out_buffer, FW_DATA_XMIT_SIZE);
+               cardp->fwfinalblk = 1;
+       }
+
+       lbs_dev_dbg(2, &cardp->udev->dev,
+                   "The firmware download is done size is %d\n",
+                   cardp->totalbytes);
+
+       kfree(fwdata);
+
+       return 0;
+}
+
+static int libertas_do_reset(wlan_private *priv)
+{
+       int ret;
+       struct usb_card_rec *cardp = priv->wlan_dev.card;
+
+       ret = usb_reset_device(cardp->udev);
+       if (!ret) {
+               msleep(10);
+               reset_device(priv);
+               msleep(10);
+       }
+       return ret;
+}
+
+/**
+ *  @brief This function transfer the data to the device.
+ *  @param priv        pointer to wlan_private
+ *  @param payload     pointer to payload data
+ *  @param nb          data length
+ *  @return            0 or -1
+ */
+int usb_tx_block(wlan_private * priv, u8 * payload, u16 nb)
+{
+       /* pointer to card structure */
+       struct usb_card_rec *cardp = priv->wlan_dev.card;
+       int ret = -1;
+
+       /* check if device is removed */
+       if (priv->adapter->surpriseremoved) {
+               lbs_dev_dbg(1, &cardp->udev->dev, "Device removed\n");
+               goto tx_ret;
+       }
+
+       usb_fill_bulk_urb(cardp->tx_urb, cardp->udev,
+                         usb_sndbulkpipe(cardp->udev,
+                                         cardp->bulk_out_endpointAddr),
+                         payload, nb, if_usb_write_bulk_callback, priv);
+
+       cardp->tx_urb->transfer_flags |= URB_ZERO_PACKET;
+
+       if ((ret = usb_submit_urb(cardp->tx_urb, GFP_ATOMIC))) {
+               /*  transfer failed */
+               lbs_dev_dbg(1, &cardp->udev->dev, "usb_submit_urb failed\n");
+               ret = -1;
+       } else {
+               lbs_dev_dbg(2, &cardp->udev->dev, "usb_submit_urb success\n");
+               ret = 0;
+       }
+
+tx_ret:
+       return ret;
+}
+
+static int __if_usb_submit_rx_urb(wlan_private * priv,
+                                 void (*callbackfn)
+                                 (struct urb *urb))
+{
+       struct usb_card_rec *cardp = priv->wlan_dev.card;
+       struct sk_buff *skb;
+       struct read_cb_info *rinfo = &cardp->rinfo;
+       int ret = -1;
+
+       if (!(skb = dev_alloc_skb(MRVDRV_ETH_RX_PACKET_BUFFER_SIZE))) {
+               lbs_pr_err("No free skb\n");
+               goto rx_ret;
+       }
+
+       rinfo->skb = skb;
+
+       /* Fill the receive configuration URB and initialise the Rx call back */
+       usb_fill_bulk_urb(cardp->rx_urb, cardp->udev,
+                         usb_rcvbulkpipe(cardp->udev,
+                                         cardp->bulk_in_endpointAddr),
+                         skb->tail + IPFIELD_ALIGN_OFFSET,
+                         MRVDRV_ETH_RX_PACKET_BUFFER_SIZE, callbackfn,
+                         rinfo);
+
+       cardp->rx_urb->transfer_flags |= URB_ZERO_PACKET;
+
+       lbs_dev_dbg(2, &cardp->udev->dev, "Pointer for rx_urb %p\n", cardp->rx_urb);
+       if ((ret = usb_submit_urb(cardp->rx_urb, GFP_ATOMIC))) {
+               /* handle failure conditions */
+               lbs_dev_dbg(1, &cardp->udev->dev, "Submit Rx URB failed\n");
+               ret = -1;
+       } else {
+               lbs_dev_dbg(2, &cardp->udev->dev, "Submit Rx URB success\n");
+               ret = 0;
+       }
+
+rx_ret:
+       return ret;
+}
+
+static inline int if_usb_submit_rx_urb_fwload(wlan_private * priv)
+{
+       return __if_usb_submit_rx_urb(priv, &if_usb_receive_fwload);
+}
+
+static inline int if_usb_submit_rx_urb(wlan_private * priv)
+{
+       return __if_usb_submit_rx_urb(priv, &if_usb_receive);
+}
+
+static void if_usb_receive_fwload(struct urb *urb)
+{
+       struct read_cb_info *rinfo = (struct read_cb_info *)urb->context;
+       wlan_private *priv = rinfo->priv;
+       struct sk_buff *skb = rinfo->skb;
+       struct usb_card_rec *cardp = (struct usb_card_rec *)priv->wlan_dev.card;
+       struct fwsyncheader *syncfwheader;
+       struct bootcmdrespStr bootcmdresp;
+
+       if (urb->status) {
+               lbs_dev_dbg(1, &cardp->udev->dev,
+                           "URB status is failed during fw load\n");
+               kfree_skb(skb);
+               return;
+       }
+
+       if (cardp->bootcmdresp == 0) {
+               memcpy (&bootcmdresp, skb->data + IPFIELD_ALIGN_OFFSET,
+                       sizeof(bootcmdresp));
+               if (cardp->udev->descriptor.bcdDevice < 0x3106) {
+                       kfree_skb(skb);
+                       if_usb_submit_rx_urb_fwload(priv);
+                       cardp->bootcmdresp = 1;
+                       lbs_dev_dbg(1, &cardp->udev->dev,
+                                   "Received valid boot command response\n");
+                       return;
+               }
+               if (bootcmdresp.u32magicnumber != BOOT_CMD_MAGIC_NUMBER) {
+                       lbs_pr_info(
+                               "boot cmd response wrong magic number (0x%x)\n",
+                               bootcmdresp.u32magicnumber);
+               } else if (bootcmdresp.u8cmd_tag != BOOT_CMD_FW_BY_USB) {
+                       lbs_pr_info(
+                               "boot cmd response cmd_tag error (%d)\n",
+                               bootcmdresp.u8cmd_tag);
+               } else if (bootcmdresp.u8result != BOOT_CMD_RESP_OK) {
+                       lbs_pr_info(
+                               "boot cmd response result error (%d)\n",
+                               bootcmdresp.u8result);
+               } else {
+                       cardp->bootcmdresp = 1;
+                       lbs_dev_dbg(1, &cardp->udev->dev,
+                                   "Received valid boot command response\n");
+               }
+               kfree_skb(skb);
+               if_usb_submit_rx_urb_fwload(priv);
+               return;
+       }
+
+       syncfwheader = kmalloc(sizeof(struct fwsyncheader), GFP_ATOMIC);
+       if (!syncfwheader) {
+               lbs_dev_dbg(1, &cardp->udev->dev, "Failure to allocate syncfwheader\n");
+               kfree_skb(skb);
+               return;
+       }
+
+       memcpy(syncfwheader, skb->data + IPFIELD_ALIGN_OFFSET,
+                       sizeof(struct fwsyncheader));
+
+       if (!syncfwheader->cmd) {
+               lbs_dev_dbg(2, &cardp->udev->dev,
+                           "FW received Blk with correct CRC\n");
+               lbs_dev_dbg(2, &cardp->udev->dev,
+                           "FW received Blk seqnum = %d\n",
+                      syncfwheader->seqnum);
+               cardp->CRC_OK = 1;
+       } else {
+               lbs_dev_dbg(1, &cardp->udev->dev,
+                           "FW received Blk with CRC error\n");
+               cardp->CRC_OK = 0;
+       }
+
+       kfree_skb(skb);
+
+       if (cardp->fwfinalblk) {
+               cardp->fwdnldover = 1;
+               goto exit;
+       }
+
+       if_prog_firmware(priv);
+
+       if_usb_submit_rx_urb_fwload(priv);
+exit:
+       kfree(syncfwheader);
+
+       return;
+
+}
+
+#define MRVDRV_MIN_PKT_LEN     30
+
+static inline void process_cmdtypedata(int recvlength, struct sk_buff *skb,
+                                      struct usb_card_rec *cardp,
+                                      wlan_private *priv)
+{
+       if (recvlength > MRVDRV_ETH_RX_PACKET_BUFFER_SIZE +
+           MESSAGE_HEADER_LEN || recvlength < MRVDRV_MIN_PKT_LEN) {
+               lbs_dev_dbg(1, &cardp->udev->dev,
+                           "Packet length is Invalid\n");
+               kfree_skb(skb);
+               return;
+       }
+
+       skb_reserve(skb, IPFIELD_ALIGN_OFFSET);
+       skb_put(skb, recvlength);
+       skb_pull(skb, MESSAGE_HEADER_LEN);
+       libertas_process_rxed_packet(priv, skb);
+       priv->wlan_dev.upld_len = (recvlength - MESSAGE_HEADER_LEN);
+}
+
+static inline void process_cmdrequest(int recvlength, u8 *recvbuff,
+                                     struct sk_buff *skb,
+                                     struct usb_card_rec *cardp,
+                                     wlan_private *priv)
+{
+       u8 *cmdbuf;
+       if (recvlength > MRVDRV_SIZE_OF_CMD_BUFFER) {
+               lbs_dev_dbg(1, &cardp->udev->dev,
+                           "The receive buffer is too large\n");
+               kfree_skb(skb);
+               return;
+       }
+
+       if (!in_interrupt())
+               BUG();
+
+       spin_lock(&priv->adapter->driver_lock);
+       /* take care of cur_cmd = NULL case by reading the
+        * data to clear the interrupt */
+       if (!priv->adapter->cur_cmd) {
+               cmdbuf = priv->wlan_dev.upld_buf;
+               priv->adapter->hisregcpy &= ~his_cmdupldrdy;
+       } else
+               cmdbuf = priv->adapter->cur_cmd->bufvirtualaddr;
+
+       cardp->usb_int_cause |= his_cmdupldrdy;
+       priv->wlan_dev.upld_len = (recvlength - MESSAGE_HEADER_LEN);
+       memcpy(cmdbuf, recvbuff + MESSAGE_HEADER_LEN,
+              priv->wlan_dev.upld_len);
+
+       kfree_skb(skb);
+       libertas_interrupt(priv->wlan_dev.netdev);
+       spin_unlock(&priv->adapter->driver_lock);
+
+       lbs_dev_dbg(1, &cardp->udev->dev,
+                   "Wake up main thread to handle cmd response\n");
+
+       return;
+}
+
+/**
+ *  @brief This function reads of the packet into the upload buff,
+ *  wake up the main thread and initialise the Rx callack.
+ *
+ *  @param urb         pointer to struct urb
+ *  @return            N/A
+ */
+static void if_usb_receive(struct urb *urb)
+{
+       struct read_cb_info *rinfo = (struct read_cb_info *)urb->context;
+       wlan_private *priv = rinfo->priv;
+       struct sk_buff *skb = rinfo->skb;
+       struct usb_card_rec *cardp = (struct usb_card_rec *)priv->wlan_dev.card;
+
+       int recvlength = urb->actual_length;
+       u8 *recvbuff = NULL;
+       u32 recvtype;
+
+       ENTER();
+
+       if (recvlength) {
+               if (urb->status) {
+                       lbs_dev_dbg(1, &cardp->udev->dev,
+                                   "URB status is failed\n");
+                       kfree_skb(skb);
+                       goto setup_for_next;
+               }
+
+               recvbuff = skb->data + IPFIELD_ALIGN_OFFSET;
+               memcpy(&recvtype, recvbuff, sizeof(u32));
+               lbs_dev_dbg(1, &cardp->udev->dev,
+                           "Recv length = 0x%x\n", recvlength);
+               lbs_dev_dbg(1, &cardp->udev->dev,
+                           "Receive type = 0x%X\n", recvtype);
+               recvtype = le32_to_cpu(recvtype);
+               lbs_dev_dbg(1, &cardp->udev->dev,
+                           "Receive type after = 0x%X\n", recvtype);
+       } else if (urb->status)
+               goto rx_exit;
+
+
+       switch (recvtype) {
+       case CMD_TYPE_DATA:
+               process_cmdtypedata(recvlength, skb, cardp, priv);
+               break;
+
+       case CMD_TYPE_REQUEST:
+               process_cmdrequest(recvlength, recvbuff, skb, cardp, priv);
+               break;
+
+       case CMD_TYPE_INDICATION:
+               /* Event cause handling */
+               spin_lock(&priv->adapter->driver_lock);
+               cardp->usb_event_cause = *(u32 *) (recvbuff + MESSAGE_HEADER_LEN);
+               lbs_dev_dbg(1, &cardp->udev->dev,"**EVENT** 0x%X\n",
+                           cardp->usb_event_cause);
+               if (cardp->usb_event_cause & 0xffff0000) {
+                       libertas_send_tx_feedback(priv);
+                       break;
+               }
+               cardp->usb_event_cause = le32_to_cpu(cardp->usb_event_cause) << 3;
+               cardp->usb_int_cause |= his_cardevent;
+               kfree_skb(skb);
+               libertas_interrupt(priv->wlan_dev.netdev);
+               spin_unlock(&priv->adapter->driver_lock);
+               goto rx_exit;
+       default:
+               kfree_skb(skb);
+               break;
+       }
+
+setup_for_next:
+       if_usb_submit_rx_urb(priv);
+rx_exit:
+       LEAVE();
+       return;
+}
+
+/**
+ *  @brief This function downloads data to FW
+ *  @param priv                pointer to wlan_private structure
+ *  @param type                type of data
+ *  @param buf         pointer to data buffer
+ *  @param len         number of bytes
+ *  @return            0 or -1
+ */
+int libertas_sbi_host_to_card(wlan_private * priv, u8 type, u8 * payload, u16 nb)
+{
+       int ret = -1;
+       u32 tmp;
+       struct usb_card_rec *cardp = (struct usb_card_rec *)priv->wlan_dev.card;
+
+       lbs_dev_dbg(1, &cardp->udev->dev,"*** type = %u\n", type);
+       lbs_dev_dbg(1, &cardp->udev->dev,"size after = %d\n", nb);
+
+       if (type == MVMS_CMD) {
+               tmp = cpu_to_le32(CMD_TYPE_REQUEST);
+               priv->wlan_dev.dnld_sent = DNLD_CMD_SENT;
+               memcpy(cardp->bulk_out_buffer, (u8 *) & tmp,
+                      MESSAGE_HEADER_LEN);
+
+       } else {
+               tmp = cpu_to_le32(CMD_TYPE_DATA);
+               priv->wlan_dev.dnld_sent = DNLD_DATA_SENT;
+               memcpy(cardp->bulk_out_buffer, (u8 *) & tmp,
+                      MESSAGE_HEADER_LEN);
+       }
+
+       memcpy((cardp->bulk_out_buffer + MESSAGE_HEADER_LEN), payload, nb);
+
+       ret =
+           usb_tx_block(priv, cardp->bulk_out_buffer, nb + MESSAGE_HEADER_LEN);
+
+       return ret;
+}
+
+/* called with adapter->driver_lock held */
+int libertas_sbi_get_int_status(wlan_private * priv, u8 * ireg)
+{
+       struct usb_card_rec *cardp = priv->wlan_dev.card;
+
+       *ireg = cardp->usb_int_cause;
+       cardp->usb_int_cause = 0;
+
+       lbs_dev_dbg(1, &cardp->udev->dev,"Int cause is 0x%X\n", *ireg);
+
+       return 0;
+}
+
+int libertas_sbi_read_event_cause(wlan_private * priv)
+{
+       struct usb_card_rec *cardp = priv->wlan_dev.card;
+       priv->adapter->eventcause = cardp->usb_event_cause;
+       /* Re-submit rx urb here to avoid event lost issue */
+       if_usb_submit_rx_urb(priv);
+       return 0;
+}
+
+int reset_device(wlan_private *priv)
+{
+       int ret;
+
+       ret = libertas_prepare_and_send_command(priv, cmd_802_11_reset,
+                                   cmd_act_halt, 0, 0, NULL);
+       msleep_interruptible(10);
+
+       return ret;
+}
+
+int libertas_sbi_unregister_dev(wlan_private * priv)
+{
+       int ret = 0;
+
+       /* Need to send a Reset command to device before USB resources freed
+        * and wlan_remove_card() called, then device can handle FW download
+        * again.
+        */
+       if (priv)
+               reset_device(priv);
+
+       return ret;
+}
+
+
+/**
+ *  @brief  This function register usb device and initialize parameter
+ *  @param             priv pointer to wlan_private
+ *  @return            0 or -1
+ */
+int libertas_sbi_register_dev(wlan_private * priv)
+{
+
+       struct usb_card_rec *cardp = (struct usb_card_rec *)priv->wlan_dev.card;
+       ENTER();
+
+       cardp->priv = priv;
+       cardp->eth_dev = priv->wlan_dev.netdev;
+       priv->hotplug_device = &(cardp->udev->dev);
+
+       SET_NETDEV_DEV(cardp->eth_dev, &(cardp->udev->dev));
+
+       lbs_dev_dbg(1, &cardp->udev->dev, "udev pointer is at %p\n",
+                   cardp->udev);
+
+       LEAVE();
+       return 0;
+}
+
+
+
+int libertas_sbi_prog_firmware(wlan_private * priv)
+{
+       struct usb_card_rec *cardp = priv->wlan_dev.card;
+       int i = 0;
+       static int reset_count = 10;
+
+       ENTER();
+
+       cardp->rinfo.priv = priv;
+
+restart:
+       if (if_usb_submit_rx_urb_fwload(priv) < 0) {
+               lbs_dev_dbg(1, &cardp->udev->dev, "URB submission is failed\n");
+               LEAVE();
+               return -1;
+       }
+
+#ifdef SUPPORT_BOOT_COMMAND
+       cardp->bootcmdresp = 0;
+       do {
+               int j = 0;
+               i++;
+               /* Issue Boot command = 1, Boot from Download-FW */
+               if_usb_issue_boot_command(priv, BOOT_CMD_FW_BY_USB);
+               /* wait for command response */
+               do {
+                       j++;
+                       msleep_interruptible(100);
+               } while (cardp->bootcmdresp == 0 && j < 10);
+       } while (cardp->bootcmdresp == 0 && i < 5);
+
+       if (cardp->bootcmdresp == 0) {
+               if (--reset_count >= 0) {
+                       libertas_do_reset(priv);
+                       goto restart;
+               }
+               return -1;
+       }
+#endif
+
+       i = 0;
+       priv->adapter->fw_ready = 0;
+
+       cardp->totalbytes = 0;
+       cardp->fwlastblksent = 0;
+       cardp->CRC_OK = 1;
+       cardp->fwdnldover = 0;
+       cardp->fwseqnum = -1;
+       cardp->totalbytes = 0;
+       cardp->fwfinalblk = 0;
+
+       if_prog_firmware(priv);
+
+       do {
+               lbs_dev_dbg(1, &cardp->udev->dev,"Wlan sched timeout\n");
+               i++;
+               msleep_interruptible(100);
+               if (priv->adapter->surpriseremoved || i >= 20)
+                       break;
+       } while (!cardp->fwdnldover);
+
+       if (!cardp->fwdnldover) {
+               lbs_pr_info("failed to load fw, resetting device!\n");
+               if (--reset_count >= 0) {
+                       libertas_do_reset(priv);
+                       goto restart;
+               }
+
+               lbs_pr_info("FW download failure, time = %d ms\n", i * 100);
+               LEAVE();
+               return -1;
+       }
+
+       if_usb_submit_rx_urb(priv);
+
+       /* Delay 200 ms to waiting for the FW ready */
+       msleep_interruptible(200);
+
+       priv->adapter->fw_ready = 1;
+
+       LEAVE();
+       return 0;
+}
+
+/**
+ *  @brief Given a usb_card_rec return its wlan_private
+ *  @param card                pointer to a usb_card_rec
+ *  @return            pointer to wlan_private
+ */
+wlan_private *libertas_sbi_get_priv(void *card)
+{
+       struct usb_card_rec *cardp = card;
+       return cardp->priv;
+}
+
+#ifdef ENABLE_PM
+int libertas_sbi_suspend(wlan_private * priv)
+{
+       return 0;
+}
+
+int libertas_sbi_resume(wlan_private * priv)
+{
+       return 0;
+}
+#endif
+
+#ifdef CONFIG_PM
+static int if_usb_suspend(struct usb_interface *intf, pm_message_t message)
+{
+       struct usb_card_rec *cardp = usb_get_intfdata(intf);
+       wlan_private *priv = cardp->priv;
+
+       ENTER();
+
+       if (priv->adapter->psstate != PS_STATE_FULL_POWER)
+               return -1;
+
+       netif_device_detach(cardp->eth_dev);
+
+       /* Unlink tx & rx urb */
+       usb_kill_urb(cardp->tx_urb);
+       usb_kill_urb(cardp->rx_urb);
+
+       cardp->rx_urb_recall = 1;
+
+       LEAVE();
+       return 0;
+}
+
+static int if_usb_resume(struct usb_interface *intf)
+{
+       struct usb_card_rec *cardp = usb_get_intfdata(intf);
+
+       ENTER();
+
+       cardp->rx_urb_recall = 0;
+
+       if_usb_submit_rx_urb(cardp->priv);
+
+       netif_device_attach(cardp->eth_dev);
+
+       LEAVE();
+       return 0;
+}
+#else
+#define if_usb_suspend NULL
+#define if_usb_resume NULL
+#endif
+
+static struct usb_driver if_usb_driver = {
+       /* driver name */
+       .name = usbdriver_name,
+       /* probe function name */
+       .probe = if_usb_probe,
+       /* disconnect function  name */
+       .disconnect = if_usb_disconnect,
+       /* device signature table */
+       .id_table = if_usb_table,
+       .suspend = if_usb_suspend,
+       .resume = if_usb_resume,
+};
+
+/**
+ *  @brief This function registers driver.
+ *  @param add         pointer to add_card callback function
+ *  @param remove      pointer to remove card callback function
+ *  @param arg         pointer to call back function parameter
+ *  @return            dummy success variable
+ */
+int libertas_sbi_register(void)
+{
+       /*
+        * API registers the Marvell USB driver
+        * to the USB system
+        */
+       usb_register(&if_usb_driver);
+
+       /* Return success to wlan layer */
+       return 0;
+}
+
+/**
+ *  @brief This function removes usb driver.
+ *  @return            N/A
+ */
+void libertas_sbi_unregister(void)
+{
+       /* API unregisters the driver from USB subsystem */
+       usb_deregister(&if_usb_driver);
+       return;
+}
diff --git a/drivers/net/wireless/libertas/if_usb.h b/drivers/net/wireless/libertas/if_usb.h
new file mode 100644 (file)
index 0000000..7851167
--- /dev/null
@@ -0,0 +1,109 @@
+/**
+  * This file contains definition for USB interface.
+  */
+#define CMD_TYPE_REQUEST                0xF00DFACE
+#define CMD_TYPE_DATA                   0xBEADC0DE
+#define CMD_TYPE_INDICATION             0xBEEFFACE
+
+#define IPFIELD_ALIGN_OFFSET   2
+
+#define USB8388_VID_1  0x1286
+#define USB8388_PID_1  0x2001
+#define USB8388_VID_2  0x05a3
+#define USB8388_PID_2  0x8388
+
+#ifdef SUPPORT_BOOT_COMMAND
+#define BOOT_CMD_FW_BY_USB     0x01
+#define BOOT_CMD_FW_IN_EEPROM  0x02
+#define BOOT_CMD_UPDATE_BOOT2  0x03
+#define BOOT_CMD_UPDATE_FW     0x04
+#define BOOT_CMD_MAGIC_NUMBER  0x4C56524D   /* M=>0x4D,R=>0x52,V=>0x56,L=>0x4C */
+
+struct bootcmdstr
+{
+       u32 u32magicnumber;
+       u8  u8cmd_tag;
+       u8  au8dumy[11];
+};
+
+#define BOOT_CMD_RESP_OK     0x0001
+#define BOOT_CMD_RESP_FAIL   0x0000
+
+struct bootcmdrespStr
+{
+       u32 u32magicnumber;
+       u8  u8cmd_tag;
+       u8  u8result;
+       u8  au8dumy[2];
+};
+#endif /* SUPPORT_BOOT_COMMAND */
+
+/* read callback private data */
+struct read_cb_info {
+        wlan_private *priv;
+        struct sk_buff *skb;
+};
+
+/** USB card description structure*/
+struct usb_card_rec {
+       struct net_device *eth_dev;
+       struct usb_device *udev;
+       struct urb *rx_urb, *tx_urb;
+       void *priv;
+       struct read_cb_info rinfo;
+
+       int bulk_in_size;
+       u8 bulk_in_endpointAddr;
+
+       u8 *bulk_out_buffer;
+       int bulk_out_size;
+       u8 bulk_out_endpointAddr;
+
+       u8 CRC_OK;
+       u32 fwseqnum;
+       u32 lastseqnum;
+       u32 totalbytes;
+       u32 fwlastblksent;
+       u8 fwdnldover;
+       u8 fwfinalblk;
+
+       u32 usb_event_cause;
+       u8 usb_int_cause;
+
+       u8 rx_urb_recall;
+
+       u8 bootcmdresp;
+};
+
+/** fwheader */
+struct fwheader {
+       u32 dnldcmd;
+       u32 baseaddr;
+       u32 datalength;
+       u32 CRC;
+};
+
+#define FW_MAX_DATA_BLK_SIZE   600
+/** FWData */
+struct FWData {
+       struct fwheader fwheader;
+       u32 seqnum;
+       u8 data[FW_MAX_DATA_BLK_SIZE];
+};
+
+/** fwsyncheader */
+struct fwsyncheader {
+       u32 cmd;
+       u32 seqnum;
+};
+
+#define FW_HAS_DATA_TO_RECV            0x00000001
+#define FW_HAS_LAST_BLOCK              0x00000004
+
+#define FW_DATA_XMIT_SIZE \
+       sizeof(struct fwheader) + fwdata->fwheader.datalength + sizeof(u32)
+
+int usb_tx_block(wlan_private *priv, u8 *payload, u16 nb);
+void if_usb_free(struct usb_card_rec *cardp);
+int if_usb_issue_boot_command(wlan_private *priv, int ivalue);
+
diff --git a/drivers/net/wireless/libertas/ioctl.c b/drivers/net/wireless/libertas/ioctl.c
new file mode 100644 (file)
index 0000000..82b3964
--- /dev/null
@@ -0,0 +1,2500 @@
+/**
+  * This file contains ioctl functions
+  */
+
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/if.h>
+#include <linux/if_arp.h>
+#include <linux/wireless.h>
+
+#include <net/iw_handler.h>
+#include <net/ieee80211.h>
+
+#include "host.h"
+#include "radiotap.h"
+#include "decl.h"
+#include "defs.h"
+#include "dev.h"
+#include "join.h"
+#include "wext.h"
+
+#define MAX_SCAN_CELL_SIZE      (IW_EV_ADDR_LEN + \
+                               IW_ESSID_MAX_SIZE + \
+                               IW_EV_UINT_LEN + IW_EV_FREQ_LEN + \
+                               IW_EV_QUAL_LEN + IW_ESSID_MAX_SIZE + \
+                               IW_EV_PARAM_LEN + 40)   /* 40 for WPAIE */
+
+#define WAIT_FOR_SCAN_RRESULT_MAX_TIME (10 * HZ)
+
+static int setrxantenna(wlan_private * priv, int mode)
+{
+       int ret = 0;
+       wlan_adapter *adapter = priv->adapter;
+
+       if (mode != RF_ANTENNA_1 && mode != RF_ANTENNA_2
+           && mode != RF_ANTENNA_AUTO) {
+               return -EINVAL;
+       }
+
+       adapter->rxantennamode = mode;
+
+       lbs_pr_debug(1, "SET RX Antenna mode to 0x%04x\n", adapter->rxantennamode);
+
+       ret = libertas_prepare_and_send_command(priv, cmd_802_11_rf_antenna,
+                                   cmd_act_set_rx,
+                                   cmd_option_waitforrsp, 0,
+                                   &adapter->rxantennamode);
+       return ret;
+}
+
+static int settxantenna(wlan_private * priv, int mode)
+{
+       int ret = 0;
+       wlan_adapter *adapter = priv->adapter;
+
+       if ((mode != RF_ANTENNA_1) && (mode != RF_ANTENNA_2)
+           && (mode != RF_ANTENNA_AUTO)) {
+               return -EINVAL;
+       }
+
+       adapter->txantennamode = mode;
+
+       lbs_pr_debug(1, "SET TX Antenna mode to 0x%04x\n", adapter->txantennamode);
+
+       ret = libertas_prepare_and_send_command(priv, cmd_802_11_rf_antenna,
+                                   cmd_act_set_tx,
+                                   cmd_option_waitforrsp, 0,
+                                   &adapter->txantennamode);
+
+       return ret;
+}
+
+static int getrxantenna(wlan_private * priv, char *buf)
+{
+       int ret = 0;
+       wlan_adapter *adapter = priv->adapter;
+
+       // clear it, so we will know if the value
+       // returned below is correct or not.
+       adapter->rxantennamode = 0;
+
+       ret = libertas_prepare_and_send_command(priv, cmd_802_11_rf_antenna,
+                                   cmd_act_get_rx,
+                                   cmd_option_waitforrsp, 0, NULL);
+
+       if (ret) {
+               LEAVE();
+               return ret;
+       }
+
+       lbs_pr_debug(1, "Get Rx Antenna mode:0x%04x\n", adapter->rxantennamode);
+
+       return sprintf(buf, "0x%04x", adapter->rxantennamode) + 1;
+}
+
+static int gettxantenna(wlan_private * priv, char *buf)
+{
+       int ret = 0;
+       wlan_adapter *adapter = priv->adapter;
+
+       // clear it, so we will know if the value
+       // returned below is correct or not.
+       adapter->txantennamode = 0;
+
+       ret = libertas_prepare_and_send_command(priv, cmd_802_11_rf_antenna,
+                                   cmd_act_get_tx,
+                                   cmd_option_waitforrsp, 0, NULL);
+
+       if (ret) {
+               LEAVE();
+               return ret;
+       }
+
+       lbs_pr_debug(1, "Get Tx Antenna mode:0x%04x\n", adapter->txantennamode);
+
+       return sprintf(buf, "0x%04x", adapter->txantennamode) + 1;
+}
+
+static int wlan_set_region(wlan_private * priv, u16 region_code)
+{
+       int i;
+
+       for (i = 0; i < MRVDRV_MAX_REGION_CODE; i++) {
+               // use the region code to search for the index
+               if (region_code == libertas_region_code_to_index[i]) {
+                       priv->adapter->regiontableindex = (u16) i;
+                       priv->adapter->regioncode = region_code;
+                       break;
+               }
+       }
+
+       // if it's unidentified region code
+       if (i >= MRVDRV_MAX_REGION_CODE) {
+               lbs_pr_debug(1, "region Code not identified\n");
+               LEAVE();
+               return -1;
+       }
+
+       if (libertas_set_regiontable(priv, priv->adapter->regioncode, 0)) {
+               LEAVE();
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/**
+ *  @brief Get/Set Firmware wakeup method
+ *
+ *  @param priv                A pointer to wlan_private structure
+ *  @param wrq         A pointer to user data
+ *  @return            0--success, otherwise fail
+ */
+static int wlan_txcontrol(wlan_private * priv, struct iwreq *wrq)
+{
+       wlan_adapter *adapter = priv->adapter;
+       int data;
+       ENTER();
+
+       if ((int)wrq->u.data.length == 0) {
+               if (copy_to_user
+                   (wrq->u.data.pointer, &adapter->pkttxctrl, sizeof(u32))) {
+                       lbs_pr_alert("copy_to_user failed!\n");
+                       return -EFAULT;
+               }
+       } else {
+               if ((int)wrq->u.data.length > 1) {
+                       lbs_pr_alert("ioctl too many args!\n");
+                       return -EFAULT;
+               }
+               if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) {
+                       lbs_pr_alert("Copy from user failed\n");
+                       return -EFAULT;
+               }
+
+               adapter->pkttxctrl = (u32) data;
+       }
+
+       wrq->u.data.length = 1;
+
+       LEAVE();
+       return 0;
+}
+
+/**
+ *  @brief Get/Set NULL Package generation interval
+ *
+ *  @param priv                A pointer to wlan_private structure
+ *  @param wrq         A pointer to user data
+ *  @return            0--success, otherwise fail
+ */
+static int wlan_null_pkt_interval(wlan_private * priv, struct iwreq *wrq)
+{
+       wlan_adapter *adapter = priv->adapter;
+       int data;
+       ENTER();
+
+       if ((int)wrq->u.data.length == 0) {
+               data = adapter->nullpktinterval;
+
+               if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) {
+                       lbs_pr_alert( "copy_to_user failed!\n");
+                       return -EFAULT;
+               }
+       } else {
+               if ((int)wrq->u.data.length > 1) {
+                       lbs_pr_alert( "ioctl too many args!\n");
+                       return -EFAULT;
+               }
+               if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) {
+                       lbs_pr_debug(1, "Copy from user failed\n");
+                       return -EFAULT;
+               }
+
+               adapter->nullpktinterval = data;
+       }
+
+       wrq->u.data.length = 1;
+
+       LEAVE();
+       return 0;
+}
+
+static int wlan_get_rxinfo(wlan_private * priv, struct iwreq *wrq)
+{
+       wlan_adapter *adapter = priv->adapter;
+       int data[2];
+       ENTER();
+       data[0] = adapter->SNR[TYPE_RXPD][TYPE_NOAVG];
+       data[1] = adapter->rxpd_rate;
+       if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) * 2)) {
+               lbs_pr_debug(1, "Copy to user failed\n");
+               return -EFAULT;
+       }
+       wrq->u.data.length = 2;
+       LEAVE();
+       return 0;
+}
+
+static int wlan_get_snr(wlan_private * priv, struct iwreq *wrq)
+{
+       int ret = 0;
+       wlan_adapter *adapter = priv->adapter;
+       int data[4];
+
+       ENTER();
+       memset(data, 0, sizeof(data));
+       if (wrq->u.data.length) {
+               if (copy_from_user(data, wrq->u.data.pointer,
+                    min_t(size_t, wrq->u.data.length, 4) * sizeof(int)))
+                       return -EFAULT;
+       }
+       if ((wrq->u.data.length == 0) || (data[0] == 0) || (data[0] == 1)) {
+               if (adapter->connect_status == libertas_connected) {
+                       ret = libertas_prepare_and_send_command(priv,
+                                                   cmd_802_11_rssi,
+                                                   0,
+                                                   cmd_option_waitforrsp,
+                                                   0, NULL);
+
+                       if (ret) {
+                               LEAVE();
+                               return ret;
+                       }
+               }
+       }
+
+       if (wrq->u.data.length == 0) {
+               data[0] = adapter->SNR[TYPE_BEACON][TYPE_NOAVG];
+               data[1] = adapter->SNR[TYPE_BEACON][TYPE_AVG];
+               data[2] = adapter->SNR[TYPE_RXPD][TYPE_NOAVG];
+               data[3] = adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE;
+               if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) * 4))
+                       return -EFAULT;
+               wrq->u.data.length = 4;
+       } else if (data[0] == 0) {
+               data[0] = adapter->SNR[TYPE_BEACON][TYPE_NOAVG];
+               if (copy_to_user(wrq->u.data.pointer, data, sizeof(int)))
+                       return -EFAULT;
+               wrq->u.data.length = 1;
+       } else if (data[0] == 1) {
+               data[0] = adapter->SNR[TYPE_BEACON][TYPE_AVG];
+               if (copy_to_user(wrq->u.data.pointer, data, sizeof(int)))
+                       return -EFAULT;
+               wrq->u.data.length = 1;
+       } else if (data[0] == 2) {
+               data[0] = adapter->SNR[TYPE_RXPD][TYPE_NOAVG];
+               if (copy_to_user(wrq->u.data.pointer, data, sizeof(int)))
+                       return -EFAULT;
+               wrq->u.data.length = 1;
+       } else if (data[0] == 3) {
+               data[0] = adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE;
+               if (copy_to_user(wrq->u.data.pointer, data, sizeof(int)))
+                       return -EFAULT;
+               wrq->u.data.length = 1;
+       } else
+               return -ENOTSUPP;
+
+       LEAVE();
+       return 0;
+}
+
+static int wlan_beacon_interval(wlan_private * priv, struct iwreq *wrq)
+{
+       int data;
+       wlan_adapter *adapter = priv->adapter;
+
+       if (wrq->u.data.length > 0) {
+               if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int)))
+                       return -EFAULT;
+
+               lbs_pr_debug(1, "WLAN SET BEACON INTERVAL: %d\n", data);
+               if ((data > MRVDRV_MAX_BEACON_INTERVAL)
+                   || (data < MRVDRV_MIN_BEACON_INTERVAL))
+                       return -ENOTSUPP;
+               adapter->beaconperiod = data;
+       }
+       data = adapter->beaconperiod;
+       if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int)))
+               return -EFAULT;
+
+       wrq->u.data.length = 1;
+
+       return 0;
+}
+
+static int wlan_get_rssi(wlan_private * priv, struct iwreq *wrq)
+{
+       int ret = 0;
+       wlan_adapter *adapter = priv->adapter;
+       int temp;
+       int data = 0;
+       int *val;
+
+       ENTER();
+       data = SUBCMD_DATA(wrq);
+       if ((data == 0) || (data == 1)) {
+               ret = libertas_prepare_and_send_command(priv,
+                                           cmd_802_11_rssi,
+                                           0, cmd_option_waitforrsp,
+                                           0, NULL);
+               if (ret) {
+                       LEAVE();
+                       return ret;
+               }
+       }
+
+       switch (data) {
+       case 0:
+
+               temp = CAL_RSSI(adapter->SNR[TYPE_BEACON][TYPE_NOAVG],
+                               adapter->NF[TYPE_BEACON][TYPE_NOAVG]);
+               break;
+       case 1:
+               temp = CAL_RSSI(adapter->SNR[TYPE_BEACON][TYPE_AVG],
+                               adapter->NF[TYPE_BEACON][TYPE_AVG]);
+               break;
+       case 2:
+               temp = CAL_RSSI(adapter->SNR[TYPE_RXPD][TYPE_NOAVG],
+                               adapter->NF[TYPE_RXPD][TYPE_NOAVG]);
+               break;
+       case 3:
+               temp = CAL_RSSI(adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE,
+                               adapter->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE);
+               break;
+       default:
+               return -ENOTSUPP;
+       }
+       val = (int *)wrq->u.name;
+       *val = temp;
+
+       LEAVE();
+       return 0;
+}
+
+static int wlan_get_nf(wlan_private * priv, struct iwreq *wrq)
+{
+       int ret = 0;
+       wlan_adapter *adapter = priv->adapter;
+       int temp;
+       int data = 0;
+       int *val;
+
+       data = SUBCMD_DATA(wrq);
+       if ((data == 0) || (data == 1)) {
+               ret = libertas_prepare_and_send_command(priv,
+                                           cmd_802_11_rssi,
+                                           0, cmd_option_waitforrsp,
+                                           0, NULL);
+
+               if (ret) {
+                       LEAVE();
+                       return ret;
+               }
+       }
+
+       switch (data) {
+       case 0:
+               temp = adapter->NF[TYPE_BEACON][TYPE_NOAVG];
+               break;
+       case 1:
+               temp = adapter->NF[TYPE_BEACON][TYPE_AVG];
+               break;
+       case 2:
+               temp = adapter->NF[TYPE_RXPD][TYPE_NOAVG];
+               break;
+       case 3:
+               temp = adapter->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE;
+               break;
+       default:
+               return -ENOTSUPP;
+       }
+
+       temp = CAL_NF(temp);
+
+       lbs_pr_debug(1, "%s: temp = %d\n", __FUNCTION__, temp);
+       val = (int *)wrq->u.name;
+       *val = temp;
+       return 0;
+}
+
+static int wlan_get_txrate_ioctl(wlan_private * priv, struct ifreq *req)
+{
+       wlan_adapter *adapter = priv->adapter;
+       int *pdata;
+       struct iwreq *wrq = (struct iwreq *)req;
+       int ret = 0;
+       adapter->txrate = 0;
+       lbs_pr_debug(1, "wlan_get_txrate_ioctl\n");
+       ret = libertas_prepare_and_send_command(priv, cmd_802_11_tx_rate_query,
+                                   cmd_act_get, cmd_option_waitforrsp,
+                                   0, NULL);
+       if (ret)
+               return ret;
+
+       pdata = (int *)wrq->u.name;
+       *pdata = (int)adapter->txrate;
+       return 0;
+}
+
+static int wlan_get_adhoc_status_ioctl(wlan_private * priv, struct iwreq *wrq)
+{
+       char status[64];
+       wlan_adapter *adapter = priv->adapter;
+
+       memset(status, 0, sizeof(status));
+
+       switch (adapter->inframode) {
+       case wlan802_11ibss:
+               if (adapter->connect_status == libertas_connected) {
+                       if (adapter->adhoccreate)
+                               memcpy(&status, "AdhocStarted", sizeof(status));
+                       else
+                               memcpy(&status, "AdhocJoined", sizeof(status));
+               } else {
+                       memcpy(&status, "AdhocIdle", sizeof(status));
+               }
+               break;
+       case wlan802_11infrastructure:
+               memcpy(&status, "Inframode", sizeof(status));
+               break;
+       default:
+               memcpy(&status, "AutoUnknownmode", sizeof(status));
+               break;
+       }
+
+       lbs_pr_debug(1, "status = %s\n", status);
+       wrq->u.data.length = strlen(status) + 1;
+
+       if (wrq->u.data.pointer) {
+               if (copy_to_user(wrq->u.data.pointer,
+                                &status, wrq->u.data.length))
+                       return -EFAULT;
+       }
+
+       LEAVE();
+       return 0;
+}
+
+/**
+ *  @brief Set/Get WPA IE
+ *  @param priv                 A pointer to wlan_private structure
+ *  @param req                 A pointer to ifreq structure
+ *  @return                    0 --success, otherwise fail
+ */
+static int wlan_setwpaie_ioctl(wlan_private * priv, struct ifreq *req)
+{
+       struct iwreq *wrq = (struct iwreq *)req;
+       wlan_adapter *adapter = priv->adapter;
+       int ret = 0;
+
+       ENTER();
+
+       if (wrq->u.data.length) {
+               if (wrq->u.data.length > sizeof(adapter->wpa_ie)) {
+                       lbs_pr_debug(1, "failed to copy WPA IE, too big \n");
+                       return -EFAULT;
+               }
+               if (copy_from_user(adapter->wpa_ie, wrq->u.data.pointer,
+                                  wrq->u.data.length)) {
+                       lbs_pr_debug(1, "failed to copy WPA IE \n");
+                       return -EFAULT;
+               }
+               adapter->wpa_ie_len = wrq->u.data.length;
+               lbs_pr_debug(1, "Set wpa_ie_len=%d IE=%#x\n", adapter->wpa_ie_len,
+                      adapter->wpa_ie[0]);
+               lbs_dbg_hex("wpa_ie", adapter->wpa_ie, adapter->wpa_ie_len);
+               if (adapter->wpa_ie[0] == WPA_IE)
+                       adapter->secinfo.WPAenabled = 1;
+               else if (adapter->wpa_ie[0] == WPA2_IE)
+                       adapter->secinfo.WPA2enabled = 1;
+               else {
+                       adapter->secinfo.WPAenabled = 0;
+                       adapter->secinfo.WPA2enabled = 0;
+               }
+       } else {
+               memset(adapter->wpa_ie, 0, sizeof(adapter->wpa_ie));
+               adapter->wpa_ie_len = wrq->u.data.length;
+               lbs_pr_debug(1, "Reset wpa_ie_len=%d IE=%#x\n",
+                      adapter->wpa_ie_len, adapter->wpa_ie[0]);
+               adapter->secinfo.WPAenabled = 0;
+               adapter->secinfo.WPA2enabled = 0;
+       }
+
+       // enable/disable RSN in firmware if WPA is enabled/disabled
+       // depending on variable adapter->secinfo.WPAenabled is set or not
+       ret = libertas_prepare_and_send_command(priv, cmd_802_11_enable_rsn,
+                                   cmd_act_set, cmd_option_waitforrsp,
+                                   0, NULL);
+
+       LEAVE();
+       return ret;
+}
+
+/**
+ *  @brief Set Auto prescan
+ *  @param priv                 A pointer to wlan_private structure
+ *  @param wrq                 A pointer to iwreq structure
+ *  @return                    0 --success, otherwise fail
+ */
+static int wlan_subcmd_setprescan_ioctl(wlan_private * priv, struct iwreq *wrq)
+{
+       int data;
+       wlan_adapter *adapter = priv->adapter;
+       int *val;
+
+       data = SUBCMD_DATA(wrq);
+       lbs_pr_debug(1, "WLAN_SUBCMD_SET_PRESCAN %d\n", data);
+       adapter->prescan = data;
+
+       val = (int *)wrq->u.name;
+       *val = data;
+       return 0;
+}
+
+static int wlan_set_multiple_dtim_ioctl(wlan_private * priv, struct ifreq *req)
+{
+       struct iwreq *wrq = (struct iwreq *)req;
+       u32 mdtim;
+       int idata;
+       int ret = -EINVAL;
+
+       ENTER();
+
+       idata = SUBCMD_DATA(wrq);
+       mdtim = (u32) idata;
+       if (((mdtim >= MRVDRV_MIN_MULTIPLE_DTIM)
+            && (mdtim <= MRVDRV_MAX_MULTIPLE_DTIM))
+           || (mdtim == MRVDRV_IGNORE_MULTIPLE_DTIM)) {
+               priv->adapter->multipledtim = mdtim;
+               ret = 0;
+       }
+       if (ret)
+               lbs_pr_debug(1, "Invalid parameter, multipledtim not changed.\n");
+
+       LEAVE();
+       return ret;
+}
+
+/**
+ *  @brief Set authentication mode
+ *  @param priv                 A pointer to wlan_private structure
+ *  @param req                 A pointer to ifreq structure
+ *  @return                    0 --success, otherwise fail
+ */
+static int wlan_setauthalg_ioctl(wlan_private * priv, struct ifreq *req)
+{
+       int alg;
+       struct iwreq *wrq = (struct iwreq *)req;
+       wlan_adapter *adapter = priv->adapter;
+
+       if (wrq->u.data.flags == 0) {
+               //from iwpriv subcmd
+               alg = SUBCMD_DATA(wrq);
+       } else {
+               //from wpa_supplicant subcmd
+               if (copy_from_user(&alg, wrq->u.data.pointer, sizeof(alg))) {
+                       lbs_pr_debug(1, "Copy from user failed\n");
+                       return -EFAULT;
+               }
+       }
+
+       lbs_pr_debug(1, "auth alg is %#x\n", alg);
+
+       switch (alg) {
+       case AUTH_ALG_SHARED_KEY:
+               adapter->secinfo.authmode = wlan802_11authmodeshared;
+               break;
+       case AUTH_ALG_NETWORK_EAP:
+               adapter->secinfo.authmode =
+                   wlan802_11authmodenetworkEAP;
+               break;
+       case AUTH_ALG_OPEN_SYSTEM:
+       default:
+               adapter->secinfo.authmode = wlan802_11authmodeopen;
+               break;
+       }
+       return 0;
+}
+
+/**
+ *  @brief Set 802.1x authentication mode
+ *  @param priv                 A pointer to wlan_private structure
+ *  @param req                 A pointer to ifreq structure
+ *  @return                    0 --success, otherwise fail
+ */
+static int wlan_set8021xauthalg_ioctl(wlan_private * priv, struct ifreq *req)
+{
+       int alg;
+       struct iwreq *wrq = (struct iwreq *)req;
+
+       if (wrq->u.data.flags == 0) {
+               //from iwpriv subcmd
+               alg = SUBCMD_DATA(wrq);
+       } else {
+               //from wpa_supplicant subcmd
+               if (copy_from_user(&alg, wrq->u.data.pointer, sizeof(int))) {
+                       lbs_pr_debug(1, "Copy from user failed\n");
+                       return -EFAULT;
+               }
+       }
+       lbs_pr_debug(1, "802.1x auth alg is %#x\n", alg);
+       priv->adapter->secinfo.auth1xalg = alg;
+       return 0;
+}
+
+static int wlan_setencryptionmode_ioctl(wlan_private * priv, struct ifreq *req)
+{
+       int mode;
+       struct iwreq *wrq = (struct iwreq *)req;
+
+       ENTER();
+
+       if (wrq->u.data.flags == 0) {
+               //from iwpriv subcmd
+               mode = SUBCMD_DATA(wrq);
+       } else {
+               //from wpa_supplicant subcmd
+               if (copy_from_user(&mode, wrq->u.data.pointer, sizeof(int))) {
+                       lbs_pr_debug(1, "Copy from user failed\n");
+                       return -EFAULT;
+               }
+       }
+       lbs_pr_debug(1, "encryption mode is %#x\n", mode);
+       priv->adapter->secinfo.Encryptionmode = mode;
+
+       LEAVE();
+       return 0;
+}
+
+static void adjust_mtu(wlan_private * priv)
+{
+       int mtu_increment = 0;
+
+       if (priv->adapter->linkmode == WLAN_LINKMODE_802_11)
+               mtu_increment += sizeof(struct ieee80211_hdr_4addr);
+
+       if (priv->adapter->radiomode == WLAN_RADIOMODE_RADIOTAP)
+               mtu_increment += max(sizeof(struct tx_radiotap_hdr),
+                                    sizeof(struct rx_radiotap_hdr));
+       priv->wlan_dev.netdev->mtu = ETH_FRAME_LEN
+           - sizeof(struct ethhdr)
+           + mtu_increment;
+}
+
+/**
+ *  @brief Set Link-Layer Layer mode
+ *  @param priv                 A pointer to wlan_private structure
+ *  @param req                 A pointer to ifreq structure
+ *  @return                    0 --success, otherwise fail
+ */
+static int wlan_set_linkmode_ioctl(wlan_private * priv, struct ifreq *req)
+{
+       int mode;
+
+       mode = (int)((struct ifreq *)((u8 *) req + 4))->ifr_data;
+
+       switch (mode) {
+       case WLAN_LINKMODE_802_3:
+               priv->adapter->linkmode = mode;
+               break;
+       case WLAN_LINKMODE_802_11:
+               priv->adapter->linkmode = mode;
+               break;
+       default:
+               lbs_pr_info("usb8388-5: invalid link-layer mode (%#x)\n",
+                      mode);
+               return -EINVAL;
+               break;
+       }
+       lbs_pr_debug(1, "usb8388-5: link-layer mode is %#x\n", mode);
+
+       adjust_mtu(priv);
+
+       return 0;
+}
+
+/**
+ *  @brief Set Radio header mode
+ *  @param priv                 A pointer to wlan_private structure
+ *  @param req                 A pointer to ifreq structure
+ *  @return                    0 --success, otherwise fail
+ */
+static int wlan_set_radiomode_ioctl(wlan_private * priv, struct ifreq *req)
+{
+       int mode;
+
+       mode = (int)((struct ifreq *)((u8 *) req + 4))->ifr_data;
+
+       switch (mode) {
+       case WLAN_RADIOMODE_NONE:
+               priv->adapter->radiomode = mode;
+               break;
+       case WLAN_RADIOMODE_RADIOTAP:
+               priv->adapter->radiomode = mode;
+               break;
+       default:
+               lbs_pr_debug(1, "usb8388-5: invalid radio header mode (%#x)\n",
+                      mode);
+               return -EINVAL;
+       }
+       lbs_pr_debug(1, "usb8388-5: radio-header mode is %#x\n", mode);
+
+       adjust_mtu(priv);
+       return 0;
+}
+
+/**
+ *  @brief Set Debug header mode
+ *  @param priv                 A pointer to wlan_private structure
+ *  @param req                 A pointer to ifreq structure
+ *  @return                    0 --success, otherwise fail
+ */
+static int wlan_set_debugmode_ioctl(wlan_private * priv, struct ifreq *req)
+{
+       priv->adapter->debugmode = (int)((struct ifreq *)
+                                        ((u8 *) req + 4))->ifr_data;
+       return 0;
+}
+
+static int wlan_subcmd_getrxantenna_ioctl(wlan_private * priv,
+                                         struct ifreq *req)
+{
+       int len;
+       char buf[8];
+       struct iwreq *wrq = (struct iwreq *)req;
+
+       lbs_pr_debug(1, "WLAN_SUBCMD_GETRXANTENNA\n");
+       len = getrxantenna(priv, buf);
+
+       wrq->u.data.length = len;
+       if (wrq->u.data.pointer) {
+               if (copy_to_user(wrq->u.data.pointer, &buf, len)) {
+                       lbs_pr_debug(1, "CopyToUser failed\n");
+                       return -EFAULT;
+               }
+       }
+
+       return 0;
+}
+
+static int wlan_subcmd_gettxantenna_ioctl(wlan_private * priv,
+                                         struct ifreq *req)
+{
+       int len;
+       char buf[8];
+       struct iwreq *wrq = (struct iwreq *)req;
+
+       lbs_pr_debug(1, "WLAN_SUBCMD_GETTXANTENNA\n");
+       len = gettxantenna(priv, buf);
+
+       wrq->u.data.length = len;
+       if (wrq->u.data.pointer) {
+               if (copy_to_user(wrq->u.data.pointer, &buf, len)) {
+                       lbs_pr_debug(1, "CopyToUser failed\n");
+                       return -EFAULT;
+               }
+       }
+       return 0;
+}
+
+/**
+ *  @brief Get the MAC TSF value from the firmware
+ *
+ *  @param priv         A pointer to wlan_private structure
+ *  @param wrq          A pointer to iwreq structure containing buffer
+ *                      space to store a TSF value retrieved from the firmware
+ *
+ *  @return             0 if successful; IOCTL error code otherwise
+ */
+static int wlan_get_tsf_ioctl(wlan_private * priv, struct iwreq *wrq)
+{
+       u64 tsfval;
+       int ret;
+
+       ret = libertas_prepare_and_send_command(priv,
+                                   cmd_get_tsf,
+                                   0, cmd_option_waitforrsp, 0, &tsfval);
+
+       lbs_pr_debug(1, "IOCTL: Get TSF = 0x%016llx\n", tsfval);
+
+       if (ret != 0) {
+               lbs_pr_debug(1, "IOCTL: Get TSF; command exec failed\n");
+               ret = -EFAULT;
+       } else {
+               if (copy_to_user(wrq->u.data.pointer,
+                                &tsfval,
+                                min_t(size_t, wrq->u.data.length,
+                                    sizeof(tsfval))) != 0) {
+
+                       lbs_pr_debug(1, "IOCTL: Get TSF; Copy to user failed\n");
+                       ret = -EFAULT;
+               } else {
+                       ret = 0;
+               }
+       }
+       return ret;
+}
+
+/**
+ *  @brief Get/Set adapt rate
+ *  @param priv                 A pointer to wlan_private structure
+ *  @param wrq                 A pointer to iwreq structure
+ *  @return                    0 --success, otherwise fail
+ */
+static int wlan_adapt_rateset(wlan_private * priv, struct iwreq *wrq)
+{
+       int ret;
+       wlan_adapter *adapter = priv->adapter;
+       int data[2];
+
+       memset(data, 0, sizeof(data));
+       if (!wrq->u.data.length) {
+               lbs_pr_debug(1, "Get ADAPT RATE SET\n");
+               ret = libertas_prepare_and_send_command(priv,
+                                           cmd_802_11_rate_adapt_rateset,
+                                           cmd_act_get,
+                                           cmd_option_waitforrsp, 0, NULL);
+               data[0] = adapter->enablehwauto;
+               data[1] = adapter->ratebitmap;
+               if (copy_to_user(wrq->u.data.pointer, data, sizeof(int) * 2)) {
+                       lbs_pr_debug(1, "Copy to user failed\n");
+                       return -EFAULT;
+               }
+#define GET_TWO_INT    2
+               wrq->u.data.length = GET_TWO_INT;
+       } else {
+               lbs_pr_debug(1, "Set ADAPT RATE SET\n");
+               if (wrq->u.data.length > 2)
+                       return -EINVAL;
+               if (copy_from_user
+                   (data, wrq->u.data.pointer,
+                    sizeof(int) * wrq->u.data.length)) {
+                       lbs_pr_debug(1, "Copy from user failed\n");
+                       return -EFAULT;
+               }
+
+               adapter->enablehwauto = data[0];
+               adapter->ratebitmap = data[1];
+               ret = libertas_prepare_and_send_command(priv,
+                                           cmd_802_11_rate_adapt_rateset,
+                                           cmd_act_set,
+                                           cmd_option_waitforrsp, 0, NULL);
+       }
+       return ret;
+}
+
+/**
+ *  @brief Get/Set inactivity timeout
+ *  @param priv                 A pointer to wlan_private structure
+ *  @param wrq                 A pointer to iwreq structure
+ *  @return                    0 --success, otherwise fail
+ */
+static int wlan_inactivity_timeout(wlan_private * priv, struct iwreq *wrq)
+{
+       int ret;
+       int data = 0;
+       u16 timeout = 0;
+
+       ENTER();
+       if (wrq->u.data.length > 1)
+               return -ENOTSUPP;
+
+       if (wrq->u.data.length == 0) {
+               /* Get */
+               ret = libertas_prepare_and_send_command(priv,
+                                           cmd_802_11_inactivity_timeout,
+                                           cmd_act_get,
+                                           cmd_option_waitforrsp, 0,
+                                           &timeout);
+               data = timeout;
+               if (copy_to_user(wrq->u.data.pointer, &data, sizeof(int))) {
+                       lbs_pr_debug(1, "Copy to user failed\n");
+                       return -EFAULT;
+               }
+       } else {
+               /* Set */
+               if (copy_from_user(&data, wrq->u.data.pointer, sizeof(int))) {
+                       lbs_pr_debug(1, "Copy from user failed\n");
+                       return -EFAULT;
+               }
+
+               timeout = data;
+               ret = libertas_prepare_and_send_command(priv,
+                                           cmd_802_11_inactivity_timeout,
+                                           cmd_act_set,
+                                           cmd_option_waitforrsp, 0,
+                                           &timeout);
+       }
+
+       wrq->u.data.length = 1;
+
+       LEAVE();
+       return ret;
+}
+
+static int wlan_do_getlog_ioctl(wlan_private * priv, struct iwreq *wrq)
+{
+       int ret;
+       char buf[GETLOG_BUFSIZE - 1];
+       wlan_adapter *adapter = priv->adapter;
+
+       lbs_pr_debug(1, " GET STATS\n");
+
+       ret = libertas_prepare_and_send_command(priv, cmd_802_11_get_log,
+                                   0, cmd_option_waitforrsp, 0, NULL);
+
+       if (ret) {
+               return ret;
+       }
+
+       if (wrq->u.data.pointer) {
+               sprintf(buf, "\n  mcasttxframe %u failed %u retry %u "
+                       "multiretry %u framedup %u "
+                       "rtssuccess %u rtsfailure %u ackfailure %u\n"
+                       "rxfrag %u mcastrxframe %u fcserror %u "
+                       "txframe %u wepundecryptable %u ",
+                       adapter->logmsg.mcasttxframe,
+                       adapter->logmsg.failed,
+                       adapter->logmsg.retry,
+                       adapter->logmsg.multiretry,
+                       adapter->logmsg.framedup,
+                       adapter->logmsg.rtssuccess,
+                       adapter->logmsg.rtsfailure,
+                       adapter->logmsg.ackfailure,
+                       adapter->logmsg.rxfrag,
+                       adapter->logmsg.mcastrxframe,
+                       adapter->logmsg.fcserror,
+                       adapter->logmsg.txframe,
+                       adapter->logmsg.wepundecryptable);
+               wrq->u.data.length = strlen(buf) + 1;
+               if (copy_to_user(wrq->u.data.pointer, buf, wrq->u.data.length)) {
+                       lbs_pr_debug(1, "Copy to user failed\n");
+                       return -EFAULT;
+               }
+       }
+
+       return 0;
+}
+
+static int wlan_scan_type_ioctl(wlan_private * priv, struct iwreq *wrq)
+{
+       u8 buf[12];
+       u8 *option[] = { "active", "passive", "get", };
+       int i, max_options = (sizeof(option) / sizeof(option[0]));
+       int ret = 0;
+       wlan_adapter *adapter = priv->adapter;
+
+       if (priv->adapter->enable11d) {
+               lbs_pr_debug(1, "11D: Cannot set scantype when 11D enabled\n");
+               return -EFAULT;
+       }
+
+       memset(buf, 0, sizeof(buf));
+
+       if (copy_from_user(buf, wrq->u.data.pointer, min_t(size_t, sizeof(buf),
+                                                        wrq->u.data.length)))
+               return -EFAULT;
+
+       lbs_pr_debug(1, "Scan type Option = %s\n", buf);
+
+       buf[sizeof(buf) - 1] = '\0';
+
+       for (i = 0; i < max_options; i++) {
+               if (!strcmp(buf, option[i]))
+                       break;
+       }
+
+       switch (i) {
+       case 0:
+               adapter->scantype = cmd_scan_type_active;
+               break;
+       case 1:
+               adapter->scantype = cmd_scan_type_passive;
+               break;
+       case 2:
+               wrq->u.data.length = strlen(option[adapter->scantype]) + 1;
+
+               if (copy_to_user(wrq->u.data.pointer,
+                                option[adapter->scantype],
+                                wrq->u.data.length)) {
+                       lbs_pr_debug(1, "Copy to user failed\n");
+                       ret = -EFAULT;
+               }
+
+               break;
+       default:
+               lbs_pr_debug(1, "Invalid Scan type Ioctl Option\n");
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static int wlan_scan_mode_ioctl(wlan_private * priv, struct iwreq *wrq)
+{
+       wlan_adapter *adapter = priv->adapter;
+       u8 buf[12];
+       u8 *option[] = { "bss", "ibss", "any", "get" };
+       int i, max_options = (sizeof(option) / sizeof(option[0]));
+       int ret = 0;
+
+       ENTER();
+
+       memset(buf, 0, sizeof(buf));
+
+       if (copy_from_user(buf, wrq->u.data.pointer, min_t(size_t, sizeof(buf),
+                                                        wrq->u.data.length))) {
+               lbs_pr_debug(1, "Copy from user failed\n");
+               return -EFAULT;
+       }
+
+       lbs_pr_debug(1, "Scan mode Option = %s\n", buf);
+
+       buf[sizeof(buf) - 1] = '\0';
+
+       for (i = 0; i < max_options; i++) {
+               if (!strcmp(buf, option[i]))
+                       break;
+       }
+
+       switch (i) {
+
+       case 0:
+               adapter->scanmode = cmd_bss_type_bss;
+               break;
+       case 1:
+               adapter->scanmode = cmd_bss_type_ibss;
+               break;
+       case 2:
+               adapter->scanmode = cmd_bss_type_any;
+               break;
+       case 3:
+
+               wrq->u.data.length = strlen(option[adapter->scanmode - 1]) + 1;
+
+               lbs_pr_debug(1, "Get Scan mode Option = %s\n",
+                      option[adapter->scanmode - 1]);
+
+               lbs_pr_debug(1, "Scan mode length %d\n", wrq->u.data.length);
+
+               if (copy_to_user(wrq->u.data.pointer,
+                                option[adapter->scanmode - 1],
+                                wrq->u.data.length)) {
+                       lbs_pr_debug(1, "Copy to user failed\n");
+                       ret = -EFAULT;
+               }
+               lbs_pr_debug(1, "GET Scan type Option after copy = %s\n",
+                      (char *)wrq->u.data.pointer);
+
+               break;
+
+       default:
+               lbs_pr_debug(1, "Invalid Scan mode Ioctl Option\n");
+               ret = -EINVAL;
+               break;
+       }
+
+       LEAVE();
+       return ret;
+}
+
+/**
+ *  @brief Get/Set Adhoc G Rate
+ *
+ *  @param priv                A pointer to wlan_private structure
+ *  @param wrq         A pointer to user data
+ *  @return            0--success, otherwise fail
+ */
+static int wlan_do_set_grate_ioctl(wlan_private * priv, struct iwreq *wrq)
+{
+       wlan_adapter *adapter = priv->adapter;
+       int data, data1;
+       int *val;
+
+       ENTER();
+
+       data1 = SUBCMD_DATA(wrq);
+       switch (data1) {
+       case 0:
+               adapter->adhoc_grate_enabled = 0;
+               break;
+       case 1:
+               adapter->adhoc_grate_enabled = 1;
+               break;
+       case 2:
+               break;
+       default:
+               return -EINVAL;
+       }
+       data = adapter->adhoc_grate_enabled;
+       val = (int *)wrq->u.name;
+       *val = data;
+       LEAVE();
+       return 0;
+}
+
+static inline int hex2int(char c)
+{
+       if (c >= '0' && c <= '9')
+               return (c - '0');
+       if (c >= 'a' && c <= 'f')
+               return (c - 'a' + 10);
+       if (c >= 'A' && c <= 'F')
+               return (c - 'A' + 10);
+       return -1;
+}
+
+/* Convert a string representation of a MAC address ("xx:xx:xx:xx:xx:xx")
+   into binary format (6 bytes).
+
+   This function expects that each byte is represented with 2 characters
+   (e.g., 11:2:11:11:11:11 is invalid)
+
+ */
+static char *eth_str2addr(char *ethstr, u8 * addr)
+{
+       int i, val, val2;
+       char *pos = ethstr;
+
+       /* get rid of initial blanks */
+       while (*pos == ' ' || *pos == '\t')
+               ++pos;
+
+       for (i = 0; i < 6; i++) {
+               val = hex2int(*pos++);
+               if (val < 0)
+                       return NULL;
+               val2 = hex2int(*pos++);
+               if (val2 < 0)
+                       return NULL;
+               addr[i] = (val * 16 + val2) & 0xff;
+
+               if (i < 5 && *pos++ != ':')
+                       return NULL;
+       }
+       return pos;
+}
+
+/* this writes xx:xx:xx:xx:xx:xx into ethstr
+   (ethstr must have space for 18 chars) */
+static int eth_addr2str(u8 * addr, char *ethstr)
+{
+       int i;
+       char *pos = ethstr;
+
+       for (i = 0; i < 6; i++) {
+               sprintf(pos, "%02x", addr[i] & 0xff);
+               pos += 2;
+               if (i < 5)
+                       *pos++ = ':';
+       }
+       return 17;
+}
+
+/**
+ *  @brief          Add an entry to the BT table
+ *  @param priv     A pointer to wlan_private structure
+ *  @param req      A pointer to ifreq structure
+ *  @return         0 --success, otherwise fail
+ */
+static int wlan_bt_add_ioctl(wlan_private * priv, struct ifreq *req)
+{
+       struct iwreq *wrq = (struct iwreq *)req;
+       char ethaddrs_str[18];
+       char *pos;
+       u8 ethaddr[ETH_ALEN];
+
+       ENTER();
+       if (copy_from_user(ethaddrs_str, wrq->u.data.pointer,
+                          sizeof(ethaddrs_str)))
+               return -EFAULT;
+
+       if ((pos = eth_str2addr(ethaddrs_str, ethaddr)) == NULL) {
+               lbs_pr_info("BT_ADD: Invalid MAC address\n");
+               return -EINVAL;
+       }
+
+       lbs_pr_debug(1, "BT: adding %s\n", ethaddrs_str);
+       LEAVE();
+       return (libertas_prepare_and_send_command(priv, cmd_bt_access,
+                                     cmd_act_bt_access_add,
+                                     cmd_option_waitforrsp, 0, ethaddr));
+}
+
+/**
+ *  @brief          Delete an entry from the BT table
+ *  @param priv     A pointer to wlan_private structure
+ *  @param req      A pointer to ifreq structure
+ *  @return         0 --success, otherwise fail
+ */
+static int wlan_bt_del_ioctl(wlan_private * priv, struct ifreq *req)
+{
+       struct iwreq *wrq = (struct iwreq *)req;
+       char ethaddrs_str[18];
+       u8 ethaddr[ETH_ALEN];
+       char *pos;
+
+       ENTER();
+       if (copy_from_user(ethaddrs_str, wrq->u.data.pointer,
+                          sizeof(ethaddrs_str)))
+               return -EFAULT;
+
+       if ((pos = eth_str2addr(ethaddrs_str, ethaddr)) == NULL) {
+               lbs_pr_info("Invalid MAC address\n");
+               return -EINVAL;
+       }
+
+       lbs_pr_debug(1, "BT: deleting %s\n", ethaddrs_str);
+
+       return (libertas_prepare_and_send_command(priv,
+                                     cmd_bt_access,
+                                     cmd_act_bt_access_del,
+                                     cmd_option_waitforrsp, 0, ethaddr));
+       LEAVE();
+       return 0;
+}
+
+/**
+ *  @brief          Reset all entries from the BT table
+ *  @param priv     A pointer to wlan_private structure
+ *  @return         0 --success, otherwise fail
+ */
+static int wlan_bt_reset_ioctl(wlan_private * priv)
+{
+       ENTER();
+
+       lbs_pr_alert( "BT: resetting\n");
+
+       return (libertas_prepare_and_send_command(priv,
+                                     cmd_bt_access,
+                                     cmd_act_bt_access_reset,
+                                     cmd_option_waitforrsp, 0, NULL));
+
+       LEAVE();
+       return 0;
+}
+
+/**
+ *  @brief          List an entry from the BT table
+ *  @param priv     A pointer to wlan_private structure
+ *  @param req      A pointer to ifreq structure
+ *  @return         0 --success, otherwise fail
+ */
+static int wlan_bt_list_ioctl(wlan_private * priv, struct ifreq *req)
+{
+       int pos;
+       char *addr1;
+       struct iwreq *wrq = (struct iwreq *)req;
+       /* used to pass id and store the bt entry returned by the FW */
+       union {
+               int id;
+               char addr1addr2[2 * ETH_ALEN];
+       } param;
+       static char outstr[64];
+       char *pbuf = outstr;
+       int ret;
+
+       ENTER();
+
+       if (copy_from_user(outstr, wrq->u.data.pointer, sizeof(outstr))) {
+               lbs_pr_debug(1, "Copy from user failed\n");
+               return -1;
+       }
+       param.id = simple_strtoul(outstr, NULL, 10);
+       pos = sprintf(pbuf, "%d: ", param.id);
+       pbuf += pos;
+
+       ret = libertas_prepare_and_send_command(priv, cmd_bt_access,
+                                   cmd_act_bt_access_list,
+                                   cmd_option_waitforrsp, 0,
+                                   (char *)&param);
+
+       if (ret == 0) {
+               addr1 = param.addr1addr2;
+
+               pos = sprintf(pbuf, "ignoring traffic from ");
+               pbuf += pos;
+               pos = eth_addr2str(addr1, pbuf);
+               pbuf += pos;
+       } else {
+               sprintf(pbuf, "(null)");
+               pbuf += pos;
+       }
+
+       wrq->u.data.length = strlen(outstr);
+       if (copy_to_user(wrq->u.data.pointer, (char *)outstr,
+                        wrq->u.data.length)) {
+               lbs_pr_debug(1, "BT_LIST: Copy to user failed!\n");
+               return -EFAULT;
+       }
+
+       LEAVE();
+       return 0;
+}
+
+/**
+ *  @brief          Find the next parameter in an input string
+ *  @param ptr      A pointer to the input parameter string
+ *  @return         A pointer to the next parameter, or 0 if no parameters left.
+ */
+static char * next_param(char * ptr)
+{
+       if (!ptr) return NULL;
+       while (*ptr == ' ' || *ptr == '\t') ++ptr;
+       return (*ptr == '\0') ? NULL : ptr;
+}
+
+/**
+ *  @brief          Add an entry to the FWT table
+ *  @param priv     A pointer to wlan_private structure
+ *  @param req      A pointer to ifreq structure
+ *  @return         0 --success, otherwise fail
+ */
+static int wlan_fwt_add_ioctl(wlan_private * priv, struct ifreq *req)
+{
+       struct iwreq *wrq = (struct iwreq *)req;
+       char in_str[128];
+       static struct cmd_ds_fwt_access fwt_access;
+       char *ptr;
+
+       ENTER();
+       if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str)))
+               return -EFAULT;
+
+       if ((ptr = eth_str2addr(in_str, fwt_access.da)) == NULL) {
+               lbs_pr_alert( "FWT_ADD: Invalid MAC address 1\n");
+               return -EINVAL;
+       }
+
+       if ((ptr = eth_str2addr(ptr, fwt_access.ra)) == NULL) {
+               lbs_pr_alert( "FWT_ADD: Invalid MAC address 2\n");
+               return -EINVAL;
+       }
+
+       if ((ptr = next_param(ptr)))
+               fwt_access.metric =
+                       cpu_to_le32(simple_strtoul(ptr, &ptr, 10));
+       else
+               fwt_access.metric = FWT_DEFAULT_METRIC;
+
+       if ((ptr = next_param(ptr)))
+               fwt_access.dir = (u8)simple_strtoul(ptr, &ptr, 10);
+       else
+               fwt_access.dir = FWT_DEFAULT_DIR;
+
+       if ((ptr = next_param(ptr)))
+               fwt_access.ssn =
+                       cpu_to_le32(simple_strtoul(ptr, &ptr, 10));
+       else
+               fwt_access.ssn = FWT_DEFAULT_SSN;
+
+       if ((ptr = next_param(ptr)))
+               fwt_access.dsn =
+                       cpu_to_le32(simple_strtoul(ptr, &ptr, 10));
+       else
+               fwt_access.dsn = FWT_DEFAULT_DSN;
+
+       if ((ptr = next_param(ptr)))
+               fwt_access.hopcount = simple_strtoul(ptr, &ptr, 10);
+       else
+               fwt_access.hopcount = FWT_DEFAULT_HOPCOUNT;
+
+       if ((ptr = next_param(ptr)))
+               fwt_access.ttl = simple_strtoul(ptr, &ptr, 10);
+       else
+               fwt_access.ttl = FWT_DEFAULT_TTL;
+
+       if ((ptr = next_param(ptr)))
+               fwt_access.expiration =
+                       cpu_to_le32(simple_strtoul(ptr, &ptr, 10));
+       else
+               fwt_access.expiration = FWT_DEFAULT_EXPIRATION;
+
+       if ((ptr = next_param(ptr)))
+               fwt_access.sleepmode = (u8)simple_strtoul(ptr, &ptr, 10);
+       else
+               fwt_access.sleepmode = FWT_DEFAULT_SLEEPMODE;
+
+       if ((ptr = next_param(ptr)))
+               fwt_access.snr =
+                       cpu_to_le32(simple_strtoul(ptr, &ptr, 10));
+       else
+               fwt_access.snr = FWT_DEFAULT_SNR;
+
+#ifdef DEBUG
+       {
+               char ethaddr1_str[18], ethaddr2_str[18];
+               eth_addr2str(fwt_access.da, ethaddr1_str);
+               eth_addr2str(fwt_access.ra, ethaddr2_str);
+               lbs_pr_debug(1, "FWT_ADD: adding (da:%s,%i,ra:%s)\n", ethaddr1_str,
+                      fwt_access.dir, ethaddr2_str);
+               lbs_pr_debug(1, "FWT_ADD: ssn:%u dsn:%u met:%u hop:%u ttl:%u exp:%u slp:%u snr:%u\n",
+                      fwt_access.ssn, fwt_access.dsn, fwt_access.metric,
+                      fwt_access.hopcount, fwt_access.ttl, fwt_access.expiration,
+                      fwt_access.sleepmode, fwt_access.snr);
+       }
+#endif
+
+       LEAVE();
+       return (libertas_prepare_and_send_command(priv, cmd_fwt_access,
+                                                 cmd_act_fwt_access_add,
+                                                 cmd_option_waitforrsp, 0,
+                                                 (void *)&fwt_access));
+}
+
+/**
+ *  @brief          Delete an entry from the FWT table
+ *  @param priv     A pointer to wlan_private structure
+ *  @param req      A pointer to ifreq structure
+ *  @return         0 --success, otherwise fail
+ */
+static int wlan_fwt_del_ioctl(wlan_private * priv, struct ifreq *req)
+{
+       struct iwreq *wrq = (struct iwreq *)req;
+       char in_str[64];
+       static struct cmd_ds_fwt_access fwt_access;
+       char *ptr;
+
+       ENTER();
+       if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str)))
+               return -EFAULT;
+
+       if ((ptr = eth_str2addr(in_str, fwt_access.da)) == NULL) {
+               lbs_pr_alert( "FWT_DEL: Invalid MAC address 1\n");
+               return -EINVAL;
+       }
+
+       if ((ptr = eth_str2addr(ptr, fwt_access.ra)) == NULL) {
+               lbs_pr_alert( "FWT_DEL: Invalid MAC address 2\n");
+               return -EINVAL;
+       }
+
+       if ((ptr = next_param(ptr)))
+               fwt_access.dir = (u8)simple_strtoul(ptr, &ptr, 10);
+       else
+               fwt_access.dir = FWT_DEFAULT_DIR;
+
+#ifdef DEBUG
+       {
+               char ethaddr1_str[18], ethaddr2_str[18];
+               lbs_pr_debug(1, "FWT_DEL: line is %s\n", in_str);
+               eth_addr2str(fwt_access.da, ethaddr1_str);
+               eth_addr2str(fwt_access.ra, ethaddr2_str);
+               lbs_pr_debug(1, "FWT_DEL: removing (da:%s,ra:%s,dir:%d)\n", ethaddr1_str,
+                      ethaddr2_str, fwt_access.dir);
+       }
+#endif
+
+       LEAVE();
+       return (libertas_prepare_and_send_command(priv,
+                                                 cmd_fwt_access,
+                                                 cmd_act_fwt_access_del,
+                                                 cmd_option_waitforrsp, 0,
+                                                 (void *)&fwt_access));
+}
+
+
+/**
+ *  @brief             Print route parameters
+ *  @param fwt_access  struct cmd_ds_fwt_access with route info
+ *  @param buf         destination buffer for route info
+ */
+static void print_route(struct cmd_ds_fwt_access fwt_access, char *buf)
+{
+       buf += sprintf(buf, " ");
+       buf += eth_addr2str(fwt_access.da, buf);
+       buf += sprintf(buf, " ");
+       buf += eth_addr2str(fwt_access.ra, buf);
+       buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.metric));
+       buf += sprintf(buf, " %u", fwt_access.dir);
+       buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.ssn));
+       buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.dsn));
+       buf += sprintf(buf, " %u", fwt_access.hopcount);
+       buf += sprintf(buf, " %u", fwt_access.ttl);
+       buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.expiration));
+       buf += sprintf(buf, " %u", fwt_access.sleepmode);
+       buf += sprintf(buf, " %u", le32_to_cpu(fwt_access.snr));
+}
+
+/**
+ *  @brief          Lookup an entry in the FWT table
+ *  @param priv     A pointer to wlan_private structure
+ *  @param req      A pointer to ifreq structure
+ *  @return         0 --success, otherwise fail
+ */
+static int wlan_fwt_lookup_ioctl(wlan_private * priv, struct ifreq *req)
+{
+       struct iwreq *wrq = (struct iwreq *)req;
+       char in_str[64];
+       char *ptr;
+       static struct cmd_ds_fwt_access fwt_access;
+       static char out_str[128];
+       int ret;
+
+       ENTER();
+       if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str)))
+               return -EFAULT;
+
+       if ((ptr = eth_str2addr(in_str, fwt_access.da)) == NULL) {
+               lbs_pr_alert( "FWT_LOOKUP: Invalid MAC address\n");
+               return -EINVAL;
+       }
+
+#ifdef DEBUG
+       {
+               char ethaddr1_str[18];
+               lbs_pr_debug(1, "FWT_LOOKUP: line is %s\n", in_str);
+               eth_addr2str(fwt_access.da, ethaddr1_str);
+               lbs_pr_debug(1, "FWT_LOOKUP: looking for (da:%s)\n", ethaddr1_str);
+       }
+#endif
+
+       ret = libertas_prepare_and_send_command(priv,
+                                               cmd_fwt_access,
+                                               cmd_act_fwt_access_lookup,
+                                               cmd_option_waitforrsp, 0,
+                                               (void *)&fwt_access);
+
+       if (ret == 0)
+               print_route(fwt_access, out_str);
+       else
+               sprintf(out_str, "(null)");
+
+       wrq->u.data.length = strlen(out_str);
+       if (copy_to_user(wrq->u.data.pointer, (char *)out_str,
+                        wrq->u.data.length)) {
+               lbs_pr_debug(1, "FWT_LOOKUP: Copy to user failed!\n");
+               return -EFAULT;
+       }
+
+       LEAVE();
+       return 0;
+}
+
+/**
+ *  @brief          Reset all entries from the FWT table
+ *  @param priv     A pointer to wlan_private structure
+ *  @return         0 --success, otherwise fail
+ */
+static int wlan_fwt_reset_ioctl(wlan_private * priv)
+{
+       lbs_pr_debug(1, "FWT: resetting\n");
+
+       return (libertas_prepare_and_send_command(priv,
+                                     cmd_fwt_access,
+                                     cmd_act_fwt_access_reset,
+                                     cmd_option_waitforrsp, 0, NULL));
+}
+
+/**
+ *  @brief          List an entry from the FWT table
+ *  @param priv     A pointer to wlan_private structure
+ *  @param req      A pointer to ifreq structure
+ *  @return         0 --success, otherwise fail
+ */
+static int wlan_fwt_list_ioctl(wlan_private * priv, struct ifreq *req)
+{
+       struct iwreq *wrq = (struct iwreq *)req;
+       char in_str[8];
+       static struct cmd_ds_fwt_access fwt_access;
+       char *ptr = in_str;
+       static char out_str[128];
+       char *pbuf = out_str;
+       int ret;
+
+       ENTER();
+       if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str)))
+               return -EFAULT;
+
+       fwt_access.id = cpu_to_le32(simple_strtoul(ptr, &ptr, 10));
+
+#ifdef DEBUG
+       {
+               lbs_pr_debug(1, "FWT_LIST: line is %s\n", in_str);
+               lbs_pr_debug(1, "FWT_LIST: listing id:%i\n", le32_to_cpu(fwt_access.id));
+       }
+#endif
+
+       ret = libertas_prepare_and_send_command(priv, cmd_fwt_access,
+                                   cmd_act_fwt_access_list,
+                                   cmd_option_waitforrsp, 0, (void *)&fwt_access);
+
+       if (ret == 0)
+               print_route(fwt_access, pbuf);
+       else
+               pbuf += sprintf(pbuf, " (null)");
+
+       wrq->u.data.length = strlen(out_str);
+       if (copy_to_user(wrq->u.data.pointer, (char *)out_str,
+                        wrq->u.data.length)) {
+               lbs_pr_debug(1, "FWT_LIST: Copy to user failed!\n");
+               return -EFAULT;
+       }
+
+       LEAVE();
+       return 0;
+}
+
+/**
+ *  @brief          List an entry from the FRT table
+ *  @param priv     A pointer to wlan_private structure
+ *  @param req      A pointer to ifreq structure
+ *  @return         0 --success, otherwise fail
+ */
+static int wlan_fwt_list_route_ioctl(wlan_private * priv, struct ifreq *req)
+{
+       struct iwreq *wrq = (struct iwreq *)req;
+       char in_str[64];
+       static struct cmd_ds_fwt_access fwt_access;
+       char *ptr = in_str;
+       static char out_str[128];
+       char *pbuf = out_str;
+       int ret;
+
+       ENTER();
+       if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str)))
+               return -EFAULT;
+
+       fwt_access.id = cpu_to_le32(simple_strtoul(ptr, &ptr, 10));
+
+#ifdef DEBUG
+       {
+               lbs_pr_debug(1, "FWT_LIST_ROUTE: line is %s\n", in_str);
+               lbs_pr_debug(1, "FWT_LIST_ROUTE: listing id:%i\n", le32_to_cpu(fwt_access.id));
+       }
+#endif
+
+       ret = libertas_prepare_and_send_command(priv, cmd_fwt_access,
+                                   cmd_act_fwt_access_list_route,
+                                   cmd_option_waitforrsp, 0, (void *)&fwt_access);
+
+       if (ret == 0) {
+               pbuf += sprintf(pbuf, " ");
+               pbuf += eth_addr2str(fwt_access.da, pbuf);
+               pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.metric));
+               pbuf += sprintf(pbuf, " %u", fwt_access.dir);
+               /* note that the firmware returns the nid in the id field */
+               pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.id));
+               pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.ssn));
+               pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.dsn));
+               pbuf += sprintf(pbuf, "  hop %u", fwt_access.hopcount);
+               pbuf += sprintf(pbuf, "  ttl %u", fwt_access.ttl);
+               pbuf += sprintf(pbuf, " %u", le32_to_cpu(fwt_access.expiration));
+       } else
+               pbuf += sprintf(pbuf, " (null)");
+
+       wrq->u.data.length = strlen(out_str);
+       if (copy_to_user(wrq->u.data.pointer, (char *)out_str,
+                        wrq->u.data.length)) {
+               lbs_pr_debug(1, "FWT_LIST_ROUTE: Copy to user failed!\n");
+               return -EFAULT;
+       }
+
+       LEAVE();
+       return 0;
+}
+
+/**
+ *  @brief          List an entry from the FNT table
+ *  @param priv     A pointer to wlan_private structure
+ *  @param req      A pointer to ifreq structure
+ *  @return         0 --success, otherwise fail
+ */
+static int wlan_fwt_list_neighbor_ioctl(wlan_private * priv, struct ifreq *req)
+{
+       struct iwreq *wrq = (struct iwreq *)req;
+       char in_str[8];
+       static struct cmd_ds_fwt_access fwt_access;
+       char *ptr = in_str;
+       static char out_str[128];
+       char *pbuf = out_str;
+       int ret;
+
+       ENTER();
+       if (copy_from_user(in_str, wrq->u.data.pointer, sizeof(in_str)))
+               return -EFAULT;
+
+       memset(&fwt_access, 0, sizeof(fwt_access));
+       fwt_access.id = cpu_to_le32(simple_strtoul(ptr, &ptr, 10));
+
+#ifdef DEBUG
+       {
+               lbs_pr_debug(1, "FWT_LIST_NEIGHBOR: line is %s\n", in_str);
+               lbs_pr_debug(1, "FWT_LIST_NEIGHBOR: listing id:%i\n", le32_to_cpu(fwt_access.id));
+       }
+#endif
+
+       ret = libertas_prepare_and_send_command(priv, cmd_fwt_access,
+                                   cmd_act_fwt_access_list_neighbor,
+                                   cmd_option_waitforrsp, 0,
+                                   (void *)&fwt_access);
+
+       if (ret == 0) {
+               pbuf += sprintf(pbuf, " ra ");
+               pbuf += eth_addr2str(fwt_access.ra, pbuf);
+               pbuf += sprintf(pbuf, "  slp %u", fwt_access.sleepmode);
+               pbuf += sprintf(pbuf, "  snr %u", le32_to_cpu(fwt_access.snr));
+               pbuf += sprintf(pbuf, "  ref %u", le32_to_cpu(fwt_access.references));
+       } else
+               pbuf += sprintf(pbuf, " (null)");
+
+       wrq->u.data.length = strlen(out_str);
+       if (copy_to_user(wrq->u.data.pointer, (char *)out_str,
+                        wrq->u.data.length)) {
+               lbs_pr_debug(1, "FWT_LIST_NEIGHBOR: Copy to user failed!\n");
+               return -EFAULT;
+       }
+
+       LEAVE();
+       return 0;
+}
+
+/**
+ *  @brief          Cleans up the route (FRT) and neighbor (FNT) tables
+ *                  (Garbage Collection)
+ *  @param priv     A pointer to wlan_private structure
+ *  @param req      A pointer to ifreq structure
+ *  @return         0 --success, otherwise fail
+ */
+static int wlan_fwt_cleanup_ioctl(wlan_private * priv, struct ifreq *req)
+{
+       static struct cmd_ds_fwt_access fwt_access;
+       int ret;
+
+       ENTER();
+
+       lbs_pr_debug(1, "FWT: cleaning up\n");
+
+       memset(&fwt_access, 0, sizeof(fwt_access));
+
+       ret = libertas_prepare_and_send_command(priv, cmd_fwt_access,
+                                   cmd_act_fwt_access_cleanup,
+                                   cmd_option_waitforrsp, 0,
+                                   (void *)&fwt_access);
+
+       if (ret == 0)
+               req->ifr_data = (char *)(le32_to_cpu(fwt_access.references));
+       else
+               return -EFAULT;
+
+       LEAVE();
+       return 0;
+}
+
+/**
+ *  @brief          Gets firmware internal time (debug purposes)
+ *  @param priv     A pointer to wlan_private structure
+ *  @param req      A pointer to ifreq structure
+ *  @return         0 --success, otherwise fail
+ */
+static int wlan_fwt_time_ioctl(wlan_private * priv, struct ifreq *req)
+{
+       static struct cmd_ds_fwt_access fwt_access;
+       int ret;
+
+       ENTER();
+
+       lbs_pr_debug(1, "FWT: getting time\n");
+
+       memset(&fwt_access, 0, sizeof(fwt_access));
+
+       ret = libertas_prepare_and_send_command(priv, cmd_fwt_access,
+                                   cmd_act_fwt_access_time,
+                                   cmd_option_waitforrsp, 0,
+                                   (void *)&fwt_access);
+
+       if (ret == 0)
+               req->ifr_data = (char *)(le32_to_cpu(fwt_access.references));
+       else
+               return -EFAULT;
+
+       LEAVE();
+       return 0;
+}
+
+/**
+ *  @brief          Gets mesh ttl from firmware
+ *  @param priv     A pointer to wlan_private structure
+ *  @param req      A pointer to ifreq structure
+ *  @return         0 --success, otherwise fail
+ */
+static int wlan_mesh_get_ttl_ioctl(wlan_private * priv, struct ifreq *req)
+{
+       struct cmd_ds_mesh_access mesh_access;
+       int ret;
+
+       ENTER();
+
+       memset(&mesh_access, 0, sizeof(mesh_access));
+
+       ret = libertas_prepare_and_send_command(priv, cmd_mesh_access,
+                                   cmd_act_mesh_get_ttl,
+                                   cmd_option_waitforrsp, 0,
+                                   (void *)&mesh_access);
+
+       if (ret == 0) {
+               req->ifr_data = (char *)(le32_to_cpu(mesh_access.data[0]));
+       }
+       else
+               return -EFAULT;
+
+       LEAVE();
+       return 0;
+}
+
+/**
+ *  @brief          Gets mesh ttl from firmware
+ *  @param priv     A pointer to wlan_private structure
+ *  @param ttl      New ttl value
+ *  @return         0 --success, otherwise fail
+ */
+static int wlan_mesh_set_ttl_ioctl(wlan_private * priv, int ttl)
+{
+       struct cmd_ds_mesh_access mesh_access;
+       int ret;
+
+       ENTER();
+
+       if( (ttl > 0xff) || (ttl < 0) )
+               return -EINVAL;
+
+       memset(&mesh_access, 0, sizeof(mesh_access));
+       mesh_access.data[0] = ttl;
+
+       ret = libertas_prepare_and_send_command(priv, cmd_mesh_access,
+                                               cmd_act_mesh_set_ttl,
+                                               cmd_option_waitforrsp, 0,
+                                               (void *)&mesh_access);
+
+       if (ret != 0)
+               ret = -EFAULT;
+
+       LEAVE();
+       return ret;
+}
+
+/**
+ *  @brief ioctl function - entry point
+ *
+ *  @param dev         A pointer to net_device structure
+ *  @param req         A pointer to ifreq structure
+ *  @param cmd                 command
+ *  @return            0--success, otherwise fail
+ */
+int libertas_do_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
+{
+       int subcmd = 0;
+       int idata = 0;
+       int *pdata;
+       int ret = 0;
+       wlan_private *priv = dev->priv;
+       wlan_adapter *adapter = priv->adapter;
+       struct iwreq *wrq = (struct iwreq *)req;
+
+       ENTER();
+
+       lbs_pr_debug(1, "libertas_do_ioctl: ioctl cmd = 0x%x\n", cmd);
+       switch (cmd) {
+       case WLANSCAN_TYPE:
+               lbs_pr_debug(1, "Scan type Ioctl\n");
+               ret = wlan_scan_type_ioctl(priv, wrq);
+               break;
+
+       case WLAN_SETNONE_GETNONE:      /* set WPA mode on/off ioctl #20 */
+               switch (wrq->u.data.flags) {
+               case WLANDEAUTH:
+                       lbs_pr_debug(1, "Deauth\n");
+                       libertas_send_deauth(priv);
+                       break;
+
+               case WLANADHOCSTOP:
+                       lbs_pr_debug(1, "Adhoc stop\n");
+                       ret = libertas_do_adhocstop_ioctl(priv);
+                       break;
+
+               case WLANRADIOON:
+                       wlan_radio_ioctl(priv, 1);
+                       break;
+
+               case WLANRADIOOFF:
+                       wlan_radio_ioctl(priv, 0);
+                       break;
+               case WLANWLANIDLEON:
+                       libertas_idle_on(priv);
+                       break;
+               case WLANWLANIDLEOFF:
+                       libertas_idle_off(priv);
+                       break;
+               case WLAN_SUBCMD_BT_RESET:      /* bt_reset */
+                       wlan_bt_reset_ioctl(priv);
+                       break;
+               case WLAN_SUBCMD_FWT_RESET:     /* fwt_reset */
+                       wlan_fwt_reset_ioctl(priv);
+                       break;
+               }               /* End of switch */
+               break;
+
+       case WLANSETWPAIE:
+               ret = wlan_setwpaie_ioctl(priv, req);
+               break;
+       case WLAN_SETINT_GETINT:
+               /* The first 4 bytes of req->ifr_data is sub-ioctl number
+                * after 4 bytes sits the payload.
+                */
+               subcmd = (int)req->ifr_data;    //from iwpriv subcmd
+               switch (subcmd) {
+               case WLANNF:
+                       ret = wlan_get_nf(priv, wrq);
+                       break;
+               case WLANRSSI:
+                       ret = wlan_get_rssi(priv, wrq);
+                       break;
+               case WLANENABLE11D:
+                       ret = libertas_cmd_enable_11d(priv, wrq);
+                       break;
+               case WLANADHOCGRATE:
+                       ret = wlan_do_set_grate_ioctl(priv, wrq);
+                       break;
+               case WLAN_SUBCMD_SET_PRESCAN:
+                       ret = wlan_subcmd_setprescan_ioctl(priv, wrq);
+                       break;
+               }
+               break;
+
+       case WLAN_SETONEINT_GETONEINT:
+               switch (wrq->u.data.flags) {
+               case WLAN_BEACON_INTERVAL:
+                       ret = wlan_beacon_interval(priv, wrq);
+                       break;
+
+               case WLAN_LISTENINTRVL:
+                       if (!wrq->u.data.length) {
+                               int data;
+                               lbs_pr_debug(1, "Get locallisteninterval value\n");
+#define GET_ONE_INT    1
+                               data = adapter->locallisteninterval;
+                               if (copy_to_user(wrq->u.data.pointer,
+                                                &data, sizeof(int))) {
+                                       lbs_pr_debug(1, "Copy to user failed\n");
+                                       return -EFAULT;
+                               }
+
+                               wrq->u.data.length = GET_ONE_INT;
+                       } else {
+                               int data;
+                               if (copy_from_user
+                                   (&data, wrq->u.data.pointer, sizeof(int))) {
+                                       lbs_pr_debug(1, "Copy from user failed\n");
+                                       return -EFAULT;
+                               }
+
+                               lbs_pr_debug(1, "Set locallisteninterval = %d\n",
+                                      data);
+#define MAX_U16_VAL    65535
+                               if (data > MAX_U16_VAL) {
+                                       lbs_pr_debug(1, "Exceeds U16 value\n");
+                                       return -EINVAL;
+                               }
+                               adapter->locallisteninterval = data;
+                       }
+                       break;
+               case WLAN_TXCONTROL:
+                       ret = wlan_txcontrol(priv, wrq);        //adds for txcontrol ioctl
+                       break;
+
+               case WLAN_NULLPKTINTERVAL:
+                       ret = wlan_null_pkt_interval(priv, wrq);
+                       break;
+
+               default:
+                       ret = -EOPNOTSUPP;
+                       break;
+               }
+               break;
+
+       case WLAN_SETONEINT_GETNONE:
+               /* The first 4 bytes of req->ifr_data is sub-ioctl number
+                * after 4 bytes sits the payload.
+                */
+               subcmd = wrq->u.data.flags;     //from wpa_supplicant subcmd
+
+               if (!subcmd)
+                       subcmd = (int)req->ifr_data;    //from iwpriv subcmd
+
+               switch (subcmd) {
+               case WLAN_SUBCMD_SETRXANTENNA:  /* SETRXANTENNA */
+                       idata = SUBCMD_DATA(wrq);
+                       ret = setrxantenna(priv, idata);
+                       break;
+               case WLAN_SUBCMD_SETTXANTENNA:  /* SETTXANTENNA */
+                       idata = SUBCMD_DATA(wrq);
+                       ret = settxantenna(priv, idata);
+                       break;
+               case WLAN_SET_ATIM_WINDOW:
+                       adapter->atimwindow = SUBCMD_DATA(wrq);
+                       adapter->atimwindow = min_t(__u16, adapter->atimwindow, 50);
+                       break;
+               case WLANSETBCNAVG:
+                       adapter->bcn_avg_factor = SUBCMD_DATA(wrq);
+                       if (adapter->bcn_avg_factor == 0)
+                               adapter->bcn_avg_factor =
+                                   DEFAULT_BCN_AVG_FACTOR;
+                       if (adapter->bcn_avg_factor > DEFAULT_BCN_AVG_FACTOR)
+                               adapter->bcn_avg_factor =
+                                   DEFAULT_BCN_AVG_FACTOR;
+                       break;
+               case WLANSETDATAAVG:
+                       adapter->data_avg_factor = SUBCMD_DATA(wrq);
+                       if (adapter->data_avg_factor == 0)
+                               adapter->data_avg_factor =
+                                   DEFAULT_DATA_AVG_FACTOR;
+                       if (adapter->data_avg_factor > DEFAULT_DATA_AVG_FACTOR)
+                               adapter->data_avg_factor =
+                                   DEFAULT_DATA_AVG_FACTOR;
+                       break;
+               case WLANSETREGION:
+                       idata = SUBCMD_DATA(wrq);
+                       ret = wlan_set_region(priv, (u16) idata);
+                       break;
+
+               case WLAN_SET_LISTEN_INTERVAL:
+                       idata = SUBCMD_DATA(wrq);
+                       adapter->listeninterval = (u16) idata;
+                       break;
+
+               case WLAN_SET_MULTIPLE_DTIM:
+                       ret = wlan_set_multiple_dtim_ioctl(priv, req);
+                       break;
+
+               case WLANSETAUTHALG:
+                       ret = wlan_setauthalg_ioctl(priv, req);
+                       break;
+
+               case WLANSET8021XAUTHALG:
+                       ret = wlan_set8021xauthalg_ioctl(priv, req);
+                       break;
+
+               case WLANSETENCRYPTIONMODE:
+                       ret = wlan_setencryptionmode_ioctl(priv, req);
+                       break;
+
+               case WLAN_SET_LINKMODE:
+                       ret = wlan_set_linkmode_ioctl(priv, req);
+                       break;
+
+               case WLAN_SET_RADIOMODE:
+                       ret = wlan_set_radiomode_ioctl(priv, req);
+                       break;
+
+               case WLAN_SET_DEBUGMODE:
+                       ret = wlan_set_debugmode_ioctl(priv, req);
+                       break;
+
+               case WLAN_SUBCMD_MESH_SET_TTL:
+                       idata = SUBCMD_DATA(wrq);
+                       ret = wlan_mesh_set_ttl_ioctl(priv, idata);
+                       break;
+
+               default:
+                       ret = -EOPNOTSUPP;
+                       break;
+               }
+
+               break;
+
+       case WLAN_SETNONE_GETTWELVE_CHAR:       /* Get Antenna settings */
+               /*
+                * We've not used IW_PRIV_TYPE_FIXED so sub-ioctl number is
+                * in flags of iwreq structure, otherwise it will be in
+                * mode member of iwreq structure.
+                */
+               switch ((int)wrq->u.data.flags) {
+               case WLAN_SUBCMD_GETRXANTENNA:  /* Get Rx Antenna */
+                       ret = wlan_subcmd_getrxantenna_ioctl(priv, req);
+                       break;
+
+               case WLAN_SUBCMD_GETTXANTENNA:  /* Get Tx Antenna */
+                       ret = wlan_subcmd_gettxantenna_ioctl(priv, req);
+                       break;
+
+               case WLAN_GET_TSF:
+                       ret = wlan_get_tsf_ioctl(priv, wrq);
+                       break;
+               }
+               break;
+
+       case WLAN_SET128CHAR_GET128CHAR:
+               switch ((int)wrq->u.data.flags) {
+
+               case WLANSCAN_MODE:
+                       lbs_pr_debug(1, "Scan mode Ioctl\n");
+                       ret = wlan_scan_mode_ioctl(priv, wrq);
+                       break;
+
+               case WLAN_GET_ADHOC_STATUS:
+                       ret = wlan_get_adhoc_status_ioctl(priv, wrq);
+                       break;
+               case WLAN_SUBCMD_BT_ADD:
+                       ret = wlan_bt_add_ioctl(priv, req);
+                       break;
+               case WLAN_SUBCMD_BT_DEL:
+                       ret = wlan_bt_del_ioctl(priv, req);
+                       break;
+               case WLAN_SUBCMD_BT_LIST:
+                       ret = wlan_bt_list_ioctl(priv, req);
+                       break;
+               case WLAN_SUBCMD_FWT_ADD:
+                       ret = wlan_fwt_add_ioctl(priv, req);
+                       break;
+               case WLAN_SUBCMD_FWT_DEL:
+                       ret = wlan_fwt_del_ioctl(priv, req);
+                       break;
+               case WLAN_SUBCMD_FWT_LOOKUP:
+                       ret = wlan_fwt_lookup_ioctl(priv, req);
+                       break;
+               case WLAN_SUBCMD_FWT_LIST_NEIGHBOR:
+                       ret = wlan_fwt_list_neighbor_ioctl(priv, req);
+                       break;
+               case WLAN_SUBCMD_FWT_LIST:
+                       ret = wlan_fwt_list_ioctl(priv, req);
+                       break;
+               case WLAN_SUBCMD_FWT_LIST_ROUTE:
+                       ret = wlan_fwt_list_route_ioctl(priv, req);
+                       break;
+               }
+               break;
+
+       case WLAN_SETNONE_GETONEINT:
+               switch ((int)req->ifr_data) {
+               case WLANGETBCNAVG:
+                       pdata = (int *)wrq->u.name;
+                       *pdata = (int)adapter->bcn_avg_factor;
+                       break;
+
+               case WLANGETREGION:
+                       pdata = (int *)wrq->u.name;
+                       *pdata = (int)adapter->regioncode;
+                       break;
+
+               case WLAN_GET_LISTEN_INTERVAL:
+                       pdata = (int *)wrq->u.name;
+                       *pdata = (int)adapter->listeninterval;
+                       break;
+
+               case WLAN_GET_LINKMODE:
+                       req->ifr_data = (char *)((u32) adapter->linkmode);
+                       break;
+
+               case WLAN_GET_RADIOMODE:
+                       req->ifr_data = (char *)((u32) adapter->radiomode);
+                       break;
+
+               case WLAN_GET_DEBUGMODE:
+                       req->ifr_data = (char *)((u32) adapter->debugmode);
+                       break;
+
+               case WLAN_GET_MULTIPLE_DTIM:
+                       pdata = (int *)wrq->u.name;
+                       *pdata = (int)adapter->multipledtim;
+                       break;
+               case WLAN_GET_TX_RATE:
+                       ret = wlan_get_txrate_ioctl(priv, req);
+                       break;
+               case WLAN_SUBCMD_FWT_CLEANUP:   /* fwt_cleanup */
+                       ret = wlan_fwt_cleanup_ioctl(priv, req);
+                       break;
+
+               case WLAN_SUBCMD_FWT_TIME:      /* fwt_time */
+                       ret = wlan_fwt_time_ioctl(priv, req);
+                       break;
+
+               case WLAN_SUBCMD_MESH_GET_TTL:
+                       ret = wlan_mesh_get_ttl_ioctl(priv, req);
+                       break;
+
+               default:
+                       ret = -EOPNOTSUPP;
+
+               }
+
+               break;
+
+       case WLANGETLOG:
+               ret = wlan_do_getlog_ioctl(priv, wrq);
+               break;
+
+       case WLAN_SET_GET_SIXTEEN_INT:
+               switch ((int)wrq->u.data.flags) {
+               case WLAN_TPCCFG:
+                       {
+                               int data[5];
+                               struct cmd_ds_802_11_tpc_cfg cfg;
+                               memset(&cfg, 0, sizeof(cfg));
+                               if ((wrq->u.data.length > 1)
+                                   && (wrq->u.data.length != 5))
+                                       return -1;
+
+                               if (wrq->u.data.length == 0) {
+                                       cfg.action =
+                                           cpu_to_le16
+                                           (cmd_act_get);
+                               } else {
+                                       if (copy_from_user
+                                           (data, wrq->u.data.pointer,
+                                            sizeof(int) * 5)) {
+                                               lbs_pr_debug(1,
+                                                      "Copy from user failed\n");
+                                               return -EFAULT;
+                                       }
+
+                                       cfg.action =
+                                           cpu_to_le16
+                                           (cmd_act_set);
+                                       cfg.enable = data[0];
+                                       cfg.usesnr = data[1];
+                                       cfg.P0 = data[2];
+                                       cfg.P1 = data[3];
+                                       cfg.P2 = data[4];
+                               }
+
+                               ret =
+                                   libertas_prepare_and_send_command(priv,
+                                                         cmd_802_11_tpc_cfg,
+                                                         0,
+                                                         cmd_option_waitforrsp,
+                                                         0, (void *)&cfg);
+
+                               data[0] = cfg.enable;
+                               data[1] = cfg.usesnr;
+                               data[2] = cfg.P0;
+                               data[3] = cfg.P1;
+                               data[4] = cfg.P2;
+                               if (copy_to_user
+                                   (wrq->u.data.pointer, data,
+                                    sizeof(int) * 5)) {
+                                       lbs_pr_debug(1, "Copy to user failed\n");
+                                       return -EFAULT;
+                               }
+
+                               wrq->u.data.length = 5;
+                       }
+                       break;
+
+               case WLAN_POWERCFG:
+                       {
+                               int data[4];
+                               struct cmd_ds_802_11_pwr_cfg cfg;
+                               memset(&cfg, 0, sizeof(cfg));
+                               if ((wrq->u.data.length > 1)
+                                   && (wrq->u.data.length != 4))
+                                       return -1;
+                               if (wrq->u.data.length == 0) {
+                                       cfg.action =
+                                           cpu_to_le16
+                                           (cmd_act_get);
+                               } else {
+                                       if (copy_from_user
+                                           (data, wrq->u.data.pointer,
+                                            sizeof(int) * 4)) {
+                                               lbs_pr_debug(1,
+                                                      "Copy from user failed\n");
+                                               return -EFAULT;
+                                       }
+
+                                       cfg.action =
+                                           cpu_to_le16
+                                           (cmd_act_set);
+                                       cfg.enable = data[0];
+                                       cfg.PA_P0 = data[1];
+                                       cfg.PA_P1 = data[2];
+                                       cfg.PA_P2 = data[3];
+                               }
+                               ret =
+                                   libertas_prepare_and_send_command(priv,
+                                                         cmd_802_11_pwr_cfg,
+                                                         0,
+                                                         cmd_option_waitforrsp,
+                                                         0, (void *)&cfg);
+                               data[0] = cfg.enable;
+                               data[1] = cfg.PA_P0;
+                               data[2] = cfg.PA_P1;
+                               data[3] = cfg.PA_P2;
+                               if (copy_to_user
+                                   (wrq->u.data.pointer, data,
+                                    sizeof(int) * 4)) {
+                                       lbs_pr_debug(1, "Copy to user failed\n");
+                                       return -EFAULT;
+                               }
+
+                               wrq->u.data.length = 4;
+                       }
+                       break;
+               case WLAN_AUTO_FREQ_SET:
+                       {
+                               int data[3];
+                               struct cmd_ds_802_11_afc afc;
+                               memset(&afc, 0, sizeof(afc));
+                               if (wrq->u.data.length != 3)
+                                       return -1;
+                               if (copy_from_user
+                                   (data, wrq->u.data.pointer,
+                                    sizeof(int) * 3)) {
+                                       lbs_pr_debug(1, "Copy from user failed\n");
+                                       return -EFAULT;
+                               }
+                               afc.afc_auto = data[0];
+
+                               if (afc.afc_auto != 0) {
+                                       afc.threshold = data[1];
+                                       afc.period = data[2];
+                               } else {
+                                       afc.timing_offset = data[1];
+                                       afc.carrier_offset = data[2];
+                               }
+                               ret =
+                                   libertas_prepare_and_send_command(priv,
+                                                         cmd_802_11_set_afc,
+                                                         0,
+                                                         cmd_option_waitforrsp,
+                                                         0, (void *)&afc);
+                       }
+                       break;
+               case WLAN_AUTO_FREQ_GET:
+                       {
+                               int data[3];
+                               struct cmd_ds_802_11_afc afc;
+                               memset(&afc, 0, sizeof(afc));
+                               ret =
+                                   libertas_prepare_and_send_command(priv,
+                                                         cmd_802_11_get_afc,
+                                                         0,
+                                                         cmd_option_waitforrsp,
+                                                         0, (void *)&afc);
+                               data[0] = afc.afc_auto;
+                               data[1] = afc.timing_offset;
+                               data[2] = afc.carrier_offset;
+                               if (copy_to_user
+                                   (wrq->u.data.pointer, data,
+                                    sizeof(int) * 3)) {
+                                       lbs_pr_debug(1, "Copy to user failed\n");
+                                       return -EFAULT;
+                               }
+
+                               wrq->u.data.length = 3;
+                       }
+                       break;
+               case WLAN_SCANPROBES:
+                       {
+                               int data;
+                               if (wrq->u.data.length > 0) {
+                                       if (copy_from_user
+                                           (&data, wrq->u.data.pointer,
+                                            sizeof(int))) {
+                                               lbs_pr_debug(1,
+                                                      "Copy from user failed\n");
+                                               return -EFAULT;
+                                       }
+
+                                       adapter->scanprobes = data;
+                               } else {
+                                       data = adapter->scanprobes;
+                                       if (copy_to_user
+                                           (wrq->u.data.pointer, &data,
+                                            sizeof(int))) {
+                                               lbs_pr_debug(1,
+                                                      "Copy to user failed\n");
+                                               return -EFAULT;
+                                       }
+                               }
+                               wrq->u.data.length = 1;
+                       }
+                       break;
+               case WLAN_LED_GPIO_CTRL:
+                       {
+                               int i;
+                               int data[16];
+
+                               struct cmd_ds_802_11_led_ctrl ctrl;
+                               struct mrvlietypes_ledgpio *gpio =
+                                   (struct mrvlietypes_ledgpio *) ctrl.data;
+
+                               memset(&ctrl, 0, sizeof(ctrl));
+                               if (wrq->u.data.length > MAX_LEDS * 2)
+                                       return -ENOTSUPP;
+                               if ((wrq->u.data.length % 2) != 0)
+                                       return -ENOTSUPP;
+                               if (wrq->u.data.length == 0) {
+                                       ctrl.action =
+                                           cpu_to_le16
+                                           (cmd_act_get);
+                               } else {
+                                       if (copy_from_user
+                                           (data, wrq->u.data.pointer,
+                                            sizeof(int) *
+                                            wrq->u.data.length)) {
+                                               lbs_pr_debug(1,
+                                                      "Copy from user failed\n");
+                                               return -EFAULT;
+                                       }
+
+                                       ctrl.action =
+                                           cpu_to_le16
+                                           (cmd_act_set);
+                                       ctrl.numled = cpu_to_le16(0);
+                                       gpio->header.type =
+                                           cpu_to_le16(TLV_TYPE_LED_GPIO);
+                                       gpio->header.len = wrq->u.data.length;
+                                       for (i = 0; i < wrq->u.data.length;
+                                            i += 2) {
+                                               gpio->ledpin[i / 2].led =
+                                                   data[i];
+                                               gpio->ledpin[i / 2].pin =
+                                                   data[i + 1];
+                                       }
+                               }
+                               ret =
+                                   libertas_prepare_and_send_command(priv,
+                                                         cmd_802_11_led_gpio_ctrl,
+                                                         0,
+                                                         cmd_option_waitforrsp,
+                                                         0, (void *)&ctrl);
+                               for (i = 0; i < gpio->header.len; i += 2) {
+                                       data[i] = gpio->ledpin[i / 2].led;
+                                       data[i + 1] = gpio->ledpin[i / 2].pin;
+                               }
+                               if (copy_to_user(wrq->u.data.pointer, data,
+                                                sizeof(int) *
+                                                gpio->header.len)) {
+                                       lbs_pr_debug(1, "Copy to user failed\n");
+                                       return -EFAULT;
+                               }
+
+                               wrq->u.data.length = gpio->header.len;
+                       }
+                       break;
+               case WLAN_ADAPT_RATESET:
+                       ret = wlan_adapt_rateset(priv, wrq);
+                       break;
+               case WLAN_INACTIVITY_TIMEOUT:
+                       ret = wlan_inactivity_timeout(priv, wrq);
+                       break;
+               case WLANSNR:
+                       ret = wlan_get_snr(priv, wrq);
+                       break;
+               case WLAN_GET_RXINFO:
+                       ret = wlan_get_rxinfo(priv, wrq);
+               }
+               break;
+
+       default:
+               ret = -EINVAL;
+               break;
+       }
+       LEAVE();
+       return ret;
+}
+
+
diff --git a/drivers/net/wireless/libertas/join.c b/drivers/net/wireless/libertas/join.c
new file mode 100644 (file)
index 0000000..11682cb
--- /dev/null
@@ -0,0 +1,1055 @@
+/**
+  *  Functions implementing wlan infrastructure and adhoc join routines,
+  *  IOCTL handlers as well as command preperation and response routines
+  *  for sending adhoc start, adhoc join, and association commands
+  *  to the firmware.
+  */
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/wireless.h>
+
+#include <net/iw_handler.h>
+
+#include "host.h"
+#include "decl.h"
+#include "join.h"
+#include "dev.h"
+
+/**
+ *  @brief This function finds out the common rates between rate1 and rate2.
+ *
+ * It will fill common rates in rate1 as output if found.
+ *
+ * NOTE: Setting the MSB of the basic rates need to be taken
+ *   care, either before or after calling this function
+ *
+ *  @param adapter     A pointer to wlan_adapter structure
+ *  @param rate1       the buffer which keeps input and output
+ *  @param rate1_size  the size of rate1 buffer
+ *  @param rate2       the buffer which keeps rate2
+ *  @param rate2_size  the size of rate2 buffer.
+ *
+ *  @return            0 or -1
+ */
+static int get_common_rates(wlan_adapter * adapter, u8 * rate1,
+                           int rate1_size, u8 * rate2, int rate2_size)
+{
+       u8 *ptr = rate1;
+       int ret = 0;
+       u8 tmp[30];
+       int i;
+
+       memset(&tmp, 0, sizeof(tmp));
+       memcpy(&tmp, rate1, min_t(size_t, rate1_size, sizeof(tmp)));
+       memset(rate1, 0, rate1_size);
+
+       /* Mask the top bit of the original values */
+       for (i = 0; tmp[i] && i < sizeof(tmp); i++)
+               tmp[i] &= 0x7F;
+
+       for (i = 0; rate2[i] && i < rate2_size; i++) {
+               /* Check for Card Rate in tmp, excluding the top bit */
+               if (strchr(tmp, rate2[i] & 0x7F)) {
+                       /* values match, so copy the Card Rate to rate1 */
+                       *rate1++ = rate2[i];
+               }
+       }
+
+       lbs_dbg_hex("rate1 (AP) rates:", tmp, sizeof(tmp));
+       lbs_dbg_hex("rate2 (Card) rates:", rate2, rate2_size);
+       lbs_dbg_hex("Common rates:", ptr, rate1_size);
+       lbs_pr_debug(1, "Tx datarate is set to 0x%X\n", adapter->datarate);
+
+       if (!adapter->is_datarate_auto) {
+               while (*ptr) {
+                       if ((*ptr & 0x7f) == adapter->datarate) {
+                               ret = 0;
+                               goto done;
+                       }
+                       ptr++;
+               }
+               lbs_pr_alert( "Previously set fixed data rate %#x isn't "
+                      "compatible with the network.\n", adapter->datarate);
+
+               ret = -1;
+               goto done;
+       }
+
+       ret = 0;
+done:
+       return ret;
+}
+
+int libertas_send_deauth(wlan_private * priv)
+{
+       wlan_adapter *adapter = priv->adapter;
+       int ret = 0;
+
+       if (adapter->inframode == wlan802_11infrastructure &&
+           adapter->connect_status == libertas_connected)
+               ret = libertas_send_deauthentication(priv);
+       else
+               ret = -ENOTSUPP;
+
+       return ret;
+}
+
+int libertas_do_adhocstop_ioctl(wlan_private * priv)
+{
+       wlan_adapter *adapter = priv->adapter;
+       int ret = 0;
+
+       if (adapter->inframode == wlan802_11ibss &&
+           adapter->connect_status == libertas_connected)
+               ret = libertas_stop_adhoc_network(priv);
+       else
+               ret = -ENOTSUPP;
+
+       return ret;
+}
+
+/**
+ *  @brief Associate to a specific BSS discovered in a scan
+ *
+ *  @param priv      A pointer to wlan_private structure
+ *  @param pbssdesc  Pointer to the BSS descriptor to associate with.
+ *
+ *  @return          0-success, otherwise fail
+ */
+int wlan_associate(wlan_private * priv, struct bss_descriptor * pbssdesc)
+{
+       wlan_adapter *adapter = priv->adapter;
+       int ret;
+
+       ENTER();
+
+       ret = libertas_prepare_and_send_command(priv, cmd_802_11_authenticate,
+                                   0, cmd_option_waitforrsp,
+                                   0, pbssdesc->macaddress);
+
+       if (ret) {
+               LEAVE();
+               return ret;
+       }
+
+       /* set preamble to firmware */
+       if (adapter->capinfo.shortpreamble && pbssdesc->cap.shortpreamble)
+               adapter->preamble = cmd_type_short_preamble;
+       else
+               adapter->preamble = cmd_type_long_preamble;
+
+       libertas_set_radio_control(priv);
+
+       ret = libertas_prepare_and_send_command(priv, cmd_802_11_associate,
+                                   0, cmd_option_waitforrsp, 0, pbssdesc);
+
+       LEAVE();
+       return ret;
+}
+
+/**
+ *  @brief Start an Adhoc Network
+ *
+ *  @param priv         A pointer to wlan_private structure
+ *  @param adhocssid    The ssid of the Adhoc Network
+ *  @return             0--success, -1--fail
+ */
+int libertas_start_adhoc_network(wlan_private * priv, struct WLAN_802_11_SSID *adhocssid)
+{
+       wlan_adapter *adapter = priv->adapter;
+       int ret = 0;
+
+       adapter->adhoccreate = 1;
+
+       if (!adapter->capinfo.shortpreamble) {
+               lbs_pr_debug(1, "AdhocStart: Long preamble\n");
+               adapter->preamble = cmd_type_long_preamble;
+       } else {
+               lbs_pr_debug(1, "AdhocStart: Short preamble\n");
+               adapter->preamble = cmd_type_short_preamble;
+       }
+
+       libertas_set_radio_control(priv);
+
+       lbs_pr_debug(1, "Adhoc channel = %d\n", adapter->adhocchannel);
+       lbs_pr_debug(1, "curbssparams.channel = %d\n",
+              adapter->curbssparams.channel);
+       lbs_pr_debug(1, "curbssparams.band = %d\n", adapter->curbssparams.band);
+
+       ret = libertas_prepare_and_send_command(priv, cmd_802_11_ad_hoc_start,
+                                   0, cmd_option_waitforrsp, 0, adhocssid);
+
+       return ret;
+}
+
+/**
+ *  @brief Join an adhoc network found in a previous scan
+ *
+ *  @param priv         A pointer to wlan_private structure
+ *  @param pbssdesc     Pointer to a BSS descriptor found in a previous scan
+ *                      to attempt to join
+ *
+ *  @return             0--success, -1--fail
+ */
+int libertas_join_adhoc_network(wlan_private * priv, struct bss_descriptor * pbssdesc)
+{
+       wlan_adapter *adapter = priv->adapter;
+       int ret = 0;
+
+       lbs_pr_debug(1, "libertas_join_adhoc_network: CurBss.ssid =%s\n",
+              adapter->curbssparams.ssid.ssid);
+       lbs_pr_debug(1, "libertas_join_adhoc_network: CurBss.ssid_len =%u\n",
+              adapter->curbssparams.ssid.ssidlength);
+       lbs_pr_debug(1, "libertas_join_adhoc_network: ssid =%s\n", pbssdesc->ssid.ssid);
+       lbs_pr_debug(1, "libertas_join_adhoc_network: ssid len =%u\n",
+              pbssdesc->ssid.ssidlength);
+
+       /* check if the requested SSID is already joined */
+       if (adapter->curbssparams.ssid.ssidlength
+           && !libertas_SSID_cmp(&pbssdesc->ssid, &adapter->curbssparams.ssid)
+           && (adapter->curbssparams.bssdescriptor.inframode ==
+               wlan802_11ibss)) {
+
+        lbs_pr_debug(1,
+                      "ADHOC_J_CMD: New ad-hoc SSID is the same as current, "
+                      "not attempting to re-join");
+
+               return -1;
+       }
+
+       /*Use shortpreamble only when both creator and card supports
+          short preamble */
+       if (!pbssdesc->cap.shortpreamble || !adapter->capinfo.shortpreamble) {
+               lbs_pr_debug(1, "AdhocJoin: Long preamble\n");
+               adapter->preamble = cmd_type_long_preamble;
+       } else {
+               lbs_pr_debug(1, "AdhocJoin: Short preamble\n");
+               adapter->preamble = cmd_type_short_preamble;
+       }
+
+       libertas_set_radio_control(priv);
+
+       lbs_pr_debug(1, "curbssparams.channel = %d\n",
+              adapter->curbssparams.channel);
+       lbs_pr_debug(1, "curbssparams.band = %c\n", adapter->curbssparams.band);
+
+       adapter->adhoccreate = 0;
+
+       ret = libertas_prepare_and_send_command(priv, cmd_802_11_ad_hoc_join,
+                                   0, cmd_option_waitforrsp,
+                                   OID_802_11_SSID, pbssdesc);
+
+       return ret;
+}
+
+int libertas_stop_adhoc_network(wlan_private * priv)
+{
+       return libertas_prepare_and_send_command(priv, cmd_802_11_ad_hoc_stop,
+                                    0, cmd_option_waitforrsp, 0, NULL);
+}
+
+/**
+ *  @brief Send Deauthentication Request
+ *
+ *  @param priv      A pointer to wlan_private structure
+ *  @return          0--success, -1--fail
+ */
+int libertas_send_deauthentication(wlan_private * priv)
+{
+       return libertas_prepare_and_send_command(priv, cmd_802_11_deauthenticate,
+                                    0, cmd_option_waitforrsp, 0, NULL);
+}
+
+/**
+ *  @brief Set Idle Off
+ *
+ *  @param priv         A pointer to wlan_private structure
+ *  @return             0 --success, otherwise fail
+ */
+int libertas_idle_off(wlan_private * priv)
+{
+       wlan_adapter *adapter = priv->adapter;
+       int ret = 0;
+       const u8 zeromac[] = { 0, 0, 0, 0, 0, 0 };
+       int i;
+
+       ENTER();
+
+       if (adapter->connect_status == libertas_disconnected) {
+               if (adapter->inframode == wlan802_11infrastructure) {
+                       if (memcmp(adapter->previousbssid, zeromac,
+                                  sizeof(zeromac)) != 0) {
+
+                               lbs_pr_debug(1, "Previous SSID = %s\n",
+                                      adapter->previousssid.ssid);
+                               lbs_pr_debug(1, "Previous BSSID = "
+                                      "%02x:%02x:%02x:%02x:%02x:%02x:\n",
+                                      adapter->previousbssid[0],
+                                      adapter->previousbssid[1],
+                                      adapter->previousbssid[2],
+                                      adapter->previousbssid[3],
+                                      adapter->previousbssid[4],
+                                      adapter->previousbssid[5]);
+
+                               i = libertas_find_SSID_in_list(adapter,
+                                                  &adapter->previousssid,
+                                                  adapter->previousbssid,
+                                                  adapter->inframode);
+
+                               if (i < 0) {
+                                       libertas_send_specific_BSSID_scan(priv,
+                                                             adapter->
+                                                             previousbssid,
+                                                             1);
+                                       i = libertas_find_SSID_in_list(adapter,
+                                                          &adapter->
+                                                          previousssid,
+                                                          adapter->
+                                                          previousbssid,
+                                                          adapter->
+                                                          inframode);
+                               }
+
+                               if (i < 0) {
+                                       /* If the BSSID could not be found, try just the SSID */
+                                       i = libertas_find_SSID_in_list(adapter,
+                                                          &adapter->
+                                                          previousssid, NULL,
+                                                          adapter->
+                                                          inframode);
+                               }
+
+                               if (i < 0) {
+                                       libertas_send_specific_SSID_scan(priv,
+                                                            &adapter->
+                                                            previousssid,
+                                                            1);
+                                       i = libertas_find_SSID_in_list(adapter,
+                                                          &adapter->
+                                                          previousssid, NULL,
+                                                          adapter->
+                                                          inframode);
+                               }
+
+                               if (i >= 0) {
+                                       ret =
+                                           wlan_associate(priv,
+                                                          &adapter->
+                                                          scantable[i]);
+                               }
+                       }
+               } else if (adapter->inframode == wlan802_11ibss) {
+                       ret = libertas_prepare_and_send_command(priv,
+                                                   cmd_802_11_ad_hoc_start,
+                                                   0,
+                                                   cmd_option_waitforrsp,
+                                                   0, &adapter->previousssid);
+               }
+       }
+       /* else it is connected */
+
+       lbs_pr_debug(1, "\nwlanidle is off");
+       LEAVE();
+       return ret;
+}
+
+/**
+ *  @brief Set Idle On
+ *
+ *  @param priv         A pointer to wlan_private structure
+ *  @return             0 --success, otherwise fail
+ */
+int libertas_idle_on(wlan_private * priv)
+{
+       wlan_adapter *adapter = priv->adapter;
+       int ret = 0;
+
+       if (adapter->connect_status == libertas_connected) {
+               if (adapter->inframode == wlan802_11infrastructure) {
+                       lbs_pr_debug(1, "Previous SSID = %s\n",
+                              adapter->previousssid.ssid);
+                       memmove(&adapter->previousssid,
+                               &adapter->curbssparams.ssid,
+                               sizeof(struct WLAN_802_11_SSID));
+                       libertas_send_deauth(priv);
+
+               } else if (adapter->inframode == wlan802_11ibss) {
+                       ret = libertas_stop_adhoc_network(priv);
+               }
+
+       }
+
+       lbs_pr_debug(1, "\nwlanidle is on");
+
+       return ret;
+}
+
+/**
+ *  @brief This function prepares command of authenticate.
+ *
+ *  @param priv      A pointer to wlan_private structure
+ *  @param cmd       A pointer to cmd_ds_command structure
+ *  @param pdata_buf Void cast of pointer to a BSSID to authenticate with
+ *
+ *  @return         0 or -1
+ */
+int libertas_cmd_80211_authenticate(wlan_private * priv,
+                                struct cmd_ds_command *cmd,
+                                void *pdata_buf)
+{
+       wlan_adapter *adapter = priv->adapter;
+       struct cmd_ds_802_11_authenticate *pauthenticate =
+           &cmd->params.auth;
+       u8 *bssid = pdata_buf;
+
+       cmd->command = cpu_to_le16(cmd_802_11_authenticate);
+       cmd->size =
+           cpu_to_le16(sizeof(struct cmd_ds_802_11_authenticate)
+                            + S_DS_GEN);
+
+       pauthenticate->authtype = adapter->secinfo.authmode;
+       memcpy(pauthenticate->macaddr, bssid, ETH_ALEN);
+
+       lbs_pr_debug(1, "AUTH_CMD: Bssid is : %x:%x:%x:%x:%x:%x\n",
+              bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]);
+
+       return 0;
+}
+
+int libertas_cmd_80211_deauthenticate(wlan_private * priv,
+                                  struct cmd_ds_command *cmd)
+{
+       wlan_adapter *adapter = priv->adapter;
+       struct cmd_ds_802_11_deauthenticate *dauth = &cmd->params.deauth;
+
+       ENTER();
+
+       cmd->command = cpu_to_le16(cmd_802_11_deauthenticate);
+       cmd->size =
+           cpu_to_le16(sizeof(struct cmd_ds_802_11_deauthenticate) +
+                            S_DS_GEN);
+
+       /* set AP MAC address */
+       memmove(dauth->macaddr, adapter->curbssparams.bssid,
+               ETH_ALEN);
+
+       /* Reason code 3 = Station is leaving */
+#define REASON_CODE_STA_LEAVING 3
+       dauth->reasoncode = cpu_to_le16(REASON_CODE_STA_LEAVING);
+
+       LEAVE();
+       return 0;
+}
+
+int libertas_cmd_80211_associate(wlan_private * priv,
+                             struct cmd_ds_command *cmd, void *pdata_buf)
+{
+       wlan_adapter *adapter = priv->adapter;
+       struct cmd_ds_802_11_associate *passo = &cmd->params.associate;
+       int ret = 0;
+       struct bss_descriptor *pbssdesc;
+       u8 *card_rates;
+       u8 *pos;
+       int card_rates_size;
+       u16 tmpcap;
+       struct mrvlietypes_ssidparamset *ssid;
+       struct mrvlietypes_phyparamset *phy;
+       struct mrvlietypes_ssparamset *ss;
+       struct mrvlietypes_ratesparamset *rates;
+       struct mrvlietypes_rsnparamset *rsn;
+
+       ENTER();
+
+       pbssdesc = pdata_buf;
+       pos = (u8 *) passo;
+
+       if (!adapter) {
+               ret = -1;
+               goto done;
+       }
+
+       cmd->command = cpu_to_le16(cmd_802_11_associate);
+
+       /* Save so we know which BSS Desc to use in the response handler */
+       adapter->pattemptedbssdesc = pbssdesc;
+
+       memcpy(passo->peerstaaddr,
+              pbssdesc->macaddress, sizeof(passo->peerstaaddr));
+       pos += sizeof(passo->peerstaaddr);
+
+       /* set the listen interval */
+       passo->listeninterval = adapter->listeninterval;
+
+       pos += sizeof(passo->capinfo);
+       pos += sizeof(passo->listeninterval);
+       pos += sizeof(passo->bcnperiod);
+       pos += sizeof(passo->dtimperiod);
+
+       ssid = (struct mrvlietypes_ssidparamset *) pos;
+       ssid->header.type = cpu_to_le16(TLV_TYPE_SSID);
+       ssid->header.len = pbssdesc->ssid.ssidlength;
+       memcpy(ssid->ssid, pbssdesc->ssid.ssid, ssid->header.len);
+       pos += sizeof(ssid->header) + ssid->header.len;
+       ssid->header.len = cpu_to_le16(ssid->header.len);
+
+       phy = (struct mrvlietypes_phyparamset *) pos;
+       phy->header.type = cpu_to_le16(TLV_TYPE_PHY_DS);
+       phy->header.len = sizeof(phy->fh_ds.dsparamset);
+       memcpy(&phy->fh_ds.dsparamset,
+              &pbssdesc->phyparamset.dsparamset.currentchan,
+              sizeof(phy->fh_ds.dsparamset));
+       pos += sizeof(phy->header) + phy->header.len;
+       phy->header.len = cpu_to_le16(phy->header.len);
+
+       ss = (struct mrvlietypes_ssparamset *) pos;
+       ss->header.type = cpu_to_le16(TLV_TYPE_CF);
+       ss->header.len = sizeof(ss->cf_ibss.cfparamset);
+       pos += sizeof(ss->header) + ss->header.len;
+       ss->header.len = cpu_to_le16(ss->header.len);
+
+       rates = (struct mrvlietypes_ratesparamset *) pos;
+       rates->header.type = cpu_to_le16(TLV_TYPE_RATES);
+
+       memcpy(&rates->rates, &pbssdesc->libertas_supported_rates, WLAN_SUPPORTED_RATES);
+
+       card_rates = libertas_supported_rates;
+       card_rates_size = sizeof(libertas_supported_rates);
+
+       if (get_common_rates(adapter, rates->rates, WLAN_SUPPORTED_RATES,
+                            card_rates, card_rates_size)) {
+               ret = -1;
+               goto done;
+       }
+
+       rates->header.len = min_t(size_t, strlen(rates->rates), WLAN_SUPPORTED_RATES);
+       adapter->curbssparams.numofrates = rates->header.len;
+
+       pos += sizeof(rates->header) + rates->header.len;
+       rates->header.len = cpu_to_le16(rates->header.len);
+
+       if (adapter->secinfo.WPAenabled || adapter->secinfo.WPA2enabled) {
+               rsn = (struct mrvlietypes_rsnparamset *) pos;
+               rsn->header.type = (u16) adapter->wpa_ie[0];    /* WPA_IE or WPA2_IE */
+               rsn->header.type = cpu_to_le16(rsn->header.type);
+               rsn->header.len = (u16) adapter->wpa_ie[1];
+               memcpy(rsn->rsnie, &adapter->wpa_ie[2], rsn->header.len);
+               lbs_dbg_hex("ASSOC_CMD: RSN IE", (u8 *) rsn,
+                       sizeof(rsn->header) + rsn->header.len);
+               pos += sizeof(rsn->header) + rsn->header.len;
+               rsn->header.len = cpu_to_le16(rsn->header.len);
+       }
+
+       /* update curbssparams */
+       adapter->curbssparams.channel =
+           (pbssdesc->phyparamset.dsparamset.currentchan);
+
+       /* Copy the infra. association rates into Current BSS state structure */
+       memcpy(&adapter->curbssparams.datarates, &rates->rates,
+              min_t(size_t, sizeof(adapter->curbssparams.datarates), rates->header.len));
+
+       lbs_pr_debug(1, "ASSOC_CMD: rates->header.len = %d\n", rates->header.len);
+
+       /* set IBSS field */
+       if (pbssdesc->inframode == wlan802_11infrastructure) {
+#define CAPINFO_ESS_MODE 1
+               passo->capinfo.ess = CAPINFO_ESS_MODE;
+       }
+
+       if (libertas_parse_dnld_countryinfo_11d(priv)) {
+               ret = -1;
+               goto done;
+       }
+
+       cmd->size = cpu_to_le16((u16) (pos - (u8 *) passo) + S_DS_GEN);
+
+       /* set the capability info at last */
+       memcpy(&tmpcap, &pbssdesc->cap, sizeof(passo->capinfo));
+       tmpcap &= CAPINFO_MASK;
+       lbs_pr_debug(1, "ASSOC_CMD: tmpcap=%4X CAPINFO_MASK=%4X\n",
+              tmpcap, CAPINFO_MASK);
+       tmpcap = cpu_to_le16(tmpcap);
+       memcpy(&passo->capinfo, &tmpcap, sizeof(passo->capinfo));
+
+      done:
+       LEAVE();
+       return ret;
+}
+
+int libertas_cmd_80211_ad_hoc_start(wlan_private * priv,
+                                struct cmd_ds_command *cmd, void *pssid)
+{
+       wlan_adapter *adapter = priv->adapter;
+       struct cmd_ds_802_11_ad_hoc_start *adhs = &cmd->params.ads;
+       int ret = 0;
+       int cmdappendsize = 0;
+       int i;
+       u16 tmpcap;
+       struct bss_descriptor *pbssdesc;
+       struct WLAN_802_11_SSID *ssid = pssid;
+
+       ENTER();
+
+       if (!adapter) {
+               ret = -1;
+               goto done;
+       }
+
+       cmd->command = cpu_to_le16(cmd_802_11_ad_hoc_start);
+
+       pbssdesc = &adapter->curbssparams.bssdescriptor;
+       adapter->pattemptedbssdesc = pbssdesc;
+
+       /*
+        * Fill in the parameters for 2 data structures:
+        *   1. cmd_ds_802_11_ad_hoc_start command
+        *   2. adapter->scantable[i]
+        *
+        * Driver will fill up SSID, bsstype,IBSS param, Physical Param,
+        *   probe delay, and cap info.
+        *
+        * Firmware will fill up beacon period, DTIM, Basic rates
+        *   and operational rates.
+        */
+
+       memset(adhs->SSID, 0, IW_ESSID_MAX_SIZE);
+
+       memcpy(adhs->SSID, ssid->ssid, ssid->ssidlength);
+
+       lbs_pr_debug(1, "ADHOC_S_CMD: SSID = %s\n", adhs->SSID);
+
+       memset(pbssdesc->ssid.ssid, 0, IW_ESSID_MAX_SIZE);
+       memcpy(pbssdesc->ssid.ssid, ssid->ssid, ssid->ssidlength);
+
+       pbssdesc->ssid.ssidlength = ssid->ssidlength;
+
+       /* set the BSS type */
+       adhs->bsstype = cmd_bss_type_ibss;
+       pbssdesc->inframode = wlan802_11ibss;
+       adhs->beaconperiod = adapter->beaconperiod;
+
+       /* set Physical param set */
+#define DS_PARA_IE_ID   3
+#define DS_PARA_IE_LEN  1
+
+       adhs->phyparamset.dsparamset.elementid = DS_PARA_IE_ID;
+       adhs->phyparamset.dsparamset.len = DS_PARA_IE_LEN;
+
+       WARN_ON(!adapter->adhocchannel);
+
+       lbs_pr_debug(1, "ADHOC_S_CMD: Creating ADHOC on channel %d\n",
+              adapter->adhocchannel);
+
+       adapter->curbssparams.channel = adapter->adhocchannel;
+
+       pbssdesc->channel = adapter->adhocchannel;
+       adhs->phyparamset.dsparamset.currentchan = adapter->adhocchannel;
+
+       memcpy(&pbssdesc->phyparamset,
+              &adhs->phyparamset, sizeof(union ieeetypes_phyparamset));
+
+       /* set IBSS param set */
+#define IBSS_PARA_IE_ID   6
+#define IBSS_PARA_IE_LEN  2
+
+       adhs->ssparamset.ibssparamset.elementid = IBSS_PARA_IE_ID;
+       adhs->ssparamset.ibssparamset.len = IBSS_PARA_IE_LEN;
+       adhs->ssparamset.ibssparamset.atimwindow = adapter->atimwindow;
+       memcpy(&pbssdesc->ssparamset,
+              &adhs->ssparamset, sizeof(union IEEEtypes_ssparamset));
+
+       /* set capability info */
+       adhs->cap.ess = 0;
+       adhs->cap.ibss = 1;
+       pbssdesc->cap.ibss = 1;
+
+       /* probedelay */
+       adhs->probedelay = cpu_to_le16(cmd_scan_probe_delay_time);
+
+       /* set up privacy in adapter->scantable[i] */
+       if (adapter->secinfo.WEPstatus == wlan802_11WEPenabled) {
+
+#define AD_HOC_CAP_PRIVACY_ON 1
+               lbs_pr_debug(1, "ADHOC_S_CMD: WEPstatus set, privacy to WEP\n");
+               pbssdesc->privacy = wlan802_11privfilter8021xWEP;
+               adhs->cap.privacy = AD_HOC_CAP_PRIVACY_ON;
+       } else {
+               lbs_pr_debug(1, "ADHOC_S_CMD: WEPstatus NOT set, Setting "
+                      "privacy to ACCEPT ALL\n");
+               pbssdesc->privacy = wlan802_11privfilteracceptall;
+       }
+
+       memset(adhs->datarate, 0, sizeof(adhs->datarate));
+
+       if (adapter->adhoc_grate_enabled) {
+               memcpy(adhs->datarate, libertas_adhoc_rates_g,
+                      min(sizeof(adhs->datarate), sizeof(libertas_adhoc_rates_g)));
+       } else {
+               memcpy(adhs->datarate, libertas_adhoc_rates_b,
+                      min(sizeof(adhs->datarate), sizeof(libertas_adhoc_rates_b)));
+       }
+
+       /* Find the last non zero */
+       for (i = 0; i < sizeof(adhs->datarate) && adhs->datarate[i]; i++) ;
+
+       adapter->curbssparams.numofrates = i;
+
+       /* Copy the ad-hoc creating rates into Current BSS state structure */
+       memcpy(&adapter->curbssparams.datarates,
+              &adhs->datarate, adapter->curbssparams.numofrates);
+
+       lbs_pr_debug(1, "ADHOC_S_CMD: rates=%02x %02x %02x %02x \n",
+              adhs->datarate[0], adhs->datarate[1],
+              adhs->datarate[2], adhs->datarate[3]);
+
+       lbs_pr_debug(1, "ADHOC_S_CMD: AD HOC Start command is ready\n");
+
+       if (libertas_create_dnld_countryinfo_11d(priv)) {
+               lbs_pr_debug(1, "ADHOC_S_CMD: dnld_countryinfo_11d failed\n");
+               ret = -1;
+               goto done;
+       }
+
+       cmd->size =
+           cpu_to_le16(sizeof(struct cmd_ds_802_11_ad_hoc_start)
+                            + S_DS_GEN + cmdappendsize);
+
+       memcpy(&tmpcap, &adhs->cap, sizeof(u16));
+       tmpcap = cpu_to_le16(tmpcap);
+       memcpy(&adhs->cap, &tmpcap, sizeof(u16));
+
+       ret = 0;
+done:
+       LEAVE();
+       return ret;
+}
+
+int libertas_cmd_80211_ad_hoc_stop(wlan_private * priv,
+                               struct cmd_ds_command *cmd)
+{
+       cmd->command = cpu_to_le16(cmd_802_11_ad_hoc_stop);
+       cmd->size = cpu_to_le16(S_DS_GEN);
+
+       return 0;
+}
+
+int libertas_cmd_80211_ad_hoc_join(wlan_private * priv,
+                               struct cmd_ds_command *cmd, void *pdata_buf)
+{
+       wlan_adapter *adapter = priv->adapter;
+       struct cmd_ds_802_11_ad_hoc_join *padhocjoin = &cmd->params.adj;
+       struct bss_descriptor *pbssdesc = pdata_buf;
+       int cmdappendsize = 0;
+       int ret = 0;
+       u8 *card_rates;
+       int card_rates_size;
+       u16 tmpcap;
+       int i;
+
+       ENTER();
+
+       adapter->pattemptedbssdesc = pbssdesc;
+
+       cmd->command = cpu_to_le16(cmd_802_11_ad_hoc_join);
+
+       padhocjoin->bssdescriptor.bsstype = cmd_bss_type_ibss;
+
+       padhocjoin->bssdescriptor.beaconperiod = pbssdesc->beaconperiod;
+
+       memcpy(&padhocjoin->bssdescriptor.BSSID,
+              &pbssdesc->macaddress, ETH_ALEN);
+
+       memcpy(&padhocjoin->bssdescriptor.SSID,
+              &pbssdesc->ssid.ssid, pbssdesc->ssid.ssidlength);
+
+       memcpy(&padhocjoin->bssdescriptor.phyparamset,
+              &pbssdesc->phyparamset, sizeof(union ieeetypes_phyparamset));
+
+       memcpy(&padhocjoin->bssdescriptor.ssparamset,
+              &pbssdesc->ssparamset, sizeof(union IEEEtypes_ssparamset));
+
+       memcpy(&tmpcap, &pbssdesc->cap, sizeof(struct ieeetypes_capinfo));
+       tmpcap &= CAPINFO_MASK;
+
+       lbs_pr_debug(1, "ADHOC_J_CMD: tmpcap=%4X CAPINFO_MASK=%4X\n",
+              tmpcap, CAPINFO_MASK);
+       memcpy(&padhocjoin->bssdescriptor.cap, &tmpcap,
+              sizeof(struct ieeetypes_capinfo));
+
+       /* information on BSSID descriptor passed to FW */
+    lbs_pr_debug(1,
+              "ADHOC_J_CMD: BSSID = %2x-%2x-%2x-%2x-%2x-%2x, SSID = %s\n",
+              padhocjoin->bssdescriptor.BSSID[0],
+              padhocjoin->bssdescriptor.BSSID[1],
+              padhocjoin->bssdescriptor.BSSID[2],
+              padhocjoin->bssdescriptor.BSSID[3],
+              padhocjoin->bssdescriptor.BSSID[4],
+              padhocjoin->bssdescriptor.BSSID[5],
+              padhocjoin->bssdescriptor.SSID);
+
+       lbs_pr_debug(1, "ADHOC_J_CMD: Data Rate = %x\n",
+              (u32) padhocjoin->bssdescriptor.datarates);
+
+       /* failtimeout */
+       padhocjoin->failtimeout = cpu_to_le16(MRVDRV_ASSOCIATION_TIME_OUT);
+
+       /* probedelay */
+       padhocjoin->probedelay =
+           cpu_to_le16(cmd_scan_probe_delay_time);
+
+       /* Copy Data rates from the rates recorded in scan response */
+       memset(padhocjoin->bssdescriptor.datarates, 0,
+              sizeof(padhocjoin->bssdescriptor.datarates));
+       memcpy(padhocjoin->bssdescriptor.datarates, pbssdesc->datarates,
+              min(sizeof(padhocjoin->bssdescriptor.datarates),
+                  sizeof(pbssdesc->datarates)));
+
+       card_rates = libertas_supported_rates;
+       card_rates_size = sizeof(libertas_supported_rates);
+
+       adapter->curbssparams.channel = pbssdesc->channel;
+
+       if (get_common_rates(adapter, padhocjoin->bssdescriptor.datarates,
+                            sizeof(padhocjoin->bssdescriptor.datarates),
+                            card_rates, card_rates_size)) {
+               lbs_pr_debug(1, "ADHOC_J_CMD: get_common_rates returns error.\n");
+               ret = -1;
+               goto done;
+       }
+
+       /* Find the last non zero */
+       for (i = 0; i < sizeof(padhocjoin->bssdescriptor.datarates)
+            && padhocjoin->bssdescriptor.datarates[i]; i++) ;
+
+       adapter->curbssparams.numofrates = i;
+
+       /*
+        * Copy the adhoc joining rates to Current BSS State structure
+        */
+       memcpy(adapter->curbssparams.datarates,
+              padhocjoin->bssdescriptor.datarates,
+              adapter->curbssparams.numofrates);
+
+       padhocjoin->bssdescriptor.ssparamset.ibssparamset.atimwindow =
+           cpu_to_le16(pbssdesc->atimwindow);
+
+       if (adapter->secinfo.WEPstatus == wlan802_11WEPenabled) {
+               padhocjoin->bssdescriptor.cap.privacy = AD_HOC_CAP_PRIVACY_ON;
+       }
+
+       if (adapter->psmode == wlan802_11powermodemax_psp) {
+               /* wake up first */
+               enum WLAN_802_11_POWER_MODE Localpsmode;
+
+               Localpsmode = wlan802_11powermodecam;
+               ret = libertas_prepare_and_send_command(priv,
+                                           cmd_802_11_ps_mode,
+                                           cmd_act_set,
+                                           0, 0, &Localpsmode);
+
+               if (ret) {
+                       ret = -1;
+                       goto done;
+               }
+       }
+
+       if (libertas_parse_dnld_countryinfo_11d(priv)) {
+               ret = -1;
+               goto done;
+       }
+
+       cmd->size =
+           cpu_to_le16(sizeof(struct cmd_ds_802_11_ad_hoc_join)
+                            + S_DS_GEN + cmdappendsize);
+
+       memcpy(&tmpcap, &padhocjoin->bssdescriptor.cap,
+              sizeof(struct ieeetypes_capinfo));
+       tmpcap = cpu_to_le16(tmpcap);
+
+       memcpy(&padhocjoin->bssdescriptor.cap,
+              &tmpcap, sizeof(struct ieeetypes_capinfo));
+
+      done:
+       LEAVE();
+       return ret;
+}
+
+int libertas_ret_80211_associate(wlan_private * priv,
+                             struct cmd_ds_command *resp)
+{
+       wlan_adapter *adapter = priv->adapter;
+       int ret = 0;
+       union iwreq_data wrqu;
+       struct ieeetypes_assocrsp *passocrsp;
+       struct bss_descriptor *pbssdesc;
+
+       ENTER();
+
+       passocrsp = (struct ieeetypes_assocrsp *) & resp->params;
+
+       if (passocrsp->statuscode) {
+
+               libertas_mac_event_disconnected(priv);
+
+        lbs_pr_debug(1,
+                      "ASSOC_RESP: Association failed, status code = %d\n",
+                      passocrsp->statuscode);
+
+               ret = -1;
+               goto done;
+       }
+
+       lbs_dbg_hex("ASSOC_RESP:", (void *)&resp->params,
+               le16_to_cpu(resp->size) - S_DS_GEN);
+
+       /* Send a Media Connected event, according to the Spec */
+       adapter->connect_status = libertas_connected;
+
+       /* Set the attempted BSSID Index to current */
+       pbssdesc = adapter->pattemptedbssdesc;
+
+       lbs_pr_debug(1, "ASSOC_RESP: %s\n", pbssdesc->ssid.ssid);
+
+       /* Set the new SSID to current SSID */
+       memcpy(&adapter->curbssparams.ssid,
+              &pbssdesc->ssid, sizeof(struct WLAN_802_11_SSID));
+
+       /* Set the new BSSID (AP's MAC address) to current BSSID */
+       memcpy(adapter->curbssparams.bssid,
+              pbssdesc->macaddress, ETH_ALEN);
+
+       /* Make a copy of current BSSID descriptor */
+       memcpy(&adapter->curbssparams.bssdescriptor,
+              pbssdesc, sizeof(struct bss_descriptor));
+
+       lbs_pr_debug(1, "ASSOC_RESP: currentpacketfilter is %x\n",
+              adapter->currentpacketfilter);
+
+       adapter->SNR[TYPE_RXPD][TYPE_AVG] = 0;
+       adapter->NF[TYPE_RXPD][TYPE_AVG] = 0;
+
+       memset(adapter->rawSNR, 0x00, sizeof(adapter->rawSNR));
+       memset(adapter->rawNF, 0x00, sizeof(adapter->rawNF));
+       adapter->nextSNRNF = 0;
+       adapter->numSNRNF = 0;
+
+       netif_carrier_on(priv->wlan_dev.netdev);
+       netif_wake_queue(priv->wlan_dev.netdev);
+
+       lbs_pr_debug(1, "ASSOC_RESP: Associated \n");
+
+       memcpy(wrqu.ap_addr.sa_data, adapter->curbssparams.bssid, ETH_ALEN);
+       wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+       wireless_send_event(priv->wlan_dev.netdev, SIOCGIWAP, &wrqu, NULL);
+
+      done:
+       LEAVE();
+       return ret;
+}
+
+int libertas_ret_80211_disassociate(wlan_private * priv,
+                                struct cmd_ds_command *resp)
+{
+       ENTER();
+
+       libertas_mac_event_disconnected(priv);
+
+       LEAVE();
+       return 0;
+}
+
+int libertas_ret_80211_ad_hoc_start(wlan_private * priv,
+                                struct cmd_ds_command *resp)
+{
+       wlan_adapter *adapter = priv->adapter;
+       int ret = 0;
+       u16 command = le16_to_cpu(resp->command);
+       u16 result = le16_to_cpu(resp->result);
+       struct cmd_ds_802_11_ad_hoc_result *padhocresult;
+       union iwreq_data wrqu;
+       struct bss_descriptor *pbssdesc;
+
+       ENTER();
+
+       padhocresult = &resp->params.result;
+
+       lbs_pr_debug(1, "ADHOC_S_RESP: size = %d\n", le16_to_cpu(resp->size));
+       lbs_pr_debug(1, "ADHOC_S_RESP: command = %x\n", command);
+       lbs_pr_debug(1, "ADHOC_S_RESP: result = %x\n", result);
+
+       pbssdesc = adapter->pattemptedbssdesc;
+
+       /*
+        * Join result code 0 --> SUCCESS
+        */
+       if (result) {
+               lbs_pr_debug(1, "ADHOC_RESP failed\n");
+               if (adapter->connect_status == libertas_connected) {
+                       libertas_mac_event_disconnected(priv);
+               }
+
+               memset(&adapter->curbssparams.bssdescriptor,
+                      0x00, sizeof(adapter->curbssparams.bssdescriptor));
+
+               LEAVE();
+               return -1;
+       }
+
+       /*
+        * Now the join cmd should be successful
+        * If BSSID has changed use SSID to compare instead of BSSID
+        */
+       lbs_pr_debug(1, "ADHOC_J_RESP  %s\n", pbssdesc->ssid.ssid);
+
+       /* Send a Media Connected event, according to the Spec */
+       adapter->connect_status = libertas_connected;
+
+       if (command == cmd_ret_802_11_ad_hoc_start) {
+               /* Update the created network descriptor with the new BSSID */
+               memcpy(pbssdesc->macaddress,
+                      padhocresult->BSSID, ETH_ALEN);
+       } else {
+
+               /* Make a copy of current BSSID descriptor, only needed for join since
+                *   the current descriptor is already being used for adhoc start
+                */
+               memmove(&adapter->curbssparams.bssdescriptor,
+                       pbssdesc, sizeof(struct bss_descriptor));
+       }
+
+       /* Set the BSSID from the joined/started descriptor */
+       memcpy(&adapter->curbssparams.bssid,
+              pbssdesc->macaddress, ETH_ALEN);
+
+       /* Set the new SSID to current SSID */
+       memcpy(&adapter->curbssparams.ssid,
+              &pbssdesc->ssid, sizeof(struct WLAN_802_11_SSID));
+
+       netif_carrier_on(priv->wlan_dev.netdev);
+       netif_wake_queue(priv->wlan_dev.netdev);
+
+       memset(&wrqu, 0, sizeof(wrqu));
+       memcpy(wrqu.ap_addr.sa_data, adapter->curbssparams.bssid, ETH_ALEN);
+       wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+       wireless_send_event(priv->wlan_dev.netdev, SIOCGIWAP, &wrqu, NULL);
+
+       lbs_pr_debug(1, "ADHOC_RESP: - Joined/Started Ad Hoc\n");
+       lbs_pr_debug(1, "ADHOC_RESP: channel = %d\n", adapter->adhocchannel);
+       lbs_pr_debug(1, "ADHOC_RESP: BSSID = %02x:%02x:%02x:%02x:%02x:%02x\n",
+              padhocresult->BSSID[0], padhocresult->BSSID[1],
+              padhocresult->BSSID[2], padhocresult->BSSID[3],
+              padhocresult->BSSID[4], padhocresult->BSSID[5]);
+
+       LEAVE();
+       return ret;
+}
+
+int libertas_ret_80211_ad_hoc_stop(wlan_private * priv,
+                               struct cmd_ds_command *resp)
+{
+       ENTER();
+
+       libertas_mac_event_disconnected(priv);
+
+       LEAVE();
+       return 0;
+}
diff --git a/drivers/net/wireless/libertas/join.h b/drivers/net/wireless/libertas/join.h
new file mode 100644 (file)
index 0000000..8efa245
--- /dev/null
@@ -0,0 +1,64 @@
+/* -*- mode: C; tab-width: 4; indent-tabs-mode: nil -*- */
+/* vi: set expandtab shiftwidth=4 tabstop=4 textwidth=78: */
+
+/**
+  * Interface for the wlan infrastructure and adhoc join routines
+  *
+  * Driver interface functions and type declarations for the join module
+  *   implemented in wlan_join.c.  Process all start/join requests for
+  *   both adhoc and infrastructure networks
+  */
+#ifndef _WLAN_JOIN_H
+#define _WLAN_JOIN_H
+
+#include "defs.h"
+
+struct cmd_ds_command;
+extern int libertas_cmd_80211_authenticate(wlan_private * priv,
+                                       struct cmd_ds_command *cmd,
+                                       void *pdata_buf);
+extern int libertas_cmd_80211_ad_hoc_join(wlan_private * priv,
+                                      struct cmd_ds_command *cmd,
+                                      void *pdata_buf);
+extern int libertas_cmd_80211_ad_hoc_stop(wlan_private * priv,
+                                      struct cmd_ds_command *cmd);
+extern int libertas_cmd_80211_ad_hoc_start(wlan_private * priv,
+                                       struct cmd_ds_command *cmd,
+                                       void *pssid);
+extern int libertas_cmd_80211_deauthenticate(wlan_private * priv,
+                                         struct cmd_ds_command *cmd);
+extern int libertas_cmd_80211_associate(wlan_private * priv,
+                                    struct cmd_ds_command *cmd,
+                                    void *pdata_buf);
+
+extern int libertas_ret_80211_ad_hoc_start(wlan_private * priv,
+                                       struct cmd_ds_command *resp);
+extern int libertas_ret_80211_ad_hoc_stop(wlan_private * priv,
+                                      struct cmd_ds_command *resp);
+extern int libertas_ret_80211_disassociate(wlan_private * priv,
+                                       struct cmd_ds_command *resp);
+extern int libertas_ret_80211_associate(wlan_private * priv,
+                                    struct cmd_ds_command *resp);
+
+extern int libertas_idle_on(wlan_private * priv);
+extern int libertas_idle_off(wlan_private * priv);
+
+extern int libertas_do_adhocstop_ioctl(wlan_private * priv);
+extern int libertas_reassociation_thread(void *data);
+
+struct WLAN_802_11_SSID;
+struct bss_descriptor;
+
+extern int libertas_start_adhoc_network(wlan_private * priv,
+                            struct WLAN_802_11_SSID *adhocssid);
+extern int libertas_join_adhoc_network(wlan_private * priv, struct bss_descriptor *pbssdesc);
+extern int libertas_stop_adhoc_network(wlan_private * priv);
+
+extern int libertas_send_deauthentication(wlan_private * priv);
+extern int libertas_send_deauth(wlan_private * priv);
+
+extern int libertas_do_adhocstop_ioctl(wlan_private * priv);
+
+int wlan_associate(wlan_private * priv, struct bss_descriptor * pbssdesc);
+
+#endif
diff --git a/drivers/net/wireless/libertas/main.c b/drivers/net/wireless/libertas/main.c
new file mode 100644 (file)
index 0000000..4970465
--- /dev/null
@@ -0,0 +1,1258 @@
+/**
+  * This file contains the major functions in WLAN
+  * driver. It includes init, exit, open, close and main
+  * thread etc..
+  */
+
+#include <linux/delay.h>
+#include <linux/freezer.h>
+#include <linux/etherdevice.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+
+#include <net/iw_handler.h>
+
+#include "host.h"
+#include "sbi.h"
+#include "decl.h"
+#include "dev.h"
+#include "fw.h"
+#include "wext.h"
+#include "debugfs.h"
+#include "assoc.h"
+
+#ifdef ENABLE_PM
+static struct pm_dev *wlan_pm_dev = NULL;
+#endif
+
+#define WLAN_TX_PWR_DEFAULT            20      /*100mW */
+#define WLAN_TX_PWR_US_DEFAULT         20      /*100mW */
+#define WLAN_TX_PWR_JP_DEFAULT         16      /*50mW */
+#define WLAN_TX_PWR_FR_DEFAULT         20      /*100mW */
+#define WLAN_TX_PWR_EMEA_DEFAULT       20      /*100mW */
+
+/* Format { channel, frequency (MHz), maxtxpower } */
+/* band: 'B/G', region: USA FCC/Canada IC */
+static struct chan_freq_power channel_freq_power_US_BG[] = {
+       {1, 2412, WLAN_TX_PWR_US_DEFAULT},
+       {2, 2417, WLAN_TX_PWR_US_DEFAULT},
+       {3, 2422, WLAN_TX_PWR_US_DEFAULT},
+       {4, 2427, WLAN_TX_PWR_US_DEFAULT},
+       {5, 2432, WLAN_TX_PWR_US_DEFAULT},
+       {6, 2437, WLAN_TX_PWR_US_DEFAULT},
+       {7, 2442, WLAN_TX_PWR_US_DEFAULT},
+       {8, 2447, WLAN_TX_PWR_US_DEFAULT},
+       {9, 2452, WLAN_TX_PWR_US_DEFAULT},
+       {10, 2457, WLAN_TX_PWR_US_DEFAULT},
+       {11, 2462, WLAN_TX_PWR_US_DEFAULT}
+};
+
+/* band: 'B/G', region: Europe ETSI */
+static struct chan_freq_power channel_freq_power_EU_BG[] = {
+       {1, 2412, WLAN_TX_PWR_EMEA_DEFAULT},
+       {2, 2417, WLAN_TX_PWR_EMEA_DEFAULT},
+       {3, 2422, WLAN_TX_PWR_EMEA_DEFAULT},
+       {4, 2427, WLAN_TX_PWR_EMEA_DEFAULT},
+       {5, 2432, WLAN_TX_PWR_EMEA_DEFAULT},
+       {6, 2437, WLAN_TX_PWR_EMEA_DEFAULT},
+       {7, 2442, WLAN_TX_PWR_EMEA_DEFAULT},
+       {8, 2447, WLAN_TX_PWR_EMEA_DEFAULT},
+       {9, 2452, WLAN_TX_PWR_EMEA_DEFAULT},
+       {10, 2457, WLAN_TX_PWR_EMEA_DEFAULT},
+       {11, 2462, WLAN_TX_PWR_EMEA_DEFAULT},
+       {12, 2467, WLAN_TX_PWR_EMEA_DEFAULT},
+       {13, 2472, WLAN_TX_PWR_EMEA_DEFAULT}
+};
+
+/* band: 'B/G', region: Spain */
+static struct chan_freq_power channel_freq_power_SPN_BG[] = {
+       {10, 2457, WLAN_TX_PWR_DEFAULT},
+       {11, 2462, WLAN_TX_PWR_DEFAULT}
+};
+
+/* band: 'B/G', region: France */
+static struct chan_freq_power channel_freq_power_FR_BG[] = {
+       {10, 2457, WLAN_TX_PWR_FR_DEFAULT},
+       {11, 2462, WLAN_TX_PWR_FR_DEFAULT},
+       {12, 2467, WLAN_TX_PWR_FR_DEFAULT},
+       {13, 2472, WLAN_TX_PWR_FR_DEFAULT}
+};
+
+/* band: 'B/G', region: Japan */
+static struct chan_freq_power channel_freq_power_JPN_BG[] = {
+       {1, 2412, WLAN_TX_PWR_JP_DEFAULT},
+       {2, 2417, WLAN_TX_PWR_JP_DEFAULT},
+       {3, 2422, WLAN_TX_PWR_JP_DEFAULT},
+       {4, 2427, WLAN_TX_PWR_JP_DEFAULT},
+       {5, 2432, WLAN_TX_PWR_JP_DEFAULT},
+       {6, 2437, WLAN_TX_PWR_JP_DEFAULT},
+       {7, 2442, WLAN_TX_PWR_JP_DEFAULT},
+       {8, 2447, WLAN_TX_PWR_JP_DEFAULT},
+       {9, 2452, WLAN_TX_PWR_JP_DEFAULT},
+       {10, 2457, WLAN_TX_PWR_JP_DEFAULT},
+       {11, 2462, WLAN_TX_PWR_JP_DEFAULT},
+       {12, 2467, WLAN_TX_PWR_JP_DEFAULT},
+       {13, 2472, WLAN_TX_PWR_JP_DEFAULT},
+       {14, 2484, WLAN_TX_PWR_JP_DEFAULT}
+};
+
+/**
+ * the structure for channel, frequency and power
+ */
+struct region_cfp_table {
+       u8 region;
+       struct chan_freq_power *cfp_BG;
+       int cfp_no_BG;
+};
+
+/**
+ * the structure for the mapping between region and CFP
+ */
+static struct region_cfp_table region_cfp_table[] = {
+       {0x10,                  /*US FCC */
+        channel_freq_power_US_BG,
+        sizeof(channel_freq_power_US_BG) / sizeof(struct chan_freq_power),
+        }
+       ,
+       {0x20,                  /*CANADA IC */
+        channel_freq_power_US_BG,
+        sizeof(channel_freq_power_US_BG) / sizeof(struct chan_freq_power),
+        }
+       ,
+       {0x30, /*EU*/ channel_freq_power_EU_BG,
+        sizeof(channel_freq_power_EU_BG) / sizeof(struct chan_freq_power),
+        }
+       ,
+       {0x31, /*SPAIN*/ channel_freq_power_SPN_BG,
+        sizeof(channel_freq_power_SPN_BG) / sizeof(struct chan_freq_power),
+        }
+       ,
+       {0x32, /*FRANCE*/ channel_freq_power_FR_BG,
+        sizeof(channel_freq_power_FR_BG) / sizeof(struct chan_freq_power),
+        }
+       ,
+       {0x40, /*JAPAN*/ channel_freq_power_JPN_BG,
+        sizeof(channel_freq_power_JPN_BG) / sizeof(struct chan_freq_power),
+        }
+       ,
+/*Add new region here */
+};
+
+/**
+ * the rates supported by the card
+ */
+u8 libertas_wlan_data_rates[WLAN_SUPPORTED_RATES] =
+    { 0x02, 0x04, 0x0B, 0x16, 0x00, 0x0C, 0x12,
+       0x18, 0x24, 0x30, 0x48, 0x60, 0x6C, 0x00
+};
+
+/**
+ * the rates supported
+ */
+u8 libertas_supported_rates[G_SUPPORTED_RATES] =
+    { 0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c,
+0 };
+
+/**
+ * the rates supported for ad-hoc G mode
+ */
+u8 libertas_adhoc_rates_g[G_SUPPORTED_RATES] =
+    { 0x82, 0x84, 0x8b, 0x96, 0x0c, 0x12, 0x18, 0x24, 0x30, 0x48, 0x60, 0x6c,
+0 };
+
+/**
+ * the rates supported for ad-hoc B mode
+ */
+u8 libertas_adhoc_rates_b[4] = { 0x82, 0x84, 0x8b, 0x96 };
+
+/**
+ * the global variable of a pointer to wlan_private
+ * structure variable
+ */
+static wlan_private *wlanpriv = NULL;
+
+#define MAX_DEVS 5
+static struct net_device *libertas_devs[MAX_DEVS];
+static int libertas_found = 0;
+
+/**
+ * the table to keep region code
+ */
+u16 libertas_region_code_to_index[MRVDRV_MAX_REGION_CODE] =
+    { 0x10, 0x20, 0x30, 0x31, 0x32, 0x40 };
+
+static u8 *default_fw_name = "usb8388.bin";
+
+/**
+ * Attributes exported through sysfs
+ */
+#define to_net_dev(class) container_of(class, struct net_device, class_dev)
+
+/**
+ * @brief Get function for sysfs attribute libertas_mpp
+ */
+static ssize_t libertas_mpp_get(struct class_device * dev, char * buf) {
+       struct cmd_ds_mesh_access mesh_access;
+
+       memset(&mesh_access, 0, sizeof(mesh_access));
+       libertas_prepare_and_send_command(to_net_dev(dev)->priv,
+                       cmd_mesh_access,
+                       cmd_act_mesh_get_mpp,
+                       cmd_option_waitforrsp, 0, (void *)&mesh_access);
+
+       return snprintf(buf, 3, "%d\n", mesh_access.data[0]);
+}
+
+/**
+ * @brief Set function for sysfs attribute libertas_mpp
+ */
+static ssize_t libertas_mpp_set(struct class_device * dev, const char * buf,
+               size_t count) {
+       struct cmd_ds_mesh_access mesh_access;
+
+
+       memset(&mesh_access, 0, sizeof(mesh_access));
+       sscanf(buf, "%d", &(mesh_access.data[0]));
+       libertas_prepare_and_send_command((to_net_dev(dev))->priv,
+                       cmd_mesh_access,
+                       cmd_act_mesh_set_mpp,
+                       cmd_option_waitforrsp, 0, (void *)&mesh_access);
+       return strlen(buf);
+}
+
+/**
+ * libertas_mpp attribute to be exported per mshX interface
+ * through sysfs (/sys/class/net/mshX/libertas-mpp)
+ */
+static CLASS_DEVICE_ATTR(libertas_mpp, 0644, libertas_mpp_get,
+               libertas_mpp_set );
+
+/**
+ *  @brief Check if the device can be open and wait if necessary.
+ *
+ *  @param dev     A pointer to net_device structure
+ *  @return       0
+ *
+ * For USB adapter, on some systems the device open handler will be
+ * called before FW ready. Use the following flag check and wait
+ * function to work around the issue.
+ *
+ */
+static int pre_open_check(struct net_device *dev) {
+       wlan_private *priv = (wlan_private *) dev->priv;
+       wlan_adapter *adapter = priv->adapter;
+       int i = 0;
+
+       while (!adapter->fw_ready && i < 20) {
+               i++;
+               msleep_interruptible(100);
+       }
+       if (!adapter->fw_ready) {
+               lbs_pr_info("FW not ready, pre_open_check() return failure\n");
+               LEAVE();
+               return -1;
+       }
+
+       return 0;
+}
+
+/**
+ *  @brief This function opens the device
+ *
+ *  @param dev     A pointer to net_device structure
+ *  @return       0
+ */
+static int wlan_dev_open(struct net_device *dev)
+{
+       wlan_private *priv = (wlan_private *) dev->priv;
+       wlan_adapter *adapter = priv->adapter;
+
+       ENTER();
+
+
+       priv->open = 1;
+
+       if (adapter->connect_status == libertas_connected) {
+               netif_carrier_on(priv->wlan_dev.netdev);
+       } else
+               netif_carrier_off(priv->wlan_dev.netdev);
+
+       LEAVE();
+       return 0;
+}
+/**
+ *  @brief This function opens the mshX interface
+ *
+ *  @param dev     A pointer to net_device structure
+ *  @return       0
+ */
+static int mesh_open(struct net_device *dev)
+{
+       wlan_private *priv = (wlan_private *) dev->priv ;
+
+       if(pre_open_check(dev) == -1)
+               return -1;
+       priv->mesh_open = 1 ;
+       netif_start_queue(priv->mesh_dev);
+       if (priv->infra_open == 0)
+               return wlan_dev_open(priv->wlan_dev.netdev) ;
+       return 0;
+}
+
+/**
+ *  @brief This function opens the ethX interface
+ *
+ *  @param dev     A pointer to net_device structure
+ *  @return       0
+ */
+static int wlan_open(struct net_device *dev)
+{
+       wlan_private *priv = (wlan_private *) dev->priv ;
+
+       if(pre_open_check(dev) == -1)
+               return -1;
+       priv->infra_open = 1 ;
+       netif_wake_queue(priv->wlan_dev.netdev);
+       if (priv->open == 0)
+               return wlan_dev_open(priv->wlan_dev.netdev) ;
+       return 0;
+}
+
+static int wlan_dev_close(struct net_device *dev)
+{
+       wlan_private *priv = dev->priv;
+
+       ENTER();
+
+       netif_carrier_off(priv->wlan_dev.netdev);
+       priv->open = 0;
+
+       LEAVE();
+       return 0;
+}
+
+/**
+ *  @brief This function closes the mshX interface
+ *
+ *  @param dev     A pointer to net_device structure
+ *  @return       0
+ */
+static int mesh_close(struct net_device *dev)
+{
+       wlan_private *priv = (wlan_private *) (dev->priv);
+
+       priv->mesh_open = 0;
+       netif_stop_queue(priv->mesh_dev);
+       if (priv->infra_open == 0)
+               return wlan_dev_close( ((wlan_private *) dev->priv)->wlan_dev.netdev) ;
+       else
+               return 0;
+}
+
+/**
+ *  @brief This function closes the ethX interface
+ *
+ *  @param dev     A pointer to net_device structure
+ *  @return       0
+ */
+static int wlan_close(struct net_device *dev) {
+       wlan_private *priv = (wlan_private *) dev->priv;
+
+       netif_stop_queue(priv->wlan_dev.netdev);
+       priv->infra_open = 0;
+       if (priv->mesh_open == 0)
+               return wlan_dev_close( ((wlan_private *) dev->priv)->wlan_dev.netdev) ;
+       else
+               return 0;
+}
+
+
+#ifdef ENABLE_PM
+
+/**
+ *  @brief This function is a callback function. it is called by
+ *  kernel to enter or exit power saving mode.
+ *
+ *  @param pmdev   A pointer to pm_dev
+ *  @param pmreq   pm_request_t
+ *  @param pmdata  A pointer to pmdata
+ *  @return       0 or -1
+ */
+static int wlan_pm_callback(struct pm_dev *pmdev, pm_request_t pmreq,
+                           void *pmdata)
+{
+       wlan_private *priv = wlanpriv;
+       wlan_adapter *adapter = priv->adapter;
+       struct net_device *dev = priv->wlan_dev.netdev;
+
+       lbs_pr_debug(1, "WPRM_PM_CALLBACK: pmreq = %d.\n", pmreq);
+
+       switch (pmreq) {
+       case PM_SUSPEND:
+               lbs_pr_debug(1, "WPRM_PM_CALLBACK: enter PM_SUSPEND.\n");
+
+               /* in associated mode */
+               if (adapter->connect_status == libertas_connected) {
+                       if ((adapter->psstate != PS_STATE_SLEEP)
+                           ) {
+                               lbs_pr_debug(1,
+                                      "wlan_pm_callback: can't enter sleep mode\n");
+                               return -1;
+                       } else {
+
+                               /*
+                                * Detach the network interface
+                                * if the network is running
+                                */
+                               if (netif_running(dev)) {
+                                       netif_device_detach(dev);
+                                       lbs_pr_debug(1,
+                                              "netif_device_detach().\n");
+                               }
+                               libertas_sbi_suspend(priv);
+                       }
+                       break;
+               }
+
+               /* in non associated mode */
+
+               /*
+                * Detach the network interface
+                * if the network is running
+                */
+               if (netif_running(dev))
+                       netif_device_detach(dev);
+
+               /*
+                * Storing and restoring of the regs be taken care
+                * at the driver rest will be done at wlan driver
+                * this makes driver independent of the card
+                */
+
+               libertas_sbi_suspend(priv);
+
+               break;
+
+       case PM_RESUME:
+               /* in associated mode */
+               if (adapter->connect_status == libertas_connected) {
+                       {
+                               /*
+                                * Bring the inteface up first
+                                * This case should not happen still ...
+                                */
+                               libertas_sbi_resume(priv);
+
+                               /*
+                                * Attach the network interface
+                                * if the network is running
+                                */
+                               if (netif_running(dev)) {
+                                       netif_device_attach(dev);
+                                       lbs_pr_debug(1,
+                                              "after netif_device_attach().\n");
+                               }
+                               lbs_pr_debug(1,
+                                      "After netif attach, in associated mode.\n");
+                       }
+                       break;
+               }
+
+               /* in non associated mode */
+
+               /*
+                * Bring the inteface up first
+                * This case should not happen still ...
+                */
+
+               libertas_sbi_resume(priv);
+
+               if (netif_running(dev))
+                       netif_device_attach(dev);
+
+               lbs_pr_debug(1, "after netif attach, in NON associated mode.\n");
+               break;
+       }
+
+       return 0;
+}
+#endif                         /* ENABLE_PM */
+
+static int wlan_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       int ret = 0;
+       wlan_private *priv = dev->priv;
+
+       ENTER();
+
+       if (priv->wlan_dev.dnld_sent || priv->adapter->TxLockFlag) {
+               priv->stats.tx_dropped++;
+               goto done;
+       }
+
+       netif_stop_queue(priv->wlan_dev.netdev);
+
+       if (libertas_process_tx(priv, skb) == 0)
+               dev->trans_start = jiffies;
+done:
+       LEAVE();
+       return ret;
+}
+
+/**
+ * @brief Mark mesh packets and handover them to wlan_hard_start_xmit
+ *
+ */
+static int mesh_pre_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       wlan_private *priv = dev->priv;
+       ENTER();
+       SET_MESH_FRAME(skb);
+       LEAVE();
+
+       return wlan_hard_start_xmit(skb, priv->wlan_dev.netdev);
+}
+
+/**
+ * @brief Mark non-mesh packets and handover them to wlan_hard_start_xmit
+ *
+ */
+static int wlan_pre_start_xmit(struct sk_buff *skb, struct net_device *dev) {
+       ENTER();
+       UNSET_MESH_FRAME(skb);
+       LEAVE();
+       return wlan_hard_start_xmit(skb, dev);
+}
+
+static void wlan_tx_timeout(struct net_device *dev)
+{
+       wlan_private *priv = (wlan_private *) dev->priv;
+
+       ENTER();
+
+       lbs_pr_err("tx watch dog timeout!\n");
+
+       priv->wlan_dev.dnld_sent = DNLD_RES_RECEIVED;
+       dev->trans_start = jiffies;
+
+       if (priv->adapter->currenttxskb) {
+               if (priv->adapter->radiomode == WLAN_RADIOMODE_RADIOTAP) {
+                       /* If we are here, we have not received feedback from
+                          the previous packet.  Assume TX_FAIL and move on. */
+                       priv->adapter->eventcause = 0x01000000;
+                       libertas_send_tx_feedback(priv);
+               } else
+                       wake_up_interruptible(&priv->mainthread.waitq);
+       } else if (priv->adapter->connect_status == libertas_connected)
+               netif_wake_queue(priv->wlan_dev.netdev);
+
+       LEAVE();
+}
+
+/**
+ *  @brief This function returns the network statistics
+ *
+ *  @param dev     A pointer to wlan_private structure
+ *  @return       A pointer to net_device_stats structure
+ */
+static struct net_device_stats *wlan_get_stats(struct net_device *dev)
+{
+       wlan_private *priv = (wlan_private *) dev->priv;
+
+       return &priv->stats;
+}
+
+static int wlan_set_mac_address(struct net_device *dev, void *addr)
+{
+       int ret = 0;
+       wlan_private *priv = (wlan_private *) dev->priv;
+       wlan_adapter *adapter = priv->adapter;
+       struct sockaddr *phwaddr = addr;
+
+       ENTER();
+
+       memset(adapter->current_addr, 0, ETH_ALEN);
+
+       /* dev->dev_addr is 8 bytes */
+       lbs_dbg_hex("dev->dev_addr:", dev->dev_addr, ETH_ALEN);
+
+       lbs_dbg_hex("addr:", phwaddr->sa_data, ETH_ALEN);
+       memcpy(adapter->current_addr, phwaddr->sa_data, ETH_ALEN);
+
+       ret = libertas_prepare_and_send_command(priv, cmd_802_11_mac_address,
+                                   cmd_act_set,
+                                   cmd_option_waitforrsp, 0, NULL);
+
+       if (ret) {
+               lbs_pr_debug(1, "set mac address failed.\n");
+               ret = -1;
+               goto done;
+       }
+
+       lbs_dbg_hex("adapter->macaddr:", adapter->current_addr, ETH_ALEN);
+       memcpy(dev->dev_addr, adapter->current_addr, ETH_ALEN);
+       memcpy(((wlan_private *) dev->priv)->mesh_dev->dev_addr, adapter->current_addr, ETH_ALEN);
+
+done:
+       LEAVE();
+       return ret;
+}
+
+static int wlan_copy_multicast_address(wlan_adapter * adapter,
+                                    struct net_device *dev)
+{
+       int i = 0;
+       struct dev_mc_list *mcptr = dev->mc_list;
+
+       for (i = 0; i < dev->mc_count; i++) {
+               memcpy(&adapter->multicastlist[i], mcptr->dmi_addr, ETH_ALEN);
+               mcptr = mcptr->next;
+       }
+
+       return i;
+
+}
+
+static void wlan_set_multicast_list(struct net_device *dev)
+{
+       wlan_private *priv = dev->priv;
+       wlan_adapter *adapter = priv->adapter;
+       int oldpacketfilter;
+
+       ENTER();
+
+       oldpacketfilter = adapter->currentpacketfilter;
+
+       if (dev->flags & IFF_PROMISC) {
+               lbs_pr_debug(1, "enable Promiscuous mode\n");
+               adapter->currentpacketfilter |=
+                   cmd_act_mac_promiscuous_enable;
+               adapter->currentpacketfilter &=
+                   ~(cmd_act_mac_all_multicast_enable |
+                     cmd_act_mac_multicast_enable);
+       } else {
+               /* Multicast */
+               adapter->currentpacketfilter &=
+                   ~cmd_act_mac_promiscuous_enable;
+
+               if (dev->flags & IFF_ALLMULTI || dev->mc_count >
+                   MRVDRV_MAX_MULTICAST_LIST_SIZE) {
+                       lbs_pr_debug(1, "Enabling All Multicast!\n");
+                       adapter->currentpacketfilter |=
+                           cmd_act_mac_all_multicast_enable;
+                       adapter->currentpacketfilter &=
+                           ~cmd_act_mac_multicast_enable;
+               } else {
+                       adapter->currentpacketfilter &=
+                           ~cmd_act_mac_all_multicast_enable;
+
+                       if (!dev->mc_count) {
+                               lbs_pr_debug(1, "No multicast addresses - "
+                                      "disabling multicast!\n");
+                               adapter->currentpacketfilter &=
+                                   ~cmd_act_mac_multicast_enable;
+                       } else {
+                               int i;
+
+                               adapter->currentpacketfilter |=
+                                   cmd_act_mac_multicast_enable;
+
+                               adapter->nr_of_multicastmacaddr =
+                                   wlan_copy_multicast_address(adapter, dev);
+
+                               lbs_pr_debug(1, "Multicast addresses: %d\n",
+                                      dev->mc_count);
+
+                               for (i = 0; i < dev->mc_count; i++) {
+                                       lbs_pr_debug(1, "Multicast address %d:"
+                                              "%x %x %x %x %x %x\n", i,
+                                              adapter->multicastlist[i][0],
+                                              adapter->multicastlist[i][1],
+                                              adapter->multicastlist[i][2],
+                                              adapter->multicastlist[i][3],
+                                              adapter->multicastlist[i][4],
+                                              adapter->multicastlist[i][5]);
+                               }
+                               /* set multicast addresses to firmware */
+                               libertas_prepare_and_send_command(priv,
+                                                     cmd_mac_multicast_adr,
+                                                     cmd_act_set, 0, 0,
+                                                     NULL);
+                       }
+               }
+       }
+
+       if (adapter->currentpacketfilter != oldpacketfilter) {
+               libertas_set_mac_packet_filter(priv);
+       }
+
+       LEAVE();
+}
+
+/**
+ *  @brief This function hanldes the major job in WLAN driver.
+ *  it handles the event generated by firmware, rx data received
+ *  from firmware and tx data sent from kernel.
+ *
+ *  @param data    A pointer to wlan_thread structure
+ *  @return       0
+ */
+static int wlan_service_main_thread(void *data)
+{
+       struct wlan_thread *thread = data;
+       wlan_private *priv = thread->priv;
+       wlan_adapter *adapter = priv->adapter;
+       wait_queue_t wait;
+       u8 ireg = 0;
+
+       ENTER();
+
+       wlan_activate_thread(thread);
+
+       init_waitqueue_entry(&wait, current);
+
+       for (;;) {
+               lbs_pr_debug(1, "main-thread 111: intcounter=%d "
+                      "currenttxskb=%p dnld_sent=%d\n",
+                      adapter->intcounter,
+                      adapter->currenttxskb, priv->wlan_dev.dnld_sent);
+
+               add_wait_queue(&thread->waitq, &wait);
+               set_current_state(TASK_INTERRUPTIBLE);
+               spin_lock_irq(&adapter->driver_lock);
+               if ((adapter->psstate == PS_STATE_SLEEP) ||
+                   (!adapter->intcounter
+                    && (priv->wlan_dev.dnld_sent || adapter->cur_cmd ||
+                        list_empty(&adapter->cmdpendingq)))) {
+                       lbs_pr_debug(1,
+                              "main-thread sleeping... Conn=%d IntC=%d PS_mode=%d PS_State=%d\n",
+                              adapter->connect_status, adapter->intcounter,
+                              adapter->psmode, adapter->psstate);
+                       spin_unlock_irq(&adapter->driver_lock);
+                       schedule();
+               } else
+                       spin_unlock_irq(&adapter->driver_lock);
+
+
+               lbs_pr_debug(1,
+                      "main-thread 222 (waking up): intcounter=%d currenttxskb=%p "
+                      "dnld_sent=%d\n", adapter->intcounter,
+                      adapter->currenttxskb, priv->wlan_dev.dnld_sent);
+
+               set_current_state(TASK_RUNNING);
+               remove_wait_queue(&thread->waitq, &wait);
+               try_to_freeze();
+
+               lbs_pr_debug(1, "main-thread 333: intcounter=%d currenttxskb=%p "
+                      "dnld_sent=%d\n",
+                      adapter->intcounter,
+                      adapter->currenttxskb, priv->wlan_dev.dnld_sent);
+
+               if (kthread_should_stop()
+                   || adapter->surpriseremoved) {
+                       lbs_pr_debug(1,
+                              "main-thread: break from main thread: surpriseremoved=0x%x\n",
+                              adapter->surpriseremoved);
+                       break;
+               }
+
+
+               spin_lock_irq(&adapter->driver_lock);
+               if (adapter->intcounter) {
+                       u8 int_status;
+                       adapter->intcounter = 0;
+                       int_status = libertas_sbi_get_int_status(priv, &ireg);
+
+                       if (int_status) {
+                               lbs_pr_debug(1,
+                                      "main-thread: reading HOST_INT_STATUS_REG failed\n");
+                               spin_unlock_irq(&adapter->driver_lock);
+                               continue;
+                       }
+                       adapter->hisregcpy |= ireg;
+               }
+
+               lbs_pr_debug(1, "main-thread 444: intcounter=%d currenttxskb=%p "
+                      "dnld_sent=%d\n",
+                      adapter->intcounter,
+                      adapter->currenttxskb, priv->wlan_dev.dnld_sent);
+
+               /* command response? */
+               if (adapter->hisregcpy & his_cmdupldrdy) {
+                       lbs_pr_debug(1, "main-thread: cmd response ready.\n");
+
+                       adapter->hisregcpy &= ~his_cmdupldrdy;
+                       spin_unlock_irq(&adapter->driver_lock);
+                       libertas_process_rx_command(priv);
+                       spin_lock_irq(&adapter->driver_lock);
+               }
+
+               /* Any Card Event */
+               if (adapter->hisregcpy & his_cardevent) {
+                       lbs_pr_debug(1, "main-thread: Card Event Activity.\n");
+
+                       adapter->hisregcpy &= ~his_cardevent;
+
+                       if (libertas_sbi_read_event_cause(priv)) {
+                               lbs_pr_alert(
+                                      "main-thread: libertas_sbi_read_event_cause failed.\n");
+                               spin_unlock_irq(&adapter->driver_lock);
+                               continue;
+                       }
+                       spin_unlock_irq(&adapter->driver_lock);
+                       libertas_process_event(priv);
+               } else
+                       spin_unlock_irq(&adapter->driver_lock);
+
+               /* Check if we need to confirm Sleep Request received previously */
+               if (adapter->psstate == PS_STATE_PRE_SLEEP) {
+                       if (!priv->wlan_dev.dnld_sent && !adapter->cur_cmd) {
+                               if (adapter->connect_status ==
+                                   libertas_connected) {
+                                       lbs_pr_debug(1,
+                                              "main_thread: PRE_SLEEP--intcounter=%d currenttxskb=%p "
+                                              "dnld_sent=%d cur_cmd=%p, confirm now\n",
+                                              adapter->intcounter,
+                                              adapter->currenttxskb,
+                                              priv->wlan_dev.dnld_sent,
+                                              adapter->cur_cmd);
+
+                                       libertas_ps_confirm_sleep(priv,
+                                                      (u16) adapter->psmode);
+                               } else {
+                                       /* workaround for firmware sending
+                                        * deauth/linkloss event immediately
+                                        * after sleep request, remove this
+                                        * after firmware fixes it
+                                        */
+                                       adapter->psstate = PS_STATE_AWAKE;
+                                       lbs_pr_alert(
+                                              "main-thread: ignore PS_SleepConfirm in non-connected state\n");
+                               }
+                       }
+               }
+
+               /* The PS state is changed during processing of Sleep Request
+                * event above
+                */
+               if ((priv->adapter->psstate == PS_STATE_SLEEP) ||
+                   (priv->adapter->psstate == PS_STATE_PRE_SLEEP))
+                       continue;
+
+               /* Execute the next command */
+               if (!priv->wlan_dev.dnld_sent && !priv->adapter->cur_cmd)
+                       libertas_execute_next_command(priv);
+
+               /* Wake-up command waiters which can't sleep in
+                * libertas_prepare_and_send_command
+                */
+               if (!adapter->nr_cmd_pending)
+                       wake_up_all(&adapter->cmd_pending);
+
+               libertas_tx_runqueue(priv);
+       }
+
+       del_timer(&adapter->command_timer);
+       adapter->nr_cmd_pending = 0;
+       wake_up_all(&adapter->cmd_pending);
+       wlan_deactivate_thread(thread);
+
+       LEAVE();
+       return 0;
+}
+
+/**
+ * @brief This function adds the card. it will probe the
+ * card, allocate the wlan_priv and initialize the device.
+ *
+ *  @param card    A pointer to card
+ *  @return       A pointer to wlan_private structure
+ */
+wlan_private *wlan_add_card(void *card)
+{
+       struct net_device *dev = NULL;
+       struct net_device *mesh_dev = NULL;
+       wlan_private *priv = NULL;
+
+       ENTER();
+
+       /* Allocate an Ethernet device and register it */
+       if (!(dev = alloc_etherdev(sizeof(wlan_private)))) {
+               lbs_pr_alert( "Init ethernet device failed!\n");
+               return NULL;
+       }
+
+       priv = dev->priv;
+
+       /* allocate buffer for wlan_adapter */
+       if (!(priv->adapter = kmalloc(sizeof(wlan_adapter), GFP_KERNEL))) {
+               lbs_pr_alert( "Allocate buffer for wlan_adapter failed!\n");
+               goto err_kmalloc;
+       }
+
+       /* Allocate a virtual mesh device */
+       if (!(mesh_dev = alloc_netdev(0, "msh%d", ether_setup))) {
+               lbs_pr_debug(1, "Init ethernet device failed!\n");
+               return NULL;
+       }
+
+       /* Both intervaces share the priv structure */
+       mesh_dev->priv = priv;
+
+       /* init wlan_adapter */
+       memset(priv->adapter, 0, sizeof(wlan_adapter));
+
+       priv->wlan_dev.netdev = dev;
+       priv->wlan_dev.card = card;
+       priv->mesh_open = 0;
+       priv->infra_open = 0;
+       priv->mesh_dev = mesh_dev;
+       wlanpriv = priv;
+
+       SET_MODULE_OWNER(dev);
+       SET_MODULE_OWNER(mesh_dev);
+
+       /* Setup the OS Interface to our functions */
+       dev->open = wlan_open;
+       dev->hard_start_xmit = wlan_pre_start_xmit;
+       dev->stop = wlan_close;
+       dev->do_ioctl = libertas_do_ioctl;
+       dev->set_mac_address = wlan_set_mac_address;
+       mesh_dev->open = mesh_open;
+       mesh_dev->hard_start_xmit = mesh_pre_start_xmit;
+       mesh_dev->stop = mesh_close;
+       mesh_dev->do_ioctl = libertas_do_ioctl;
+       memcpy(mesh_dev->dev_addr, wlanpriv->wlan_dev.netdev->dev_addr,
+                       sizeof(wlanpriv->wlan_dev.netdev->dev_addr));
+
+#define        WLAN_WATCHDOG_TIMEOUT   (5 * HZ)
+
+       dev->tx_timeout = wlan_tx_timeout;
+       dev->get_stats = wlan_get_stats;
+       dev->watchdog_timeo = WLAN_WATCHDOG_TIMEOUT;
+       dev->ethtool_ops = &libertas_ethtool_ops;
+       mesh_dev->get_stats = wlan_get_stats;
+       mesh_dev->ethtool_ops = &libertas_ethtool_ops;
+
+#ifdef WIRELESS_EXT
+       dev->wireless_handlers = (struct iw_handler_def *)&libertas_handler_def;
+       mesh_dev->wireless_handlers = (struct iw_handler_def *)&libertas_handler_def;
+#endif
+#define NETIF_F_DYNALLOC 16
+       dev->features |= NETIF_F_DYNALLOC;
+       dev->flags |= IFF_BROADCAST | IFF_MULTICAST;
+       dev->set_multicast_list = wlan_set_multicast_list;
+
+       INIT_LIST_HEAD(&priv->adapter->cmdfreeq);
+       INIT_LIST_HEAD(&priv->adapter->cmdpendingq);
+
+       spin_lock_init(&priv->adapter->driver_lock);
+       init_waitqueue_head(&priv->adapter->cmd_pending);
+       priv->adapter->nr_cmd_pending = 0;
+
+       lbs_pr_debug(1, "Starting kthread...\n");
+       priv->mainthread.priv = priv;
+       wlan_create_thread(wlan_service_main_thread,
+                          &priv->mainthread, "wlan_main_service");
+
+       priv->assoc_thread =
+               create_singlethread_workqueue("libertas_assoc");
+       INIT_DELAYED_WORK(&priv->assoc_work, wlan_association_worker);
+
+       /*
+        * Register the device. Fillup the private data structure with
+        * relevant information from the card and request for the required
+        * IRQ.
+        */
+       if (libertas_sbi_register_dev(priv) < 0) {
+               lbs_pr_info("failed to register wlan device!\n");
+               goto err_registerdev;
+       }
+
+       /* init FW and HW */
+       if (libertas_init_fw(priv)) {
+               lbs_pr_debug(1, "Firmware Init failed\n");
+               goto err_registerdev;
+       }
+
+       if (register_netdev(dev)) {
+               lbs_pr_err("Cannot register network device!\n");
+               goto err_init_fw;
+       }
+
+       /* Register virtual mesh interface */
+       if (register_netdev(mesh_dev)) {
+               lbs_pr_info("Cannot register mesh virtual interface!\n");
+               goto err_init_fw;
+       }
+
+       lbs_pr_info("%s: Marvell Wlan 802.11 adapter ", dev->name);
+
+       libertas_debugfs_init_one(priv, dev);
+
+       if (libertas_found == MAX_DEVS)
+               goto err_init_fw;
+       libertas_devs[libertas_found] = dev;
+       libertas_found++;
+#ifdef ENABLE_PM
+       if (!(wlan_pm_dev = pm_register(PM_UNKNOWN_DEV, 0, wlan_pm_callback)))
+               lbs_pr_alert( "failed to register PM callback\n");
+#endif
+       if (class_device_create_file(&(mesh_dev->class_dev), &class_device_attr_libertas_mpp))
+               goto err_create_file;
+
+       LEAVE();
+       return priv;
+
+err_create_file:
+       class_device_remove_file(&(mesh_dev->class_dev), &class_device_attr_libertas_mpp);
+err_init_fw:
+       libertas_sbi_unregister_dev(priv);
+err_registerdev:
+       destroy_workqueue(priv->assoc_thread);
+       /* Stop the thread servicing the interrupts */
+       wake_up_interruptible(&priv->mainthread.waitq);
+       wlan_terminate_thread(&priv->mainthread);
+       kfree(priv->adapter);
+err_kmalloc:
+       free_netdev(dev);
+       free_netdev(mesh_dev);
+       wlanpriv = NULL;
+
+       LEAVE();
+       return NULL;
+}
+
+static void wake_pending_cmdnodes(wlan_private *priv)
+{
+       struct cmd_ctrl_node *cmdnode;
+       unsigned long flags;
+
+       spin_lock_irqsave(&priv->adapter->driver_lock, flags);
+       list_for_each_entry(cmdnode, &priv->adapter->cmdpendingq, list) {
+               cmdnode->cmdwaitqwoken = 1;
+               wake_up_interruptible(&cmdnode->cmdwait_q);
+       }
+       spin_unlock_irqrestore(&priv->adapter->driver_lock, flags);
+}
+
+
+int wlan_remove_card(void *card)
+{
+       wlan_private *priv = libertas_sbi_get_priv(card);
+       wlan_adapter *adapter;
+       struct net_device *dev;
+       struct net_device *mesh_dev;
+       union iwreq_data wrqu;
+       int i;
+
+       ENTER();
+
+       if (!priv) {
+               LEAVE();
+               return 0;
+       }
+
+       adapter = priv->adapter;
+
+       if (!adapter) {
+               LEAVE();
+               return 0;
+       }
+
+       dev = priv->wlan_dev.netdev;
+       mesh_dev = priv->mesh_dev;
+
+       netif_stop_queue(mesh_dev);
+       netif_stop_queue(priv->wlan_dev.netdev);
+       netif_carrier_off(priv->wlan_dev.netdev);
+
+       wake_pending_cmdnodes(priv);
+
+       class_device_remove_file(&(mesh_dev->class_dev), &class_device_attr_libertas_mpp);
+       unregister_netdev(mesh_dev);
+       unregister_netdev(dev);
+
+       cancel_delayed_work(&priv->assoc_work);
+       destroy_workqueue(priv->assoc_thread);
+
+       if (adapter->psmode == wlan802_11powermodemax_psp) {
+               adapter->psmode = wlan802_11powermodecam;
+               libertas_ps_wakeup(priv, cmd_option_waitforrsp);
+       }
+
+       memset(wrqu.ap_addr.sa_data, 0xaa, ETH_ALEN);
+       wrqu.ap_addr.sa_family = ARPHRD_ETHER;
+       wireless_send_event(priv->wlan_dev.netdev, SIOCGIWAP, &wrqu, NULL);
+
+#ifdef ENABLE_PM
+       pm_unregister(wlan_pm_dev);
+#endif
+
+       adapter->surpriseremoved = 1;
+
+       /* Stop the thread servicing the interrupts */
+       wlan_terminate_thread(&priv->mainthread);
+
+       libertas_debugfs_remove_one(priv);
+
+       lbs_pr_debug(1, "Free adapter\n");
+       libertas_free_adapter(priv);
+
+       for (i = 0; i<libertas_found; i++) {
+               if (libertas_devs[i]==priv->wlan_dev.netdev) {
+                       libertas_devs[i] = libertas_devs[--libertas_found];
+                       libertas_devs[libertas_found] = NULL ;
+                       break ;
+               }
+       }
+
+       lbs_pr_debug(1, "Unregister finish\n");
+
+       priv->wlan_dev.netdev = NULL;
+       priv->mesh_dev = NULL ;
+       free_netdev(mesh_dev);
+       free_netdev(dev);
+       wlanpriv = NULL;
+
+       LEAVE();
+       return 0;
+}
+
+/**
+ *  @brief This function finds the CFP in
+ *  region_cfp_table based on region and band parameter.
+ *
+ *  @param region  The region code
+ *  @param band           The band
+ *  @param cfp_no  A pointer to CFP number
+ *  @return       A pointer to CFP
+ */
+struct chan_freq_power *libertas_get_region_cfp_table(u8 region, u8 band, int *cfp_no)
+{
+       int i, end;
+
+       ENTER();
+
+       end = sizeof(region_cfp_table)/sizeof(struct region_cfp_table);
+
+       for (i = 0; i < end ; i++) {
+               lbs_pr_debug(1, "region_cfp_table[i].region=%d\n",
+                       region_cfp_table[i].region);
+               if (region_cfp_table[i].region == region) {
+                       *cfp_no = region_cfp_table[i].cfp_no_BG;
+                       LEAVE();
+                       return region_cfp_table[i].cfp_BG;
+               }
+       }
+
+       LEAVE();
+       return NULL;
+}
+
+int libertas_set_regiontable(wlan_private * priv, u8 region, u8 band)
+{
+       wlan_adapter *adapter = priv->adapter;
+       int i = 0;
+
+       struct chan_freq_power *cfp;
+       int cfp_no;
+
+       ENTER();
+
+       memset(adapter->region_channel, 0, sizeof(adapter->region_channel));
+
+       {
+               cfp = libertas_get_region_cfp_table(region, band, &cfp_no);
+               if (cfp != NULL) {
+                       adapter->region_channel[i].nrcfp = cfp_no;
+                       adapter->region_channel[i].CFP = cfp;
+               } else {
+                       lbs_pr_debug(1, "wrong region code %#x in band B-G\n",
+                              region);
+                       return -1;
+               }
+               adapter->region_channel[i].valid = 1;
+               adapter->region_channel[i].region = region;
+               adapter->region_channel[i].band = band;
+               i++;
+       }
+       LEAVE();
+       return 0;
+}
+
+/**
+ *  @brief This function handles the interrupt. it will change PS
+ *  state if applicable. it will wake up main_thread to handle
+ *  the interrupt event as well.
+ *
+ *  @param dev     A pointer to net_device structure
+ *  @return       n/a
+ */
+void libertas_interrupt(struct net_device *dev)
+{
+       wlan_private *priv = dev->priv;
+
+       ENTER();
+
+       lbs_pr_debug(1, "libertas_interrupt: intcounter=%d\n",
+              priv->adapter->intcounter);
+
+       priv->adapter->intcounter++;
+
+       if (priv->adapter->psstate == PS_STATE_SLEEP) {
+               priv->adapter->psstate = PS_STATE_AWAKE;
+               netif_wake_queue(dev);
+       }
+
+       wake_up_interruptible(&priv->mainthread.waitq);
+
+       LEAVE();
+}
+
+static int wlan_init_module(void)
+{
+       int ret = 0;
+
+       ENTER();
+
+       if (libertas_fw_name == NULL) {
+               libertas_fw_name = default_fw_name;
+       }
+
+       libertas_debugfs_init();
+
+       if (libertas_sbi_register()) {
+               ret = -1;
+               libertas_debugfs_remove();
+               goto done;
+       }
+
+done:
+       LEAVE();
+       return ret;
+}
+
+static void wlan_cleanup_module(void)
+{
+       int i;
+
+       ENTER();
+
+       for (i = 0; i<libertas_found; i++) {
+               wlan_private *priv = libertas_devs[i]->priv;
+               reset_device(priv);
+       }
+
+       libertas_sbi_unregister();
+       libertas_debugfs_remove();
+
+       LEAVE();
+}
+
+module_init(wlan_init_module);
+module_exit(wlan_cleanup_module);
+
+MODULE_DESCRIPTION("M-WLAN Driver");
+MODULE_AUTHOR("Marvell International Ltd.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/libertas/radiotap.h b/drivers/net/wireless/libertas/radiotap.h
new file mode 100644 (file)
index 0000000..5d118f4
--- /dev/null
@@ -0,0 +1,57 @@
+#include <net/ieee80211_radiotap.h>
+
+struct tx_radiotap_hdr {
+       struct ieee80211_radiotap_header hdr;
+       u8 rate;
+       u8 txpower;
+       u8 rts_retries;
+       u8 data_retries;
+#if 0
+       u8 pad[IEEE80211_RADIOTAP_HDRLEN - 12];
+#endif
+} __attribute__ ((packed));
+
+#define TX_RADIOTAP_PRESENT (                          \
+       (1 << IEEE80211_RADIOTAP_RATE) |                \
+       (1 << IEEE80211_RADIOTAP_DBM_TX_POWER) |        \
+       (1 << IEEE80211_RADIOTAP_RTS_RETRIES) |         \
+       (1 << IEEE80211_RADIOTAP_DATA_RETRIES)  |       \
+       0)
+
+#define IEEE80211_FC_VERSION_MASK    0x0003
+#define IEEE80211_FC_TYPE_MASK       0x000c
+#define IEEE80211_FC_TYPE_MGT        0x0000
+#define IEEE80211_FC_TYPE_CTL        0x0004
+#define IEEE80211_FC_TYPE_DATA       0x0008
+#define IEEE80211_FC_SUBTYPE_MASK    0x00f0
+#define IEEE80211_FC_TOFROMDS_MASK   0x0300
+#define IEEE80211_FC_TODS_MASK       0x0100
+#define IEEE80211_FC_FROMDS_MASK     0x0200
+#define IEEE80211_FC_NODS            0x0000
+#define IEEE80211_FC_TODS            0x0100
+#define IEEE80211_FC_FROMDS          0x0200
+#define IEEE80211_FC_DSTODS          0x0300
+
+struct rx_radiotap_hdr {
+       struct ieee80211_radiotap_header hdr;
+       u8 flags;
+       u8 rate;
+       u16 chan_freq;
+       u16 chan_flags;
+       u8 antenna;
+       u8 antsignal;
+       u16 rx_flags;
+#if 0
+       u8 pad[IEEE80211_RADIOTAP_HDRLEN - 18];
+#endif
+} __attribute__ ((packed));
+
+#define RX_RADIOTAP_PRESENT (                  \
+       (1 << IEEE80211_RADIOTAP_FLAGS) |       \
+       (1 << IEEE80211_RADIOTAP_RATE) |        \
+       (1 << IEEE80211_RADIOTAP_CHANNEL) |     \
+       (1 << IEEE80211_RADIOTAP_ANTENNA) |     \
+       (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL) |\
+       (1 << IEEE80211_RADIOTAP_RX_FLAGS) |    \
+       0)
+
diff --git a/drivers/net/wireless/libertas/rx.c b/drivers/net/wireless/libertas/rx.c
new file mode 100644 (file)
index 0000000..7e3f78f
--- /dev/null
@@ -0,0 +1,459 @@
+/**
+  * This file contains the handling of RX in wlan driver.
+  */
+#include <linux/etherdevice.h>
+#include <linux/types.h>
+
+#include "hostcmd.h"
+#include "radiotap.h"
+#include "decl.h"
+#include "dev.h"
+#include "wext.h"
+
+struct eth803hdr {
+       u8 dest_addr[6];
+       u8 src_addr[6];
+       u16 h803_len;
+} __attribute__ ((packed));
+
+struct rfc1042hdr {
+       u8 llc_dsap;
+       u8 llc_ssap;
+       u8 llc_ctrl;
+       u8 snap_oui[3];
+       u16 snap_type;
+} __attribute__ ((packed));
+
+struct rxpackethdr {
+       struct rxpd rx_pd;
+       struct eth803hdr eth803_hdr;
+       struct rfc1042hdr rfc1042_hdr;
+} __attribute__ ((packed));
+
+struct rx80211packethdr {
+       struct rxpd rx_pd;
+       void *eth80211_hdr;
+} __attribute__ ((packed));
+
+static int process_rxed_802_11_packet(wlan_private * priv, struct sk_buff *skb);
+
+/**
+ *  @brief This function computes the avgSNR .
+ *
+ *  @param priv    A pointer to wlan_private structure
+ *  @return       avgSNR
+ */
+static u8 wlan_getavgsnr(wlan_private * priv)
+{
+       u8 i;
+       u16 temp = 0;
+       wlan_adapter *adapter = priv->adapter;
+       if (adapter->numSNRNF == 0)
+               return 0;
+       for (i = 0; i < adapter->numSNRNF; i++)
+               temp += adapter->rawSNR[i];
+       return (u8) (temp / adapter->numSNRNF);
+
+}
+
+/**
+ *  @brief This function computes the AvgNF
+ *
+ *  @param priv    A pointer to wlan_private structure
+ *  @return       AvgNF
+ */
+static u8 wlan_getavgnf(wlan_private * priv)
+{
+       u8 i;
+       u16 temp = 0;
+       wlan_adapter *adapter = priv->adapter;
+       if (adapter->numSNRNF == 0)
+               return 0;
+       for (i = 0; i < adapter->numSNRNF; i++)
+               temp += adapter->rawNF[i];
+       return (u8) (temp / adapter->numSNRNF);
+
+}
+
+/**
+ *  @brief This function save the raw SNR/NF to our internel buffer
+ *
+ *  @param priv    A pointer to wlan_private structure
+ *  @param prxpd   A pointer to rxpd structure of received packet
+ *  @return       n/a
+ */
+static void wlan_save_rawSNRNF(wlan_private * priv, struct rxpd *p_rx_pd)
+{
+       wlan_adapter *adapter = priv->adapter;
+       if (adapter->numSNRNF < adapter->data_avg_factor)
+               adapter->numSNRNF++;
+       adapter->rawSNR[adapter->nextSNRNF] = p_rx_pd->snr;
+       adapter->rawNF[adapter->nextSNRNF] = p_rx_pd->nf;
+       adapter->nextSNRNF++;
+       if (adapter->nextSNRNF >= adapter->data_avg_factor)
+               adapter->nextSNRNF = 0;
+       return;
+}
+
+/**
+ *  @brief This function computes the RSSI in received packet.
+ *
+ *  @param priv    A pointer to wlan_private structure
+ *  @param prxpd   A pointer to rxpd structure of received packet
+ *  @return       n/a
+ */
+static void wlan_compute_rssi(wlan_private * priv, struct rxpd *p_rx_pd)
+{
+       wlan_adapter *adapter = priv->adapter;
+
+       ENTER();
+
+       lbs_pr_debug(1, "rxpd: SNR = %d, NF = %d\n", p_rx_pd->snr, p_rx_pd->nf);
+       lbs_pr_debug(1, "Before computing SNR: SNR- avg = %d, NF-avg = %d\n",
+              adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE,
+              adapter->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE);
+
+       adapter->SNR[TYPE_RXPD][TYPE_NOAVG] = p_rx_pd->snr;
+       adapter->NF[TYPE_RXPD][TYPE_NOAVG] = p_rx_pd->nf;
+       wlan_save_rawSNRNF(priv, p_rx_pd);
+
+       adapter->rxpd_rate = p_rx_pd->rx_rate;
+
+       adapter->SNR[TYPE_RXPD][TYPE_AVG] = wlan_getavgsnr(priv) * AVG_SCALE;
+       adapter->NF[TYPE_RXPD][TYPE_AVG] = wlan_getavgnf(priv) * AVG_SCALE;
+       lbs_pr_debug(1, "After computing SNR: SNR-avg = %d, NF-avg = %d\n",
+              adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE,
+              adapter->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE);
+
+       adapter->RSSI[TYPE_RXPD][TYPE_NOAVG] =
+           CAL_RSSI(adapter->SNR[TYPE_RXPD][TYPE_NOAVG],
+                    adapter->NF[TYPE_RXPD][TYPE_NOAVG]);
+
+       adapter->RSSI[TYPE_RXPD][TYPE_AVG] =
+           CAL_RSSI(adapter->SNR[TYPE_RXPD][TYPE_AVG] / AVG_SCALE,
+                    adapter->NF[TYPE_RXPD][TYPE_AVG] / AVG_SCALE);
+
+       LEAVE();
+}
+
+int libertas_upload_rx_packet(wlan_private * priv, struct sk_buff *skb)
+{
+       lbs_pr_debug(1, "skb->data=%p\n", skb->data);
+
+       if(IS_MESH_FRAME(skb))
+               skb->dev = priv->mesh_dev;
+       else
+               skb->dev = priv->wlan_dev.netdev;
+       skb->protocol = eth_type_trans(skb, priv->wlan_dev.netdev);
+       skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+       netif_rx(skb);
+
+       return 0;
+}
+
+/**
+ *  @brief This function processes received packet and forwards it
+ *  to kernel/upper layer
+ *
+ *  @param priv    A pointer to wlan_private
+ *  @param skb     A pointer to skb which includes the received packet
+ *  @return       0 or -1
+ */
+int libertas_process_rxed_packet(wlan_private * priv, struct sk_buff *skb)
+{
+       wlan_adapter *adapter = priv->adapter;
+       int ret = 0;
+
+       struct rxpackethdr *p_rx_pkt;
+       struct rxpd *p_rx_pd;
+
+       int hdrchop;
+       struct ethhdr *p_ethhdr;
+
+       const u8 rfc1042_eth_hdr[] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
+
+       ENTER();
+
+       if (priv->adapter->debugmode & MRVDRV_DEBUG_RX_PATH)
+               lbs_dbg_hex("RX packet: ", skb->data,
+                        min_t(unsigned int, skb->len, 100));
+
+       if (priv->adapter->linkmode == WLAN_LINKMODE_802_11)
+               return process_rxed_802_11_packet(priv, skb);
+
+       p_rx_pkt = (struct rxpackethdr *) skb->data;
+       p_rx_pd = &p_rx_pkt->rx_pd;
+       if (p_rx_pd->rx_control & RxPD_MESH_FRAME)
+               SET_MESH_FRAME(skb);
+       else
+               UNSET_MESH_FRAME(skb);
+
+       lbs_dbg_hex("RX Data: Before chop rxpd", skb->data,
+                min_t(unsigned int, skb->len, 100));
+
+       if (skb->len < (ETH_HLEN + 8 + sizeof(struct rxpd))) {
+               lbs_pr_debug(1, "RX error: FRAME RECEIVED WITH BAD LENGTH\n");
+               priv->stats.rx_length_errors++;
+               ret = 0;
+               goto done;
+       }
+
+       /*
+        * Check rxpd status and update 802.3 stat,
+        */
+       if (!(p_rx_pd->status & MRVDRV_RXPD_STATUS_OK)) {
+               lbs_pr_debug(1, "RX error: frame received with bad status\n");
+               lbs_pr_alert("rxpd Not OK\n");
+               priv->stats.rx_errors++;
+               ret = 0;
+               goto done;
+       }
+
+       lbs_pr_debug(1, "RX Data: skb->len - sizeof(RxPd) = %d - %d = %d\n",
+              skb->len, sizeof(struct rxpd), skb->len - sizeof(struct rxpd));
+
+       lbs_dbg_hex("RX Data: Dest", p_rx_pkt->eth803_hdr.dest_addr,
+               sizeof(p_rx_pkt->eth803_hdr.dest_addr));
+       lbs_dbg_hex("RX Data: Src", p_rx_pkt->eth803_hdr.src_addr,
+               sizeof(p_rx_pkt->eth803_hdr.src_addr));
+
+       if (memcmp(&p_rx_pkt->rfc1042_hdr,
+                  rfc1042_eth_hdr, sizeof(rfc1042_eth_hdr)) == 0) {
+               /*
+                *  Replace the 803 header and rfc1042 header (llc/snap) with an
+                *    EthernetII header, keep the src/dst and snap_type (ethertype)
+                *
+                *  The firmware only passes up SNAP frames converting
+                *    all RX Data from 802.11 to 802.2/LLC/SNAP frames.
+                *
+                *  To create the Ethernet II, just move the src, dst address right
+                *    before the snap_type.
+                */
+               p_ethhdr = (struct ethhdr *)
+                   ((u8 *) & p_rx_pkt->eth803_hdr
+                    + sizeof(p_rx_pkt->eth803_hdr) + sizeof(p_rx_pkt->rfc1042_hdr)
+                    - sizeof(p_rx_pkt->eth803_hdr.dest_addr)
+                    - sizeof(p_rx_pkt->eth803_hdr.src_addr)
+                    - sizeof(p_rx_pkt->rfc1042_hdr.snap_type));
+
+               memcpy(p_ethhdr->h_source, p_rx_pkt->eth803_hdr.src_addr,
+                      sizeof(p_ethhdr->h_source));
+               memcpy(p_ethhdr->h_dest, p_rx_pkt->eth803_hdr.dest_addr,
+                      sizeof(p_ethhdr->h_dest));
+
+               /* Chop off the rxpd + the excess memory from the 802.2/llc/snap header
+                *   that was removed
+                */
+               hdrchop = (u8 *) p_ethhdr - (u8 *) p_rx_pkt;
+       } else {
+               lbs_dbg_hex("RX Data: LLC/SNAP",
+                       (u8 *) & p_rx_pkt->rfc1042_hdr,
+                       sizeof(p_rx_pkt->rfc1042_hdr));
+
+               /* Chop off the rxpd */
+               hdrchop = (u8 *) & p_rx_pkt->eth803_hdr - (u8 *) p_rx_pkt;
+       }
+
+       /* Chop off the leading header bytes so the skb points to the start of
+        *   either the reconstructed EthII frame or the 802.2/llc/snap frame
+        */
+       skb_pull(skb, hdrchop);
+
+       /* Take the data rate from the rxpd structure
+        * only if the rate is auto
+        */
+       if (adapter->is_datarate_auto)
+               adapter->datarate = libertas_index_to_data_rate(p_rx_pd->rx_rate);
+
+       wlan_compute_rssi(priv, p_rx_pd);
+
+       lbs_pr_debug(1, "RX Data: size of actual packet = %d\n", skb->len);
+       if (libertas_upload_rx_packet(priv, skb)) {
+               lbs_pr_debug(1, "RX error: libertas_upload_rx_packet"
+                      " returns failure\n");
+               ret = -1;
+               goto done;
+       }
+       priv->stats.rx_bytes += skb->len;
+       priv->stats.rx_packets++;
+
+       ret = 0;
+done:
+       LEAVE();
+
+       return ret;
+}
+
+/**
+ *  @brief This function converts Tx/Rx rates from the Marvell WLAN format
+ *  (see Table 2 in Section 3.1) to IEEE80211_RADIOTAP_RATE units (500 Kb/s)
+ *
+ *  @param rate    Input rate
+ *  @return       Output Rate (0 if invalid)
+ */
+static u8 convert_mv_rate_to_radiotap(u8 rate)
+{
+       switch (rate) {
+       case 0:         /*   1 Mbps */
+               return 2;
+       case 1:         /*   2 Mbps */
+               return 4;
+       case 2:         /* 5.5 Mbps */
+               return 11;
+       case 3:         /*  11 Mbps */
+               return 22;
+       case 4:         /*   6 Mbps */
+               return 12;
+       case 5:         /*   9 Mbps */
+               return 18;
+       case 6:         /*  12 Mbps */
+               return 24;
+       case 7:         /*  18 Mbps */
+               return 36;
+       case 8:         /*  24 Mbps */
+               return 48;
+       case 9:         /*  36 Mbps */
+               return 72;
+       case 10:                /*  48 Mbps */
+               return 96;
+       case 11:                /*  54 Mbps */
+               return 108;
+       }
+       lbs_pr_alert( "Invalid Marvell WLAN rate (%i)\n", rate);
+       return 0;
+}
+
+/**
+ *  @brief This function processes a received 802.11 packet and forwards it
+ *  to kernel/upper layer
+ *
+ *  @param priv    A pointer to wlan_private
+ *  @param skb     A pointer to skb which includes the received packet
+ *  @return       0 or -1
+ */
+static int process_rxed_802_11_packet(wlan_private * priv, struct sk_buff *skb)
+{
+       wlan_adapter *adapter = priv->adapter;
+       int ret = 0;
+
+       struct rx80211packethdr *p_rx_pkt;
+       struct rxpd *prxpd;
+       struct rx_radiotap_hdr radiotap_hdr;
+       struct rx_radiotap_hdr *pradiotap_hdr;
+
+       ENTER();
+
+       p_rx_pkt = (struct rx80211packethdr *) skb->data;
+       prxpd = &p_rx_pkt->rx_pd;
+
+       // lbs_dbg_hex("RX Data: Before chop rxpd", skb->data, min(skb->len, 100));
+
+       if (skb->len < (ETH_HLEN + 8 + sizeof(struct rxpd))) {
+               lbs_pr_debug(1, "RX error: FRAME RECEIVED WITH BAD LENGTH\n");
+               priv->stats.rx_length_errors++;
+               ret = 0;
+               goto done;
+       }
+
+       /*
+        * Check rxpd status and update 802.3 stat,
+        */
+       if (!(prxpd->status & MRVDRV_RXPD_STATUS_OK)) {
+               //lbs_pr_debug(1, "RX error: frame received with bad status\n");
+               priv->stats.rx_errors++;
+       }
+
+       lbs_pr_debug(1, "RX Data: skb->len - sizeof(RxPd) = %d - %d = %d\n",
+              skb->len, sizeof(struct rxpd), skb->len - sizeof(struct rxpd));
+
+       /* create the exported radio header */
+       switch (priv->adapter->radiomode) {
+       case WLAN_RADIOMODE_NONE:
+               /* no radio header */
+               /* chop the rxpd */
+               skb_pull(skb, sizeof(struct rxpd));
+               break;
+
+       case WLAN_RADIOMODE_RADIOTAP:
+               /* radiotap header */
+               radiotap_hdr.hdr.it_version = 0;
+               /* XXX must check this value for pad */
+               radiotap_hdr.hdr.it_pad = 0;
+               radiotap_hdr.hdr.it_len = sizeof(struct rx_radiotap_hdr);
+               radiotap_hdr.hdr.it_present = RX_RADIOTAP_PRESENT;
+               /* unknown values */
+               radiotap_hdr.flags = 0;
+               radiotap_hdr.chan_freq = 0;
+               radiotap_hdr.chan_flags = 0;
+               radiotap_hdr.antenna = 0;
+               /* known values */
+               radiotap_hdr.rate = convert_mv_rate_to_radiotap(prxpd->rx_rate);
+               /* XXX must check no carryout */
+               radiotap_hdr.antsignal = prxpd->snr + prxpd->nf;
+               radiotap_hdr.rx_flags = 0;
+               if (!(prxpd->status & MRVDRV_RXPD_STATUS_OK))
+                       radiotap_hdr.rx_flags |= IEEE80211_RADIOTAP_F_RX_BADFCS;
+               //memset(radiotap_hdr.pad, 0x11, IEEE80211_RADIOTAP_HDRLEN - 18);
+
+               // lbs_dbg_hex1("RX radiomode packet BEF: ", skb->data, min(skb->len, 100));
+
+               /* chop the rxpd */
+               skb_pull(skb, sizeof(struct rxpd));
+
+               /* add space for the new radio header */
+               if ((skb_headroom(skb) < sizeof(struct rx_radiotap_hdr)) &&
+                   pskb_expand_head(skb, sizeof(struct rx_radiotap_hdr), 0,
+                                    GFP_ATOMIC)) {
+                       lbs_pr_alert( "%s: couldn't pskb_expand_head\n",
+                              __func__);
+               }
+
+               pradiotap_hdr =
+                   (struct rx_radiotap_hdr *)skb_push(skb,
+                                                    sizeof(struct
+                                                           rx_radiotap_hdr));
+               memcpy(pradiotap_hdr, &radiotap_hdr,
+                      sizeof(struct rx_radiotap_hdr));
+               //lbs_dbg_hex1("RX radiomode packet AFT: ", skb->data, min(skb->len, 100));
+               break;
+
+       default:
+               /* unknown header */
+               lbs_pr_alert( "Unknown radiomode (%i)\n",
+                      priv->adapter->radiomode);
+               /* don't export any header */
+               /* chop the rxpd */
+               skb_pull(skb, sizeof(struct rxpd));
+               break;
+       }
+
+       /* Take the data rate from the rxpd structure
+        * only if the rate is auto
+        */
+       if (adapter->is_datarate_auto) {
+               adapter->datarate = libertas_index_to_data_rate(prxpd->rx_rate);
+       }
+
+       wlan_compute_rssi(priv, prxpd);
+
+       lbs_pr_debug(1, "RX Data: size of actual packet = %d\n", skb->len);
+
+       if (libertas_upload_rx_packet(priv, skb)) {
+               lbs_pr_debug(1, "RX error: libertas_upload_rx_packet "
+                       "returns failure\n");
+               ret = -1;
+               goto done;
+       }
+
+       priv->stats.rx_bytes += skb->len;
+       priv->stats.rx_packets++;
+
+       ret = 0;
+done:
+       LEAVE();
+
+       skb->protocol = __constant_htons(0x0019);       /* ETH_P_80211_RAW */
+
+       return (ret);
+}
diff --git a/drivers/net/wireless/libertas/sbi.h b/drivers/net/wireless/libertas/sbi.h
new file mode 100644 (file)
index 0000000..59d3a59
--- /dev/null
@@ -0,0 +1,40 @@
+/**
+  * This file contains IF layer definitions.
+  */
+
+#ifndef        _SBI_H_
+#define        _SBI_H_
+
+#include <linux/interrupt.h>
+
+#include "defs.h"
+
+/** INT status Bit Definition*/
+#define his_cmddnldrdy                 0x01
+#define his_cardevent                  0x02
+#define his_cmdupldrdy                 0x04
+
+#ifndef DEV_NAME_LEN
+#define DEV_NAME_LEN                   32
+#endif
+
+#define SBI_EVENT_CAUSE_SHIFT          3
+
+/* Probe and Check if the card is present*/
+int libertas_sbi_register_dev(wlan_private * priv);
+int libertas_sbi_unregister_dev(wlan_private *);
+int libertas_sbi_get_int_status(wlan_private * priv, u8 *);
+int libertas_sbi_register(void);
+void libertas_sbi_unregister(void);
+int libertas_sbi_prog_firmware(wlan_private *);
+
+int libertas_sbi_read_event_cause(wlan_private *);
+int libertas_sbi_host_to_card(wlan_private * priv, u8 type, u8 * payload, u16 nb);
+wlan_private *libertas_sbi_get_priv(void *card);
+
+#ifdef ENABLE_PM
+int libertas_sbi_suspend(wlan_private *);
+int libertas_sbi_resume(wlan_private *);
+#endif
+
+#endif                         /* _SBI_H */
diff --git a/drivers/net/wireless/libertas/scan.c b/drivers/net/wireless/libertas/scan.c
new file mode 100644 (file)
index 0000000..e187062
--- /dev/null
@@ -0,0 +1,2044 @@
+/* -*- mode: C; tab-width: 4; indent-tabs-mode: nil -*- */
+/* vi: set expandtab shiftwidth=4 tabstop=4 textwidth=78: */
+
+/**
+  * Functions implementing wlan scan IOCTL and firmware command APIs
+  *
+  * IOCTL handlers as well as command preperation and response routines
+  *  for sending scan commands to the firmware.
+  */
+#include <linux/ctype.h>
+#include <linux/if.h>
+#include <linux/netdevice.h>
+#include <linux/wireless.h>
+
+#include <net/ieee80211.h>
+#include <net/iw_handler.h>
+
+#include "host.h"
+#include "decl.h"
+#include "dev.h"
+#include "scan.h"
+
+//! Approximate amount of data needed to pass a scan result back to iwlist
+#define MAX_SCAN_CELL_SIZE  (IW_EV_ADDR_LEN             \
+                             + IW_ESSID_MAX_SIZE        \
+                             + IW_EV_UINT_LEN           \
+                             + IW_EV_FREQ_LEN           \
+                             + IW_EV_QUAL_LEN           \
+                             + IW_ESSID_MAX_SIZE        \
+                             + IW_EV_PARAM_LEN          \
+                             + 40)     /* 40 for WPAIE */
+
+//! Memory needed to store a max sized channel List TLV for a firmware scan
+#define CHAN_TLV_MAX_SIZE  (sizeof(struct mrvlietypesheader)    \
+                            + (MRVDRV_MAX_CHANNELS_PER_SCAN     \
+                               * sizeof(struct chanscanparamset)))
+
+//! Memory needed to store a max number/size SSID TLV for a firmware scan
+#define SSID_TLV_MAX_SIZE  (1 * sizeof(struct mrvlietypes_ssidparamset))
+
+//! Maximum memory needed for a wlan_scan_cmd_config with all TLVs at max
+#define MAX_SCAN_CFG_ALLOC (sizeof(struct wlan_scan_cmd_config)  \
+                            + sizeof(struct mrvlietypes_numprobes)   \
+                            + CHAN_TLV_MAX_SIZE                 \
+                            + SSID_TLV_MAX_SIZE)
+
+//! The maximum number of channels the firmware can scan per command
+#define MRVDRV_MAX_CHANNELS_PER_SCAN   14
+
+/**
+ * @brief Number of channels to scan per firmware scan command issuance.
+ *
+ *  Number restricted to prevent hitting the limit on the amount of scan data
+ *  returned in a single firmware scan command.
+ */
+#define MRVDRV_CHANNELS_PER_SCAN_CMD   4
+
+//! Scan time specified in the channel TLV for each channel for passive scans
+#define MRVDRV_PASSIVE_SCAN_CHAN_TIME  100
+
+//! Scan time specified in the channel TLV for each channel for active scans
+#define MRVDRV_ACTIVE_SCAN_CHAN_TIME   100
+
+//! Macro to enable/disable SSID checking before storing a scan table
+#ifdef DISCARD_BAD_SSID
+#define CHECK_SSID_IS_VALID(x) ssid_valid(&bssidEntry.ssid)
+#else
+#define CHECK_SSID_IS_VALID(x) 1
+#endif
+
+/**
+ *  @brief Check if a scanned network compatible with the driver settings
+ *
+ *   WEP     WPA     WPA2    ad-hoc  encrypt                      Network
+ * enabled enabled  enabled   AES     mode   privacy  WPA  WPA2  Compatible
+ *    0       0        0       0      NONE      0      0    0   yes No security
+ *    1       0        0       0      NONE      1      0    0   yes Static WEP
+ *    0       1        0       0       x        1x     1    x   yes WPA
+ *    0       0        1       0       x        1x     x    1   yes WPA2
+ *    0       0        0       1      NONE      1      0    0   yes Ad-hoc AES
+ *    0       0        0       0     !=NONE     1      0    0   yes Dynamic WEP
+ *
+ *
+ *  @param adapter A pointer to wlan_adapter
+ *  @param index   Index in scantable to check against current driver settings
+ *  @param mode    Network mode: Infrastructure or IBSS
+ *
+ *  @return        Index in scantable, or error code if negative
+ */
+static int is_network_compatible(wlan_adapter * adapter, int index, int mode)
+{
+       ENTER();
+
+       if (adapter->scantable[index].inframode == mode) {
+               if (adapter->secinfo.WEPstatus == wlan802_11WEPdisabled
+                   && !adapter->secinfo.WPAenabled
+                   && !adapter->secinfo.WPA2enabled
+                   && adapter->scantable[index].wpa_supplicant.wpa_ie[0] !=
+                   WPA_IE
+                   && adapter->scantable[index].wpa2_supplicant.wpa_ie[0] !=
+                   WPA2_IE && adapter->secinfo.Encryptionmode == CIPHER_NONE
+                   && !adapter->scantable[index].privacy) {
+                       /* no security */
+                       LEAVE();
+                       return index;
+               } else if (adapter->secinfo.WEPstatus == wlan802_11WEPenabled
+                          && !adapter->secinfo.WPAenabled
+                          && !adapter->secinfo.WPA2enabled
+                          && adapter->scantable[index].privacy) {
+                       /* static WEP enabled */
+                       LEAVE();
+                       return index;
+               } else if (adapter->secinfo.WEPstatus == wlan802_11WEPdisabled
+                          && adapter->secinfo.WPAenabled
+                          && !adapter->secinfo.WPA2enabled
+                          && (adapter->scantable[index].wpa_supplicant.
+                              wpa_ie[0]
+                              == WPA_IE)
+                          /* privacy bit may NOT be set in some APs like LinkSys WRT54G
+                             && adapter->scantable[index].privacy */
+                   ) {
+                       /* WPA enabled */
+            lbs_pr_debug(1,
+                              "is_network_compatible() WPA: index=%d wpa_ie=%#x "
+                              "wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s Encmode=%#x "
+                              "privacy=%#x\n", index,
+                              adapter->scantable[index].wpa_supplicant.
+                              wpa_ie[0],
+                              adapter->scantable[index].wpa2_supplicant.
+                              wpa_ie[0],
+                              (adapter->secinfo.WEPstatus ==
+                               wlan802_11WEPenabled) ? "e" : "d",
+                              (adapter->secinfo.WPAenabled) ? "e" : "d",
+                              (adapter->secinfo.WPA2enabled) ? "e" : "d",
+                              adapter->secinfo.Encryptionmode,
+                              adapter->scantable[index].privacy);
+                       LEAVE();
+                       return index;
+               } else if (adapter->secinfo.WEPstatus == wlan802_11WEPdisabled
+                          && !adapter->secinfo.WPAenabled
+                          && adapter->secinfo.WPA2enabled
+                          && (adapter->scantable[index].wpa2_supplicant.
+                              wpa_ie[0]
+                              == WPA2_IE)
+                          /* privacy bit may NOT be set in some APs like LinkSys WRT54G
+                             && adapter->scantable[index].privacy */
+                   ) {
+                       /* WPA2 enabled */
+            lbs_pr_debug(1,
+                              "is_network_compatible() WPA2: index=%d wpa_ie=%#x "
+                              "wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s Encmode=%#x "
+                              "privacy=%#x\n", index,
+                              adapter->scantable[index].wpa_supplicant.
+                              wpa_ie[0],
+                              adapter->scantable[index].wpa2_supplicant.
+                              wpa_ie[0],
+                              (adapter->secinfo.WEPstatus ==
+                               wlan802_11WEPenabled) ? "e" : "d",
+                              (adapter->secinfo.WPAenabled) ? "e" : "d",
+                              (adapter->secinfo.WPA2enabled) ? "e" : "d",
+                              adapter->secinfo.Encryptionmode,
+                              adapter->scantable[index].privacy);
+                       LEAVE();
+                       return index;
+               } else if (adapter->secinfo.WEPstatus == wlan802_11WEPdisabled
+                          && !adapter->secinfo.WPAenabled
+                          && !adapter->secinfo.WPA2enabled
+                          && (adapter->scantable[index].wpa_supplicant.
+                              wpa_ie[0]
+                              != WPA_IE)
+                          && (adapter->scantable[index].wpa2_supplicant.
+                              wpa_ie[0]
+                              != WPA2_IE)
+                          && adapter->secinfo.Encryptionmode != CIPHER_NONE
+                          && adapter->scantable[index].privacy) {
+                       /* dynamic WEP enabled */
+            lbs_pr_debug(1,
+                              "is_network_compatible() dynamic WEP: index=%d "
+                              "wpa_ie=%#x wpa2_ie=%#x Encmode=%#x privacy=%#x\n",
+                              index,
+                              adapter->scantable[index].wpa_supplicant.
+                              wpa_ie[0],
+                              adapter->scantable[index].wpa2_supplicant.
+                              wpa_ie[0], adapter->secinfo.Encryptionmode,
+                              adapter->scantable[index].privacy);
+                       LEAVE();
+                       return index;
+               }
+
+               /* security doesn't match */
+        lbs_pr_debug(1,
+                      "is_network_compatible() FAILED: index=%d wpa_ie=%#x "
+                      "wpa2_ie=%#x WEP=%s WPA=%s WPA2=%s Encmode=%#x privacy=%#x\n",
+                      index,
+                      adapter->scantable[index].wpa_supplicant.wpa_ie[0],
+                      adapter->scantable[index].wpa2_supplicant.wpa_ie[0],
+                      (adapter->secinfo.WEPstatus ==
+                       wlan802_11WEPenabled) ? "e" : "d",
+                      (adapter->secinfo.WPAenabled) ? "e" : "d",
+                      (adapter->secinfo.WPA2enabled) ? "e" : "d",
+                      adapter->secinfo.Encryptionmode,
+                      adapter->scantable[index].privacy);
+               LEAVE();
+               return -ECONNREFUSED;
+       }
+
+       /* mode doesn't match */
+       LEAVE();
+       return -ENETUNREACH;
+}
+
+/**
+ *  @brief This function validates a SSID as being able to be printed
+ *
+ *  @param pssid   SSID structure to validate
+ *
+ *  @return        TRUE or FALSE
+ */
+static u8 ssid_valid(struct WLAN_802_11_SSID *pssid)
+{
+       int ssididx;
+
+       for (ssididx = 0; ssididx < pssid->ssidlength; ssididx++) {
+               if (!isprint(pssid->ssid[ssididx])) {
+                       return 0;
+               }
+       }
+
+       return 1;
+}
+
+/**
+ *  @brief Post process the scan table after a new scan command has completed
+ *
+ *  Inspect each entry of the scan table and try to find an entry that
+ *    matches our current associated/joined network from the scan.  If
+ *    one is found, update the stored copy of the bssdescriptor for our
+ *    current network.
+ *
+ *  Debug dump the current scan table contents if compiled accordingly.
+ *
+ *  @param priv   A pointer to wlan_private structure
+ *
+ *  @return       void
+ */
+static void wlan_scan_process_results(wlan_private * priv)
+{
+       wlan_adapter *adapter = priv->adapter;
+       int foundcurrent;
+       int i;
+
+       foundcurrent = 0;
+
+       if (adapter->connect_status == libertas_connected) {
+               /* try to find the current BSSID in the new scan list */
+               for (i = 0; i < adapter->numinscantable; i++) {
+                       if (!libertas_SSID_cmp(&adapter->scantable[i].ssid,
+                                    &adapter->curbssparams.ssid) &&
+                           !memcmp(adapter->curbssparams.bssid,
+                                   adapter->scantable[i].macaddress,
+                                   ETH_ALEN)) {
+                               foundcurrent = 1;
+                       }
+               }
+
+               if (foundcurrent) {
+                       /* Make a copy of current BSSID descriptor */
+                       memcpy(&adapter->curbssparams.bssdescriptor,
+                              &adapter->scantable[i],
+                              sizeof(adapter->curbssparams.bssdescriptor));
+               }
+       }
+
+       for (i = 0; i < adapter->numinscantable; i++) {
+               lbs_pr_debug(1, "Scan:(%02d) %02x:%02x:%02x:%02x:%02x:%02x, "
+                      "RSSI[%03d], SSID[%s]\n",
+                      i,
+                      adapter->scantable[i].macaddress[0],
+                      adapter->scantable[i].macaddress[1],
+                      adapter->scantable[i].macaddress[2],
+                      adapter->scantable[i].macaddress[3],
+                      adapter->scantable[i].macaddress[4],
+                      adapter->scantable[i].macaddress[5],
+                      (s32) adapter->scantable[i].rssi,
+                      adapter->scantable[i].ssid.ssid);
+       }
+}
+
+/**
+ *  @brief Create a channel list for the driver to scan based on region info
+ *
+ *  Use the driver region/band information to construct a comprehensive list
+ *    of channels to scan.  This routine is used for any scan that is not
+ *    provided a specific channel list to scan.
+ *
+ *  @param priv          A pointer to wlan_private structure
+ *  @param scanchanlist  Output parameter: resulting channel list to scan
+ *  @param filteredscan  Flag indicating whether or not a BSSID or SSID filter
+ *                       is being sent in the command to firmware.  Used to
+ *                       increase the number of channels sent in a scan
+ *                       command and to disable the firmware channel scan
+ *                       filter.
+ *
+ *  @return              void
+ */
+static void wlan_scan_create_channel_list(wlan_private * priv,
+                                         struct chanscanparamset * scanchanlist,
+                                         u8 filteredscan)
+{
+
+       wlan_adapter *adapter = priv->adapter;
+       struct region_channel *scanregion;
+       struct chan_freq_power *cfp;
+       int rgnidx;
+       int chanidx;
+       int nextchan;
+       u8 scantype;
+
+       chanidx = 0;
+
+       /* Set the default scan type to the user specified type, will later
+        *   be changed to passive on a per channel basis if restricted by
+        *   regulatory requirements (11d or 11h)
+        */
+       scantype = adapter->scantype;
+
+       for (rgnidx = 0; rgnidx < ARRAY_SIZE(adapter->region_channel); rgnidx++) {
+               if (priv->adapter->enable11d &&
+                   adapter->connect_status != libertas_connected) {
+                       /* Scan all the supported chan for the first scan */
+                       if (!adapter->universal_channel[rgnidx].valid)
+                               continue;
+                       scanregion = &adapter->universal_channel[rgnidx];
+
+                       /* clear the parsed_region_chan for the first scan */
+                       memset(&adapter->parsed_region_chan, 0x00,
+                              sizeof(adapter->parsed_region_chan));
+               } else {
+                       if (!adapter->region_channel[rgnidx].valid)
+                               continue;
+                       scanregion = &adapter->region_channel[rgnidx];
+               }
+
+               for (nextchan = 0;
+                    nextchan < scanregion->nrcfp; nextchan++, chanidx++) {
+
+                       cfp = scanregion->CFP + nextchan;
+
+                       if (priv->adapter->enable11d) {
+                               scantype =
+                                   libertas_get_scan_type_11d(cfp->channel,
+                                                          &adapter->
+                                                          parsed_region_chan);
+                       }
+
+                       switch (scanregion->band) {
+                       case BAND_B:
+                       case BAND_G:
+                       default:
+                               scanchanlist[chanidx].radiotype =
+                                   cmd_scan_radio_type_bg;
+                               break;
+                       }
+
+                       if (scantype == cmd_scan_type_passive) {
+                               scanchanlist[chanidx].maxscantime =
+                                   cpu_to_le16
+                                   (MRVDRV_PASSIVE_SCAN_CHAN_TIME);
+                               scanchanlist[chanidx].chanscanmode.passivescan =
+                                   1;
+                       } else {
+                               scanchanlist[chanidx].maxscantime =
+                                   cpu_to_le16
+                                   (MRVDRV_ACTIVE_SCAN_CHAN_TIME);
+                               scanchanlist[chanidx].chanscanmode.passivescan =
+                                   0;
+                       }
+
+                       scanchanlist[chanidx].channumber = cfp->channel;
+
+                       if (filteredscan) {
+                               scanchanlist[chanidx].chanscanmode.
+                                   disablechanfilt = 1;
+                       }
+               }
+       }
+}
+
+/**
+ *  @brief Construct a wlan_scan_cmd_config structure to use in issue scan cmds
+ *
+ *  Application layer or other functions can invoke wlan_scan_networks
+ *    with a scan configuration supplied in a wlan_ioctl_user_scan_cfg struct.
+ *    This structure is used as the basis of one or many wlan_scan_cmd_config
+ *    commands that are sent to the command processing module and sent to
+ *    firmware.
+ *
+ *  Create a wlan_scan_cmd_config based on the following user supplied
+ *    parameters (if present):
+ *             - SSID filter
+ *             - BSSID filter
+ *             - Number of Probes to be sent
+ *             - channel list
+ *
+ *  If the SSID or BSSID filter is not present, disable/clear the filter.
+ *  If the number of probes is not set, use the adapter default setting
+ *  Qualify the channel
+ *
+ *  @param priv             A pointer to wlan_private structure
+ *  @param puserscanin      NULL or pointer to scan configuration parameters
+ *  @param ppchantlvout     Output parameter: Pointer to the start of the
+ *                          channel TLV portion of the output scan config
+ *  @param pscanchanlist    Output parameter: Pointer to the resulting channel
+ *                          list to scan
+ *  @param pmaxchanperscan  Output parameter: Number of channels to scan for
+ *                          each issuance of the firmware scan command
+ *  @param pfilteredscan    Output parameter: Flag indicating whether or not
+ *                          a BSSID or SSID filter is being sent in the
+ *                          command to firmware.  Used to increase the number
+ *                          of channels sent in a scan command and to
+ *                          disable the firmware channel scan filter.
+ *  @param pscancurrentonly Output parameter: Flag indicating whether or not
+ *                          we are only scanning our current active channel
+ *
+ *  @return                 resulting scan configuration
+ */
+static struct wlan_scan_cmd_config *
+wlan_scan_setup_scan_config(wlan_private * priv,
+                           const struct wlan_ioctl_user_scan_cfg * puserscanin,
+                           struct mrvlietypes_chanlistparamset ** ppchantlvout,
+                           struct chanscanparamset * pscanchanlist,
+                           int *pmaxchanperscan,
+                           u8 * pfilteredscan,
+                           u8 * pscancurrentonly)
+{
+       wlan_adapter *adapter = priv->adapter;
+       const u8 zeromac[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+       struct mrvlietypes_numprobes *pnumprobestlv;
+       struct mrvlietypes_ssidparamset *pssidtlv;
+       struct wlan_scan_cmd_config * pscancfgout = NULL;
+       u8 *ptlvpos;
+       u16 numprobes;
+       u16 ssidlen;
+       int chanidx;
+       int scantype;
+       int scandur;
+       int channel;
+       int radiotype;
+
+       pscancfgout = kzalloc(MAX_SCAN_CFG_ALLOC, GFP_KERNEL);
+       if (pscancfgout == NULL)
+               goto out;
+
+       /* The tlvbufferlen is calculated for each scan command.  The TLVs added
+        *   in this routine will be preserved since the routine that sends
+        *   the command will append channelTLVs at *ppchantlvout.  The difference
+        *   between the *ppchantlvout and the tlvbuffer start will be used
+        *   to calculate the size of anything we add in this routine.
+        */
+       pscancfgout->tlvbufferlen = 0;
+
+       /* Running tlv pointer.  Assigned to ppchantlvout at end of function
+        *  so later routines know where channels can be added to the command buf
+        */
+       ptlvpos = pscancfgout->tlvbuffer;
+
+       /*
+        * Set the initial scan paramters for progressive scanning.  If a specific
+        *   BSSID or SSID is used, the number of channels in the scan command
+        *   will be increased to the absolute maximum
+        */
+       *pmaxchanperscan = MRVDRV_CHANNELS_PER_SCAN_CMD;
+
+       /* Initialize the scan as un-filtered by firmware, set to TRUE below if
+        *   a SSID or BSSID filter is sent in the command
+        */
+       *pfilteredscan = 0;
+
+       /* Initialize the scan as not being only on the current channel.  If
+        *   the channel list is customized, only contains one channel, and
+        *   is the active channel, this is set true and data flow is not halted.
+        */
+       *pscancurrentonly = 0;
+
+       if (puserscanin) {
+
+               /* Set the bss type scan filter, use adapter setting if unset */
+               pscancfgout->bsstype =
+                   (puserscanin->bsstype ? puserscanin->bsstype : adapter->
+                    scanmode);
+
+               /* Set the number of probes to send, use adapter setting if unset */
+               numprobes = (puserscanin->numprobes ? puserscanin->numprobes :
+                            adapter->scanprobes);
+
+               /*
+                * Set the BSSID filter to the incoming configuration,
+                *   if non-zero.  If not set, it will remain disabled (all zeros).
+                */
+               memcpy(pscancfgout->specificBSSID,
+                      puserscanin->specificBSSID,
+                      sizeof(pscancfgout->specificBSSID));
+
+               ssidlen = strlen(puserscanin->specificSSID);
+
+               if (ssidlen) {
+                       pssidtlv =
+                           (struct mrvlietypes_ssidparamset *) pscancfgout->
+                           tlvbuffer;
+                       pssidtlv->header.type = cpu_to_le16(TLV_TYPE_SSID);
+                       pssidtlv->header.len = cpu_to_le16(ssidlen);
+                       memcpy(pssidtlv->ssid, puserscanin->specificSSID,
+                              ssidlen);
+                       ptlvpos += sizeof(pssidtlv->header) + ssidlen;
+               }
+
+               /*
+                *  The default number of channels sent in the command is low to
+                *    ensure the response buffer from the firmware does not truncate
+                *    scan results.  That is not an issue with an SSID or BSSID
+                *    filter applied to the scan results in the firmware.
+                */
+               if (ssidlen || (memcmp(pscancfgout->specificBSSID,
+                                      &zeromac, sizeof(zeromac)) != 0)) {
+                       *pmaxchanperscan = MRVDRV_MAX_CHANNELS_PER_SCAN;
+                       *pfilteredscan = 1;
+               }
+       } else {
+               pscancfgout->bsstype = adapter->scanmode;
+               numprobes = adapter->scanprobes;
+       }
+
+       /* If the input config or adapter has the number of Probes set, add tlv */
+       if (numprobes) {
+               pnumprobestlv = (struct mrvlietypes_numprobes *) ptlvpos;
+               pnumprobestlv->header.type =
+                   cpu_to_le16(TLV_TYPE_NUMPROBES);
+               pnumprobestlv->header.len = sizeof(pnumprobestlv->numprobes);
+               pnumprobestlv->numprobes = cpu_to_le16(numprobes);
+
+               ptlvpos +=
+                   sizeof(pnumprobestlv->header) + pnumprobestlv->header.len;
+
+               pnumprobestlv->header.len =
+                   cpu_to_le16(pnumprobestlv->header.len);
+       }
+
+       /*
+        * Set the output for the channel TLV to the address in the tlv buffer
+        *   past any TLVs that were added in this fuction (SSID, numprobes).
+        *   channel TLVs will be added past this for each scan command, preserving
+        *   the TLVs that were previously added.
+        */
+       *ppchantlvout = (struct mrvlietypes_chanlistparamset *) ptlvpos;
+
+       if (puserscanin && puserscanin->chanlist[0].channumber) {
+
+               lbs_pr_debug(1, "Scan: Using supplied channel list\n");
+
+               for (chanidx = 0;
+                    chanidx < WLAN_IOCTL_USER_SCAN_CHAN_MAX
+                    && puserscanin->chanlist[chanidx].channumber; chanidx++) {
+
+                       channel = puserscanin->chanlist[chanidx].channumber;
+                       (pscanchanlist + chanidx)->channumber = channel;
+
+                       radiotype = puserscanin->chanlist[chanidx].radiotype;
+                       (pscanchanlist + chanidx)->radiotype = radiotype;
+
+                       scantype = puserscanin->chanlist[chanidx].scantype;
+
+                       if (scantype == cmd_scan_type_passive) {
+                               (pscanchanlist +
+                                chanidx)->chanscanmode.passivescan = 1;
+                       } else {
+                               (pscanchanlist +
+                                chanidx)->chanscanmode.passivescan = 0;
+                       }
+
+                       if (puserscanin->chanlist[chanidx].scantime) {
+                               scandur =
+                                   puserscanin->chanlist[chanidx].scantime;
+                       } else {
+                               if (scantype == cmd_scan_type_passive) {
+                                       scandur = MRVDRV_PASSIVE_SCAN_CHAN_TIME;
+                               } else {
+                                       scandur = MRVDRV_ACTIVE_SCAN_CHAN_TIME;
+                               }
+                       }
+
+                       (pscanchanlist + chanidx)->minscantime =
+                           cpu_to_le16(scandur);
+                       (pscanchanlist + chanidx)->maxscantime =
+                           cpu_to_le16(scandur);
+               }
+
+               /* Check if we are only scanning the current channel */
+               if ((chanidx == 1) && (puserscanin->chanlist[0].channumber
+                                      ==
+                                      priv->adapter->curbssparams.channel)) {
+                       *pscancurrentonly = 1;
+                       lbs_pr_debug(1, "Scan: Scanning current channel only");
+               }
+
+       } else {
+               lbs_pr_debug(1, "Scan: Creating full region channel list\n");
+               wlan_scan_create_channel_list(priv, pscanchanlist,
+                                             *pfilteredscan);
+       }
+
+out:
+       return pscancfgout;
+}
+
+/**
+ *  @brief Construct and send multiple scan config commands to the firmware
+ *
+ *  Previous routines have created a wlan_scan_cmd_config with any requested
+ *   TLVs.  This function splits the channel TLV into maxchanperscan lists
+ *   and sends the portion of the channel TLV along with the other TLVs
+ *   to the wlan_cmd routines for execution in the firmware.
+ *
+ *  @param priv            A pointer to wlan_private structure
+ *  @param maxchanperscan  Maximum number channels to be included in each
+ *                         scan command sent to firmware
+ *  @param filteredscan    Flag indicating whether or not a BSSID or SSID
+ *                         filter is being used for the firmware command
+ *                         scan command sent to firmware
+ *  @param pscancfgout     Scan configuration used for this scan.
+ *  @param pchantlvout     Pointer in the pscancfgout where the channel TLV
+ *                         should start.  This is past any other TLVs that
+ *                         must be sent down in each firmware command.
+ *  @param pscanchanlist   List of channels to scan in maxchanperscan segments
+ *
+ *  @return                0 or error return otherwise
+ */
+static int wlan_scan_channel_list(wlan_private * priv,
+                                 int maxchanperscan,
+                                 u8 filteredscan,
+                                 struct wlan_scan_cmd_config * pscancfgout,
+                                 struct mrvlietypes_chanlistparamset * pchantlvout,
+                                 struct chanscanparamset * pscanchanlist)
+{
+       struct chanscanparamset *ptmpchan;
+       struct chanscanparamset *pstartchan;
+       u8 scanband;
+       int doneearly;
+       int tlvidx;
+       int ret = 0;
+
+       ENTER();
+
+       if (pscancfgout == 0 || pchantlvout == 0 || pscanchanlist == 0) {
+               lbs_pr_debug(1, "Scan: Null detect: %p, %p, %p\n",
+                      pscancfgout, pchantlvout, pscanchanlist);
+               return -1;
+       }
+
+       pchantlvout->header.type = cpu_to_le16(TLV_TYPE_CHANLIST);
+
+       /* Set the temp channel struct pointer to the start of the desired list */
+       ptmpchan = pscanchanlist;
+
+       /* Loop through the desired channel list, sending a new firmware scan
+        *   commands for each maxchanperscan channels (or for 1,6,11 individually
+        *   if configured accordingly)
+        */
+       while (ptmpchan->channumber) {
+
+               tlvidx = 0;
+               pchantlvout->header.len = 0;
+               scanband = ptmpchan->radiotype;
+               pstartchan = ptmpchan;
+               doneearly = 0;
+
+               /* Construct the channel TLV for the scan command.  Continue to
+                *  insert channel TLVs until:
+                *    - the tlvidx hits the maximum configured per scan command
+                *    - the next channel to insert is 0 (end of desired channel list)
+                *    - doneearly is set (controlling individual scanning of 1,6,11)
+                */
+               while (tlvidx < maxchanperscan && ptmpchan->channumber
+                      && !doneearly) {
+
+            lbs_pr_debug(1,
+                    "Scan: Chan(%3d), Radio(%d), mode(%d,%d), Dur(%d)\n",
+                ptmpchan->channumber, ptmpchan->radiotype,
+                ptmpchan->chanscanmode.passivescan,
+                ptmpchan->chanscanmode.disablechanfilt,
+                ptmpchan->maxscantime);
+
+                       /* Copy the current channel TLV to the command being prepared */
+                       memcpy(pchantlvout->chanscanparam + tlvidx,
+                              ptmpchan, sizeof(pchantlvout->chanscanparam));
+
+                       /* Increment the TLV header length by the size appended */
+                       pchantlvout->header.len +=
+                           sizeof(pchantlvout->chanscanparam);
+
+                       /*
+                        *  The tlv buffer length is set to the number of bytes of the
+                        *    between the channel tlv pointer and the start of the
+                        *    tlv buffer.  This compensates for any TLVs that were appended
+                        *    before the channel list.
+                        */
+                       pscancfgout->tlvbufferlen = ((u8 *) pchantlvout
+                                                    - pscancfgout->tlvbuffer);
+
+                       /*  Add the size of the channel tlv header and the data length */
+                       pscancfgout->tlvbufferlen +=
+                           (sizeof(pchantlvout->header)
+                            + pchantlvout->header.len);
+
+                       /* Increment the index to the channel tlv we are constructing */
+                       tlvidx++;
+
+                       doneearly = 0;
+
+                       /* Stop the loop if the *current* channel is in the 1,6,11 set
+                        *   and we are not filtering on a BSSID or SSID.
+                        */
+                       if (!filteredscan && (ptmpchan->channumber == 1
+                                             || ptmpchan->channumber == 6
+                                             || ptmpchan->channumber == 11)) {
+                               doneearly = 1;
+                       }
+
+                       /* Increment the tmp pointer to the next channel to be scanned */
+                       ptmpchan++;
+
+                       /* Stop the loop if the *next* channel is in the 1,6,11 set.
+                        *  This will cause it to be the only channel scanned on the next
+                        *  interation
+                        */
+                       if (!filteredscan && (ptmpchan->channumber == 1
+                                             || ptmpchan->channumber == 6
+                                             || ptmpchan->channumber == 11)) {
+                               doneearly = 1;
+                       }
+               }
+
+               /* Send the scan command to the firmware with the specified cfg */
+               ret = libertas_prepare_and_send_command(priv, cmd_802_11_scan, 0,
+                                           0, 0, pscancfgout);
+       }
+
+       LEAVE();
+       return ret;
+}
+
+/**
+ *  @brief Internal function used to start a scan based on an input config
+ *
+ *  Use the input user scan configuration information when provided in
+ *    order to send the appropriate scan commands to firmware to populate or
+ *    update the internal driver scan table
+ *
+ *  @param priv          A pointer to wlan_private structure
+ *  @param puserscanin   Pointer to the input configuration for the requested
+ *                       scan.
+ *
+ *  @return              0 or < 0 if error
+ */
+int wlan_scan_networks(wlan_private * priv,
+                             const struct wlan_ioctl_user_scan_cfg * puserscanin)
+{
+       wlan_adapter *adapter = priv->adapter;
+       struct mrvlietypes_chanlistparamset *pchantlvout;
+       struct chanscanparamset * scan_chan_list = NULL;
+       struct wlan_scan_cmd_config * scan_cfg = NULL;
+       u8 keeppreviousscan;
+       u8 filteredscan;
+       u8 scancurrentchanonly;
+       int maxchanperscan;
+       int ret;
+
+       ENTER();
+
+       scan_chan_list = kzalloc(sizeof(struct chanscanparamset) *
+                               WLAN_IOCTL_USER_SCAN_CHAN_MAX, GFP_KERNEL);
+       if (scan_chan_list == NULL) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       scan_cfg = wlan_scan_setup_scan_config(priv,
+                                              puserscanin,
+                                              &pchantlvout,
+                                              scan_chan_list,
+                                              &maxchanperscan,
+                                              &filteredscan,
+                                              &scancurrentchanonly);
+       if (scan_cfg == NULL) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       keeppreviousscan = 0;
+
+       if (puserscanin) {
+               keeppreviousscan = puserscanin->keeppreviousscan;
+       }
+
+       if (!keeppreviousscan) {
+               memset(adapter->scantable, 0x00,
+                      sizeof(struct bss_descriptor) * MRVDRV_MAX_BSSID_LIST);
+               adapter->numinscantable = 0;
+       }
+
+       /* Keep the data path active if we are only scanning our current channel */
+       if (!scancurrentchanonly) {
+               netif_stop_queue(priv->wlan_dev.netdev);
+               netif_carrier_off(priv->wlan_dev.netdev);
+       }
+
+       ret = wlan_scan_channel_list(priv,
+                                    maxchanperscan,
+                                    filteredscan,
+                                    scan_cfg,
+                                    pchantlvout,
+                                    scan_chan_list);
+
+       /*  Process the resulting scan table:
+        *    - Remove any bad ssids
+        *    - Update our current BSS information from scan data
+        */
+       wlan_scan_process_results(priv);
+
+       if (priv->adapter->connect_status == libertas_connected) {
+               netif_carrier_on(priv->wlan_dev.netdev);
+               netif_wake_queue(priv->wlan_dev.netdev);
+       }
+
+out:
+       if (scan_cfg)
+               kfree(scan_cfg);
+
+       if (scan_chan_list)
+               kfree(scan_chan_list);
+
+       LEAVE();
+       return ret;
+}
+
+/**
+ *  @brief Inspect the scan response buffer for pointers to expected TLVs
+ *
+ *  TLVs can be included at the end of the scan response BSS information.
+ *    Parse the data in the buffer for pointers to TLVs that can potentially
+ *    be passed back in the response
+ *
+ *  @param ptlv        Pointer to the start of the TLV buffer to parse
+ *  @param tlvbufsize  size of the TLV buffer
+ *  @param ptsftlv     Output parameter: Pointer to the TSF TLV if found
+ *
+ *  @return            void
+ */
+static
+void wlan_ret_802_11_scan_get_tlv_ptrs(struct mrvlietypes_data * ptlv,
+                                      int tlvbufsize,
+                                      struct mrvlietypes_tsftimestamp ** ptsftlv)
+{
+       struct mrvlietypes_data *pcurrenttlv;
+       int tlvbufleft;
+       u16 tlvtype;
+       u16 tlvlen;
+
+       pcurrenttlv = ptlv;
+       tlvbufleft = tlvbufsize;
+       *ptsftlv = NULL;
+
+       lbs_pr_debug(1, "SCAN_RESP: tlvbufsize = %d\n", tlvbufsize);
+       lbs_dbg_hex("SCAN_RESP: TLV Buf", (u8 *) ptlv, tlvbufsize);
+
+       while (tlvbufleft >= sizeof(struct mrvlietypesheader)) {
+               tlvtype = le16_to_cpu(pcurrenttlv->header.type);
+               tlvlen = le16_to_cpu(pcurrenttlv->header.len);
+
+               switch (tlvtype) {
+               case TLV_TYPE_TSFTIMESTAMP:
+                       *ptsftlv = (struct mrvlietypes_tsftimestamp *) pcurrenttlv;
+                       break;
+
+               default:
+                       lbs_pr_debug(1, "SCAN_RESP: Unhandled TLV = %d\n",
+                              tlvtype);
+                       /* Give up, this seems corrupted */
+                       return;
+               }               /* switch */
+
+               tlvbufleft -= (sizeof(ptlv->header) + tlvlen);
+               pcurrenttlv =
+                   (struct mrvlietypes_data *) (pcurrenttlv->Data + tlvlen);
+       }                       /* while */
+}
+
+/**
+ *  @brief Interpret a BSS scan response returned from the firmware
+ *
+ *  Parse the various fixed fields and IEs passed back for a a BSS probe
+ *   response or beacon from the scan command.  Record information as needed
+ *   in the scan table struct bss_descriptor for that entry.
+ *
+ *  @param pBSSIDEntry  Output parameter: Pointer to the BSS Entry
+ *
+ *  @return             0 or -1
+ */
+static int InterpretBSSDescriptionWithIE(struct bss_descriptor * pBSSEntry,
+                                        u8 ** pbeaconinfo, int *bytesleft)
+{
+       enum ieeetypes_elementid elemID;
+       struct ieeetypes_fhparamset *pFH;
+       struct ieeetypes_dsparamset *pDS;
+       struct ieeetypes_cfparamset *pCF;
+       struct ieeetypes_ibssparamset *pibss;
+       struct ieeetypes_capinfo *pcap;
+       struct WLAN_802_11_FIXED_IEs fixedie;
+       u8 *pcurrentptr;
+       u8 *pRate;
+       u8 elemlen;
+       u8 bytestocopy;
+       u8 ratesize;
+       u16 beaconsize;
+       u8 founddatarateie;
+       int bytesleftforcurrentbeacon;
+
+       struct WPA_SUPPLICANT *pwpa_supplicant;
+       struct WPA_SUPPLICANT *pwpa2_supplicant;
+       struct IE_WPA *pIe;
+       const u8 oui01[4] = { 0x00, 0x50, 0xf2, 0x01 };
+
+       struct ieeetypes_countryinfoset *pcountryinfo;
+
+       ENTER();
+
+       founddatarateie = 0;
+       ratesize = 0;
+       beaconsize = 0;
+
+       if (*bytesleft >= sizeof(beaconsize)) {
+               /* Extract & convert beacon size from the command buffer */
+               memcpy(&beaconsize, *pbeaconinfo, sizeof(beaconsize));
+               beaconsize = le16_to_cpu(beaconsize);
+               *bytesleft -= sizeof(beaconsize);
+               *pbeaconinfo += sizeof(beaconsize);
+       }
+
+       if (beaconsize == 0 || beaconsize > *bytesleft) {
+
+               *pbeaconinfo += *bytesleft;
+               *bytesleft = 0;
+
+               return -1;
+       }
+
+       /* Initialize the current working beacon pointer for this BSS iteration */
+       pcurrentptr = *pbeaconinfo;
+
+       /* Advance the return beacon pointer past the current beacon */
+       *pbeaconinfo += beaconsize;
+       *bytesleft -= beaconsize;
+
+       bytesleftforcurrentbeacon = beaconsize;
+
+       pwpa_supplicant = &pBSSEntry->wpa_supplicant;
+       pwpa2_supplicant = &pBSSEntry->wpa2_supplicant;
+
+       memcpy(pBSSEntry->macaddress, pcurrentptr, ETH_ALEN);
+       lbs_pr_debug(1, "InterpretIE: AP MAC Addr-%x:%x:%x:%x:%x:%x\n",
+              pBSSEntry->macaddress[0], pBSSEntry->macaddress[1],
+              pBSSEntry->macaddress[2], pBSSEntry->macaddress[3],
+              pBSSEntry->macaddress[4], pBSSEntry->macaddress[5]);
+
+       pcurrentptr += ETH_ALEN;
+       bytesleftforcurrentbeacon -= ETH_ALEN;
+
+       if (bytesleftforcurrentbeacon < 12) {
+               lbs_pr_debug(1, "InterpretIE: Not enough bytes left\n");
+               return -1;
+       }
+
+       /*
+        * next 4 fields are RSSI, time stamp, beacon interval,
+        *   and capability information
+        */
+
+       /* RSSI is 1 byte long */
+       pBSSEntry->rssi = le32_to_cpu((long)(*pcurrentptr));
+       lbs_pr_debug(1, "InterpretIE: RSSI=%02X\n", *pcurrentptr);
+       pcurrentptr += 1;
+       bytesleftforcurrentbeacon -= 1;
+
+       /* time stamp is 8 bytes long */
+       memcpy(fixedie.timestamp, pcurrentptr, 8);
+       memcpy(pBSSEntry->timestamp, pcurrentptr, 8);
+       pcurrentptr += 8;
+       bytesleftforcurrentbeacon -= 8;
+
+       /* beacon interval is 2 bytes long */
+       memcpy(&fixedie.beaconinterval, pcurrentptr, 2);
+       pBSSEntry->beaconperiod = le16_to_cpu(fixedie.beaconinterval);
+       pcurrentptr += 2;
+       bytesleftforcurrentbeacon -= 2;
+
+       /* capability information is 2 bytes long */
+       memcpy(&fixedie.capabilities, pcurrentptr, 2);
+       lbs_pr_debug(1, "InterpretIE: fixedie.capabilities=0x%X\n",
+              fixedie.capabilities);
+       fixedie.capabilities = le16_to_cpu(fixedie.capabilities);
+       pcap = (struct ieeetypes_capinfo *) & fixedie.capabilities;
+       memcpy(&pBSSEntry->cap, pcap, sizeof(struct ieeetypes_capinfo));
+       pcurrentptr += 2;
+       bytesleftforcurrentbeacon -= 2;
+
+       /* rest of the current buffer are IE's */
+       lbs_pr_debug(1, "InterpretIE: IElength for this AP = %d\n",
+              bytesleftforcurrentbeacon);
+
+       lbs_dbg_hex("InterpretIE: IE info", (u8 *) pcurrentptr,
+               bytesleftforcurrentbeacon);
+
+       if (pcap->privacy) {
+               lbs_pr_debug(1, "InterpretIE: AP WEP enabled\n");
+               pBSSEntry->privacy = wlan802_11privfilter8021xWEP;
+       } else {
+               pBSSEntry->privacy = wlan802_11privfilteracceptall;
+       }
+
+       if (pcap->ibss == 1) {
+               pBSSEntry->inframode = wlan802_11ibss;
+       } else {
+               pBSSEntry->inframode = wlan802_11infrastructure;
+       }
+
+       /* process variable IE */
+       while (bytesleftforcurrentbeacon >= 2) {
+               elemID = (enum ieeetypes_elementid) (*((u8 *) pcurrentptr));
+               elemlen = *((u8 *) pcurrentptr + 1);
+
+               if (bytesleftforcurrentbeacon < elemlen) {
+                       lbs_pr_debug(1, "InterpretIE: error in processing IE, "
+                              "bytes left < IE length\n");
+                       bytesleftforcurrentbeacon = 0;
+                       continue;
+               }
+
+               switch (elemID) {
+
+               case SSID:
+                       pBSSEntry->ssid.ssidlength = elemlen;
+                       memcpy(pBSSEntry->ssid.ssid, (pcurrentptr + 2),
+                              elemlen);
+                       lbs_pr_debug(1, "ssid: %32s", pBSSEntry->ssid.ssid);
+                       break;
+
+               case SUPPORTED_RATES:
+                       memcpy(pBSSEntry->datarates, (pcurrentptr + 2),
+                              elemlen);
+                       memmove(pBSSEntry->libertas_supported_rates, (pcurrentptr + 2),
+                               elemlen);
+                       ratesize = elemlen;
+                       founddatarateie = 1;
+                       break;
+
+               case EXTRA_IE:
+                       lbs_pr_debug(1, "InterpretIE: EXTRA_IE Found!\n");
+                       pBSSEntry->extra_ie = 1;
+                       break;
+
+               case FH_PARAM_SET:
+                       pFH = (struct ieeetypes_fhparamset *) pcurrentptr;
+                       memmove(&pBSSEntry->phyparamset.fhparamset, pFH,
+                               sizeof(struct ieeetypes_fhparamset));
+                       pBSSEntry->phyparamset.fhparamset.dwelltime
+                           =
+                           le16_to_cpu(pBSSEntry->phyparamset.fhparamset.
+                                            dwelltime);
+                       break;
+
+               case DS_PARAM_SET:
+                       pDS = (struct ieeetypes_dsparamset *) pcurrentptr;
+
+                       pBSSEntry->channel = pDS->currentchan;
+
+                       memcpy(&pBSSEntry->phyparamset.dsparamset, pDS,
+                              sizeof(struct ieeetypes_dsparamset));
+                       break;
+
+               case CF_PARAM_SET:
+                       pCF = (struct ieeetypes_cfparamset *) pcurrentptr;
+
+                       memcpy(&pBSSEntry->ssparamset.cfparamset, pCF,
+                              sizeof(struct ieeetypes_cfparamset));
+                       break;
+
+               case IBSS_PARAM_SET:
+                       pibss = (struct ieeetypes_ibssparamset *) pcurrentptr;
+                       pBSSEntry->atimwindow =
+                           le32_to_cpu(pibss->atimwindow);
+
+                       memmove(&pBSSEntry->ssparamset.ibssparamset, pibss,
+                               sizeof(struct ieeetypes_ibssparamset));
+
+                       pBSSEntry->ssparamset.ibssparamset.atimwindow
+                           =
+                           le16_to_cpu(pBSSEntry->ssparamset.ibssparamset.
+                                            atimwindow);
+                       break;
+
+                       /* Handle Country Info IE */
+               case COUNTRY_INFO:
+                       pcountryinfo =
+                           (struct ieeetypes_countryinfoset *) pcurrentptr;
+
+                       if (pcountryinfo->len <
+                           sizeof(pcountryinfo->countrycode)
+                           || pcountryinfo->len > 254) {
+                               lbs_pr_debug(1, "InterpretIE: 11D- Err "
+                                      "CountryInfo len =%d min=%d max=254\n",
+                                      pcountryinfo->len,
+                                      sizeof(pcountryinfo->countrycode));
+                               LEAVE();
+                               return -1;
+                       }
+
+                       memcpy(&pBSSEntry->countryinfo,
+                              pcountryinfo, pcountryinfo->len + 2);
+                       lbs_dbg_hex("InterpretIE: 11D- CountryInfo:",
+                               (u8 *) pcountryinfo,
+                               (u32) (pcountryinfo->len + 2));
+                       break;
+
+               case EXTENDED_SUPPORTED_RATES:
+                       /*
+                        * only process extended supported rate
+                        * if data rate is already found.
+                        * data rate IE should come before
+                        * extended supported rate IE
+                        */
+                       if (founddatarateie) {
+                               if ((elemlen + ratesize) > WLAN_SUPPORTED_RATES) {
+                                       bytestocopy =
+                                           (WLAN_SUPPORTED_RATES - ratesize);
+                               } else {
+                                       bytestocopy = elemlen;
+                               }
+
+                               pRate = (u8 *) pBSSEntry->datarates;
+                               pRate += ratesize;
+                               memmove(pRate, (pcurrentptr + 2), bytestocopy);
+
+                               pRate = (u8 *) pBSSEntry->libertas_supported_rates;
+
+                               pRate += ratesize;
+                               memmove(pRate, (pcurrentptr + 2), bytestocopy);
+                       }
+                       break;
+
+               case VENDOR_SPECIFIC_221:
+#define IE_ID_LEN_FIELDS_BYTES 2
+                       pIe = (struct IE_WPA *)pcurrentptr;
+
+                       if (!memcmp(pIe->oui, oui01, sizeof(oui01))) {
+                               pwpa_supplicant->wpa_ie_len
+                                   = min_t(size_t, elemlen + IE_ID_LEN_FIELDS_BYTES,
+                                         sizeof(pwpa_supplicant->wpa_ie));
+                               memcpy(pwpa_supplicant->wpa_ie,
+                                      pcurrentptr,
+                                      pwpa_supplicant->wpa_ie_len);
+                               lbs_dbg_hex("InterpretIE: Resp WPA_IE",
+                                       pwpa_supplicant->wpa_ie, elemlen);
+                       }
+                       break;
+               case WPA2_IE:
+                       pIe = (struct IE_WPA *)pcurrentptr;
+                       pwpa2_supplicant->wpa_ie_len
+                           = min_t(size_t, elemlen + IE_ID_LEN_FIELDS_BYTES,
+                                 sizeof(pwpa2_supplicant->wpa_ie));
+                       memcpy(pwpa2_supplicant->wpa_ie,
+                              pcurrentptr, pwpa2_supplicant->wpa_ie_len);
+
+                       lbs_dbg_hex("InterpretIE: Resp WPA2_IE",
+                               pwpa2_supplicant->wpa_ie, elemlen);
+                       break;
+               case TIM:
+                       break;
+
+               case CHALLENGE_TEXT:
+                       break;
+               }
+
+               pcurrentptr += elemlen + 2;
+
+               /* need to account for IE ID and IE len */
+               bytesleftforcurrentbeacon -= (elemlen + 2);
+
+       }                       /* while (bytesleftforcurrentbeacon > 2) */
+
+       return 0;
+}
+
+/**
+ *  @brief Compare two SSIDs
+ *
+ *  @param ssid1    A pointer to ssid to compare
+ *  @param ssid2    A pointer to ssid to compare
+ *
+ *  @return         0--ssid is same, otherwise is different
+ */
+int libertas_SSID_cmp(struct WLAN_802_11_SSID *ssid1, struct WLAN_802_11_SSID *ssid2)
+{
+       if (!ssid1 || !ssid2)
+               return -1;
+
+       if (ssid1->ssidlength != ssid2->ssidlength)
+               return -1;
+
+       return memcmp(ssid1->ssid, ssid2->ssid, ssid1->ssidlength);
+}
+
+/**
+ *  @brief This function finds a specific compatible BSSID in the scan list
+ *
+ *  @param adapter  A pointer to wlan_adapter
+ *  @param bssid    BSSID to find in the scan list
+ *  @param mode     Network mode: Infrastructure or IBSS
+ *
+ *  @return         index in BSSID list, or error return code (< 0)
+ */
+int libertas_find_BSSID_in_list(wlan_adapter * adapter, u8 * bssid, int mode)
+{
+       int ret = -ENETUNREACH;
+       int i;
+
+       if (!bssid)
+               return -EFAULT;
+
+       lbs_pr_debug(1, "FindBSSID: Num of BSSIDs = %d\n",
+              adapter->numinscantable);
+
+       /* Look through the scan table for a compatible match. The ret return
+        *   variable will be equal to the index in the scan table (greater
+        *   than zero) if the network is compatible.  The loop will continue
+        *   past a matched bssid that is not compatible in case there is an
+        *   AP with multiple SSIDs assigned to the same BSSID
+        */
+       for (i = 0; ret < 0 && i < adapter->numinscantable; i++) {
+               if (!memcmp(adapter->scantable[i].macaddress, bssid, ETH_ALEN)) {
+                       switch (mode) {
+                       case wlan802_11infrastructure:
+                       case wlan802_11ibss:
+                               ret = is_network_compatible(adapter, i, mode);
+                               break;
+                       default:
+                               ret = i;
+                               break;
+                       }
+               }
+       }
+
+       return ret;
+}
+
+/**
+ *  @brief This function finds ssid in ssid list.
+ *
+ *  @param adapter  A pointer to wlan_adapter
+ *  @param ssid     SSID to find in the list
+ *  @param bssid    BSSID to qualify the SSID selection (if provided)
+ *  @param mode     Network mode: Infrastructure or IBSS
+ *
+ *  @return         index in BSSID list
+ */
+int libertas_find_SSID_in_list(wlan_adapter * adapter,
+                  struct WLAN_802_11_SSID *ssid, u8 * bssid, int mode)
+{
+       int net = -ENETUNREACH;
+       u8 bestrssi = 0;
+       int i;
+       int j;
+
+       lbs_pr_debug(1, "Num of Entries in Table = %d\n", adapter->numinscantable);
+
+       for (i = 0; i < adapter->numinscantable; i++) {
+               if (!libertas_SSID_cmp(&adapter->scantable[i].ssid, ssid) &&
+                   (!bssid ||
+                    !memcmp(adapter->scantable[i].
+                            macaddress, bssid, ETH_ALEN))) {
+                       switch (mode) {
+                       case wlan802_11infrastructure:
+                       case wlan802_11ibss:
+                               j = is_network_compatible(adapter, i, mode);
+
+                               if (j >= 0) {
+                                       if (bssid) {
+                                               return i;
+                                       }
+
+                                       if (SCAN_RSSI
+                                           (adapter->scantable[i].rssi)
+                                           > bestrssi) {
+                                               bestrssi =
+                                                   SCAN_RSSI(adapter->
+                                                             scantable[i].
+                                                             rssi);
+                                               net = i;
+                                       }
+                               } else {
+                                       if (net == -ENETUNREACH) {
+                                               net = j;
+                                       }
+                               }
+                               break;
+                       case wlan802_11autounknown:
+                       default:
+                               if (SCAN_RSSI(adapter->scantable[i].rssi)
+                                   > bestrssi) {
+                                       bestrssi =
+                                           SCAN_RSSI(adapter->scantable[i].
+                                                     rssi);
+                                       net = i;
+                               }
+                               break;
+                       }
+               }
+       }
+
+       return net;
+}
+
+/**
+ *  @brief This function finds the best SSID in the Scan List
+ *
+ *  Search the scan table for the best SSID that also matches the current
+ *   adapter network preference (infrastructure or adhoc)
+ *
+ *  @param adapter  A pointer to wlan_adapter
+ *
+ *  @return         index in BSSID list
+ */
+int libertas_find_best_SSID_in_list(wlan_adapter * adapter,
+                                    enum WLAN_802_11_NETWORK_INFRASTRUCTURE mode)
+{
+       int bestnet = -ENETUNREACH;
+       u8 bestrssi = 0;
+       int i;
+
+       ENTER();
+
+       lbs_pr_debug(1, "Num of BSSIDs = %d\n", adapter->numinscantable);
+
+       for (i = 0; i < adapter->numinscantable; i++) {
+               switch (mode) {
+               case wlan802_11infrastructure:
+               case wlan802_11ibss:
+                       if (is_network_compatible(adapter, i, mode) >= 0) {
+                               if (SCAN_RSSI(adapter->scantable[i].rssi) >
+                                   bestrssi) {
+                                       bestrssi =
+                                           SCAN_RSSI(adapter->scantable[i].
+                                                     rssi);
+                                       bestnet = i;
+                               }
+                       }
+                       break;
+               case wlan802_11autounknown:
+               default:
+                       if (SCAN_RSSI(adapter->scantable[i].rssi) > bestrssi) {
+                               bestrssi =
+                                   SCAN_RSSI(adapter->scantable[i].rssi);
+                               bestnet = i;
+                       }
+                       break;
+               }
+       }
+
+       LEAVE();
+       return bestnet;
+}
+
+/**
+ *  @brief Find the AP with specific ssid in the scan list
+ *
+ *  @param priv         A pointer to wlan_private structure
+ *  @param pSSID        A pointer to AP's ssid
+ *
+ *  @return             0--success, otherwise--fail
+ */
+int libertas_find_best_network_SSID(wlan_private * priv,
+                                    struct WLAN_802_11_SSID *pSSID,
+                                    enum WLAN_802_11_NETWORK_INFRASTRUCTURE preferred_mode,
+                                    enum WLAN_802_11_NETWORK_INFRASTRUCTURE *out_mode)
+{
+       wlan_adapter *adapter = priv->adapter;
+       int ret = 0;
+       struct bss_descriptor *preqbssid;
+       int i;
+
+       ENTER();
+
+       memset(pSSID, 0, sizeof(struct WLAN_802_11_SSID));
+
+       wlan_scan_networks(priv, NULL);
+       if (adapter->surpriseremoved)
+               return -1;
+       wait_event_interruptible(adapter->cmd_pending, !adapter->nr_cmd_pending);
+
+       i = libertas_find_best_SSID_in_list(adapter, preferred_mode);
+       if (i < 0) {
+               ret = -1;
+               goto out;
+       }
+
+       preqbssid = &adapter->scantable[i];
+       memcpy(pSSID, &preqbssid->ssid,
+              sizeof(struct WLAN_802_11_SSID));
+       *out_mode = preqbssid->inframode;
+
+       if (!pSSID->ssidlength) {
+               ret = -1;
+       }
+
+out:
+       LEAVE();
+       return ret;
+}
+
+/**
+ *  @brief Scan Network
+ *
+ *  @param dev          A pointer to net_device structure
+ *  @param info         A pointer to iw_request_info structure
+ *  @param vwrq         A pointer to iw_param structure
+ *  @param extra        A pointer to extra data buf
+ *
+ *  @return             0 --success, otherwise fail
+ */
+int libertas_set_scan(struct net_device *dev, struct iw_request_info *info,
+                 struct iw_param *vwrq, char *extra)
+{
+       wlan_private *priv = dev->priv;
+       wlan_adapter *adapter = priv->adapter;
+       union iwreq_data wrqu;
+
+       ENTER();
+
+       if (!wlan_scan_networks(priv, NULL)) {
+               memset(&wrqu, 0, sizeof(union iwreq_data));
+               wireless_send_event(priv->wlan_dev.netdev, SIOCGIWSCAN, &wrqu,
+                                   NULL);
+       }
+
+       if (adapter->surpriseremoved)
+               return -1;
+
+       LEAVE();
+       return 0;
+}
+
+/**
+ *  @brief Send a scan command for all available channels filtered on a spec
+ *
+ *  @param priv             A pointer to wlan_private structure
+ *  @param prequestedssid   A pointer to AP's ssid
+ *  @param keeppreviousscan Flag used to save/clear scan table before scan
+ *
+ *  @return                0-success, otherwise fail
+ */
+int libertas_send_specific_SSID_scan(wlan_private * priv,
+                        struct WLAN_802_11_SSID *prequestedssid,
+                        u8 keeppreviousscan)
+{
+       wlan_adapter *adapter = priv->adapter;
+       struct wlan_ioctl_user_scan_cfg scancfg;
+
+       ENTER();
+
+       if (prequestedssid == NULL) {
+               return -1;
+       }
+
+       memset(&scancfg, 0x00, sizeof(scancfg));
+
+       memcpy(scancfg.specificSSID, prequestedssid->ssid,
+              prequestedssid->ssidlength);
+       scancfg.keeppreviousscan = keeppreviousscan;
+
+       wlan_scan_networks(priv, &scancfg);
+       if (adapter->surpriseremoved)
+               return -1;
+       wait_event_interruptible(adapter->cmd_pending, !adapter->nr_cmd_pending);
+
+       LEAVE();
+       return 0;
+}
+
+/**
+ *  @brief scan an AP with specific BSSID
+ *
+ *  @param priv             A pointer to wlan_private structure
+ *  @param bssid            A pointer to AP's bssid
+ *  @param keeppreviousscan Flag used to save/clear scan table before scan
+ *
+ *  @return          0-success, otherwise fail
+ */
+int libertas_send_specific_BSSID_scan(wlan_private * priv, u8 * bssid, u8 keeppreviousscan)
+{
+       struct wlan_ioctl_user_scan_cfg scancfg;
+
+       ENTER();
+
+       if (bssid == NULL) {
+               return -1;
+       }
+
+       memset(&scancfg, 0x00, sizeof(scancfg));
+       memcpy(scancfg.specificBSSID, bssid, sizeof(scancfg.specificBSSID));
+       scancfg.keeppreviousscan = keeppreviousscan;
+
+       wlan_scan_networks(priv, &scancfg);
+       if (priv->adapter->surpriseremoved)
+               return -1;
+       wait_event_interruptible(priv->adapter->cmd_pending,
+               !priv->adapter->nr_cmd_pending);
+
+       LEAVE();
+       return 0;
+}
+
+/**
+ *  @brief  Retrieve the scan table entries via wireless tools IOCTL call
+ *
+ *  @param dev          A pointer to net_device structure
+ *  @param info         A pointer to iw_request_info structure
+ *  @param dwrq         A pointer to iw_point structure
+ *  @param extra        A pointer to extra data buf
+ *
+ *  @return             0 --success, otherwise fail
+ */
+int libertas_get_scan(struct net_device *dev, struct iw_request_info *info,
+                 struct iw_point *dwrq, char *extra)
+{
+       wlan_private *priv = dev->priv;
+       wlan_adapter *adapter = priv->adapter;
+       int ret = 0;
+       char *current_ev = extra;
+       char *end_buf = extra + IW_SCAN_MAX_DATA;
+       struct chan_freq_power *cfp;
+       struct bss_descriptor *pscantable;
+       char *current_val;      /* For rates */
+       struct iw_event iwe;    /* Temporary buffer */
+       int i;
+       int j;
+       int rate;
+#define PERFECT_RSSI ((u8)50)
+#define WORST_RSSI   ((u8)0)
+#define RSSI_DIFF    ((u8)(PERFECT_RSSI - WORST_RSSI))
+       u8 rssi;
+
+       u8 buf[16 + 256 * 2];
+       u8 *ptr;
+
+       ENTER();
+
+       /*
+        * if there's either commands in the queue or one being
+        * processed return -EAGAIN for iwlist to retry later.
+        */
+    if (adapter->nr_cmd_pending)
+               return -EAGAIN;
+
+       if (adapter->connect_status == libertas_connected)
+               lbs_pr_debug(1, "Current ssid: %32s\n",
+                      adapter->curbssparams.ssid.ssid);
+
+       lbs_pr_debug(1, "Scan: Get: numinscantable = %d\n",
+              adapter->numinscantable);
+
+       /* The old API using SIOCGIWAPLIST had a hard limit of IW_MAX_AP.
+        * The new API using SIOCGIWSCAN is only limited by buffer size
+        * WE-14 -> WE-16 the buffer is limited to IW_SCAN_MAX_DATA bytes
+        * which is 4096.
+        */
+       for (i = 0; i < adapter->numinscantable; i++) {
+               if ((current_ev + MAX_SCAN_CELL_SIZE) >= end_buf) {
+                       lbs_pr_debug(1, "i=%d break out: current_ev=%p end_buf=%p "
+                              "MAX_SCAN_CELL_SIZE=%d\n",
+                              i, current_ev, end_buf, MAX_SCAN_CELL_SIZE);
+                       break;
+               }
+
+               pscantable = &adapter->scantable[i];
+
+               lbs_pr_debug(1, "i=%d  ssid: %32s\n", i, pscantable->ssid.ssid);
+
+               cfp =
+                   libertas_find_cfp_by_band_and_channel(adapter, 0,
+                                                pscantable->channel);
+               if (!cfp) {
+                       lbs_pr_debug(1, "Invalid channel number %d\n",
+                              pscantable->channel);
+                       continue;
+               }
+
+               if (!ssid_valid(&adapter->scantable[i].ssid)) {
+                       continue;
+               }
+
+               /* First entry *MUST* be the AP MAC address */
+               iwe.cmd = SIOCGIWAP;
+               iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
+               memcpy(iwe.u.ap_addr.sa_data,
+                      &adapter->scantable[i].macaddress, ETH_ALEN);
+
+               iwe.len = IW_EV_ADDR_LEN;
+               current_ev =
+                   iwe_stream_add_event(current_ev, end_buf, &iwe, iwe.len);
+
+               //Add the ESSID
+               iwe.u.data.length = adapter->scantable[i].ssid.ssidlength;
+
+               if (iwe.u.data.length > 32) {
+                       iwe.u.data.length = 32;
+               }
+
+               iwe.cmd = SIOCGIWESSID;
+               iwe.u.data.flags = 1;
+               iwe.len = IW_EV_POINT_LEN + iwe.u.data.length;
+               current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe,
+                                                 adapter->scantable[i].ssid.
+                                                 ssid);
+
+               //Add mode
+               iwe.cmd = SIOCGIWMODE;
+               iwe.u.mode = adapter->scantable[i].inframode + 1;
+               iwe.len = IW_EV_UINT_LEN;
+               current_ev =
+                   iwe_stream_add_event(current_ev, end_buf, &iwe, iwe.len);
+
+               //frequency
+               iwe.cmd = SIOCGIWFREQ;
+               iwe.u.freq.m = (long)cfp->freq * 100000;
+               iwe.u.freq.e = 1;
+               iwe.len = IW_EV_FREQ_LEN;
+               current_ev =
+                   iwe_stream_add_event(current_ev, end_buf, &iwe, iwe.len);
+
+               /* Add quality statistics */
+               iwe.cmd = IWEVQUAL;
+               iwe.u.qual.updated = IW_QUAL_ALL_UPDATED;
+               iwe.u.qual.level = SCAN_RSSI(adapter->scantable[i].rssi);
+
+               rssi = iwe.u.qual.level - MRVDRV_NF_DEFAULT_SCAN_VALUE;
+               iwe.u.qual.qual =
+                   (100 * RSSI_DIFF * RSSI_DIFF - (PERFECT_RSSI - rssi) *
+                    (15 * (RSSI_DIFF) + 62 * (PERFECT_RSSI - rssi))) /
+                   (RSSI_DIFF * RSSI_DIFF);
+               if (iwe.u.qual.qual > 100)
+                       iwe.u.qual.qual = 100;
+               else if (iwe.u.qual.qual < 1)
+                       iwe.u.qual.qual = 0;
+
+               if (adapter->NF[TYPE_BEACON][TYPE_NOAVG] == 0) {
+                       iwe.u.qual.noise = MRVDRV_NF_DEFAULT_SCAN_VALUE;
+               } else {
+                       iwe.u.qual.noise =
+                           CAL_NF(adapter->NF[TYPE_BEACON][TYPE_NOAVG]);
+               }
+               if ((adapter->inframode == wlan802_11ibss) &&
+                   !libertas_SSID_cmp(&adapter->curbssparams.ssid,
+                            &adapter->scantable[i].ssid)
+                   && adapter->adhoccreate) {
+                       ret = libertas_prepare_and_send_command(priv,
+                                                   cmd_802_11_rssi,
+                                                   0,
+                                                   cmd_option_waitforrsp,
+                                                   0, NULL);
+
+                       if (!ret) {
+                               iwe.u.qual.level =
+                                   CAL_RSSI(adapter->SNR[TYPE_RXPD][TYPE_AVG] /
+                                            AVG_SCALE,
+                                            adapter->NF[TYPE_RXPD][TYPE_AVG] /
+                                            AVG_SCALE);
+                       }
+               }
+               iwe.len = IW_EV_QUAL_LEN;
+               current_ev =
+                   iwe_stream_add_event(current_ev, end_buf, &iwe, iwe.len);
+
+               /* Add encryption capability */
+               iwe.cmd = SIOCGIWENCODE;
+               if (adapter->scantable[i].privacy) {
+                       iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
+               } else {
+                       iwe.u.data.flags = IW_ENCODE_DISABLED;
+               }
+               iwe.u.data.length = 0;
+               iwe.len = IW_EV_POINT_LEN + iwe.u.data.length;
+               current_ev = iwe_stream_add_point(current_ev, end_buf, &iwe,
+                                                 adapter->scantable->ssid.
+                                                 ssid);
+
+               current_val = current_ev + IW_EV_LCP_LEN;
+
+               iwe.cmd = SIOCGIWRATE;
+
+               iwe.u.bitrate.fixed = 0;
+               iwe.u.bitrate.disabled = 0;
+               iwe.u.bitrate.value = 0;
+
+               /* Bit rate given in 500 kb/s units (+ 0x80) */
+               for (j = 0; j < sizeof(adapter->scantable[i].libertas_supported_rates);
+                    j++) {
+                       if (adapter->scantable[i].libertas_supported_rates[j] == 0) {
+                               break;
+                       }
+                       rate =
+                           (adapter->scantable[i].libertas_supported_rates[j] & 0x7F) *
+                           500000;
+                       if (rate > iwe.u.bitrate.value) {
+                               iwe.u.bitrate.value = rate;
+                       }
+
+                       iwe.u.bitrate.value =
+                           (adapter->scantable[i].libertas_supported_rates[j]
+                            & 0x7f) * 500000;
+                       iwe.len = IW_EV_PARAM_LEN;
+                       current_ev =
+                           iwe_stream_add_value(current_ev, current_val,
+                                                end_buf, &iwe, iwe.len);
+
+               }
+               if ((adapter->scantable[i].inframode == wlan802_11ibss)
+                   && !libertas_SSID_cmp(&adapter->curbssparams.ssid,
+                               &adapter->scantable[i].ssid)
+                   && adapter->adhoccreate) {
+                       iwe.u.bitrate.value = 22 * 500000;
+               }
+               iwe.len = IW_EV_PARAM_LEN;
+               current_ev =
+                   iwe_stream_add_value(current_ev, current_val, end_buf, &iwe,
+                                        iwe.len);
+
+               /* Add new value to event */
+               current_val = current_ev + IW_EV_LCP_LEN;
+
+               if (adapter->scantable[i].wpa2_supplicant.wpa_ie[0] == WPA2_IE) {
+                       memset(&iwe, 0, sizeof(iwe));
+                       memset(buf, 0, sizeof(buf));
+                       memcpy(buf, adapter->scantable[i].
+                                               wpa2_supplicant.wpa_ie,
+                                       adapter->scantable[i].wpa2_supplicant.
+                                               wpa_ie_len);
+                       iwe.cmd = IWEVGENIE;
+                       iwe.u.data.length = adapter->scantable[i].
+                                       wpa2_supplicant.wpa_ie_len;
+                       iwe.len = IW_EV_POINT_LEN + iwe.u.data.length;
+                       current_ev = iwe_stream_add_point(current_ev, end_buf,
+                                       &iwe, buf);
+               }
+               if (adapter->scantable[i].wpa_supplicant.wpa_ie[0] == WPA_IE) {
+                       memset(&iwe, 0, sizeof(iwe));
+                       memset(buf, 0, sizeof(buf));
+                       memcpy(buf, adapter->scantable[i].
+                                               wpa_supplicant.wpa_ie,
+                                       adapter->scantable[i].wpa_supplicant.
+                                               wpa_ie_len);
+                       iwe.cmd = IWEVGENIE;
+                       iwe.u.data.length = adapter->scantable[i].
+                                       wpa_supplicant.wpa_ie_len;
+                       iwe.len = IW_EV_POINT_LEN + iwe.u.data.length;
+                       current_ev = iwe_stream_add_point(current_ev, end_buf,
+                                       &iwe, buf);
+               }
+
+
+               if (adapter->scantable[i].extra_ie != 0) {
+                       memset(&iwe, 0, sizeof(iwe));
+                       memset(buf, 0, sizeof(buf));
+                       ptr = buf;
+                       ptr += sprintf(ptr, "extra_ie");
+                       iwe.u.data.length = strlen(buf);
+
+                       lbs_pr_debug(1, "iwe.u.data.length %d\n",
+                              iwe.u.data.length);
+                       lbs_pr_debug(1, "BUF: %s \n", buf);
+
+                       iwe.cmd = IWEVCUSTOM;
+                       iwe.len = IW_EV_POINT_LEN + iwe.u.data.length;
+                       current_ev =
+                           iwe_stream_add_point(current_ev, end_buf, &iwe,
+                                                buf);
+               }
+
+               current_val = current_ev + IW_EV_LCP_LEN;
+
+               /*
+                * Check if we added any event
+                */
+               if ((current_val - current_ev) > IW_EV_LCP_LEN)
+                       current_ev = current_val;
+       }
+
+       dwrq->length = (current_ev - extra);
+       dwrq->flags = 0;
+
+       LEAVE();
+       return 0;
+}
+
+/**
+ *  @brief Prepare a scan command to be sent to the firmware
+ *
+ *  Use the wlan_scan_cmd_config sent to the command processing module in
+ *   the libertas_prepare_and_send_command to configure a cmd_ds_802_11_scan command
+ *   struct to send to firmware.
+ *
+ *  The fixed fields specifying the BSS type and BSSID filters as well as a
+ *   variable number/length of TLVs are sent in the command to firmware.
+ *
+ *  @param priv       A pointer to wlan_private structure
+ *  @param cmd        A pointer to cmd_ds_command structure to be sent to
+ *                    firmware with the cmd_DS_801_11_SCAN structure
+ *  @param pdata_buf  Void pointer cast of a wlan_scan_cmd_config struct used
+ *                    to set the fields/TLVs for the command sent to firmware
+ *
+ *  @return           0 or -1
+ *
+ *  @sa wlan_scan_create_channel_list
+ */
+int libertas_cmd_80211_scan(wlan_private * priv,
+                        struct cmd_ds_command *cmd, void *pdata_buf)
+{
+       struct cmd_ds_802_11_scan *pscan = &cmd->params.scan;
+       struct wlan_scan_cmd_config *pscancfg;
+
+       ENTER();
+
+       pscancfg = pdata_buf;
+
+       /* Set fixed field variables in scan command */
+       pscan->bsstype = pscancfg->bsstype;
+       memcpy(pscan->BSSID, pscancfg->specificBSSID, sizeof(pscan->BSSID));
+       memcpy(pscan->tlvbuffer, pscancfg->tlvbuffer, pscancfg->tlvbufferlen);
+
+       cmd->command = cpu_to_le16(cmd_802_11_scan);
+
+       /* size is equal to the sizeof(fixed portions) + the TLV len + header */
+       cmd->size = cpu_to_le16(sizeof(pscan->bsstype)
+                                    + sizeof(pscan->BSSID)
+                                    + pscancfg->tlvbufferlen + S_DS_GEN);
+
+       lbs_pr_debug(1, "SCAN_CMD: command=%x, size=%x, seqnum=%x\n",
+              cmd->command, cmd->size, cmd->seqnum);
+       LEAVE();
+       return 0;
+}
+
+/**
+ *  @brief This function handles the command response of scan
+ *
+ *   The response buffer for the scan command has the following
+ *      memory layout:
+ *
+ *     .-----------------------------------------------------------.
+ *     |  header (4 * sizeof(u16)):  Standard command response hdr |
+ *     .-----------------------------------------------------------.
+ *     |  bufsize (u16) : sizeof the BSS Description data          |
+ *     .-----------------------------------------------------------.
+ *     |  NumOfSet (u8) : Number of BSS Descs returned             |
+ *     .-----------------------------------------------------------.
+ *     |  BSSDescription data (variable, size given in bufsize)    |
+ *     .-----------------------------------------------------------.
+ *     |  TLV data (variable, size calculated using header->size,  |
+ *     |            bufsize and sizeof the fixed fields above)     |
+ *     .-----------------------------------------------------------.
+ *
+ *  @param priv    A pointer to wlan_private structure
+ *  @param resp    A pointer to cmd_ds_command
+ *
+ *  @return        0 or -1
+ */
+int libertas_ret_80211_scan(wlan_private * priv, struct cmd_ds_command *resp)
+{
+       wlan_adapter *adapter = priv->adapter;
+       struct cmd_ds_802_11_scan_rsp *pscan;
+       struct bss_descriptor newbssentry;
+       struct mrvlietypes_data *ptlv;
+       struct mrvlietypes_tsftimestamp *ptsftlv;
+       u8 *pbssinfo;
+       u16 scanrespsize;
+       int bytesleft;
+       int numintable;
+       int bssIdx;
+       int idx;
+       int tlvbufsize;
+       u64 tsfval;
+
+       ENTER();
+
+       pscan = &resp->params.scanresp;
+
+       if (pscan->nr_sets > MRVDRV_MAX_BSSID_LIST) {
+        lbs_pr_debug(1,
+                      "SCAN_RESP: Invalid number of AP returned (%d)!!\n",
+                      pscan->nr_sets);
+               LEAVE();
+               return -1;
+       }
+
+       bytesleft = le16_to_cpu(pscan->bssdescriptsize);
+       lbs_pr_debug(1, "SCAN_RESP: bssdescriptsize %d\n", bytesleft);
+
+       scanrespsize = le16_to_cpu(resp->size);
+       lbs_pr_debug(1, "SCAN_RESP: returned %d AP before parsing\n",
+              pscan->nr_sets);
+
+       numintable = adapter->numinscantable;
+       pbssinfo = pscan->bssdesc_and_tlvbuffer;
+
+       /* The size of the TLV buffer is equal to the entire command response
+        *   size (scanrespsize) minus the fixed fields (sizeof()'s), the
+        *   BSS Descriptions (bssdescriptsize as bytesLef) and the command
+        *   response header (S_DS_GEN)
+        */
+       tlvbufsize = scanrespsize - (bytesleft + sizeof(pscan->bssdescriptsize)
+                                    + sizeof(pscan->nr_sets)
+                                    + S_DS_GEN);
+
+       ptlv = (struct mrvlietypes_data *) (pscan->bssdesc_and_tlvbuffer + bytesleft);
+
+       /* Search the TLV buffer space in the scan response for any valid TLVs */
+       wlan_ret_802_11_scan_get_tlv_ptrs(ptlv, tlvbufsize, &ptsftlv);
+
+       /*
+        *  Process each scan response returned (pscan->nr_sets).  Save
+        *    the information in the newbssentry and then insert into the
+        *    driver scan table either as an update to an existing entry
+        *    or as an addition at the end of the table
+        */
+       for (idx = 0; idx < pscan->nr_sets && bytesleft; idx++) {
+               /* Zero out the newbssentry we are about to store info in */
+               memset(&newbssentry, 0x00, sizeof(newbssentry));
+
+               /* Process the data fields and IEs returned for this BSS */
+               if ((InterpretBSSDescriptionWithIE(&newbssentry,
+                                                  &pbssinfo,
+                                                  &bytesleft) ==
+                    0)
+                   && CHECK_SSID_IS_VALID(&newbssentry.ssid)) {
+
+            lbs_pr_debug(1,
+                              "SCAN_RESP: BSSID = %02x:%02x:%02x:%02x:%02x:%02x\n",
+                              newbssentry.macaddress[0],
+                              newbssentry.macaddress[1],
+                              newbssentry.macaddress[2],
+                              newbssentry.macaddress[3],
+                              newbssentry.macaddress[4],
+                              newbssentry.macaddress[5]);
+
+                       /*
+                        * Search the scan table for the same bssid
+                        */
+                       for (bssIdx = 0; bssIdx < numintable; bssIdx++) {
+                               if (memcmp(newbssentry.macaddress,
+                                          adapter->scantable[bssIdx].
+                                          macaddress,
+                                          sizeof(newbssentry.macaddress)) ==
+                                   0) {
+                                       /*
+                                        * If the SSID matches as well, it is a duplicate of
+                                        *   this entry.  Keep the bssIdx set to this
+                                        *   entry so we replace the old contents in the table
+                                        */
+                                       if ((newbssentry.ssid.ssidlength ==
+                                            adapter->scantable[bssIdx].ssid.
+                                            ssidlength)
+                                           &&
+                                           (memcmp
+                                            (newbssentry.ssid.ssid,
+                                             adapter->scantable[bssIdx].ssid.
+                                             ssid,
+                                             newbssentry.ssid.ssidlength) ==
+                                            0)) {
+                        lbs_pr_debug(1,
+                                                      "SCAN_RESP: Duplicate of index: %d\n",
+                                                      bssIdx);
+                                               break;
+                                       }
+                               }
+                       }
+                       /*
+                        * If the bssIdx is equal to the number of entries in the table,
+                        *   the new entry was not a duplicate; append it to the scan
+                        *   table
+                        */
+                       if (bssIdx == numintable) {
+                               /* Range check the bssIdx, keep it limited to the last entry */
+                               if (bssIdx == MRVDRV_MAX_BSSID_LIST) {
+                                       bssIdx--;
+                               } else {
+                                       numintable++;
+                               }
+                       }
+
+                       /*
+                        * If the TSF TLV was appended to the scan results, save the
+                        *   this entries TSF value in the networktsf field.  The
+                        *   networktsf is the firmware's TSF value at the time the
+                        *   beacon or probe response was received.
+                        */
+                       if (ptsftlv) {
+                               memcpy(&tsfval, &ptsftlv->tsftable[idx],
+                                      sizeof(tsfval));
+                               tsfval = le64_to_cpu(tsfval);
+
+                               memcpy(&newbssentry.networktsf,
+                                      &tsfval, sizeof(newbssentry.networktsf));
+                       }
+
+                       /* Copy the locally created newbssentry to the scan table */
+                       memcpy(&adapter->scantable[bssIdx],
+                              &newbssentry,
+                              sizeof(adapter->scantable[bssIdx]));
+
+               } else {
+
+                       /* error parsing/interpreting the scan response, skipped */
+                       lbs_pr_debug(1, "SCAN_RESP: "
+                              "InterpretBSSDescriptionWithIE returned ERROR\n");
+               }
+       }
+
+       lbs_pr_debug(1, "SCAN_RESP: Scanned %2d APs, %d valid, %d total\n",
+              pscan->nr_sets, numintable - adapter->numinscantable,
+              numintable);
+
+       /* Update the total number of BSSIDs in the scan table */
+       adapter->numinscantable = numintable;
+
+       LEAVE();
+       return 0;
+}
diff --git a/drivers/net/wireless/libertas/scan.h b/drivers/net/wireless/libertas/scan.h
new file mode 100644 (file)
index 0000000..d93aa7f
--- /dev/null
@@ -0,0 +1,216 @@
+/* -*- mode: C; tab-width: 4; indent-tabs-mode: nil -*- */
+/* vi: set expandtab shiftwidth=4 tabstop=4 textwidth=78: */
+
+/**
+  * Interface for the wlan network scan routines
+  *
+  * Driver interface functions and type declarations for the scan module
+  *   implemented in wlan_scan.c.
+  */
+#ifndef _WLAN_SCAN_H
+#define _WLAN_SCAN_H
+
+#include "hostcmd.h"
+
+/**
+ *  @brief Maximum number of channels that can be sent in a setuserscan ioctl
+ *
+ *  @sa wlan_ioctl_user_scan_cfg
+ */
+#define WLAN_IOCTL_USER_SCAN_CHAN_MAX  50
+
+//! Infrastructure BSS scan type in wlan_scan_cmd_config
+#define WLAN_SCAN_BSS_TYPE_BSS         1
+
+//! Adhoc BSS scan type in wlan_scan_cmd_config
+#define WLAN_SCAN_BSS_TYPE_IBSS        2
+
+//! Adhoc or Infrastructure BSS scan type in wlan_scan_cmd_config, no filter
+#define WLAN_SCAN_BSS_TYPE_ANY         3
+
+/**
+ * @brief Structure used internally in the wlan driver to configure a scan.
+ *
+ * Sent to the command processing module to configure the firmware
+ *   scan command prepared by libertas_cmd_80211_scan.
+ *
+ * @sa wlan_scan_networks
+ *
+ */
+struct wlan_scan_cmd_config {
+    /**
+     *  @brief BSS type to be sent in the firmware command
+     *
+     *  Field can be used to restrict the types of networks returned in the
+     *    scan.  valid settings are:
+     *
+     *   - WLAN_SCAN_BSS_TYPE_BSS  (infrastructure)
+     *   - WLAN_SCAN_BSS_TYPE_IBSS (adhoc)
+     *   - WLAN_SCAN_BSS_TYPE_ANY  (unrestricted, adhoc and infrastructure)
+     */
+       u8 bsstype;
+
+    /**
+     *  @brief Specific BSSID used to filter scan results in the firmware
+     */
+       u8 specificBSSID[ETH_ALEN];
+
+    /**
+     *  @brief length of TLVs sent in command starting at tlvBuffer
+     */
+       int tlvbufferlen;
+
+    /**
+     *  @brief SSID TLV(s) and ChanList TLVs to be sent in the firmware command
+     *
+     *  @sa TLV_TYPE_CHANLIST, mrvlietypes_chanlistparamset_t
+     *  @sa TLV_TYPE_SSID, mrvlietypes_ssidparamset_t
+     */
+       u8 tlvbuffer[1];        //!< SSID TLV(s) and ChanList TLVs are stored here
+};
+
+/**
+ *  @brief IOCTL channel sub-structure sent in wlan_ioctl_user_scan_cfg
+ *
+ *  Multiple instances of this structure are included in the IOCTL command
+ *   to configure a instance of a scan on the specific channel.
+ */
+struct wlan_ioctl_user_scan_chan {
+       u8 channumber;          //!< channel Number to scan
+       u8 radiotype;           //!< Radio type: 'B/G' band = 0, 'A' band = 1
+       u8 scantype;            //!< Scan type: Active = 0, Passive = 1
+       u16 scantime;           //!< Scan duration in milliseconds; if 0 default used
+};
+
+/**
+ *  @brief IOCTL input structure to configure an immediate scan cmd to firmware
+ *
+ *  Used in the setuserscan (WLAN_SET_USER_SCAN) private ioctl.  Specifies
+ *   a number of parameters to be used in general for the scan as well
+ *   as a channel list (wlan_ioctl_user_scan_chan) for each scan period
+ *   desired.
+ *
+ *  @sa libertas_set_user_scan_ioctl
+ */
+struct wlan_ioctl_user_scan_cfg {
+
+    /**
+     *  @brief Flag set to keep the previous scan table intact
+     *
+     *  If set, the scan results will accumulate, replacing any previous
+     *   matched entries for a BSS with the new scan data
+     */
+       u8 keeppreviousscan;    //!< Do not erase the existing scan results
+
+    /**
+     *  @brief BSS type to be sent in the firmware command
+     *
+     *  Field can be used to restrict the types of networks returned in the
+     *    scan.  valid settings are:
+     *
+     *   - WLAN_SCAN_BSS_TYPE_BSS  (infrastructure)
+     *   - WLAN_SCAN_BSS_TYPE_IBSS (adhoc)
+     *   - WLAN_SCAN_BSS_TYPE_ANY  (unrestricted, adhoc and infrastructure)
+     */
+       u8 bsstype;
+
+    /**
+     *  @brief Configure the number of probe requests for active chan scans
+     */
+       u8 numprobes;
+
+    /**
+     *  @brief BSSID filter sent in the firmware command to limit the results
+     */
+       u8 specificBSSID[ETH_ALEN];
+
+    /**
+     *  @brief SSID filter sent in the firmware command to limit the results
+     */
+       char specificSSID[IW_ESSID_MAX_SIZE + 1];
+
+    /**
+     *  @brief Variable number (fixed maximum) of channels to scan up
+     */
+       struct wlan_ioctl_user_scan_chan chanlist[WLAN_IOCTL_USER_SCAN_CHAN_MAX];
+};
+
+/**
+ *  @brief Structure used to store information for each beacon/probe response
+ */
+struct bss_descriptor {
+       u8 macaddress[ETH_ALEN];
+
+       struct WLAN_802_11_SSID ssid;
+
+       /* WEP encryption requirement */
+       u32 privacy;
+
+       /* receive signal strength in dBm */
+       long rssi;
+
+       u32 channel;
+
+       u16 beaconperiod;
+
+       u32 atimwindow;
+
+       enum WLAN_802_11_NETWORK_INFRASTRUCTURE inframode;
+       u8 libertas_supported_rates[WLAN_SUPPORTED_RATES];
+
+       int extra_ie;
+
+       u8 timestamp[8];        //!< TSF value included in the beacon/probe response
+       union ieeetypes_phyparamset phyparamset;
+       union IEEEtypes_ssparamset ssparamset;
+       struct ieeetypes_capinfo cap;
+       u8 datarates[WLAN_SUPPORTED_RATES];
+
+       __le64 networktsf;              //!< TSF timestamp from the current firmware TSF
+
+       struct ieeetypes_countryinfofullset countryinfo;
+
+       struct WPA_SUPPLICANT wpa_supplicant;
+       struct WPA_SUPPLICANT wpa2_supplicant;
+
+};
+
+extern int libertas_SSID_cmp(struct WLAN_802_11_SSID *ssid1,
+                  struct WLAN_802_11_SSID *ssid2);
+extern int libertas_find_SSID_in_list(wlan_adapter * adapter, struct WLAN_802_11_SSID *ssid,
+                         u8 * bssid, int mode);
+int libertas_find_best_SSID_in_list(wlan_adapter * adapter, enum WLAN_802_11_NETWORK_INFRASTRUCTURE mode);
+extern int libertas_find_BSSID_in_list(wlan_adapter * adapter, u8 * bssid, int mode);
+
+int libertas_find_best_network_SSID(wlan_private * priv,
+                       struct WLAN_802_11_SSID *pSSID,
+                       enum WLAN_802_11_NETWORK_INFRASTRUCTURE preferred_mode,
+                       enum WLAN_802_11_NETWORK_INFRASTRUCTURE *out_mode);
+
+extern int libertas_send_specific_SSID_scan(wlan_private * priv,
+                               struct WLAN_802_11_SSID *prequestedssid,
+                               u8 keeppreviousscan);
+extern int libertas_send_specific_BSSID_scan(wlan_private * priv,
+                                u8 * bssid, u8 keeppreviousscan);
+
+extern int libertas_cmd_80211_scan(wlan_private * priv,
+                               struct cmd_ds_command *cmd,
+                               void *pdata_buf);
+
+extern int libertas_ret_80211_scan(wlan_private * priv,
+                               struct cmd_ds_command *resp);
+
+int wlan_scan_networks(wlan_private * priv,
+                const struct wlan_ioctl_user_scan_cfg * puserscanin);
+
+struct ifreq;
+
+struct iw_point;
+struct iw_param;
+struct iw_request_info;
+extern int libertas_get_scan(struct net_device *dev, struct iw_request_info *info,
+                        struct iw_point *dwrq, char *extra);
+extern int libertas_set_scan(struct net_device *dev, struct iw_request_info *info,
+                        struct iw_param *vwrq, char *extra);
+
+#endif                         /* _WLAN_SCAN_H */
diff --git a/drivers/net/wireless/libertas/thread.h b/drivers/net/wireless/libertas/thread.h
new file mode 100644 (file)
index 0000000..207b8a6
--- /dev/null
@@ -0,0 +1,52 @@
+#ifndef        __WLAN_THREAD_H_
+#define        __WLAN_THREAD_H_
+
+#include       <linux/kthread.h>
+
+struct wlan_thread {
+       struct task_struct *task;
+       wait_queue_head_t waitq;
+       pid_t pid;
+       void *priv;
+};
+
+static inline void wlan_activate_thread(struct wlan_thread * thr)
+{
+       /** Record the thread pid */
+       thr->pid = current->pid;
+
+       /** Initialize the wait queue */
+       init_waitqueue_head(&thr->waitq);
+}
+
+static inline void wlan_deactivate_thread(struct wlan_thread * thr)
+{
+       ENTER();
+
+       thr->pid = 0;
+
+       LEAVE();
+}
+
+static inline void wlan_create_thread(int (*wlanfunc) (void *),
+                                     struct wlan_thread * thr, char *name)
+{
+       thr->task = kthread_run(wlanfunc, thr, "%s", name);
+}
+
+static inline int wlan_terminate_thread(struct wlan_thread * thr)
+{
+       ENTER();
+
+       /* Check if the thread is active or not */
+       if (!thr->pid) {
+               printk(KERN_ERR "Thread does not exist\n");
+               return -1;
+       }
+       kthread_stop(thr->task);
+
+       LEAVE();
+       return 0;
+}
+
+#endif
diff --git a/drivers/net/wireless/libertas/tx.c b/drivers/net/wireless/libertas/tx.c
new file mode 100644 (file)
index 0000000..82d0622
--- /dev/null
@@ -0,0 +1,285 @@
+/**
+  * This file contains the handling of TX in wlan driver.
+  */
+#include <linux/netdevice.h>
+
+#include "hostcmd.h"
+#include "radiotap.h"
+#include "sbi.h"
+#include "decl.h"
+#include "defs.h"
+#include "dev.h"
+#include "wext.h"
+
+/**
+ *  @brief This function converts Tx/Rx rates from IEEE80211_RADIOTAP_RATE
+ *  units (500 Kb/s) into Marvell WLAN format (see Table 8 in Section 3.2.1)
+ *
+ *  @param rate    Input rate
+ *  @return      Output Rate (0 if invalid)
+ */
+static u32 convert_radiotap_rate_to_mv(u8 rate)
+{
+       switch (rate) {
+       case 2:         /*   1 Mbps */
+               return 0 | (1 << 4);
+       case 4:         /*   2 Mbps */
+               return 1 | (1 << 4);
+       case 11:                /* 5.5 Mbps */
+               return 2 | (1 << 4);
+       case 22:                /*  11 Mbps */
+               return 3 | (1 << 4);
+       case 12:                /*   6 Mbps */
+               return 4 | (1 << 4);
+       case 18:                /*   9 Mbps */
+               return 5 | (1 << 4);
+       case 24:                /*  12 Mbps */
+               return 6 | (1 << 4);
+       case 36:                /*  18 Mbps */
+               return 7 | (1 << 4);
+       case 48:                /*  24 Mbps */
+               return 8 | (1 << 4);
+       case 72:                /*  36 Mbps */
+               return 9 | (1 << 4);
+       case 96:                /*  48 Mbps */
+               return 10 | (1 << 4);
+       case 108:               /*  54 Mbps */
+               return 11 | (1 << 4);
+       }
+       return 0;
+}
+
+/**
+ *  @brief This function processes a single packet and sends
+ *  to IF layer
+ *
+ *  @param priv    A pointer to wlan_private structure
+ *  @param skb     A pointer to skb which includes TX packet
+ *  @return       0 or -1
+ */
+static int SendSinglePacket(wlan_private * priv, struct sk_buff *skb)
+{
+       wlan_adapter *adapter = priv->adapter;
+       int ret = 0;
+       struct txpd localtxpd;
+       struct txpd *plocaltxpd = &localtxpd;
+       u8 *p802x_hdr;
+       struct tx_radiotap_hdr *pradiotap_hdr;
+       u32 new_rate;
+       u8 *ptr = priv->adapter->tmptxbuf;
+
+       ENTER();
+
+       if (priv->adapter->surpriseremoved)
+               return -1;
+
+       if ((priv->adapter->debugmode & MRVDRV_DEBUG_TX_PATH) != 0)
+               lbs_dbg_hex("TX packet: ", skb->data,
+                        min_t(unsigned int, skb->len, 100));
+
+       if (!skb->len || (skb->len > MRVDRV_ETH_TX_PACKET_BUFFER_SIZE)) {
+               lbs_pr_debug(1, "Tx error: Bad skb length %d : %d\n",
+                      skb->len, MRVDRV_ETH_TX_PACKET_BUFFER_SIZE);
+               ret = -1;
+               goto done;
+       }
+
+       memset(plocaltxpd, 0, sizeof(struct txpd));
+
+       plocaltxpd->tx_packet_length = skb->len;
+
+       /* offset of actual data */
+       plocaltxpd->tx_packet_location = sizeof(struct txpd);
+
+       /* TxCtrl set by user or default */
+       plocaltxpd->tx_control = adapter->pkttxctrl;
+
+       p802x_hdr = skb->data;
+       if (priv->adapter->radiomode == WLAN_RADIOMODE_RADIOTAP) {
+
+               /* locate radiotap header */
+               pradiotap_hdr = (struct tx_radiotap_hdr *)skb->data;
+
+               /* set txpd fields from the radiotap header */
+               new_rate = convert_radiotap_rate_to_mv(pradiotap_hdr->rate);
+               if (new_rate != 0) {
+                       /* erase tx_control[4:0] */
+                       plocaltxpd->tx_control &= ~0x1f;
+                       /* write new tx_control[4:0] */
+                       plocaltxpd->tx_control |= new_rate;
+               }
+
+               /* skip the radiotap header */
+               p802x_hdr += sizeof(struct tx_radiotap_hdr);
+               plocaltxpd->tx_packet_length -= sizeof(struct tx_radiotap_hdr);
+
+       }
+       /* copy destination address from 802.3 or 802.11 header */
+       if (priv->adapter->linkmode == WLAN_LINKMODE_802_11)
+               memcpy(plocaltxpd->tx_dest_addr_high, p802x_hdr + 4, ETH_ALEN);
+       else
+               memcpy(plocaltxpd->tx_dest_addr_high, p802x_hdr, ETH_ALEN);
+
+       lbs_dbg_hex("txpd", (u8 *) plocaltxpd, sizeof(struct txpd));
+
+       if (IS_MESH_FRAME(skb)) {
+               plocaltxpd->tx_control |= TxPD_MESH_FRAME;
+       }
+
+       memcpy(ptr, plocaltxpd, sizeof(struct txpd));
+
+       ptr += sizeof(struct txpd);
+
+       lbs_dbg_hex("Tx Data", (u8 *) p802x_hdr, plocaltxpd->tx_packet_length);
+       memcpy(ptr, p802x_hdr, plocaltxpd->tx_packet_length);
+       ret = libertas_sbi_host_to_card(priv, MVMS_DAT,
+                              priv->adapter->tmptxbuf,
+                              plocaltxpd->tx_packet_length +
+                              sizeof(struct txpd));
+
+       if (ret) {
+               lbs_pr_debug(1, "Tx error: libertas_sbi_host_to_card failed: 0x%X\n", ret);
+               goto done;
+       }
+
+       lbs_pr_debug(1, "SendSinglePacket succeeds\n");
+
+      done:
+       if (!ret) {
+               priv->stats.tx_packets++;
+               priv->stats.tx_bytes += skb->len;
+       } else {
+               priv->stats.tx_dropped++;
+               priv->stats.tx_errors++;
+       }
+
+       if (!ret && priv->adapter->radiomode == WLAN_RADIOMODE_RADIOTAP) {
+               /* Keep the skb to echo it back once Tx feedback is
+                  received from FW */
+               skb_orphan(skb);
+               /* stop processing outgoing pkts */
+               netif_stop_queue(priv->wlan_dev.netdev);
+               /* freeze any packets already in our queues */
+               priv->adapter->TxLockFlag = 1;
+       } else {
+               dev_kfree_skb_any(skb);
+               priv->adapter->currenttxskb = NULL;
+       }
+
+       LEAVE();
+       return ret;
+}
+
+
+void libertas_tx_runqueue(wlan_private *priv)
+{
+       wlan_adapter *adapter = priv->adapter;
+       int i;
+
+       spin_lock(&adapter->txqueue_lock);
+       for (i = 0; i < adapter->tx_queue_idx; i++) {
+               struct sk_buff *skb = adapter->tx_queue_ps[i];
+               spin_unlock(&adapter->txqueue_lock);
+               SendSinglePacket(priv, skb);
+               spin_lock(&adapter->txqueue_lock);
+       }
+       adapter->tx_queue_idx = 0;
+       spin_unlock(&adapter->txqueue_lock);
+}
+
+static void wlan_tx_queue(wlan_private *priv, struct sk_buff *skb)
+{
+       wlan_adapter *adapter = priv->adapter;
+
+       spin_lock(&adapter->txqueue_lock);
+
+       WARN_ON(priv->adapter->tx_queue_idx >= NR_TX_QUEUE);
+       adapter->tx_queue_ps[adapter->tx_queue_idx++] = skb;
+       if (adapter->tx_queue_idx == NR_TX_QUEUE)
+               netif_stop_queue(priv->wlan_dev.netdev);
+       else
+               netif_start_queue(priv->wlan_dev.netdev);
+
+       spin_unlock(&adapter->txqueue_lock);
+}
+
+/**
+ *  @brief This function checks the conditions and sends packet to IF
+ *  layer if everything is ok.
+ *
+ *  @param priv    A pointer to wlan_private structure
+ *  @return       n/a
+ */
+int libertas_process_tx(wlan_private * priv, struct sk_buff *skb)
+{
+       int ret = -1;
+
+       ENTER();
+
+       lbs_dbg_hex("TX Data", skb->data, min_t(unsigned int, skb->len, 100));
+
+       if (priv->wlan_dev.dnld_sent) {
+               lbs_pr_alert( "TX error: dnld_sent = %d, not sending\n",
+                      priv->wlan_dev.dnld_sent);
+               goto done;
+       }
+
+       if ((priv->adapter->psstate == PS_STATE_SLEEP) ||
+           (priv->adapter->psstate == PS_STATE_PRE_SLEEP)) {
+               wlan_tx_queue(priv, skb);
+               return ret;
+       }
+
+       priv->adapter->currenttxskb = skb;
+
+       ret = SendSinglePacket(priv, skb);
+done:
+       LEAVE();
+       return ret;
+}
+
+/**
+ *  @brief This function sends to the host the last transmitted packet,
+ *  filling the radiotap headers with transmission information.
+ *
+ *  @param priv     A pointer to wlan_private structure
+ *  @param status   A 32 bit value containing transmission status.
+ *
+ *  @returns void
+ */
+void libertas_send_tx_feedback(wlan_private * priv)
+{
+       wlan_adapter *adapter = priv->adapter;
+       struct tx_radiotap_hdr *radiotap_hdr;
+       u32 status = adapter->eventcause;
+       int txfail;
+       int try_count;
+
+       if (adapter->radiomode != WLAN_RADIOMODE_RADIOTAP ||
+           adapter->currenttxskb == NULL)
+               return;
+
+       radiotap_hdr = (struct tx_radiotap_hdr *)adapter->currenttxskb->data;
+
+       if ((adapter->debugmode & MRVDRV_DEBUG_TX_PATH) != 0)
+               lbs_dbg_hex("TX feedback: ", (u8 *) radiotap_hdr,
+                       min_t(unsigned int, adapter->currenttxskb->len, 100));
+
+       txfail = (status >> 24);
+
+#if 0
+       /* The version of roofnet that we've tested does not use this yet
+        * But it may be used in the future.
+        */
+       if (txfail)
+               radiotap_hdr->flags &= IEEE80211_RADIOTAP_F_TX_FAIL;
+#endif
+       try_count = (status >> 16) & 0xff;
+       radiotap_hdr->data_retries = (try_count) ?
+           (1 + adapter->txretrycount - try_count) : 0;
+       libertas_upload_rx_packet(priv, adapter->currenttxskb);
+       adapter->currenttxskb = NULL;
+       priv->adapter->TxLockFlag = 0;
+       if (priv->adapter->connect_status == libertas_connected)
+               netif_wake_queue(priv->wlan_dev.netdev);
+}
diff --git a/drivers/net/wireless/libertas/types.h b/drivers/net/wireless/libertas/types.h
new file mode 100644 (file)
index 0000000..09d62f8
--- /dev/null
@@ -0,0 +1,289 @@
+/**
+  * This header file contains definition for global types
+  */
+#ifndef _WLAN_TYPES_
+#define _WLAN_TYPES_
+
+#include <linux/if_ether.h>
+
+/** IEEE type definitions  */
+enum ieeetypes_elementid {
+       SSID = 0,
+       SUPPORTED_RATES,
+       FH_PARAM_SET,
+       DS_PARAM_SET,
+       CF_PARAM_SET,
+       TIM,
+       IBSS_PARAM_SET,
+       COUNTRY_INFO = 7,
+
+       CHALLENGE_TEXT = 16,
+
+       EXTENDED_SUPPORTED_RATES = 50,
+
+       VENDOR_SPECIFIC_221 = 221,
+
+       WPA_IE = 221,
+       WPA2_IE = 48,
+
+       EXTRA_IE = 133,
+} __attribute__ ((packed));
+
+#define CAPINFO_MASK   (~(0xda00))
+
+struct ieeetypes_capinfo {
+       u8 ess:1;
+       u8 ibss:1;
+       u8 cfpollable:1;
+       u8 cfpollrqst:1;
+       u8 privacy:1;
+       u8 shortpreamble:1;
+       u8 pbcc:1;
+       u8 chanagility:1;
+       u8 spectrummgmt:1;
+       u8 rsrvd3:1;
+       u8 shortslottime:1;
+       u8 apsd:1;
+       u8 rsvrd2:1;
+       u8 dsssofdm:1;
+       u8 rsrvd1:2;
+} __attribute__ ((packed));
+
+struct ieeetypes_cfparamset {
+       u8 elementid;
+       u8 len;
+       u8 cfpcnt;
+       u8 cfpperiod;
+       u16 cfpmaxduration;
+       u16 cfpdurationremaining;
+} __attribute__ ((packed));
+
+
+struct ieeetypes_ibssparamset {
+       u8 elementid;
+       u8 len;
+       u16 atimwindow;
+} __attribute__ ((packed));
+
+union IEEEtypes_ssparamset {
+       struct ieeetypes_cfparamset cfparamset;
+       struct ieeetypes_ibssparamset ibssparamset;
+} __attribute__ ((packed));
+
+struct ieeetypes_fhparamset {
+       u8 elementid;
+       u8 len;
+       u16 dwelltime;
+       u8 hopset;
+       u8 hoppattern;
+       u8 hopindex;
+} __attribute__ ((packed));
+
+struct ieeetypes_dsparamset {
+       u8 elementid;
+       u8 len;
+       u8 currentchan;
+} __attribute__ ((packed));
+
+union ieeetypes_phyparamset {
+       struct ieeetypes_fhparamset fhparamset;
+       struct ieeetypes_dsparamset dsparamset;
+} __attribute__ ((packed));
+
+struct ieeetypes_assocrsp {
+       struct ieeetypes_capinfo capability;
+       u16 statuscode;
+       u16 aid;
+       u8 iebuffer[1];
+} __attribute__ ((packed));
+
+/** TLV  type ID definition */
+#define PROPRIETARY_TLV_BASE_ID                0x0100
+
+/* Terminating TLV type */
+#define MRVL_TERMINATE_TLV_ID          0xffff
+
+#define TLV_TYPE_SSID                          0x0000
+#define TLV_TYPE_RATES                         0x0001
+#define TLV_TYPE_PHY_FH                                0x0002
+#define TLV_TYPE_PHY_DS                                0x0003
+#define TLV_TYPE_CF                                0x0004
+#define TLV_TYPE_IBSS                          0x0006
+
+#define TLV_TYPE_DOMAIN                                0x0007
+
+#define TLV_TYPE_POWER_CAPABILITY      0x0021
+
+#define TLV_TYPE_KEY_MATERIAL       (PROPRIETARY_TLV_BASE_ID + 0)
+#define TLV_TYPE_CHANLIST           (PROPRIETARY_TLV_BASE_ID + 1)
+#define TLV_TYPE_NUMPROBES          (PROPRIETARY_TLV_BASE_ID + 2)
+#define TLV_TYPE_RSSI_LOW           (PROPRIETARY_TLV_BASE_ID + 4)
+#define TLV_TYPE_SNR_LOW            (PROPRIETARY_TLV_BASE_ID + 5)
+#define TLV_TYPE_FAILCOUNT          (PROPRIETARY_TLV_BASE_ID + 6)
+#define TLV_TYPE_BCNMISS            (PROPRIETARY_TLV_BASE_ID + 7)
+#define TLV_TYPE_LED_GPIO           (PROPRIETARY_TLV_BASE_ID + 8)
+#define TLV_TYPE_LEDBEHAVIOR        (PROPRIETARY_TLV_BASE_ID + 9)
+#define TLV_TYPE_PASSTHROUGH        (PROPRIETARY_TLV_BASE_ID + 10)
+#define TLV_TYPE_REASSOCAP          (PROPRIETARY_TLV_BASE_ID + 11)
+#define TLV_TYPE_POWER_TBL_2_4GHZ   (PROPRIETARY_TLV_BASE_ID + 12)
+#define TLV_TYPE_POWER_TBL_5GHZ     (PROPRIETARY_TLV_BASE_ID + 13)
+#define TLV_TYPE_BCASTPROBE        (PROPRIETARY_TLV_BASE_ID + 14)
+#define TLV_TYPE_NUMSSID_PROBE     (PROPRIETARY_TLV_BASE_ID + 15)
+#define TLV_TYPE_WMMQSTATUS        (PROPRIETARY_TLV_BASE_ID + 16)
+#define TLV_TYPE_CRYPTO_DATA       (PROPRIETARY_TLV_BASE_ID + 17)
+#define TLV_TYPE_WILDCARDSSID      (PROPRIETARY_TLV_BASE_ID + 18)
+#define TLV_TYPE_TSFTIMESTAMP      (PROPRIETARY_TLV_BASE_ID + 19)
+#define TLV_TYPE_RSSI_HIGH          (PROPRIETARY_TLV_BASE_ID + 22)
+#define TLV_TYPE_SNR_HIGH           (PROPRIETARY_TLV_BASE_ID + 23)
+
+/** TLV related data structures*/
+struct mrvlietypesheader {
+       u16 type;
+       u16 len;
+} __attribute__ ((packed));
+
+struct mrvlietypes_data {
+       struct mrvlietypesheader header;
+       u8 Data[1];
+} __attribute__ ((packed));
+
+struct mrvlietypes_ratesparamset {
+       struct mrvlietypesheader header;
+       u8 rates[1];
+} __attribute__ ((packed));
+
+struct mrvlietypes_ssidparamset {
+       struct mrvlietypesheader header;
+       u8 ssid[1];
+} __attribute__ ((packed));
+
+struct mrvlietypes_wildcardssidparamset {
+       struct mrvlietypesheader header;
+       u8 MaxSsidlength;
+       u8 ssid[1];
+} __attribute__ ((packed));
+
+struct chanscanmode {
+       u8 passivescan:1;
+       u8 disablechanfilt:1;
+       u8 reserved_2_7:6;
+} __attribute__ ((packed));
+
+struct chanscanparamset {
+       u8 radiotype;
+       u8 channumber;
+       struct chanscanmode chanscanmode;
+       u16 minscantime;
+       u16 maxscantime;
+} __attribute__ ((packed));
+
+struct mrvlietypes_chanlistparamset {
+       struct mrvlietypesheader header;
+       struct chanscanparamset chanscanparam[1];
+} __attribute__ ((packed));
+
+struct cfparamset {
+       u8 cfpcnt;
+       u8 cfpperiod;
+       u16 cfpmaxduration;
+       u16 cfpdurationremaining;
+} __attribute__ ((packed));
+
+struct ibssparamset {
+       u16 atimwindow;
+} __attribute__ ((packed));
+
+struct mrvlietypes_ssparamset {
+       struct mrvlietypesheader header;
+       union {
+               struct cfparamset cfparamset[1];
+               struct ibssparamset ibssparamset[1];
+       } cf_ibss;
+} __attribute__ ((packed));
+
+struct fhparamset {
+       u16 dwelltime;
+       u8 hopset;
+       u8 hoppattern;
+       u8 hopindex;
+} __attribute__ ((packed));
+
+struct dsparamset {
+       u8 currentchan;
+} __attribute__ ((packed));
+
+struct mrvlietypes_phyparamset {
+       struct mrvlietypesheader header;
+       union {
+               struct fhparamset fhparamset[1];
+               struct dsparamset dsparamset[1];
+       } fh_ds;
+} __attribute__ ((packed));
+
+struct mrvlietypes_rsnparamset {
+       struct mrvlietypesheader header;
+       u8 rsnie[1];
+} __attribute__ ((packed));
+
+struct mrvlietypes_tsftimestamp {
+       struct mrvlietypesheader header;
+       __le64 tsftable[1];
+} __attribute__ ((packed));
+
+/**  Local Power capability */
+struct mrvlietypes_powercapability {
+       struct mrvlietypesheader header;
+       s8 minpower;
+       s8 maxpower;
+} __attribute__ ((packed));
+
+struct mrvlietypes_rssithreshold {
+       struct mrvlietypesheader header;
+       u8 rssivalue;
+       u8 rssifreq;
+} __attribute__ ((packed));
+
+struct mrvlietypes_snrthreshold {
+       struct mrvlietypesheader header;
+       u8 snrvalue;
+       u8 snrfreq;
+} __attribute__ ((packed));
+
+struct mrvlietypes_failurecount {
+       struct mrvlietypesheader header;
+       u8 failvalue;
+       u8 Failfreq;
+} __attribute__ ((packed));
+
+struct mrvlietypes_beaconsmissed {
+       struct mrvlietypesheader header;
+       u8 beaconmissed;
+       u8 reserved;
+} __attribute__ ((packed));
+
+struct mrvlietypes_numprobes {
+       struct mrvlietypesheader header;
+       u16 numprobes;
+} __attribute__ ((packed));
+
+struct mrvlietypes_bcastprobe {
+       struct mrvlietypesheader header;
+       u16 bcastprobe;
+} __attribute__ ((packed));
+
+struct mrvlietypes_numssidprobe {
+       struct mrvlietypesheader header;
+       u16 numssidprobe;
+} __attribute__ ((packed));
+
+struct led_pin {
+       u8 led;
+       u8 pin;
+} __attribute__ ((packed));
+
+struct mrvlietypes_ledgpio {
+       struct mrvlietypesheader header;
+       struct led_pin ledpin[1];
+} __attribute__ ((packed));
+
+#endif                         /* _WLAN_TYPES_ */
diff --git a/drivers/net/wireless/libertas/version.h b/drivers/net/wireless/libertas/version.h
new file mode 100644 (file)
index 0000000..e86f65a
--- /dev/null
@@ -0,0 +1,8 @@
+#define DRIVER_RELEASE_VERSION "320.p0"
+const char libertas_driver_version[] = "COMM-USB8388-" DRIVER_RELEASE_VERSION
+#ifdef  DEBUG
+    "-dbg"
+#endif
+    "";
+
+
diff --git a/drivers/net/wireless/libertas/wext.c b/drivers/net/wireless/libertas/wext.c
new file mode 100644 (file)
index 0000000..4a52336
--- /dev/null
@@ -0,0 +1,2769 @@
+/**
+  * This file contains ioctl functions
+  */
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/if.h>
+#include <linux/if_arp.h>
+#include <linux/wireless.h>
+#include <linux/bitops.h>
+
+#include <net/ieee80211.h>
+#include <net/iw_handler.h>
+
+#include "host.h"
+#include "radiotap.h"
+#include "decl.h"
+#include "defs.h"
+#include "dev.h"
+#include "join.h"
+#include "version.h"
+#include "wext.h"
+#include "assoc.h"
+
+
+/**
+ *  @brief Convert mw value to dbm value
+ *
+ *  @param mw     the value of mw
+ *  @return       the value of dbm
+ */
+static int mw_to_dbm(int mw)
+{
+       if (mw < 2)
+               return 0;
+       else if (mw < 3)
+               return 3;
+       else if (mw < 4)
+               return 5;
+       else if (mw < 6)
+               return 7;
+       else if (mw < 7)
+               return 8;
+       else if (mw < 8)
+               return 9;
+       else if (mw < 10)
+               return 10;
+       else if (mw < 13)
+               return 11;
+       else if (mw < 16)
+               return 12;
+       else if (mw < 20)
+               return 13;
+       else if (mw < 25)
+               return 14;
+       else if (mw < 32)
+               return 15;
+       else if (mw < 40)
+               return 16;
+       else if (mw < 50)
+               return 17;
+       else if (mw < 63)
+               return 18;
+       else if (mw < 79)
+               return 19;
+       else if (mw < 100)
+               return 20;
+       else
+               return 21;
+}
+
+/**
+ *  @brief Find the channel frequency power info with specific channel
+ *
+ *  @param adapter     A pointer to wlan_adapter structure
+ *  @param band                it can be BAND_A, BAND_G or BAND_B
+ *  @param channel      the channel for looking
+ *  @return            A pointer to struct chan_freq_power structure or NULL if not find.
+ */
+struct chan_freq_power *libertas_find_cfp_by_band_and_channel(wlan_adapter * adapter,
+                                                u8 band, u16 channel)
+{
+       struct chan_freq_power *cfp = NULL;
+       struct region_channel *rc;
+       int count = sizeof(adapter->region_channel) /
+           sizeof(adapter->region_channel[0]);
+       int i, j;
+
+       for (j = 0; !cfp && (j < count); j++) {
+               rc = &adapter->region_channel[j];
+
+               if (adapter->enable11d)
+                       rc = &adapter->universal_channel[j];
+               if (!rc->valid || !rc->CFP)
+                       continue;
+               if (rc->band != band)
+                       continue;
+               for (i = 0; i < rc->nrcfp; i++) {
+                       if (rc->CFP[i].channel == channel) {
+                               cfp = &rc->CFP[i];
+                               break;
+                       }
+               }
+       }
+
+       if (!cfp && channel)
+               lbs_pr_debug(1, "libertas_find_cfp_by_band_and_channel(): cannot find "
+                      "cfp by band %d & channel %d\n", band, channel);
+
+       return cfp;
+}
+
+/**
+ *  @brief Find the channel frequency power info with specific frequency
+ *
+ *  @param adapter     A pointer to wlan_adapter structure
+ *  @param band                it can be BAND_A, BAND_G or BAND_B
+ *  @param freq                the frequency for looking
+ *  @return            A pointer to struct chan_freq_power structure or NULL if not find.
+ */
+static struct chan_freq_power *find_cfp_by_band_and_freq(wlan_adapter * adapter,
+                                                    u8 band, u32 freq)
+{
+       struct chan_freq_power *cfp = NULL;
+       struct region_channel *rc;
+       int count = sizeof(adapter->region_channel) /
+           sizeof(adapter->region_channel[0]);
+       int i, j;
+
+       for (j = 0; !cfp && (j < count); j++) {
+               rc = &adapter->region_channel[j];
+
+               if (adapter->enable11d)
+                       rc = &adapter->universal_channel[j];
+               if (!rc->valid || !rc->CFP)
+                       continue;
+               if (rc->band != band)
+                       continue;
+               for (i = 0; i < rc->nrcfp; i++) {
+                       if (rc->CFP[i].freq == freq) {
+                               cfp = &rc->CFP[i];
+                               break;
+                       }
+               }
+       }
+
+       if (!cfp && freq)
+               lbs_pr_debug(1, "find_cfp_by_band_and_freql(): cannot find cfp by "
+                      "band %d & freq %d\n", band, freq);
+
+       return cfp;
+}
+
+static int updatecurrentchannel(wlan_private * priv)
+{
+       int ret;
+
+       /*
+        ** the channel in f/w could be out of sync, get the current channel
+        */
+       ret = libertas_prepare_and_send_command(priv, cmd_802_11_rf_channel,
+                                   cmd_opt_802_11_rf_channel_get,
+                                   cmd_option_waitforrsp, 0, NULL);
+
+       lbs_pr_debug(1, "Current channel = %d\n",
+              priv->adapter->curbssparams.channel);
+
+       return ret;
+}
+
+static int setcurrentchannel(wlan_private * priv, int channel)
+{
+       lbs_pr_debug(1, "Set channel = %d\n", channel);
+
+       /*
+        **  Current channel is not set to adhocchannel requested, set channel
+        */
+       return (libertas_prepare_and_send_command(priv, cmd_802_11_rf_channel,
+                                     cmd_opt_802_11_rf_channel_set,
+                                     cmd_option_waitforrsp, 0, &channel));
+}
+
+static int changeadhocchannel(wlan_private * priv, int channel)
+{
+       int ret = 0;
+       wlan_adapter *adapter = priv->adapter;
+
+       adapter->adhocchannel = channel;
+
+       updatecurrentchannel(priv);
+
+       if (adapter->curbssparams.channel == adapter->adhocchannel) {
+               /* adhocchannel is set to the current channel already */
+               LEAVE();
+               return 0;
+       }
+
+       lbs_pr_debug(1, "Updating channel from %d to %d\n",
+              adapter->curbssparams.channel, adapter->adhocchannel);
+
+       setcurrentchannel(priv, adapter->adhocchannel);
+
+       updatecurrentchannel(priv);
+
+       if (adapter->curbssparams.channel != adapter->adhocchannel) {
+               lbs_pr_debug(1, "failed to updated channel to %d, channel = %d\n",
+                      adapter->adhocchannel, adapter->curbssparams.channel);
+               LEAVE();
+               return -1;
+       }
+
+       if (adapter->connect_status == libertas_connected) {
+               int i;
+               struct WLAN_802_11_SSID curadhocssid;
+
+               lbs_pr_debug(1, "channel Changed while in an IBSS\n");
+
+               /* Copy the current ssid */
+               memcpy(&curadhocssid, &adapter->curbssparams.ssid,
+                      sizeof(struct WLAN_802_11_SSID));
+
+               /* Exit Adhoc mode */
+               lbs_pr_debug(1, "In changeadhocchannel(): Sending Adhoc Stop\n");
+               ret = libertas_stop_adhoc_network(priv);
+
+               if (ret) {
+                       LEAVE();
+                       return ret;
+               }
+               /* Scan for the network, do not save previous results.  Stale
+                *   scan data will cause us to join a non-existant adhoc network
+                */
+               libertas_send_specific_SSID_scan(priv, &curadhocssid, 0);
+
+               // find out the BSSID that matches the current SSID
+               i = libertas_find_SSID_in_list(adapter, &curadhocssid, NULL,
+                                  wlan802_11ibss);
+
+               if (i >= 0) {
+                       lbs_pr_debug(1, "SSID found at %d in List,"
+                              "so join\n", i);
+                       libertas_join_adhoc_network(priv, &adapter->scantable[i]);
+               } else {
+                       // else send START command
+                       lbs_pr_debug(1, "SSID not found in list, "
+                              "so creating adhoc with ssid = %s\n",
+                              curadhocssid.ssid);
+                       libertas_start_adhoc_network(priv, &curadhocssid);
+               }               // end of else (START command)
+       }
+
+       LEAVE();
+       return 0;
+}
+
+/**
+ *  @brief Set Radio On/OFF
+ *
+ *  @param priv                 A pointer to wlan_private structure
+ *  @option                    Radio Option
+ *  @return                    0 --success, otherwise fail
+ */
+int wlan_radio_ioctl(wlan_private * priv, u8 option)
+{
+       int ret = 0;
+       wlan_adapter *adapter = priv->adapter;
+
+       ENTER();
+
+       if (adapter->radioon != option) {
+               lbs_pr_debug(1, "Switching %s the Radio\n", option ? "On" : "Off");
+               adapter->radioon = option;
+
+               ret = libertas_prepare_and_send_command(priv,
+                                           cmd_802_11_radio_control,
+                                           cmd_act_set,
+                                           cmd_option_waitforrsp, 0, NULL);
+       }
+
+       LEAVE();
+       return ret;
+}
+
+/**
+ *  @brief Copy rates
+ *
+ *  @param dest                 A pointer to Dest Buf
+ *  @param src                 A pointer to Src Buf
+ *  @param len                  The len of Src Buf
+ *  @return                    Number of rates copyed
+ */
+static inline int copyrates(u8 * dest, int pos, u8 * src, int len)
+{
+       int i;
+
+       for (i = 0; i < len && src[i]; i++, pos++) {
+               if (pos >= sizeof(u8) * WLAN_SUPPORTED_RATES)
+                       break;
+               dest[pos] = src[i];
+       }
+
+       return pos;
+}
+
+/**
+ *  @brief Get active data rates
+ *
+ *  @param adapter              A pointer to wlan_adapter structure
+ *  @param rate                        The buf to return the active rates
+ *  @return                    The number of rates
+ */
+static int get_active_data_rates(wlan_adapter * adapter,
+                                u8* rates)
+{
+       int k = 0;
+
+       ENTER();
+
+       if (adapter->connect_status != libertas_connected) {
+               if (adapter->inframode == wlan802_11infrastructure) {
+                       //Infra. mode
+                       lbs_pr_debug(1, "Infra\n");
+                       k = copyrates(rates, k, libertas_supported_rates,
+                                     sizeof(libertas_supported_rates));
+               } else {
+                       //ad-hoc mode
+                       lbs_pr_debug(1, "Adhoc G\n");
+                       k = copyrates(rates, k, libertas_adhoc_rates_g,
+                                     sizeof(libertas_adhoc_rates_g));
+               }
+       } else {
+               k = copyrates(rates, 0, adapter->curbssparams.datarates,
+                             adapter->curbssparams.numofrates);
+       }
+
+       LEAVE();
+
+       return k;
+}
+
+static int wlan_get_name(struct net_device *dev, struct iw_request_info *info,
+                        char *cwrq, char *extra)
+{
+       const char *cp;
+       char comm[6] = { "COMM-" };
+       char mrvl[6] = { "MRVL-" };
+       int cnt;
+
+       ENTER();
+
+       strcpy(cwrq, mrvl);
+
+       cp = strstr(libertas_driver_version, comm);
+       if (cp == libertas_driver_version)      //skip leading "COMM-"
+               cp = libertas_driver_version + strlen(comm);
+       else
+               cp = libertas_driver_version;
+
+       cnt = strlen(mrvl);
+       cwrq += cnt;
+       while (cnt < 16 && (*cp != '-')) {
+               *cwrq++ = toupper(*cp++);
+               cnt++;
+       }
+       *cwrq = '\0';
+
+       LEAVE();
+
+       return 0;
+}
+
+static int wlan_get_freq(struct net_device *dev, struct iw_request_info *info,
+                        struct iw_freq *fwrq, char *extra)
+{
+       wlan_private *priv = dev->priv;
+       wlan_adapter *adapter = priv->adapter;
+       struct chan_freq_power *cfp;
+
+       ENTER();
+
+       cfp = libertas_find_cfp_by_band_and_channel(adapter, 0,
+                                          adapter->curbssparams.channel);
+
+       if (!cfp) {
+               if (adapter->curbssparams.channel)
+                       lbs_pr_debug(1, "Invalid channel=%d\n",
+                              adapter->curbssparams.channel);
+               return -EINVAL;
+       }
+
+       fwrq->m = (long)cfp->freq * 100000;
+       fwrq->e = 1;
+
+       lbs_pr_debug(1, "freq=%u\n", fwrq->m);
+
+       LEAVE();
+       return 0;
+}
+
+static int wlan_get_wap(struct net_device *dev, struct iw_request_info *info,
+                       struct sockaddr *awrq, char *extra)
+{
+       wlan_private *priv = dev->priv;
+       wlan_adapter *adapter = priv->adapter;
+
+       ENTER();
+
+       if (adapter->connect_status == libertas_connected) {
+               memcpy(awrq->sa_data, adapter->curbssparams.bssid, ETH_ALEN);
+       } else {
+               memset(awrq->sa_data, 0, ETH_ALEN);
+       }
+       awrq->sa_family = ARPHRD_ETHER;
+
+       LEAVE();
+       return 0;
+}
+
+static int wlan_set_nick(struct net_device *dev, struct iw_request_info *info,
+                        struct iw_point *dwrq, char *extra)
+{
+       wlan_private *priv = dev->priv;
+       wlan_adapter *adapter = priv->adapter;
+
+       ENTER();
+
+       /*
+        * Check the size of the string
+        */
+
+       if (dwrq->length > 16) {
+               return -E2BIG;
+       }
+
+       mutex_lock(&adapter->lock);
+       memset(adapter->nodename, 0, sizeof(adapter->nodename));
+       memcpy(adapter->nodename, extra, dwrq->length);
+       mutex_unlock(&adapter->lock);
+
+       LEAVE();
+       return 0;
+}
+
+static int wlan_get_nick(struct net_device *dev, struct iw_request_info *info,
+                        struct iw_point *dwrq, char *extra)
+{
+       wlan_private *priv = dev->priv;
+       wlan_adapter *adapter = priv->adapter;
+
+       ENTER();
+
+       /*
+        * Get the Nick Name saved
+        */
+
+       mutex_lock(&adapter->lock);
+       strncpy(extra, adapter->nodename, 16);
+       mutex_unlock(&adapter->lock);
+
+       extra[16] = '\0';
+
+       /*
+        * If none, we may want to get the one that was set
+        */
+
+       /*
+        * Push it out !
+        */
+       dwrq->length = strlen(extra) + 1;
+
+       LEAVE();
+       return 0;
+}
+
+static int wlan_set_rts(struct net_device *dev, struct iw_request_info *info,
+                       struct iw_param *vwrq, char *extra)
+{
+       int ret = 0;
+       wlan_private *priv = dev->priv;
+       wlan_adapter *adapter = priv->adapter;
+       int rthr = vwrq->value;
+
+       ENTER();
+
+       if (vwrq->disabled) {
+               adapter->rtsthsd = rthr = MRVDRV_RTS_MAX_VALUE;
+       } else {
+               if (rthr < MRVDRV_RTS_MIN_VALUE || rthr > MRVDRV_RTS_MAX_VALUE)
+                       return -EINVAL;
+               adapter->rtsthsd = rthr;
+       }
+
+       ret = libertas_prepare_and_send_command(priv, cmd_802_11_snmp_mib,
+                                   cmd_act_set, cmd_option_waitforrsp,
+                                   OID_802_11_RTS_THRESHOLD, &rthr);
+
+       LEAVE();
+       return ret;
+}
+
+static int wlan_get_rts(struct net_device *dev, struct iw_request_info *info,
+                       struct iw_param *vwrq, char *extra)
+{
+       int ret = 0;
+       wlan_private *priv = dev->priv;
+       wlan_adapter *adapter = priv->adapter;
+
+       ENTER();
+
+       adapter->rtsthsd = 0;
+       ret = libertas_prepare_and_send_command(priv, cmd_802_11_snmp_mib,
+                                   cmd_act_get, cmd_option_waitforrsp,
+                                   OID_802_11_RTS_THRESHOLD, NULL);
+       if (ret) {
+               LEAVE();
+               return ret;
+       }
+
+       vwrq->value = adapter->rtsthsd;
+       vwrq->disabled = ((vwrq->value < MRVDRV_RTS_MIN_VALUE)
+                         || (vwrq->value > MRVDRV_RTS_MAX_VALUE));
+       vwrq->fixed = 1;
+
+       LEAVE();
+       return 0;
+}
+
+static int wlan_set_frag(struct net_device *dev, struct iw_request_info *info,
+                        struct iw_param *vwrq, char *extra)
+{
+       int ret = 0;
+       int fthr = vwrq->value;
+       wlan_private *priv = dev->priv;
+       wlan_adapter *adapter = priv->adapter;
+
+       ENTER();
+
+       if (vwrq->disabled) {
+               adapter->fragthsd = fthr = MRVDRV_FRAG_MAX_VALUE;
+       } else {
+               if (fthr < MRVDRV_FRAG_MIN_VALUE
+                   || fthr > MRVDRV_FRAG_MAX_VALUE)
+                       return -EINVAL;
+               adapter->fragthsd = fthr;
+       }
+
+       ret = libertas_prepare_and_send_command(priv, cmd_802_11_snmp_mib,
+                                   cmd_act_set, cmd_option_waitforrsp,
+                                   OID_802_11_FRAGMENTATION_THRESHOLD, &fthr);
+       LEAVE();
+       return ret;
+}
+
+static int wlan_get_frag(struct net_device *dev, struct iw_request_info *info,
+                        struct iw_param *vwrq, char *extra)
+{
+       int ret = 0;
+       wlan_private *priv = dev->priv;
+       wlan_adapter *adapter = priv->adapter;
+
+       ENTER();
+
+       adapter->fragthsd = 0;
+       ret = libertas_prepare_and_send_command(priv,
+                                   cmd_802_11_snmp_mib,
+                                   cmd_act_get, cmd_option_waitforrsp,
+                                   OID_802_11_FRAGMENTATION_THRESHOLD, NULL);
+       if (ret) {
+               LEAVE();
+               return ret;
+       }
+
+       vwrq->value = adapter->fragthsd;
+       vwrq->disabled = ((vwrq->value < MRVDRV_FRAG_MIN_VALUE)
+                         || (vwrq->value > MRVDRV_FRAG_MAX_VALUE));
+       vwrq->fixed = 1;
+
+       LEAVE();
+       return ret;
+}
+
+static int wlan_get_mode(struct net_device *dev,
+                        struct iw_request_info *info, u32 * uwrq, char *extra)
+{
+       wlan_private *priv = dev->priv;
+       wlan_adapter *adapter = priv->adapter;
+
+       ENTER();
+
+       switch (adapter->inframode) {
+       case wlan802_11ibss:
+               *uwrq = IW_MODE_ADHOC;
+               break;
+
+       case wlan802_11infrastructure:
+               *uwrq = IW_MODE_INFRA;
+               break;
+
+       default:
+       case wlan802_11autounknown:
+               *uwrq = IW_MODE_AUTO;
+               break;
+       }
+
+       LEAVE();
+       return 0;
+}
+
+static int wlan_get_txpow(struct net_device *dev,
+                         struct iw_request_info *info,
+                         struct iw_param *vwrq, char *extra)
+{
+       int ret = 0;
+       wlan_private *priv = dev->priv;
+       wlan_adapter *adapter = priv->adapter;
+
+       ENTER();
+
+       ret = libertas_prepare_and_send_command(priv,
+                                   cmd_802_11_rf_tx_power,
+                                   cmd_act_tx_power_opt_get,
+                                   cmd_option_waitforrsp, 0, NULL);
+
+       if (ret) {
+               LEAVE();
+               return ret;
+       }
+
+       lbs_pr_debug(1, "TXPOWER GET %d dbm.\n", adapter->txpowerlevel);
+       vwrq->value = adapter->txpowerlevel;
+       vwrq->fixed = 1;
+       if (adapter->radioon) {
+               vwrq->disabled = 0;
+               vwrq->flags = IW_TXPOW_DBM;
+       } else {
+               vwrq->disabled = 1;
+       }
+
+       LEAVE();
+       return 0;
+}
+
+static int wlan_set_retry(struct net_device *dev, struct iw_request_info *info,
+                         struct iw_param *vwrq, char *extra)
+{
+       int ret = 0;
+       wlan_private *priv = dev->priv;
+       wlan_adapter *adapter = priv->adapter;
+
+       ENTER();
+
+       if (vwrq->flags == IW_RETRY_LIMIT) {
+               /* The MAC has a 4-bit Total_Tx_Count register
+                  Total_Tx_Count = 1 + Tx_Retry_Count */
+#define TX_RETRY_MIN 0
+#define TX_RETRY_MAX 14
+               if (vwrq->value < TX_RETRY_MIN || vwrq->value > TX_RETRY_MAX)
+                       return -EINVAL;
+
+               /* Adding 1 to convert retry count to try count */
+               adapter->txretrycount = vwrq->value + 1;
+
+               ret = libertas_prepare_and_send_command(priv, cmd_802_11_snmp_mib,
+                                           cmd_act_set,
+                                           cmd_option_waitforrsp,
+                                           OID_802_11_TX_RETRYCOUNT, NULL);
+
+               if (ret) {
+                       LEAVE();
+                       return ret;
+               }
+       } else {
+               return -EOPNOTSUPP;
+       }
+
+       LEAVE();
+       return 0;
+}
+
+static int wlan_get_retry(struct net_device *dev, struct iw_request_info *info,
+                         struct iw_param *vwrq, char *extra)
+{
+       wlan_private *priv = dev->priv;
+       wlan_adapter *adapter = priv->adapter;
+       int ret = 0;
+
+       ENTER();
+       adapter->txretrycount = 0;
+       ret = libertas_prepare_and_send_command(priv,
+                                   cmd_802_11_snmp_mib,
+                                   cmd_act_get, cmd_option_waitforrsp,
+                                   OID_802_11_TX_RETRYCOUNT, NULL);
+       if (ret) {
+               LEAVE();
+               return ret;
+       }
+       vwrq->disabled = 0;
+       if (!vwrq->flags) {
+               vwrq->flags = IW_RETRY_LIMIT;
+               /* Subtract 1 to convert try count to retry count */
+               vwrq->value = adapter->txretrycount - 1;
+       }
+
+       LEAVE();
+       return 0;
+}
+
+static inline void sort_channels(struct iw_freq *freq, int num)
+{
+       int i, j;
+       struct iw_freq temp;
+
+       for (i = 0; i < num; i++)
+               for (j = i + 1; j < num; j++)
+                       if (freq[i].i > freq[j].i) {
+                               temp.i = freq[i].i;
+                               temp.m = freq[i].m;
+
+                               freq[i].i = freq[j].i;
+                               freq[i].m = freq[j].m;
+
+                               freq[j].i = temp.i;
+                               freq[j].m = temp.m;
+                       }
+}
+
+/* data rate listing
+       MULTI_BANDS:
+               abg             a       b       b/g
+   Infra       G(12)           A(8)    B(4)    G(12)
+   Adhoc       A+B(12)         A(8)    B(4)    B(4)
+
+       non-MULTI_BANDS:
+                                       b       b/g
+   Infra                               B(4)    G(12)
+   Adhoc                               B(4)    B(4)
+ */
+/**
+ *  @brief Get Range Info
+ *
+ *  @param dev                  A pointer to net_device structure
+ *  @param info                        A pointer to iw_request_info structure
+ *  @param vwrq                A pointer to iw_param structure
+ *  @param extra               A pointer to extra data buf
+ *  @return                    0 --success, otherwise fail
+ */
+static int wlan_get_range(struct net_device *dev, struct iw_request_info *info,
+                         struct iw_point *dwrq, char *extra)
+{
+       int i, j;
+       wlan_private *priv = dev->priv;
+       wlan_adapter *adapter = priv->adapter;
+       struct iw_range *range = (struct iw_range *)extra;
+       struct chan_freq_power *cfp;
+       u8 rates[WLAN_SUPPORTED_RATES];
+
+       u8 flag = 0;
+
+       ENTER();
+
+       dwrq->length = sizeof(struct iw_range);
+       memset(range, 0, sizeof(struct iw_range));
+
+       range->min_nwid = 0;
+       range->max_nwid = 0;
+
+       memset(rates, 0, sizeof(rates));
+       range->num_bitrates = get_active_data_rates(adapter, rates);
+
+       for (i = 0; i < min_t(__u8, range->num_bitrates, IW_MAX_BITRATES) && rates[i];
+            i++) {
+               range->bitrate[i] = (rates[i] & 0x7f) * 500000;
+       }
+       range->num_bitrates = i;
+       lbs_pr_debug(1, "IW_MAX_BITRATES=%d num_bitrates=%d\n", IW_MAX_BITRATES,
+              range->num_bitrates);
+
+       range->num_frequency = 0;
+       if (priv->adapter->enable11d &&
+           adapter->connect_status == libertas_connected) {
+               u8 chan_no;
+               u8 band;
+
+               struct parsed_region_chan_11d *parsed_region_chan =
+                   &adapter->parsed_region_chan;
+
+               if (parsed_region_chan == NULL) {
+                       lbs_pr_debug(1, "11D:parsed_region_chan is NULL\n");
+                       LEAVE();
+                       return 0;
+               }
+               band = parsed_region_chan->band;
+               lbs_pr_debug(1, "band=%d NoOfChan=%d\n", band,
+                      parsed_region_chan->nr_chan);
+
+               for (i = 0; (range->num_frequency < IW_MAX_FREQUENCIES)
+                    && (i < parsed_region_chan->nr_chan); i++) {
+                       chan_no = parsed_region_chan->chanpwr[i].chan;
+                       lbs_pr_debug(1, "chan_no=%d\n", chan_no);
+                       range->freq[range->num_frequency].i = (long)chan_no;
+                       range->freq[range->num_frequency].m =
+                           (long)libertas_chan_2_freq(chan_no, band) * 100000;
+                       range->freq[range->num_frequency].e = 1;
+                       range->num_frequency++;
+               }
+               flag = 1;
+       }
+       if (!flag) {
+               for (j = 0; (range->num_frequency < IW_MAX_FREQUENCIES)
+                    && (j < sizeof(adapter->region_channel)
+                        / sizeof(adapter->region_channel[0])); j++) {
+                       cfp = adapter->region_channel[j].CFP;
+                       for (i = 0; (range->num_frequency < IW_MAX_FREQUENCIES)
+                            && adapter->region_channel[j].valid
+                            && cfp
+                            && (i < adapter->region_channel[j].nrcfp); i++) {
+                               range->freq[range->num_frequency].i =
+                                   (long)cfp->channel;
+                               range->freq[range->num_frequency].m =
+                                   (long)cfp->freq * 100000;
+                               range->freq[range->num_frequency].e = 1;
+                               cfp++;
+                               range->num_frequency++;
+                       }
+               }
+       }
+
+       lbs_pr_debug(1, "IW_MAX_FREQUENCIES=%d num_frequency=%d\n",
+              IW_MAX_FREQUENCIES, range->num_frequency);
+
+       range->num_channels = range->num_frequency;
+
+       sort_channels(&range->freq[0], range->num_frequency);
+
+       /*
+        * Set an indication of the max TCP throughput in bit/s that we can
+        * expect using this interface
+        */
+       if (i > 2)
+               range->throughput = 5000 * 1000;
+       else
+               range->throughput = 1500 * 1000;
+
+       range->min_rts = MRVDRV_RTS_MIN_VALUE;
+       range->max_rts = MRVDRV_RTS_MAX_VALUE;
+       range->min_frag = MRVDRV_FRAG_MIN_VALUE;
+       range->max_frag = MRVDRV_FRAG_MAX_VALUE;
+
+       range->encoding_size[0] = 5;
+       range->encoding_size[1] = 13;
+       range->num_encoding_sizes = 2;
+       range->max_encoding_tokens = 4;
+
+       range->min_pmp = 1000000;
+       range->max_pmp = 120000000;
+       range->min_pmt = 1000;
+       range->max_pmt = 1000000;
+       range->pmp_flags = IW_POWER_PERIOD;
+       range->pmt_flags = IW_POWER_TIMEOUT;
+       range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R;
+
+       /*
+        * Minimum version we recommend
+        */
+       range->we_version_source = 15;
+
+       /*
+        * Version we are compiled with
+        */
+       range->we_version_compiled = WIRELESS_EXT;
+
+       range->retry_capa = IW_RETRY_LIMIT;
+       range->retry_flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
+
+       range->min_retry = TX_RETRY_MIN;
+       range->max_retry = TX_RETRY_MAX;
+
+       /*
+        * Set the qual, level and noise range values
+        */
+       range->max_qual.qual = 100;
+       range->max_qual.level = 0;
+       range->max_qual.noise = 0;
+       range->max_qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
+
+       range->avg_qual.qual = 70;
+       /* TODO: Find real 'good' to 'bad' threshold value for RSSI */
+       range->avg_qual.level = 0;
+       range->avg_qual.noise = 0;
+       range->avg_qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
+
+       range->sensitivity = 0;
+
+       /*
+        * Setup the supported power level ranges
+        */
+       memset(range->txpower, 0, sizeof(range->txpower));
+       range->txpower[0] = 5;
+       range->txpower[1] = 7;
+       range->txpower[2] = 9;
+       range->txpower[3] = 11;
+       range->txpower[4] = 13;
+       range->txpower[5] = 15;
+       range->txpower[6] = 17;
+       range->txpower[7] = 19;
+
+       range->num_txpower = 8;
+       range->txpower_capa = IW_TXPOW_DBM;
+       range->txpower_capa |= IW_TXPOW_RANGE;
+
+       range->event_capa[0] = (IW_EVENT_CAPA_K_0 |
+                               IW_EVENT_CAPA_MASK(SIOCGIWAP) |
+                               IW_EVENT_CAPA_MASK(SIOCGIWSCAN));
+       range->event_capa[1] = IW_EVENT_CAPA_K_1;
+
+       if (adapter->fwcapinfo & FW_CAPINFO_WPA) {
+               range->enc_capa =   IW_ENC_CAPA_WPA
+                                 | IW_ENC_CAPA_WPA2
+                                 | IW_ENC_CAPA_CIPHER_TKIP
+                                 | IW_ENC_CAPA_CIPHER_CCMP;
+       }
+
+       LEAVE();
+       return 0;
+}
+
+static int wlan_set_power(struct net_device *dev, struct iw_request_info *info,
+                         struct iw_param *vwrq, char *extra)
+{
+       wlan_private *priv = dev->priv;
+       wlan_adapter *adapter = priv->adapter;
+
+       ENTER();
+
+       /* PS is currently supported only in Infrastructure mode
+        * Remove this check if it is to be supported in IBSS mode also
+        */
+
+       if (vwrq->disabled) {
+               adapter->psmode = wlan802_11powermodecam;
+               if (adapter->psstate != PS_STATE_FULL_POWER) {
+                       libertas_ps_wakeup(priv, cmd_option_waitforrsp);
+               }
+
+               return 0;
+       }
+
+       if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
+               lbs_pr_debug(1,
+                      "Setting power timeout command is not supported\n");
+               return -EINVAL;
+       } else if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_PERIOD) {
+               lbs_pr_debug(1, "Setting power period command is not supported\n");
+               return -EINVAL;
+       }
+
+       if (adapter->psmode != wlan802_11powermodecam) {
+               return 0;
+       }
+
+       adapter->psmode = wlan802_11powermodemax_psp;
+
+       if (adapter->connect_status == libertas_connected) {
+               libertas_ps_sleep(priv, cmd_option_waitforrsp);
+       }
+
+       LEAVE();
+       return 0;
+}
+
+static int wlan_get_power(struct net_device *dev, struct iw_request_info *info,
+                         struct iw_param *vwrq, char *extra)
+{
+       wlan_private *priv = dev->priv;
+       wlan_adapter *adapter = priv->adapter;
+       int mode;
+
+       ENTER();
+
+       mode = adapter->psmode;
+
+       if ((vwrq->disabled = (mode == wlan802_11powermodecam))
+           || adapter->connect_status == libertas_disconnected) {
+               LEAVE();
+               return 0;
+       }
+
+       vwrq->value = 0;
+
+       LEAVE();
+       return 0;
+}
+
+/*
+ * iwpriv settable callbacks
+ */
+
+static const iw_handler wlan_private_handler[] = {
+       NULL,                   /* SIOCIWFIRSTPRIV */
+};
+
+static const struct iw_priv_args wlan_private_args[] = {
+       /*
+        * { cmd, set_args, get_args, name }
+        */
+       {
+        WLANSCAN_TYPE,
+        IW_PRIV_TYPE_CHAR | 8,
+        IW_PRIV_TYPE_CHAR | 8,
+        "scantype"},
+
+       {
+        WLAN_SETINT_GETINT,
+        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        ""},
+       {
+        WLANNF,
+        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        "getNF"},
+       {
+        WLANRSSI,
+        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        "getRSSI"},
+       {
+        WLANENABLE11D,
+        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        "enable11d"},
+       {
+        WLANADHOCGRATE,
+        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        "adhocgrate"},
+
+       {
+        WLAN_SUBCMD_SET_PRESCAN,
+        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        "prescan"},
+       {
+        WLAN_SETONEINT_GETONEINT,
+        IW_PRIV_TYPE_INT | 1,
+        IW_PRIV_TYPE_INT | 1,
+        ""},
+       {
+        WLAN_BEACON_INTERVAL,
+        IW_PRIV_TYPE_INT | 1,
+        IW_PRIV_TYPE_INT | 1,
+        "bcninterval"},
+       {
+        WLAN_LISTENINTRVL,
+        IW_PRIV_TYPE_INT | 1,
+        IW_PRIV_TYPE_INT | 1,
+        "lolisteninter"},
+       {
+        WLAN_TXCONTROL,
+        IW_PRIV_TYPE_INT | 1,
+        IW_PRIV_TYPE_INT | 1,
+        "txcontrol"},
+       {
+        WLAN_NULLPKTINTERVAL,
+        IW_PRIV_TYPE_INT | 1,
+        IW_PRIV_TYPE_INT | 1,
+        "psnullinterval"},
+       /* Using iwpriv sub-command feature */
+       {
+        WLAN_SETONEINT_GETNONE,        /* IOCTL: 24 */
+        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        IW_PRIV_TYPE_NONE,
+        ""},
+
+       {
+        WLAN_SUBCMD_SETRXANTENNA,
+        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        IW_PRIV_TYPE_NONE,
+        "setrxant"},
+       {
+        WLAN_SUBCMD_SETTXANTENNA,
+        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        IW_PRIV_TYPE_NONE,
+        "settxant"},
+       {
+        WLANSETAUTHALG,
+        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        IW_PRIV_TYPE_NONE,
+        "authalgs",
+        },
+       {
+        WLANSET8021XAUTHALG,
+        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        IW_PRIV_TYPE_NONE,
+        "8021xauthalgs",
+        },
+       {
+        WLANSETENCRYPTIONMODE,
+        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        IW_PRIV_TYPE_NONE,
+        "encryptionmode",
+        },
+       {
+        WLANSETREGION,
+        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        IW_PRIV_TYPE_NONE,
+        "setregioncode"},
+       {
+        WLAN_SET_LISTEN_INTERVAL,
+        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        IW_PRIV_TYPE_NONE,
+        "setlisteninter"},
+       {
+        WLAN_SET_MULTIPLE_DTIM,
+        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        IW_PRIV_TYPE_NONE,
+        "setmultipledtim"},
+       {
+        WLAN_SET_ATIM_WINDOW,
+        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        IW_PRIV_TYPE_NONE,
+        "atimwindow"},
+       {
+        WLANSETBCNAVG,
+        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        IW_PRIV_TYPE_NONE,
+        "setbcnavg"},
+       {
+        WLANSETDATAAVG,
+        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        IW_PRIV_TYPE_NONE,
+        "setdataavg"},
+       {
+        WLAN_SET_LINKMODE,
+        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        IW_PRIV_TYPE_NONE,
+        "linkmode"},
+       {
+        WLAN_SET_RADIOMODE,
+        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        IW_PRIV_TYPE_NONE,
+        "radiomode"},
+       {
+        WLAN_SET_DEBUGMODE,
+        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        IW_PRIV_TYPE_NONE,
+        "debugmode"},
+       {
+        WLAN_SUBCMD_MESH_SET_TTL,
+        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        IW_PRIV_TYPE_NONE,
+        "mesh_set_ttl"},
+       {
+        WLAN_SETNONE_GETONEINT,
+        IW_PRIV_TYPE_NONE,
+        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        ""},
+       {
+        WLANGETREGION,
+        IW_PRIV_TYPE_NONE,
+        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        "getregioncode"},
+       {
+        WLAN_GET_LISTEN_INTERVAL,
+        IW_PRIV_TYPE_NONE,
+        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        "getlisteninter"},
+       {
+        WLAN_GET_MULTIPLE_DTIM,
+        IW_PRIV_TYPE_NONE,
+        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        "getmultipledtim"},
+       {
+        WLAN_GET_TX_RATE,
+        IW_PRIV_TYPE_NONE,
+        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        "gettxrate"},
+       {
+        WLANGETBCNAVG,
+        IW_PRIV_TYPE_NONE,
+        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        "getbcnavg"},
+       {
+        WLAN_GET_LINKMODE,
+        IW_PRIV_TYPE_NONE,
+        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        "get_linkmode"},
+       {
+        WLAN_GET_RADIOMODE,
+        IW_PRIV_TYPE_NONE,
+        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        "get_radiomode"},
+       {
+        WLAN_GET_DEBUGMODE,
+        IW_PRIV_TYPE_NONE,
+        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        "get_debugmode"},
+       {
+        WLAN_SUBCMD_FWT_CLEANUP,
+        IW_PRIV_TYPE_NONE,
+        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        "fwt_cleanup"},
+       {
+        WLAN_SUBCMD_FWT_TIME,
+        IW_PRIV_TYPE_NONE,
+        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        "fwt_time"},
+       {
+        WLAN_SUBCMD_MESH_GET_TTL,
+        IW_PRIV_TYPE_NONE,
+        IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+        "mesh_get_ttl"},
+       {
+        WLAN_SETNONE_GETTWELVE_CHAR,
+        IW_PRIV_TYPE_NONE,
+        IW_PRIV_TYPE_CHAR | 12,
+        ""},
+       {
+        WLAN_SUBCMD_GETRXANTENNA,
+        IW_PRIV_TYPE_NONE,
+        IW_PRIV_TYPE_CHAR | 12,
+        "getrxant"},
+       {
+        WLAN_SUBCMD_GETTXANTENNA,
+        IW_PRIV_TYPE_NONE,
+        IW_PRIV_TYPE_CHAR | 12,
+        "gettxant"},
+       {
+        WLAN_GET_TSF,
+        IW_PRIV_TYPE_NONE,
+        IW_PRIV_TYPE_CHAR | 12,
+        "gettsf"},
+       {
+        WLAN_SETNONE_GETNONE,
+        IW_PRIV_TYPE_NONE,
+        IW_PRIV_TYPE_NONE,
+        ""},
+       {
+        WLANDEAUTH,
+        IW_PRIV_TYPE_NONE,
+        IW_PRIV_TYPE_NONE,
+        "deauth"},
+       {
+        WLANADHOCSTOP,
+        IW_PRIV_TYPE_NONE,
+        IW_PRIV_TYPE_NONE,
+        "adhocstop"},
+       {
+        WLANRADIOON,
+        IW_PRIV_TYPE_NONE,
+        IW_PRIV_TYPE_NONE,
+        "radioon"},
+       {
+        WLANRADIOOFF,
+        IW_PRIV_TYPE_NONE,
+        IW_PRIV_TYPE_NONE,
+        "radiooff"},
+       {
+        WLANWLANIDLEON,
+        IW_PRIV_TYPE_NONE,
+        IW_PRIV_TYPE_NONE,
+        "wlanidle-on"},
+       {
+        WLANWLANIDLEOFF,
+        IW_PRIV_TYPE_NONE,
+        IW_PRIV_TYPE_NONE,
+        "wlanidle-off"},
+       {
+        WLAN_SUBCMD_FWT_RESET,
+        IW_PRIV_TYPE_NONE,
+        IW_PRIV_TYPE_NONE,
+        "fwt_reset"},
+       {
+        WLAN_SUBCMD_BT_RESET,
+        IW_PRIV_TYPE_NONE,
+        IW_PRIV_TYPE_NONE,
+        "bt_reset"},
+       {
+        WLAN_SET128CHAR_GET128CHAR,
+        IW_PRIV_TYPE_CHAR | 128,
+        IW_PRIV_TYPE_CHAR | 128,
+        ""},
+       /* BT Management */
+       {
+        WLAN_SUBCMD_BT_ADD,
+        IW_PRIV_TYPE_CHAR | 128,
+        IW_PRIV_TYPE_CHAR | 128,
+        "bt_add"},
+       {
+        WLAN_SUBCMD_BT_DEL,
+        IW_PRIV_TYPE_CHAR | 128,
+        IW_PRIV_TYPE_CHAR | 128,
+        "bt_del"},
+       {
+        WLAN_SUBCMD_BT_LIST,
+        IW_PRIV_TYPE_CHAR | 128,
+        IW_PRIV_TYPE_CHAR | 128,
+        "bt_list"},
+       /* FWT Management */
+       {
+        WLAN_SUBCMD_FWT_ADD,
+        IW_PRIV_TYPE_CHAR | 128,
+        IW_PRIV_TYPE_CHAR | 128,
+        "fwt_add"},
+       {
+        WLAN_SUBCMD_FWT_DEL,
+        IW_PRIV_TYPE_CHAR | 128,
+        IW_PRIV_TYPE_CHAR | 128,
+        "fwt_del"},
+       {
+        WLAN_SUBCMD_FWT_LOOKUP,
+        IW_PRIV_TYPE_CHAR | 128,
+        IW_PRIV_TYPE_CHAR | 128,
+        "fwt_lookup"},
+       {
+        WLAN_SUBCMD_FWT_LIST_NEIGHBOR,
+        IW_PRIV_TYPE_CHAR | 128,
+        IW_PRIV_TYPE_CHAR | 128,
+        "fwt_list_neigh"},
+       {
+        WLAN_SUBCMD_FWT_LIST,
+        IW_PRIV_TYPE_CHAR | 128,
+        IW_PRIV_TYPE_CHAR | 128,
+        "fwt_list"},
+       {
+        WLAN_SUBCMD_FWT_LIST_ROUTE,
+        IW_PRIV_TYPE_CHAR | 128,
+        IW_PRIV_TYPE_CHAR | 128,
+        "fwt_list_route"},
+       {
+        WLANSCAN_MODE,
+        IW_PRIV_TYPE_CHAR | 128,
+        IW_PRIV_TYPE_CHAR | 128,
+        "scanmode"},
+       {
+        WLAN_GET_ADHOC_STATUS,
+        IW_PRIV_TYPE_CHAR | 128,
+        IW_PRIV_TYPE_CHAR | 128,
+        "getadhocstatus"},
+       {
+        WLAN_SETNONE_GETWORDCHAR,
+        IW_PRIV_TYPE_NONE,
+        IW_PRIV_TYPE_CHAR | 128,
+        ""},
+       {
+        WLANSETWPAIE,
+        IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 24,
+        IW_PRIV_TYPE_NONE,
+        "setwpaie"},
+       {
+        WLANGETLOG,
+        IW_PRIV_TYPE_NONE,
+        IW_PRIV_TYPE_CHAR | GETLOG_BUFSIZE,
+        "getlog"},
+       {
+        WLAN_SET_GET_SIXTEEN_INT,
+        IW_PRIV_TYPE_INT | 16,
+        IW_PRIV_TYPE_INT | 16,
+        ""},
+       {
+        WLAN_TPCCFG,
+        IW_PRIV_TYPE_INT | 16,
+        IW_PRIV_TYPE_INT | 16,
+        "tpccfg"},
+       {
+        WLAN_POWERCFG,
+        IW_PRIV_TYPE_INT | 16,
+        IW_PRIV_TYPE_INT | 16,
+        "powercfg"},
+       {
+        WLAN_AUTO_FREQ_SET,
+        IW_PRIV_TYPE_INT | 16,
+        IW_PRIV_TYPE_INT | 16,
+        "setafc"},
+       {
+        WLAN_AUTO_FREQ_GET,
+        IW_PRIV_TYPE_INT | 16,
+        IW_PRIV_TYPE_INT | 16,
+        "getafc"},
+       {
+        WLAN_SCANPROBES,
+        IW_PRIV_TYPE_INT | 16,
+        IW_PRIV_TYPE_INT | 16,
+        "scanprobes"},
+       {
+        WLAN_LED_GPIO_CTRL,
+        IW_PRIV_TYPE_INT | 16,
+        IW_PRIV_TYPE_INT | 16,
+        "ledgpio"},
+       {
+        WLAN_ADAPT_RATESET,
+        IW_PRIV_TYPE_INT | 16,
+        IW_PRIV_TYPE_INT | 16,
+        "rateadapt"},
+       {
+        WLAN_INACTIVITY_TIMEOUT,
+        IW_PRIV_TYPE_INT | 16,
+        IW_PRIV_TYPE_INT | 16,
+        "inactivityto"},
+       {
+        WLANSNR,
+        IW_PRIV_TYPE_INT | 16,
+        IW_PRIV_TYPE_INT | 16,
+        "getSNR"},
+       {
+        WLAN_GET_RATE,
+        IW_PRIV_TYPE_INT | 16,
+        IW_PRIV_TYPE_INT | 16,
+        "getrate"},
+       {
+        WLAN_GET_RXINFO,
+        IW_PRIV_TYPE_INT | 16,
+        IW_PRIV_TYPE_INT | 16,
+        "getrxinfo"},
+};
+
+static struct iw_statistics *wlan_get_wireless_stats(struct net_device *dev)
+{
+       enum {
+               POOR = 30,
+               FAIR = 60,
+               GOOD = 80,
+               VERY_GOOD = 90,
+               EXCELLENT = 95,
+               PERFECT = 100
+       };
+       wlan_private *priv = dev->priv;
+       wlan_adapter *adapter = priv->adapter;
+       u32 rssi_qual;
+       u32 tx_qual;
+       u32 quality = 0;
+       int stats_valid = 0;
+       u8 rssi;
+       u32 tx_retries;
+
+       ENTER();
+
+       priv->wstats.status = adapter->inframode;
+
+       /* If we're not associated, all quality values are meaningless */
+       if (adapter->connect_status != libertas_connected)
+               goto out;
+
+       /* Quality by RSSI */
+       priv->wstats.qual.level =
+           CAL_RSSI(adapter->SNR[TYPE_BEACON][TYPE_NOAVG],
+            adapter->NF[TYPE_BEACON][TYPE_NOAVG]);
+
+       if (adapter->NF[TYPE_BEACON][TYPE_NOAVG] == 0) {
+               priv->wstats.qual.noise = MRVDRV_NF_DEFAULT_SCAN_VALUE;
+       } else {
+               priv->wstats.qual.noise =
+                   CAL_NF(adapter->NF[TYPE_BEACON][TYPE_NOAVG]);
+       }
+
+       lbs_pr_debug(1, "Signal Level = %#x\n", priv->wstats.qual.level);
+       lbs_pr_debug(1, "Noise = %#x\n", priv->wstats.qual.noise);
+
+       rssi = priv->wstats.qual.level - priv->wstats.qual.noise;
+       if (rssi < 15)
+               rssi_qual = rssi * POOR / 10;
+       else if (rssi < 20)
+               rssi_qual = (rssi - 15) * (FAIR - POOR) / 5 + POOR;
+       else if (rssi < 30)
+               rssi_qual = (rssi - 20) * (GOOD - FAIR) / 5 + FAIR;
+       else if (rssi < 40)
+               rssi_qual = (rssi - 30) * (VERY_GOOD - GOOD) /
+                   10 + GOOD;
+       else
+               rssi_qual = (rssi - 40) * (PERFECT - VERY_GOOD) /
+                   10 + VERY_GOOD;
+       quality = rssi_qual;
+
+       /* Quality by TX errors */
+       priv->wstats.discard.retries = priv->stats.tx_errors;
+
+       tx_retries = adapter->logmsg.retry;
+
+       if (tx_retries > 75)
+               tx_qual = (90 - tx_retries) * POOR / 15;
+       else if (tx_retries > 70)
+               tx_qual = (75 - tx_retries) * (FAIR - POOR) / 5 + POOR;
+       else if (tx_retries > 65)
+               tx_qual = (70 - tx_retries) * (GOOD - FAIR) / 5 + FAIR;
+       else if (tx_retries > 50)
+               tx_qual = (65 - tx_retries) * (VERY_GOOD - GOOD) /
+                   15 + GOOD;
+       else
+               tx_qual = (50 - tx_retries) *
+                   (PERFECT - VERY_GOOD) / 50 + VERY_GOOD;
+       quality = min(quality, tx_qual);
+
+       priv->wstats.discard.code = adapter->logmsg.wepundecryptable;
+       priv->wstats.discard.fragment = adapter->logmsg.fcserror;
+       priv->wstats.discard.retries = tx_retries;
+       priv->wstats.discard.misc = adapter->logmsg.ackfailure;
+
+       /* Calculate quality */
+       priv->wstats.qual.qual = max(quality, (u32)100);
+       priv->wstats.qual.updated = IW_QUAL_ALL_UPDATED | IW_QUAL_DBM;
+       stats_valid = 1;
+
+       /* update stats asynchronously for future calls */
+       libertas_prepare_and_send_command(priv, cmd_802_11_rssi, 0,
+                                       0, 0, NULL);
+       libertas_prepare_and_send_command(priv, cmd_802_11_get_log, 0,
+                                       0, 0, NULL);
+out:
+       if (!stats_valid) {
+               priv->wstats.miss.beacon = 0;
+               priv->wstats.discard.retries = 0;
+               priv->wstats.qual.qual = 0;
+               priv->wstats.qual.level = 0;
+               priv->wstats.qual.noise = 0;
+               priv->wstats.qual.updated = IW_QUAL_ALL_UPDATED;
+               priv->wstats.qual.updated |= IW_QUAL_NOISE_INVALID |
+                   IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID;
+       }
+
+       LEAVE ();
+       return &priv->wstats;
+
+
+}
+
+static int wlan_set_freq(struct net_device *dev, struct iw_request_info *info,
+                 struct iw_freq *fwrq, char *extra)
+{
+       int ret = 0;
+       wlan_private *priv = dev->priv;
+       wlan_adapter *adapter = priv->adapter;
+       int rc = -EINPROGRESS;  /* Call commit handler */
+       struct chan_freq_power *cfp;
+
+       ENTER();
+
+       /*
+        * If setting by frequency, convert to a channel
+        */
+       if (fwrq->e == 1) {
+
+               long f = fwrq->m / 100000;
+               int c = 0;
+
+               cfp = find_cfp_by_band_and_freq(adapter, 0, f);
+               if (!cfp) {
+                       lbs_pr_debug(1, "Invalid freq=%ld\n", f);
+                       return -EINVAL;
+               }
+
+               c = (int)cfp->channel;
+
+               if (c < 0)
+                       return -EINVAL;
+
+               fwrq->e = 0;
+               fwrq->m = c;
+       }
+
+       /*
+        * Setting by channel number
+        */
+       if (fwrq->m > 1000 || fwrq->e > 0) {
+               rc = -EOPNOTSUPP;
+       } else {
+               int channel = fwrq->m;
+
+               cfp = libertas_find_cfp_by_band_and_channel(adapter, 0, channel);
+               if (!cfp) {
+                       rc = -EINVAL;
+               } else {
+                       if (adapter->inframode == wlan802_11ibss) {
+                               rc = changeadhocchannel(priv, channel);
+                               /*  If station is WEP enabled, send the
+                                *  command to set WEP in firmware
+                                */
+                               if (adapter->secinfo.WEPstatus ==
+                                   wlan802_11WEPenabled) {
+                                       lbs_pr_debug(1, "set_freq: WEP enabled\n");
+                                       ret = libertas_prepare_and_send_command(priv,
+                                                                   cmd_802_11_set_wep,
+                                                                   cmd_act_add,
+                                                                   cmd_option_waitforrsp,
+                                                                   0,
+                                                                   NULL);
+
+                                       if (ret) {
+                                               LEAVE();
+                                               return ret;
+                                       }
+
+                                       adapter->currentpacketfilter |=
+                                           cmd_act_mac_wep_enable;
+
+                                       libertas_set_mac_packet_filter(priv);
+                               }
+                       } else {
+                               rc = -EOPNOTSUPP;
+                       }
+               }
+       }
+
+       LEAVE();
+       return rc;
+}
+
+/**
+ *  @brief use index to get the data rate
+ *
+ *  @param index                The index of data rate
+ *  @return                    data rate or 0
+ */
+u32 libertas_index_to_data_rate(u8 index)
+{
+       if (index >= sizeof(libertas_wlan_data_rates))
+               index = 0;
+
+       return libertas_wlan_data_rates[index];
+}
+
+/**
+ *  @brief use rate to get the index
+ *
+ *  @param rate                 data rate
+ *  @return                    index or 0
+ */
+u8 libertas_data_rate_to_index(u32 rate)
+{
+       u8 *ptr;
+
+       if (rate)
+               if ((ptr = memchr(libertas_wlan_data_rates, (u8) rate,
+                                 sizeof(libertas_wlan_data_rates))))
+                       return (ptr - libertas_wlan_data_rates);
+
+       return 0;
+}
+
+static int wlan_set_rate(struct net_device *dev, struct iw_request_info *info,
+                 struct iw_param *vwrq, char *extra)
+{
+       wlan_private *priv = dev->priv;
+       wlan_adapter *adapter = priv->adapter;
+       u32 data_rate;
+       u16 action;
+       int ret = 0;
+       u8 rates[WLAN_SUPPORTED_RATES];
+       u8 *rate;
+
+       ENTER();
+
+       lbs_pr_debug(1, "Vwrq->value = %d\n", vwrq->value);
+
+       if (vwrq->value == -1) {
+               action = cmd_act_set_tx_auto;   // Auto
+               adapter->is_datarate_auto = 1;
+               adapter->datarate = 0;
+       } else {
+               if (vwrq->value % 100000) {
+                       return -EINVAL;
+               }
+
+               data_rate = vwrq->value / 500000;
+
+               memset(rates, 0, sizeof(rates));
+               get_active_data_rates(adapter, rates);
+               rate = rates;
+               while (*rate) {
+                       lbs_pr_debug(1, "Rate=0x%X  Wanted=0x%X\n", *rate,
+                              data_rate);
+                       if ((*rate & 0x7f) == (data_rate & 0x7f))
+                               break;
+                       rate++;
+               }
+               if (!*rate) {
+                       lbs_pr_alert( "The fixed data rate 0x%X is out "
+                              "of range.\n", data_rate);
+                       return -EINVAL;
+               }
+
+               adapter->datarate = data_rate;
+               action = cmd_act_set_tx_fix_rate;
+               adapter->is_datarate_auto = 0;
+       }
+
+       ret = libertas_prepare_and_send_command(priv, cmd_802_11_data_rate,
+                                   action, cmd_option_waitforrsp, 0, NULL);
+
+       LEAVE();
+       return ret;
+}
+
+static int wlan_get_rate(struct net_device *dev, struct iw_request_info *info,
+                 struct iw_param *vwrq, char *extra)
+{
+       wlan_private *priv = dev->priv;
+       wlan_adapter *adapter = priv->adapter;
+
+       ENTER();
+
+       if (adapter->is_datarate_auto) {
+               vwrq->fixed = 0;
+       } else {
+               vwrq->fixed = 1;
+       }
+
+       vwrq->value = adapter->datarate * 500000;
+
+       LEAVE();
+       return 0;
+}
+
+static int wlan_set_mode(struct net_device *dev,
+                 struct iw_request_info *info, u32 * uwrq, char *extra)
+{
+       int ret = 0;
+       wlan_private *priv = dev->priv;
+       wlan_adapter *adapter = priv->adapter;
+       struct assoc_request * assoc_req;
+       enum WLAN_802_11_NETWORK_INFRASTRUCTURE new_mode;
+
+       ENTER();
+
+       switch (*uwrq) {
+       case IW_MODE_ADHOC:
+               lbs_pr_debug(1, "Wanted mode is ad-hoc: current datarate=%#x\n",
+                      adapter->datarate);
+               new_mode = wlan802_11ibss;
+               adapter->adhocchannel = DEFAULT_AD_HOC_CHANNEL;
+               break;
+
+       case IW_MODE_INFRA:
+               lbs_pr_debug(1, "Wanted mode is Infrastructure\n");
+               new_mode = wlan802_11infrastructure;
+               break;
+
+       case IW_MODE_AUTO:
+               lbs_pr_debug(1, "Wanted mode is Auto\n");
+               new_mode = wlan802_11autounknown;
+               break;
+
+       default:
+               lbs_pr_debug(1, "Wanted mode is Unknown: 0x%x\n", *uwrq);
+               return -EINVAL;
+       }
+
+       mutex_lock(&adapter->lock);
+       assoc_req = wlan_get_association_request(adapter);
+       if (!assoc_req) {
+               ret = -ENOMEM;
+       } else {
+               assoc_req->mode = new_mode;
+       }
+
+       if (ret == 0) {
+               set_bit(ASSOC_FLAG_MODE, &assoc_req->flags);
+               wlan_postpone_association_work(priv);
+       } else {
+               wlan_cancel_association_work(priv);
+       }
+       mutex_unlock(&adapter->lock);
+
+       LEAVE();
+       return ret;
+}
+
+
+/**
+ *  @brief Get Encryption key
+ *
+ *  @param dev                  A pointer to net_device structure
+ *  @param info                        A pointer to iw_request_info structure
+ *  @param vwrq                A pointer to iw_param structure
+ *  @param extra               A pointer to extra data buf
+ *  @return                    0 --success, otherwise fail
+ */
+static int wlan_get_encode(struct net_device *dev,
+                          struct iw_request_info *info,
+                          struct iw_point *dwrq, u8 * extra)
+{
+       wlan_private *priv = dev->priv;
+       wlan_adapter *adapter = priv->adapter;
+       int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
+
+       ENTER();
+
+       lbs_pr_debug(1, "flags=0x%x index=%d length=%d wep_tx_keyidx=%d\n",
+              dwrq->flags, index, dwrq->length, adapter->wep_tx_keyidx);
+
+       dwrq->flags = 0;
+
+       /* Authentication method */
+       switch (adapter->secinfo.authmode) {
+       case wlan802_11authmodeopen:
+               dwrq->flags = IW_ENCODE_OPEN;
+               break;
+
+       case wlan802_11authmodeshared:
+       case wlan802_11authmodenetworkEAP:
+               dwrq->flags = IW_ENCODE_RESTRICTED;
+               break;
+       default:
+               dwrq->flags = IW_ENCODE_DISABLED | IW_ENCODE_OPEN;
+               break;
+       }
+
+       if ((adapter->secinfo.WEPstatus == wlan802_11WEPenabled)
+           || adapter->secinfo.WPAenabled || adapter->secinfo.WPA2enabled) {
+               dwrq->flags &= ~IW_ENCODE_DISABLED;
+       } else {
+               dwrq->flags |= IW_ENCODE_DISABLED;
+       }
+
+       memset(extra, 0, 16);
+
+       mutex_lock(&adapter->lock);
+
+       /* Default to returning current transmit key */
+       if (index < 0)
+               index = adapter->wep_tx_keyidx;
+
+       if ((adapter->wep_keys[index].len) &&
+           (adapter->secinfo.WEPstatus == wlan802_11WEPenabled)) {
+               memcpy(extra, adapter->wep_keys[index].key,
+                      adapter->wep_keys[index].len);
+               dwrq->length = adapter->wep_keys[index].len;
+
+               dwrq->flags |= (index + 1);
+               /* Return WEP enabled */
+               dwrq->flags &= ~IW_ENCODE_DISABLED;
+       } else if ((adapter->secinfo.WPAenabled)
+                  || (adapter->secinfo.WPA2enabled)) {
+               /* return WPA enabled */
+               dwrq->flags &= ~IW_ENCODE_DISABLED;
+       } else {
+               dwrq->flags |= IW_ENCODE_DISABLED;
+       }
+
+       mutex_unlock(&adapter->lock);
+
+       dwrq->flags |= IW_ENCODE_NOKEY;
+
+       lbs_pr_debug(1, "key:%02x:%02x:%02x:%02x:%02x:%02x keylen=%d\n",
+              extra[0], extra[1], extra[2],
+              extra[3], extra[4], extra[5], dwrq->length);
+
+       lbs_pr_debug(1, "Return flags=0x%x\n", dwrq->flags);
+
+       LEAVE();
+       return 0;
+}
+
+/**
+ *  @brief Set Encryption key (internal)
+ *
+ *  @param priv                        A pointer to private card structure
+ *  @param key_material                A pointer to key material
+ *  @param key_length          length of key material
+ *  @param index               key index to set
+ *  @param set_tx_key          Force set TX key (1 = yes, 0 = no)
+ *  @return                    0 --success, otherwise fail
+ */
+static int wlan_set_wep_key(struct assoc_request *assoc_req,
+                           const char *key_material,
+                           u16 key_length,
+                           u16 index,
+                           int set_tx_key)
+{
+       struct WLAN_802_11_KEY *pkey;
+
+       ENTER();
+
+       /* Paranoid validation of key index */
+       if (index > 3) {
+               LEAVE();
+               return -EINVAL;
+       }
+
+       /* validate max key length */
+       if (key_length > KEY_LEN_WEP_104) {
+               LEAVE();
+               return -EINVAL;
+       }
+
+       pkey = &assoc_req->wep_keys[index];
+
+       if (key_length > 0) {
+               memset(pkey, 0, sizeof(struct WLAN_802_11_KEY));
+               pkey->type = KEY_TYPE_ID_WEP;
+
+               /* Standardize the key length */
+               pkey->len = (key_length > KEY_LEN_WEP_40) ?
+                               KEY_LEN_WEP_104 : KEY_LEN_WEP_40;
+               memcpy(pkey->key, key_material, key_length);
+       }
+
+       if (set_tx_key) {
+               /* Ensure the chosen key is valid */
+               if (!pkey->len) {
+                       lbs_pr_debug(1, "key not set, so cannot enable it\n");
+                       LEAVE();
+                       return -EINVAL;
+               }
+               assoc_req->wep_tx_keyidx = index;
+       }
+
+       assoc_req->secinfo.WEPstatus = wlan802_11WEPenabled;
+
+       LEAVE();
+       return 0;
+}
+
+static int validate_key_index(u16 def_index, u16 raw_index,
+                             u16 *out_index, u16 *is_default)
+{
+       if (!out_index || !is_default)
+               return -EINVAL;
+
+       /* Verify index if present, otherwise use default TX key index */
+       if (raw_index > 0) {
+               if (raw_index > 4)
+                       return -EINVAL;
+               *out_index = raw_index - 1;
+       } else {
+               *out_index = def_index;
+               *is_default = 1;
+       }
+       return 0;
+}
+
+static void disable_wep(struct assoc_request *assoc_req)
+{
+       int i;
+
+       /* Set Open System auth mode */
+       assoc_req->secinfo.authmode = wlan802_11authmodeopen;
+
+       /* Clear WEP keys and mark WEP as disabled */
+       assoc_req->secinfo.WEPstatus = wlan802_11WEPdisabled;
+       for (i = 0; i < 4; i++)
+               assoc_req->wep_keys[i].len = 0;
+
+       set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
+       set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags);
+}
+
+/**
+ *  @brief Set Encryption key
+ *
+ *  @param dev                  A pointer to net_device structure
+ *  @param info                        A pointer to iw_request_info structure
+ *  @param vwrq                A pointer to iw_param structure
+ *  @param extra               A pointer to extra data buf
+ *  @return                    0 --success, otherwise fail
+ */
+static int wlan_set_encode(struct net_device *dev,
+                   struct iw_request_info *info,
+                   struct iw_point *dwrq, char *extra)
+{
+       int ret = 0;
+       wlan_private *priv = dev->priv;
+       wlan_adapter *adapter = priv->adapter;
+       struct assoc_request * assoc_req;
+       u16 is_default = 0, index = 0, set_tx_key = 0;
+
+       ENTER();
+
+       mutex_lock(&adapter->lock);
+       assoc_req = wlan_get_association_request(adapter);
+       if (!assoc_req) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       if (dwrq->flags & IW_ENCODE_DISABLED) {
+               disable_wep (assoc_req);
+               goto out;
+       }
+
+       ret = validate_key_index(assoc_req->wep_tx_keyidx,
+                                (dwrq->flags & IW_ENCODE_INDEX),
+                                &index, &is_default);
+       if (ret) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       /* If WEP isn't enabled, or if there is no key data but a valid
+        * index, set the TX key.
+        */
+       if ((assoc_req->secinfo.WEPstatus != wlan802_11WEPenabled)
+           || (dwrq->length == 0 && !is_default))
+               set_tx_key = 1;
+
+       ret = wlan_set_wep_key(assoc_req, extra, dwrq->length, index, set_tx_key);
+       if (ret)
+               goto out;
+
+       if (dwrq->length)
+               set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags);
+       if (set_tx_key)
+               set_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags);
+
+       if (dwrq->flags & IW_ENCODE_RESTRICTED) {
+               assoc_req->secinfo.authmode = wlan802_11authmodeshared;
+       } else if (dwrq->flags & IW_ENCODE_OPEN) {
+               assoc_req->secinfo.authmode = wlan802_11authmodeopen;
+       }
+
+out:
+       if (ret == 0) {
+               set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
+               wlan_postpone_association_work(priv);
+       } else {
+               wlan_cancel_association_work(priv);
+       }
+       mutex_unlock(&adapter->lock);
+
+       LEAVE();
+       return ret;
+}
+
+/**
+ *  @brief Get Extended Encryption key (WPA/802.1x and WEP)
+ *
+ *  @param dev                  A pointer to net_device structure
+ *  @param info                        A pointer to iw_request_info structure
+ *  @param vwrq                A pointer to iw_param structure
+ *  @param extra               A pointer to extra data buf
+ *  @return                    0 on success, otherwise failure
+ */
+static int wlan_get_encodeext(struct net_device *dev,
+                             struct iw_request_info *info,
+                             struct iw_point *dwrq,
+                             char *extra)
+{
+       int ret = -EINVAL;
+       wlan_private *priv = dev->priv;
+       wlan_adapter *adapter = priv->adapter;
+       struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
+       int index, max_key_len;
+
+       ENTER();
+
+       max_key_len = dwrq->length - sizeof(*ext);
+       if (max_key_len < 0)
+               goto out;
+
+       index = dwrq->flags & IW_ENCODE_INDEX;
+       if (index) {
+               if (index < 1 || index > 4)
+                       goto out;
+               index--;
+       } else {
+               index = adapter->wep_tx_keyidx;
+       }
+
+       if (!ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY &&
+           ext->alg != IW_ENCODE_ALG_WEP) {
+               if (index != 0 || adapter->inframode != wlan802_11infrastructure)
+                       goto out;
+       }
+
+       dwrq->flags = index + 1;
+       memset(ext, 0, sizeof(*ext));
+
+       if ((adapter->secinfo.WEPstatus == wlan802_11WEPdisabled)
+           && !adapter->secinfo.WPAenabled && !adapter->secinfo.WPA2enabled) {
+               ext->alg = IW_ENCODE_ALG_NONE;
+               ext->key_len = 0;
+               dwrq->flags |= IW_ENCODE_DISABLED;
+       } else {
+               u8 *key = NULL;
+
+               if ((adapter->secinfo.WEPstatus == wlan802_11WEPenabled)
+                   && !adapter->secinfo.WPAenabled
+                   && !adapter->secinfo.WPA2enabled) {
+                       ext->alg = IW_ENCODE_ALG_WEP;
+                       ext->key_len = adapter->wep_keys[index].len;
+                       key = &adapter->wep_keys[index].key[0];
+               } else if ((adapter->secinfo.WEPstatus == wlan802_11WEPdisabled) &&
+                          (adapter->secinfo.WPAenabled ||
+                           adapter->secinfo.WPA2enabled)) {
+                       /* WPA */
+                       ext->alg = IW_ENCODE_ALG_TKIP;
+                       ext->key_len = 0;
+               } else {
+                       goto out;
+               }
+
+               if (ext->key_len > max_key_len) {
+                       ret = -E2BIG;
+                       goto out;
+               }
+
+               if (ext->key_len)
+                       memcpy(ext->key, key, ext->key_len);
+               else
+                       dwrq->flags |= IW_ENCODE_NOKEY;
+               dwrq->flags |= IW_ENCODE_ENABLED;
+       }
+       ret = 0;
+
+out:
+       LEAVE();
+       return ret;
+}
+
+/**
+ *  @brief Set Encryption key Extended (WPA/802.1x and WEP)
+ *
+ *  @param dev                  A pointer to net_device structure
+ *  @param info                        A pointer to iw_request_info structure
+ *  @param vwrq                A pointer to iw_param structure
+ *  @param extra               A pointer to extra data buf
+ *  @return                    0 --success, otherwise fail
+ */
+static int wlan_set_encodeext(struct net_device *dev,
+                             struct iw_request_info *info,
+                             struct iw_point *dwrq,
+                             char *extra)
+{
+       int ret = 0;
+       wlan_private *priv = dev->priv;
+       wlan_adapter *adapter = priv->adapter;
+       struct iw_encode_ext *ext = (struct iw_encode_ext *)extra;
+       int alg = ext->alg;
+       struct assoc_request * assoc_req;
+
+       ENTER();
+
+       mutex_lock(&adapter->lock);
+       assoc_req = wlan_get_association_request(adapter);
+       if (!assoc_req) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       if ((alg == IW_ENCODE_ALG_NONE) || (dwrq->flags & IW_ENCODE_DISABLED)) {
+               disable_wep (assoc_req);
+       } else if (alg == IW_ENCODE_ALG_WEP) {
+               u16 is_default = 0, index, set_tx_key = 0;
+
+               ret = validate_key_index(assoc_req->wep_tx_keyidx,
+                                        (dwrq->flags & IW_ENCODE_INDEX),
+                                        &index, &is_default);
+               if (ret)
+                       goto out;
+
+               /* If WEP isn't enabled, or if there is no key data but a valid
+                * index, or if the set-TX-key flag was passed, set the TX key.
+                */
+               if ((assoc_req->secinfo.WEPstatus != wlan802_11WEPenabled)
+                   || (dwrq->length == 0 && !is_default)
+                   || (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY))
+                       set_tx_key = 1;
+
+               /* Copy key to driver */
+               ret = wlan_set_wep_key (assoc_req, ext->key, ext->key_len, index,
+                                       set_tx_key);
+               if (ret)
+                       goto out;
+
+               if (dwrq->flags & IW_ENCODE_RESTRICTED) {
+                       assoc_req->secinfo.authmode =
+                           wlan802_11authmodeshared;
+               } else if (dwrq->flags & IW_ENCODE_OPEN) {
+                       assoc_req->secinfo.authmode =
+                           wlan802_11authmodeopen;
+               }
+
+               /* Mark the various WEP bits as modified */
+               set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
+               if (dwrq->length)
+                       set_bit(ASSOC_FLAG_WEP_KEYS, &assoc_req->flags);
+               if (set_tx_key)
+                       set_bit(ASSOC_FLAG_WEP_TX_KEYIDX, &assoc_req->flags);
+
+       } else if ((alg == IW_ENCODE_ALG_TKIP) || (alg == IW_ENCODE_ALG_CCMP)) {
+               struct WLAN_802_11_KEY * pkey;
+
+               /* validate key length */
+               if (((alg == IW_ENCODE_ALG_TKIP)
+                       && (ext->key_len != KEY_LEN_WPA_TKIP))
+                   || ((alg == IW_ENCODE_ALG_CCMP)
+                       && (ext->key_len != KEY_LEN_WPA_AES))) {
+                               lbs_pr_debug(1, "Invalid size %d for key of alg"
+                                      "type %d.\n",
+                                      ext->key_len,
+                                      alg);
+                               ret = -EINVAL;
+                               goto out;
+               }
+
+               if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY)
+                       pkey = &assoc_req->wpa_mcast_key;
+               else
+                       pkey = &assoc_req->wpa_unicast_key;
+
+               memset(pkey, 0, sizeof (struct WLAN_802_11_KEY));
+               memcpy(pkey->key, ext->key, ext->key_len);
+               pkey->len = ext->key_len;
+               pkey->flags = KEY_INFO_WPA_ENABLED;
+
+               if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) {
+                       pkey->flags |= KEY_INFO_WPA_MCAST;
+                       set_bit(ASSOC_FLAG_WPA_MCAST_KEY, &assoc_req->flags);
+               } else {
+                       pkey->flags |= KEY_INFO_WPA_UNICAST;
+                       set_bit(ASSOC_FLAG_WPA_UCAST_KEY, &assoc_req->flags);
+               }
+
+               if (alg == IW_ENCODE_ALG_TKIP)
+                       pkey->type = KEY_TYPE_ID_TKIP;
+               else if (alg == IW_ENCODE_ALG_CCMP)
+                       pkey->type = KEY_TYPE_ID_AES;
+
+               /* If WPA isn't enabled yet, do that now */
+               if (   assoc_req->secinfo.WPAenabled == 0
+                   && assoc_req->secinfo.WPA2enabled == 0) {
+                       assoc_req->secinfo.WPAenabled = 1;
+                       assoc_req->secinfo.WPA2enabled = 1;
+                       set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
+               }
+
+               disable_wep (assoc_req);
+       }
+
+out:
+       if (ret == 0) {
+               wlan_postpone_association_work(priv);
+       } else {
+               wlan_cancel_association_work(priv);
+       }
+       mutex_unlock(&adapter->lock);
+
+       LEAVE();
+       return ret;
+}
+
+
+static int wlan_set_genie(struct net_device *dev,
+                         struct iw_request_info *info,
+                         struct iw_point *dwrq,
+                         char *extra)
+{
+       wlan_private *priv = dev->priv;
+       wlan_adapter *adapter = priv->adapter;
+       int ret = 0;
+       struct assoc_request * assoc_req;
+
+       ENTER();
+
+       mutex_lock(&adapter->lock);
+       assoc_req = wlan_get_association_request(adapter);
+       if (!assoc_req) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       if (dwrq->length > MAX_WPA_IE_LEN ||
+           (dwrq->length && extra == NULL)) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       if (dwrq->length) {
+               memcpy(&assoc_req->wpa_ie[0], extra, dwrq->length);
+               assoc_req->wpa_ie_len = dwrq->length;
+       } else {
+               memset(&assoc_req->wpa_ie[0], 0, sizeof(adapter->wpa_ie));
+               assoc_req->wpa_ie_len = 0;
+       }
+
+out:
+       if (ret == 0) {
+               set_bit(ASSOC_FLAG_WPA_IE, &assoc_req->flags);
+               wlan_postpone_association_work(priv);
+       } else {
+               wlan_cancel_association_work(priv);
+       }
+       mutex_unlock(&adapter->lock);
+
+       LEAVE();
+       return ret;
+}
+
+static int wlan_get_genie(struct net_device *dev,
+                         struct iw_request_info *info,
+                         struct iw_point *dwrq,
+                         char *extra)
+{
+       wlan_private *priv = dev->priv;
+       wlan_adapter *adapter = priv->adapter;
+
+       ENTER();
+
+       if (adapter->wpa_ie_len == 0) {
+               dwrq->length = 0;
+               LEAVE();
+               return 0;
+       }
+
+       if (dwrq->length < adapter->wpa_ie_len) {
+               LEAVE();
+               return -E2BIG;
+       }
+
+       dwrq->length = adapter->wpa_ie_len;
+       memcpy(extra, &adapter->wpa_ie[0], adapter->wpa_ie_len);
+
+       LEAVE();
+       return 0;
+}
+
+
+static int wlan_set_auth(struct net_device *dev,
+                        struct iw_request_info *info,
+                        struct iw_param *dwrq,
+                        char *extra)
+{
+       wlan_private *priv = dev->priv;
+       wlan_adapter *adapter = priv->adapter;
+       struct assoc_request * assoc_req;
+       int ret = 0;
+       int updated = 0;
+
+       ENTER();
+
+       mutex_lock(&adapter->lock);
+       assoc_req = wlan_get_association_request(adapter);
+       if (!assoc_req) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       switch (dwrq->flags & IW_AUTH_INDEX) {
+       case IW_AUTH_TKIP_COUNTERMEASURES:
+       case IW_AUTH_CIPHER_PAIRWISE:
+       case IW_AUTH_CIPHER_GROUP:
+       case IW_AUTH_KEY_MGMT:
+               /*
+                * libertas does not use these parameters
+                */
+               break;
+
+       case IW_AUTH_WPA_VERSION:
+               if (dwrq->value & IW_AUTH_WPA_VERSION_DISABLED) {
+                       assoc_req->secinfo.WPAenabled = 0;
+                       assoc_req->secinfo.WPA2enabled = 0;
+               }
+               if (dwrq->value & IW_AUTH_WPA_VERSION_WPA) {
+                       assoc_req->secinfo.WPAenabled = 1;
+                       assoc_req->secinfo.WEPstatus = wlan802_11WEPdisabled;
+                       assoc_req->secinfo.authmode =
+                           wlan802_11authmodeopen;
+               }
+               if (dwrq->value & IW_AUTH_WPA_VERSION_WPA2) {
+                       assoc_req->secinfo.WPA2enabled = 1;
+                       assoc_req->secinfo.WEPstatus = wlan802_11WEPdisabled;
+                       assoc_req->secinfo.authmode =
+                           wlan802_11authmodeopen;
+               }
+               updated = 1;
+               break;
+
+       case IW_AUTH_DROP_UNENCRYPTED:
+               if (dwrq->value) {
+                       adapter->currentpacketfilter |=
+                           cmd_act_mac_strict_protection_enable;
+               } else {
+                       adapter->currentpacketfilter &=
+                           ~cmd_act_mac_strict_protection_enable;
+               }
+               updated = 1;
+               break;
+
+       case IW_AUTH_80211_AUTH_ALG:
+               if (dwrq->value & IW_AUTH_ALG_SHARED_KEY) {
+                       assoc_req->secinfo.authmode =
+                           wlan802_11authmodeshared;
+               } else if (dwrq->value & IW_AUTH_ALG_OPEN_SYSTEM) {
+                       assoc_req->secinfo.authmode =
+                           wlan802_11authmodeopen;
+               } else if (dwrq->value & IW_AUTH_ALG_LEAP) {
+                       assoc_req->secinfo.authmode =
+                           wlan802_11authmodenetworkEAP;
+               } else {
+                       ret = -EINVAL;
+               }
+               updated = 1;
+               break;
+
+       case IW_AUTH_WPA_ENABLED:
+               if (dwrq->value) {
+                       if (!assoc_req->secinfo.WPAenabled &&
+                           !assoc_req->secinfo.WPA2enabled) {
+                               assoc_req->secinfo.WPAenabled = 1;
+                               assoc_req->secinfo.WPA2enabled = 1;
+                               assoc_req->secinfo.WEPstatus = wlan802_11WEPdisabled;
+                               assoc_req->secinfo.authmode =
+                                   wlan802_11authmodeopen;
+                       }
+               } else {
+                       assoc_req->secinfo.WPAenabled = 0;
+                       assoc_req->secinfo.WPA2enabled = 0;
+               }
+               updated = 1;
+               break;
+
+       default:
+               ret = -EOPNOTSUPP;
+               break;
+       }
+
+out:
+       if (ret == 0) {
+               if (updated)
+                       set_bit(ASSOC_FLAG_SECINFO, &assoc_req->flags);
+               wlan_postpone_association_work(priv);
+       } else if (ret != -EOPNOTSUPP) {
+               wlan_cancel_association_work(priv);
+       }
+       mutex_unlock(&adapter->lock);
+
+       LEAVE();
+       return ret;
+}
+
+static int wlan_get_auth(struct net_device *dev,
+                        struct iw_request_info *info,
+                        struct iw_param *dwrq,
+                        char *extra)
+{
+       wlan_private *priv = dev->priv;
+       wlan_adapter *adapter = priv->adapter;
+
+       ENTER();
+
+       switch (dwrq->flags & IW_AUTH_INDEX) {
+       case IW_AUTH_WPA_VERSION:
+               dwrq->value = 0;
+               if (adapter->secinfo.WPAenabled)
+                       dwrq->value |= IW_AUTH_WPA_VERSION_WPA;
+               if (adapter->secinfo.WPA2enabled)
+                       dwrq->value |= IW_AUTH_WPA_VERSION_WPA2;
+               if (!dwrq->value)
+                       dwrq->value |= IW_AUTH_WPA_VERSION_DISABLED;
+               break;
+
+       case IW_AUTH_DROP_UNENCRYPTED:
+               dwrq->value = 0;
+               if (adapter->currentpacketfilter &
+                   cmd_act_mac_strict_protection_enable)
+                       dwrq->value = 1;
+               break;
+
+       case IW_AUTH_80211_AUTH_ALG:
+               switch (adapter->secinfo.authmode) {
+               case wlan802_11authmodeshared:
+                       dwrq->value = IW_AUTH_ALG_SHARED_KEY;
+                       break;
+               case wlan802_11authmodeopen:
+                       dwrq->value = IW_AUTH_ALG_OPEN_SYSTEM;
+                       break;
+               case wlan802_11authmodenetworkEAP:
+                       dwrq->value = IW_AUTH_ALG_LEAP;
+                       break;
+               default:
+                       break;
+               }
+               break;
+
+       case IW_AUTH_WPA_ENABLED:
+               if (adapter->secinfo.WPAenabled && adapter->secinfo.WPA2enabled)
+                       dwrq->value = 1;
+               break;
+
+       default:
+               LEAVE();
+               return -EOPNOTSUPP;
+       }
+
+       LEAVE();
+       return 0;
+}
+
+
+static int wlan_set_txpow(struct net_device *dev, struct iw_request_info *info,
+                  struct iw_param *vwrq, char *extra)
+{
+       int ret = 0;
+       wlan_private *priv = dev->priv;
+       wlan_adapter *adapter = priv->adapter;
+
+       u16 dbm;
+
+       ENTER();
+
+       if (vwrq->disabled) {
+               wlan_radio_ioctl(priv, RADIO_OFF);
+               return 0;
+       }
+
+       adapter->preamble = cmd_type_auto_preamble;
+
+       wlan_radio_ioctl(priv, RADIO_ON);
+
+       if ((vwrq->flags & IW_TXPOW_TYPE) == IW_TXPOW_MWATT) {
+               dbm = (u16) mw_to_dbm(vwrq->value);
+       } else
+               dbm = (u16) vwrq->value;
+
+       /* auto tx power control */
+
+       if (vwrq->fixed == 0)
+               dbm = 0xffff;
+
+       lbs_pr_debug(1, "<1>TXPOWER SET %d dbm.\n", dbm);
+
+       ret = libertas_prepare_and_send_command(priv,
+                                   cmd_802_11_rf_tx_power,
+                                   cmd_act_tx_power_opt_set_low,
+                                   cmd_option_waitforrsp, 0, (void *)&dbm);
+
+       LEAVE();
+       return ret;
+}
+
+static int wlan_get_essid(struct net_device *dev, struct iw_request_info *info,
+                  struct iw_point *dwrq, char *extra)
+{
+       wlan_private *priv = dev->priv;
+       wlan_adapter *adapter = priv->adapter;
+
+       ENTER();
+       /*
+        * Note : if dwrq->flags != 0, we should get the relevant SSID from
+        * the SSID list...
+        */
+
+       /*
+        * Get the current SSID
+        */
+       if (adapter->connect_status == libertas_connected) {
+               memcpy(extra, adapter->curbssparams.ssid.ssid,
+                      adapter->curbssparams.ssid.ssidlength);
+               extra[adapter->curbssparams.ssid.ssidlength] = '\0';
+       } else {
+               memset(extra, 0, 32);
+               extra[adapter->curbssparams.ssid.ssidlength] = '\0';
+       }
+       /*
+        * If none, we may want to get the one that was set
+        */
+
+       /* To make the driver backward compatible with WPA supplicant v0.2.4 */
+       if (dwrq->length == 32) /* check with WPA supplicant buffer size */
+               dwrq->length = min_t(size_t, adapter->curbssparams.ssid.ssidlength,
+                                  IW_ESSID_MAX_SIZE);
+       else
+               dwrq->length = adapter->curbssparams.ssid.ssidlength + 1;
+
+       dwrq->flags = 1;        /* active */
+
+       LEAVE();
+       return 0;
+}
+
+static int wlan_set_essid(struct net_device *dev, struct iw_request_info *info,
+                  struct iw_point *dwrq, char *extra)
+{
+       wlan_private *priv = dev->priv;
+       wlan_adapter *adapter = priv->adapter;
+       int ret = 0;
+       struct WLAN_802_11_SSID ssid;
+       struct assoc_request * assoc_req;
+       int ssid_len = dwrq->length;
+
+       ENTER();
+
+       /*
+        * WE-20 and earlier NULL pad the end of the SSID and increment
+        * SSID length so it can be used like a string.  WE-21 and later don't,
+        * but some userspace tools aren't able to cope with the change.
+        */
+       if ((ssid_len > 0) && (extra[ssid_len - 1] == '\0'))
+               ssid_len--;
+
+       /* Check the size of the string */
+       if (ssid_len > IW_ESSID_MAX_SIZE) {
+               ret = -E2BIG;
+               goto out;
+       }
+
+       memset(&ssid, 0, sizeof(struct WLAN_802_11_SSID));
+
+       if (!dwrq->flags || !ssid_len) {
+               /* "any" SSID requested; leave SSID blank */
+       } else {
+               /* Specific SSID requested */
+               memcpy(&ssid.ssid, extra, ssid_len);
+               ssid.ssidlength = ssid_len;
+       }
+
+       lbs_pr_debug(1, "Requested new SSID = %s\n",
+              (ssid.ssidlength > 0) ? (char *)ssid.ssid : "any");
+
+out:
+       mutex_lock(&adapter->lock);
+       if (ret == 0) {
+               /* Get or create the current association request */
+               assoc_req = wlan_get_association_request(adapter);
+               if (!assoc_req) {
+                       ret = -ENOMEM;
+               } else {
+                       /* Copy the SSID to the association request */
+                       memcpy(&assoc_req->ssid, &ssid, sizeof(struct WLAN_802_11_SSID));
+                       set_bit(ASSOC_FLAG_SSID, &assoc_req->flags);
+                       wlan_postpone_association_work(priv);
+               }
+       }
+
+       /* Cancel the association request if there was an error */
+       if (ret != 0) {
+               wlan_cancel_association_work(priv);
+       }
+
+       mutex_unlock(&adapter->lock);
+
+       LEAVE();
+       return ret;
+}
+
+/**
+ *  @brief Connect to the AP or Ad-hoc Network with specific bssid
+ *
+ *  @param dev          A pointer to net_device structure
+ *  @param info         A pointer to iw_request_info structure
+ *  @param awrq         A pointer to iw_param structure
+ *  @param extra        A pointer to extra data buf
+ *  @return             0 --success, otherwise fail
+ */
+static int wlan_set_wap(struct net_device *dev, struct iw_request_info *info,
+                struct sockaddr *awrq, char *extra)
+{
+       wlan_private *priv = dev->priv;
+       wlan_adapter *adapter = priv->adapter;
+       struct assoc_request * assoc_req;
+       int ret = 0;
+
+       ENTER();
+
+       if (awrq->sa_family != ARPHRD_ETHER)
+               return -EINVAL;
+
+       lbs_pr_debug(1, "ASSOC: WAP: sa_data: " MAC_FMT "\n", MAC_ARG(awrq->sa_data));
+
+       mutex_lock(&adapter->lock);
+
+       /* Get or create the current association request */
+       assoc_req = wlan_get_association_request(adapter);
+       if (!assoc_req) {
+               wlan_cancel_association_work(priv);
+               ret = -ENOMEM;
+       } else {
+               /* Copy the BSSID to the association request */
+               memcpy(&assoc_req->bssid, awrq->sa_data, ETH_ALEN);
+               set_bit(ASSOC_FLAG_BSSID, &assoc_req->flags);
+               wlan_postpone_association_work(priv);
+       }
+
+       mutex_unlock(&adapter->lock);
+
+       return ret;
+}
+
+void libertas_get_fwversion(wlan_adapter * adapter, char *fwversion, int maxlen)
+{
+       union {
+               u32 l;
+               u8 c[4];
+       } ver;
+       char fwver[32];
+
+       mutex_lock(&adapter->lock);
+       ver.l = adapter->fwreleasenumber;
+       mutex_unlock(&adapter->lock);
+
+       if (ver.c[3] == 0)
+               sprintf(fwver, "%u.%u.%u", ver.c[2], ver.c[1], ver.c[0]);
+       else
+               sprintf(fwver, "%u.%u.%u.p%u",
+                       ver.c[2], ver.c[1], ver.c[0], ver.c[3]);
+
+       snprintf(fwversion, maxlen, fwver);
+}
+
+
+/*
+ * iwconfig settable callbacks
+ */
+static const iw_handler wlan_handler[] = {
+       (iw_handler) NULL,      /* SIOCSIWCOMMIT */
+       (iw_handler) wlan_get_name,     /* SIOCGIWNAME */
+       (iw_handler) NULL,      /* SIOCSIWNWID */
+       (iw_handler) NULL,      /* SIOCGIWNWID */
+       (iw_handler) wlan_set_freq,     /* SIOCSIWFREQ */
+       (iw_handler) wlan_get_freq,     /* SIOCGIWFREQ */
+       (iw_handler) wlan_set_mode,     /* SIOCSIWMODE */
+       (iw_handler) wlan_get_mode,     /* SIOCGIWMODE */
+       (iw_handler) NULL,      /* SIOCSIWSENS */
+       (iw_handler) NULL,      /* SIOCGIWSENS */
+       (iw_handler) NULL,      /* SIOCSIWRANGE */
+       (iw_handler) wlan_get_range,    /* SIOCGIWRANGE */
+       (iw_handler) NULL,      /* SIOCSIWPRIV */
+       (iw_handler) NULL,      /* SIOCGIWPRIV */
+       (iw_handler) NULL,      /* SIOCSIWSTATS */
+       (iw_handler) NULL,      /* SIOCGIWSTATS */
+       iw_handler_set_spy,     /* SIOCSIWSPY */
+       iw_handler_get_spy,     /* SIOCGIWSPY */
+       iw_handler_set_thrspy,  /* SIOCSIWTHRSPY */
+       iw_handler_get_thrspy,  /* SIOCGIWTHRSPY */
+       (iw_handler) wlan_set_wap,      /* SIOCSIWAP */
+       (iw_handler) wlan_get_wap,      /* SIOCGIWAP */
+       (iw_handler) NULL,      /* SIOCSIWMLME */
+       (iw_handler) NULL,      /* SIOCGIWAPLIST - deprecated */
+       (iw_handler) libertas_set_scan, /* SIOCSIWSCAN */
+       (iw_handler) libertas_get_scan, /* SIOCGIWSCAN */
+       (iw_handler) wlan_set_essid,    /* SIOCSIWESSID */
+       (iw_handler) wlan_get_essid,    /* SIOCGIWESSID */
+       (iw_handler) wlan_set_nick,     /* SIOCSIWNICKN */
+       (iw_handler) wlan_get_nick,     /* SIOCGIWNICKN */
+       (iw_handler) NULL,      /* -- hole -- */
+       (iw_handler) NULL,      /* -- hole -- */
+       (iw_handler) wlan_set_rate,     /* SIOCSIWRATE */
+       (iw_handler) wlan_get_rate,     /* SIOCGIWRATE */
+       (iw_handler) wlan_set_rts,      /* SIOCSIWRTS */
+       (iw_handler) wlan_get_rts,      /* SIOCGIWRTS */
+       (iw_handler) wlan_set_frag,     /* SIOCSIWFRAG */
+       (iw_handler) wlan_get_frag,     /* SIOCGIWFRAG */
+       (iw_handler) wlan_set_txpow,    /* SIOCSIWTXPOW */
+       (iw_handler) wlan_get_txpow,    /* SIOCGIWTXPOW */
+       (iw_handler) wlan_set_retry,    /* SIOCSIWRETRY */
+       (iw_handler) wlan_get_retry,    /* SIOCGIWRETRY */
+       (iw_handler) wlan_set_encode,   /* SIOCSIWENCODE */
+       (iw_handler) wlan_get_encode,   /* SIOCGIWENCODE */
+       (iw_handler) wlan_set_power,    /* SIOCSIWPOWER */
+       (iw_handler) wlan_get_power,    /* SIOCGIWPOWER */
+       (iw_handler) NULL,      /* -- hole -- */
+       (iw_handler) NULL,      /* -- hole -- */
+       (iw_handler) wlan_set_genie,    /* SIOCSIWGENIE */
+       (iw_handler) wlan_get_genie,    /* SIOCGIWGENIE */
+       (iw_handler) wlan_set_auth,     /* SIOCSIWAUTH */
+       (iw_handler) wlan_get_auth,     /* SIOCGIWAUTH */
+       (iw_handler) wlan_set_encodeext,/* SIOCSIWENCODEEXT */
+       (iw_handler) wlan_get_encodeext,/* SIOCGIWENCODEEXT */
+       (iw_handler) NULL,              /* SIOCSIWPMKSA */
+};
+
+struct iw_handler_def libertas_handler_def = {
+       .num_standard   = sizeof(wlan_handler) / sizeof(iw_handler),
+       .num_private    = sizeof(wlan_private_handler) / sizeof(iw_handler),
+       .num_private_args = sizeof(wlan_private_args) /
+               sizeof(struct iw_priv_args),
+       .standard       = (iw_handler *) wlan_handler,
+       .private        = (iw_handler *) wlan_private_handler,
+       .private_args   = (struct iw_priv_args *)wlan_private_args,
+       .get_wireless_stats = wlan_get_wireless_stats,
+};
diff --git a/drivers/net/wireless/libertas/wext.h b/drivers/net/wireless/libertas/wext.h
new file mode 100644 (file)
index 0000000..39f367c
--- /dev/null
@@ -0,0 +1,147 @@
+/**
+  * This file contains definition for IOCTL call.
+  */
+#ifndef        _WLAN_WEXT_H_
+#define        _WLAN_WEXT_H_
+
+#define SUBCMD_OFFSET                  4
+#define SUBCMD_DATA(x)                 *((int *)(x->u.name + SUBCMD_OFFSET))
+
+/** PRIVATE CMD ID */
+#define        WLANIOCTL                       SIOCIWFIRSTPRIV
+
+#define WLANSETWPAIE                   (WLANIOCTL + 0)
+
+#define WLAN_SETINT_GETINT             (WLANIOCTL + 7)
+#define WLANNF                                 1
+#define WLANRSSI                               2
+#define WLANENABLE11D                          5
+#define WLANADHOCGRATE                         6
+#define WLAN_SUBCMD_SET_PRESCAN                        11
+
+#define WLAN_SETNONE_GETNONE           (WLANIOCTL + 8)
+#define WLANDEAUTH                             1
+#define WLANRADIOON                            2
+#define WLANRADIOOFF                           3
+#define WLANREMOVEADHOCAES                     4
+#define WLANADHOCSTOP                          5
+#define WLANCIPHERTEST                         6
+#define WLANCRYPTOTEST                         7
+
+#define WLANWLANIDLEON                         10
+#define WLANWLANIDLEOFF                                11
+#define WLAN_SUBCMD_BT_RESET                   13
+#define WLAN_SUBCMD_FWT_RESET                  14
+
+#define WLANGETLOG                     (WLANIOCTL + 9)
+#define GETLOG_BUFSIZE  300
+
+#define WLANSCAN_TYPE                  (WLANIOCTL + 11)
+
+#define WLAN_SETNONE_GETONEINT         (WLANIOCTL + 15)
+#define WLANGETREGION                          1
+#define WLAN_GET_LISTEN_INTERVAL               2
+#define WLAN_GET_MULTIPLE_DTIM                 3
+#define WLAN_GET_TX_RATE                       4
+#define        WLANGETBCNAVG                           5
+
+#define WLAN_GET_LINKMODE                      6
+#define WLAN_GET_RADIOMODE                     7
+#define WLAN_GET_DEBUGMODE                     8
+#define WLAN_SUBCMD_FWT_CLEANUP                        15
+#define WLAN_SUBCMD_FWT_TIME                   16
+#define WLAN_SUBCMD_MESH_GET_TTL               17
+
+#define WLANREGCFRDWR                  (WLANIOCTL + 18)
+
+#define WLAN_SETNONE_GETTWELVE_CHAR (WLANIOCTL + 19)
+#define WLAN_SUBCMD_GETRXANTENNA    1
+#define WLAN_SUBCMD_GETTXANTENNA    2
+#define WLAN_GET_TSF                3
+
+#define WLAN_SETNONE_GETWORDCHAR       (WLANIOCTL + 21)
+#define WLANGETADHOCAES                                1
+
+#define WLAN_SETONEINT_GETONEINT       (WLANIOCTL + 23)
+#define WLAN_BEACON_INTERVAL                   1
+#define        WLAN_LISTENINTRVL                       4
+
+#define WLAN_TXCONTROL                         6
+#define WLAN_NULLPKTINTERVAL                   7
+
+#define WLAN_SETONEINT_GETNONE         (WLANIOCTL + 24)
+#define WLAN_SUBCMD_SETRXANTENNA               1
+#define WLAN_SUBCMD_SETTXANTENNA               2
+#define WLANSETAUTHALG                         5
+#define WLANSET8021XAUTHALG                    6
+#define WLANSETENCRYPTIONMODE                  7
+#define WLANSETREGION                          8
+#define WLAN_SET_LISTEN_INTERVAL               9
+
+#define WLAN_SET_MULTIPLE_DTIM                 10
+#define WLAN_SET_ATIM_WINDOW                   11
+#define WLANSETBCNAVG                          13
+#define WLANSETDATAAVG                         14
+#define WLAN_SET_LINKMODE                      15
+#define WLAN_SET_RADIOMODE                     16
+#define WLAN_SET_DEBUGMODE                     17
+#define WLAN_SUBCMD_MESH_SET_TTL               18
+
+#define WLAN_SET128CHAR_GET128CHAR     (WLANIOCTL + 25)
+#define WLANSCAN_MODE                          6
+
+#define WLAN_GET_ADHOC_STATUS                  9
+
+#define WLAN_SUBCMD_BT_ADD                     18
+#define WLAN_SUBCMD_BT_DEL                     19
+#define WLAN_SUBCMD_BT_LIST                    20
+#define WLAN_SUBCMD_FWT_ADD                            21
+#define WLAN_SUBCMD_FWT_DEL            22
+#define WLAN_SUBCMD_FWT_LOOKUP         23
+#define WLAN_SUBCMD_FWT_LIST_NEIGHBOR                  24
+#define WLAN_SUBCMD_FWT_LIST                   25
+#define WLAN_SUBCMD_FWT_LIST_ROUTE                     26
+
+#define WLAN_SET_GET_SIXTEEN_INT       (WLANIOCTL + 29)
+#define WLAN_TPCCFG                             1
+#define WLAN_POWERCFG                           2
+
+#define WLAN_AUTO_FREQ_SET                     3
+#define WLAN_AUTO_FREQ_GET                     4
+#define WLAN_LED_GPIO_CTRL                     5
+#define WLAN_SCANPROBES                        6
+#define        WLAN_ADAPT_RATESET                      8
+#define        WLAN_INACTIVITY_TIMEOUT                 9
+#define WLANSNR                                        10
+#define WLAN_GET_RATE                          11
+#define        WLAN_GET_RXINFO                         12
+
+#define WLANCMD52RDWR                  (WLANIOCTL + 30)
+#define WLANCMD53RDWR                  (WLANIOCTL + 31)
+#define CMD53BUFLEN                            32
+
+#define        REG_MAC                                 0x19
+#define        REG_BBP                                 0x1a
+#define        REG_RF                                  0x1b
+#define        REG_EEPROM                              0x59
+#define WLAN_LINKMODE_802_3                    0
+#define WLAN_LINKMODE_802_11                   2
+#define WLAN_RADIOMODE_NONE                            0
+#define WLAN_RADIOMODE_RADIOTAP                        2
+
+/** wlan_ioctl_regrdwr */
+struct wlan_ioctl_regrdwr {
+       /** Which register to access */
+       u16 whichreg;
+       /** Read or Write */
+       u16 action;
+       u32 offset;
+       u16 NOB;
+       u32 value;
+};
+
+extern struct iw_handler_def libertas_handler_def;
+int libertas_do_ioctl(struct net_device *dev, struct ifreq *req, int i);
+int wlan_radio_ioctl(wlan_private * priv, u8 option);
+
+#endif                         /* _WLAN_WEXT_H_ */
index 429b73892a5fc62f91e4a4b05da40859604fa791..c6e0d81ea785ee004e8851a0879f9b3ea22385ca 100644 (file)
@@ -168,6 +168,23 @@ struct ieee80211_radiotap_header {
  *      Unitless indication of the Rx/Tx antenna for this packet.
  *      The first antenna is antenna 0.
  *
+ * IEEE80211_RADIOTAP_RX_FLAGS          u_int16_t       bitmap
+ *
+ *     Properties of received frames. See flags defined below.
+ *
+ * IEEE80211_RADIOTAP_TX_FLAGS          u_int16_t       bitmap
+ *
+ *     Properties of transmitted frames. See flags defined below.
+ *
+ * IEEE80211_RADIOTAP_RTS_RETRIES       u_int8_t        data
+ *
+ *     Number of rts retries a transmitted frame used.
+ *
+ * IEEE80211_RADIOTAP_DATA_RETRIES      u_int8_t        data
+ *
+ *     Number of unicast retries a transmitted frame used.
+ *
+ *
  * IEEE80211_RADIOTAP_FCS              u32       data
  *
  *     FCS from frame in network byte order.
@@ -187,7 +204,11 @@ enum ieee80211_radiotap_type {
        IEEE80211_RADIOTAP_ANTENNA = 11,
        IEEE80211_RADIOTAP_DB_ANTSIGNAL = 12,
        IEEE80211_RADIOTAP_DB_ANTNOISE = 13,
-       IEEE80211_RADIOTAP_EXT = 31,
+       IEEE80211_RADIOTAP_RX_FLAGS = 14,
+       IEEE80211_RADIOTAP_TX_FLAGS = 15,
+       IEEE80211_RADIOTAP_RTS_RETRIES = 16,
+       IEEE80211_RADIOTAP_DATA_RETRIES = 17,
+       IEEE80211_RADIOTAP_EXT = 31
 };
 
 /* Channel flags. */
@@ -219,6 +240,14 @@ enum ieee80211_radiotap_type {
                                                 * 802.11 header and payload
                                                 * (to 32-bit boundary)
                                                 */
+/* For IEEE80211_RADIOTAP_RX_FLAGS */
+#define IEEE80211_RADIOTAP_F_RX_BADFCS 0x0001  /* frame failed crc check */
+
+/* For IEEE80211_RADIOTAP_TX_FLAGS */
+#define IEEE80211_RADIOTAP_F_TX_FAIL   0x0001  /* failed due to excessive
+                                                * retries */
+#define IEEE80211_RADIOTAP_F_TX_CTS    0x0002  /* used cts 'protection' */
+#define IEEE80211_RADIOTAP_F_TX_RTS    0x0004  /* used rts/cts handshake */
 
 /* Ugly macro to convert literal channel numbers into their mhz equivalents
  * There are certianly some conditions that will break this (like feeding it '30')