Search Linux Wireless

[PATCH] Add stlc45xx, wi-fi driver for stlc4550/4560

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This patch adds a new driver called stlc45xx, which supports wi-fi chipsets
stlc4550 and stlc4560 from ST-NXP Wireless. The chipset can be found, for
example, from Nokia N800 and N810 products.

The driver is implemented based on the firmware interface information
published by ST-NXP Wireless here:

http://wireless.kernel.org/en/developers/Documentation/specs#STMicroelectronicshardware

Currently only SPI interface is supported.

Signed-off-by: Kalle Valo <kalle.valo@xxxxxxxxx>
---

 MAINTAINERS                          |    8 
 drivers/net/wireless/Kconfig         |    9 
 drivers/net/wireless/Makefile        |    2 
 drivers/net/wireless/stlc45xx.c      | 2594 ++++++++++++++++++++++++++++++++++
 drivers/net/wireless/stlc45xx.h      |  285 ++++
 drivers/net/wireless/stlc45xx_lmac.h |  434 ++++++
 6 files changed, 3332 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/wireless/stlc45xx.c
 create mode 100644 drivers/net/wireless/stlc45xx.h
 create mode 100644 drivers/net/wireless/stlc45xx_lmac.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 189def1..b4a6fa1 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4051,6 +4051,14 @@ STARMODE RADIO IP (STRIP) PROTOCOL DRIVER
 W:	http://mosquitonet.Stanford.EDU/strip.html
 S:	Orphan
 
+STLC45XX WIRELESS DRIVER
+P:	Kalle Valo
+M:	kalle.valo@xxxxxxxxx
+L:	linux-wireless@xxxxxxxxxxxxxxx
+L:	stlc45xx-devel@xxxxxxxxxxxxxxxx
+W:	http://stlc45xx.garage.maemo.org/
+S:	Maintained
+
 STRADIS MPEG-2 DECODER DRIVER
 P:	Nathan Laredo
 M:	laredo@xxxxxxx
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index ea543fc..3c70a16 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -580,6 +580,15 @@ config MAC80211_HWSIM
 	  To compile this driver as a module, choose M here: the module will be
 	  called mac80211_hwsim.  If unsure, say N.
 
+config STLC45XX
+	tristate "stlc4550/4560 support"
+	depends on MAC80211 && WLAN_80211 && SPI_MASTER
+	---help---
+	  This is a driver for stlc4550 and stlc4560 chipsets.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called stlc45xx.  If unsure, say N.
+
 source "drivers/net/wireless/p54/Kconfig"
 source "drivers/net/wireless/ath5k/Kconfig"
 source "drivers/net/wireless/ath9k/Kconfig"
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index ac590e1..dfee9fe 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -55,3 +55,5 @@ obj-$(CONFIG_ATH5K)	+= ath5k/
 obj-$(CONFIG_ATH9K)	+= ath9k/
 
 obj-$(CONFIG_MAC80211_HWSIM)	+= mac80211_hwsim.o
+
+obj-$(CONFIG_STLC45XX)	+= stlc45xx.o
diff --git a/drivers/net/wireless/stlc45xx.c b/drivers/net/wireless/stlc45xx.c
new file mode 100644
index 0000000..ee7dea3
--- /dev/null
+++ b/drivers/net/wireless/stlc45xx.c
@@ -0,0 +1,2594 @@
+/*
+ * This file is part of stlc45xx
+ *
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * Contact: Kalle Valo <kalle.valo@xxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include "stlc45xx.h"
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/firmware.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/spi/spi.h>
+#include <linux/etherdevice.h>
+#include <linux/gpio.h>
+
+#include "stlc45xx_lmac.h"
+
+static const u8 default_cal_channels[] = {
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x09,
+	0x00, 0x00, 0xc9, 0xff, 0xd8, 0xff, 0x00, 0x00, 0x00, 0x01, 0x10,
+	0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0xe0, 0x00, 0xe0, 0x00,
+	0xe0, 0x00, 0xe0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0,
+	0x00, 0x54, 0x01, 0xab, 0xf6, 0xc0, 0x42, 0xc0, 0x42, 0xc0, 0x42,
+	0xc0, 0x42, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00,
+	0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x22, 0x01, 0x37, 0xa9,
+	0xc0, 0x33, 0xc0, 0x33, 0xc0, 0x33, 0xc0, 0x33, 0x00, 0xbc, 0x00,
+	0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc,
+	0x00, 0xbc, 0xfb, 0x00, 0xca, 0x79, 0xc0, 0x2b, 0xc0, 0x2b, 0xc0,
+	0x2b, 0xc0, 0x2b, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4,
+	0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0xd0, 0x00, 0x5d,
+	0x54, 0xc0, 0x21, 0xc0, 0x21, 0xc0, 0x21, 0xc0, 0x21, 0x00, 0xaa,
+	0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00,
+	0xaa, 0x00, 0xaa, 0xa7, 0x00, 0xa9, 0x3d, 0xc0, 0x17, 0xc0, 0x17,
+	0xc0, 0x17, 0xc0, 0x17, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00,
+	0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x7a, 0x00,
+	0x06, 0x2c, 0xc0, 0x0d, 0xc0, 0x0d, 0xc0, 0x0d, 0xc0, 0x0d, 0x00,
+	0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96,
+	0x00, 0x96, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x06, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x71, 0x09, 0x00, 0x00, 0xc9, 0xff, 0xd8,
+	0xff, 0x00, 0x00, 0x00, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01,
+	0x10, 0x01, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xd0,
+	0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0x54, 0x01, 0xab, 0xf6,
+	0xc0, 0x42, 0xc0, 0x42, 0xc0, 0x42, 0xc0, 0x42, 0x00, 0xcb, 0x00,
+	0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb,
+	0x00, 0xcb, 0x22, 0x01, 0x37, 0xa9, 0xc0, 0x33, 0xc0, 0x33, 0xc0,
+	0x33, 0xc0, 0x33, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc,
+	0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0xfb, 0x00, 0xca,
+	0x79, 0xc0, 0x2b, 0xc0, 0x2b, 0xc0, 0x2b, 0xc0, 0x2b, 0x00, 0xb4,
+	0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00,
+	0xb4, 0x00, 0xb4, 0xd0, 0x00, 0x5d, 0x54, 0xc0, 0x21, 0xc0, 0x21,
+	0xc0, 0x21, 0xc0, 0x21, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00,
+	0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0xa7, 0x00,
+	0xa9, 0x3d, 0xc0, 0x17, 0xc0, 0x17, 0xc0, 0x17, 0xc0, 0x17, 0x00,
+	0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0,
+	0x00, 0xa0, 0x00, 0xa0, 0x7a, 0x00, 0x06, 0x2c, 0xc0, 0x0d, 0xc0,
+	0x0d, 0xc0, 0x0d, 0xc0, 0x0d, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96,
+	0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76,
+	0x09, 0x00, 0x00, 0xc9, 0xff, 0xd8, 0xff, 0x00, 0x00, 0x00, 0x01,
+	0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0xf0, 0x00, 0xf0,
+	0x00, 0xf0, 0x00, 0xf0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00,
+	0xd0, 0x00, 0x54, 0x01, 0xab, 0xf6, 0xc0, 0x42, 0xc0, 0x42, 0xc0,
+	0x42, 0xc0, 0x42, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb,
+	0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x22, 0x01, 0x37,
+	0xa9, 0xc0, 0x33, 0xc0, 0x33, 0xc0, 0x33, 0xc0, 0x33, 0x00, 0xbc,
+	0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00,
+	0xbc, 0x00, 0xbc, 0xfb, 0x00, 0xca, 0x79, 0xc0, 0x2b, 0xc0, 0x2b,
+	0xc0, 0x2b, 0xc0, 0x2b, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00,
+	0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0xd0, 0x00,
+	0x5d, 0x54, 0xc0, 0x21, 0xc0, 0x21, 0xc0, 0x21, 0xc0, 0x21, 0x00,
+	0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa,
+	0x00, 0xaa, 0x00, 0xaa, 0xa7, 0x00, 0xa9, 0x3d, 0xc0, 0x17, 0xc0,
+	0x17, 0xc0, 0x17, 0xc0, 0x17, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0,
+	0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x7a,
+	0x00, 0x06, 0x2c, 0xc0, 0x0d, 0xc0, 0x0d, 0xc0, 0x0d, 0xc0, 0x0d,
+	0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00,
+	0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 0x09, 0x00, 0x00, 0xc9, 0xff,
+	0xd8, 0xff, 0x00, 0x00, 0x00, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10,
+	0x01, 0x10, 0x01, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00,
+	0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0x54, 0x01, 0xab,
+	0xf6, 0xc0, 0x42, 0xc0, 0x42, 0xc0, 0x42, 0xc0, 0x42, 0x00, 0xcb,
+	0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00,
+	0xcb, 0x00, 0xcb, 0x22, 0x01, 0x37, 0xa9, 0xc0, 0x33, 0xc0, 0x33,
+	0xc0, 0x33, 0xc0, 0x33, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00,
+	0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0xfb, 0x00,
+	0xca, 0x79, 0xc0, 0x2b, 0xc0, 0x2b, 0xc0, 0x2b, 0xc0, 0x2b, 0x00,
+	0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4,
+	0x00, 0xb4, 0x00, 0xb4, 0xd0, 0x00, 0x5d, 0x54, 0xc0, 0x21, 0xc0,
+	0x21, 0xc0, 0x21, 0xc0, 0x21, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa,
+	0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0xa7,
+	0x00, 0xa9, 0x3d, 0xc0, 0x17, 0xc0, 0x17, 0xc0, 0x17, 0xc0, 0x17,
+	0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00,
+	0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x7a, 0x00, 0x06, 0x2c, 0xc0, 0x0d,
+	0xc0, 0x0d, 0xc0, 0x0d, 0xc0, 0x0d, 0x00, 0x96, 0x00, 0x96, 0x00,
+	0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x80,
+	0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x80, 0x09, 0x00, 0x00, 0xc9, 0xff, 0xd8, 0xff, 0x00, 0x00, 0x00,
+	0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0xf0, 0x00,
+	0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0,
+	0x00, 0xd0, 0x00, 0x54, 0x01, 0xab, 0xf6, 0xc0, 0x42, 0xc0, 0x42,
+	0xc0, 0x42, 0xc0, 0x42, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00,
+	0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x22, 0x01,
+	0x37, 0xa9, 0xc0, 0x33, 0xc0, 0x33, 0xc0, 0x33, 0xc0, 0x33, 0x00,
+	0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc,
+	0x00, 0xbc, 0x00, 0xbc, 0xfb, 0x00, 0xca, 0x79, 0xc0, 0x2b, 0xc0,
+	0x2b, 0xc0, 0x2b, 0xc0, 0x2b, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4,
+	0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0xd0,
+	0x00, 0x5d, 0x54, 0xc0, 0x21, 0xc0, 0x21, 0xc0, 0x21, 0xc0, 0x21,
+	0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00,
+	0xaa, 0x00, 0xaa, 0x00, 0xaa, 0xa7, 0x00, 0xa9, 0x3d, 0xc0, 0x17,
+	0xc0, 0x17, 0xc0, 0x17, 0xc0, 0x17, 0x00, 0xa0, 0x00, 0xa0, 0x00,
+	0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0,
+	0x7a, 0x00, 0x06, 0x2c, 0xc0, 0x0d, 0xc0, 0x0d, 0xc0, 0x0d, 0xc0,
+	0x0d, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96,
+	0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x85, 0x09, 0x00, 0x00, 0xc9,
+	0xff, 0xd8, 0xff, 0x00, 0x00, 0x00, 0x01, 0x10, 0x01, 0x10, 0x01,
+	0x10, 0x01, 0x10, 0x01, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0,
+	0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0x54, 0x01,
+	0xab, 0xf6, 0xc0, 0x42, 0xc0, 0x42, 0xc0, 0x42, 0xc0, 0x42, 0x00,
+	0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb,
+	0x00, 0xcb, 0x00, 0xcb, 0x22, 0x01, 0x37, 0xa9, 0xc0, 0x33, 0xc0,
+	0x33, 0xc0, 0x33, 0xc0, 0x33, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc,
+	0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0xfb,
+	0x00, 0xca, 0x79, 0xc0, 0x2b, 0xc0, 0x2b, 0xc0, 0x2b, 0xc0, 0x2b,
+	0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00,
+	0xb4, 0x00, 0xb4, 0x00, 0xb4, 0xd0, 0x00, 0x5d, 0x54, 0xc0, 0x21,
+	0xc0, 0x21, 0xc0, 0x21, 0xc0, 0x21, 0x00, 0xaa, 0x00, 0xaa, 0x00,
+	0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa,
+	0xa7, 0x00, 0xa9, 0x3d, 0xc0, 0x17, 0xc0, 0x17, 0xc0, 0x17, 0xc0,
+	0x17, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0,
+	0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x7a, 0x00, 0x06, 0x2c, 0xc0,
+	0x0d, 0xc0, 0x0d, 0xc0, 0x0d, 0xc0, 0x0d, 0x00, 0x96, 0x00, 0x96,
+	0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00,
+	0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06,
+	0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x8a, 0x09, 0x00, 0x00, 0xc9, 0xff, 0xd8, 0xff, 0x00, 0x00,
+	0x00, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0xf0,
+	0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xd0, 0x00, 0xd0, 0x00,
+	0xd0, 0x00, 0xd0, 0x00, 0x54, 0x01, 0xab, 0xf6, 0xc0, 0x42, 0xc0,
+	0x42, 0xc0, 0x42, 0xc0, 0x42, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb,
+	0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x22,
+	0x01, 0x37, 0xa9, 0xc0, 0x33, 0xc0, 0x33, 0xc0, 0x33, 0xc0, 0x33,
+	0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00,
+	0xbc, 0x00, 0xbc, 0x00, 0xbc, 0xfb, 0x00, 0xca, 0x79, 0xc0, 0x2b,
+	0xc0, 0x2b, 0xc0, 0x2b, 0xc0, 0x2b, 0x00, 0xb4, 0x00, 0xb4, 0x00,
+	0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4,
+	0xd0, 0x00, 0x5d, 0x54, 0xc0, 0x21, 0xc0, 0x21, 0xc0, 0x21, 0xc0,
+	0x21, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa,
+	0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0xa7, 0x00, 0xa9, 0x3d, 0xc0,
+	0x17, 0xc0, 0x17, 0xc0, 0x17, 0xc0, 0x17, 0x00, 0xa0, 0x00, 0xa0,
+	0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00,
+	0xa0, 0x7a, 0x00, 0x06, 0x2c, 0xc0, 0x0d, 0xc0, 0x0d, 0xc0, 0x0d,
+	0xc0, 0x0d, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00,
+	0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8f, 0x09, 0x00, 0x00,
+	0xc9, 0xff, 0xd8, 0xff, 0x00, 0x00, 0x00, 0x01, 0x10, 0x01, 0x10,
+	0x01, 0x10, 0x01, 0x10, 0x01, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00,
+	0xf0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0x54,
+	0x01, 0xab, 0xf6, 0xc0, 0x42, 0xc0, 0x42, 0xc0, 0x42, 0xc0, 0x42,
+	0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00,
+	0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x22, 0x01, 0x37, 0xa9, 0xc0, 0x33,
+	0xc0, 0x33, 0xc0, 0x33, 0xc0, 0x33, 0x00, 0xbc, 0x00, 0xbc, 0x00,
+	0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc,
+	0xfb, 0x00, 0xca, 0x79, 0xc0, 0x2b, 0xc0, 0x2b, 0xc0, 0x2b, 0xc0,
+	0x2b, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4,
+	0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0xd0, 0x00, 0x5d, 0x54, 0xc0,
+	0x21, 0xc0, 0x21, 0xc0, 0x21, 0xc0, 0x21, 0x00, 0xaa, 0x00, 0xaa,
+	0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00,
+	0xaa, 0xa7, 0x00, 0xa9, 0x3d, 0xc0, 0x17, 0xc0, 0x17, 0xc0, 0x17,
+	0xc0, 0x17, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00,
+	0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x7a, 0x00, 0x06, 0x2c,
+	0xc0, 0x0d, 0xc0, 0x0d, 0xc0, 0x0d, 0xc0, 0x0d, 0x00, 0x96, 0x00,
+	0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96,
+	0x00, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x06, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x94, 0x09, 0x00, 0x00, 0xc9, 0xff, 0xd8, 0xff, 0x00,
+	0x00, 0x00, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01,
+	0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xd0, 0x00, 0xd0,
+	0x00, 0xd0, 0x00, 0xd0, 0x00, 0x54, 0x01, 0xab, 0xf6, 0xc0, 0x42,
+	0xc0, 0x42, 0xc0, 0x42, 0xc0, 0x42, 0x00, 0xcb, 0x00, 0xcb, 0x00,
+	0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb,
+	0x22, 0x01, 0x37, 0xa9, 0xc0, 0x33, 0xc0, 0x33, 0xc0, 0x33, 0xc0,
+	0x33, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc,
+	0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0xfb, 0x00, 0xca, 0x79, 0xc0,
+	0x2b, 0xc0, 0x2b, 0xc0, 0x2b, 0xc0, 0x2b, 0x00, 0xb4, 0x00, 0xb4,
+	0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00,
+	0xb4, 0xd0, 0x00, 0x5d, 0x54, 0xc0, 0x21, 0xc0, 0x21, 0xc0, 0x21,
+	0xc0, 0x21, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00,
+	0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0xa7, 0x00, 0xa9, 0x3d,
+	0xc0, 0x17, 0xc0, 0x17, 0xc0, 0x17, 0xc0, 0x17, 0x00, 0xa0, 0x00,
+	0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0,
+	0x00, 0xa0, 0x7a, 0x00, 0x06, 0x2c, 0xc0, 0x0d, 0xc0, 0x0d, 0xc0,
+	0x0d, 0xc0, 0x0d, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96,
+	0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0x09, 0x00,
+	0x00, 0xc9, 0xff, 0xd8, 0xff, 0x00, 0x00, 0x00, 0x01, 0x10, 0x01,
+	0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0xf0, 0x00, 0xf0, 0x00, 0xf0,
+	0x00, 0xf0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00,
+	0x54, 0x01, 0xab, 0xf6, 0xc0, 0x42, 0xc0, 0x42, 0xc0, 0x42, 0xc0,
+	0x42, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb,
+	0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x22, 0x01, 0x37, 0xa9, 0xc0,
+	0x33, 0xc0, 0x33, 0xc0, 0x33, 0xc0, 0x33, 0x00, 0xbc, 0x00, 0xbc,
+	0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00,
+	0xbc, 0xfb, 0x00, 0xca, 0x79, 0xc0, 0x2b, 0xc0, 0x2b, 0xc0, 0x2b,
+	0xc0, 0x2b, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00,
+	0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0xd0, 0x00, 0x5d, 0x54,
+	0xc0, 0x21, 0xc0, 0x21, 0xc0, 0x21, 0xc0, 0x21, 0x00, 0xaa, 0x00,
+	0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa,
+	0x00, 0xaa, 0xa7, 0x00, 0xa9, 0x3d, 0xc0, 0x17, 0xc0, 0x17, 0xc0,
+	0x17, 0xc0, 0x17, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0,
+	0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x7a, 0x00, 0x06,
+	0x2c, 0xc0, 0x0d, 0xc0, 0x0d, 0xc0, 0x0d, 0xc0, 0x0d, 0x00, 0x96,
+	0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00,
+	0x96, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x06, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x9e, 0x09, 0x00, 0x00, 0xc9, 0xff, 0xd8, 0xff,
+	0x00, 0x00, 0x00, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10, 0x01, 0x10,
+	0x01, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xf0, 0x00, 0xd0, 0x00,
+	0xd0, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0x54, 0x01, 0xab, 0xf6, 0xc0,
+	0x42, 0xc0, 0x42, 0xc0, 0x42, 0xc0, 0x42, 0x00, 0xcb, 0x00, 0xcb,
+	0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00, 0xcb, 0x00,
+	0xcb, 0x22, 0x01, 0x37, 0xa9, 0xc0, 0x33, 0xc0, 0x33, 0xc0, 0x33,
+	0xc0, 0x33, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00,
+	0xbc, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbc, 0xfb, 0x00, 0xca, 0x79,
+	0xc0, 0x2b, 0xc0, 0x2b, 0xc0, 0x2b, 0xc0, 0x2b, 0x00, 0xb4, 0x00,
+	0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4, 0x00, 0xb4,
+	0x00, 0xb4, 0xd0, 0x00, 0x5d, 0x54, 0xc0, 0x21, 0xc0, 0x21, 0xc0,
+	0x21, 0xc0, 0x21, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa,
+	0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xaa, 0xa7, 0x00, 0xa9,
+	0x3d, 0xc0, 0x17, 0xc0, 0x17, 0xc0, 0x17, 0xc0, 0x17, 0x00, 0xa0,
+	0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00, 0xa0, 0x00,
+	0xa0, 0x00, 0xa0, 0x7a, 0x00, 0x06, 0x2c, 0xc0, 0x0d, 0xc0, 0x0d,
+	0xc0, 0x0d, 0xc0, 0x0d, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00,
+	0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x96, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x80, 0x80, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00 };
+
+static const u8 default_cal_rssi[] = {
+	0x0a, 0x01, 0x72, 0xfe, 0x1a, 0x00, 0x00, 0x00, 0x0a, 0x01, 0x72,
+	0xfe, 0x1a, 0x00, 0x00, 0x00, 0x0a, 0x01, 0x72, 0xfe, 0x1a, 0x00,
+	0x00, 0x00, 0x0a, 0x01, 0x72, 0xfe, 0x1a, 0x00, 0x00, 0x00, 0x0a,
+	0x01, 0x72, 0xfe, 0x1a, 0x00, 0x00, 0x00, 0x0a, 0x01, 0x72, 0xfe,
+	0x1a, 0x00, 0x00, 0x00, 0x0a, 0x01, 0x72, 0xfe, 0x1a, 0x00, 0x00,
+	0x00, 0x0a, 0x01, 0x72, 0xfe, 0x1a, 0x00, 0x00, 0x00, 0x0a, 0x01,
+	0x72, 0xfe, 0x1a, 0x00, 0x00, 0x00, 0x0a, 0x01, 0x72, 0xfe, 0x1a,
+	0x00, 0x00, 0x00, 0x0a, 0x01, 0x72, 0xfe, 0x1a, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00, 0x00, 0x00, 0x00 };
+
+static void stlc45xx_tx_edcf(struct stlc45xx *stlc);
+static void stlc45xx_tx_setup(struct stlc45xx *stlc);
+static void stlc45xx_tx_scan(struct stlc45xx *stlc);
+static void stlc45xx_tx_psm(struct stlc45xx *stlc, bool enable);
+static int stlc45xx_tx_nullfunc(struct stlc45xx *stlc, bool powersave);
+static int stlc45xx_tx_pspoll(struct stlc45xx *stlc, bool powersave);
+
+static ssize_t stlc45xx_sysfs_show_cal_rssi(struct device *dev,
+					    struct device_attribute *attr,
+					    char *buf)
+{
+	struct stlc45xx *stlc = dev_get_drvdata(dev);
+	ssize_t len;
+
+	stlc45xx_debug(DEBUG_FUNC, "%s", __func__);
+
+	len = PAGE_SIZE;
+
+	mutex_lock(&stlc->mutex);
+
+	if (stlc->cal_rssi)
+		hex_dump_to_buffer(stlc->cal_rssi, RSSI_CAL_ARRAY_LEN, 16,
+				   2, buf, len, 0);
+	mutex_unlock(&stlc->mutex);
+
+	len = strlen(buf);
+
+	return len;
+}
+
+static ssize_t stlc45xx_sysfs_store_cal_rssi(struct device *dev,
+					     struct device_attribute *attr,
+					     const char *buf, size_t count)
+{
+	struct stlc45xx *stlc = dev_get_drvdata(dev);
+
+	stlc45xx_debug(DEBUG_FUNC, "%s", __func__);
+
+	mutex_lock(&stlc->mutex);
+
+	if (count != RSSI_CAL_ARRAY_LEN) {
+		stlc45xx_error("invalid cal_rssi length: %d", count);
+		count = 0;
+		goto out_unlock;
+	}
+
+	kfree(stlc->cal_rssi);
+
+	stlc->cal_rssi = kmemdup(buf, RSSI_CAL_ARRAY_LEN, GFP_KERNEL);
+
+	if (!stlc->cal_rssi) {
+		stlc45xx_error("failed to allocate memory for cal_rssi");
+		count = 0;
+		goto out_unlock;
+	}
+
+ out_unlock:
+	mutex_unlock(&stlc->mutex);
+
+	return count;
+}
+
+static ssize_t stlc45xx_sysfs_show_cal_channels(struct device *dev,
+						struct device_attribute *attr,
+						char *buf)
+{
+	struct stlc45xx *stlc = dev_get_drvdata(dev);
+	ssize_t len;
+
+	stlc45xx_debug(DEBUG_FUNC, "%s", __func__);
+
+	len = PAGE_SIZE;
+
+	mutex_lock(&stlc->mutex);
+
+	if (stlc->cal_channels)
+		hex_dump_to_buffer(stlc->cal_channels, CHANNEL_CAL_ARRAY_LEN,
+				   16, 2, buf, len, 0);
+
+	mutex_unlock(&stlc->mutex);
+
+	len = strlen(buf);
+
+	return len;
+}
+
+static ssize_t stlc45xx_sysfs_store_cal_channels(struct device *dev,
+						 struct device_attribute *attr,
+						 const char *buf, size_t count)
+{
+	struct stlc45xx *stlc = dev_get_drvdata(dev);
+
+	stlc45xx_debug(DEBUG_FUNC, "%s", __func__);
+
+	mutex_lock(&stlc->mutex);
+
+	if (count != CHANNEL_CAL_ARRAY_LEN) {
+		stlc45xx_error("invalid cal_channels size: %d ", count);
+		count = 0;
+		goto out_unlock;
+	}
+
+	kfree(stlc->cal_channels);
+
+	stlc->cal_channels = kmemdup(buf, count, GFP_KERNEL);
+
+	if (!stlc->cal_channels) {
+		stlc45xx_error("failed to allocate memory for cal_channels");
+		count = 0;
+		goto out_unlock;
+	}
+
+out_unlock:
+	mutex_unlock(&stlc->mutex);
+
+	return count;
+}
+
+static ssize_t stlc45xx_sysfs_show_tx_buf(struct device *dev,
+					  struct device_attribute *attr,
+					  char *buf)
+{
+	struct stlc45xx *stlc = dev_get_drvdata(dev);
+	struct txbuffer *entry;
+	ssize_t len = 0;
+
+	stlc45xx_debug(DEBUG_FUNC, "%s()", __func__);
+
+	mutex_lock(&stlc->mutex);
+
+	list_for_each_entry(entry, &stlc->tx_sent, tx_list) {
+		len += sprintf(buf + len, "0x%x: 0x%x-0x%x\n",
+			       entry->handle, entry->start,
+			       entry->end);
+	}
+
+	mutex_unlock(&stlc->mutex);
+
+	return len;
+}
+
+static DEVICE_ATTR(cal_rssi, S_IRUGO | S_IWUSR,
+		   stlc45xx_sysfs_show_cal_rssi,
+		   stlc45xx_sysfs_store_cal_rssi);
+static DEVICE_ATTR(cal_channels, S_IRUGO | S_IWUSR,
+		   stlc45xx_sysfs_show_cal_channels,
+		   stlc45xx_sysfs_store_cal_channels);
+static DEVICE_ATTR(tx_buf, S_IRUGO, stlc45xx_sysfs_show_tx_buf, NULL);
+
+static void stlc45xx_spi_read(struct stlc45xx *stlc, unsigned long addr,
+			      void *buf, size_t len)
+{
+	struct spi_transfer t[2];
+	struct spi_message m;
+
+	/* We first push the address */
+	addr = (addr << 8) | ADDR_READ_BIT_15;
+
+	spi_message_init(&m);
+	memset(t, 0, sizeof(t));
+
+	t[0].tx_buf = &addr;
+	t[0].len = 2;
+	spi_message_add_tail(&t[0], &m);
+
+	t[1].rx_buf = buf;
+	t[1].len = len;
+	spi_message_add_tail(&t[1], &m);
+
+	spi_sync(stlc->spi, &m);
+}
+
+
+static void stlc45xx_spi_write(struct stlc45xx *stlc, unsigned long addr,
+			       void *buf, size_t len)
+{
+	struct spi_transfer t[3];
+	struct spi_message m;
+	u16 last_word;
+
+	/* We first push the address */
+	addr = addr << 8;
+
+	spi_message_init(&m);
+	memset(t, 0, sizeof(t));
+
+	t[0].tx_buf = &addr;
+	t[0].len = 2;
+	spi_message_add_tail(&t[0], &m);
+
+	t[1].tx_buf = buf;
+	t[1].len = len;
+	spi_message_add_tail(&t[1], &m);
+
+	if (len % 2) {
+		last_word = ((u8 *)buf)[len - 1];
+
+		t[2].tx_buf = &last_word;
+		t[2].len = 2;
+		spi_message_add_tail(&t[2], &m);
+	}
+
+	spi_sync(stlc->spi, &m);
+}
+
+static u16 stlc45xx_read16(struct stlc45xx *stlc, unsigned long addr)
+{
+	u16 val;
+
+	stlc45xx_spi_read(stlc, addr, &val, sizeof(val));
+
+	return val;
+}
+
+static u32 stlc45xx_read32(struct stlc45xx *stlc, unsigned long addr)
+{
+	u32 val;
+
+	stlc45xx_spi_read(stlc, addr, &val, sizeof(val));
+
+	return val;
+}
+
+static void stlc45xx_write16(struct stlc45xx *stlc, unsigned long addr, u16 val)
+{
+	stlc45xx_spi_write(stlc, addr, &val, sizeof(val));
+}
+
+static void stlc45xx_write32(struct stlc45xx *stlc, unsigned long addr, u32 val)
+{
+	stlc45xx_spi_write(stlc, addr, &val, sizeof(val));
+}
+
+struct stlc45xx_spi_reg {
+	u16 address;
+	u16 length;
+	char *name;
+};
+
+/* caller must hold tx_lock */
+static void stlc45xx_txbuffer_dump(struct stlc45xx *stlc)
+{
+	struct txbuffer *txbuffer;
+	char *buf, *pos;
+	int buf_len, l, count;
+
+	if (!(DEBUG_LEVEL & DEBUG_TXBUFFER))
+		return;
+
+	stlc45xx_debug(DEBUG_FUNC, "%s()", __func__);
+
+	buf_len = 500;
+	buf = kmalloc(buf_len, GFP_ATOMIC);
+	if (!buf)
+		return;
+
+	pos = buf;
+	count = 0;
+
+	list_for_each_entry(txbuffer, &stlc->txbuffer, buffer_list) {
+		l = snprintf(pos, buf_len, "0x%x-0x%x,",
+			     txbuffer->start, txbuffer->end);
+		/* drop the null byte */
+		pos += l;
+		buf_len -= l;
+		count++;
+	}
+
+	if (count == 0)
+		*pos = '\0';
+	else
+		*--pos = '\0';
+
+	stlc45xx_debug(DEBUG_TXBUFFER, "txbuffer: in buffer %d regions: %s",
+		       count, buf);
+
+	kfree(buf);
+}
+
+/* caller must hold tx_lock */
+static int stlc45xx_txbuffer_find(struct stlc45xx *stlc, size_t len)
+{
+	struct txbuffer *txbuffer;
+	int pos;
+
+	stlc45xx_debug(DEBUG_FUNC, "%s()", __func__);
+
+	pos = FIRMWARE_TXBUFFER_START;
+
+	if (list_empty(&stlc->txbuffer))
+		goto out;
+
+	/*
+	 * the entries in txbuffer must be in the same order as they are in
+	 * the real buffer
+	 */
+	list_for_each_entry(txbuffer, &stlc->txbuffer, buffer_list) {
+		if (pos + len < txbuffer->start)
+			break;
+		pos = ALIGN(txbuffer->end + 1, 4);
+	}
+
+	if (pos + len > FIRMWARE_TXBUFFER_END)
+		/* not enough room */
+		pos = -1;
+
+	stlc45xx_debug(DEBUG_TXBUFFER, "txbuffer: find %d B: 0x%x", len, pos);
+
+out:
+	return pos;
+}
+
+static int stlc45xx_txbuffer_add(struct stlc45xx *stlc,
+				 struct txbuffer *txbuffer)
+{
+	struct txbuffer *r, *prev = NULL;
+
+	if (list_empty(&stlc->txbuffer)) {
+		list_add(&txbuffer->buffer_list, &stlc->txbuffer);
+		return 0;
+	}
+
+	r = list_first_entry(&stlc->txbuffer, struct txbuffer, buffer_list);
+
+	if (txbuffer->start < r->start) {
+		/* add to the beginning of the list */
+		list_add(&txbuffer->buffer_list, &stlc->txbuffer);
+		return 0;
+	}
+
+	prev = NULL;
+	list_for_each_entry(r, &stlc->txbuffer, buffer_list) {
+		/* skip first entry, we checked for that above */
+		if (!prev) {
+			prev = r;
+			continue;
+		}
+
+		/* double-check overlaps */
+		WARN_ON_ONCE(txbuffer->start >= r->start &&
+			     txbuffer->start <= r->end);
+		WARN_ON_ONCE(txbuffer->end >= r->start &&
+			     txbuffer->end <= r->end);
+
+		if (prev->end < txbuffer->start &&
+		    txbuffer->end < r->start) {
+			/* insert at this spot */
+			list_add_tail(&txbuffer->buffer_list, &r->buffer_list);
+			return 0;
+		}
+
+		prev = r;
+	}
+
+	/* not found */
+	list_add_tail(&txbuffer->buffer_list, &stlc->txbuffer);
+
+	return 0;
+
+}
+
+/* caller must hold tx_lock */
+static struct txbuffer *stlc45xx_txbuffer_alloc(struct stlc45xx *stlc,
+						 size_t frame_len)
+{
+	struct txbuffer *entry = NULL;
+	size_t len;
+	int pos;
+
+	stlc45xx_debug(DEBUG_FUNC, "%s()", __func__);
+
+	len = FIRMWARE_TXBUFFER_HEADER + frame_len + FIRMWARE_TXBUFFER_TRAILER;
+	pos = stlc45xx_txbuffer_find(stlc, len);
+
+	if (pos < 0)
+		return NULL;
+
+	WARN_ON_ONCE(pos + len > FIRMWARE_TXBUFFER_END);
+	WARN_ON_ONCE(pos < FIRMWARE_TXBUFFER_START);
+
+	entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
+	entry->start = pos;
+	entry->frame_start = pos + FIRMWARE_TXBUFFER_HEADER;
+	entry->end = entry->start + len - 1;
+
+	stlc45xx_debug(DEBUG_TXBUFFER, "txbuffer: allocated 0x%x-0x%x",
+		       entry->start, entry->end);
+
+	stlc45xx_txbuffer_add(stlc, entry);
+
+	stlc45xx_txbuffer_dump(stlc);
+
+	return entry;
+}
+
+/* caller must hold tx_lock */
+static void stlc45xx_txbuffer_free(struct stlc45xx *stlc,
+				   struct txbuffer *txbuffer)
+{
+	stlc45xx_debug(DEBUG_FUNC, "%s()", __func__);
+
+	stlc45xx_debug(DEBUG_TXBUFFER, "txbuffer: freed 0x%x-0x%x",
+		       txbuffer->start, txbuffer->end);
+
+	list_del(&txbuffer->buffer_list);
+	kfree(txbuffer);
+}
+
+
+static int stlc45xx_wait_bit(struct stlc45xx *stlc, u16 reg, u32 mask,
+			     u32 expected)
+{
+	int i;
+	char buffer[4];
+
+	for (i = 0; i < 2000; i++) {
+		stlc45xx_spi_read(stlc, reg, buffer, sizeof(buffer));
+		if (((*(u32 *)buffer) & mask) == expected)
+			return 1;
+		msleep(1);
+	}
+
+	return 0;
+}
+
+static int stlc45xx_request_firmware(struct stlc45xx *stlc)
+{
+	const struct firmware *fw;
+	int ret;
+
+	/* FIXME: should driver use it's own struct device? */
+	ret = request_firmware(&fw, "3826.arm", &stlc->spi->dev);
+
+	if (ret < 0) {
+		stlc45xx_error("request_firmware() failed: %d", ret);
+		return ret;
+	}
+
+	if (fw->size % 4) {
+		stlc45xx_error("firmware size is not multiple of 32bit: %d",
+			       fw->size);
+		return -EILSEQ; /* Illegal byte sequence  */;
+	}
+
+	if (fw->size < 1000) {
+		stlc45xx_error("firmware is too small: %d", fw->size);
+		return -EILSEQ;
+	}
+
+	stlc->fw = kmemdup(fw->data, fw->size, GFP_KERNEL);
+	if (!stlc->fw) {
+		stlc45xx_error("could not allocate memory for firmware");
+		return -ENOMEM;
+	}
+
+	stlc->fw_len = fw->size;
+
+	release_firmware(fw);
+
+	return 0;
+}
+
+static int stlc45xx_upload_firmware(struct stlc45xx *stlc)
+{
+	struct s_dma_regs dma_regs;
+	unsigned long fw_len, fw_addr;
+	long _fw_len;
+	int ret;
+
+	stlc45xx_debug(DEBUG_FUNC, "%s", __func__);
+
+	if (!stlc->fw) {
+		ret = stlc45xx_request_firmware(stlc);
+		if (ret < 0)
+			return ret;
+	}
+
+	/* stop the device */
+	stlc45xx_write16(stlc, SPI_ADRS_DEV_CTRL_STAT,
+			 SPI_CTRL_STAT_HOST_OVERRIDE | SPI_CTRL_STAT_HOST_RESET
+			 | SPI_CTRL_STAT_START_HALTED);
+
+	msleep(TARGET_BOOT_SLEEP);
+
+	stlc45xx_write16(stlc, SPI_ADRS_DEV_CTRL_STAT,
+			 SPI_CTRL_STAT_HOST_OVERRIDE
+			 | SPI_CTRL_STAT_START_HALTED);
+
+	msleep(TARGET_BOOT_SLEEP);
+
+	fw_addr = FIRMWARE_ADDRESS;
+	fw_len = stlc->fw_len;
+
+	while (fw_len > 0) {
+		_fw_len = (fw_len > SPI_MAX_PACKET_SIZE)
+			? SPI_MAX_PACKET_SIZE : fw_len;
+		dma_regs.cmd = SPI_DMA_WRITE_CTRL_ENABLE;
+		dma_regs.len = cpu_to_le16(_fw_len);
+		dma_regs.addr = cpu_to_le32(fw_addr);
+
+		fw_len -= _fw_len;
+		fw_addr += _fw_len;
+
+		stlc45xx_write16(stlc, SPI_ADRS_DMA_WRITE_CTRL, dma_regs.cmd);
+
+		if (stlc45xx_wait_bit(stlc, SPI_ADRS_DMA_WRITE_CTRL,
+				      HOST_ALLOWED, HOST_ALLOWED) == 0) {
+			stlc45xx_error("fw_upload not allowed to DMA write");
+			return -EAGAIN;
+		}
+
+		stlc45xx_write16(stlc, SPI_ADRS_DMA_WRITE_LEN, dma_regs.len);
+		stlc45xx_write32(stlc, SPI_ADRS_DMA_WRITE_BASE, dma_regs.addr);
+
+		stlc45xx_spi_write(stlc, SPI_ADRS_DMA_DATA, stlc->fw, _fw_len);
+
+		/* FIXME: I think this doesn't work if firmware is large,
+		 * this loop goes to second round. fw->data is not
+		 * increased at all! */
+	}
+
+	BUG_ON(fw_len != 0);
+
+	/* enable host interrupts */
+	stlc45xx_write32(stlc, SPI_ADRS_HOST_INT_EN, SPI_HOST_INTS_DEFAULT);
+
+	/* boot the device */
+	stlc45xx_write16(stlc, SPI_ADRS_DEV_CTRL_STAT,
+			 SPI_CTRL_STAT_HOST_OVERRIDE | SPI_CTRL_STAT_HOST_RESET
+			 | SPI_CTRL_STAT_RAM_BOOT);
+
+	msleep(TARGET_BOOT_SLEEP);
+
+	stlc45xx_write16(stlc, SPI_ADRS_DEV_CTRL_STAT,
+			 SPI_CTRL_STAT_HOST_OVERRIDE | SPI_CTRL_STAT_RAM_BOOT);
+	msleep(TARGET_BOOT_SLEEP);
+
+	return 0;
+}
+
+/* caller must hold tx_lock */
+static void stlc45xx_check_txsent(struct stlc45xx *stlc)
+{
+	struct txbuffer *entry, *n;
+
+	list_for_each_entry_safe(entry, n, &stlc->tx_sent, tx_list) {
+		if (time_after(jiffies, entry->lifetime)) {
+			if (net_ratelimit())
+				stlc45xx_warning("frame 0x%x lifetime exceeded",
+						 entry->start);
+			list_del(&entry->tx_list);
+			skb_pull(entry->skb, entry->header_len);
+			ieee80211_tx_status(stlc->hw, entry->skb);
+			stlc45xx_txbuffer_free(stlc, entry);
+		}
+	}
+}
+
+static void stlc45xx_power_off(struct stlc45xx *stlc)
+{
+	disable_irq(gpio_to_irq(stlc->config->irq_gpio));
+	gpio_set_value(stlc->config->power_gpio, 0);
+}
+
+static void stlc45xx_power_on(struct stlc45xx *stlc)
+{
+	gpio_set_value(stlc->config->power_gpio, 1);
+	enable_irq(gpio_to_irq(stlc->config->irq_gpio));
+
+	/*
+	 * need to wait a while before device can be accessed, the length
+	 * is just a guess
+	 */
+	msleep(10);
+}
+
+/* caller must hold tx_lock */
+static void stlc45xx_flush_queues(struct stlc45xx *stlc)
+{
+	struct txbuffer *entry;
+
+	while (!list_empty(&stlc->tx_sent)) {
+		entry = list_first_entry(&stlc->tx_sent,
+					 struct txbuffer, tx_list);
+		list_del(&entry->tx_list);
+		dev_kfree_skb(entry->skb);
+		stlc45xx_txbuffer_free(stlc, entry);
+	}
+
+	WARN_ON(!list_empty(&stlc->tx_sent));
+
+	while (!list_empty(&stlc->tx_pending)) {
+		entry = list_first_entry(&stlc->tx_pending,
+					 struct txbuffer, tx_list);
+		list_del(&entry->tx_list);
+		dev_kfree_skb(entry->skb);
+		stlc45xx_txbuffer_free(stlc, entry);
+	}
+
+	WARN_ON(!list_empty(&stlc->tx_pending));
+	WARN_ON(!list_empty(&stlc->txbuffer));
+}
+
+static void stlc45xx_work_reset(struct work_struct *work)
+{
+	struct stlc45xx *stlc = container_of(work, struct stlc45xx,
+					     work_reset);
+
+	mutex_lock(&stlc->mutex);
+
+	if (stlc->fw_state != FW_STATE_RESET)
+		goto out;
+
+	stlc45xx_power_off(stlc);
+
+	mutex_unlock(&stlc->mutex);
+
+	/* wait that all work_structs have finished, we can't hold
+	 * stlc->mutex to avoid deadlock */
+	cancel_work_sync(&stlc->work);
+
+	/* FIXME: find out good value to wait for chip power down */
+	msleep(100);
+
+	mutex_lock(&stlc->mutex);
+
+	/* FIXME: we should gracefully handle if the state has changed
+	 * after re-acquiring mutex */
+	WARN_ON(stlc->fw_state != FW_STATE_RESET);
+
+	spin_lock_bh(&stlc->tx_lock);
+	stlc45xx_flush_queues(stlc);
+	spin_unlock_bh(&stlc->tx_lock);
+
+	stlc->fw_state = FW_STATE_RESETTING;
+
+	stlc45xx_power_on(stlc);
+	stlc45xx_upload_firmware(stlc);
+
+out:
+	mutex_unlock(&stlc->mutex);
+}
+
+/* caller must hold mutex */
+static void stlc45xx_reset(struct stlc45xx *stlc)
+{
+	stlc45xx_warning("resetting firmware");
+	stlc->fw_state = FW_STATE_RESET;
+	ieee80211_stop_queues(stlc->hw);
+	queue_work(stlc->hw->workqueue, &stlc->work_reset);
+}
+
+static void stlc45xx_work_tx_timeout(struct work_struct *work)
+{
+	struct stlc45xx *stlc = container_of(work, struct stlc45xx,
+					     work_tx_timeout.work);
+
+	stlc45xx_warning("tx timeout");
+
+	mutex_lock(&stlc->mutex);
+
+	if (stlc->fw_state != FW_STATE_READY)
+		goto out;
+
+	stlc45xx_reset(stlc);
+
+out:
+	mutex_unlock(&stlc->mutex);
+}
+
+static void stlc45xx_int_ack(struct stlc45xx *stlc, u32 val)
+{
+	stlc45xx_debug(DEBUG_FUNC, "%s", __func__);
+
+	stlc45xx_write32(stlc, SPI_ADRS_HOST_INT_ACK, val);
+}
+
+static void stlc45xx_wakeup(struct stlc45xx *stlc)
+{
+	unsigned long timeout;
+	u32 ints;
+
+	stlc45xx_debug(DEBUG_FUNC, "%s", __func__);
+
+	/* wake the chip */
+	stlc45xx_write32(stlc, SPI_ADRS_ARM_INTERRUPTS, SPI_TARGET_INT_WAKEUP);
+
+	/* And wait for the READY interrupt */
+	timeout = jiffies + HZ;
+
+	ints = stlc45xx_read32(stlc, SPI_ADRS_HOST_INTERRUPTS);
+	while (!(ints & SPI_HOST_INT_READY)) {
+		if (time_after(jiffies, timeout))
+				goto out;
+		ints = stlc45xx_read32(stlc, SPI_ADRS_HOST_INTERRUPTS);
+	}
+
+	stlc45xx_int_ack(stlc, SPI_HOST_INT_READY);
+
+out:
+	return;
+}
+
+static void stlc45xx_sleep(struct stlc45xx *stlc)
+{
+	stlc45xx_debug(DEBUG_FUNC, "%s", __func__);
+
+	stlc45xx_write32(stlc, SPI_ADRS_ARM_INTERRUPTS, SPI_TARGET_INT_SLEEP);
+}
+
+static void stlc45xx_int_ready(struct stlc45xx *stlc)
+{
+	stlc45xx_debug(DEBUG_FUNC, "%s", __func__);
+
+	stlc45xx_write32(stlc, SPI_ADRS_HOST_INT_EN,
+			 SPI_HOST_INT_UPDATE | SPI_HOST_INT_SW_UPDATE);
+
+	switch (stlc->fw_state) {
+	case FW_STATE_BOOTING:
+		stlc->fw_state = FW_STATE_READY;
+		complete(&stlc->fw_comp);
+		break;
+	case FW_STATE_RESETTING:
+		stlc->fw_state = FW_STATE_READY;
+
+		stlc45xx_tx_scan(stlc);
+		stlc45xx_tx_setup(stlc);
+		stlc45xx_tx_edcf(stlc);
+
+		ieee80211_wake_queues(stlc->hw);
+		break;
+	default:
+		break;
+	}
+}
+
+static int stlc45xx_rx_txack(struct stlc45xx *stlc, struct sk_buff *skb)
+{
+	struct ieee80211_tx_info *info;
+	struct s_lm_control *control;
+	struct s_lmo_tx *tx;
+	struct txbuffer *entry;
+	int found = 0;
+
+	stlc45xx_debug(DEBUG_FUNC, "%s", __func__);
+
+	control = (struct s_lm_control *) skb->data;
+	tx = (struct s_lmo_tx *) (control + 1);
+
+	if (list_empty(&stlc->tx_sent)) {
+		if (net_ratelimit())
+			stlc45xx_warning("no frames waiting for "
+					 "acknowledgement");
+		return -1;
+	}
+
+	list_for_each_entry(entry, &stlc->tx_sent, tx_list) {
+		if (control->handle == entry->handle) {
+			found = 1;
+			break;
+		}
+	}
+
+	if (!found) {
+		if (net_ratelimit())
+			stlc45xx_warning("couldn't find frame for tx ack 0x%x",
+					 control->handle);
+		return -1;
+	}
+
+	stlc45xx_debug(DEBUG_TX, "TX ACK 0x%x", entry->handle);
+
+	if (entry->status_needed) {
+		info = IEEE80211_SKB_CB(entry->skb);
+
+		if (!(tx->flags & LM_TX_FAILED)) {
+			/* frame was acked */
+			info->flags |= IEEE80211_TX_STAT_ACK;
+			info->status.ack_signal = tx->rcpi / 2 - 110;
+		}
+
+		skb_pull(entry->skb, entry->header_len);
+
+		ieee80211_tx_status(stlc->hw, entry->skb);
+	}
+
+	list_del(&entry->tx_list);
+
+	stlc45xx_check_txsent(stlc);
+	if (list_empty(&stlc->tx_sent))
+		/* there are no pending frames, we can stop the tx timeout
+		 * timer */
+		cancel_delayed_work(&stlc->work_tx_timeout);
+
+	spin_lock_bh(&stlc->tx_lock);
+
+	stlc45xx_txbuffer_free(stlc, entry);
+
+	if (stlc->tx_queue_stopped &&
+	    stlc45xx_txbuffer_find(stlc, MAX_FRAME_LEN) != -1) {
+		stlc45xx_debug(DEBUG_QUEUE, "room in tx buffer, waking queues");
+		ieee80211_wake_queues(stlc->hw);
+		stlc->tx_queue_stopped = 0;
+	}
+
+	spin_unlock_bh(&stlc->tx_lock);
+
+	return 0;
+}
+
+static int stlc45xx_rx_control(struct stlc45xx *stlc, struct sk_buff *skb)
+{
+	struct s_lm_control *control;
+	int ret = 0;
+
+	stlc45xx_debug(DEBUG_FUNC, "%s", __func__);
+
+	control = (struct s_lm_control *) skb->data;
+
+	switch (control->oid) {
+	case LM_OID_TX:
+		ret = stlc45xx_rx_txack(stlc, skb);
+		break;
+	case LM_OID_SETUP:
+	case LM_OID_SCAN:
+	case LM_OID_TRAP:
+	case LM_OID_EDCF:
+	case LM_OID_KEYCACHE:
+	case LM_OID_PSM:
+	case LM_OID_STATS:
+	case LM_OID_LED:
+	default:
+		stlc45xx_warning("unhandled rx control oid %d\n",
+				 control->oid);
+		break;
+	}
+
+	dev_kfree_skb(skb);
+
+	return ret;
+}
+
+/* copied from mac80211 */
+static void stlc45xx_parse_elems(u8 *start, size_t len,
+				 struct stlc45xx_ie_tim **tim,
+				 size_t *tim_len)
+{
+	size_t left = len;
+	u8 *pos = start;
+
+	while (left >= 2) {
+		u8 id, elen;
+
+		id = *pos++;
+		elen = *pos++;
+		left -= 2;
+
+		if (elen > left)
+			return;
+
+		switch (id) {
+		case WLAN_EID_TIM:
+			*tim = (struct stlc45xx_ie_tim *) pos;
+			*tim_len = elen;
+			break;
+		default:
+			break;
+		}
+
+		left -= elen;
+		pos += elen;
+	}
+}
+
+/*
+ * mac80211 doesn't have support for asking frames with PS-Poll, so let's
+ * implement in the driver for now. We have to add support to mac80211
+ * later.
+ */
+static int stlc45xx_check_more_data(struct stlc45xx *stlc, struct sk_buff *skb)
+{
+	struct s_lm_data_in *data = (struct s_lm_data_in *) skb->data;
+	struct ieee80211_hdr *hdr;
+	size_t len;
+	u16 fc;
+
+	hdr = (void *) skb->data + sizeof(*data);
+	len = skb->len - sizeof(*data);
+
+	/* minimum frame length is the null frame length 24 bytes */
+	if (len < 24) {
+		stlc45xx_warning("invalid frame length when checking for "
+				 "more data");
+		return -EINVAL;
+	}
+
+	fc = le16_to_cpu(hdr->frame_control);
+	if (!(fc & IEEE80211_FCTL_FROMDS))
+		/* this is not from DS */
+		return 0;
+
+	if (compare_ether_addr(hdr->addr1, stlc->mac_addr) != 0)
+		/* the frame was not for us */
+		return 0;
+
+	if (!(fc & IEEE80211_FCTL_MOREDATA)) {
+		/* AP has no more frames buffered for us */
+		stlc45xx_debug(DEBUG_PSM, "all buffered frames retrieved");
+		stlc->pspolling = false;
+		return 0;
+	}
+
+	/* MOREDATA bit is set, let's ask for a new frame from the AP */
+	stlc45xx_tx_pspoll(stlc, stlc->psm);
+
+	return 0;
+}
+
+/*
+ * mac80211 cannot read TIM from beacons, so let's add a hack to the
+ * driver. We have to add support to mac80211 later.
+ */
+static int stlc45xx_rx_data_beacon(struct stlc45xx *stlc, struct sk_buff *skb)
+{
+	struct s_lm_data_in *data = (struct s_lm_data_in *) skb->data;
+	size_t len = skb->len, tim_len = 0, baselen, pvbmap_len;
+	struct ieee80211_mgmt *mgmt;
+	struct stlc45xx_ie_tim *tim = NULL;
+	int bmap_offset, index, aid_bit;
+
+	mgmt = (void *) skb->data + sizeof(*data);
+
+	baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt;
+	if (baselen > len) {
+		stlc45xx_warning("invalid baselen in beacon");
+		return -EINVAL;
+	}
+
+	stlc45xx_parse_elems(mgmt->u.beacon.variable, len - baselen, &tim,
+			     &tim_len);
+
+	if (!tim) {
+		stlc45xx_warning("didn't find tim from a beacon");
+		return -EINVAL;
+	}
+
+	bmap_offset = tim->bmap_control & 0xfe;
+	index = stlc->aid / 8 - bmap_offset;
+
+	pvbmap_len = tim_len - 3;
+	if (index > pvbmap_len)
+		return -EINVAL;
+
+	aid_bit = !!(tim->pvbmap[index] & (1 << stlc->aid % 8));
+
+	stlc45xx_debug(DEBUG_PSM, "fc 0x%x duration %d seq %d dtim %u "
+		       "bmap_control 0x%x aid_bit %d",
+		       mgmt->frame_control, mgmt->duration, mgmt->seq_ctrl >> 4,
+		       tim->dtim_count, tim->bmap_control, aid_bit);
+
+	if (!aid_bit)
+		return 0;
+
+	stlc->pspolling = true;
+	stlc45xx_tx_pspoll(stlc, stlc->psm);
+
+	return 0;
+}
+
+static int stlc45xx_rx_data(struct stlc45xx *stlc, struct sk_buff *skb)
+{
+	struct ieee80211_rx_status status;
+	struct s_lm_data_in *data = (struct s_lm_data_in *) skb->data;
+	int align = 0;
+	u8 *p, align_len;
+	u16 len;
+
+	stlc45xx_debug(DEBUG_FUNC, "%s", __func__);
+
+	if (stlc->psm) {
+		if (data->flags & LM_IN_BEACON)
+			stlc45xx_rx_data_beacon(stlc, skb);
+		else if (stlc->pspolling && (data->flags & LM_IN_DATA))
+			stlc45xx_check_more_data(stlc, skb);
+	}
+
+	memset(&status, 0, sizeof(status));
+
+	status.freq = data->frequency;
+	status.signal = data->rcpi / 2 - 110;
+
+	/* let's assume that maximum rcpi value is 140 (= 35 dBm) */
+	status.qual = data->rcpi * 100 / 140;
+
+	status.band = IEEE80211_BAND_2GHZ;
+
+	/*
+	 * FIXME: this gives warning from __ieee80211_rx()
+	 *
+	 * status.rate_idx = data->rate;
+	 */
+
+	len = data->length;
+
+	if (data->flags & LM_FLAG_ALIGN)
+		align = 1;
+
+	skb_pull(skb, sizeof(*data));
+
+	if (align) {
+		p = skb->data;
+		align_len = *p;
+		skb_pull(skb, align_len);
+	}
+
+	skb_trim(skb, len);
+
+	stlc45xx_debug(DEBUG_RX, "rx data 0x%p %d B", skb->data, skb->len);
+	stlc45xx_dump(DEBUG_RX_CONTENT, skb->data, skb->len);
+
+	ieee80211_rx(stlc->hw, skb, &status);
+
+	return 0;
+}
+
+
+
+static int stlc45xx_rx(struct stlc45xx *stlc)
+{
+	struct s_lm_control *control;
+	struct sk_buff *skb;
+	int ret;
+	u16 len;
+
+	stlc45xx_debug(DEBUG_FUNC, "%s", __func__);
+
+	stlc45xx_wakeup(stlc);
+
+	/* dummy read to flush SPI DMA controller bug */
+	stlc45xx_read16(stlc, SPI_ADRS_GEN_PURP_1);
+
+	len = stlc45xx_read16(stlc, SPI_ADRS_DMA_DATA);
+
+	if (len == 0) {
+		stlc45xx_warning("rx request of zero bytes");
+		return 0;
+	}
+
+	skb = dev_alloc_skb(len);
+	if (!skb) {
+		stlc45xx_warning("could not alloc skb");
+		return 0;
+	}
+
+	stlc45xx_spi_read(stlc, SPI_ADRS_DMA_DATA, skb_put(skb, len), len);
+
+	stlc45xx_sleep(stlc);
+
+	stlc45xx_debug(DEBUG_RX, "rx frame 0x%p %d B", skb->data, skb->len);
+	stlc45xx_dump(DEBUG_RX_CONTENT, skb->data, skb->len);
+
+	control = (struct s_lm_control *) skb->data;
+
+	if (control->flags & LM_FLAG_CONTROL)
+		ret = stlc45xx_rx_control(stlc, skb);
+	else
+		ret = stlc45xx_rx_data(stlc, skb);
+
+	return ret;
+}
+
+
+static irqreturn_t stlc45xx_interrupt(int irq, void *config)
+{
+	struct spi_device *spi = config;
+	struct stlc45xx *stlc = dev_get_drvdata(&spi->dev);
+
+	stlc45xx_debug(DEBUG_IRQ, "IRQ");
+
+	queue_work(stlc->hw->workqueue, &stlc->work);
+
+	return IRQ_HANDLED;
+}
+
+static int stlc45xx_tx_frame(struct stlc45xx *stlc, u32 address,
+			     void *buf, size_t len)
+{
+	struct s_dma_regs dma_regs;
+	unsigned long timeout;
+	int ret = 0;
+	u32 ints;
+
+	stlc->tx_frames++;
+
+	stlc45xx_debug(DEBUG_FUNC, "%s", __func__);
+
+	stlc45xx_debug(DEBUG_TX, "tx frame 0x%p %d B", buf, len);
+	stlc45xx_dump(DEBUG_TX_CONTENT, buf, len);
+
+	stlc45xx_wakeup(stlc);
+
+	dma_regs.cmd  = SPI_DMA_WRITE_CTRL_ENABLE;
+	dma_regs.len  = cpu_to_le16(len);
+	dma_regs.addr = cpu_to_le32(address);
+
+	stlc45xx_spi_write(stlc, SPI_ADRS_DMA_WRITE_CTRL, &dma_regs,
+			   sizeof(dma_regs));
+
+	stlc45xx_spi_write(stlc, SPI_ADRS_DMA_DATA, buf, len);
+
+	timeout = jiffies + 2 * HZ;
+	ints = stlc45xx_read32(stlc, SPI_ADRS_HOST_INTERRUPTS);
+	while (!(ints & SPI_HOST_INT_WR_READY)) {
+		if (time_after(jiffies, timeout)) {
+			stlc45xx_warning("WR_READY timeout");
+			ret = -1;
+			goto out;
+		}
+		ints = stlc45xx_read32(stlc, SPI_ADRS_HOST_INTERRUPTS);
+	}
+
+	stlc45xx_int_ack(stlc, SPI_HOST_INT_WR_READY);
+
+	stlc45xx_sleep(stlc);
+
+out:
+	return ret;
+}
+
+static int stlc45xx_wq_tx(struct stlc45xx *stlc)
+{
+	struct txbuffer *entry;
+	int ret = 0;
+
+	spin_lock_bh(&stlc->tx_lock);
+
+	while (!list_empty(&stlc->tx_pending)) {
+		entry = list_entry(stlc->tx_pending.next,
+				   struct txbuffer, tx_list);
+
+		list_del_init(&entry->tx_list);
+
+		spin_unlock_bh(&stlc->tx_lock);
+
+		ret = stlc45xx_tx_frame(stlc, entry->frame_start,
+					entry->skb->data, entry->skb->len);
+
+		spin_lock_bh(&stlc->tx_lock);
+
+		if (ret < 0) {
+			/* frame transfer to firmware buffer failed */
+			/* FIXME: report this to mac80211 */
+			dev_kfree_skb(entry->skb);
+			stlc45xx_txbuffer_free(stlc, entry);
+			goto out;
+		}
+
+		list_add(&entry->tx_list, &stlc->tx_sent);
+		queue_delayed_work(stlc->hw->workqueue,
+				   &stlc->work_tx_timeout,
+				   msecs_to_jiffies(TX_TIMEOUT));
+	}
+
+out:
+	spin_unlock_bh(&stlc->tx_lock);
+	return ret;
+}
+
+static void stlc45xx_work(struct work_struct *work)
+{
+	struct stlc45xx *stlc = container_of(work, struct stlc45xx, work);
+	u32 ints;
+	int ret;
+
+	stlc45xx_debug(DEBUG_FUNC, "%s", __func__);
+
+	mutex_lock(&stlc->mutex);
+
+	if (stlc->fw_state == FW_STATE_OFF &&
+	    stlc->fw_state == FW_STATE_RESET)
+		goto out;
+
+	ints = stlc45xx_read32(stlc, SPI_ADRS_HOST_INTERRUPTS);
+	stlc45xx_debug(DEBUG_BH, "begin host_ints 0x%08x", ints);
+
+	if (ints & SPI_HOST_INT_READY) {
+		stlc45xx_int_ready(stlc);
+		stlc45xx_int_ack(stlc, SPI_HOST_INT_READY);
+	}
+
+	if (stlc->fw_state != FW_STATE_READY)
+		goto out;
+
+	if (ints & SPI_HOST_INT_UPDATE) {
+		stlc45xx_int_ack(stlc, SPI_HOST_INT_UPDATE);
+		ret = stlc45xx_rx(stlc);
+		if (ret < 0) {
+			stlc45xx_reset(stlc);
+			goto out;
+		}
+	}
+	if (ints & SPI_HOST_INT_SW_UPDATE) {
+		stlc45xx_int_ack(stlc, SPI_HOST_INT_SW_UPDATE);
+		ret = stlc45xx_rx(stlc);
+		if (ret < 0) {
+			stlc45xx_reset(stlc);
+			goto out;
+		}
+	}
+
+	ret = stlc45xx_wq_tx(stlc);
+	if (ret < 0) {
+		stlc45xx_reset(stlc);
+		goto out;
+	}
+
+	ints = stlc45xx_read32(stlc, SPI_ADRS_HOST_INTERRUPTS);
+	stlc45xx_debug(DEBUG_BH, "end host_ints 0x%08x", ints);
+
+out:
+	mutex_unlock(&stlc->mutex);
+}
+
+static void stlc45xx_tx_edcf(struct stlc45xx *stlc)
+{
+	struct s_lm_control *control;
+	struct s_lmo_edcf *edcf;
+	size_t len, edcf_len;
+
+	stlc45xx_debug(DEBUG_FUNC, "%s", __func__);
+
+	edcf_len = sizeof(*edcf);
+	len = sizeof(*control) + edcf_len;
+	control = kzalloc(len, GFP_KERNEL);
+	edcf = (struct s_lmo_edcf *) (control + 1);
+
+	control->flags = LM_FLAG_CONTROL | LM_CTRL_OPSET;
+	control->length = edcf_len;
+	control->oid = LM_OID_EDCF;
+
+	edcf->slottime = 0x14;
+	edcf->sifs = 10;
+	edcf->eofpad = 6;
+	edcf->maxburst = 1500;
+
+	edcf->queues[0].aifs = 2;
+	edcf->queues[0].pad0 = 1;
+	edcf->queues[0].cwmin = 3;
+	edcf->queues[0].cwmax = 7;
+	edcf->queues[0].txop = 47;
+	edcf->queues[1].aifs = 2;
+	edcf->queues[1].pad0 = 0;
+	edcf->queues[1].cwmin = 7;
+	edcf->queues[1].cwmax = 15;
+	edcf->queues[1].txop = 94;
+	edcf->queues[2].aifs = 3;
+	edcf->queues[2].pad0 = 0;
+	edcf->queues[2].cwmin = 15;
+	edcf->queues[2].cwmax = 1023;
+	edcf->queues[2].txop = 0;
+	edcf->queues[3].aifs = 7;
+	edcf->queues[3].pad0 = 0;
+	edcf->queues[3].cwmin = 15;
+	edcf->queues[3].cwmax = 1023;
+	edcf->queues[3].txop = 0;
+	edcf->queues[4].aifs = 13;
+	edcf->queues[4].pad0 = 99;
+	edcf->queues[4].cwmin = 3437;
+	edcf->queues[4].cwmax = 512;
+	edcf->queues[4].txop = 12;
+	edcf->queues[5].aifs = 142;
+	edcf->queues[5].pad0 = 109;
+	edcf->queues[5].cwmin = 8756;
+	edcf->queues[5].cwmax = 6;
+	edcf->queues[5].txop = 0;
+	edcf->queues[6].aifs = 4;
+	edcf->queues[6].pad0 = 0;
+	edcf->queues[6].cwmin = 0;
+	edcf->queues[6].cwmax = 58705;
+	edcf->queues[6].txop = 25716;
+	edcf->queues[7].aifs = 0;
+	edcf->queues[7].pad0 = 0;
+	edcf->queues[7].cwmin = 0;
+	edcf->queues[7].cwmax = 0;
+	edcf->queues[7].txop = 0;
+
+	stlc45xx_tx_frame(stlc, FIRMWARE_CONFIG_START, control, len);
+
+	kfree(control);
+}
+
+static void stlc45xx_tx_setup(struct stlc45xx *stlc)
+{
+	struct s_lm_control *control;
+	struct s_lmo_setup *setup;
+	size_t len, setup_len;
+
+	stlc45xx_debug(DEBUG_FUNC, "%s", __func__);
+
+	setup_len = sizeof(*setup);
+	len = sizeof(*control) + setup_len;
+	control = kzalloc(len, GFP_KERNEL);
+	setup = (struct s_lmo_setup *) (control + 1);
+
+	control->flags = LM_FLAG_CONTROL | LM_CTRL_OPSET;
+	control->length = setup_len;
+	control->oid = LM_OID_SETUP;
+
+	setup->flags = LM_SETUP_INFRA;
+	setup->antenna = 2;
+	setup->rx_align = 0;
+	setup->rx_buffer = FIRMWARE_RXBUFFER_START;
+	setup->rx_mtu = FIRMWARE_MTU;
+	setup->frontend = 5;
+	setup->timeout = 0;
+	setup->truncate = 48896;
+	setup->bratemask = 0xffffffff;
+	setup->ref_clock = 644245094;
+	setup->lpf_bandwidth = 65535;
+	setup->osc_start_delay = 65535;
+
+	memcpy(setup->macaddr, stlc->mac_addr, ETH_ALEN);
+	memcpy(setup->bssid, stlc->bssid, ETH_ALEN);
+
+	stlc45xx_tx_frame(stlc, FIRMWARE_CONFIG_START, control, len);
+
+	kfree(control);
+}
+
+static void stlc45xx_tx_scan(struct stlc45xx *stlc)
+{
+	struct s_lm_control *control;
+	struct s_lmo_scan *scan;
+	size_t len, scan_len;
+
+	stlc45xx_debug(DEBUG_FUNC, "%s", __func__);
+
+	scan_len = sizeof(*scan);
+	len = sizeof(*control) + scan_len;
+	control = kzalloc(len, GFP_KERNEL);
+	scan = (struct s_lmo_scan *) (control + 1);
+
+	control->flags = LM_FLAG_CONTROL | LM_CTRL_OPSET;
+	control->length = scan_len;
+	control->oid = LM_OID_SCAN;
+
+	scan->flags = LM_SCAN_EXIT;
+	scan->bratemask = 0x15f;
+	scan->aloft[0] = 3;
+	scan->aloft[1] = 3;
+	scan->aloft[2] = 1;
+	scan->aloft[3] = 0;
+	scan->aloft[4] = 0;
+	scan->aloft[5] = 0;
+	scan->aloft[6] = 0;
+	scan->aloft[7] = 0;
+
+	memcpy(&scan->rssical, &stlc->cal_rssi[(stlc->channel - 1) *
+					       RSSI_CAL_LEN],
+	       RSSI_CAL_LEN);
+	memcpy(&scan->channel, &stlc->cal_channels[(stlc->channel - 1) *
+						   CHANNEL_CAL_LEN],
+	       CHANNEL_CAL_LEN);
+
+	stlc45xx_tx_frame(stlc, FIRMWARE_CONFIG_START, control, len);
+
+	kfree(control);
+}
+
+/*
+ * caller must hold mutex
+ */
+static int stlc45xx_tx_pspoll(struct stlc45xx *stlc, bool powersave)
+{
+	struct ieee80211_hdr *pspoll;
+	int payload_len, padding, i;
+	struct s_lm_data_out *data;
+	struct txbuffer *entry;
+	DECLARE_MAC_BUF(mac);
+	struct sk_buff *skb;
+	char *payload;
+	u16 fc;
+
+	skb = dev_alloc_skb(stlc->hw->extra_tx_headroom + 16);
+	if (!skb) {
+		stlc45xx_warning("failed to allocate pspoll frame");
+		return -ENOMEM;
+	}
+	skb_reserve(skb, stlc->hw->extra_tx_headroom);
+
+	pspoll = (struct ieee80211_hdr *) skb_put(skb, 16);
+	memset(pspoll, 0, 16);
+	fc = IEEE80211_FTYPE_CTL | IEEE80211_STYPE_PSPOLL;
+	if (powersave)
+		fc |= IEEE80211_FCTL_PM;
+	pspoll->frame_control = cpu_to_le16(fc);
+	pspoll->duration_id = cpu_to_le16(stlc->aid);
+
+	/* aid in PS-Poll has its two MSBs each set to 1 */
+	pspoll->duration_id |= cpu_to_le16(1 << 15) | cpu_to_le16(1 << 14);
+
+	memcpy(pspoll->addr1, stlc->bssid, ETH_ALEN);
+	memcpy(pspoll->addr2, stlc->mac_addr, ETH_ALEN);
+
+	stlc45xx_debug(DEBUG_PSM, "sending PS-Poll frame to %s (powersave %d, "
+		       "fc 0x%x, aid %d)", print_mac(mac, pspoll->addr1),
+		       powersave, fc, stlc->aid);
+
+	spin_lock_bh(&stlc->tx_lock);
+
+	entry = stlc45xx_txbuffer_alloc(stlc, skb->len);
+
+	spin_unlock_bh(&stlc->tx_lock);
+
+	if (!entry) {
+		/*
+		 * The queue should be stopped before the firmware buffer
+		 * is full, so firmware buffer should always have enough
+		 * space.
+		 *
+		 * But I'm too lazy and omit it for now.
+		 */
+		if (net_ratelimit())
+			stlc45xx_warning("firmware tx buffer full is full "
+					 "for null frame");
+		return -ENOSPC;
+	}
+
+	payload = skb->data;
+	payload_len = skb->len;
+	padding = (int) (skb->data - sizeof(*data)) & 3;
+	entry->header_len = sizeof(*data) + padding;
+
+	entry->skb = skb;
+	entry->status_needed = false;
+	entry->handle = (u32) skb;
+	entry->lifetime = jiffies + msecs_to_jiffies(TX_FRAME_LIFETIME);
+
+	stlc45xx_debug(DEBUG_TX, "tx data 0x%x (0x%p payload %d B "
+		       "padding %d header_len %d)",
+		       entry->handle, payload, payload_len, padding,
+		       entry->header_len);
+	stlc45xx_dump(DEBUG_TX_CONTENT, payload, payload_len);
+
+	data = (struct s_lm_data_out *) skb_push(skb, entry->header_len);
+
+	memset(data, 0, entry->header_len);
+
+	if (padding)
+		data->flags = LM_FLAG_ALIGN;
+
+	data->flags = LM_OUT_BURST;
+	data->length = payload_len;
+	data->handle = entry->handle;
+	data->aid = 1;
+	data->rts_retries = 7;
+	data->retries = 7;
+	data->aloft_ctrl = 0;
+	data->crypt_offset = 58;
+	data->keytype = 0;
+	data->keylen = 0;
+	data->queue = LM_QUEUE_DATA3;
+	data->backlog = 32;
+	data->antenna = 2;
+	data->cts = 3;
+	data->power = 127;
+
+	for (i = 0; i < 8; i++)
+		data->aloft[i] = 0;
+
+	/*
+	 * check if there's enough space in tx buffer
+	 *
+	 * FIXME: ignored for now
+	 */
+
+	stlc45xx_tx_frame(stlc, entry->start, skb->data, skb->len);
+
+	list_add(&entry->tx_list, &stlc->tx_sent);
+
+	return 0;
+}
+
+/*
+ * caller must hold mutex
+ *
+ * shamelessly stolen from mac80211/ieee80211_send_nullfunc
+ */
+static int stlc45xx_tx_nullfunc(struct stlc45xx *stlc, bool powersave)
+{
+	struct ieee80211_hdr *nullfunc;
+	int payload_len, padding, i;
+	struct s_lm_data_out *data;
+	struct txbuffer *entry;
+	DECLARE_MAC_BUF(mac);
+	struct sk_buff *skb;
+	char *payload;
+	u16 fc;
+
+	skb = dev_alloc_skb(stlc->hw->extra_tx_headroom + 24);
+	if (!skb) {
+		stlc45xx_warning("failed to allocate buffer for null frame\n");
+		return -ENOMEM;
+	}
+	skb_reserve(skb, stlc->hw->extra_tx_headroom);
+
+	nullfunc = (struct ieee80211_hdr *) skb_put(skb, 24);
+	memset(nullfunc, 0, 24);
+	fc = IEEE80211_FTYPE_DATA | IEEE80211_STYPE_NULLFUNC |
+	     IEEE80211_FCTL_TODS;
+
+	if (powersave)
+		fc |= IEEE80211_FCTL_PM;
+
+	nullfunc->frame_control = cpu_to_le16(fc);
+	memcpy(nullfunc->addr1, stlc->bssid, ETH_ALEN);
+	memcpy(nullfunc->addr2, stlc->mac_addr, ETH_ALEN);
+	memcpy(nullfunc->addr3, stlc->bssid, ETH_ALEN);
+
+	stlc45xx_debug(DEBUG_PSM, "sending Null frame to %s (powersave %d, "
+		       "fc 0x%x)",
+		       print_mac(mac, nullfunc->addr1), powersave, fc);
+
+	spin_lock_bh(&stlc->tx_lock);
+
+	entry = stlc45xx_txbuffer_alloc(stlc, skb->len);
+
+	spin_unlock_bh(&stlc->tx_lock);
+
+	if (!entry) {
+		/*
+		 * The queue should be stopped before the firmware buffer
+		 * is full, so firmware buffer should always have enough
+		 * space.
+		 *
+		 * But I'm too lazy and omit it for now.
+		 */
+		if (net_ratelimit())
+			stlc45xx_warning("firmware tx buffer full is full "
+					 "for null frame");
+		return -ENOSPC;
+	}
+
+	payload = skb->data;
+	payload_len = skb->len;
+	padding = (int) (skb->data - sizeof(*data)) & 3;
+	entry->header_len = sizeof(*data) + padding;
+
+	entry->skb = skb;
+	entry->status_needed = false;
+	entry->handle = (u32) skb;
+	entry->lifetime = jiffies + msecs_to_jiffies(TX_FRAME_LIFETIME);
+
+	stlc45xx_debug(DEBUG_TX, "tx data 0x%x (0x%p payload %d B "
+		       "padding %d header_len %d)",
+		       entry->handle, payload, payload_len, padding,
+		       entry->header_len);
+	stlc45xx_dump(DEBUG_TX_CONTENT, payload, payload_len);
+
+	data = (struct s_lm_data_out *) skb_push(skb, entry->header_len);
+
+	memset(data, 0, entry->header_len);
+
+	if (padding)
+		data->flags = LM_FLAG_ALIGN;
+
+	data->flags = LM_OUT_BURST;
+	data->length = payload_len;
+	data->handle = entry->handle;
+	data->aid = 1;
+	data->rts_retries = 7;
+	data->retries = 7;
+	data->aloft_ctrl = 0;
+	data->crypt_offset = 58;
+	data->keytype = 0;
+	data->keylen = 0;
+	data->queue = LM_QUEUE_DATA3;
+	data->backlog = 32;
+	data->antenna = 2;
+	data->cts = 3;
+	data->power = 127;
+
+	for (i = 0; i < 8; i++)
+		data->aloft[i] = 0;
+
+	/*
+	 * check if there's enough space in tx buffer
+	 *
+	 * FIXME: ignored for now
+	 */
+
+	stlc45xx_tx_frame(stlc, entry->start, skb->data, skb->len);
+
+	list_add(&entry->tx_list, &stlc->tx_sent);
+
+	return 0;
+}
+
+/* caller must hold mutex */
+static void stlc45xx_tx_psm(struct stlc45xx *stlc, bool enable)
+{
+	struct s_lm_control *control;
+	struct s_lmo_psm *psm;
+	size_t len, psm_len;
+
+	WARN_ON(!stlc->associated);
+	WARN_ON(stlc->aid < 1);
+	WARN_ON(stlc->aid > 2007);
+
+	psm_len = sizeof(*psm);
+	len = sizeof(*control) + psm_len;
+	control = kzalloc(len, GFP_KERNEL);
+	psm = (struct s_lmo_psm *) (control + 1);
+
+	control->flags = LM_FLAG_CONTROL | LM_CTRL_OPSET;
+	control->length = psm_len;
+	control->oid = LM_OID_PSM;
+
+	if (enable)
+		psm->flags |= LM_PSM;
+
+	psm->aid = stlc->aid;
+
+	psm->beacon_rcpi_skip_max = 60;
+
+	psm->intervals[0].interval = 1;
+	psm->intervals[0].periods = 1;
+	psm->intervals[1].interval = 1;
+	psm->intervals[1].periods = 1;
+	psm->intervals[2].interval = 1;
+	psm->intervals[2].periods = 1;
+	psm->intervals[3].interval = 1;
+	psm->intervals[3].periods = 1;
+
+	psm->nr = 0;
+	psm->exclude[0] = 0;
+
+	stlc45xx_debug(DEBUG_PSM, "sending LM_OID_PSM (aid %d, interval %d)",
+		       psm->aid, psm->intervals[0].interval);
+
+	stlc45xx_tx_frame(stlc, FIRMWARE_CONFIG_START, control, len);
+
+	kfree(control);
+}
+
+static int stlc45xx_op_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
+{
+	struct stlc45xx *stlc = hw->priv;
+	struct ieee80211_tx_info *info;
+	struct ieee80211_rate *rate;
+	int payload_len, padding, i;
+	struct s_lm_data_out *data;
+	struct txbuffer *entry;
+	char *payload;
+
+	stlc45xx_debug(DEBUG_FUNC, "%s", __func__);
+
+	spin_lock_bh(&stlc->tx_lock);
+
+	entry = stlc45xx_txbuffer_alloc(stlc, skb->len);
+	if (!entry) {
+		/* the queue should be stopped before the firmware buffer
+		 * is full, so firmware buffer should always have enough
+		 * space */
+		if (net_ratelimit())
+			stlc45xx_warning("firmware buffer full");
+		spin_unlock_bh(&stlc->tx_lock);
+		return NETDEV_TX_BUSY;
+	}
+
+	info = IEEE80211_SKB_CB(skb);
+
+	payload = skb->data;
+	payload_len = skb->len;
+	padding = (int) (skb->data - sizeof(*data)) & 3;
+	entry->header_len = sizeof(*data) + padding;
+
+	entry->skb = skb;
+	entry->status_needed = true;
+	entry->handle = (u32) skb;
+	entry->lifetime = jiffies + msecs_to_jiffies(TX_FRAME_LIFETIME);
+
+	stlc45xx_debug(DEBUG_TX, "tx data 0x%x (0x%p payload %d B "
+		       "padding %d header_len %d)",
+		       entry->handle, payload, payload_len, padding,
+		       entry->header_len);
+	stlc45xx_dump(DEBUG_TX_CONTENT, payload, payload_len);
+
+	data = (struct s_lm_data_out *) skb_push(skb, entry->header_len);
+
+	memset(data, 0, entry->header_len);
+
+	if (padding)
+		data->flags = LM_FLAG_ALIGN;
+
+	data->flags = LM_OUT_BURST;
+	data->length = payload_len;
+	data->handle = entry->handle;
+	data->aid = 1;
+	data->rts_retries = 7;
+	data->retries = 7;
+	data->aloft_ctrl = 0;
+	data->crypt_offset = 58;
+	data->keytype = 0;
+	data->keylen = 0;
+	data->queue = 2;
+	data->backlog = 32;
+	data->antenna = 2;
+	data->cts = 3;
+	data->power = 127;
+
+	for (i = 0; i < 8; i++) {
+		rate = ieee80211_get_tx_rate(stlc->hw, info);
+		data->aloft[i] = rate->hw_value;
+	}
+
+	list_add_tail(&entry->tx_list, &stlc->tx_pending);
+
+	/* check if there's enough space in tx buffer */
+	if (stlc45xx_txbuffer_find(stlc, MAX_FRAME_LEN) == -1) {
+		stlc45xx_debug(DEBUG_QUEUE, "tx buffer full, stopping queues");
+		stlc->tx_queue_stopped = 1;
+		ieee80211_stop_queues(stlc->hw);
+	}
+
+	queue_work(stlc->hw->workqueue, &stlc->work);
+
+	spin_unlock_bh(&stlc->tx_lock);
+
+	return NETDEV_TX_OK;
+}
+
+static int stlc45xx_op_start(struct ieee80211_hw *hw)
+{
+	struct stlc45xx *stlc = hw->priv;
+	unsigned long timeout;
+	int ret = 0;
+
+	stlc45xx_debug(DEBUG_FUNC, "%s", __func__);
+
+	mutex_lock(&stlc->mutex);
+
+	stlc->fw_state = FW_STATE_BOOTING;
+	stlc->channel = 1;
+
+	stlc45xx_power_on(stlc);
+
+	ret = stlc45xx_upload_firmware(stlc);
+	if (ret < 0) {
+		stlc45xx_power_off(stlc);
+		goto out_unlock;
+	}
+
+	stlc->tx_queue_stopped = 0;
+
+	mutex_unlock(&stlc->mutex);
+
+	timeout = msecs_to_jiffies(2000);
+	timeout = wait_for_completion_interruptible_timeout(&stlc->fw_comp,
+							    timeout);
+	if (!timeout) {
+		stlc45xx_error("firmware boot failed");
+		stlc45xx_power_off(stlc);
+		ret = -1;
+		goto out;
+	}
+
+	stlc45xx_debug(DEBUG_BOOT, "firmware booted");
+
+	/* FIXME: should we take mutex just after wait_for_completion()? */
+	mutex_lock(&stlc->mutex);
+
+	WARN_ON(stlc->fw_state != FW_STATE_READY);
+
+out_unlock:
+	mutex_unlock(&stlc->mutex);
+
+out:
+	return ret;
+}
+
+static void stlc45xx_op_stop(struct ieee80211_hw *hw)
+{
+	struct stlc45xx *stlc = hw->priv;
+
+	stlc45xx_debug(DEBUG_FUNC, "%s", __func__);
+
+	mutex_lock(&stlc->mutex);
+
+	WARN_ON(stlc->fw_state != FW_STATE_READY);
+
+	stlc45xx_power_off(stlc);
+
+	/* FIXME: make sure that all work_structs have completed */
+
+	spin_lock_bh(&stlc->tx_lock);
+	stlc45xx_flush_queues(stlc);
+	spin_unlock_bh(&stlc->tx_lock);
+
+	stlc->fw_state = FW_STATE_OFF;
+
+	mutex_unlock(&stlc->mutex);
+}
+
+static int stlc45xx_op_add_interface(struct ieee80211_hw *hw,
+				     struct ieee80211_if_init_conf *conf)
+{
+	struct stlc45xx *stlc = hw->priv;
+
+	stlc45xx_debug(DEBUG_FUNC, "%s", __func__);
+
+	switch (conf->type) {
+	case NL80211_IFTYPE_STATION:
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	memcpy(stlc->mac_addr, conf->mac_addr, ETH_ALEN);
+
+	return 0;
+}
+
+static void stlc45xx_op_remove_interface(struct ieee80211_hw *hw,
+					 struct ieee80211_if_init_conf *conf)
+{
+	stlc45xx_debug(DEBUG_FUNC, "%s", __func__);
+}
+
+static int stlc45xx_op_config_interface(struct ieee80211_hw *hw,
+					struct ieee80211_vif *vif,
+					struct ieee80211_if_conf *conf)
+{
+	struct stlc45xx *stlc = hw->priv;
+
+	stlc45xx_debug(DEBUG_FUNC, "%s", __func__);
+
+	mutex_lock(&stlc->mutex);
+
+	memcpy(stlc->bssid, conf->bssid, ETH_ALEN);
+	stlc45xx_tx_setup(stlc);
+
+	mutex_unlock(&stlc->mutex);
+
+	return 0;
+}
+
+static int stlc45xx_op_config(struct ieee80211_hw *hw, u32 changed)
+{
+	struct stlc45xx *stlc = hw->priv;
+
+	stlc45xx_debug(DEBUG_FUNC, "%s", __func__);
+
+	mutex_lock(&stlc->mutex);
+
+	stlc->channel = hw->conf.channel->hw_value;
+	stlc45xx_tx_scan(stlc);
+	stlc45xx_tx_setup(stlc);
+	stlc45xx_tx_edcf(stlc);
+
+	if ((hw->conf.flags & IEEE80211_CONF_PS) != stlc->psm) {
+		stlc->psm = hw->conf.flags & IEEE80211_CONF_PS;
+		if (stlc->associated) {
+			stlc45xx_tx_psm(stlc, stlc->psm);
+			stlc45xx_tx_nullfunc(stlc, stlc->psm);
+		}
+	}
+
+	mutex_unlock(&stlc->mutex);
+
+	return 0;
+}
+
+static void stlc45xx_op_configure_filter(struct ieee80211_hw *hw,
+				      unsigned int changed_flags,
+				      unsigned int *total_flags,
+				      int mc_count,
+				      struct dev_addr_list *mc_list)
+{
+	*total_flags = 0;
+}
+
+static void stlc45xx_op_bss_info_changed(struct ieee80211_hw *hw,
+					 struct ieee80211_vif *vif,
+					 struct ieee80211_bss_conf *info,
+					 u32 changed)
+{
+	struct stlc45xx *stlc = hw->priv;
+
+	if (changed & BSS_CHANGED_ASSOC) {
+		stlc->associated = info->assoc;
+		if (info->assoc)
+			stlc->aid = info->aid;
+		else
+			stlc->aid = -1;
+
+		if (stlc->psm) {
+			stlc45xx_tx_psm(stlc, stlc->psm);
+			stlc45xx_tx_nullfunc(stlc, stlc->psm);
+		}
+	}
+}
+
+
+/* can't be const, mac80211 writes to this */
+static struct ieee80211_rate stlc45xx_rates[] = {
+	{ .bitrate = 10,  .hw_value = 0,    .hw_value_short = 0, },
+	{ .bitrate = 20,  .hw_value = 1,    .hw_value_short = 1, },
+	{ .bitrate = 55,  .hw_value = 2,    .hw_value_short = 2, },
+	{ .bitrate = 110, .hw_value = 3,    .hw_value_short = 3, },
+	{ .bitrate = 60,  .hw_value = 4,    .hw_value_short = 4, },
+	{ .bitrate = 90,  .hw_value = 5,    .hw_value_short = 5, },
+	{ .bitrate = 120, .hw_value = 6,    .hw_value_short = 6, },
+	{ .bitrate = 180, .hw_value = 7,    .hw_value_short = 7, },
+	{ .bitrate = 240, .hw_value = 8,    .hw_value_short = 8, },
+	{ .bitrate = 360, .hw_value = 9,    .hw_value_short = 9, },
+	{ .bitrate = 480, .hw_value = 10,   .hw_value_short = 10, },
+	{ .bitrate = 540, .hw_value = 11,   .hw_value_short = 11, },
+};
+
+/* can't be const, mac80211 writes to this */
+static struct ieee80211_channel stlc45xx_channels[] = {
+	{ .hw_value = 1, .center_freq = 2412},
+	{ .hw_value = 2, .center_freq = 2417},
+	{ .hw_value = 3, .center_freq = 2422},
+	{ .hw_value = 4, .center_freq = 2427},
+	{ .hw_value = 5, .center_freq = 2432},
+	{ .hw_value = 6, .center_freq = 2437},
+	{ .hw_value = 7, .center_freq = 2442},
+	{ .hw_value = 8, .center_freq = 2447},
+	{ .hw_value = 9, .center_freq = 2452},
+	{ .hw_value = 10, .center_freq = 2457},
+	{ .hw_value = 11, .center_freq = 2462},
+	{ .hw_value = 12, .center_freq = 2467},
+	{ .hw_value = 13, .center_freq = 2472},
+};
+
+/* can't be const, mac80211 writes to this */
+static struct ieee80211_supported_band stlc45xx_band_2ghz = {
+	.channels = stlc45xx_channels,
+	.n_channels = ARRAY_SIZE(stlc45xx_channels),
+	.bitrates = stlc45xx_rates,
+	.n_bitrates = ARRAY_SIZE(stlc45xx_rates),
+};
+
+static const struct ieee80211_ops stlc45xx_ops = {
+	.start = stlc45xx_op_start,
+	.stop = stlc45xx_op_stop,
+	.add_interface = stlc45xx_op_add_interface,
+	.remove_interface = stlc45xx_op_remove_interface,
+	.config = stlc45xx_op_config,
+	.config_interface = stlc45xx_op_config_interface,
+	.configure_filter = stlc45xx_op_configure_filter,
+	.tx = stlc45xx_op_tx,
+	.bss_info_changed = stlc45xx_op_bss_info_changed,
+};
+
+static int stlc45xx_register_mac80211(struct stlc45xx *stlc)
+{
+	/* FIXME: SET_IEEE80211_PERM_ADDR() requires default_mac_addr
+	   to be non-const for some strange reason */
+	static u8 default_mac_addr[ETH_ALEN] = {
+		0x00, 0x02, 0xee, 0xc0, 0xff, 0xee
+	};
+	int ret;
+
+	SET_IEEE80211_PERM_ADDR(stlc->hw, default_mac_addr);
+
+	ret = ieee80211_register_hw(stlc->hw);
+	if (ret) {
+		stlc45xx_error("unable to register mac80211 hw: %d", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static void stlc45xx_device_release(struct device *dev)
+{
+
+}
+
+static struct platform_device stlc45xx_device = {
+	.name		= "stlc45xx",
+	.id		= -1,
+
+	/* device model insists to have a release function */
+	.dev            = {
+		.release = stlc45xx_device_release,
+	},
+};
+
+static int __devinit stlc45xx_probe(struct spi_device *spi)
+{
+	struct stlc45xx *stlc;
+	struct ieee80211_hw *hw;
+	int ret;
+
+	stlc45xx_debug(DEBUG_FUNC, "%s", __func__);
+
+	/* mac80211 alloc */
+	hw = ieee80211_alloc_hw(sizeof(*stlc), &stlc45xx_ops);
+	if (!hw) {
+		stlc45xx_error("could not alloc ieee80211_hw");
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	/* mac80211 clears hw->priv */
+	stlc = hw->priv;
+
+	stlc->hw = hw;
+	dev_set_drvdata(&spi->dev, stlc);
+	stlc->spi = spi;
+
+	stlc->config = omap_get_config(OMAP_TAG_WLAN_CX3110X,
+				       struct omap_wlan_cx3110x_config);
+
+	spi->bits_per_word = 16;
+	spi->max_speed_hz = 24000000;
+
+	ret = spi_setup(spi);
+	if (ret < 0)
+		stlc45xx_error("spi_setup failed");
+
+	ret = gpio_request(stlc->config->power_gpio, "stlc45xx power");
+	if (ret < 0) {
+		stlc45xx_error("power GPIO request failed: %d", ret);
+		return ret;
+	}
+
+	ret = gpio_request(stlc->config->irq_gpio, "stlc45xx irq");
+	if (ret < 0) {
+		stlc45xx_error("irq GPIO request failed: %d", ret);
+		goto out;
+	}
+
+	gpio_direction_output(stlc->config->power_gpio, 0);
+	gpio_direction_input(stlc->config->irq_gpio);
+
+	ret = request_irq(gpio_to_irq(stlc->config->irq_gpio),
+			  stlc45xx_interrupt, IRQF_DISABLED, "stlc45xx",
+			  stlc->spi);
+	if (ret < 0)
+		/* FIXME: handle the error */
+		stlc45xx_error("request_irq() failed");
+
+	set_irq_type(gpio_to_irq(stlc->config->irq_gpio),
+		     IRQ_TYPE_EDGE_RISING);
+
+	disable_irq(gpio_to_irq(stlc->config->irq_gpio));
+
+	ret = platform_device_register(&stlc45xx_device);
+	if (ret) {
+		stlc45xx_error("Couldn't register wlan_omap device.");
+		return ret;
+	}
+	dev_set_drvdata(&stlc45xx_device.dev, stlc);
+
+	INIT_WORK(&stlc->work, stlc45xx_work);
+	INIT_WORK(&stlc->work_reset, stlc45xx_work_reset);
+	INIT_DELAYED_WORK(&stlc->work_tx_timeout, stlc45xx_work_tx_timeout);
+	mutex_init(&stlc->mutex);
+	init_completion(&stlc->fw_comp);
+	spin_lock_init(&stlc->tx_lock);
+	INIT_LIST_HEAD(&stlc->txbuffer);
+	INIT_LIST_HEAD(&stlc->tx_pending);
+	INIT_LIST_HEAD(&stlc->tx_sent);
+
+	hw->flags = IEEE80211_HW_RX_INCLUDES_FCS |
+		IEEE80211_HW_SIGNAL_DBM |
+		IEEE80211_HW_NOISE_DBM;
+	/* four bytes for padding */
+	hw->extra_tx_headroom = sizeof(struct s_lm_data_out) + 4;
+
+	/* unit us */
+	hw->channel_change_time = 1000;
+
+	hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &stlc45xx_band_2ghz;
+
+	SET_IEEE80211_DEV(hw, &spi->dev);
+
+	BUILD_BUG_ON(sizeof(default_cal_rssi) != RSSI_CAL_ARRAY_LEN);
+	BUILD_BUG_ON(sizeof(default_cal_channels) != CHANNEL_CAL_ARRAY_LEN);
+
+	stlc->cal_rssi = kmemdup(default_cal_rssi, RSSI_CAL_ARRAY_LEN,
+				 GFP_KERNEL);
+	stlc->cal_channels = kmemdup(default_cal_channels,
+				     CHANNEL_CAL_ARRAY_LEN,
+				     GFP_KERNEL);
+
+	ret = device_create_file(&stlc45xx_device.dev, &dev_attr_cal_rssi);
+	if (ret < 0) {
+		stlc45xx_error("failed to create sysfs file cal_rssi");
+		goto out;
+	}
+
+	ret = device_create_file(&stlc45xx_device.dev, &dev_attr_cal_channels);
+	if (ret < 0) {
+		stlc45xx_error("failed to create sysfs file cal_channels");
+		goto out;
+	}
+
+	ret = device_create_file(&stlc45xx_device.dev, &dev_attr_tx_buf);
+	if (ret < 0) {
+		stlc45xx_error("failed to create sysfs file tx_buf");
+		goto out;
+	}
+
+	ret = stlc45xx_register_mac80211(stlc);
+	if (ret < 0)
+		goto out;
+
+	stlc45xx_info("v" DRIVER_VERSION " loaded");
+
+	stlc45xx_info("config buffer 0x%x-0x%x",
+		      FIRMWARE_CONFIG_START, FIRMWARE_CONFIG_END);
+	stlc45xx_info("tx 0x%x-0x%x, rx 0x%x-0x%x",
+		      FIRMWARE_TXBUFFER_START, FIRMWARE_TXBUFFER_END,
+		      FIRMWARE_RXBUFFER_START, FIRMWARE_RXBUFFER_END);
+
+out:
+	return ret;
+}
+
+static int __devexit stlc45xx_remove(struct spi_device *spi)
+{
+	struct stlc45xx *stlc = dev_get_drvdata(&spi->dev);
+
+	stlc45xx_debug(DEBUG_FUNC, "%s", __func__);
+
+	platform_device_unregister(&stlc45xx_device);
+
+	ieee80211_unregister_hw(stlc->hw);
+
+	free_irq(gpio_to_irq(stlc->config->irq_gpio), spi);
+
+	gpio_free(stlc->config->power_gpio);
+	gpio_free(stlc->config->irq_gpio);
+
+	/* FIXME: free cal_channels and cal_rssi? */
+
+	kfree(stlc->fw);
+
+	mutex_destroy(&stlc->mutex);
+
+	/* frees also stlc */
+	ieee80211_free_hw(stlc->hw);
+	stlc = NULL;
+
+	return 0;
+}
+
+
+static struct spi_driver stlc45xx_spi_driver = {
+	.driver = {
+		/* use cx3110x name because board-n800.c uses that for the
+		 * SPI port */
+		.name		= "cx3110x",
+		.bus		= &spi_bus_type,
+		.owner		= THIS_MODULE,
+	},
+
+	.probe		= stlc45xx_probe,
+	.remove		= __devexit_p(stlc45xx_remove),
+};
+
+static int __init stlc45xx_init(void)
+{
+	int ret;
+
+	stlc45xx_debug(DEBUG_FUNC, "%s", __func__);
+
+	ret = spi_register_driver(&stlc45xx_spi_driver);
+	if (ret < 0) {
+		stlc45xx_error("failed to register SPI driver: %d", ret);
+		goto out;
+	}
+
+out:
+	return ret;
+}
+
+static void __exit stlc45xx_exit(void)
+{
+	stlc45xx_debug(DEBUG_FUNC, "%s", __func__);
+
+	spi_unregister_driver(&stlc45xx_spi_driver);
+
+	stlc45xx_info("unloaded");
+}
+
+module_init(stlc45xx_init);
+module_exit(stlc45xx_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Kalle Valo <kalle.valo@xxxxxxxxx>");
diff --git a/drivers/net/wireless/stlc45xx.h b/drivers/net/wireless/stlc45xx.h
new file mode 100644
index 0000000..517df6e
--- /dev/null
+++ b/drivers/net/wireless/stlc45xx.h
@@ -0,0 +1,285 @@
+/*
+ * This file is part of stlc45xx
+ *
+ * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * Contact: Kalle Valo <kalle.valo@xxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <net/mac80211.h>
+#include <mach/board.h>
+
+#include "stlc45xx_lmac.h"
+
+#define DRIVER_NAME "stlc45xx"
+#define DRIVER_VERSION "0.1.3"
+
+#define DRIVER_PREFIX DRIVER_NAME ": "
+
+enum {
+	DEBUG_NONE = 0,
+	DEBUG_FUNC = 1 << 0,
+	DEBUG_IRQ = 1 << 1,
+	DEBUG_BH = 1 << 2,
+	DEBUG_RX = 1 << 3,
+	DEBUG_RX_CONTENT = 1 << 5,
+	DEBUG_TX = 1 << 6,
+	DEBUG_TX_CONTENT = 1 << 8,
+	DEBUG_TXBUFFER = 1 << 9,
+	DEBUG_QUEUE = 1 << 10,
+	DEBUG_BOOT = 1 << 11,
+	DEBUG_PSM = 1 << 12,
+	DEBUG_ALL = ~0,
+};
+
+#define DEBUG_LEVEL DEBUG_NONE
+/* #define DEBUG_LEVEL DEBUG_ALL */
+/* #define DEBUG_LEVEL (DEBUG_TX | DEBUG_RX | DEBUG_IRQ) */
+/* #define DEBUG_LEVEL (DEBUG_TX | DEBUG_MEMREGION | DEBUG_QUEUE) */
+/* #define DEBUG_LEVEL (DEBUG_MEMREGION | DEBUG_QUEUE) */
+
+#define stlc45xx_error(fmt, arg...) \
+	printk(KERN_ERR DRIVER_PREFIX "ERROR " fmt "\n", ##arg)
+
+#define stlc45xx_warning(fmt, arg...) \
+	printk(KERN_WARNING DRIVER_PREFIX "WARNING " fmt "\n", ##arg)
+
+#define stlc45xx_info(fmt, arg...) \
+	printk(KERN_INFO DRIVER_PREFIX fmt "\n", ##arg)
+
+#define stlc45xx_debug(level, fmt, arg...) \
+	do { \
+		if (level & DEBUG_LEVEL) \
+			printk(KERN_DEBUG DRIVER_PREFIX fmt "\n", ##arg); \
+	} while (0)
+
+#define stlc45xx_dump(level, buf, len)		\
+	do { \
+		if (level & DEBUG_LEVEL) \
+			print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, \
+				       16, 1, buf, len, 1);		\
+	} while (0)
+
+#define MAC2STR(a) ((a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5])
+#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
+
+/* Bit 15 is read/write bit; ON = READ, OFF = WRITE */
+#define ADDR_READ_BIT_15  0x8000
+
+#define SPI_ADRS_ARM_INTERRUPTS     0x00
+#define SPI_ADRS_ARM_INT_EN	    0x04
+
+#define SPI_ADRS_HOST_INTERRUPTS    0x08
+#define SPI_ADRS_HOST_INT_EN	    0x0c
+#define SPI_ADRS_HOST_INT_ACK	    0x10
+
+#define SPI_ADRS_GEN_PURP_1   	    0x14
+#define SPI_ADRS_GEN_PURP_2   	    0x18
+
+/* high word */
+#define SPI_ADRS_DEV_CTRL_STAT      0x26
+
+#define SPI_ADRS_DMA_DATA      	    0x28
+
+#define SPI_ADRS_DMA_WRITE_CTRL     0x2c
+#define SPI_ADRS_DMA_WRITE_LEN      0x2e
+#define SPI_ADRS_DMA_WRITE_BASE     0x30
+
+#define SPI_ADRS_DMA_READ_CTRL      0x34
+#define SPI_ADRS_DMA_READ_LEN       0x36
+#define SPI_ADRS_DMA_READ_BASE      0x38
+
+#define SPI_CTRL_STAT_HOST_OVERRIDE 0x8000
+#define SPI_CTRL_STAT_START_HALTED  0x4000
+#define SPI_CTRL_STAT_RAM_BOOT      0x2000
+#define SPI_CTRL_STAT_HOST_RESET    0x1000
+#define SPI_CTRL_STAT_HOST_CPU_EN   0x0800
+
+#define SPI_DMA_WRITE_CTRL_ENABLE   0x0001
+#define SPI_DMA_READ_CTRL_ENABLE    0x0001
+#define HOST_ALLOWED                (1 << 7)
+
+#define FIRMWARE_ADDRESS                        0x20000
+
+#define SPI_TIMEOUT                             100         /* msec */
+
+#define SPI_MAX_TX_PACKETS                      32
+
+#define SPI_MAX_PACKET_SIZE                     32767
+
+#define SPI_TARGET_INT_WAKEUP                   0x00000001
+#define SPI_TARGET_INT_SLEEP                    0x00000002
+#define SPI_TARGET_INT_RDDONE                   0x00000004
+
+#define SPI_TARGET_INT_CTS                      0x00004000
+#define SPI_TARGET_INT_DR                       0x00008000
+
+#define SPI_HOST_INT_READY                      0x00000001
+#define SPI_HOST_INT_WR_READY                   0x00000002
+#define SPI_HOST_INT_SW_UPDATE                  0x00000004
+#define SPI_HOST_INT_UPDATE                     0x10000000
+
+/* clear to send */
+#define SPI_HOST_INT_CTS	                0x00004000
+
+/* data ready */
+#define SPI_HOST_INT_DR	                        0x00008000
+
+#define SPI_HOST_INTS_DEFAULT \
+	(SPI_HOST_INT_READY | SPI_HOST_INT_UPDATE | SPI_HOST_INT_SW_UPDATE)
+
+#define TARGET_BOOT_SLEEP 50
+
+/* The firmware buffer is divided into three areas:
+ *
+ * o config area (for control commands)
+ * o tx buffer
+ * o rx buffer
+ */
+#define FIRMWARE_BUFFER_START 0x20200
+#define FIRMWARE_BUFFER_END 0x27c60
+#define FIRMWARE_BUFFER_LEN (FIRMWARE_BUFFER_END - FIRMWARE_BUFFER_START)
+#define FIRMWARE_MTU 3240
+#define FIRMWARE_CONFIG_PAYLOAD_LEN 1024
+#define FIRMWARE_CONFIG_START FIRMWARE_BUFFER_START
+#define FIRMWARE_CONFIG_LEN (sizeof(struct s_lm_control) + \
+			     FIRMWARE_CONFIG_PAYLOAD_LEN)
+#define FIRMWARE_CONFIG_END (FIRMWARE_CONFIG_START + FIRMWARE_CONFIG_LEN - 1)
+#define FIRMWARE_RXBUFFER_LEN (5 * FIRMWARE_MTU + 1024)
+#define FIRMWARE_RXBUFFER_START (FIRMWARE_BUFFER_END - FIRMWARE_RXBUFFER_LEN)
+#define FIRMWARE_RXBUFFER_END (FIRMWARE_RXBUFFER_START + \
+			       FIRMWARE_RXBUFFER_LEN - 1)
+#define FIRMWARE_TXBUFFER_START (FIRMWARE_BUFFER_START + FIRMWARE_CONFIG_LEN)
+#define FIRMWARE_TXBUFFER_LEN (FIRMWARE_BUFFER_LEN - FIRMWARE_CONFIG_LEN - \
+			       FIRMWARE_RXBUFFER_LEN)
+#define FIRMWARE_TXBUFFER_END (FIRMWARE_TXBUFFER_START + \
+			       FIRMWARE_TXBUFFER_LEN - 1)
+
+#define FIRMWARE_TXBUFFER_HEADER 100
+#define FIRMWARE_TXBUFFER_TRAILER 4
+
+/* FIXME: come up with a proper value */
+#define MAX_FRAME_LEN 2500
+
+/* unit is ms */
+#define TX_FRAME_LIFETIME 2000
+#define TX_TIMEOUT 4000
+
+#define SUPPORTED_CHANNELS 13
+
+/* FIXME */
+/* #define CHANNEL_CAL_LEN offsetof(struct s_lmo_scan, bratemask) - \ */
+/* 	offsetof(struct s_lmo_scan, channel) */
+#define CHANNEL_CAL_LEN 292
+#define CHANNEL_CAL_ARRAY_LEN (SUPPORTED_CHANNELS * CHANNEL_CAL_LEN)
+/* FIXME */
+/* #define RSSI_CAL_LEN sizeof(struct s_lmo_scan) - \ */
+/* 	offsetof(struct s_lmo_scan, rssical) */
+#define RSSI_CAL_LEN 8
+#define RSSI_CAL_ARRAY_LEN (SUPPORTED_CHANNELS * RSSI_CAL_LEN)
+
+struct s_dma_regs {
+	unsigned short cmd;
+	unsigned short len;
+	unsigned long addr;
+};
+
+struct stlc45xx_ie_tim {
+	u8 dtim_count;
+	u8 dtim_period;
+	u8 bmap_control;
+	u8 pvbmap[251];
+};
+
+struct txbuffer {
+	/* can be removed when switched to skb queue */
+	struct list_head tx_list;
+
+	struct list_head buffer_list;
+
+	int start;
+	int frame_start;
+	int end;
+
+	struct sk_buff *skb;
+	u32 handle;
+
+	bool status_needed;
+
+	int header_len;
+
+	/* unit jiffies */
+	unsigned long lifetime;
+};
+
+enum fw_state {
+	FW_STATE_OFF,
+	FW_STATE_BOOTING,
+	FW_STATE_READY,
+	FW_STATE_RESET,
+	FW_STATE_RESETTING,
+};
+
+struct stlc45xx {
+	struct ieee80211_hw *hw;
+	struct spi_device *spi;
+	const struct omap_wlan_cx3110x_config *config;
+	struct work_struct work;
+	struct work_struct work_reset;
+	struct delayed_work work_tx_timeout;
+	struct mutex mutex;
+	struct completion fw_comp;
+
+
+	u8 bssid[ETH_ALEN];
+	u8 mac_addr[ETH_ALEN];
+	int channel;
+
+	u8 *cal_rssi;
+	u8 *cal_channels;
+
+	enum fw_state fw_state;
+
+	spinlock_t tx_lock;
+
+	/* protected by tx_lock */
+	struct list_head txbuffer;
+
+	/* protected by tx_lock */
+	struct list_head tx_pending;
+
+	/* protected by tx_lock */
+	int tx_queue_stopped;
+
+	/* protected by mutex */
+	struct list_head tx_sent;
+
+	int tx_frames;
+
+	u8 *fw;
+	int fw_len;
+
+	bool psm;
+	bool associated;
+	int aid;
+	bool pspolling;
+};
+
+
diff --git a/drivers/net/wireless/stlc45xx_lmac.h b/drivers/net/wireless/stlc45xx_lmac.h
new file mode 100644
index 0000000..af5db80
--- /dev/null
+++ b/drivers/net/wireless/stlc45xx_lmac.h
@@ -0,0 +1,434 @@
+/************************************************************************
+*  This is the LMAC API interface header file for STLC4560.        	*
+*  Copyright (C) 2007 Conexant Systems, Inc.                            *
+*  This program is free software; you can redistribute it and/or        *
+*  modify it under the terms of the GNU General Public License          *
+*  as published by the Free Software Foundation; either version 2	*
+*  of the License, or (at your option) any later version.               *
+*                                                                       *
+*  This program is distributed in the hope that it will be useful,      *
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of	*
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        *
+*  GNU General Public License for more details.                         *
+*                                                                       *
+*  You should have received a copy of the GNU General Public License    *
+*  along with this program.  If not, see <http://www.gnu.org/licenses/>.*
+*************************************************************************/
+
+#ifndef __lmac_h__
+#define __lmac_h__
+
+#define LM_TOP_VARIANT      0x0506
+#define LM_BOTTOM_VARIANT   0x0506
+
+/*
+ * LMAC - UMAC interface definition:
+ */
+
+#define LM_FLAG_CONTROL     0x8000
+#define LM_FLAG_ALIGN       0x4000
+
+#define LM_CTRL_OPSET       0x0001
+
+#define LM_OUT_PROMISC      0x0001
+#define LM_OUT_TIMESTAMP    0x0002
+#define LM_OUT_SEQNR        0x0004
+#define LM_OUT_BURST        0x0010
+#define LM_OUT_NOCANCEL     0x0020
+#define LM_OUT_CLEARTIM     0x0040
+#define LM_OUT_HITCHHIKE    0x0080
+#define LM_OUT_COMPRESS     0x0100
+#define LM_OUT_CONCAT       0x0200
+#define LM_OUT_PCS_ACCEPT   0x0400
+#define LM_OUT_WAITEOSP     0x0800
+
+
+#define LM_ALOFT_SP         0x10
+#define LM_ALOFT_CTS        0x20
+#define LM_ALOFT_RTS        0x40
+#define LM_ALOFT_MASK       0x1f
+#define LM_ALOFT_RATE       0x0f
+
+#define LM_IN_FCS_GOOD      0x0001
+#define LM_IN_MATCH_MAC     0x0002
+#define LM_IN_MCBC          0x0004
+#define LM_IN_BEACON        0x0008
+#define LM_IN_MATCH_BSS     0x0010
+#define LM_IN_BCAST_BSS     0x0020
+#define LM_IN_DATA          0x0040
+#define LM_IN_TRUNCATED     0x0080
+
+#define LM_IN_TRANSPARENT   0x0200
+
+#define LM_QUEUE_BEACON     0
+#define LM_QUEUE_SCAN       1
+#define LM_QUEUE_MGT        2
+#define LM_QUEUE_MCBC       3
+#define LM_QUEUE_DATA       4
+#define LM_QUEUE_DATA0      4
+#define LM_QUEUE_DATA1      5
+#define LM_QUEUE_DATA2      6
+#define LM_QUEUE_DATA3      7
+
+#define LM_SETUP_INFRA          0x0001
+#define LM_SETUP_IBSS           0x0002
+#define LM_SETUP_TRANSPARENT    0x0008
+#define LM_SETUP_PROMISCUOUS    0x0010
+#define LM_SETUP_HIBERNATE      0x0020
+#define LM_SETUP_NOACK          0x0040
+#define LM_SETUP_RX_DISABLED    0x0080
+
+#define LM_ANTENNA_0            0
+#define LM_ANTENNA_1            1
+#define LM_ANTENNA_DIVERSITY    2
+
+#define LM_TX_FAILED            0x0001
+#define LM_TX_PSM               0x0002
+#define LM_TX_PSM_CANCELLED	0x0004
+
+#define LM_SCAN_EXIT            0x0001
+#define LM_SCAN_TRAP            0x0002
+#define LM_SCAN_ACTIVE          0x0004
+#define LM_SCAN_FILTER          0x0008
+
+#define LM_PSM                  0x0001
+#define LM_PSM_DTIM             0x0002
+#define LM_PSM_MCBC             0x0004
+#define LM_PSM_CHECKSUM         0x0008
+#define LM_PSM_SKIP_MORE_DATA   0x0010
+#define LM_PSM_BEACON_TIMEOUT   0x0020
+#define LM_PSM_HFOSLEEP         0x0040
+#define LM_PSM_AUTOSWITCH_SLEEP 0x0080
+#define	LM_PSM_LPIT		0x0100
+#define LM_PSM_BF_UCAST_SKIP    0x0200
+#define LM_PSM_BF_MCAST_SKIP    0x0400
+
+/* hfosleep */
+#define LM_PSM_SLEEP_OPTION_MASK (LM_PSM_AUTOSWITCH_SLEEP | LM_PSM_HFOSLEEP)
+#define LM_PSM_SLEEP_OPTION_SHIFT       6
+/* hfosleepend */
+#define LM_PSM_BF_OPTION_MASK (LM_PSM_BF_MCAST_SKIP | LM_PSM_BF_UCAST_SKIP)
+#define LM_PSM_BF_OPTION_SHIFT  9
+
+
+#define LM_PRIVACC_WEP          0x01
+#define LM_PRIVACC_TKIP         0x02
+#define LM_PRIVACC_MICHAEL      0x04
+#define LM_PRIVACC_CCX_KP       0x08
+#define LM_PRIVACC_CCX_MIC      0x10
+#define LM_PRIVACC_AES_CCMP     0x20
+
+/* size of s_lm_descr in words */
+#define LM_DESCR_SIZE_WORDS     11
+
+#ifndef __ASSEMBLER__
+
+enum {
+    LM_MODE_CLIENT = 0,
+    LM_MODE_AP
+};
+
+struct s_lm_descr {
+    uint16_t modes;
+    uint16_t flags;
+    uint32_t buffer_start;
+    uint32_t buffer_end;
+    uint8_t header;
+    uint8_t trailer;
+    uint8_t tx_queues;
+    uint8_t tx_depth;
+    uint8_t privacy;
+    uint8_t rx_keycache;
+    uint8_t tim_size;
+    uint8_t pad1;
+    uint8_t rates[16];
+    uint32_t link;
+	uint16_t mtu;
+};
+
+
+struct s_lm_control {
+    uint16_t flags;
+    uint16_t length;
+    uint32_t handle;
+    uint16_t oid;
+    uint16_t pad;
+    /* uint8_t data[]; */
+};
+
+enum {
+    LM_PRIV_NONE = 0,
+    LM_PRIV_WEP,
+    LM_PRIV_TKIP,
+    LM_PRIV_TKIPMICHAEL,
+    LM_PRIV_CCX_WEPMIC,
+    LM_PRIV_CCX_KPMIC,
+    LM_PRIV_CCX_KP,
+    LM_PRIV_AES_CCMP
+};
+
+enum {
+    LM_DECRYPT_NONE,
+    LM_DECRYPT_OK,
+    LM_DECRYPT_NOKEY,
+    LM_DECRYPT_NOMICHAEL,
+    LM_DECRYPT_NOCKIPMIC,
+    LM_DECRYPT_FAIL_WEP,
+    LM_DECRYPT_FAIL_TKIP,
+    LM_DECRYPT_FAIL_MICHAEL,
+    LM_DECRYPT_FAIL_CKIPKP,
+    LM_DECRYPT_FAIL_CKIPMIC,
+    LM_DECRYPT_FAIL_AESCCMP
+};
+
+struct s_lm_data_out {
+    uint16_t flags;
+    uint16_t length;
+    uint32_t handle;
+    uint16_t aid;
+    uint8_t rts_retries;
+    uint8_t retries;
+    uint8_t aloft[8];
+    uint8_t aloft_ctrl;
+    uint8_t crypt_offset;
+    uint8_t keytype;
+    uint8_t keylen;
+    uint8_t key[16];
+    uint8_t queue;
+    uint8_t backlog;
+    uint16_t durations[4];
+    uint8_t antenna;
+    uint8_t cts;
+    int16_t power;
+    uint8_t pad[2];
+    /*uint8_t data[];*/
+};
+
+#define LM_RCPI_INVALID         (0xff)
+
+struct s_lm_data_in {
+    uint16_t flags;
+    uint16_t length;
+    uint16_t frequency;
+    uint8_t antenna;
+    uint8_t rate;
+    uint8_t rcpi;
+    uint8_t sq;
+    uint8_t decrypt;
+    uint8_t rssi_raw;
+    uint32_t clock[2];
+    /*uint8_t data[];*/
+};
+
+union u_lm_data {
+    struct s_lm_data_out out;
+    struct s_lm_data_in in;
+};
+
+enum {
+    LM_OID_SETUP	= 0,
+    LM_OID_SCAN		= 1,
+    LM_OID_TRAP		= 2,
+    LM_OID_EDCF		= 3,
+    LM_OID_KEYCACHE	= 4,
+    LM_OID_PSM		= 6,
+    LM_OID_TXCANCEL	= 7,
+    LM_OID_TX		= 8,
+    LM_OID_BURST	= 9,
+    LM_OID_STATS	= 10,
+    LM_OID_LED		= 13,
+    LM_OID_TIMER	= 15,
+    LM_OID_NAV		= 20,
+    LM_OID_PCS		= 22,
+    LM_OID_BT_BALANCER  = 28,
+    LM_OID_GROUP_ADDRESS_TABLE	= 30,
+    LM_OID_ARPTABLE     = 31,
+    LM_OID_BT_OPTIONS = 35
+};
+
+enum {
+    LM_FRONTEND_UNKNOWN = 0,
+    LM_FRONTEND_DUETTE3,
+    LM_FRONTEND_DUETTE2,
+    LM_FRONTEND_FRISBEE,
+    LM_FRONTEND_CROSSBOW,
+    LM_FRONTEND_LONGBOW
+};
+
+
+#define INVALID_LPF_BANDWIDTH   0xffff
+#define INVALID_OSC_START_DELAY 0xffff
+
+struct s_lmo_setup {
+    uint16_t flags;
+    uint8_t  macaddr[6];
+    uint8_t  bssid[6];
+    uint8_t  antenna;
+    uint8_t  rx_align;
+    uint32_t rx_buffer;
+    uint16_t rx_mtu;
+    uint16_t frontend;
+    uint16_t timeout;
+    uint16_t truncate;
+    uint32_t bratemask;
+    uint8_t  sbss_offset;
+    uint8_t  mcast_window;
+    uint8_t  rx_rssi_threshold;
+    uint8_t  rx_ed_threshold;
+    uint32_t ref_clock;
+    uint16_t lpf_bandwidth;
+    uint16_t osc_start_delay;
+};
+
+
+struct s_lmo_scan {
+    uint16_t flags;
+    uint16_t dwell;
+    uint8_t channel[292];
+    uint32_t bratemask;
+    uint8_t  aloft[8];
+    uint8_t  rssical[8];
+};
+
+
+enum {
+    LM_TRAP_SCAN = 0,
+    LM_TRAP_TIMER,
+    LM_TRAP_BEACON_TX,
+    LM_TRAP_FAA_RADIO_ON,
+    LM_TRAP_FAA_RADIO_OFF,
+    LM_TRAP_RADAR,
+    LM_TRAP_NO_BEACON,
+    LM_TRAP_TBTT,
+    LM_TRAP_SCO_ENTER,
+    LM_TRAP_SCO_EXIT
+};
+
+struct s_lmo_trap {
+    uint16_t event;
+    uint16_t frequency;
+};
+
+struct s_lmo_timer {
+    uint32_t interval;
+};
+
+struct s_lmo_nav {
+    uint32_t period;
+};
+
+
+struct s_lmo_edcf_queue;
+
+struct s_lmo_edcf {
+    uint8_t  flags;
+    uint8_t  slottime;
+    uint8_t  sifs;
+    uint8_t  eofpad;
+    struct s_lmo_edcf_queue {
+	    uint8_t  aifs;
+	    uint8_t  pad0;
+	    uint16_t cwmin;
+	    uint16_t cwmax;
+	    uint16_t txop;
+    } queues[8];
+    uint8_t  mapping[4];
+    uint16_t maxburst;
+    uint16_t round_trip_delay;
+};
+
+struct s_lmo_keycache {
+    uint8_t entry;
+    uint8_t keyid;
+    uint8_t address[6];
+    uint8_t pad[2];
+    uint8_t keytype;
+    uint8_t keylen;
+    uint8_t key[24];
+};
+
+
+struct s_lm_interval;
+
+struct s_lmo_psm {
+    uint16_t    flags;
+    uint16_t    aid;
+    struct s_lm_interval {
+	    uint16_t interval;
+	    uint16_t periods;
+    } intervals[4];
+    /* uint16_t    pad; */
+    uint8_t     beacon_rcpi_skip_max;
+    uint8_t     rcpi_delta_threshold;
+    uint8_t     nr;
+    uint8_t     exclude[1];
+};
+
+#define MC_FILTER_ADDRESS_NUM   4
+
+struct s_lmo_group_address_table {
+    uint16_t    filter_enable;
+    uint16_t    num_address;
+    uint8_t     macaddr_list[MC_FILTER_ADDRESS_NUM][6];
+};
+
+struct s_lmo_txcancel {
+    uint32_t address[1];
+};
+
+
+struct s_lmo_tx {
+    uint8_t  flags;
+    uint8_t  retries;
+    uint8_t  rcpi;
+    uint8_t  sq;
+    uint16_t seqctrl;
+    uint8_t  antenna;
+    uint8_t  pad;
+};
+
+struct s_lmo_burst {
+    uint8_t  flags;
+    uint8_t  queue;
+    uint8_t  backlog;
+    uint8_t  pad;
+    uint16_t durations[32];
+};
+
+struct s_lmo_stats {
+    uint32_t valid;
+    uint32_t fcs;
+    uint32_t abort;
+    uint32_t phyabort;
+    uint32_t rts_success;
+    uint32_t rts_fail;
+    uint32_t timestamp;
+    uint32_t time_tx;
+    uint32_t noisefloor;
+    uint32_t sample_noise[8];
+    uint32_t sample_cca;
+    uint32_t sample_tx;
+};
+
+
+struct s_lmo_led {
+    uint16_t flags;
+    uint16_t mask[2];
+    uint16_t delay/*[2]*/;
+};
+
+
+struct s_lmo_bt_balancer {
+    uint16_t prio_thresh;
+    uint16_t acl_thresh;
+};
+
+
+struct s_lmo_arp_table {
+    uint16_t    filter_enable;
+    uint32_t    ipaddr;
+};
+
+#endif /* __ASSEMBLER__ */
+
+#endif /* __lmac_h__ */

--
To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Host AP]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Linux Kernel]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Samba]     [Device Mapper]
  Powered by Linux