Commit 14c0b3ed authored by Kalle Valo's avatar Kalle Valo Committed by Greg Kroah-Hartman

Staging: Add stlc45xx, wi-fi driver for stlc4550/4560

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: default avatarKalle Valo <kalle.valo@nokia.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 0564200d
......@@ -97,5 +97,7 @@ source "drivers/staging/dst/Kconfig"
source "drivers/staging/pohmelfs/Kconfig"
source "drivers/staging/stlc45xx/Kconfig"
endif # !STAGING_EXCLUDE_BUILD
endif # STAGING
......@@ -31,3 +31,4 @@ obj-$(CONFIG_EPL) += epl/
obj-$(CONFIG_ANDROID) += android/
obj-$(CONFIG_DST) += dst/
obj-$(CONFIG_POHMELFS) += pohmelfs/
obj-$(CONFIG_STLC45XX) += stlc45xx/
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.
obj-$(CONFIG_STLC45XX) += stlc45xx.o
/*
* This file is part of stlc45xx
*
* Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
*
* Contact: Kalle Valo <kalle.valo@nokia.com>
*
* 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 <linux/moduleparam.h>
#include "stlc45xx_lmac.h"
/*
* gpios should be handled in board files and provided via platform data,
* but because it's currently impossible for stlc45xx to have a header file
* in include/linux, let's use module paramaters for now
*/
static int stlc45xx_gpio_power = 97;
module_param(stlc45xx_gpio_power, int, 0444);
MODULE_PARM_DESC(stlc45xx_gpio_power, "stlc45xx gpio number for power line");
static int stlc45xx_gpio_irq = 87;
module_param(stlc45xx_gpio_irq, int, 0444);
MODULE_PARM_DESC(stlc45xx_gpio_irq, "stlc45xx gpio number for irq line");
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(stlc45xx_gpio_irq));
gpio_set_value(stlc45xx_gpio_power, 0);
}
static void stlc45xx_power_on(struct stlc45xx *stlc)
{
gpio_set_value(stlc45xx_gpio_power, 1);
enable_irq(gpio_to_irq(stlc45xx_gpio_irq));
/*
* 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;
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(stlc45xx_gpio_power, "stlc45xx power");
if (ret < 0) {
stlc45xx_error("power GPIO request failed: %d", ret);
return ret;
}
ret = gpio_request(stlc45xx_gpio_irq, "stlc45xx irq");
if (ret < 0) {
stlc45xx_error("irq GPIO request failed: %d", ret);
goto out;
}
gpio_direction_output(stlc45xx_gpio_power, 0);
gpio_direction_input(stlc45xx_gpio_irq);
ret = request_irq(gpio_to_irq(stlc45xx_gpio_irq),
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(stlc45xx_gpio_irq),
IRQ_TYPE_EDGE_RISING);
disable_irq(gpio_to_irq(stlc45xx_gpio_irq));
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->interface_modes = BIT(NL80211_IFTYPE_STATION);
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(stlc45xx_gpio_irq), spi);
gpio_free(stlc45xx_gpio_power);
gpio_free(stlc45xx_gpio_irq);
/* 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@nokia.com>");
/*
* This file is part of stlc45xx
*
* Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
*
* Contact: Kalle Valo <kalle.valo@nokia.com>
*
* 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 "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;
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;
};
/************************************************************************
* 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__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment