summaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/marvell/libertas
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/marvell/libertas')
-rw-r--r--drivers/net/wireless/marvell/libertas/Kconfig45
-rw-r--r--drivers/net/wireless/marvell/libertas/LICENSE16
-rw-r--r--drivers/net/wireless/marvell/libertas/Makefile21
-rw-r--r--drivers/net/wireless/marvell/libertas/README239
-rw-r--r--drivers/net/wireless/marvell/libertas/cfg.c2215
-rw-r--r--drivers/net/wireless/marvell/libertas/cfg.h21
-rw-r--r--drivers/net/wireless/marvell/libertas/cmd.c1725
-rw-r--r--drivers/net/wireless/marvell/libertas/cmd.h141
-rw-r--r--drivers/net/wireless/marvell/libertas/cmdresp.c353
-rw-r--r--drivers/net/wireless/marvell/libertas/debugfs.c989
-rw-r--r--drivers/net/wireless/marvell/libertas/debugfs.h10
-rw-r--r--drivers/net/wireless/marvell/libertas/decl.h82
-rw-r--r--drivers/net/wireless/marvell/libertas/defs.h394
-rw-r--r--drivers/net/wireless/marvell/libertas/dev.h211
-rw-r--r--drivers/net/wireless/marvell/libertas/ethtool.c120
-rw-r--r--drivers/net/wireless/marvell/libertas/firmware.c227
-rw-r--r--drivers/net/wireless/marvell/libertas/host.h978
-rw-r--r--drivers/net/wireless/marvell/libertas/if_cs.c1006
-rw-r--r--drivers/net/wireless/marvell/libertas/if_sdio.c1453
-rw-r--r--drivers/net/wireless/marvell/libertas/if_sdio.h52
-rw-r--r--drivers/net/wireless/marvell/libertas/if_spi.c1318
-rw-r--r--drivers/net/wireless/marvell/libertas/if_spi.h206
-rw-r--r--drivers/net/wireless/marvell/libertas/if_usb.c1018
-rw-r--r--drivers/net/wireless/marvell/libertas/if_usb.h106
-rw-r--r--drivers/net/wireless/marvell/libertas/main.c1225
-rw-r--r--drivers/net/wireless/marvell/libertas/mesh.c1187
-rw-r--r--drivers/net/wireless/marvell/libertas/mesh.h77
-rw-r--r--drivers/net/wireless/marvell/libertas/radiotap.h44
-rw-r--r--drivers/net/wireless/marvell/libertas/rx.c286
-rw-r--r--drivers/net/wireless/marvell/libertas/tx.c207
-rw-r--r--drivers/net/wireless/marvell/libertas/types.h268
31 files changed, 16240 insertions, 0 deletions
diff --git a/drivers/net/wireless/marvell/libertas/Kconfig b/drivers/net/wireless/marvell/libertas/Kconfig
new file mode 100644
index 000000000000..e6268ceacbf1
--- /dev/null
+++ b/drivers/net/wireless/marvell/libertas/Kconfig
@@ -0,0 +1,45 @@
+config LIBERTAS
+ tristate "Marvell 8xxx Libertas WLAN driver support"
+ depends on CFG80211
+ select WIRELESS_EXT
+ select WEXT_SPY
+ select LIB80211
+ select FW_LOADER
+ ---help---
+ A library for Marvell Libertas 8xxx devices.
+
+config LIBERTAS_USB
+ tristate "Marvell Libertas 8388 USB 802.11b/g cards"
+ depends on LIBERTAS && USB
+ ---help---
+ A driver for Marvell Libertas 8388 USB devices.
+
+config LIBERTAS_CS
+ tristate "Marvell Libertas 8385 CompactFlash 802.11b/g cards"
+ depends on LIBERTAS && PCMCIA && HAS_IOPORT_MAP
+ ---help---
+ A driver for Marvell Libertas 8385 CompactFlash devices.
+
+config LIBERTAS_SDIO
+ tristate "Marvell Libertas 8385/8686/8688 SDIO 802.11b/g cards"
+ depends on LIBERTAS && MMC
+ ---help---
+ A driver for Marvell Libertas 8385/8686/8688 SDIO devices.
+
+config LIBERTAS_SPI
+ tristate "Marvell Libertas 8686 SPI 802.11b/g cards"
+ depends on LIBERTAS && SPI
+ ---help---
+ A driver for Marvell Libertas 8686 SPI devices.
+
+config LIBERTAS_DEBUG
+ bool "Enable full debugging output in the Libertas module."
+ depends on LIBERTAS
+ ---help---
+ Debugging support.
+
+config LIBERTAS_MESH
+ bool "Enable mesh support"
+ depends on LIBERTAS
+ help
+ This enables Libertas' MESH support, used by e.g. the OLPC people.
diff --git a/drivers/net/wireless/marvell/libertas/LICENSE b/drivers/net/wireless/marvell/libertas/LICENSE
new file mode 100644
index 000000000000..8862742213b9
--- /dev/null
+++ b/drivers/net/wireless/marvell/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/marvell/libertas/Makefile b/drivers/net/wireless/marvell/libertas/Makefile
new file mode 100644
index 000000000000..eac72f7bd341
--- /dev/null
+++ b/drivers/net/wireless/marvell/libertas/Makefile
@@ -0,0 +1,21 @@
+libertas-y += cfg.o
+libertas-y += cmd.o
+libertas-y += cmdresp.o
+libertas-y += debugfs.o
+libertas-y += ethtool.o
+libertas-y += main.o
+libertas-y += rx.o
+libertas-y += tx.o
+libertas-y += firmware.o
+libertas-$(CONFIG_LIBERTAS_MESH) += mesh.o
+
+usb8xxx-objs += if_usb.o
+libertas_cs-objs += if_cs.o
+libertas_sdio-objs += if_sdio.o
+libertas_spi-objs += if_spi.o
+
+obj-$(CONFIG_LIBERTAS) += libertas.o
+obj-$(CONFIG_LIBERTAS_USB) += usb8xxx.o
+obj-$(CONFIG_LIBERTAS_CS) += libertas_cs.o
+obj-$(CONFIG_LIBERTAS_SDIO) += libertas_sdio.o
+obj-$(CONFIG_LIBERTAS_SPI) += libertas_spi.o
diff --git a/drivers/net/wireless/marvell/libertas/README b/drivers/net/wireless/marvell/libertas/README
new file mode 100644
index 000000000000..1a554a685e91
--- /dev/null
+++ b/drivers/net/wireless/marvell/libertas/README
@@ -0,0 +1,239 @@
+================================================================================
+ README for Libertas
+
+ (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 on the worldwide
+ web at http://www.gnu.org/licenses/gpl.txt.
+
+ THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
+ ARE EXPRESSLY DISCLAIMED. The License provides additional details about
+ this warranty disclaimer.
+================================================================================
+
+=====================
+DRIVER LOADING
+=====================
+
+ o. Copy the firmware image (e.g. usb8388.bin) to /lib/firmware/
+
+ o. Load driver by using the following command:
+
+ insmod usb8388.ko [fw_name=usb8388.bin]
+
+=========================
+ETHTOOL
+=========================
+
+
+Use the -i option to retrieve version information from the driver.
+
+# ethtool -i eth0
+driver: libertas
+version: COMM-USB8388-318.p4
+firmware-version: 5.110.7
+bus-info:
+
+Use the -e option to read the EEPROM contents of the card.
+
+ Usage:
+ ethtool -e ethX [raw on|off] [offset N] [length N]
+
+ -e retrieves and prints an EEPROM dump for the specified ethernet
+ device. When raw is enabled, then it dumps the raw EEPROM data
+ to stdout. The length and offset parameters allow dumping cer-
+ tain portions of the EEPROM. Default is to dump the entire EEP-
+ ROM.
+
+# ethtool -e eth0 offset 0 length 16
+Offset Values
+------ ------
+0x0000 38 33 30 58 00 00 34 f4 00 00 10 00 00 c4 17 00
+
+========================
+DEBUGFS COMMANDS
+========================
+
+those commands are used via debugfs interface
+
+===========
+rdmac
+rdbbp
+rdrf
+ These commands are used to read the MAC, BBP and RF registers from the
+ card. These commands take one parameter that specifies the offset
+ location that is to be read. This parameter must be specified in
+ hexadecimal (its possible to precede preceding the number with a "0x").
+
+ Path: /sys/kernel/debug/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 precede 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: /sys/kernel/debug/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: /sys/kernel/debug/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: /sys/kernel/debug/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: /sys/kernel/debug/libertas_wireless/ethX/
+
+ Usage:
+ cat getscantable
+
+setuserscan
+ Initiate a customized scan and retrieve the results
+
+
+ Path: /sys/kernel/debug/libertas_wireless/ethX/
+
+ Usage:
+ echo "[ARGS]" > setuserscan
+
+ where [ARGS]:
+
+ bssid=xx:xx:xx:xx:xx:xx specify a BSSID filter for the scan
+ ssid="[SSID]" specify a SSID filter for the scan
+ keep=[0 or 1] keep the previous scan results (1), discard (0)
+ dur=[scan time] time to scan for each channel in milliseconds
+ type=[1,2,3] BSS type: 1 (Infra), 2(Adhoc), 3(Any)
+
+ Any combination of the above arguments can be supplied on the command
+ line. If dur tokens are absent, the driver default setting will be used.
+ The bssid and ssid fields, if blank, will produce an unfiltered scan.
+ The type field will default to 3 (Any) and the keep field will default
+ to 0 (Discard).
+
+ Examples:
+ 1) Perform a passive scan on all channels for 20 ms per channel:
+ echo "dur=20" > setuserscan
+
+ 2) Perform an active scan for a specific SSID:
+ echo "ssid="TestAP"" > setuserscan
+
+ 3) Scan all available channels (B/G, A bands) for a specific BSSID, keep
+ the current scan table intact, update existing or append new scan data:
+ echo "bssid=00:50:43:20:12:82 keep=1" > setuserscan
+
+ 4) Scan for all infrastructure networks.
+ Keep the previous scan table intact. Update any duplicate BSSID/SSID
+ matches with the new scan data:
+ echo "type=1 keep=1" > setuserscan
+
+ All entries in the scan table (not just the new scan data when keep=1)
+ will be displayed upon completion by use of the getscantable ioctl.
+
+hostsleep
+ This command is used to enable/disable host sleep.
+ Note: Host sleep parameters should be configured using
+ "ethtool -s ethX wol X" command before enabling host sleep.
+
+ Path: /sys/kernel/debug/libertas_wireless/ethX/
+
+ Usage:
+ cat hostsleep: reads the current hostsleep state
+ echo "1" > hostsleep : enable host sleep.
+ echo "0" > hostsleep : disable host sleep
+
diff --git a/drivers/net/wireless/marvell/libertas/cfg.c b/drivers/net/wireless/marvell/libertas/cfg.c
new file mode 100644
index 000000000000..8317afd065b4
--- /dev/null
+++ b/drivers/net/wireless/marvell/libertas/cfg.c
@@ -0,0 +1,2215 @@
+/*
+ * Implement cfg80211 ("iw") support.
+ *
+ * Copyright (C) 2009 M&N Solutions GmbH, 61191 Rosbach, Germany
+ * Holger Schurig <hs4233@mail.mn-solutions.de>
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/hardirq.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/slab.h>
+#include <linux/ieee80211.h>
+#include <net/cfg80211.h>
+#include <asm/unaligned.h>
+
+#include "decl.h"
+#include "cfg.h"
+#include "cmd.h"
+#include "mesh.h"
+
+
+#define CHAN2G(_channel, _freq, _flags) { \
+ .band = IEEE80211_BAND_2GHZ, \
+ .center_freq = (_freq), \
+ .hw_value = (_channel), \
+ .flags = (_flags), \
+ .max_antenna_gain = 0, \
+ .max_power = 30, \
+}
+
+static struct ieee80211_channel lbs_2ghz_channels[] = {
+ CHAN2G(1, 2412, 0),
+ CHAN2G(2, 2417, 0),
+ CHAN2G(3, 2422, 0),
+ CHAN2G(4, 2427, 0),
+ CHAN2G(5, 2432, 0),
+ CHAN2G(6, 2437, 0),
+ CHAN2G(7, 2442, 0),
+ CHAN2G(8, 2447, 0),
+ CHAN2G(9, 2452, 0),
+ CHAN2G(10, 2457, 0),
+ CHAN2G(11, 2462, 0),
+ CHAN2G(12, 2467, 0),
+ CHAN2G(13, 2472, 0),
+ CHAN2G(14, 2484, 0),
+};
+
+#define RATETAB_ENT(_rate, _hw_value, _flags) { \
+ .bitrate = (_rate), \
+ .hw_value = (_hw_value), \
+ .flags = (_flags), \
+}
+
+
+/* Table 6 in section 3.2.1.1 */
+static struct ieee80211_rate lbs_rates[] = {
+ RATETAB_ENT(10, 0, 0),
+ RATETAB_ENT(20, 1, 0),
+ RATETAB_ENT(55, 2, 0),
+ RATETAB_ENT(110, 3, 0),
+ RATETAB_ENT(60, 9, 0),
+ RATETAB_ENT(90, 6, 0),
+ RATETAB_ENT(120, 7, 0),
+ RATETAB_ENT(180, 8, 0),
+ RATETAB_ENT(240, 9, 0),
+ RATETAB_ENT(360, 10, 0),
+ RATETAB_ENT(480, 11, 0),
+ RATETAB_ENT(540, 12, 0),
+};
+
+static struct ieee80211_supported_band lbs_band_2ghz = {
+ .channels = lbs_2ghz_channels,
+ .n_channels = ARRAY_SIZE(lbs_2ghz_channels),
+ .bitrates = lbs_rates,
+ .n_bitrates = ARRAY_SIZE(lbs_rates),
+};
+
+
+static const u32 cipher_suites[] = {
+ WLAN_CIPHER_SUITE_WEP40,
+ WLAN_CIPHER_SUITE_WEP104,
+ WLAN_CIPHER_SUITE_TKIP,
+ WLAN_CIPHER_SUITE_CCMP,
+};
+
+/* Time to stay on the channel */
+#define LBS_DWELL_PASSIVE 100
+#define LBS_DWELL_ACTIVE 40
+
+
+/***************************************************************************
+ * Misc utility functions
+ *
+ * TLVs are Marvell specific. They are very similar to IEs, they have the
+ * same structure: type, length, data*. The only difference: for IEs, the
+ * type and length are u8, but for TLVs they're __le16.
+ */
+
+/*
+ * Convert NL80211's auth_type to the one from Libertas, see chapter 5.9.1
+ * in the firmware spec
+ */
+static int lbs_auth_to_authtype(enum nl80211_auth_type auth_type)
+{
+ int ret = -ENOTSUPP;
+
+ switch (auth_type) {
+ case NL80211_AUTHTYPE_OPEN_SYSTEM:
+ case NL80211_AUTHTYPE_SHARED_KEY:
+ ret = auth_type;
+ break;
+ case NL80211_AUTHTYPE_AUTOMATIC:
+ ret = NL80211_AUTHTYPE_OPEN_SYSTEM;
+ break;
+ case NL80211_AUTHTYPE_NETWORK_EAP:
+ ret = 0x80;
+ break;
+ default:
+ /* silence compiler */
+ break;
+ }
+ return ret;
+}
+
+
+/*
+ * Various firmware commands need the list of supported rates, but with
+ * the hight-bit set for basic rates
+ */
+static int lbs_add_rates(u8 *rates)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(lbs_rates); i++) {
+ u8 rate = lbs_rates[i].bitrate / 5;
+ if (rate == 0x02 || rate == 0x04 ||
+ rate == 0x0b || rate == 0x16)
+ rate |= 0x80;
+ rates[i] = rate;
+ }
+ return ARRAY_SIZE(lbs_rates);
+}
+
+
+/***************************************************************************
+ * TLV utility functions
+ *
+ * TLVs are Marvell specific. They are very similar to IEs, they have the
+ * same structure: type, length, data*. The only difference: for IEs, the
+ * type and length are u8, but for TLVs they're __le16.
+ */
+
+
+/*
+ * Add ssid TLV
+ */
+#define LBS_MAX_SSID_TLV_SIZE \
+ (sizeof(struct mrvl_ie_header) \
+ + IEEE80211_MAX_SSID_LEN)
+
+static int lbs_add_ssid_tlv(u8 *tlv, const u8 *ssid, int ssid_len)
+{
+ struct mrvl_ie_ssid_param_set *ssid_tlv = (void *)tlv;
+
+ /*
+ * TLV-ID SSID 00 00
+ * length 06 00
+ * ssid 4d 4e 54 45 53 54
+ */
+ ssid_tlv->header.type = cpu_to_le16(TLV_TYPE_SSID);
+ ssid_tlv->header.len = cpu_to_le16(ssid_len);
+ memcpy(ssid_tlv->ssid, ssid, ssid_len);
+ return sizeof(ssid_tlv->header) + ssid_len;
+}
+
+
+/*
+ * Add channel list TLV (section 8.4.2)
+ *
+ * Actual channel data comes from priv->wdev->wiphy->channels.
+ */
+#define LBS_MAX_CHANNEL_LIST_TLV_SIZE \
+ (sizeof(struct mrvl_ie_header) \
+ + (LBS_SCAN_BEFORE_NAP * sizeof(struct chanscanparamset)))
+
+static int lbs_add_channel_list_tlv(struct lbs_private *priv, u8 *tlv,
+ int last_channel, int active_scan)
+{
+ int chanscanparamsize = sizeof(struct chanscanparamset) *
+ (last_channel - priv->scan_channel);
+
+ struct mrvl_ie_header *header = (void *) tlv;
+
+ /*
+ * TLV-ID CHANLIST 01 01
+ * length 0e 00
+ * channel 00 01 00 00 00 64 00
+ * radio type 00
+ * channel 01
+ * scan type 00
+ * min scan time 00 00
+ * max scan time 64 00
+ * channel 2 00 02 00 00 00 64 00
+ *
+ */
+
+ header->type = cpu_to_le16(TLV_TYPE_CHANLIST);
+ header->len = cpu_to_le16(chanscanparamsize);
+ tlv += sizeof(struct mrvl_ie_header);
+
+ /* lbs_deb_scan("scan: channels %d to %d\n", priv->scan_channel,
+ last_channel); */
+ memset(tlv, 0, chanscanparamsize);
+
+ while (priv->scan_channel < last_channel) {
+ struct chanscanparamset *param = (void *) tlv;
+
+ param->radiotype = CMD_SCAN_RADIO_TYPE_BG;
+ param->channumber =
+ priv->scan_req->channels[priv->scan_channel]->hw_value;
+ if (active_scan) {
+ param->maxscantime = cpu_to_le16(LBS_DWELL_ACTIVE);
+ } else {
+ param->chanscanmode.passivescan = 1;
+ param->maxscantime = cpu_to_le16(LBS_DWELL_PASSIVE);
+ }
+ tlv += sizeof(struct chanscanparamset);
+ priv->scan_channel++;
+ }
+ return sizeof(struct mrvl_ie_header) + chanscanparamsize;
+}
+
+
+/*
+ * Add rates TLV
+ *
+ * The rates are in lbs_bg_rates[], but for the 802.11b
+ * rates the high bit is set. We add this TLV only because
+ * there's a firmware which otherwise doesn't report all
+ * APs in range.
+ */
+#define LBS_MAX_RATES_TLV_SIZE \
+ (sizeof(struct mrvl_ie_header) \
+ + (ARRAY_SIZE(lbs_rates)))
+
+/* Adds a TLV with all rates the hardware supports */
+static int lbs_add_supported_rates_tlv(u8 *tlv)
+{
+ size_t i;
+ struct mrvl_ie_rates_param_set *rate_tlv = (void *)tlv;
+
+ /*
+ * TLV-ID RATES 01 00
+ * length 0e 00
+ * rates 82 84 8b 96 0c 12 18 24 30 48 60 6c
+ */
+ rate_tlv->header.type = cpu_to_le16(TLV_TYPE_RATES);
+ tlv += sizeof(rate_tlv->header);
+ i = lbs_add_rates(tlv);
+ tlv += i;
+ rate_tlv->header.len = cpu_to_le16(i);
+ return sizeof(rate_tlv->header) + i;
+}
+
+/* Add common rates from a TLV and return the new end of the TLV */
+static u8 *
+add_ie_rates(u8 *tlv, const u8 *ie, int *nrates)
+{
+ int hw, ap, ap_max = ie[1];
+ u8 hw_rate;
+
+ /* Advance past IE header */
+ ie += 2;
+
+ lbs_deb_hex(LBS_DEB_ASSOC, "AP IE Rates", (u8 *) ie, ap_max);
+
+ for (hw = 0; hw < ARRAY_SIZE(lbs_rates); hw++) {
+ hw_rate = lbs_rates[hw].bitrate / 5;
+ for (ap = 0; ap < ap_max; ap++) {
+ if (hw_rate == (ie[ap] & 0x7f)) {
+ *tlv++ = ie[ap];
+ *nrates = *nrates + 1;
+ }
+ }
+ }
+ return tlv;
+}
+
+/*
+ * Adds a TLV with all rates the hardware *and* BSS supports.
+ */
+static int lbs_add_common_rates_tlv(u8 *tlv, struct cfg80211_bss *bss)
+{
+ struct mrvl_ie_rates_param_set *rate_tlv = (void *)tlv;
+ const u8 *rates_eid, *ext_rates_eid;
+ int n = 0;
+
+ rcu_read_lock();
+ rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_SUPP_RATES);
+ ext_rates_eid = ieee80211_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES);
+
+ /*
+ * 01 00 TLV_TYPE_RATES
+ * 04 00 len
+ * 82 84 8b 96 rates
+ */
+ rate_tlv->header.type = cpu_to_le16(TLV_TYPE_RATES);
+ tlv += sizeof(rate_tlv->header);
+
+ /* Add basic rates */
+ if (rates_eid) {
+ tlv = add_ie_rates(tlv, rates_eid, &n);
+
+ /* Add extended rates, if any */
+ if (ext_rates_eid)
+ tlv = add_ie_rates(tlv, ext_rates_eid, &n);
+ } else {
+ lbs_deb_assoc("assoc: bss had no basic rate IE\n");
+ /* Fallback: add basic 802.11b rates */
+ *tlv++ = 0x82;
+ *tlv++ = 0x84;
+ *tlv++ = 0x8b;
+ *tlv++ = 0x96;
+ n = 4;
+ }
+ rcu_read_unlock();
+
+ rate_tlv->header.len = cpu_to_le16(n);
+ return sizeof(rate_tlv->header) + n;
+}
+
+
+/*
+ * Add auth type TLV.
+ *
+ * This is only needed for newer firmware (V9 and up).
+ */
+#define LBS_MAX_AUTH_TYPE_TLV_SIZE \
+ sizeof(struct mrvl_ie_auth_type)
+
+static int lbs_add_auth_type_tlv(u8 *tlv, enum nl80211_auth_type auth_type)
+{
+ struct mrvl_ie_auth_type *auth = (void *) tlv;
+
+ /*
+ * 1f 01 TLV_TYPE_AUTH_TYPE
+ * 01 00 len
+ * 01 auth type
+ */
+ auth->header.type = cpu_to_le16(TLV_TYPE_AUTH_TYPE);
+ auth->header.len = cpu_to_le16(sizeof(*auth)-sizeof(auth->header));
+ auth->auth = cpu_to_le16(lbs_auth_to_authtype(auth_type));
+ return sizeof(*auth);
+}
+
+
+/*
+ * Add channel (phy ds) TLV
+ */
+#define LBS_MAX_CHANNEL_TLV_SIZE \
+ sizeof(struct mrvl_ie_header)
+
+static int lbs_add_channel_tlv(u8 *tlv, u8 channel)
+{
+ struct mrvl_ie_ds_param_set *ds = (void *) tlv;
+
+ /*
+ * 03 00 TLV_TYPE_PHY_DS
+ * 01 00 len
+ * 06 channel
+ */
+ ds->header.type = cpu_to_le16(TLV_TYPE_PHY_DS);
+ ds->header.len = cpu_to_le16(sizeof(*ds)-sizeof(ds->header));
+ ds->channel = channel;
+ return sizeof(*ds);
+}
+
+
+/*
+ * Add (empty) CF param TLV of the form:
+ */
+#define LBS_MAX_CF_PARAM_TLV_SIZE \
+ sizeof(struct mrvl_ie_header)
+
+static int lbs_add_cf_param_tlv(u8 *tlv)
+{
+ struct mrvl_ie_cf_param_set *cf = (void *)tlv;
+
+ /*
+ * 04 00 TLV_TYPE_CF
+ * 06 00 len
+ * 00 cfpcnt
+ * 00 cfpperiod
+ * 00 00 cfpmaxduration
+ * 00 00 cfpdurationremaining
+ */
+ cf->header.type = cpu_to_le16(TLV_TYPE_CF);
+ cf->header.len = cpu_to_le16(sizeof(*cf)-sizeof(cf->header));
+ return sizeof(*cf);
+}
+
+/*
+ * Add WPA TLV
+ */
+#define LBS_MAX_WPA_TLV_SIZE \
+ (sizeof(struct mrvl_ie_header) \
+ + 128 /* TODO: I guessed the size */)
+
+static int lbs_add_wpa_tlv(u8 *tlv, const u8 *ie, u8 ie_len)
+{
+ size_t tlv_len;
+
+ /*
+ * We need just convert an IE to an TLV. IEs use u8 for the header,
+ * u8 type
+ * u8 len
+ * u8[] data
+ * but TLVs use __le16 instead:
+ * __le16 type
+ * __le16 len
+ * u8[] data
+ */
+ *tlv++ = *ie++;
+ *tlv++ = 0;
+ tlv_len = *tlv++ = *ie++;
+ *tlv++ = 0;
+ while (tlv_len--)
+ *tlv++ = *ie++;
+ /* the TLV is two bytes larger than the IE */
+ return ie_len + 2;
+}
+
+/*
+ * Set Channel
+ */
+
+static int lbs_cfg_set_monitor_channel(struct wiphy *wiphy,
+ struct cfg80211_chan_def *chandef)
+{
+ struct lbs_private *priv = wiphy_priv(wiphy);
+ int ret = -ENOTSUPP;
+
+ lbs_deb_enter_args(LBS_DEB_CFG80211, "freq %d, type %d",
+ chandef->chan->center_freq,
+ cfg80211_get_chandef_type(chandef));
+
+ if (cfg80211_get_chandef_type(chandef) != NL80211_CHAN_NO_HT)
+ goto out;
+
+ ret = lbs_set_channel(priv, chandef->chan->hw_value);
+
+ out:
+ lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
+ return ret;
+}
+
+static int lbs_cfg_set_mesh_channel(struct wiphy *wiphy,
+ struct net_device *netdev,
+ struct ieee80211_channel *channel)
+{
+ struct lbs_private *priv = wiphy_priv(wiphy);
+ int ret = -ENOTSUPP;
+
+ lbs_deb_enter_args(LBS_DEB_CFG80211, "iface %s freq %d",
+ netdev_name(netdev), channel->center_freq);
+
+ if (netdev != priv->mesh_dev)
+ goto out;
+
+ ret = lbs_mesh_set_channel(priv, channel->hw_value);
+
+ out:
+ lbs_deb_leave_args(LBS_DEB_CFG80211, "ret %d", ret);
+ return ret;
+}
+
+
+
+/*
+ * Scanning
+ */
+
+/*
+ * When scanning, the firmware doesn't send a nul packet with the power-safe
+ * bit to the AP. So we cannot stay away from our current channel too long,
+ * otherwise we loose data. So take a "nap" while scanning every other
+ * while.
+ */
+#define LBS_SCAN_BEFORE_NAP 4
+
+
+/*
+ * When the firmware reports back a scan-result, it gives us an "u8 rssi",
+ * which isn't really an RSSI, as it becomes larger when moving away from
+ * the AP. Anyway, we need to convert that into mBm.
+ */
+#define LBS_SCAN_RSSI_TO_MBM(rssi) \
+ ((-(int)rssi + 3)*100)
+
+static int lbs_ret_scan(struct lbs_private *priv, unsigned long dummy,
+ struct cmd_header *resp)
+{
+ struct cfg80211_bss *bss;
+ struct cmd_ds_802_11_scan_rsp *scanresp = (void *)resp;
+ int bsssize;
+ const u8 *pos;
+ const u8 *tsfdesc;
+ int tsfsize;
+ int i;
+ int ret = -EILSEQ;
+
+ lbs_deb_enter(LBS_DEB_CFG80211);
+
+ bsssize = get_unaligned_le16(&scanresp->bssdescriptsize);
+
+ lbs_deb_scan("scan response: %d BSSs (%d bytes); resp size %d bytes\n",
+ scanresp->nr_sets, bsssize, le16_to_cpu(resp->size));
+
+ if (scanresp->nr_sets == 0) {
+ ret = 0;
+ goto done;
+ }
+
+ /*
+ * The general layout of the scan response is described in chapter
+ * 5.7.1. Basically we have a common part, then any number of BSS
+ * descriptor sections. Finally we have section with the same number
+ * of TSFs.
+ *
+ * cmd_ds_802_11_scan_rsp
+ * cmd_header
+ * pos_size
+ * nr_sets
+ * bssdesc 1
+ * bssid
+ * rssi
+ * timestamp
+ * intvl
+ * capa
+ * IEs
+ * bssdesc 2
+ * bssdesc n
+ * MrvlIEtypes_TsfFimestamp_t
+ * TSF for BSS 1
+ * TSF for BSS 2
+ * TSF for BSS n
+ */
+
+ pos = scanresp->bssdesc_and_tlvbuffer;
+
+ lbs_deb_hex(LBS_DEB_SCAN, "SCAN_RSP", scanresp->bssdesc_and_tlvbuffer,
+ scanresp->bssdescriptsize);
+
+ tsfdesc = pos + bsssize;
+ tsfsize = 4 + 8 * scanresp->nr_sets;
+ lbs_deb_hex(LBS_DEB_SCAN, "SCAN_TSF", (u8 *) tsfdesc, tsfsize);
+
+ /* Validity check: