aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcelo Tosatti <marcelo@kvack.org>2007-02-10 12:25:27 -0200
committerJeff Garzik <jeff@garzik.org>2007-04-28 11:00:54 -0400
commit876c9d3aeb989cf1961f2c228d309ba5dcfb1172 (patch)
tree239e9db92d13abc799c1ffc5304d8ec1503dbc61
parent35c3404efa7407811b706453f83d39b2539dcbd0 (diff)
[PATCH] Marvell Libertas 8388 802.11b/g USB driver
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>
-rw-r--r--drivers/net/wireless/Kconfig13
-rw-r--r--drivers/net/wireless/Makefile1
-rw-r--r--drivers/net/wireless/libertas/11d.c754
-rw-r--r--drivers/net/wireless/libertas/11d.h105
-rw-r--r--drivers/net/wireless/libertas/LICENSE16
-rw-r--r--drivers/net/wireless/libertas/Makefile21
-rw-r--r--drivers/net/wireless/libertas/README1044
-rw-r--r--drivers/net/wireless/libertas/assoc.c588
-rw-r--r--drivers/net/wireless/libertas/assoc.h30
-rw-r--r--drivers/net/wireless/libertas/cmd.c1958
-rw-r--r--drivers/net/wireless/libertas/cmdresp.c1031
-rw-r--r--drivers/net/wireless/libertas/debugfs.c1968
-rw-r--r--drivers/net/wireless/libertas/debugfs.h6
-rw-r--r--drivers/net/wireless/libertas/decl.h83
-rw-r--r--drivers/net/wireless/libertas/defs.h369
-rw-r--r--drivers/net/wireless/libertas/dev.h403
-rw-r--r--drivers/net/wireless/libertas/ethtool.c184
-rw-r--r--drivers/net/wireless/libertas/fw.c361
-rw-r--r--drivers/net/wireless/libertas/fw.h13
-rw-r--r--drivers/net/wireless/libertas/host.h338
-rw-r--r--drivers/net/wireless/libertas/hostcmd.h693
-rw-r--r--drivers/net/wireless/libertas/if_bootcmd.c38
-rw-r--r--drivers/net/wireless/libertas/if_usb.c952
-rw-r--r--drivers/net/wireless/libertas/if_usb.h109
-rw-r--r--drivers/net/wireless/libertas/ioctl.c2500
-rw-r--r--drivers/net/wireless/libertas/join.c1055
-rw-r--r--drivers/net/wireless/libertas/join.h64
-rw-r--r--drivers/net/wireless/libertas/main.c1258
-rw-r--r--drivers/net/wireless/libertas/radiotap.h57
-rw-r--r--drivers/net/wireless/libertas/rx.c459
-rw-r--r--drivers/net/wireless/libertas/sbi.h40
-rw-r--r--drivers/net/wireless/libertas/scan.c2044
-rw-r--r--drivers/net/wireless/libertas/scan.h216
-rw-r--r--drivers/net/wireless/libertas/thread.h52
-rw-r--r--drivers/net/wireless/libertas/tx.c285
-rw-r--r--drivers/net/wireless/libertas/types.h289
-rw-r--r--drivers/net/wireless/libertas/version.h8
-rw-r--r--drivers/net/wireless/libertas/wext.c2769
-rw-r--r--drivers/net/wireless/libertas/wext.h147
-rw-r--r--include/net/ieee80211_radiotap.h31
40 files changed, 22351 insertions, 1 deletions
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index 4426841b2be..c4b3dc291a5 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -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)
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index c613af17a15..d2124602263 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -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
index 00000000000..e0ecc4d483b
--- /dev/null
+++ b/drivers/net/wireless/libertas/11d.c
@@ -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
index 00000000000..db2ebea9f23
--- /dev/null
+++ b/drivers/net/wireless/libertas/11d.h
@@ -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
index 00000000000..8862742213b
--- /dev/null
+++ b/drivers/net/wireless/libertas/LICENSE
@@ -0,0 +1,16 @@
+ Copyright (c) 2003-2006, Marvell International Ltd.
+ All Rights Reserved
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc., 59
+ Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
diff --git a/drivers/net/wireless/libertas/Makefile b/drivers/net/wireless/libertas/Makefile
new file mode 100644
index 00000000000..19c935071d8
--- /dev/null
+++ b/drivers/net/wireless/libertas/Makefile
@@ -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
index 00000000000..688da4c784b
--- /dev/null
+++ b/drivers/net/wireless/libertas/README
@@ -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
index 00000000000..b55c7f57aca
--- /dev/null
+++ b/drivers/net/wireless/libertas/assoc.c
@@ -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
index 00000000000..2ffd82d99b3
--- /dev/null
+++ b/drivers/net/wireless/libertas/assoc.h
@@ -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
index 00000000000..bfdac58b5c0
--- /dev/null
+++ b/drivers/net/wireless/libertas/cmd.c
@@ -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
index 00000000000..cdb012c7e9c
--- /dev/null
+++ b/drivers/net/wireless/libertas/cmdresp.c
@@ -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
index 00000000000..3ad1e0339ed
--- /dev/null
+++ b/drivers/net/wireless/libertas/debugfs.c
@@ -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
index 00000000000..880a11b95d2
--- /dev/null
+++ b/drivers/net/wireless/libertas/debugfs.h
@@ -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
index 00000000000..606bdd002be
--- /dev/null
+++ b/drivers/net/wireless/libertas/decl.h
@@ -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
index 00000000000..fb1478c1b87
--- /dev/null
+++ b/drivers/net/wireless/libertas/defs.h
@@ -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
index 00000000000..b1f876f9693
--- /dev/null
+++ b/drivers/net/wireless/libertas/dev.h
@@ -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
index 00000000000..0064de54296
--- /dev/null
+++ b/drivers/net/wireless/libertas/ethtool.c
@@ -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
index 00000000000..b194a457079
--- /dev/null
+++ b/drivers/net/wireless/libertas/fw.c
@@ -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
index 00000000000..1f9ae267a9e
--- /dev/null
+++ b/drivers/net/wireless/libertas/fw.h
@@ -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
index 00000000000..c0faaecaf5b
--- /dev/null
+++ b/drivers/net/wireless/libertas/host.h
@@ -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
index 00000000000..f239e5d2435
--- /dev/null
+++ b/drivers/net/wireless/libertas/hostcmd.h
@@ -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
index 00000000000..567000c3e87
--- /dev/null
+++ b/drivers/net/wireless/libertas/if_bootcmd.c
@@ -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
index 00000000000..695fb6a66ff
--- /dev/null
+++ b/drivers/net/wireless/libertas/if_usb.c
@@ -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
index 00000000000..785116720bc
--- /dev/null
+++ b/drivers/net/wireless/libertas/if_usb.h
@@ -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
index 00000000000..82b39642423
--- /dev/null
+++ b/drivers/net/wireless/libertas/ioctl.c
@@ -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
index 00000000000..11682cbe752
--- /dev/null
+++ b/drivers/net/wireless/libertas/join.c
@@ -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
index 00000000000..8efa2455af9
--- /dev/null
+++ b/drivers/net/wireless/libertas/join.h
@@ -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
index 00000000000..49704653011
--- /dev/null
+++ b/drivers/net/wireless/libertas/main.c
@@ -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
index 00000000000..5d118f40cfb
--- /dev/null
+++ b/drivers/net/wireless/libertas/radiotap.h
@@ -0,0 +1,57 @@
+#include <net/ieee80211_radiotap.h>
+
+struct tx_radiotap_hdr {
+ struct ieee80211_radiotap_header hdr;
+ u8 rate;
+ u8 txpower;
+ u8 rts_retries;
+ u8 data_retries;
+#if 0
+ u8 pad[IEEE80211_RADIOTAP_HDRLEN - 12];
+#endif
+} __attribute__ ((packed));
+
+#define TX_RADIOTAP_PRESENT ( \
+ (1 << IEEE80211_RADIOTAP_RATE) | \
+ (1 << IEEE80211_RADIOTAP_DBM_TX_POWER) | \
+ (1 << IEEE80211_RADIOTAP_RTS_RETRIES) | \
+ (1 << IEEE80211_RADIOTAP_DATA_RETRIES) | \
+ 0)
+
+#define IEEE80211_FC_VERSION_MASK 0x0003
+#define IEEE80211_FC_TYPE_MASK 0x000c
+#define IEEE80211_FC_TYPE_MGT 0x0000
+#define IEEE80211_FC_TYPE_CTL 0x0004
+#define IEEE80211_FC_TYPE_DATA 0x0008
+#define IEEE80211_FC_SUBTYPE_MASK 0x00f0
+#define IEEE80211_FC_TOFROMDS_MASK 0x0300
+#define IEEE80211_FC_TODS_MASK 0x0100
+#define IEEE80211_FC_FROMDS_MASK 0x0200
+#define IEEE80211_FC_NODS 0x0000
+#define IEEE80211_FC_TODS 0x0100
+#define IEEE80211_FC_FROMDS 0x0200
+#define IEEE80211_FC_DSTODS 0x0300
+
+struct rx_radiotap_hdr {
+ struct ieee80211_radiotap_header hdr;
+ u8 flags;
+ u8 rate;
+ u16 chan_freq;
+ u16 chan_flags;
+ u8 antenna;
+ u8 antsignal;
+ u16 rx_flags;
+#if 0
+ u8 pad[IEEE80211_RADIOTAP_HDRLEN - 18];
+#endif
+} __attribute__ ((packed));
+
+#define RX_RADIOTAP_PRESENT ( \
+ (1 << IEEE80211_RADIOTAP_FLAGS) | \
+ (1 << IEEE80211_RADIOTAP_RATE) | \
+ (1 << IEEE80211_RADIOTAP_CHANNEL) | \
+ (1 << IEEE80211_RADIOTAP_ANTENNA) | \
+ (1 << IEEE80211_RADIOTAP_DB_ANTSIGNAL) |\
+ (1 << IEEE80211_RADIOTAP_RX_FLAGS) | \
+ 0)
+
diff --git a/drivers/net/wireless/libertas/rx.c b/drivers/net/wireless/libertas/rx.c
new file mode 100644
index 00000000000..7e3f78f092d
--- /dev/null
+++ b/drivers/net/wireless/libertas/rx.c
@@ -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
index 00000000000..59d3a59ccef
--- /dev/null
+++ b/drivers/net/wireless/libertas/sbi.h
@@ -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
index 00000000000..e1870623895
--- /dev/null
+++ b/drivers/net/wireless/libertas/scan.c
@@ -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
index 00000000000..d93aa7fa44f
--- /dev/null
+++ b/drivers/net/wireless/libertas/scan.h
@@ -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
index 00000000000..207b8a6cc33
--- /dev/null
+++ b/drivers/net/wireless/libertas/thread.h
@@ -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
index 00000000000..82d06223043
--- /dev/null
+++ b/drivers/net/wireless/libertas/tx.c
@@ -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
index 00000000000..09d62f8b1a1
--- /dev/null
+++ b/drivers/net/wireless/libertas/types.h
@@ -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
index 00000000000..e86f65ae79b
--- /dev/null
+++ b/drivers/net/wireless/libertas/version.h
@@ -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
index 00000000000..4a52336bc0f
--- /dev/null
+++ b/drivers/net/wireless/libertas/wext.c
@@ -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
index 00000000000..39f367c38d9
--- /dev/null
+++ b/drivers/net/wireless/libertas/wext.h
@@ -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_ */
diff --git a/include/net/ieee80211_radiotap.h b/include/net/ieee80211_radiotap.h
index 429b73892a5..c6e0d81ea78 100644
--- a/include/net/ieee80211_radiotap.h
+++ b/include/net/ieee80211_radiotap.h
@@ -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')