summaryrefslogtreecommitdiff
path: root/drivers/net/wireless/digiPiper
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/digiPiper')
-rw-r--r--drivers/net/wireless/digiPiper/Kconfig27
-rw-r--r--drivers/net/wireless/digiPiper/Makefile16
-rw-r--r--drivers/net/wireless/digiPiper/adc121c027.c161
-rw-r--r--drivers/net/wireless/digiPiper/adc121c027.h17
-rw-r--r--drivers/net/wireless/digiPiper/airoha.c986
-rw-r--r--drivers/net/wireless/digiPiper/airoha.h30
-rw-r--r--drivers/net/wireless/digiPiper/airohaCalibration.c974
-rw-r--r--drivers/net/wireless/digiPiper/airohaCalibration.h108
-rw-r--r--drivers/net/wireless/digiPiper/digiDebug.c205
-rw-r--r--drivers/net/wireless/digiPiper/digiIsr.c159
-rw-r--r--drivers/net/wireless/digiPiper/digiMac80211.c1000
-rw-r--r--drivers/net/wireless/digiPiper/digiPs.c1164
-rw-r--r--drivers/net/wireless/digiPiper/digiPs.h50
-rw-r--r--drivers/net/wireless/digiPiper/digiRx.c412
-rw-r--r--drivers/net/wireless/digiPiper/digiTx.c604
-rw-r--r--drivers/net/wireless/digiPiper/mac.h375
-rw-r--r--drivers/net/wireless/digiPiper/phy.c269
-rw-r--r--drivers/net/wireless/digiPiper/phy.h28
-rw-r--r--drivers/net/wireless/digiPiper/piper.c1032
-rw-r--r--drivers/net/wireless/digiPiper/piperDsp.c285
-rw-r--r--drivers/net/wireless/digiPiper/piperDsp.h8
-rw-r--r--drivers/net/wireless/digiPiper/piperMacAssist.c285
-rw-r--r--drivers/net/wireless/digiPiper/piperMacAssist.h9
-rw-r--r--drivers/net/wireless/digiPiper/pipermain.h381
24 files changed, 8585 insertions, 0 deletions
diff --git a/drivers/net/wireless/digiPiper/Kconfig b/drivers/net/wireless/digiPiper/Kconfig
new file mode 100644
index 000000000000..aa0659f0e499
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/Kconfig
@@ -0,0 +1,27 @@
+config DIGI_PIPER_WIFI
+ bool "Digi Piper Wifi support"
+ depends on MAC80211 && WLAN_80211 && I2C && (MACH_CCW9P9215JS || MACH_CCW9M2443JS)
+ ---help---
+ This driver is for the Piper 802.11 MAC by Digi. This MAC is
+ supported on the Digi ConnectCore Wi-9P 9215 and Digi ConnectCore Wi-9M 2443
+ embedded modules.
+
+config PIPER_STATUS_LED
+ bool "Enable GPIO for Wifi status LED"
+ depends on DIGI_PIPER_WIFI && MACH_CCW9M2443JS
+ default y
+ help
+ ConnectCore Wi-9M 2443 does not provide any dedicated LED
+ in the module for showing the Wifi status.
+ This option lets you define one available CPU GPIO for this purpose.
+ Default value is set to USER LED 1 on JumpStart board.
+
+config PIPER_STATUS_LED_GPIO
+ int "GPIO for Wifi status LED (0-144)"
+ range 0 144
+ depends on PIPER_STATUS_LED
+ default "141"
+ help
+ Set CPU GPIO for Wifi status LED.
+
+ Default: User LED 1 on JumpStart board (LE5)
diff --git a/drivers/net/wireless/digiPiper/Makefile b/drivers/net/wireless/digiPiper/Makefile
new file mode 100644
index 000000000000..a3686b942088
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/Makefile
@@ -0,0 +1,16 @@
+
+mpiper-y := piper.o
+mpiper-y += airoha.o
+mpiper-y += digiDebug.o
+mpiper-y += digiIsr.o
+mpiper-y += digiTx.o
+mpiper-y += digiRx.o
+mpiper-y += digiMac80211.o
+mpiper-y += piperDsp.o
+mpiper-y += piperMacAssist.o
+mpiper-y += phy.o
+mpiper-y += adc121c027.o
+mpiper-y += airohaCalibration.o
+mpiper-y += digiPs.o
+obj-$(CONFIG_DIGI_PIPER_WIFI) := mpiper.o
+
diff --git a/drivers/net/wireless/digiPiper/adc121c027.c b/drivers/net/wireless/digiPiper/adc121c027.c
new file mode 100644
index 000000000000..17564bcd7a66
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/adc121c027.c
@@ -0,0 +1,161 @@
+/*
+ adc121C027.c - Analog to Digital converter integrated into Piper.
+
+ Copyright (C) 2009 Digi International <sales2@digi.com>
+
+ 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; version 2 of the License.
+*/
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/hwmon-sysfs.h>
+
+#include "airohaCalibration.h"
+#include "pipermain.h"
+
+/* Addresses to scan: none, device is not autodetected */
+/* static const unsigned short normal_i2c[] = { I2C_CLIENT_END }; */
+
+#define ADC_I2C_ADDR (0x51)
+#define ADC_CYCLE_TIME (0x20)
+
+static const unsigned short normal_i2c[] = { ADC_I2C_ADDR, I2C_CLIENT_END };
+static const unsigned short dummy_i2c_addrlist[] = { I2C_CLIENT_END };
+
+static struct i2c_client_address_data addr = {
+ .normal_i2c = normal_i2c,
+ .probe = dummy_i2c_addrlist,
+ .ignore = dummy_i2c_addrlist,
+};
+
+enum adc121C027_cmd {
+ ADC_RESULT = 0,
+ ADC_ALERT_STATUS = 1,
+ ADC_CONFIGURATION = 2,
+ ADC_LOW_LIMIT = 3,
+ ADC_HIGH_LIMIT = 4,
+ ADC_HYSTERESIS = 5,
+ ADC_LOWEST_VALUE = 6,
+ ADC_HIGHEST_VALUE = 7,
+};
+
+static u16 adc121C027_read_peak(struct airohaCalibrationData *cal)
+{
+ struct i2c_client *i2cclient = (struct i2c_client *)cal->priv;
+
+ return be16_to_cpu(i2c_smbus_read_word_data(i2cclient, ADC_HIGHEST_VALUE));
+}
+
+static void adc121C027_clear_peak(struct airohaCalibrationData *cal)
+{
+ struct i2c_client *i2cclient = (struct i2c_client *)cal->priv;
+
+ i2c_smbus_write_word_data(i2cclient, ADC_HIGHEST_VALUE, 0);
+}
+
+static u16 adc121C027_read_last_sample(struct airohaCalibrationData *cal)
+{
+ struct i2c_client *i2cclient = (struct i2c_client *)cal->priv;
+
+ return be16_to_cpu(i2c_smbus_read_word_data(i2cclient, ADC_RESULT));
+}
+
+static int adc121C027_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ return (client->addr != ADC_I2C_ADDR) ? -EINVAL : 0;
+}
+
+static int adc121C027_remove(struct i2c_client *client)
+{
+ /* Real shut down will be done by adc121C027_shutdown() */
+ return 0;
+}
+
+static const struct i2c_device_id adc121C027_id[] = {
+ { "adc121C027", 0 },
+ {}
+};
+
+static struct i2c_driver adc121C027_driver = {
+ .driver = {
+ .name = "adc121C027",
+ },
+ .probe = adc121C027_probe,
+ .remove = __devexit_p(adc121C027_remove),
+ .id_table = adc121C027_id,
+ .address_data = &addr,
+};
+
+/* Turn on automatic A/D process by setting a non zero cycle time */
+static void adc121C027_hw_init(struct airohaCalibrationData *cal)
+{
+ struct i2c_client *i2cclient = (struct i2c_client *)cal->priv;
+
+ i2c_smbus_write_word_data(i2cclient, ADC_CONFIGURATION, ADC_CYCLE_TIME);
+}
+
+void adc121C027_shutdown(struct airohaCalibrationData *cal)
+{
+ struct i2c_client *i2cclient = (struct i2c_client *)cal->priv;
+
+ if (i2cclient) {
+ i2c_unregister_device(i2cclient);
+ cal->priv = NULL;
+ }
+ i2c_del_driver(&adc121C027_driver);
+}
+
+int adc121C027_init(struct airohaCalibrationData *cal, int i2cadapter)
+{
+ struct i2c_board_info board_info = {
+ .type = "adc121C027",
+ .addr = ADC_I2C_ADDR,
+ };
+ struct i2c_adapter *adapter;
+ struct i2c_client *adc_i2c_client;
+ int ret;
+
+ ret = i2c_add_driver(&adc121C027_driver);
+ if (ret) {
+ printk(KERN_WARNING PIPER_DRIVER_NAME
+ ": error adding driver adc121C027_driver (%d)\n", ret);
+ return ret;
+ }
+
+ adapter = i2c_get_adapter(i2cadapter);
+ if (!adapter) {
+ printk(KERN_WARNING PIPER_DRIVER_NAME
+ ": error getting i2c adapter\n");
+ return -EINVAL;
+ }
+
+ adc_i2c_client = i2c_new_device(adapter, &board_info);
+ if (!adc_i2c_client) {
+ printk(KERN_WARNING PIPER_DRIVER_NAME
+ ": error creating new i2c client\n");
+ return -EINVAL;
+ }
+
+ cal->priv = (void *)adc_i2c_client;
+ adc121C027_hw_init(cal);
+
+ cal->cops = kmalloc(sizeof(struct calibration_ops), GFP_KERNEL);
+ if (!cal->cops) {
+ printk(KERN_WARNING PIPER_DRIVER_NAME
+ ": unable to allocate memory for cal->cops\n");
+ return -ENOMEM;
+ }
+ cal->cops->adc_read_peak = adc121C027_read_peak;
+ cal->cops->adc_clear_peak = adc121C027_clear_peak;
+ cal->cops->adc_read_last_val = adc121C027_read_last_sample;
+ cal->cops->adc_shutdown = adc121C027_shutdown;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(adc121C027_init);
+
+
diff --git a/drivers/net/wireless/digiPiper/adc121c027.h b/drivers/net/wireless/digiPiper/adc121c027.h
new file mode 100644
index 000000000000..1453d8a66963
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/adc121c027.h
@@ -0,0 +1,17 @@
+/*
+ adc121C027.h - Analog to Digital converter integrated into Piper.
+
+ Copyright (C) 2009 Digi International <sales2@digi.com>
+
+ 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; version 2 of the License.
+*/
+
+#ifndef ADC121C027_H
+#define ADC121C027_H
+
+int adc121C027_init(struct airohaCalibrationData *cal, int i2cadapter);
+
+#endif /* ADC121C027_H */
+
diff --git a/drivers/net/wireless/digiPiper/airoha.c b/drivers/net/wireless/digiPiper/airoha.c
new file mode 100644
index 000000000000..17ef8a812f98
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/airoha.c
@@ -0,0 +1,986 @@
+/*
+ * Ubec AH7230 radio support.
+ *
+ * Copyright © 2009 Digi International, Inc
+ *
+ * Author: Contact support@digi.com for information about this software.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <net/mac80211.h>
+#include <net/wireless.h>
+
+#include "pipermain.h"
+#include "mac.h"
+#include "airohaCalibration.h"
+#include "airoha.h"
+
+/*
+ * Number of us to change channels. I counted the number of udelays once
+ * and it was about 2030, plus the 1 us delays for each register write.
+ * So probably about 2200 in reality, I'm saying 2500 to be safe.
+ */
+#define CHANNEL_CHANGE_TIME (2500)
+
+/*
+ * Maximum possible receive signal strength in dbm. Most of the
+ * values will be negative.
+ */
+#define MAX_SIGNAL_IN_DBM (5)
+
+#define read_reg(reg) priv->ac->rd_reg(priv,reg)
+#define write_reg(reg,val,op) priv->ac->wr_reg(priv,reg,val,op)
+#define mac_set_tx_power(x) al7230_set_txpwr(hw,x)
+
+static unsigned int hw_revision = WCD_HW_REV_A;
+static unsigned int hw_platform = WCD_CCW9P_PLATFORM;
+
+static void InitializeRF(struct ieee80211_hw *hw, int band_selection);
+static int al7230_set_txpwr(struct ieee80211_hw *hw, uint8_t val);
+
+static const struct {
+ unsigned int integer;
+ unsigned int fraction;
+ unsigned int address4;
+ unsigned int tracking;
+} freqTableAiroha_7230[] = {
+ { 0, 0, 0, 0 }, // 0
+
+ // 2.4 GHz band (802.11b/g)
+ { 0x00379, 0x13333, 0x7FD78, TRACK_BG_BAND }, // B-1 (2412 MHz) 1
+ { 0x00379, 0x1B333, 0x7FD78, TRACK_BG_BAND }, // B-2 (2417 MHz) 2
+ { 0x00379, 0x03333, 0x7FD78, TRACK_BG_BAND }, // B-3 (2422 MHz) 3
+ { 0x00379, 0x0B333, 0x7FD78, TRACK_BG_BAND }, // B-4 (2427 MHz) 4
+ { 0x0037A, 0x13333, 0x7FD78, TRACK_BG_BAND }, // B-5 (2432 MHz) 5
+ { 0x0037A, 0x1B333, 0x7FD78, TRACK_BG_BAND }, // B-6 (2437 MHz) 6
+ { 0x0037A, 0x03333, 0x7FD78, TRACK_BG_BAND }, // B-7 (2442 MHz) 7
+ { 0x0037A, 0x0B333, 0x7FD78, TRACK_BG_BAND }, // B-8 (2447 MHz) 8
+ { 0x0037B, 0x13333, 0x7FD78, TRACK_BG_BAND }, // B-9 (2452 MHz) 9
+ { 0x0037B, 0x1B333, 0x7FD78, TRACK_BG_BAND }, // B-10 (2457 MHz) 10
+ { 0x0037B, 0x03333, 0x7FD78, TRACK_BG_BAND }, // B-11 (2462 MHz) 11
+ { 0x0037B, 0x0B333, 0x7FD78, TRACK_BG_BAND }, // B-12 (2467 MHz) 12
+ { 0x0037C, 0x13333, 0x7FD78, TRACK_BG_BAND }, // B-13 (2472 MHz) 13
+ { 0x0037C, 0x06666, 0x7FD78, TRACK_BG_BAND }, // B-14 (2484 MHz) 14
+
+ { 0, 0, 0, 0 }, // reserved for future b/g expansion 15
+ { 0, 0, 0, 0 }, // reserved for future b/g expansion 16
+
+ // Extended 4 GHz bands (802.11a) - Lower Band
+ { 0x0FF52, 0x00000, 0x67F78, TRACK_4920_4980_A_BAND }, // L-184 (4920 MHz) 17
+ { 0x0FF52, 0x0AAAA, 0x77F78, TRACK_4920_4980_A_BAND }, // L-188 (4940 MHz) 18
+ { 0x0FF53, 0x15555, 0x77F78, TRACK_4920_4980_A_BAND }, // L-192 (4960 MHz) 19
+ { 0x0FF53, 0x00000, 0x67F78, TRACK_4920_4980_A_BAND }, // L-196 (4980 MHz) 20
+
+ // Extended 5 GHz bands (802.11a)
+ { 0x0FF54, 0x00000, 0x67F78, TRACK_5150_5350_A_BAND }, // A-8 (5040 MHz) 21 tracking?
+ { 0x0FF54, 0x0AAAA, 0x77F78, TRACK_5150_5350_A_BAND }, // A-12 (5060 MHz) 22 tracking?
+ { 0x0FF55, 0x15555, 0x77F78, TRACK_5150_5350_A_BAND }, // A-16 (5080 MHz) 23 tracking?
+ { 0x0FF56, 0x05555, 0x77F78, TRACK_5150_5350_A_BAND }, // A-34 (5170 MHz) 24
+ { 0x0FF56, 0x0AAAA, 0x77F78, TRACK_5150_5350_A_BAND }, // A-36 (5180 MHz) 25
+ { 0x0FF57, 0x10000, 0x77F78, TRACK_5150_5350_A_BAND }, // A-38 (5190 MHz) 26
+ { 0x0FF57, 0x15555, 0x77F78, TRACK_5150_5350_A_BAND }, // A-40 (5200 MHz) 27
+ { 0x0FF57, 0x1AAAA, 0x77F78, TRACK_5150_5350_A_BAND }, // A-42 (5210 MHz) 28
+ { 0x0FF57, 0x00000, 0x67F78, TRACK_5150_5350_A_BAND }, // A-44 (5220 MHz) 29
+ { 0x0FF57, 0x05555, 0x77F78, TRACK_5150_5350_A_BAND }, // A-46 (5230 MHz) 30
+ { 0x0FF57, 0x0AAAA, 0x77F78, TRACK_5150_5350_A_BAND }, // A-48 (5240 MHz) 31
+
+ { 0x0FF58, 0x15555, 0x77F78, TRACK_5150_5350_A_BAND }, // A-52 (5260 MHz) 32
+ { 0x0FF58, 0x00000, 0x67F78, TRACK_5150_5350_A_BAND }, // A-56 (5280 MHz) 33
+ { 0x0FF58, 0x0AAAA, 0x77F78, TRACK_5150_5350_A_BAND }, // A-60 (5300 MHz) 34
+ { 0x0FF59, 0x15555, 0x77F78, TRACK_5150_5350_A_BAND }, // A-64 (5320 MHz) 35
+
+ { 0x0FF5C, 0x15555, 0x77F78, TRACK_5470_5725_A_BAND }, // A-100 (5500 MHz) 36
+ { 0x0FF5C, 0x00000, 0x67F78, TRACK_5470_5725_A_BAND }, // A-104 (5520 MHz) 37
+ { 0x0FF5C, 0x0AAAA, 0x77F78, TRACK_5470_5725_A_BAND }, // A-108 (5540 MHz) 38
+ { 0x0FF5D, 0x15555, 0x77F78, TRACK_5470_5725_A_BAND }, // A-112 (5560 MHz) 39
+ { 0x0FF5D, 0x00000, 0x67F78, TRACK_5470_5725_A_BAND }, // A-116 (5580 MHz) 40
+ { 0x0FF5D, 0x0AAAA, 0x77F78, TRACK_5470_5725_A_BAND }, // A-120 (5600 MHz) 41
+ { 0x0FF5E, 0x15555, 0x77F78, TRACK_5470_5725_A_BAND }, // A-124 (5620 MHz) 42
+ { 0x0FF5E, 0x00000, 0x67F78, TRACK_5470_5725_A_BAND }, // A-128 (5640 MHz) 43
+ { 0x0FF5E, 0x0AAAA, 0x77F78, TRACK_5470_5725_A_BAND }, // A-132 (5660 MHz) 44
+ { 0x0FF5F, 0x15555, 0x77F78, TRACK_5470_5725_A_BAND }, // A-136 (5680 MHz) 45
+ { 0x0FF5F, 0x00000, 0x67F78, TRACK_5470_5725_A_BAND }, // A-140 (5700 MHz) 46
+
+ { 0x0FF60, 0x18000, 0x77F78, TRACK_5725_5825_A_BAND }, // A-149 (5745 MHz) 47
+ { 0x0FF60, 0x02AAA, 0x77F78, TRACK_5725_5825_A_BAND }, // A-153 (5765 MHz) 48
+ { 0x0FF60, 0x0D555, 0x77F78, TRACK_5725_5825_A_BAND }, // A-157 (5785 MHz) 49
+ { 0x0FF61, 0x18000, 0x77F78, TRACK_5725_5825_A_BAND }, // A-161 (5805 MHz) 50
+ { 0x0FF61, 0x02AAA, 0x77F78, TRACK_5725_5825_A_BAND }, // A-165 (5825 MHz) 51
+};
+
+#define CHAN4G(idx, _freq) \
+ .band = IEEE80211_BAND_2GHZ, \
+ .center_freq = (_freq), \
+ .hw_value = idx, \
+ .max_antenna_gain = 0, \
+ .max_power = 12
+
+static struct ieee80211_channel al7230_bg_channels[] = {
+ { CHAN4G(1, 2412) },
+ { CHAN4G(2, 2417) },
+ { CHAN4G(3, 2422) },
+ { CHAN4G(4, 2427) },
+ { CHAN4G(5, 2432) },
+ { CHAN4G(6, 2437) },
+ { CHAN4G(7, 2442) },
+ { CHAN4G(8, 2447) },
+ { CHAN4G(9, 2452) },
+ { CHAN4G(10, 2457) },
+ { CHAN4G(11, 2462) },
+ { CHAN4G(12, 2467) },
+ { CHAN4G(13, 2472) },
+ { CHAN4G(14, 2484) },
+};
+
+static const struct ieee80211_rate al7230_bg_rates[] = {
+ /* psk/cck rates */
+ {
+ .bitrate = 10,
+ .flags = IEEE80211_RATE_SHORT_PREAMBLE,
+ },
+ {
+ .bitrate = 20,
+ .flags = IEEE80211_RATE_SHORT_PREAMBLE,
+ },
+ {
+ .bitrate = 55,
+ .flags = IEEE80211_RATE_SHORT_PREAMBLE,
+ },
+ {
+ .bitrate = 110,
+ .flags = IEEE80211_RATE_SHORT_PREAMBLE,
+ },
+ /* ofdm rates */
+ {
+ .bitrate = 60,
+ .hw_value = 0xb,
+ },
+ {
+ .bitrate = 90,
+ .hw_value = 0xf,
+ },
+ {
+ .bitrate = 120,
+ .hw_value = 0xa,
+ },
+ {
+ .bitrate = 180,
+ .hw_value = 0xe,
+ },
+ {
+ .bitrate = 240,
+ .hw_value = 0x9,
+ },
+ {
+ .bitrate = 360,
+ .hw_value = 0xd,
+ },
+ {
+ .bitrate = 480,
+ .hw_value = 0x8,
+ },
+ {
+ .bitrate = 540,
+ .hw_value = 0xc,
+ },
+};
+
+#define CHAN5G(idx, frequency) \
+ .band = IEEE80211_BAND_5GHZ, \
+ .center_freq = frequency, \
+ .max_antenna_gain = 0, \
+ .max_power = 8, \
+ .hw_value = idx
+
+static struct ieee80211_channel al7230_a_channels[] = {
+ { CHAN5G(17, 4920) },
+ { CHAN5G(18, 4940) },
+ { CHAN5G(19, 4960) },
+ { CHAN5G(20, 4980) },
+
+ { CHAN5G(21, 5040) },
+ { CHAN5G(22, 5060) },
+ { CHAN5G(23, 5080) },
+ { CHAN5G(24, 5170) },
+ { CHAN5G(25, 5180) },
+ { CHAN5G(26, 5190) },
+ { CHAN5G(27, 5200) },
+ { CHAN5G(28, 5210) },
+ { CHAN5G(29, 5220) },
+ { CHAN5G(30, 5230) },
+ { CHAN5G(31, 5240) },
+
+ { CHAN5G(32, 5260) },
+ { CHAN5G(33, 5280) },
+ { CHAN5G(34, 5300) },
+ { CHAN5G(35, 5320) },
+
+ { CHAN5G(36, 5500) },
+ { CHAN5G(37, 5520) },
+ { CHAN5G(38, 5540) },
+ { CHAN5G(39, 5560) },
+ { CHAN5G(40, 5580) },
+ { CHAN5G(41, 5600) },
+ { CHAN5G(42, 5620) },
+ { CHAN5G(43, 5640) },
+ { CHAN5G(44, 5660) },
+ { CHAN5G(45, 5680) },
+ { CHAN5G(46, 5700) },
+
+ { CHAN5G(47, 5745) },
+ { CHAN5G(48, 5765) },
+ { CHAN5G(49, 5785) },
+ { CHAN5G(50, 5805) },
+ { CHAN5G(51, 5825) }
+};
+
+static const struct ieee80211_rate al7230_a_rates[] = {
+ /* ofdm rates */
+ {
+ .bitrate = 60,
+ .hw_value = 0xb,
+ },
+ {
+ .bitrate = 90,
+ .hw_value = 0xf,
+ },
+ {
+ .bitrate = 120,
+ .hw_value = 0xa,
+ },
+ {
+ .bitrate = 180,
+ .hw_value = 0xe,
+ },
+ {
+ .bitrate = 240,
+ .hw_value = 0x9,
+ },
+ {
+ .bitrate = 360,
+ .hw_value = 0xd,
+ },
+ {
+ .bitrate = 480,
+ .hw_value = 0x8,
+ },
+ {
+ .bitrate = 540,
+ .hw_value = 0xc,
+ },
+};
+
+static enum ieee80211_band getBand(int channelIndex)
+{
+ enum ieee80211_band result;
+
+ if (channelIndex >= BAND_A_OFFSET) {
+ result = IEEE80211_BAND_5GHZ;
+ } else {
+ result = IEEE80211_BAND_2GHZ;
+ }
+
+ return result;
+}
+
+static int getFrequency(int channelIndex)
+{
+ int result;
+
+ if (getBand(channelIndex) == IEEE80211_BAND_5GHZ) {
+ result = al7230_a_channels[channelIndex - BAND_A_OFFSET].center_freq;
+ } else {
+ result = al7230_bg_channels[channelIndex - 1].center_freq;
+ }
+
+ return result;
+}
+
+static int write_rf(struct ieee80211_hw *hw, unsigned char reg, unsigned int val)
+{
+ struct piper_priv *priv = hw->priv;
+ int err;
+
+ err = write_reg(BB_SPI_DATA, val << 4 | reg, op_write);
+ udelay(3); /* Mike Schaffner says to allow 2 us or more between all writes */
+ return err;
+}
+
+
+/*
+ * This function is called to set the value of Airoha register
+ * 0xc. This register must be set to different values depending
+ * on the H/W revision of the board due to changes in the board
+ * design.
+ */
+static void set_hw_specific_parameters(struct ieee80211_hw *hw,
+ unsigned int band,
+ unsigned int hw_revision,
+ unsigned int hw_platform)
+{
+ switch (hw_platform) {
+ case WCD_CCW9P_PLATFORM:
+ switch (hw_revision) {
+ case WCD_HW_REV_PROTOTYPE:
+ case WCD_HW_REV_PILOT:
+ case WCD_HW_REV_A:
+ default:
+ if (band == IEEE80211_BAND_2GHZ) {
+ write_rf(hw, 0xc, 0x2b);
+ } else {
+ write_rf(hw, 0xc, 0x00143 );
+ }
+ break;
+ }
+ break;
+ case WCD_CCW9M_PLATFORM:
+ switch (hw_revision) {
+ case WCD_HW_REV_PROTOTYPE:
+ case WCD_HW_REV_PILOT:
+ if (band == IEEE80211_BAND_2GHZ) {
+ write_rf(hw, 0xc, 0xa3);
+ } else {
+ write_rf(hw, 0xc, 0x00143 );
+ }
+ break;
+
+ case WCD_HW_REV_A:
+ default:
+ if (band == IEEE80211_BAND_2GHZ) {
+ write_rf(hw, 0xc, 0x70);
+ } else {
+ write_rf(hw, 0xc, 0x00143 );
+ }
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static int al7230_rf_set_chan_private(struct ieee80211_hw *hw, int channelIndex, bool enable_rx)
+{
+ struct piper_priv *priv = hw->priv;
+ static int rf_band;
+#ifdef WANT_DEBUG
+ const char *channelLookup[] = {
+ "invalid 0",
+ "B-1",
+ "B-2",
+ "B-3",
+ "B-4",
+ "B-5",
+ "B-6",
+ "B-7",
+ "B-8",
+ "B-9",
+ "B-10",
+ "B-11",
+ "B-12",
+ "B-13",
+ "B-14",
+ "invalid 15",
+ "invalid 16",
+ "L-184",
+ "L-188",
+ "L-192",
+ "L-196",
+ "A-8",
+ "A-12",
+ "A-16",
+ "A-34",
+ "A-36",
+ "A-38",
+ "A-40",
+ "A-42",
+ "A-44",
+ "A-46",
+ "A-48",
+ "A-52",
+ "A-56",
+ "A-60",
+ "A-64",
+ "A-100",
+ "A-104",
+ "A-108",
+ "A-112",
+ "A-116",
+ "A-120",
+ "A-124",
+ "A-128",
+ "A-132",
+ "A-136",
+ "A-140",
+ "A-149",
+ "A-153",
+ "A-157",
+ "A-161",
+ "A-165"
+ };
+printk(KERN_ERR "Setting channel %s\n", channelLookup[channelIndex]);
+#endif
+ if (channelIndex >= BAND_A_OFFSET)
+ rf_band = IEEE80211_BAND_5GHZ;
+ else
+ rf_band = IEEE80211_BAND_2GHZ;
+ /* Disable the rx processing path */
+ write_reg(BB_GENERAL_CTL, ~BB_GENERAL_CTL_RX_EN, op_and);
+
+ write_reg(BB_OUTPUT_CONTROL, 0xfffff33f, op_and);
+ write_reg(BB_OUTPUT_CONTROL, 0x00000880, op_or);
+
+ if (priv->pdata->rf_transceiver == RF_AIROHA_2236) {
+/* TODO, when using this transceiver, resolve this commented code */
+#ifdef BUILD_THIS_CODE_SECTION
+ write_reg(BB_GENERAL_STAT, BB_GENERAL_STAT_B_EN, op_or);
+
+ if (macParams.band == WLN_BAND_B) {
+ /* turn off OFDM */
+ write_reg(BB_GENERAL_STAT, ~BB_GENERAL_STAT_A_EN, op_and);
+ } else {
+ /* turn on OFDM */
+ write_reg(BB_GENERAL_STAT, BB_GENERAL_STAT_A_EN, op_or);
+ }
+ /* set the 802.11b/g frequency band specific tracking constant */
+ write_reg(BB_TRACK_CONTROL, 0xff00ffff, op_and);
+
+ write_reg(BB_TRACK_CONTROL, TRACK_BG_BAND, op_or);
+
+ /* perform chip and frequency-band specific RF initialization */
+ InitializeRF(hw, rf_band);
+
+ mac_set_tx_power(priv->tx_power);
+
+ write_rf(hw, 0, freqTableAiroha_2236[channelIndex].integer);
+ write_rf(hw, 1, freqTableAiroha_2236[channelIndex].fraction);
+
+ /* critical delay for correct calibration */
+ udelay(150);
+
+ /*
+ * TXON, PAON and RXON should all be low before Calibration
+ * TXON and PAON will be low as long as no frames are written to the TX
+ * DATA fifo.
+ * RXON will be low as long as the receive path is not enabled (bit 0 of
+ * GEN CTL register is 0).
+ */
+
+ /* calibrate RF transceiver */
+
+ /* TXDCOC->active; RCK->disable */
+ write_rf(hw, 15, 0x00D87);
+ udelay(50);
+ /* TXDCOC->disable; RCK->enable */
+ write_rf(hw, 15, 0x00787);
+ udelay(50);
+ /* TXDCOC->disable; RCK->disable */
+ write_rf(hw, 15, 0x00587);
+ udelay(50);
+
+ /* configure the baseband processing engine */
+ write_reg(BB_GENERAL_CTL, ~BB_GENERAL_CTL_GEN_5GEN, op_and);
+
+ /*Re-enable the rx processing path */
+ write_reg(BB_GENERAL_CTL, BB_GENERAL_CTL_RX_EN, op_or);
+#endif
+ } else if (priv->pdata->rf_transceiver == RF_AIROHA_7230) {
+ /* enable the frequency-band specific PA */
+ if (rf_band == IEEE80211_BAND_2GHZ) {
+ //HW_GEN_CONTROL &= ~GEN_PA_ON;
+ write_reg(BB_GENERAL_STAT, BB_GENERAL_STAT_A_EN | BB_GENERAL_STAT_B_EN,
+ op_or);
+
+ /* set the 802.11b/g frequency band specific tracking constant */
+ write_reg(BB_TRACK_CONTROL, 0xff00ffff, op_and);
+
+ write_reg(BB_TRACK_CONTROL, TRACK_BG_BAND, op_or);
+
+ } else {
+ //HW_GEN_CONTROL |= GEN_PA_ON;
+
+ // turn off PSK/CCK
+ write_reg(BB_GENERAL_STAT, ~BB_GENERAL_STAT_B_EN, op_and);
+
+ // turn on OFDM
+ write_reg(BB_GENERAL_STAT, BB_GENERAL_STAT_A_EN, op_or);
+
+ /* Set the 802.11a frequency sub-band specific tracking constant */
+ /* All 8 supported 802.11a channels are in this 802.11a frequency sub-band */
+ write_reg(BB_TRACK_CONTROL, 0xff00ffff, op_and);
+
+ write_reg(BB_TRACK_CONTROL, freqTableAiroha_7230[channelIndex].tracking,
+ op_or);
+ }
+
+ /* perform chip and frequency-band specific RF initialization */
+ InitializeRF(hw, rf_band);
+
+ mac_set_tx_power(priv->tx_power);
+
+ /* Set the channel frequency */
+ write_rf(hw, 0, freqTableAiroha_7230[channelIndex].integer);
+ udelay(150); /* Mike Schaffner says this is needed here */
+ write_rf(hw, 1, freqTableAiroha_7230[channelIndex].fraction);
+ udelay(150); /* Mike Schaffner says this is needed here */
+ write_rf(hw, 4, freqTableAiroha_7230[channelIndex].address4);
+ udelay(150); /* Mike Schaffner says this is needed here */
+
+
+ // Select the frequency band: 5Ghz or 2.4Ghz
+ if (rf_band == IEEE80211_BAND_5GHZ) {
+ /* calibrate RF transceiver */
+
+ /* TXDCOC->active; RCK->disable */
+ write_rf(hw, 15, 0x9ABA8);
+ udelay(50);
+
+ /* TXDCOC->disable; RCK->enable */
+ write_rf(hw, 15, 0x3ABA8);
+ udelay(50);
+
+ /* TXDCOC->disable; RCK->disable */
+ write_rf(hw, 15, 0x12BAC);
+ udelay(50);
+
+ /* configure the baseband processing engine */
+ /*
+ * This bit always as to be turned off when we are using
+ * the Airoha chip, even though it's named the 5G EN bit.
+ * It has to do with how they hooked up the Airoha.
+ */
+ write_reg(BB_GENERAL_CTL, ~BB_GENERAL_CTL_GEN_5GEN, op_and);
+ } else {
+ /* calibrate RF transceiver */
+
+ /* TXDCOC->active; RCK->disable */
+ write_rf(hw, 15, 0x9ABA8);
+ udelay(50);
+
+ /* TXDCOC->disable; RCK->enable */
+ write_rf(hw, 15, 0x3ABA8);
+ udelay(50);
+
+ /* TXDCOC->disable; RCK->disable */
+ write_rf(hw, 15, 0x1ABA8);
+ udelay(50);
+
+ /* configure the baseband processing engine */
+ write_reg(BB_GENERAL_CTL, ~BB_GENERAL_CTL_GEN_5GEN, op_and);
+ /*
+ * No short preambles allowed for ODFM.
+ */
+ write_reg(BB_GENERAL_CTL, ~BB_GENERAL_CTL_SH_PRE, op_and);
+ }
+
+ /*Re-enable the rx processing path */
+ if (enable_rx)
+ write_reg(BB_GENERAL_CTL, BB_GENERAL_CTL_RX_EN, op_or);
+
+ /* re-enable transmitter */
+ write_reg(BB_OUTPUT_CONTROL, 0xfffff33f, op_and);
+ } else {
+ printk(KERN_WARNING PIPER_DRIVER_NAME ": undefined rf transceiver!\n");
+ return -EINVAL;
+ }
+
+ /*
+ * This is a patch for a problem which should be corrected in
+ * hardware on new units. We are rewriting the MAC address
+ * because on units without the H/W patch the address can
+ * be corrupted when we change channels.
+ */
+ piper_set_macaddr(priv);
+digiWifiDumpRegisters(priv, MAIN_REGS);
+
+ return 0;
+}
+
+static int al7230_rf_set_chan(struct ieee80211_hw *hw, int channelIndex)
+{
+ return al7230_rf_set_chan_private(hw, channelIndex, true);
+}
+
+static int al7230_rf_set_chan_no_rx(struct ieee80211_hw *hw, int channelIndex)
+{
+ return al7230_rf_set_chan_private(hw, channelIndex, false);
+}
+
+
+
+static int al7230_set_txpwr(struct ieee80211_hw *hw, uint8_t value)
+{
+ struct piper_priv *priv = hw->priv;
+
+ if (priv->pdata->rf_transceiver == RF_AIROHA_2236) {
+ const unsigned char powerTable_2236[] = {
+ 4, 10, 10, 18, 22, 22, 28, 28,
+ 33, 33, 36, 38, 40, 43, 45, 47
+ };
+ write_rf(hw, 9, 0x05440 | powerTable_2236[value & 0xf]);
+ } else if (priv->pdata->rf_transceiver == RF_AIROHA_7230) {
+ const unsigned char powerTable_7230[] = {
+ 0x14, 0x14, 0x14, 0x18, 0x18, 0x1c, 0x1c, 0x20,
+ 0x20, 0x24, 0x24, 0x29, 0x29, 0x2c, 0x2c, 0x30
+ };
+ int correctedPowerIndex = digiWifiCalibrationPowerIndex(priv);
+
+ if (correctedPowerIndex != -1) {
+ write_rf(hw, 11, 0x08040 | correctedPowerIndex);
+ } else {
+ write_rf(hw, 11, 0x08040 | powerTable_7230[value & 0xf]);
+ }
+ } else {
+ printk(KERN_WARNING PIPER_DRIVER_NAME
+ ": undefined rf transceiver!\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void al7230_set_power_index(struct ieee80211_hw *hw, unsigned int value)
+{
+ write_rf(hw, 11, 0x08040 | value);
+}
+
+static void InitializeRF(struct ieee80211_hw *hw, int band_selection)
+{
+ struct piper_priv *priv = hw->priv;
+
+ if (priv->pdata->rf_transceiver == RF_AIROHA_2236) {
+ digi_dbg("**** transceiver == RF_AIROHA_2236\n");
+ /* Initial settings for 20 MHz reference frequency, 802.11b/g */
+
+ /* CH_integer: Frequency register 0 */
+ write_rf(hw, 0, 0x01f79 );
+
+ /* CH_fraction: Frequency register 1 */
+ write_rf(hw, 1, 0x03333 );
+
+ /*Config 1 = default value */
+ write_rf(hw, 2, 0x00B80 );
+
+ /*Config 2 = default value */
+ write_rf(hw, 3, 0x00E7F );
+
+ /*Config 3 = default value */
+ write_rf(hw, 4, 0x0905A );
+
+ /*Config 4 = default value */
+ write_rf(hw, 5, 0x0F4DC );
+
+ /*Config 5 = default value */
+ write_rf(hw, 6, 0x0805B );
+
+ /*Config 6 = Crystal frequency /2 to pll reference divider */
+ write_rf(hw, 7, 0x0116C );
+
+ /*Config 7 = RSSI = default value */
+ write_rf(hw, 8, 0x05B68 );
+
+ /* TX gain control for LA2236 */
+ write_rf(hw, 9, 0x05460 ); // sit at the middle
+
+ /* RX Gain = digi specific value: AGC adjustment is done over the GC1-GC7
+ IC pins interface. AGC MAX GAIN value is configured in the FPGA BB register
+ instead of the RF register here below */
+ write_rf(hw, 10, 0x001BB );
+
+ /* TX Gain = digi specific vaue: TX GAIN set using the register */
+ write_rf(hw, 11, 0x000f9 );
+
+ /* PA current = default value */
+ write_rf(hw, 12, 0x039D8 );
+
+ /* Config 8 = default value */
+ write_rf(hw, 13, 0x08000 );
+
+ /* Config 9 = default value */
+ write_rf(hw, 14, 0x00000 );
+
+ /* Config 10 = default value */
+ write_rf(hw, 15, 0x00587 );
+
+ //mac_set_tx_power (macParams.tx_power);
+
+ /* Calibration procedure */
+ write_reg(BB_OUTPUT_CONTROL, 0x00000300, op_or);
+ udelay(150);
+
+ /* TXDCOC->active; RCK->disable */
+ write_rf(hw, 15, 0x00D87 );
+ udelay(50);
+
+ /* TXDCOC->disable; RCK->enable */
+ write_rf(hw, 15, 0x00787 );
+ udelay(50);
+
+ /* TXDCOC->disable; RCK->disable */
+ write_rf(hw, 15, 0x00587 );
+ udelay(50);
+ } else if (priv->pdata->rf_transceiver == RF_AIROHA_7230) {
+ switch (band_selection) {
+ case IEEE80211_BAND_2GHZ:
+ /* Initial settings for 20 MHz reference frequency, 802.11b/g */
+ write_reg(BB_OUTPUT_CONTROL, 0xfffffcff, op_and);
+ write_reg(BB_OUTPUT_CONTROL, 0x00000200, op_or);
+ udelay(150);
+
+ /* Frequency register 0 */
+ write_rf(hw, 0, 0x00379 );
+
+ /* Frequency register 1 */
+ write_rf(hw, 1, 0x13333 );
+ udelay(10);
+
+ /*Config 1 = default value */
+ write_rf(hw, 2, 0x841FF );
+
+ /*Config 2 = default value */
+ write_rf(hw, 3, 0x3FDFA );
+
+ /*Config 3 = default value */
+ write_rf(hw, 4, 0x7FD78 );
+
+ /*Config 4 = default value */
+ write_rf(hw, 5, 0x802BF );
+
+ /*Config 5 = default value */
+ write_rf(hw, 6, 0x56AF3 );
+
+ /*Config 6 = Crystal frequency /2 to pll reference divider */
+ write_rf(hw, 7, 0xCE000 );
+
+ /*Config 7 = RSSI = default value */
+ write_rf(hw, 8, 0x6EBC0 );
+
+ /* Filter BW = default value */
+ write_rf(hw, 9, 0x221BB );
+
+ /* RX Gain = digi specific value: AGC adjustment is done over the GC1-GC7
+ IC pins interface. AGC MAX GAIN value is configured in the FPGA BB register
+ instead of the RF register here below */
+ write_rf(hw, 10, 0xE0040 );
+
+ /* TX Gain = digi specific vaue: TX GAIN set using the register */
+ // write_rf(hw, 11, 0x08070);
+ mac_set_tx_power (priv->tx_power); //Digi value
+
+ /* PA current = default value */
+ set_hw_specific_parameters(hw, IEEE80211_BAND_2GHZ, hw_revision, hw_platform);
+
+ /* Config 8 = default value */
+ write_rf(hw, 13, 0xFFFFF );
+
+ /* Config 9 = default value */
+ write_rf(hw, 14, 0x00000 );
+
+ /* Config 10 = default value */
+ write_rf(hw, 15, 0x1ABA8 );
+
+ /* Calibration procedure */
+ write_reg(BB_OUTPUT_CONTROL, 0x00000300, op_or);
+
+ udelay(150);
+
+ /* Calibration procedure */
+
+ /* TXDCOC->active; RCK->disable */
+ write_rf(hw, 15, 0x9ABA8 );
+ udelay(50);
+
+ /* TXDCOC->disable; RCK->enable */
+ write_rf(hw, 15, 0x3ABA8 );
+ udelay(50);
+
+ /* TXDCOC->disable; RCK->disable */
+ write_rf(hw, 15, 0x1ABA8 );
+ udelay(50);
+
+ write_reg(BB_GENERAL_CTL, ~BB_GENERAL_CTL_MAX_GAIN_MASK, op_and);
+ write_reg(BB_GENERAL_CTL, BB_GENERAL_CTL_DEFAULT_MAX_GAIN_BG, op_or);
+ break;
+
+ case IEEE80211_BAND_5GHZ:
+ /* Initial settings for 20 MHz reference frequency, 802.11a */
+ write_reg(BB_OUTPUT_CONTROL, 0xfffffcff, op_and);
+ write_reg(BB_OUTPUT_CONTROL, 0x00000200, op_or);
+ udelay(150);
+
+ /* Frequency register 0 */
+ write_rf(hw, 0, 0x0FF56 );
+
+ /* Frequency register 1 */
+ write_rf(hw, 1, 0x0AAAA );
+
+ udelay(10);
+
+ /*Config 1 = default value */
+ write_rf(hw, 2, 0x451FE );
+
+ /*Config 2 = default value */
+ write_rf(hw, 3, 0x5FDFA );
+
+ /*Config 3 = default value */
+ write_rf(hw, 4, 0x67f78 );
+
+ /*Config 4 = default value */
+ write_rf(hw, 5, 0x853FF );
+
+ /*Config 5 = default value */
+ write_rf(hw, 6, 0x56AF3 );
+
+ /*Config 6 = Crystal frequency /2 to pll reference divider */
+ write_rf(hw, 7, 0xCE000 );
+
+ /*Config 7 = RSSI = default value */
+ write_rf(hw, 8, 0x6EBC0 );
+
+ /* Filter BW = default value */
+ write_rf(hw, 9, 0x221BB );
+
+ /* RX Gain = digi value */
+ write_rf(hw, 10, 0xE0600 );
+
+ /* TX Gain = digi specific vaue: TX GAIN set using the register */
+ // write_rf(hw, 11, 0x08070 );
+ mac_set_tx_power (priv->tx_power); //Digi value
+
+ /* PA current = default value */
+ set_hw_specific_parameters(hw, IEEE80211_BAND_5GHZ, hw_revision, hw_platform);
+
+ /* Config 8 = default value */
+ write_rf(hw, 13, 0xFFFFF );
+
+ /* Config 9 = default value */
+ write_rf(hw, 14, 0x00000 );
+
+ /* Config 10 = default value */
+ write_rf(hw, 15, 0x12BAC );
+
+ /* Calibration procedure */
+ write_reg(BB_OUTPUT_CONTROL, 0x00000300, op_or);
+
+ udelay(150);
+
+ /* Calibration procedure */
+
+ /* TXDCOC->active; RCK->disable */
+ write_rf(hw, 15, 0x9ABA8 );
+ udelay(50);
+
+ /* TXDCOC->disable; RCK->enable */
+ write_rf(hw, 15, 0x3ABA8 );
+ udelay(50);
+
+ /* TXDCOC->disable; RCK->disable */
+ write_rf(hw, 15, 0x12BAC );
+ udelay(50);
+ write_reg(BB_GENERAL_CTL, ~BB_GENERAL_CTL_MAX_GAIN_MASK, op_and);
+ write_reg(BB_GENERAL_CTL, BB_GENERAL_CTL_DEFAULT_MAX_GAIN_A, op_or);
+ break;
+ }
+ } else {
+ printk(KERN_WARNING PIPER_DRIVER_NAME
+ ": undefined rf transceiver!\n");
+ }
+}
+
+static int al7230_rf_stop(struct ieee80211_hw *hw)
+{
+ return 0;
+}
+
+static void getOfdmBrs(int channelIndex, u64 brsBitMask, unsigned int *ofdm, unsigned int *psk)
+{
+ /*
+ * brsBitMask is a bit mask into the al7230_bg_rates array. Bit 0 refers
+ * to the first entry in the array, bit 1 the second, and so on. The first
+ * 4 bits/array entries refer to the PSK bit rates we support, the next 8
+ * bits/array entries refer to the OFDM rates we support. So the PSK BRS
+ * mask is bits 0-3, the OFDM bit mask is bits 4-11.
+ */
+
+ if (getBand(channelIndex) == IEEE80211_BAND_2GHZ)
+ {
+ *psk = brsBitMask & 0xf;
+ *ofdm = (brsBitMask & 0xff0) >> 4;
+ }
+ else
+ {
+ *psk = 0;
+ *ofdm = (brsBitMask & 0xff);
+ }
+}
+
+static struct ieee80211_supported_band al7230_bands[] = {
+ {
+ .band = IEEE80211_BAND_2GHZ,
+ .n_channels = ARRAY_SIZE(al7230_bg_channels),
+ .n_bitrates = ARRAY_SIZE(al7230_bg_rates),
+ .channels = (struct ieee80211_channel *) al7230_bg_channels,
+ .bitrates = (struct ieee80211_rate *) al7230_bg_rates,
+ },
+ {
+ .band = IEEE80211_BAND_5GHZ,
+ .n_channels = ARRAY_SIZE(al7230_a_channels),
+ .n_bitrates = ARRAY_SIZE(al7230_a_rates),
+ .channels = (struct ieee80211_channel *) al7230_a_channels,
+ .bitrates = (struct ieee80211_rate *) al7230_a_rates,
+ },
+};
+
+static const struct ieee80211_rate *getRate(unsigned int rateIndex)
+{
+ return &al7230_bg_rates[rateIndex];
+}
+
+
+/*
+ * This routine can power up or power down the airoha transceiver.
+ * When the transceiver is powered back up, you must delay 1 ms and
+ * then call the set channel routine to make it operational again.
+ */
+static void power_on(struct ieee80211_hw *hw, bool want_power_on)
+{
+ if (want_power_on) {
+ write_rf(hw, 15, 0x1ABA8 ); /* this is actually for 2 Ghz */
+ } else {
+ write_rf(hw, 15, 0x1ABAE );
+ }
+}
+
+static void al7230_set_hw_info(struct ieee80211_hw *hw, int channel,
+ u16 hw_platform_code)
+{
+ hw_revision = hw_platform_code & WCD_HW_REV_MASK;
+ hw_platform = (hw_platform_code & WCD_PLATFORM_MASK);
+
+ set_hw_specific_parameters(hw, getBand(channel), hw_revision, hw_platform);
+}
+
+struct digi_rf_ops al7230_rf_ops = {
+ .name = "Airoha 7230",
+ .init = InitializeRF,
+ .stop = al7230_rf_stop,
+ .set_chan = al7230_rf_set_chan,
+ .set_chan_no_rx = al7230_rf_set_chan_no_rx,
+ .set_pwr = al7230_set_txpwr,
+ .set_pwr_index = al7230_set_power_index,
+ .set_hw_info = al7230_set_hw_info,
+ .channelChangeTime = CHANNEL_CHANGE_TIME,
+ .maxSignal = MAX_SIGNAL_IN_DBM,
+ .getOfdmBrs = getOfdmBrs,
+ .getBand = getBand,
+ .getFrequency = getFrequency,
+ .getRate = getRate,
+ .bands = al7230_bands,
+ .power_on = power_on,
+ .n_bands = ARRAY_SIZE(al7230_bands),
+};
+EXPORT_SYMBOL_GPL(al7230_rf_ops);
diff --git a/drivers/net/wireless/digiPiper/airoha.h b/drivers/net/wireless/digiPiper/airoha.h
new file mode 100644
index 000000000000..aa3b1d10fbe3
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/airoha.h
@@ -0,0 +1,30 @@
+#ifndef DIGI_RF_AH7230_H_
+#define DIGI_RF_AH7230_H_
+
+/* 0 is reserved for unknown transceiver */
+#define RF_AIROHA_7230 (1)
+#define RF_AIROHA_2236 (2)
+
+#define SPI_INIT_AIROHA (0x00000018) /* AIROHA-specific SPI length */
+#define SPI_INIT_AIROHA2236 (0x00000014) /* AIROHA 2236-specific SPI length */
+#define GEN_INIT_AIROHA_24GHZ (0x31720005) /* Initial state; 2.4GHZ_PA_ON= active low; bit 25 */
+#define GEN_INIT_AIROHA_50GHZ (0x33760008) /* Initial state; 5.0GHZ_PA_ON= active high; bit 25 */
+
+#define AIROHA_LOWEST_PSK_RATE_INDEX (0)
+#define AIROHA_LOWEST_OFDM_RATE_INDEX (4)
+#define AIROHA_55_MBPS_RATE_INDEX (2)
+
+/*
+ * Subtract this number from the channel index to index into
+ * the 802.11a channel array.
+ */
+#define BAND_A_OFFSET (17)
+
+struct digi_rf_freq {
+ uint16_t integer;
+ uint16_t fract;
+};
+
+extern struct digi_rf_ops al7230_rf_ops;
+
+#endif
diff --git a/drivers/net/wireless/digiPiper/airohaCalibration.c b/drivers/net/wireless/digiPiper/airohaCalibration.c
new file mode 100644
index 000000000000..9da912f320ba
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/airohaCalibration.c
@@ -0,0 +1,974 @@
+/*
+ * This file contains the code which performs automatic recalibration of the
+ * Airoha transceiver.
+ *
+ * Copyright (C) 2009 by Digi International Inc.
+ * All rights reserved.
+ *
+ * This program is free softbware; 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+#include <linux/timer.h>
+#include <linux/crc32.h>
+
+#include "pipermain.h"
+#include "mac.h"
+#include "airoha.h"
+#include "airohaCalibration.h"
+#include "adc121c027.h"
+
+#define POWER_INDEX_STEP (1) /* TODO: come up with a rational value for this */
+
+#define SAMPLE_TIMEOUT (HZ * 10) /* TODO: What is a good sample timeout? Do we need one? */
+#define RECALIBRATION_PERIOD (HZ * 15) /* amount of time to wait between recalibrations */
+
+/*
+ * This defines the amount of time to wait for the power levels
+ * to settle down after making a large correction (user has
+ * changed power level).
+ */
+#define DEBOUNCE_DELAY (HZ * 2)
+
+#define MAX_TOLERATED_ERROR_HIGH (300) /* maximum over target we will allow */
+#define MAX_TOLERATED_ERROR_LOW (500) /* maximum under the target we will allow */
+#define CONVERT_TO_MDBM(x) (1000 * (x)) /* power levels are dBm externally, but dBm/1000 internally */
+
+#define NVRAM_WCAL_SIGNATURE "WCALDATA"
+
+#define MINIMUM_POWER_INDEX (10)
+
+/*
+ * Set piperp->calibrationTxRate to this value to make the
+ * transmit routine use the data rate specified by the
+ * mac80211 library.
+ */
+#define USE_MAC80211_DATA_RATE (NULL)
+
+/*
+ * Events we will wait for, also return values for waitForEvent().
+ */
+#define TIMED_OUT_EVENT (1 << 0)
+#define TRANSMIT_DONE_EVENT (1 << 1)
+#define SHUTDOWN_AUTOCALIBRATION_EVENT (1 << 2)
+#define RESTART_AUTOCALIBRATION_EVENT (1 << 3)
+
+/*
+ * Set this constant to (1) if you want to force calibration status to
+ * be printed.
+ */
+#define WANT_CALIBRATION_STATUS (0)
+
+
+static struct airohaCalibrationData calibration;
+
+static DECLARE_WAIT_QUEUE_HEAD(waitQueue);
+
+
+
+/*
+ * This routine is called to shut down the transmit ADC sampler.
+ */
+static void stopSampler(struct piper_priv *digi)
+{
+ digi->tx_calib_cb = NULL;
+}
+
+/*
+ * This routine is called to update the state of the calibration state machine
+ * and wake up its thread.
+ */
+static void kickCalibrationThread(struct piper_priv *digi, unsigned int event)
+{
+ unsigned long spinlockFlags;
+
+ spin_lock_irqsave(&calibration.lock, spinlockFlags);
+ calibration.events |= event;
+ spin_unlock_irqrestore(&calibration.lock, spinlockFlags);
+ wake_up_interruptible(&waitQueue);
+}
+
+
+/*
+ * This routine is called each time we complete a transmit while we are in
+ * the sampling state. We record the peak ADC reading. We kick the state
+ * machine if we now have all the samples we need.
+ */
+static void processSample(struct piper_priv *digi)
+{
+#define MINIMUM_ADC_VALUE (10) /* if ADC is below this value, it's probably bad */
+ if (calibration.sampleCount < MAX_SAMPLES) {
+ /*
+ * Read the ADC value. It is a 12-bit value. We shift it 4 bits to
+ * create an 8-bit value.
+ */
+ calibration.sample[calibration.sampleCount].sample =
+ (calibration.cops->adc_read_peak(&calibration) >> 4);
+ if (calibration.sample[calibration.sampleCount].sample >
+ MINIMUM_ADC_VALUE) {
+ calibration.sampleCount++;
+ }
+ }
+}
+
+static void transmitHasCompleted(struct piper_priv *digi)
+{
+ stopSampler(digi);
+ kickCalibrationThread(digi, TRANSMIT_DONE_EVENT);
+}
+
+
+
+/*
+ * Determine the appropriate transmit rate to use during calibration.
+ */
+static struct ieee80211_rate *determineCalibrationTxRate(struct piper_priv
+ *digi)
+{
+ unsigned int rates = digi->ac->rd_reg(digi, MAC_SSID_LEN);
+ struct ieee80211_rate *calibrationTxRate;
+
+ rates &= (MAC_PSK_BRS_MASK | MAC_OFDM_BRS_MASK);
+
+ if ((digi->rf->getBand(digi->channel) == IEEE80211_BAND_2GHZ)
+ && (rates & MAC_PSK_BRS_MASK)) {
+ calibrationTxRate =
+ (struct ieee80211_rate *) digi->rf->
+ getRate(AIROHA_LOWEST_PSK_RATE_INDEX);
+ } else {
+ calibrationTxRate =
+ (struct ieee80211_rate *) digi->rf->
+ getRate(AIROHA_LOWEST_OFDM_RATE_INDEX);
+ }
+
+ return calibrationTxRate;
+}
+
+
+
+
+/*
+ * Start collecting sample ADC peak measurements for calibration. Start
+ * the process by installing the callbacks which the transmit code will
+ * use to notify us when transmit frames go out.
+ */
+static void startSampleCollection(struct piper_priv *digi)
+{
+ calibration.cops->adc_clear_peak(&calibration);
+ digi->tx_calib_cb = transmitHasCompleted;
+}
+
+
+static unsigned int waitForEvent(unsigned int timeout, unsigned int eventToWaitFor)
+{
+#define ALL_EVENTS_TO_WAIT_FOR(x) (eventToWaitFor \
+ | SHUTDOWN_AUTOCALIBRATION_EVENT \
+ | RESTART_AUTOCALIBRATION_EVENT)
+
+ unsigned long spinlockFlags;
+ int ccode;
+ unsigned int event;
+ int result = TIMED_OUT_EVENT;
+
+ if (timeout != 0) {
+ ccode = wait_event_interruptible_timeout(waitQueue,
+ ((calibration.
+ events &
+ ALL_EVENTS_TO_WAIT_FOR
+ (eventToWaitFor))
+ != 0), timeout);
+ __set_current_state(TASK_RUNNING);
+
+ } else {
+ ccode = 0;
+ }
+ spin_lock_irqsave(&calibration.lock, spinlockFlags);
+ event = calibration.events;
+ calibration.events = 0;
+ spin_unlock_irqrestore(&calibration.lock, spinlockFlags);
+
+ if ((ccode < 0) || (event & SHUTDOWN_AUTOCALIBRATION_EVENT)) {
+ result = SHUTDOWN_AUTOCALIBRATION_EVENT;
+ } else if (event & RESTART_AUTOCALIBRATION_EVENT) {
+ result = RESTART_AUTOCALIBRATION_EVENT;
+ } else if (event & eventToWaitFor) {
+ result = (event & eventToWaitFor);
+ } else {
+ result = TIMED_OUT_EVENT;
+ }
+
+ return result;
+}
+
+
+#ifdef WANT_CAL_DEBUG
+static void printPoint(wcd_point_t * p)
+{
+ printk("(%d, %d, %d)", p->out_power, p->adc_val, p->power_index);
+}
+#endif
+
+
+/*
+ * This routine finds the closest pair of points in a calibration curve.
+ *
+ * curve calibration curve
+ * value look for the pr of points closest to this value
+ * p1 storage for one point
+ * p2 storage for another point
+ * field tells us which field in the point struct to compare
+ */
+static void findClosestPoints(wcd_curve_t * curve, int value,
+ wcd_point_t ** p1, wcd_point_t ** p2, int field)
+{
+ if (value <= curve->points[0].out_power) {
+ *p1 = &curve->points[0];
+ *p2 = &curve->points[1];
+ } else if (value >=
+ curve->points[calibration.nvram->header.numcalpoints - 1].out_power) {
+ *p1 = &curve->points[calibration.nvram->header.numcalpoints - 2];
+ *p2 = &curve->points[calibration.nvram->header.numcalpoints - 1];
+ } else {
+ unsigned int idx;
+
+ for (idx = 1; idx < calibration.nvram->header.numcalpoints; idx++) {
+ if ((value < curve->points[idx].out_power)
+ || (idx == (calibration.nvram->header.numcalpoints - 1))) {
+ *p1 = &curve->points[idx - 1];
+ *p2 = &curve->points[idx];
+ break;
+ } else if (value == curve->points[idx].out_power) {
+ /*
+ * Note that the if statement befpre the for loop already tested for the
+ * value being equal to the first or last point in the curve, so we don't
+ * have to worry about that condition in the code below.
+ */
+ if ((value -
+ curve->points[idx - 1].out_power) >=
+ (curve->points[idx + 1].out_power - value)) {
+ /*
+ * If the two points are equal distant, then favor the larger pair
+ * because I think the values on the low end are screwy.
+ */
+ *p1 = &curve->points[idx];
+ *p2 = &curve->points[idx + 1];
+ } else {
+ *p1 = &curve->points[idx - 1];
+ *p2 = &curve->points[idx];
+ }
+ break;
+ }
+ }
+ }
+}
+
+
+/*
+ * Compute the slope of a curve between 2 points. The slope is the rise over the run,
+ * or (Y2 - Y1)/(X2 - X1). This function handles more than one type of slope.
+ */
+static int computeSlopeTimes1000(wcd_point_t * p1, wcd_point_t * p2, int slopeType)
+{
+ int slope = 0;
+ int divisor;
+
+ switch (slopeType) {
+ default:
+ digi_dbg("Unexpected slope type %d.\n", slopeType);
+ break;
+ case POWER_INDEX_OVER_OUT_POWER:
+ divisor = (p2->out_power - p1->out_power);
+ if (divisor != 0) {
+ slope =
+ (((p2->power_index - p1->power_index) * 1000) +
+ (divisor / 2)) / divisor;
+ } else {
+ digi_dbg("divisor is zero\n");
+ }
+ break;
+ case ADC_OVER_OUT_POWER:
+ divisor = (p2->out_power - p1->out_power);
+ if (divisor != 0) {
+ slope =
+ (((p2->adc_val - p1->adc_val) * 1000) +
+ (divisor / 2)) / divisor;
+ } else {
+ digi_dbg("divisor is zero\n");
+ }
+ break;
+ case OUT_POWER_OVER_ADC:
+ divisor = (p2->adc_val - p1->adc_val);
+ if (divisor != 0) {
+ slope =
+ (((p2->out_power - p1->out_power) * 1000) +
+ (divisor / 2)) / divisor;
+ } else {
+ digi_dbg("divisor is zero\n");
+ }
+ break;
+ case POWER_INDEX_OVER_ADC:
+ divisor = (p2->adc_val - p1->adc_val);
+ if (divisor != 0) {
+ slope =
+ (((p2->power_index - p1->power_index) * 1000) +
+ (divisor / 2)) / divisor;
+ } else {
+ digi_dbg("divisor is zero\n");
+ }
+ break;
+ }
+
+ return slope;
+}
+
+
+/*
+ * If (x,y) is a point on a curve, then compute y given x, the slope of the curve,
+ * and a known point on the curve.
+ *
+ * If (Xd, Yd) is the desired point, p1 is the known point, and m the slope, then
+ *
+ * Yd - p1->y = m(Xd - p1->x)
+ * Yd = m(Xd - p1->x) + p1->y
+ * Yd = m(Xd) - m(p1->x) + p1->y
+ */
+static int computeY(wcd_point_t * p1, int slopeTimes1000, int x, int slopeType)
+{
+ int y = 0;
+
+ switch (slopeType) {
+ default:
+ digi_dbg("Unexpected slope type %d.\n", slopeType);
+ break;
+ case POWER_INDEX_OVER_OUT_POWER:
+ y = (((slopeTimes1000 * x) -
+ (slopeTimes1000 * p1->out_power) + 500) / 1000) + p1->power_index;
+ break;
+ case ADC_OVER_OUT_POWER:
+ y = (((slopeTimes1000 * x) -
+ (slopeTimes1000 * p1->out_power) + 500) / 1000) + p1->adc_val;
+ break;
+ case OUT_POWER_OVER_ADC:
+ y = (((slopeTimes1000 * x) -
+ (slopeTimes1000 * p1->adc_val) + 500) / 1000) + p1->out_power;
+ break;
+ case POWER_INDEX_OVER_ADC:
+ y = (((slopeTimes1000 * x) -
+ (slopeTimes1000 * p1->adc_val) + 500) / 1000) + p1->power_index;
+ break;
+ }
+
+ return y;
+}
+
+
+/*
+ * Return a pointer to the curve we should use for the currently selected
+ * channel.
+ */
+static wcd_curve_t *determineCurve(struct piper_priv *digi)
+{
+ unsigned int rates = digi->ac->rd_reg(digi, MAC_SSID_LEN);
+ wcd_curve_t *curve = NULL;
+
+ if (digi->rf->getBand(digi->channel) == IEEE80211_BAND_2GHZ) {
+ if (rates & MAC_PSK_BRS_MASK) {
+ /*
+ * This is the normal case for 802.11b and 802.11bg. We select
+ * the PSK curve.
+ */
+ digi_dbg("Using bg curve [%d][%d]\n",
+ digi->channel, WCD_B_CURVE_INDEX);
+ curve = &calibration.nvram->cal_curves_bg[digi->channel]
+ [WCD_B_CURVE_INDEX];
+ } else { /* if associated with AP that only supports G rates */
+
+ /*
+ * This is a very unusual, but theoretically possible case. We
+ * are associated with an AP that only supports OFDM modulation
+ * (G only, no B). We determine this by looking at the BRS
+ * setting. If no PSK rates are set for BRS, then we assume that
+ * this must be a G only AP. Obviously, we must select the OFDM curve.
+ */
+ digi_dbg("Using bg curve [%d][%d]\n",
+ digi->channel, WCD_G_CURVE_INDEX);
+ curve = &calibration.nvram->cal_curves_bg[digi->channel]
+ [WCD_G_CURVE_INDEX];
+ }
+ } else {
+ /*
+ * An 802.11a channel is selected.
+ */
+ curve = &calibration.nvram->cal_curves_a[digi->channel - BAND_A_OFFSET];
+ digi_dbg("Using A curve [%d]\n", digi->channel - BAND_A_OFFSET);
+ }
+
+ return curve;
+}
+
+/*
+ * Determine the maximum mdBm allowed for the current channel. Return the
+ * lesser of the max and the mdBm value passed to us.
+ */
+static int getFilteredPower(struct piper_priv *digi, int mdBm)
+{
+ int max = 0;
+
+ if (digi->channel < BAND_A_OFFSET) {
+ max = 16000; /* all BG channels can go to 16 dBm */
+ } else if (digi->channel < (BAND_A_OFFSET + 4)) {
+ max = 3000; /* first 4 A channels max out at 3 dBm */
+ } else {
+ max = 8000; /* all other A channels can handle 8 dBm */
+ }
+
+ if (mdBm > max) {
+ mdBm = max;
+ }
+
+ return mdBm;
+}
+
+
+/*
+ * This routine performs open loop calibration for Airoha. It takes a value in mdbm
+ * and uses the factory calibration routines to determine the appropriate register
+ * value to write to airoha.
+ */
+static void setInitialPowerLevel(struct piper_priv *digi, int mdBm)
+{
+ wcd_point_t *p1, *p2;
+
+ mdBm = getFilteredPower(digi, mdBm);
+ digi_dbg("Setting initial powerlevel %d milli dBm.\n", mdBm);
+ calibration.curve = determineCurve(digi);
+ findClosestPoints(calibration.curve, mdBm, &p1, &p2, OUT_POWER);
+ calibration.slopeTimes1000 =
+ computeSlopeTimes1000(p1, p2, POWER_INDEX_OVER_OUT_POWER);
+ if (abs(p1->out_power - mdBm) < abs(p2->out_power - mdBm)) {
+ calibration.p1 = p1;
+ } else {
+ calibration.p1 = p2;
+ }
+ calibration.powerIndex =
+ computeY(calibration.p1, calibration.slopeTimes1000, mdBm,
+ POWER_INDEX_OVER_OUT_POWER);
+ calibration.correctedPowerIndex = calibration.powerIndex;
+
+ digi_dbg("Computed power index = %d.\n", calibration.powerIndex);
+ digi->rf->set_pwr_index(digi->hw, calibration.powerIndex);
+
+ /*
+ * Let's compute and save the expected ADC value while we have all the necessary
+ * information handy.
+ */
+#ifdef WANT_CAL_DEBUG
+ digi_dbg("Using points ");
+ printPoint(p1);
+ printPoint(p2);
+ printk("\n");
+ digi_dbg("Max ADC = %d.\n", calibration.curve->max_adc_value);
+#endif
+ calibration.adcSlopeTimes1000 = computeSlopeTimes1000(p1, p2, ADC_OVER_OUT_POWER);
+ calibration.expectedAdc =
+ computeY(calibration.p1, calibration.adcSlopeTimes1000, mdBm,
+ ADC_OVER_OUT_POWER);
+ if (calibration.expectedAdc > calibration.curve->max_adc_value) {
+ calibration.expectedAdc = calibration.curve->max_adc_value;
+ }
+ digi_dbg("adcSlopeTimes1000 = %d, expectedAdc = %d\n",
+ calibration.adcSlopeTimes1000, calibration.expectedAdc);
+ calibration.outPowerSlopeTimes1000 =
+ computeSlopeTimes1000(p1, p2, OUT_POWER_OVER_ADC);
+ calibration.powerIndexSlopeTimes1000 =
+ computeSlopeTimes1000(p1, p2, POWER_INDEX_OVER_ADC);
+}
+
+
+
+/*
+ * This routine performs closed loop recalibration. It is called periodically
+ * to adjust the transmit power level. It will be called after the ADC levels
+ * for several transmit frames have been sampled. It does the following:
+ *
+ * 1. Average the samples together.
+ * 2. If the measured ADC level is too low, then increase the power
+ * level one step.
+ * 3. If the measured ADC level is too high, then decrease the power
+ * level one step.
+ */
+static void recalibrate(struct piper_priv *digi)
+{
+ unsigned int idx;
+ int actualAdc = 0;
+ int errorInAdc, errorInMdbm;
+ wcd_point_t p = {
+ .out_power = 0,
+ .adc_val = 0
+ };
+ int needCorrection = 0;
+
+#ifdef WANT_CAL_DEBUG_1
+ digi_dbg("Samples: ");
+#endif
+ for (idx = 0; idx < calibration.sampleCount; idx++) {
+#ifdef WANT_CAL_DEBUG_1
+ printk("%d, ", calibration.sample[idx].sample);
+#endif
+ actualAdc += calibration.sample[idx].sample;
+ }
+#ifdef WANT_CAL_DEBUG_1
+ printk("\n");
+#endif
+ actualAdc = actualAdc / calibration.sampleCount;
+
+#ifdef WANT_TO_FORCE_26
+
+ digi->rf->set_pwr_index(digi->hw, 26);
+#else
+
+ errorInAdc = actualAdc - calibration.expectedAdc;
+ errorInMdbm =
+ computeY(&p, calibration.outPowerSlopeTimes1000, errorInAdc,
+ OUT_POWER_OVER_ADC);
+ needCorrection = (errorInMdbm > MAX_TOLERATED_ERROR_HIGH);
+ if (errorInMdbm < 0) {
+ needCorrection = ((MAX_TOLERATED_ERROR_LOW + errorInMdbm) < 0);
+ }
+ if (needCorrection) {
+ int correction = computeY(calibration.p1,
+ calibration.powerIndexSlopeTimes1000,
+ actualAdc, POWER_INDEX_OVER_ADC);
+#if defined(WANT_CAL_DEBUG)
+ int oldIndex = calibration.correctedPowerIndex;
+#endif
+ correction = (3 * (calibration.powerIndex - correction)) / 4;
+ if (correction == 0) {
+ if (errorInAdc > 0) {
+ /*
+ * If power level is too high but the minimum correction would
+ * overshoot the target, do it anyway because we would rather
+ * be below the target rather than over the target.
+ */
+ correction = -1;
+ } else {
+ /*
+ * But if the power level is too low, but the minimum correction
+ * would overshoot, then leave it be. Better too low than too high.
+ */
+ }
+ }
+ calibration.correctedPowerIndex += correction;
+ if (calibration.correctedPowerIndex < MINIMUM_POWER_INDEX) {
+ calibration.correctedPowerIndex = MINIMUM_POWER_INDEX;
+ }
+ digi->rf->set_pwr_index(digi->hw, calibration.correctedPowerIndex);
+#ifdef WANT_CAL_DEBUG
+ digi_dbg
+ ("actualAdc = %d, expectedAdc = %d, error mdbm = %d\n",
+ actualAdc, calibration.expectedAdc, errorInMdbm);
+ digi_dbg
+ ("Power index corrected by %d: was %d, set to %d.\n",
+ correction, oldIndex, calibration.correctedPowerIndex);
+#endif
+#if WANT_CALIBRATION_STATUS
+ if (correction != 0)
+ printk(KERN_ERR
+ "error mdBm = %d, correcting airoha index by %d\n",
+ errorInMdbm, correction);
+#endif
+ }
+#endif
+}
+
+
+/*
+ * This routine is called by the 80211mac library to set a new power level. We
+ * update the value in context memory and then kick the autocalibration thread.
+ */
+static int setNewPowerLevel(struct ieee80211_hw *hw, uint8_t newPowerLevel)
+{
+ struct piper_priv *digi = hw->priv;
+
+ (void) newPowerLevel; /* has already been written into piper->tx_power */
+ /*
+ * Kick the calibration thread.
+ */
+ stopSampler(digi);
+ kickCalibrationThread(digi, RESTART_AUTOCALIBRATION_EVENT);
+
+ return 0;
+}
+
+
+
+
+/*
+ * Compute the maximum ADC value for a curve given the maximum
+ * allowed power in dBm.
+ */
+static u8 getMaxAdcValue(wcd_curve_t * curve, int dBm)
+{
+ wcd_point_t *p1, *p2;
+ int slopeTimes1000;
+ u8 result = 0;
+ int mdBm = 1000 * dBm;
+
+ findClosestPoints(curve, mdBm, &p1, &p2, OUT_POWER);
+ slopeTimes1000 = computeSlopeTimes1000(p1, p2, ADC_OVER_OUT_POWER);
+
+ if (abs(p1->out_power - mdBm) < abs(p2->out_power - mdBm)) {
+ result = computeY(p1, slopeTimes1000, mdBm, ADC_OVER_OUT_POWER);
+ } else {
+ result = computeY(p2, slopeTimes1000, mdBm, ADC_OVER_OUT_POWER);
+ }
+
+ return result;
+}
+
+
+/*
+ * The version 1.0 calibration data provided maximum Airoha index
+ * values, but later versions provided maximum ADC values. This
+ * routine replaces the max Airoha indexes with max ADC values.
+ */
+static void determineMaxAdcValues(wcd_data_t * cdata)
+{
+ int i;
+
+ for (i = 0; i < WCD_CHANNELS_BG; i++) {
+ /*
+ * All BG channels are limited to 16 dBm.
+ */
+ cdata->cal_curves_bg[i][0].max_adc_value =
+ getMaxAdcValue(&cdata->cal_curves_bg[i][0], 16);
+ cdata->cal_curves_bg[i][1].max_adc_value =
+ getMaxAdcValue(&cdata->cal_curves_bg[i][1], 16);
+ }
+ for (i = 0; i < 4; i++) {
+ /*
+ * First 4 802.11a channels are limited to 3 dBm.
+ */
+ cdata->cal_curves_a[i].max_adc_value =
+ getMaxAdcValue(&cdata->cal_curves_a[i], 3);
+ }
+ for (i = 4; i < WCD_CHANNELS_A; i++) {
+ /*
+ * All other 802.11a channels are limited to 8 dBm.
+ */
+ cdata->cal_curves_a[i].max_adc_value =
+ getMaxAdcValue(&cdata->cal_curves_a[i], 8);
+ }
+}
+
+
+
+/*
+ * This routine writes a default set of calibration values into the
+ * calibration data structure. Maximum power output is severely limited.
+ */
+static void setDefaultCalibrationValues(struct piper_priv *piperp)
+{
+#define MIN_MDBM (-2905)
+#define MAX_BG_MDBM (6000)
+#define DEFAULT_NUM_POINTS (DEFAULT_NUM_POINTS)
+#define BAND_A_1_START (0)
+#define BAND_A_2_START (4)
+#define BAND_A_3_START (7)
+#define BAND_A_4_START (15)
+#define BAND_A_5_START (19)
+#define BAND_A_6_START (30)
+
+ int i;
+
+ calibration.nvram->header.numcalpoints = 2;
+
+ for (i = 0; i < WCD_CHANNELS_BG; i++) {
+ calibration.nvram->cal_curves_bg[i][0].max_adc_value = 52;
+ calibration.nvram->cal_curves_bg[i][0].points[0].out_power = MIN_MDBM;
+ calibration.nvram->cal_curves_bg[i][0].points[0].adc_val = 19;
+ calibration.nvram->cal_curves_bg[i][0].points[0].power_index = 0;
+ calibration.nvram->cal_curves_bg[i][0].points[1].out_power = 6000;
+ calibration.nvram->cal_curves_bg[i][0].points[1].adc_val = 52;
+ calibration.nvram->cal_curves_bg[i][0].points[1].power_index = 16;
+
+ calibration.nvram->cal_curves_bg[i][0].max_adc_value = 48;
+ calibration.nvram->cal_curves_bg[i][1].points[0].out_power = MIN_MDBM;
+ calibration.nvram->cal_curves_bg[i][1].points[0].adc_val = 12;
+ calibration.nvram->cal_curves_bg[i][1].points[0].power_index = 0;
+ calibration.nvram->cal_curves_bg[i][1].points[1].out_power = 6000;
+ calibration.nvram->cal_curves_bg[i][1].points[1].adc_val = 48;
+ calibration.nvram->cal_curves_bg[i][1].points[1].power_index = 24;
+ }
+
+ for (i = BAND_A_1_START; i < BAND_A_2_START; i++) {
+ calibration.nvram->cal_curves_a[i].max_adc_value = 22;
+ calibration.nvram->cal_curves_a[i].points[0].out_power = MIN_MDBM;
+ calibration.nvram->cal_curves_a[i].points[0].adc_val = 11;
+ calibration.nvram->cal_curves_a[i].points[0].power_index = 0;
+ calibration.nvram->cal_curves_a[i].points[0].out_power = 0;
+ calibration.nvram->cal_curves_a[i].points[0].adc_val = 22;
+ calibration.nvram->cal_curves_a[i].points[0].power_index = 19;
+ }
+ for (i = BAND_A_2_START; i < BAND_A_3_START; i++) {
+ calibration.nvram->cal_curves_a[i].max_adc_value = 29;
+ calibration.nvram->cal_curves_a[i].points[0].out_power = MIN_MDBM;
+ calibration.nvram->cal_curves_a[i].points[0].adc_val = 13;
+ calibration.nvram->cal_curves_a[i].points[0].power_index = 0;
+ calibration.nvram->cal_curves_a[i].points[0].out_power = 2000;
+ calibration.nvram->cal_curves_a[i].points[0].adc_val = 29;
+ calibration.nvram->cal_curves_a[i].points[0].power_index = 20;
+ }
+ for (i = BAND_A_3_START; i < BAND_A_4_START; i++) {
+ calibration.nvram->cal_curves_a[i].max_adc_value = 42;
+ calibration.nvram->cal_curves_a[i].points[0].out_power = MIN_MDBM;
+ calibration.nvram->cal_curves_a[i].points[0].adc_val = 15;
+ calibration.nvram->cal_curves_a[i].points[0].power_index = 0;
+ calibration.nvram->cal_curves_a[i].points[0].out_power = 4000;
+ calibration.nvram->cal_curves_a[i].points[0].adc_val = 42;
+ calibration.nvram->cal_curves_a[i].points[0].power_index = 22;
+ }
+ for (i = BAND_A_4_START; i < BAND_A_5_START; i++) {
+ calibration.nvram->cal_curves_a[i].max_adc_value = 54;
+ calibration.nvram->cal_curves_a[i].points[0].out_power = MIN_MDBM;
+ calibration.nvram->cal_curves_a[i].points[0].adc_val = 21;
+ calibration.nvram->cal_curves_a[i].points[0].power_index = 0;
+ calibration.nvram->cal_curves_a[i].points[0].out_power = 2000;
+ calibration.nvram->cal_curves_a[i].points[0].adc_val = 54;
+ calibration.nvram->cal_curves_a[i].points[0].power_index = 18;
+ }
+ for (i = BAND_A_5_START; i < BAND_A_6_START; i++) {
+ calibration.nvram->cal_curves_a[i].max_adc_value = 39;
+ calibration.nvram->cal_curves_a[i].points[0].out_power = MIN_MDBM;
+ calibration.nvram->cal_curves_a[i].points[0].adc_val = 13;
+ calibration.nvram->cal_curves_a[i].points[0].power_index = 0;
+ calibration.nvram->cal_curves_a[i].points[0].out_power = 2000;
+ calibration.nvram->cal_curves_a[i].points[0].adc_val = 39;
+ calibration.nvram->cal_curves_a[i].points[0].power_index = 26;
+ }
+ for (i = BAND_A_6_START; i < WCD_CHANNELS_A; i++) {
+ calibration.nvram->cal_curves_a[i].max_adc_value = 31;
+ calibration.nvram->cal_curves_a[i].points[0].out_power = MIN_MDBM;
+ calibration.nvram->cal_curves_a[i].points[0].adc_val = 11;
+ calibration.nvram->cal_curves_a[i].points[0].power_index = 0;
+ calibration.nvram->cal_curves_a[i].points[0].out_power = 2000;
+ calibration.nvram->cal_curves_a[i].points[0].adc_val = 31;
+ calibration.nvram->cal_curves_a[i].points[0].power_index = 30;
+ }
+}
+
+
+/*
+ * The calibration data is passed to the kernel from U-Boot. The kernel
+ * start up routines will have copied the data into digi->pdata->wcd.
+ * We do a few sanity checks on the data and set up our own pointers to
+ * it.
+ */
+static int getCalibrationData(struct piper_priv *digi)
+{
+ int result = 0;
+
+ calibration.nvram = &digi->pdata->wcd;
+
+ if (strncmp(calibration.nvram->header.magic_string,
+ NVRAM_WCAL_SIGNATURE, strlen(NVRAM_WCAL_SIGNATURE)) == 0) {
+ unsigned int crc = ~crc32_le(~0,
+ (unsigned char const *) calibration.nvram->
+ cal_curves_bg,
+ calibration.nvram->header.wcd_len);
+
+ if (crc == calibration.nvram->header.wcd_crc) {
+ digi_dbg("CRC and signature for calibration data is okay\n");
+ result = 0;
+ if ((calibration.nvram->header.ver_major == '1')
+ && (calibration.nvram->header.ver_minor == '0')) {
+ digi_dbg("Converting version 1.0 calibration data\n");
+ determineMaxAdcValues(calibration.nvram);
+ /*
+ * Now that we have updated the format of the data, we need
+ * to recompute the check sum and set the new version.
+ */
+ calibration.nvram->header.ver_minor = '1';
+ calibration.nvram->header.wcd_crc =
+ ~crc32_le(~0, (unsigned char const *)
+ calibration.nvram->
+ cal_curves_bg,
+ calibration.nvram->header.wcd_len);
+ }
+ digi->rf->set_hw_info(digi->hw, digi->channel,
+ calibration.nvram->header.hw_platform);
+ } else {
+ digi_dbg("Calibration data has invalid CRC.\n");
+ setDefaultCalibrationValues(digi);
+ }
+ } else {
+ digi_dbg("Calibration data has invalid signature.\n");
+ setDefaultCalibrationValues(digi);
+ }
+
+ return result;
+}
+
+
+
+/*
+ * This routine:
+ *
+ * 1. Loads the ADC driver.
+ * 2. Loads the calibration data.
+ * 3. Implements the automatic calibration state machine.
+ *
+ */
+static int digiWifiCalibrationThreadEntry(void *data)
+{
+ struct piper_priv *digi = data;
+ int state;
+
+ __set_current_state(TASK_RUNNING);
+
+ while (1) {
+ /*
+ * We, the wireless driver, may be loaded before the I2C core has
+ * loaded. Therefore we may not be able to load our ADC driver,
+ * which is an I2C client driver, when we load. This loop tries
+ * and retries to load the ADC driver until it succeeds.
+ */
+
+ /* TODO, FIXME make following code dependent on platform information
+ * allowign to initialize different adc */
+ if (adc121C027_init(&calibration, digi->pdata->i2c_adapter_num) == 0) {
+ digi_dbg("ADC driver loaded...\n");
+ break;
+ }
+ digi_dbg("Will try to load ADC driver again...\n");
+ ssleep(10);
+ }
+
+ if (getCalibrationData(digi) == 0) {
+ digi->rf->set_pwr = setNewPowerLevel;
+
+ state = RESTART_STATE;
+
+ digi_dbg("Starting autocalibration state machine.\n");
+ do {
+ int event;
+ int timeout = 0;
+ int expectedEvent = TIMED_OUT_EVENT;
+ int nextState = RESTART_STATE;
+
+ switch (state) {
+ case RESTART_STATE:
+ setInitialPowerLevel(digi,
+ CONVERT_TO_MDBM(digi->tx_power));
+ calibration.initialized = true;
+ calibration.sampleCount = 0;
+ nextState = COLLECT_SAMPLES_STATE;
+ timeout = DEBOUNCE_DELAY;
+ expectedEvent = TIMED_OUT_EVENT;
+ break;
+ case COLLECT_SAMPLES_STATE:
+ digi->calibrationTxRate =
+ determineCalibrationTxRate(digi);
+ startSampleCollection(digi);
+ nextState = GOT_SAMPLE_STATE;
+ timeout = SAMPLE_TIMEOUT;
+ expectedEvent = TRANSMIT_DONE_EVENT;
+ break;
+ case GOT_SAMPLE_STATE:
+ processSample(digi);
+ if (calibration.sampleCount < MAX_SAMPLES) {
+ nextState = COLLECT_SAMPLES_STATE;
+ timeout = 0;
+ break;
+ }
+ /* fall through is intended operation */
+ case RECALIBRATE_STATE:
+ recalibrate(digi);
+ calibration.sampleCount = 0;
+ digi->calibrationTxRate = USE_MAC80211_DATA_RATE;
+ nextState = COLLECT_SAMPLES_STATE;
+ timeout = RECALIBRATION_PERIOD;
+ expectedEvent = TIMED_OUT_EVENT;
+ break;
+ default:
+ digi_dbg("Unknown state %d\n", state);
+ nextState = COLLECT_SAMPLES_STATE;
+ timeout = RECALIBRATION_PERIOD;
+ expectedEvent = TIMED_OUT_EVENT;
+ break;
+ }
+
+ state = nextState;
+ event = waitForEvent(timeout, expectedEvent);
+
+ switch (event) {
+ case SHUTDOWN_AUTOCALIBRATION_EVENT:
+ digi_dbg("Received SHUTDOWN_AUTOCALIBRATION_EVENT\n");
+ break;
+ case RESTART_AUTOCALIBRATION_EVENT:
+ digi_dbg("Received RESTART_AUTOCALIBRATION_EVENT\n");
+ state = RESTART_STATE;
+ break;
+ case TRANSMIT_DONE_EVENT:
+ break;
+ case TIMED_OUT_EVENT:
+ if (state == GOT_SAMPLE_STATE) {
+ state = COLLECT_SAMPLES_STATE;
+ }
+ }
+ } while (!kthread_should_stop());
+ } else {
+ printk(KERN_ERR
+ "\nWarning: Wireless interface calibration data is corrupted.\n");
+ printk(KERN_ERR
+ " The wireless interface will operate at a low power level.\n");
+ while (!kthread_should_stop()) {
+ ssleep(1);
+ }
+ }
+ calibration.cops->adc_shutdown(&calibration);
+
+ return 0;
+}
+
+
+
+/*
+ * This routine is called at initialization to set up the Airoha calibration routines.
+ */
+void digiWifiInitCalibration(struct piper_priv *digi)
+{
+
+ calibration.events = 0;
+ calibration.sampleCount = 0;
+ calibration.initialized = false;
+
+ spin_lock_init(&calibration.lock);
+
+ calibration.threadCB =
+ kthread_run(digiWifiCalibrationThreadEntry, digi,
+ PIPER_DRIVER_NAME " - calibration");
+}
+
+int digiWifiCalibrationPowerIndex(struct piper_priv *piperp)
+{
+ if (calibration.initialized)
+ return calibration.correctedPowerIndex;
+ else
+ return -1;
+}
+
+
+void digiWifiDeInitCalibration(struct piper_priv *digi)
+{
+ calibration.events = SHUTDOWN_AUTOCALIBRATION_EVENT;
+ wake_up_interruptible(&waitQueue);
+ kthread_stop(calibration.threadCB);
+ calibration.initialized = false;
+}
+
+EXPORT_SYMBOL_GPL(digiWifiDeInitCalibration);
diff --git a/drivers/net/wireless/digiPiper/airohaCalibration.h b/drivers/net/wireless/digiPiper/airohaCalibration.h
new file mode 100644
index 000000000000..36543c87fc51
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/airohaCalibration.h
@@ -0,0 +1,108 @@
+/*
+ * This file contains the code which performs automatic recalibration of the
+ * Airoha transceiver.
+ *
+ * Copyright (C) 2009 by Digi International Inc.
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+#ifndef AIROHA_CALIBRATION_H
+#define AIROHA_CALIBRATION_H
+
+#include "pipermain.h"
+
+
+#define MAX_SAMPLES (3)
+
+/*
+ * Field values used for computing ABS values.
+ */
+enum {
+ OUT_POWER,
+ ADC_VAL,
+ POWER_INDEX
+};
+
+#ifdef WANT_CAL_DEBUG
+static const char *fieldDescription[] = {
+ "OUT_POWER",
+ "ADC_VAL",
+ "POWER_INDEX"
+};
+#endif
+
+/*
+ * States for the auto calibration thread.
+ */
+enum {
+ RESTART_STATE,
+ COLLECT_SAMPLES_STATE,
+ GOT_SAMPLE_STATE,
+ RECALIBRATE_STATE
+};
+
+#ifdef WANT_CAL_DEBUG
+static const char *stateText[] = {
+ "RESTART_STATE",
+ "COLLECT_SAMPLES_STATE",
+ "GOT_SAMPLE_STATE",
+ "RECALIBRATE_STATE"
+};
+#endif
+
+
+/*
+ * Slope types accepted by computeSlope().
+ */
+enum {
+ POWER_INDEX_OVER_OUT_POWER,
+ ADC_OVER_OUT_POWER,
+ OUT_POWER_OVER_ADC,
+ POWER_INDEX_OVER_ADC
+};
+
+
+typedef struct {
+ unsigned rate; /* rate packet transmitted at */
+ unsigned int sample; /* measured sample */
+} sampleInfo_t;
+
+struct airohaCalibrationData {
+ struct task_struct *threadCB;
+ spinlock_t lock;
+ volatile unsigned int events;
+ unsigned int sampleCount;
+ sampleInfo_t sample[MAX_SAMPLES];
+ wcd_data_t *nvram;
+ wcd_curve_t *curve;
+ int slopeTimes1000;
+ int adcSlopeTimes1000;
+ int outPowerSlopeTimes1000;
+ int powerIndexSlopeTimes1000;
+ int expectedAdc;
+ int powerIndex, correctedPowerIndex;
+ wcd_point_t *p1;
+
+
+ void *priv;
+ struct calibration_ops *cops;
+ bool initialized;
+};
+
+struct calibration_ops {
+ u16(*adc_read_peak) (struct airohaCalibrationData *);
+ void (*adc_clear_peak) (struct airohaCalibrationData *);
+ u16(*adc_read_last_val) (struct airohaCalibrationData *);
+ void (*adc_shutdown) (struct airohaCalibrationData *);
+};
+
+
+void digiWifiInitCalibration(struct piper_priv *digi);
+void digiWifiDeInitCalibration(struct piper_priv *digi);
+int digiWifiCalibrationPowerIndex(struct piper_priv *piperp);
+
+#endif /* AIROHA_CALIBRATION_H */
diff --git a/drivers/net/wireless/digiPiper/digiDebug.c b/drivers/net/wireless/digiPiper/digiDebug.c
new file mode 100644
index 000000000000..627dd8c698ec
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/digiDebug.c
@@ -0,0 +1,205 @@
+/*
+ * digiDebug.c
+ *
+ * Copyright (C) 2009 by Digi International Inc.
+ * All rights reserved.
+ *
+ * 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 file contains some debugging routines.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+
+#include "pipermain.h"
+#include "mac.h"
+
+#define DUMP_WORDS_MAX (700)
+static unsigned int dumpWordsWord[DUMP_WORDS_MAX];
+static unsigned int dumpWordsCount = 0;
+
+void digiWifiDumpWordsAdd(unsigned int word)
+{
+ if (dumpWordsCount < DUMP_WORDS_MAX)
+ {
+ dumpWordsWord[dumpWordsCount++] = word;
+ }
+}
+void digiWifiDumpWordsDump(void)
+{
+ unsigned int *p = dumpWordsWord;
+ unsigned int wordsToGo = dumpWordsCount;
+
+ dumpWordsCount = 0;
+
+ while (wordsToGo >= 4)
+ {
+ digi_dbg("%8.8X %8.8X - %8.8X %8.8X\n", p[0], p[1], p[2], p[3]);
+ p += 4;
+ wordsToGo -= 4;
+ }
+ if (wordsToGo == 3)
+ {
+ digi_dbg("%8.8X %8.8X - %8.8X\n", p[0], p[1], p[2]);
+ }
+ else if (wordsToGo == 2)
+ {
+ digi_dbg("%8.8X %8.8X \n", p[0], p[1]);
+ }
+ else if (wordsToGo == 1)
+ {
+ digi_dbg("%8.8X \n", p[0]);
+ }
+ digi_dbg("--------------\n");
+}
+
+void digiWifiDumpWordsReset(void)
+{
+ dumpWordsCount = 0;
+}
+
+
+void digiWifiDumpBuffer(u8 *buffer, unsigned int length)
+{
+ unsigned int i, word;
+
+ digiWifiDumpWordsReset();
+
+ for (i = 0; i < length / sizeof(unsigned int); i++)
+ {
+ memcpy(&word, &buffer[i*sizeof(unsigned int)], sizeof(word));
+ digiWifiDumpWordsAdd(cpu_to_be32(word));
+ }
+
+ digiWifiDumpWordsDump();
+}
+
+
+void digiWifiDumpSkb(struct sk_buff *skb)
+{
+ unsigned int bytesLeft = skb->len;
+ unsigned char *p = skb->data;
+
+ digi_dbg("skb has %d bytes\n", skb->len);
+ while (bytesLeft >= 16)
+ {
+ digi_dbg("%2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X - %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
+ p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8], p[9], p[10], p[11], p[12], p[13], p[14], p[15]);
+ p += 16;
+ bytesLeft -= 16;
+ }
+ if (bytesLeft >= 8)
+ {
+ digi_dbg("%2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
+ p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
+ p += 8;
+ bytesLeft -= 8;
+ }
+ if (bytesLeft >= 4)
+ {
+ digi_dbg("%2.2X %2.2X %2.2X %2.2X \n",
+ p[0], p[1], p[2], p[3]);
+ p += 4;
+ bytesLeft -= 4;
+ }
+ if (bytesLeft >= 2)
+ {
+ digi_dbg("%2.2X %2.2X \n",
+ p[0], p[1]);
+ p += 2;
+ bytesLeft -= 2;
+ }
+ if (bytesLeft >= 1)
+ {
+ digi_dbg("%2.2X \n",
+ p[0]);
+ p += 1;
+ bytesLeft -= 1;
+ }
+}
+
+EXPORT_SYMBOL_GPL(digiWifiDumpSkb);
+
+
+void digiWifiDumpRegisters(struct piper_priv *digi, unsigned int regs)
+{
+#ifdef WANT_DEBUG
+ unsigned int i;
+
+ if (regs & CTRL_STATUS_REGS)
+ {
+ printk(KERN_ERR " %10.10s = 0x%8.8X %10.10s = 0x%8.8X\n", "Gen Ctrl", digi->ac->rd_reg(digi, BB_GENERAL_CTL),
+ "Gen Status", digi->ac->rd_reg(digi, BB_GENERAL_STAT));
+ }
+ else if (regs & IRQ_REGS)
+ {
+ printk(KERN_ERR " %10.10s = 0x%8.8X %10.10s = 0x%8.8X\n", "IRQ Mask", digi->ac->rd_reg(digi, BB_IRQ_MASK),
+ "IRQ Status", digi->ac->rd_reg(digi, BB_IRQ_STAT));
+ }
+ else if (regs & MAIN_REGS)
+ {
+ const char *regNames[] = {"Version", "Gen Ctrl", "Gen Status", "RSSI/AES",
+ "Int Mask", "Int Status", "SPI Data", "SPI Ctrl",
+ "Data FIFO", "not used", "conf-1", "conf-2", "AES FIFO",
+ "not used", "AES Ctrl", "IO Ctrl"};
+ printk(KERN_ERR "Main Registers:\n");
+ for (i = BB_VERSION; i <= BB_OUTPUT_CONTROL; i = i+8)
+ {
+ if ((i != BB_DATA_FIFO) && (i != BB_AES_FIFO))
+ {
+ printk(KERN_ERR " %10.10s = 0x%8.8X %10.10s = 0x%8.8X\n", regNames[i>>2], digi->ac->rd_reg(digi, i), regNames[(i>>2) + 1], digi->ac->rd_reg(digi, i+4));
+ }
+ }
+ }
+ if (regs & MAC_REGS)
+ {
+ const char *regNames[] = {"STA ID0", "STA ID1", "BSS ID0", "BSS ID1",
+ "OFDM/PSK", "Backoff", "DTIM/List", "B Int",
+ "Rev/M Stat", "C C/M CTL", "Measure", "Beac Fltr"};
+
+ printk(KERN_ERR "Secondary Registers:\n");
+ for (i = MAC_STA_ID0; i <= MAC_BEACON_FILT; i = i+8)
+ {
+ printk(KERN_ERR " %10.10s = 0x%8.8X %10.10s = 0x%8.8X\n", regNames[((i - MAC_STA_ID0) >>2)], digi->ac->rd_reg(digi, i), regNames[((i - MAC_STA_ID0)>>2) + 1], digi->ac->rd_reg(digi, i+4));
+ }
+ }
+ if (regs & FRAME_BUFFER_REGS)
+ {
+ unsigned int word[4];
+ printk(KERN_ERR "Real time frame buffer\n");
+
+ word[0] = be32_to_cpu(digi->ac->rd_reg(digi, 0xc0));
+ word[1] = be32_to_cpu(digi->ac->rd_reg(digi, 0xc4));
+ word[2] = be32_to_cpu(digi->ac->rd_reg(digi, 0xc8));
+ word[3] = be32_to_cpu(digi->ac->rd_reg(digi, 0xcc));
+ printk(KERN_ERR " %8.8X %8.8X - %8.8X %8.8X\n", word[0], word[1], word[2], word[3]);
+ word[0] = be32_to_cpu(digi->ac->rd_reg(digi, 0xd0));
+ word[1] = be32_to_cpu(digi->ac->rd_reg(digi, 0xd4));
+ word[2] = be32_to_cpu(digi->ac->rd_reg(digi, 0xd8));
+ word[3] = be32_to_cpu(digi->ac->rd_reg(digi, 0xdc));
+ printk(KERN_ERR " %8.8X %8.8X - %8.8X %8.8X\n", word[0], word[1], word[2], word[3]);
+ }
+ if (regs & FIFO_REGS)
+ {
+ unsigned int word[4];
+ printk(KERN_ERR "FIFO contents\n");
+
+ word[0] = digi->ac->rd_reg(digi, BB_DATA_FIFO);
+ word[1] = digi->ac->rd_reg(digi, BB_DATA_FIFO);
+ word[2] = digi->ac->rd_reg(digi, BB_DATA_FIFO);
+ word[3] = digi->ac->rd_reg(digi, BB_DATA_FIFO);
+ printk(KERN_ERR " %8.8X %8.8X - %8.8X %8.8X\n", word[0], word[1], word[2], word[3]);
+ word[0] = digi->ac->rd_reg(digi, BB_DATA_FIFO);
+ word[1] = digi->ac->rd_reg(digi, BB_DATA_FIFO);
+ word[2] = digi->ac->rd_reg(digi, BB_DATA_FIFO);
+ word[3] = digi->ac->rd_reg(digi, BB_DATA_FIFO);
+ printk(KERN_ERR " %8.8X %8.8X - %8.8X %8.8X\n", word[0], word[1], word[2], word[3]);
+ }
+#endif
+}
+EXPORT_SYMBOL_GPL(digiWifiDumpRegisters);
diff --git a/drivers/net/wireless/digiPiper/digiIsr.c b/drivers/net/wireless/digiPiper/digiIsr.c
new file mode 100644
index 000000000000..e2128e254055
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/digiIsr.c
@@ -0,0 +1,159 @@
+/*
+ * digiIsr.c
+ *
+ * Copyright (C) 2009 by Digi International Inc.
+ * All rights reserved.
+ *
+ * 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 file contains the routines that are related to processing interrupts
+ * from the MAC.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+
+#include "pipermain.h"
+#include "mac.h"
+#include "phy.h"
+
+#define IRQ_DEBUG (0)
+#if IRQ_DEBUG
+static int dlevel = DWARNING;
+#define dprintk(level, fmt, arg...) if (level >= dlevel) \
+ printk(KERN_ERR PIPER_DRIVER_NAME \
+ ": %s - " fmt, __func__, ##arg)
+#else
+#define dprintk(level, fmt, arg...) do {} while (0)
+#endif
+
+/*
+ * This routine handles interrupts from the MAC.
+ */
+irqreturn_t piper_irq_handler(int irq, void *dev_id)
+{
+ struct piper_priv *piperp = dev_id;
+ u32 status;
+
+ /* Acknowledge pending interrupts */
+ status = piperp->ac->rd_reg(piperp, BB_IRQ_STAT);
+ status &= piperp->ac->rd_reg(piperp, BB_IRQ_MASK);
+ piperp->ac->wr_reg(piperp, BB_IRQ_STAT, status, op_write);
+
+ if (status & BB_IRQ_MASK_RX_FIFO) {
+ /*
+ * This interrupt indicates we have a frame in the FIFO.
+ * Set up to receive the packet. Disable further interrupts
+ * until the receive is complete.
+ */
+ piperp->clear_irq_mask_bit(piperp, BB_IRQ_MASK_RX_FIFO);
+ /*
+ * Call the receive routine directly inside the irq handler
+ * or in the tasklet, depending on configuration.
+ */
+#if WANT_TO_RECEIVE_FRAMES_IN_ISR
+ piper_rx_tasklet((unsigned long) piperp);
+#else
+ tasklet_hi_schedule(&piperp->rx_tasklet);
+#endif
+ }
+
+ if (status & BB_IRQ_MASK_TX_FIFO_EMPTY) {
+ /*
+ * Transmit complete interrupt. This IRQ is only unmasked if we are
+ * not expecting the packet to be ACKed. This will be the case for
+ * broadcasts. In this case, tell mac80211 the transmit occurred and
+ * restart the tx queue.
+ */
+ if (piper_tx_getqueue(piperp) != NULL) {
+ piperp->tx_signal_strength = 0;
+ piperp->tx_result = TX_COMPLETE;
+ tasklet_hi_schedule(&piperp->tx_tasklet);
+ } else {
+ dprintk(DWARNING, "BB_IRQ_MASK_TX_FIFO_EMPTY and null packet?\n");
+ }
+ piperp->clear_irq_mask_bit(piperp, BB_IRQ_MASK_TX_FIFO_EMPTY |
+ BB_IRQ_MASK_TIMEOUT | BB_IRQ_MASK_TX_ABORT);
+ }
+
+ if (status & BB_IRQ_MASK_TIMEOUT) {
+ /* AP did not ACK our TX packet */
+ if (piper_tx_getqueue(piperp) != NULL) {
+ /* Update retry counter */
+ tasklet_hi_schedule(&piperp->tx_tasklet);
+ } else {
+ dprintk(DWARNING, "BB_IRQ_MASK_TIMEOUT and null packet?\n");
+ }
+ piperp->clear_irq_mask_bit(piperp, BB_IRQ_MASK_TX_FIFO_EMPTY |
+ BB_IRQ_MASK_TIMEOUT | BB_IRQ_MASK_TX_ABORT);
+ }
+
+ if (unlikely(status & BB_IRQ_MASK_TX_ABORT)) {
+ dprintk(DWARNING, "TX abort\n");
+
+ /* Could not transmit a packet because the media was busy */
+ if (piper_tx_getqueue(piperp) != NULL) {
+ tasklet_hi_schedule(&piperp->tx_tasklet);
+ } else {
+ dprintk(DWARNING, "BB_IRQ_MASK_TX_ABORT and null packet?\n");
+ }
+ piperp->clear_irq_mask_bit(piperp, BB_IRQ_MASK_TX_FIFO_EMPTY |
+ BB_IRQ_MASK_TIMEOUT | BB_IRQ_MASK_TX_ABORT);
+ }
+
+ if (status & BB_IRQ_MASK_TBTT) {
+ /*
+ * This interrupt occurs at the start of a beacon period. The only thing
+ * we need to do is to write a new beacon backoff value.
+ */
+ u32 reg = piperp->ac->rd_reg(piperp, MAC_BEACON_FILT) & ~MAC_BEACON_BACKOFF_MASK;
+ piperp->ac->wr_reg(piperp, MAC_BEACON_FILT,
+ reg | piperp->get_next_beacon_backoff(), op_write);
+ /*
+ * TODO:
+ * Improve the way we keep track of whether or not we sent the last
+ * beacon. What we are doing now is to assume that we did until and
+ * unless we receive a beacon. What we should do is look for either
+ * a beacon or a TX end interrupt. However, since mac80211 doesn't
+ * tell us what the ATIM window is, we have to assume it is zero,
+ * which means we could be transmitting a frame at the same
+ * time we are sending the beacon, so there isn't really any easy
+ * way for us to do this. In fact, even if there was an ATIM
+ * window, we could have started a transmit just before we get this
+ * interrupt, so I'm not sure how we are really suppose to keep
+ * track of this.
+ */
+ /* assume we sent last beacon unless we receive one */
+ piperp->beacon.weSentLastOne = true;
+ }
+
+ if (status & BB_IRQ_MASK_ATIM) {
+ /*
+ * This interrupt should not occur since we are not using it. When in
+ * IBSS mode, the beacon period starts at the TBTT interrupt and ends
+ * at this interrupt. We are not suppose to send packets between the
+ * two interrupts. However, mac80211 does not seem to provide a way
+ * for us to find out how long the ATIM period is, so we have to assume
+ * that there isn't one.
+ *
+ * If we were supporting this interrupt we would have to synchronize
+ * with the transmit routine so that transmit is paused during this
+ * time.
+ */
+ dprintk(DWARNING, "BB_IRQ_MASK_ATIM irq (0x%08x)\n", status);
+ piperp->clear_irq_mask_bit(piperp, BB_IRQ_MASK_ATIM);
+ }
+
+ if (unlikely(status & BB_IRQ_MASK_RX_OVERRUN))
+ piperp->pstats.rx_overruns++;
+
+ return IRQ_HANDLED;
+}
+EXPORT_SYMBOL_GPL(piper_irq_handler);
+
+
+
diff --git a/drivers/net/wireless/digiPiper/digiMac80211.c b/drivers/net/wireless/digiPiper/digiMac80211.c
new file mode 100644
index 000000000000..b9ca3ef10230
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/digiMac80211.c
@@ -0,0 +1,1000 @@
+/*
+ * digiMac80211.c
+ *
+ * Copyright (C) 2009 by Digi International Inc.
+ * All rights reserved.
+ *
+ * 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 file contains the routines that interface with the mac80211
+ * library.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+#include <linux/etherdevice.h>
+#include <net/mac80211.h>
+#include <crypto/aes.h>
+#include "pipermain.h"
+#include "mac.h"
+#include "phy.h"
+#include "digiPs.h"
+
+#define MAC_DEBUG (1)
+
+#if MAC_DEBUG
+static int dlevel = DWARNING;
+#define dprintk(level, fmt, arg...) if (level >= dlevel) \
+ printk(KERN_ERR PIPER_DRIVER_NAME \
+ ": %s - " fmt, __func__, ##arg)
+#else
+#define dprintk(level, fmt, arg...) do {} while (0)
+#endif
+
+/*
+ * This constant determines how many times per second the led_timer_fn
+ * function will be called. (HZ >> 3) means 8 times a second.
+ */
+#define LED_TIMER_RATE (HZ >> 3)
+#define LED_MAX_COUNT (15)
+
+/*
+ * This function is called from a timer to blink an LED in order to
+ * indicate what are current state is.
+ */
+static void led_timer_fn(unsigned long context)
+{
+ struct piper_priv *piperp = (struct piper_priv *) context;
+ static unsigned int count = 0;
+
+ if(!piperp->pdata->set_led)
+ return;
+
+ switch (piperp->led_state) {
+ case led_shutdown:
+ /* Turn LED off if we are shut down */
+ piperp->pdata->set_led(piperp, STATUS_LED, 0);
+ break;
+ case led_adhoc:
+ /* Blink LED slowly in ad-hoc mode */
+ piperp->pdata->set_led(piperp, STATUS_LED, (count & 8) ? 0 : 1);
+ break;
+ case led_not_associated:
+ /* Blink LED rapidly when not associated with an AP */
+ piperp->pdata->set_led(piperp, STATUS_LED, (count & 1) ? 0 : 1);
+ break;
+ case led_associated:
+ /* LED steadily on when associated */
+ piperp->pdata->set_led(piperp, STATUS_LED, 1);
+ break;
+ default:
+ /* Oops. How did we get here? */
+ break;
+ }
+ count++;
+ if (count > LED_MAX_COUNT) {
+ count = 0;
+ }
+
+ piperp->led_timer.expires += LED_TIMER_RATE;
+ add_timer(&piperp->led_timer);
+}
+
+/*
+ * This function sets the current LED state.
+ */
+static int piper_set_status_led(struct ieee80211_hw *hw, enum led_states state)
+{
+ struct piper_priv *piperp = (struct piper_priv *)hw->priv;
+
+ piperp->led_state = state;
+
+ if(!piperp->pdata->set_led)
+ return -ENOSYS;
+
+ if (state == led_shutdown)
+ piperp->pdata->set_led(piperp, STATUS_LED, 0);
+
+ return 0;
+}
+
+/*
+ * This routine is called to enable IBSS support whenever we receive
+ * configuration commands from mac80211 related to IBSS support. The
+ * routine examines the configuration settings to determine if IBSS
+ * support should be enabled and, if so, turns on automatic beacon
+ * generation.
+ *
+ * TODO: This code may need to be moved into piper.c since other
+ * H/W does not implement automatic generate of beacons.
+ */
+static void piper_enable_ibss(struct piper_priv *piperp, enum nl80211_iftype iftype)
+{
+ dprintk(DVVERBOSE, "\n");
+
+ if (((iftype == NL80211_IFTYPE_ADHOC) || (iftype == NL80211_IFTYPE_MESH_POINT))
+ && (piperp->beacon.loaded) && (piperp->beacon.enabled)
+ && ((piperp->ac->rd_reg(piperp, MAC_CFP_ATIM) & MAC_BEACON_INTERVAL_MASK) != 0)) {
+ /*
+ * If we come here, then we are running in IBSS mode, beacons are enabled,
+ * and we have the information we need, so start sending beacons.
+ */
+ /* TODO: Handle non-zero ATIM period. mac80211 currently has no way to
+ * tell us what the ATIM period is, but someday they might fix that.*/
+
+ u32 reg = piperp->ac->rd_reg(piperp, MAC_BEACON_FILT) & ~MAC_BEACON_BACKOFF_MASK;
+ piperp->ac->wr_reg(piperp, MAC_BEACON_FILT,
+ reg | piperp->get_next_beacon_backoff(), op_write);
+ /* enable beacon interrupts*/
+ piperp->set_irq_mask_bit(piperp, BB_IRQ_MASK_TBTT);
+ piperp->ac->wr_reg(piperp,
+ MAC_CTL, MAC_CTL_BEACON_TX | MAC_CTL_IBSS, op_or);
+ dprintk(DVERBOSE, "IBSS turned ON\n");
+ piper_set_status_led(piperp->hw, led_adhoc);
+ } else {
+ /*
+ * If we come here, then either we are not suppose to transmit beacons,
+ * or we do not yet have all the information we need to transmit
+ * beacons. Make sure the automatic beacon function is disabled.
+ */
+ /* shut down beacons */
+ piperp->ac->wr_reg(piperp, MAC_CTL,
+ ~(MAC_CTL_BEACON_TX | MAC_CTL_IBSS), op_and);
+ piperp->set_irq_mask_bit(piperp, BB_IRQ_MASK_TBTT);
+ dprintk(DVERBOSE, "IBSS turned OFF\n");
+ }
+}
+
+/*
+ * Set the transmit power level. The real work is done in the
+ * transceiver code.
+ */
+static int piper_set_tx_power(struct ieee80211_hw *hw, int power)
+{
+ struct piper_priv *digi = hw->priv;
+ int err;
+ int oldTxPower = digi->tx_power;
+
+ dprintk(DVVERBOSE, "\n");
+
+ if (power == digi->tx_power)
+ return 0;
+
+ digi->tx_power = power;
+ err = digi->rf->set_pwr(hw, power);
+ if (err)
+ digi->tx_power = oldTxPower;
+
+ return err;
+}
+
+/*
+ * Utility routine that sets a sequence number for a data packet.
+ */
+static void assign_seq_number(struct sk_buff *skb, bool increment)
+{
+#define SEQUENCE_NUMBER_MASK (0xfff0)
+ static u16 seq_number = 0;
+ _80211HeaderType *header = (_80211HeaderType *)skb->data;
+
+ dprintk(DVVERBOSE, "\n");
+
+ if (skb->len >= sizeof(*header)) {
+ u16 seq_field;
+
+ /*
+ * TODO: memcpy's are here because I am concerned we may get
+ * an unaligned frame. Is this a real possibility? Or
+ * am I just wasting CPU time?
+ */
+ memcpy(&seq_field, &header->squ.sq16, sizeof(header->squ.sq16));
+ seq_field &= ~SEQUENCE_NUMBER_MASK;
+ seq_field |= (SEQUENCE_NUMBER_MASK & (seq_number << 4));
+ memcpy(&header->squ.sq16, &seq_field, sizeof(header->squ.sq16));
+ if (increment)
+ seq_number++;
+ }
+}
+
+#define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ ((u32)(pt)[2] << 8) ^ ((u32)(pt)[3]))
+/* Get 16 bits at byte pointer */
+#define GET16(bp) ((bp)[0] | ((bp)[1] << 8))
+/* Get 32 bits at byte pointer */
+#define GET32(bp) ((bp)[0] | ((bp)[1] << 8) | ((bp)[2] << 16) | ((bp)[3] << 24))
+/* Store 16 bits at byte pointer */
+#define SET16(bp, data) { (bp)[0] = (data); \
+ (bp)[1] = (data) >> 8; }
+/* Store 32 bits at byte pointer */
+#define SET32(bp, data) { (bp)[0] = (data); \
+ (bp)[1] = (data) >> 8; \
+ (bp)[2] = (data) >> 16; \
+ (bp)[3] = (data) >> 24; }
+
+static inline void dw_inc_48(u48* n)
+{
+ (*n)++;
+ *n &= ((u64) 1 << 48) - 1;
+}
+
+/*
+ * This function prepares a blob of data we will have to send to the AES
+ * H/W encryption engine. The data consists of the AES initialization
+ * vector and 2 16 byte headers.
+ *
+ * Returns true if successful, or false if something goes wrong
+ */
+bool piper_prepare_aes_datablob(struct piper_priv *piperp, unsigned int keyIndex,
+ u8 *aesBlob, u8 *frame, u32 length, bool isTransmit)
+{
+ _80211HeaderType *header = (_80211HeaderType *)frame;
+ u8 *body = &frame[sizeof(*header)];
+ int dlen = length - (_80211_HEADER_LENGTH + PIPER_EXTIV_SIZE);
+
+ dprintk(DVVERBOSE, "\n");
+
+ if (keyIndex >= PIPER_MAX_KEYS) {
+ dprintk(DWARNING, "encryption key index %d is out of range.\n",
+ keyIndex);
+ return false;
+ }
+
+ if (piperp->key[keyIndex].valid == false)
+ return false;
+
+ /* Set up CCM initial block for MIC IV */
+ memset(aesBlob, 0, AES_BLOB_LENGTH);
+ aesBlob[0] = 0x59;
+ aesBlob[1] = 0;
+ memcpy (&aesBlob[2], header->addr2, ETH_ALEN);
+ aesBlob[8] = body[7];
+ aesBlob[9] = body[6];
+ aesBlob[10] = body[5];
+ aesBlob[11] = body[4];
+ aesBlob[12] = body[1];
+ aesBlob[13] = body[0];
+ aesBlob[14] = dlen >> 8;
+ aesBlob[15] = dlen;
+
+ /* Set up MIC header blocks */
+#define AES_HEADER_0_OFFSET (16)
+#define AES_HEADER_1_OFFSET (32)
+ aesBlob[AES_HEADER_0_OFFSET+0] = 0;
+ aesBlob[AES_HEADER_0_OFFSET+1] = 22;
+ aesBlob[AES_HEADER_0_OFFSET+2] = frame[0] & 0xcf;
+ aesBlob[AES_HEADER_0_OFFSET+3] = frame[1] & 0xd7;
+ /*
+ * This memcpy writes data into the last 12 bytes of the first header
+ * and the first 6 bytes of the 2nd header. I did it as one memcpy
+ * call for efficiency.
+ */
+ memcpy(&aesBlob[AES_HEADER_0_OFFSET+4], header->addr1, 3*ETH_ALEN);
+ aesBlob[AES_HEADER_1_OFFSET+6] = header->squ.sq.frag;
+ aesBlob[AES_HEADER_1_OFFSET+7] = 0;
+ memset (&aesBlob[AES_HEADER_1_OFFSET+8], 0, 8); /* clear vector location in data */
+
+ return true;
+}
+
+/*
+ * mac80211 calls this routine to transmit a frame. We set up
+ * up the information the trasmit tasklet will need, and then
+ * schedule the tasklet.
+ */
+int piper_hw_tx_private(struct ieee80211_hw *hw, struct sk_buff *skb, tx_skb_return_cb_t skb_return_cb)
+{
+ struct piper_priv *piperp = hw->priv;
+ struct ieee80211_tx_info *txInfo = IEEE80211_SKB_CB(skb);
+ unsigned long flags;
+
+ dprintk(DVVERBOSE, "\n");
+
+ if (piperp->is_radio_on == false) {
+ dprintk(DERROR, "called with radio off\n");
+ return -EBUSY;
+ }
+
+ /*
+ * Our H/W can only transmit a single packet at a time. mac80211
+ * already maintains a queue of packets, so there is no reason
+ * for us to set up another one. We stop the mac80211 queue everytime
+ * we get a transmit request and restart it when the transmit
+ * operation completes.
+ */
+ ieee80211_stop_queues(hw);
+
+ if (txInfo->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) {
+ assign_seq_number(skb,
+ !!(txInfo->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT));
+ }
+
+ piperp->use_hw_aes = false;
+ if (txInfo->control.hw_key != NULL) {
+ /*
+ * We've been passed an encryption key, so mac80211 must want us
+ * to encrypt the packet with our fancy H/W encryptor. Let's get
+ * set up for that now.
+ */
+ piperp->tx_aes_key = txInfo->control.hw_key->hw_key_idx;
+ piperp->use_hw_aes =
+ piper_prepare_aes_datablob(piperp,
+ txInfo->control.hw_key->hw_key_idx,
+ (u8 *)piperp->tx_aes_blob, skb->data,
+ skb->len, true);
+ }
+ piper_ps_set_header_flag(piperp, ((_80211HeaderType *) skb->data)); /* set power management bit as appropriate*/
+
+ /*
+ * Add space at the start of the frame for the H/W level transmit header.
+ * We can't generate the header now. It must be generated everytime we
+ * transmit because the transmit rate changes when we do retries.
+ */
+ skb_push(skb, TX_HEADER_LENGTH);
+
+ piperp->pstats.tx_retry_index = 0;
+ piperp->pstats.tx_total_tetries = 0;
+ memset(piperp->pstats.tx_retry_count, 0, sizeof(piperp->pstats.tx_retry_count));
+ txInfo->flags &= ~(IEEE80211_TX_STAT_TX_FILTERED |
+ IEEE80211_TX_STAT_ACK |
+ IEEE80211_TX_STAT_AMPDU |
+ IEEE80211_TX_STAT_AMPDU_NO_BACK);
+ piperp->pstats.tx_queue.len++;
+ piperp->pstats.tx_queue.count++;
+
+ if (piper_tx_enqueue(piperp, skb, skb_return_cb) == -1) {
+ skb_pull(skb, TX_HEADER_LENGTH); /* undo the skb_push above */
+ return -EBUSY;
+ }
+
+ spin_lock_irqsave(&piperp->tx_tasklet_lock, flags);
+ if (!piperp->tx_tasklet_running) {
+ piperp->tx_tasklet_running = true;
+ tasklet_hi_schedule(&piperp->tx_tasklet);
+ }
+
+ spin_unlock_irqrestore(&piperp->tx_tasklet_lock, flags);
+
+ piperp->pstats.tx_start_count++;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(piper_hw_tx_private);
+
+
+int piper_hw_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
+{
+ return piper_hw_tx_private(hw, skb, ieee80211_tx_status_irqsafe);
+}
+
+
+/*
+ * mac80211 calls this routine to initialize the H/W.
+ */
+static int piper_hw_start(struct ieee80211_hw *hw)
+{
+ struct piper_priv *piperp = hw->priv;
+ int ret = 0;
+
+ dprintk(DVVERBOSE, "\n");
+ piperp->if_type = __NL80211_IFTYPE_AFTER_LAST;
+
+ /* Initialize the MAC H/W */
+ if ((ret = piperp->init_hw(piperp, IEEE80211_BAND_2GHZ)) != 0) {
+ dprintk(DERROR, "unable to initialize piper HW (%d)\n", ret);
+ return ret;
+ }
+
+ piperp->is_radio_on = true;
+
+ /*
+ * Initialize the antenna with the defualt setting defined in the
+ * probe function. This can be changed, currently, through a sysfs
+ * entry in the device directory
+ */
+ if ((ret = piperp->set_antenna(piperp, piperp->antenna)) != 0) {
+ dprintk(DERROR, "piper_set_antenna_div() failed (%d)\n", ret);
+ return ret;
+ }
+
+ /* set status led to link off */
+ piper_set_status_led(hw, led_shutdown);
+
+ /* Get the tasklets ready to roll */
+ piperp->tx_result = TX_NOT_DONE;
+ tasklet_enable(&piperp->rx_tasklet);
+ tasklet_enable(&piperp->tx_tasklet);
+
+ /*
+ * Enable receive interrupts, but leave the transmit interrupts
+ * and beacon interrupts off for now.
+ */
+ piperp->clear_irq_mask_bit(piperp, 0xffffffff);
+ piperp->set_irq_mask_bit(piperp,
+ BB_IRQ_MASK_RX_OVERRUN | BB_IRQ_MASK_RX_FIFO);
+ enable_irq(piperp->irq);
+
+ memset(piperp->bssid, 0, ETH_ALEN);
+
+ return 0;
+}
+
+/*
+ * mac80211 calls this routine to shut us down.
+ */
+static void piper_hw_stop(struct ieee80211_hw *hw)
+{
+ struct piper_priv *piperp = hw->priv;
+
+ dprintk(DVVERBOSE, "\n");
+
+ /* Initialize the MAC H/W */
+ if (piperp->deinit_hw)
+ piperp->deinit_hw(piperp);
+
+ /* set status led to link off */
+ if (piper_set_status_led(hw, led_shutdown))
+ return; /* hardware's probably gone, give up */
+
+ /* turn off phy */
+ piperp->rf->stop(hw);
+
+ /* Disable interrupts before turning off */
+ disable_irq(piperp->irq);
+
+ /* turn off MAX_GAIN, ADC clocks, and so on */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, ~BB_GENERAL_CTL_RESET, op_and);
+
+ /* turn off MAC control/mac filt/aes key */
+ piperp->ac->wr_reg(piperp, MAC_CTL, 0, op_write);
+
+ /* turn off interrupts */
+ tasklet_disable(&piperp->rx_tasklet);
+ tasklet_disable(&piperp->tx_tasklet);
+ piperp->clear_irq_mask_bit(piperp, 0xffffffff);
+}
+
+/*
+ * mac80211 calls this routine to really start the H/W.
+ * The device type is also set here.
+ */
+static int piper_hw_add_intf(struct ieee80211_hw *hw,
+ struct ieee80211_if_init_conf *conf)
+{
+ struct piper_priv *piperp = hw->priv;
+
+ dprintk(DVVERBOSE, "if type: %x\n", conf->type);
+
+ /* __NL80211_IFTYPE_AFTER_LAST means no mode selected */
+ if (piperp->if_type != __NL80211_IFTYPE_AFTER_LAST) {
+ dprintk(DERROR, "unsupported interface type %x, expected %x\n",
+ conf->type, __NL80211_IFTYPE_AFTER_LAST);
+ return -EOPNOTSUPP;
+ }
+
+ switch (conf->type) {
+ case NL80211_IFTYPE_ADHOC:
+ piper_set_status_led(piperp->hw, led_adhoc);
+ piperp->if_type = conf->type;
+ break;
+
+ case NL80211_IFTYPE_STATION:
+ piper_set_status_led(hw, led_not_associated);
+ piperp->if_type = conf->type;
+ break;
+
+ case NL80211_IFTYPE_MESH_POINT:
+ piperp->if_type = conf->type;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+/*
+ * mac80211 calls this function to shut down us down.
+ */
+static void piper_hw_rm_intf(struct ieee80211_hw *hw,
+ struct ieee80211_if_init_conf *conf)
+{
+ struct piper_priv *digi = hw->priv;
+
+ dprintk(DVVERBOSE, "\n");
+ digi->if_type = __NL80211_IFTYPE_AFTER_LAST;
+}
+
+/*
+ * mac80211 calls this function to pass us configuration information.
+ */
+static int piper_config(struct ieee80211_hw *hw, u32 changed)
+{
+ struct piper_priv *piperp = hw->priv;
+ struct ieee80211_conf *conf = &hw->conf;
+ u32 tempval;
+ int err;
+
+ dprintk(DVVERBOSE, "\n");
+
+ if (changed & IEEE80211_CONF_CHANGE_PS) {
+ /*
+ * Enable power save mode if bit set in flags, and if we are in station
+ * mode. Power save is not supported in ad-hoc/mesh mode.
+ */
+ piper_ps_scan_event(piperp);
+ piper_ps_set(piperp, ( (conf->flags & IEEE80211_CONF_PS)
+ && (piperp->if_type == NL80211_IFTYPE_STATION)
+ && (piperp->areWeAssociated)));
+ }
+ /* Should we turn the radio off? */
+ if ((piperp->is_radio_on = (conf->radio_enabled != 0)) != 0) {
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL,
+ BB_GENERAL_CTL_RX_EN, op_or);
+ } else {
+ dprintk(DNORMAL, "Turning radio off\n");
+ return piperp->ac->wr_reg(piperp, BB_GENERAL_CTL,
+ ~BB_GENERAL_CTL_RX_EN, op_and);
+ }
+
+ /* Adjust the beacon interval and listen interval */
+ tempval = piperp->ac->rd_reg(piperp, MAC_CFP_ATIM) & ~MAC_BEACON_INTERVAL_MASK;
+ tempval |= conf->beacon_int << MAC_BEACON_INTERVAL_SHIFT;
+ piperp->ac->wr_reg(piperp, MAC_CFP_ATIM, tempval, op_write);
+
+ tempval = piperp->ac->rd_reg(piperp, MAC_DTIM_PERIOD) & ~MAC_LISTEN_INTERVAL_MASK;
+ tempval |= conf->listen_interval;
+ piperp->ac->wr_reg(piperp, MAC_DTIM_PERIOD, tempval, op_write);
+
+ /* Adjust the power level */
+ if ((err = piper_set_tx_power(hw, conf->power_level)) != 0) {
+ dprintk(DERROR, "unable to set tx power to %d\n",
+ conf->power_level);
+ return err;
+ }
+
+ /* Set channel */
+ if (conf->channel->hw_value != piperp->channel) {
+ piper_ps_scan_event(piperp);
+ if ((err = piperp->rf->set_chan(hw, conf->channel->hw_value)) !=0) {
+ dprintk(DERROR, "unable to set ch to %d\n",
+ conf->channel->hw_value);
+ return err;
+ }
+ piperp->channel = conf->channel->hw_value;
+ }
+
+ return 0;
+}
+
+/*
+ * mac80211 calls this routine to set BSS related configuration settings.
+ */
+static int piper_hw_config_intf(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_if_conf *conf)
+{
+ struct piper_priv *piperp = hw->priv;
+ u32 bssid[2];
+
+ dprintk(DVVERBOSE, "\n");
+
+ if (conf->changed & IEEE80211_IFCC_BSSID &&
+ !is_zero_ether_addr(conf->bssid) &&
+ !is_multicast_ether_addr(conf->bssid)) {
+
+ piper_ps_scan_event(piperp);
+ switch (vif->type) {
+ case NL80211_IFTYPE_STATION:
+ case NL80211_IFTYPE_ADHOC:
+ dprintk(DVERBOSE, "BSSID: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X\n",
+ conf->bssid[0], conf->bssid[1], conf->bssid[2],
+ conf->bssid[3], conf->bssid[4], conf->bssid[5]);
+
+ bssid[0] = conf->bssid[3] | conf->bssid[2] << 8 |
+ conf->bssid[1] << 16 | conf->bssid[0] << 24;
+ bssid[1] = conf->bssid[5] << 16 | conf->bssid[4] << 24;
+
+ if ((bssid[0] == 0) && (bssid[1] == 0)) {
+ /*
+ * If we come here, then the MAC layer is telling us to set a 0
+ * SSID. In this case, we really want to set the SSID to the
+ * broadcast address so that we receive broadcasts.
+ */
+ bssid[0] = 0xffffffff;
+ bssid[1] = 0xffffffff;
+ }
+ piperp->ac->wr_reg(piperp, MAC_BSS_ID0, bssid[0], op_write);
+ piperp->ac->wr_reg(piperp, MAC_BSS_ID1, bssid[1], op_write);
+ memcpy(piperp->bssid, conf->bssid, ETH_ALEN);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if ((conf->changed & IEEE80211_IFCC_BEACON) &&
+ (piperp->if_type == NL80211_IFTYPE_ADHOC)) {
+ struct sk_buff *beacon = ieee80211_beacon_get(hw, vif);
+ struct ieee80211_rate rate;
+
+ if (!beacon)
+ return -ENOMEM;
+
+ rate.bitrate = 10; /* beacons always sent at 1 Megabit*/
+ skb_push(beacon, TX_HEADER_LENGTH);
+ phy_set_plcp(beacon->data, beacon->len - TX_HEADER_LENGTH, &rate, 0);
+ piperp->ac->wr_reg(piperp, MAC_CTL, ~MAC_CTL_BEACON_TX, op_and);
+ piperp->load_beacon(piperp, beacon->data, beacon->len);
+
+ /* TODO: digi->beacon.enabled should be set by IEEE80211_IFCC_BEACON_ENABLED
+ when we update to latest mac80211 */
+ piperp->beacon.enabled = true;
+ piper_enable_ibss(piperp, vif->type);
+ dev_kfree_skb(beacon); /* we are responsible for freeing this buffer*/
+ }
+
+ return 0;
+}
+
+/*
+ * mac80211 wants to change our frame filtering settings. We don't
+ * actually support this.
+ */
+static void piper_hw_config_filter(struct ieee80211_hw *hw,
+ unsigned int changed_flags, unsigned int *total_flags,
+ int mc_count, struct dev_addr_list *mclist)
+{
+ dprintk(DVVERBOSE, "\n");
+
+ /* we don't support filtering so clear all flags; however, we also
+ * can't pass failed FCS/PLCP frames, so don't clear those. */
+ *total_flags &= (FIF_FCSFAIL | FIF_PLCPFAIL);
+}
+
+/*
+ * There are 1024 TU's (time units) to a second, and HZ jiffies to a
+ * second. This macro converts TUs to jiffies.
+ */
+#define TU_TO_JIFFIES(x) (((x*HZ) + 512) / 1024)
+
+
+/*
+ * mac80211 calls this routine when something about our BSS has changed.
+ * Usually, this routine only gets called when we associate, or disassociate.
+ */
+static void piper_hw_bss_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+ struct ieee80211_bss_conf *conf, u32 changed)
+{
+ struct piper_priv *piperp = hw->priv;
+ u32 reg;
+
+ dprintk(DVVERBOSE, " changed = 0x%08x\n", changed);
+
+ if (changed & BSS_CHANGED_ASSOC) {
+ piper_ps_scan_event(piperp);
+ /* Our association status has changed */
+ if (piperp->if_type == NL80211_IFTYPE_STATION) {
+ piper_set_status_led(hw, conf->assoc ? led_associated : led_not_associated);
+ }
+ piperp->areWeAssociated = conf->assoc;
+ piperp->ps.aid = conf->aid;
+
+ digi_dbg(" AP %s\n", conf->assoc ?
+ "associated" : "disassociated");
+ }
+ if (changed & BSS_CHANGED_ERP_CTS_PROT) {
+ piperp->tx_cts = conf->use_cts_prot;
+ }
+ if (changed & BSS_CHANGED_ERP_PREAMBLE) {
+#define WANT_SHORT_PREAMBLE_SUPPORT (1)
+/* TODO: Determine if short preambles really hurt us, or if I'm just seeing things. */
+#if WANT_SHORT_PREAMBLE_SUPPORT
+ if (conf->use_short_preamble) {
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL,
+ BB_GENERAL_CTL_SH_PRE, op_or);
+ } else {
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL,
+ ~BB_GENERAL_CTL_SH_PRE, op_and);
+ }
+ piperp->use_short_preamble = conf->use_short_preamble;
+#else
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, BB_GENERAL_CTL_SH_PRE, op_or);
+#endif
+ }
+
+ if (changed & BSS_CHANGED_BASIC_RATES) {
+ /*
+ * The list of transmit rates has changed. Update the
+ * rates we will receive at to match those the AP will
+ * transmit at. This should improve our receive performance
+ * since we won't listen to junk at the wrong rate.
+ */
+ unsigned int ofdm = 0, psk = 0;
+
+ reg = piperp->ac->rd_reg(piperp, MAC_SSID_LEN) &
+ ~(MAC_OFDM_BRS_MASK | MAC_PSK_BRS_MASK);
+
+ piperp->rf->getOfdmBrs(piperp->channel, conf->basic_rates, &ofdm, &psk);
+ reg |= ofdm << MAC_OFDM_BRS_SHIFT;
+ reg |= psk << MAC_PSK_BRS_SHIFT;
+ piperp->ac->wr_reg(piperp, MAC_SSID_LEN, reg, op_write);
+
+ dprintk(DVERBOSE, "BRS mask set to 0x%8.8X\n", reg);
+
+ if (ofdm == 0) {
+ /* Disable ofdm receiver if no ofdm rates supported */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_STAT,
+ ~BB_GENERAL_STAT_A_EN, op_and);
+ } else {
+ /* Enable ofdm receiver if any ofdm rates supported */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_STAT,
+ BB_GENERAL_STAT_A_EN, op_or);
+ }
+ }
+
+ /* Save new DTIM period */
+ reg = piperp->ac->rd_reg(piperp, MAC_DTIM_PERIOD) & ~MAC_DTIM_PERIOD_MASK;
+ reg |= conf->dtim_period << MAC_DTIM_PERIOD_SHIFT;
+ piperp->ac->wr_reg(piperp, MAC_DTIM_PERIOD, reg, op_write);
+ reg = piperp->ac->rd_reg(piperp, MAC_CFP_ATIM) & ~MAC_DTIM_CFP_MASK;
+ piperp->ps.beacon_int = conf->beacon_int;
+ reg |= conf->beacon_int << 16;
+ piperp->ac->wr_reg(piperp, MAC_CFP_ATIM, reg, op_write);
+}
+
+/*
+ * Use the SSL library routines to expand the AES key.
+ */
+static int piper_expand_aes_key(struct ieee80211_key_conf *key,
+ u32 *expandedKey)
+{
+ struct crypto_aes_ctx aes;
+ int ret;
+
+ dprintk(DVVERBOSE, "\n");
+
+ if (key->keylen != AES_KEYSIZE_128)
+ return -EOPNOTSUPP;
+
+ ret = crypto_aes_expand_key(&aes, key->key, key->keylen);
+ if (ret)
+ return -EOPNOTSUPP;
+
+ memcpy(expandedKey, aes.key_enc, EXPANDED_KEY_LENGTH);
+
+ return 0;
+}
+
+/*
+ * mac80211 calls this routine to set a new encryption key, or to
+ * retire an old one. We support H/W AES encryption/decryption, so
+ * save the AES related keys.
+ */
+static int piper_hw_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd,
+ const u8 *local_address, const u8 *address,
+ struct ieee80211_key_conf *key)
+{
+ struct piper_priv *piperp = hw->priv;
+ int ret = 0;
+
+ dprintk(DVVERBOSE, "\n");
+
+ if ((key->alg != ALG_CCMP) || (key->keyidx >= PIPER_MAX_KEYS)) {
+ /*
+ * If we come here, then mac80211 was trying to set a key for some
+ * algorithm other than AES, or trying to set a key index greater
+ * than 3. We only support AES, and only 4 keys.
+ */
+ ret = -EOPNOTSUPP;
+ goto set_key_error;
+ }
+ key->hw_key_idx = key->keyidx;
+
+ if (cmd == SET_KEY) {
+ ret = piper_expand_aes_key(key, piperp->key[key->keyidx].expandedKey);
+ if (ret)
+ goto set_key_error;
+
+ if (!piperp->key[key->keyidx].valid)
+ piperp->aes_key_count++;
+ piperp->key[key->keyidx].txPn = 0;
+ piperp->key[key->keyidx].rxPn = 0;
+ piperp->key[key->keyidx].valid = (ret == 0);
+ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV;
+ } else {
+ /* disable key */
+ if (piperp->key[key->keyidx].valid)
+ piperp->aes_key_count--;
+ piperp->key[key->keyidx].valid = false;
+ }
+
+ if (piperp->aes_key_count > 0)
+ piperp->ac->wr_reg(piperp, MAC_CTL, ~MAC_CTL_AES_DISABLE, op_and);
+ else
+ piperp->ac->wr_reg(piperp, MAC_CTL, MAC_CTL_AES_DISABLE, op_or);
+
+set_key_error:
+ if (ret)
+ dprintk(DVERBOSE, "unable to set AES key\n");
+
+ return ret;
+}
+
+/*
+ * mac80211 calls this routine to determine if we transmitted the
+ * last beacon. Unfortunately, we can't tell for sure. We give
+ * mac80211 our best guess.
+ */
+static int piper_hw_tx_last_beacon(struct ieee80211_hw *hw)
+{
+ struct piper_priv *piperp = hw->priv;
+
+ dprintk(DVVERBOSE, "\n");
+ return piperp->beacon.weSentLastOne ? 1 : 0;
+}
+
+static int piper_get_tx_stats(struct ieee80211_hw *hw,
+ struct ieee80211_tx_queue_stats *stats)
+{
+ struct piper_priv *piperp = hw->priv;
+
+ dprintk(DVVERBOSE, "\n");
+ if (stats)
+ memcpy(stats, &piperp->pstats.tx_queue, sizeof(piperp->pstats.tx_queue));
+
+ return 0;
+}
+
+static int piper_get_stats(struct ieee80211_hw *hw,
+ struct ieee80211_low_level_stats *stats)
+{
+ struct piper_priv *piperp = hw->priv;
+
+ dprintk(DVVERBOSE, "\n");
+ if (stats)
+ memcpy(stats, &piperp->pstats.ll_stats, sizeof(piperp->pstats.ll_stats));
+
+ return 0;
+}
+
+const struct ieee80211_ops hw_ops = {
+ .tx = piper_hw_tx,
+ .start = piper_hw_start,
+ .stop = piper_hw_stop,
+ .add_interface = piper_hw_add_intf,
+ .remove_interface = piper_hw_rm_intf,
+ .config = piper_config,
+ .config_interface = piper_hw_config_intf,
+ .configure_filter = piper_hw_config_filter,
+ .bss_info_changed = piper_hw_bss_changed,
+ .tx_last_beacon = piper_hw_tx_last_beacon,
+ .set_key = piper_hw_set_key,
+ .get_tx_stats = piper_get_tx_stats,
+ .get_stats = piper_get_stats,
+};
+
+/*
+ * This routine is called by the probe routine to allocate the
+ * data structure we need to communicate with mac80211.
+ */
+int piper_alloc_hw(struct piper_priv **priv, size_t priv_sz)
+{
+ struct piper_priv *piperp;
+ struct ieee80211_hw *hw;
+
+ hw = ieee80211_alloc_hw(priv_sz, &hw_ops);
+ if (!hw)
+ return -ENOMEM;
+
+ hw->flags |= IEEE80211_HW_RX_INCLUDES_FCS
+ | IEEE80211_HW_SIGNAL_DBM
+ | IEEE80211_HW_NOISE_DBM
+ | IEEE80211_HW_SPECTRUM_MGMT
+ | IEEE80211_HW_NO_STACK_DYNAMIC_PS
+#if !WANT_SHORT_PREAMBLE_SUPPORT
+ | IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE
+#endif
+ /* | IEEE80211_HW_SPECTRUM_MGMT TODO: Turn this on when we are ready*/;
+
+ hw->queues = 1;
+ hw->ampdu_queues = 0;
+ hw->extra_tx_headroom = 4 + sizeof(struct ofdm_hdr);
+ piperp = hw->priv;
+ *priv = piperp;
+ piperp->pstats.tx_queue.len = 0;
+ piperp->pstats.tx_queue.limit = 1;
+ piperp->pstats.tx_queue.count = 0;
+ piperp->areWeAssociated = false;
+ memset(&piperp->pstats.ll_stats, 0, sizeof(piperp->pstats.ll_stats));
+ piperp->hw = hw;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(piper_alloc_hw);
+
+/*
+ * This routine is called by the remove function to free the memory
+ * allocated by piper_alloc_hw.
+ */
+void piper_free_hw(struct piper_priv *piperp)
+{
+ ieee80211_free_hw(piperp->hw);
+}
+EXPORT_SYMBOL_GPL(piper_free_hw);
+
+/*
+ * This routine is called by the probe routine to register
+ * with mac80211.
+ */
+int piper_register_hw(struct piper_priv *priv, struct device *dev,
+ struct digi_rf_ops *rf)
+{
+ struct ieee80211_hw *hw = priv->hw;
+ u8 macaddr[8];
+ u32 temp;
+ int i, ret;
+
+ dprintk(DVVERBOSE, "\n");
+
+ priv->rf = rf;
+ for (i = 0; i < rf->n_bands; i++) {
+ enum ieee80211_band b = rf->bands[i].band;
+ hw->wiphy->bands[b] = &rf->bands[i];
+ }
+ hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_ADHOC)
+ | BIT(NL80211_IFTYPE_STATION)
+/* TODO: Enable this | BIT(NL80211_IFTYPE_MESH_POINT) */
+ ;
+ hw->channel_change_time = rf->channelChangeTime;
+ hw->vif_data_size = 0;
+ hw->sta_data_size = 0;
+ hw->max_rates = IEEE80211_TX_MAX_RATES;
+ hw->max_rate_tries = 5; /* completely arbitrary, and apparently ignored by the rate algorithm */
+ hw->max_signal = rf->maxSignal;
+ hw->max_listen_interval = 10; /* I don't think APs will work with values larger than 4 actually */
+
+ SET_IEEE80211_DEV(hw, dev);
+
+ temp = cpu_to_be32(priv->ac->rd_reg(priv, MAC_STA_ID0));
+ memcpy(macaddr, &temp, sizeof(temp));
+ temp = cpu_to_be32(priv->ac->rd_reg(priv, MAC_STA_ID1));
+ memcpy(&macaddr[4], &temp, sizeof(temp));
+ SET_IEEE80211_PERM_ADDR(hw, macaddr);
+
+ if ((ret = ieee80211_register_hw(hw)) != 0) {
+ dprintk(DERROR, "unable to register ieee80211 hw\n");
+ goto error;
+ }
+
+ printk(KERN_INFO PIPER_DRIVER_NAME ": registered as %s\n",
+ wiphy_name(hw->wiphy));
+
+ init_timer(&priv->led_timer);
+ priv->led_state = led_shutdown;
+ priv->led_timer.function = led_timer_fn;
+ priv->led_timer.data = (unsigned long) priv;
+ priv->led_timer.expires = jiffies + LED_TIMER_RATE;
+ add_timer(&priv->led_timer);
+
+error:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(piper_register_hw);
+
+
+void piper_unregister_hw(struct piper_priv *piperp)
+{
+ dprintk(DVVERBOSE, "\n");
+ del_timer_sync(&piperp->led_timer);
+ piper_set_status_led(piperp->hw, led_shutdown);
+ ieee80211_unregister_hw(piperp->hw);
+}
+EXPORT_SYMBOL_GPL(piper_unregister_hw);
+
+
+MODULE_DESCRIPTION("Digi Piper WLAN core");
+MODULE_AUTHOR("contact support@digi.com for information about this code");
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/digiPiper/digiPs.c b/drivers/net/wireless/digiPiper/digiPs.c
new file mode 100644
index 000000000000..c6ae6b0b656a
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/digiPs.c
@@ -0,0 +1,1164 @@
+/*
+ * digiPs.c
+ *
+ * Copyright (C) 2009 by Digi International Inc.
+ * All rights reserved.
+ *
+ * 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 file contains the routines that are related to transmitting
+ * frames.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+
+#include "pipermain.h"
+#include "mac.h"
+#include "phy.h"
+#include "airoha.h"
+#include "digiPs.h"
+
+/*
+ * Macro converts milliseconds to HZ.
+ *
+ * TODO: Look for standard Linux version of this.
+ */
+#define MILLS_TO_JIFFIES(x) ((((x)*HZ) + 500) / 1000)
+
+
+
+
+#define MINIMUM_SLEEP_PERIOD (20)
+
+/*
+ * Amount of time before shutdown deadline to start the shutdown process. This
+ * should allow enough time to get one last frame out, which will be the
+ * null-data frame notifying the AP that we are shutting down.
+ */
+#define PS_TRANSMITTER_SHUTDOWN_MS (10)
+
+/*
+ * Amount of time we will wake up before the next beacon. We try to wake up before
+ * the next beacon so that we don't miss it.
+ */
+#define PS_WAKE_BEFORE_BEACON_MS (20)
+
+/*
+ * Minimum amount of time we we keep awake for.
+ */
+#define PS_MINUMUM_POWER_UP_PERIOD_MS (10)
+
+/*
+ * Minimum amount of time we will sleep. If we will end up sleeping
+ * for less than this, then don't go to sleep.
+ */
+#define PS_MINIMUM_SLEEP_TIME (10)
+
+/*
+ * Length of one tick of the transmit clean up timer in ms. This is
+ * the minimum amount of time we will sleep for.
+ */
+#define PS_TRANSMIT_TIMER_TICK_MS ((1000/HZ) ? (1000/HZ) : 1)
+
+/*
+ * Length of time we will wait past the expected arrival of a beacon before assuming
+ * that we missed it.
+ */
+#define PS_BEACON_TIMEOUT_MS (40)
+
+
+/*
+ * Minimum beacon interval we will support for duty cycling. There is so much overhead
+ * in duty cycling that it doesn't make sense to do it for short beacon intervals.
+ */
+#define PS_MINIMUM_BEACON_INT (100)
+
+/*
+ * Amount of time we will pause duty cycling for after receiving an event that suggests
+ * wpa_supplicant is attempting to reassociate with an AP.
+ */
+#define PS_SCAN_DELAY (5000)
+
+
+// Powersave register index
+#define INDX_GEN_CONTROL 0 // General control
+#define INDX_GEN_STATUS 1 // General status
+#define INDX_RSSI_AES 2 // RSSI and AES status
+#define INDX_INTR_MASK 3 // Interrupt mask
+#define INDX_SPI_CTRL 4 // RF SPI control
+#define INDX_CONF1 5 // Configuration 1
+#define INDX_CONF2 6 // Configuration 2
+#define INDX_AES_MODE 7 // ARS mode
+#define INDX_OUT_CTRL 8 // Output control
+#define INDX_MAC_CONTROL 9 // MAC control
+#define INDX_STAID_1 10 // first part of stations ID
+#define INDX_STAID_2 11 // 2nd part of station ID
+#define INDX_BSSID_1 12 // 1st part of BSS ID
+#define INDX_BSSID_2 13 // 2nd part of BSS ID
+#define INDX_BRS_SSID 14 // BRS mask and SSID length
+#define INDX_BACKOFF 15 // backoff
+#define INDX_DTIM_LISTEN 16 // DTIM perido and listen interval
+#define INDX_BEACON_INT 17 // beacon interval
+#define INDX_MAC_CTL 18 // MAC control register
+#define INDX_BEACON_MASK 19 // beacon mask and backoff
+#define INDX_TOTAL 20
+
+static u32 savedRegs[INDX_TOTAL]; // Used to save registers for sleep mode
+
+
+/*
+ * TODO: Delete this.
+ */
+struct ps_stats {
+ unsigned int modeStart;
+ unsigned int cycleStart;
+ unsigned int receivedBeacons;
+ unsigned int missedBeacons;
+ unsigned int jiffiesOn;
+ unsigned int jiffiesOff;
+} stats;
+
+
+
+static void ps_free_frame(struct ieee80211_hw *hw, struct sk_buff *skb)
+{
+ struct piper_priv *piperp = hw->priv;
+
+ if (skb) {
+ dev_kfree_skb(skb);
+ piperp->ps.frames_pending--;
+ }
+}
+
+
+
+
+
+#define ACK_SIZE (14) /* length of ACK in bytes */
+
+// Length (in usecs) of a MAC frame of bytes at rate (in 500kbps units)
+// not including SIFS and PLCP preamble/header
+#define CCK_DURATION(bytes, rate) ((16*(bytes)+(rate)-1)/(rate))
+
+#define USE_SHORTPREAMBLE(is_1_Mbit, use_short_preamble) ((!is_1_Mbit) & use_short_preamble)
+
+// Length (in usecs) of SIFS and PLCP preamble/header.
+#define PRE_LEN(is_1_Mbit, use_short_preamble) (USE_SHORTPREAMBLE(is_1_Mbit, use_short_preamble) ? 106 : 202)
+
+// Duration (in usecs) of an OFDM frame at rate (in 500kbps units)
+// including SIFS and PLCP preamble/header
+#define OFDM_DURATION(bytes, rate) (36 + 4*((4*(bytes)+(rate)+10)/(rate)))
+
+static unsigned int getRateIndex(struct piper_priv *piperp)
+{
+ unsigned int rates = piperp->ac->rd_reg(piperp, MAC_SSID_LEN);
+ unsigned int result = AIROHA_LOWEST_OFDM_RATE_INDEX;
+
+ if (piperp->rf->getBand(piperp->channel) == IEEE80211_BAND_2GHZ) {
+ if ((rates & MAC_PSK_BRS_MASK) != 0) {
+ result = AIROHA_LOWEST_PSK_RATE_INDEX;
+ }
+ }
+
+ return result;
+}
+
+static int getAckDuration(struct piper_priv *piperp)
+{
+ bool is_1_Mbit = (getRateIndex(piperp) == AIROHA_LOWEST_PSK_RATE_INDEX);
+ int duration = 0;
+
+ if (is_1_Mbit) {
+ duration = CCK_DURATION(ACK_SIZE, 1);
+ } else {
+ duration = OFDM_DURATION(ACK_SIZE, 6);
+ }
+
+ duration += PRE_LEN(is_1_Mbit, piperp->use_short_preamble);
+
+ return duration;
+}
+
+
+/*
+ * This function is used to notify the AP about the current state of
+ * power save. One of the bits in the 802.11 header field is set to
+ * indicate the status of power save. This bit is actually set appropriately
+ * for all frames sent, we just send a null data frame just to make
+ * sure something is sent to the AP in a reasonable amount of time.
+ */
+/*
+ * Possible values for is_power_management_on argument
+ */
+#define POWERING_DOWN (true)
+#define POWERED_UP (false)
+void piper_sendNullDataFrame(struct piper_priv *piperp, bool is_power_management_on)
+{
+ struct sk_buff *skb = NULL;
+ _80211HeaderType *header;
+ struct ieee80211_tx_info *tx_info;
+
+ skb =
+ __dev_alloc_skb(sizeof(_80211HeaderType) +
+ piperp->hw->extra_tx_headroom, GFP_ATOMIC);
+ if (skb == NULL)
+ goto piper_sendNullDataFrame_Exit;
+
+ tx_info = (struct ieee80211_tx_info *) skb->cb;
+
+ skb_reserve(skb, piperp->hw->extra_tx_headroom);
+ header = (_80211HeaderType *) skb_put(skb, sizeof(_80211HeaderType));
+ memset(header, 0, sizeof(*header));
+ header->fc.type = TYPE_NULL_DATA;
+ header->fc.pwrMgt = is_power_management_on;
+ header->duration = getAckDuration(piperp);
+ memcpy(header->addr1, piperp->bssid, sizeof(header->addr1));
+ memcpy(header->addr2, piperp->pdata->macaddr, sizeof(header->addr2));
+ memcpy(header->addr3, piperp->bssid, sizeof(header->addr3));
+
+ tx_info->flags = IEEE80211_TX_CTL_ASSIGN_SEQ | IEEE80211_TX_CTL_FIRST_FRAGMENT;
+ tx_info->band = piperp->rf->getBand(piperp->channel);
+ tx_info->antenna_sel_tx = 1; /* actually this is ignored for now */
+ tx_info->control.rates[0].idx = 0;
+ tx_info->control.rates[0].count = 2; /* no retries. Don't tie us up waiting for an ACK */
+ tx_info->control.rates[0].flags = 0;
+ tx_info->control.rates[1].idx = -1;
+ tx_info->control.rts_cts_rate_idx = -1;
+ piperp->ps.frames_pending++;
+
+ if (piper_hw_tx_private(piperp->hw, skb, ps_free_frame) != 0) {
+ /* printk(KERN_ERR
+ "piper_hw_tx() failed unexpectedly when sending null data frame.\n"); */
+ ps_free_frame(piperp->hw, skb);
+ }
+
+piper_sendNullDataFrame_Exit:
+ return;
+}
+
+
+
+
+#define RESET_PIPER (1) /* must be set to 1 (0 case is only for debugging)*/
+#define PS_DONT_FORCE (false) /* set force to this value if we want to be safe */
+#define PS_FORCE_POWER_DOWN (true) /* set force to this value to shut down the H/W reguardless*/
+/*
+ * This routine shuts down Piper and the Airoha transceiver. First we check to
+ * make sure the driver and H/W are idle. Then we save the state of the H/W.
+ * Then we shut down the Airoha and place piper into reset.
+ */
+int piper_MacEnterSleepMode(struct piper_priv *piperp, bool force)
+{
+ /*
+ * Interrupts are already disabled when we get here.
+ */
+
+ if (piperp->ps.poweredDown)
+ return 0;
+
+ savedRegs[INDX_INTR_MASK] = piperp->ac->rd_reg(piperp, BB_IRQ_MASK);
+ piperp->ac->wr_reg(piperp, BB_IRQ_MASK, 0, op_write);
+
+ if (!force) {
+ if ( (piperp->ps.rxTaskletRunning)
+ || ((piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_BUSY))
+ || ( (piperp->ac->rd_reg(piperp, BB_GENERAL_CTL)
+ & BB_GENERAL_CTL_TX_FIFO_EMPTY) == 0)
+ || (piperp->tx_tasklet_running)
+ || ( (piperp->ac->rd_reg(piperp, BB_GENERAL_STAT)
+ & BB_GENERAL_STAT_RX_FIFO_EMPTY) == 0)
+ || (piperp->ac->rd_reg(piperp, BB_IRQ_STAT) & savedRegs[INDX_INTR_MASK])) {
+
+ piperp->ac->wr_reg(piperp, BB_IRQ_MASK, savedRegs[INDX_INTR_MASK], op_write);
+ return -1;
+ }
+ }
+
+ disable_irq(piperp->irq);
+
+ savedRegs[INDX_GEN_CONTROL] = piperp->ac->rd_reg(piperp, BB_GENERAL_CTL);
+ savedRegs[INDX_GEN_STATUS] = piperp->ac->rd_reg(piperp, BB_GENERAL_STAT);
+ savedRegs[INDX_RSSI_AES] = piperp->ac->rd_reg(piperp, BB_RSSI) & ~BB_RSSI_EAS_BUSY;
+ savedRegs[INDX_SPI_CTRL] = piperp->ac->rd_reg(piperp, BB_SPI_CTRL);
+ savedRegs[INDX_CONF1] = piperp->ac->rd_reg(piperp, BB_TRACK_CONTROL);
+ savedRegs[INDX_CONF2] = piperp->ac->rd_reg(piperp, BB_CONF_2);
+ savedRegs[INDX_OUT_CTRL] = piperp->ac->rd_reg(piperp, BB_OUTPUT_CONTROL);
+ savedRegs[INDX_MAC_CONTROL] = piperp->ac->rd_reg(piperp, MAC_CTL);
+
+ savedRegs[INDX_STAID_1] = piperp->ac->rd_reg(piperp, MAC_STA_ID0);
+ savedRegs[INDX_STAID_2] = piperp->ac->rd_reg(piperp, MAC_STA_ID1);
+ savedRegs[INDX_BSSID_1] = piperp->ac->rd_reg(piperp, MAC_BSS_ID0);
+ savedRegs[INDX_BSSID_2] = piperp->ac->rd_reg(piperp, MAC_BSS_ID1);
+ savedRegs[INDX_BRS_SSID] = piperp->ac->rd_reg(piperp, MAC_SSID_LEN);
+ savedRegs[INDX_BACKOFF] = piperp->ac->rd_reg(piperp, MAC_BACKOFF);
+ savedRegs[INDX_DTIM_LISTEN] = piperp->ac->rd_reg(piperp, MAC_DTIM_PERIOD);
+ savedRegs[INDX_BEACON_INT] = piperp->ac->rd_reg(piperp, MAC_CFP_ATIM);
+ savedRegs[INDX_MAC_CTL] = piperp->ac->rd_reg(piperp, MAC_CTL);
+ savedRegs[INDX_BEACON_MASK] = piperp->ac->rd_reg(piperp, MAC_BEACON_FILT);
+
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, ~BB_GENERAL_CTL_RX_EN, op_and); //disable receiving
+ piperp->ac->wr_reg(piperp, MAC_CTL, 0, op_write);
+ piperp->ac->wr_reg(piperp, BB_IRQ_MASK, 0, op_write);
+
+#if RESET_PIPER
+ // Power down the airoha transceiver
+ piperp->rf->power_on(piperp->hw, false);
+ udelay(10);
+ // hold the transceiver in reset mode
+ if (piperp->pdata->reset)
+ piperp->pdata->reset(piperp, 1);
+#endif
+ stats.jiffiesOn += jiffies - stats.cycleStart;
+ stats.cycleStart = jiffies;
+ piperp->ps.poweredDown = true;
+
+ return 0;
+}
+
+
+#define PS_NO_SPIKE_SUPPRESSION (false) /* want_spike_suppression = don't want spike suppression*/
+#define PS_WANT_SPIKE_SUPPRESSION (true) /* want_spike_suppression = do spike suppression*/
+/*
+ * Turn the H/W back on after it was shutdown with piper_MacEnterSleepMode.
+ *
+ * 1. Power up the hardware.
+ * 2. Perform the spike suppression routine if desired. The spike suppression
+ * routine resyncs the clocks in Piper in order to prevent us from generating
+ * noise spikes at 1 and 2 MHz. Unfortunately it takes an indeterminate amount
+ * of time, so we normally don't do it and just make sure we do not send at
+ * at those data rates while duty cycling.
+ * 3. Set the channel. The Airoha was shut off so we have to set the channel
+ * again.
+ * 4. Restore the state of piper registers.
+ * 5. Zero out and reset the transmitter FIFO. Mike Schaffner claims this should
+ * not be necessary, but we seem to run into trouble when we don't do it.
+ * 6. Restore the interrupts.
+ */
+void piper_MacEnterActiveMode(struct piper_priv *piperp, bool want_spike_suppression)
+{
+ int i;
+// #define WANT_DEBUG
+#ifdef WANT_DEBUG
+ static unsigned int run = 0;
+#endif
+
+#if RESET_PIPER
+
+ if (piperp->pdata->reset) {
+#ifdef WANT_DEBUG
+ if (piperp->ac->rd_reg(piperp, BB_GENERAL_CTL) & BB_GENERAL_CTL_TX_FIFO_FULL) {
+ printk(KERN_ERR "**** While in reset, run = %d\n", run);
+ digiWifiDumpRegisters(piperp, MAIN_REGS);
+ while(1);
+ }
+ if (piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_BUSY) {
+ printk(KERN_ERR "**** While in reset AES busy set, run = %d\n", run);
+ digiWifiDumpRegisters(piperp, MAIN_REGS);
+ while(1);
+ }
+#endif
+ piperp->pdata->reset(piperp, 0);
+ udelay(10);
+
+#ifdef WANT_DEBUG
+ if (piperp->ac->rd_reg(piperp, BB_GENERAL_CTL) & BB_GENERAL_CTL_TX_FIFO_FULL) {
+ printk(KERN_ERR "**** After reset, run = %d\n", run);
+ digiWifiDumpRegisters(piperp, MAIN_REGS);
+ while(1);
+ }
+ if (piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_BUSY) {
+ printk(KERN_ERR "**** After reset AES busy set, run = %d\n", run);
+ digiWifiDumpRegisters(piperp, MAIN_REGS);
+ while(1);
+ }
+ run++;
+#endif
+ piperp->rf->power_on(piperp->hw, true);
+ mdelay(1);
+ piper_spike_suppression(piperp, want_spike_suppression);
+ }
+#endif
+
+ piperp->ac->wr_reg(piperp, BB_IRQ_STAT, 0xff, op_write);
+
+
+#if RESET_PIPER
+ piperp->rf->set_chan_no_rx(piperp->hw, piperp->channel);
+#endif
+
+ // store the registers back
+
+ piperp->ac->wr_reg(piperp, BB_GENERAL_STAT, 0x30000000, op_write);
+ piperp->ac->wr_reg(piperp, BB_RSSI, savedRegs[INDX_RSSI_AES] & ~BB_RSSI_EAS_BUSY, op_write);
+
+// piperp->ac->wr_reg(piperp, BB_IRQ_MASK, savedRegs[INDX_INTR_MASK], op_write);
+ piperp->ac->wr_reg(piperp, BB_SPI_CTRL, savedRegs[INDX_SPI_CTRL], op_write);
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, savedRegs[INDX_CONF1], op_write);
+ piperp->ac->wr_reg(piperp, BB_CONF_2, savedRegs[INDX_CONF2], op_write);
+ piperp->ac->wr_reg(piperp, BB_AES_CTL, 0, op_write);
+ piperp->ac->wr_reg(piperp, BB_OUTPUT_CONTROL, savedRegs[INDX_OUT_CTRL], op_write);
+ piperp->ac->wr_reg(piperp, MAC_CTL, savedRegs[INDX_MAC_CONTROL], op_write);
+
+ piperp->ac->wr_reg(piperp, MAC_STA_ID0, savedRegs[INDX_STAID_1], op_write);
+ piperp->ac->wr_reg(piperp, MAC_STA_ID1, savedRegs[INDX_STAID_2], op_write);
+ piperp->ac->wr_reg(piperp, MAC_BSS_ID0, savedRegs[INDX_BSSID_1], op_write);
+ piperp->ac->wr_reg(piperp, MAC_BSS_ID1, savedRegs[INDX_BSSID_2], op_write);
+ piperp->ac->wr_reg(piperp, MAC_SSID_LEN, savedRegs[INDX_BRS_SSID], op_write);
+ piperp->ac->wr_reg(piperp, MAC_BACKOFF, savedRegs[INDX_BACKOFF], op_write);
+ piperp->ac->wr_reg(piperp, MAC_DTIM_PERIOD, savedRegs[INDX_DTIM_LISTEN], op_write);
+ piperp->ac->wr_reg(piperp, MAC_CFP_ATIM, savedRegs[INDX_BEACON_INT], op_write);
+ piperp->ac->wr_reg(piperp, MAC_CTL, savedRegs[INDX_MAC_CTL], op_write);
+ piperp->ac->wr_reg(piperp, MAC_BEACON_FILT, savedRegs[INDX_BEACON_MASK], op_write);
+
+//****
+ // set bit-11 in the general control register to a 1 to start the processors
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL,
+ BB_GENERAL_CTL_MAC_ASSIST_ENABLE, op_or);
+
+ // set the TX-hold bit
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, 0x37720080, op_write);
+
+ // clear the TX-FIFO memory
+ for (i = 0; i < 448; i++)
+ piperp->ac->wr_reg(piperp, BB_DATA_FIFO, 0, op_write);
+
+ // clear RX-FIFO memory
+ for (i = 0; i < 512; i++)
+ piperp->ac->rd_reg(piperp, BB_DATA_FIFO);
+
+ // reset the TX-FIFO and RX-FIFO
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, 0x377200E0, op_write);
+
+
+ // release the TX-hold and reset
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, 0x37720000, op_write);
+
+
+//***
+ /*
+ * Reset the interrupt mask. We could have been receiving a frame when we
+ * powered down. This could cause us to store the wrong mask, so we want
+ * to make sure we enabe the RX interrupts. However, we should not have the
+ * TX interrupts enabled when we come out of power save mode.
+ */
+ udelay(50);
+ piperp->ac->wr_reg(piperp, BB_IRQ_STAT, 0xff, op_write);
+ piperp->clear_irq_mask_bit(piperp, 0xffffffff);
+ piperp->set_irq_mask_bit(piperp, BB_IRQ_MASK_RX_OVERRUN | BB_IRQ_MASK_RX_FIFO);
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL,
+ (savedRegs[INDX_GEN_CONTROL] | 0x37000000 |
+ BB_GENERAL_CTL_RX_EN), op_write);
+
+ stats.jiffiesOff += jiffies - stats.cycleStart;
+ stats.cycleStart = jiffies;
+
+ /* TODO, this is a temporary workaround and should be better analyzed in future.
+ * The problem is that the general power save code is not synchronized with the
+ * dynamic PW and this is causing that, the following line, unbalances the
+ * piper wireless irq */
+ if (piperp->ps.poweredDown != false)
+ enable_irq(piperp->irq);
+
+ piperp->ps.poweredDown = false;
+}
+
+
+
+
+/*
+ * So what crazy thing are we doing here? Well, Piper has a bug where it
+ * can send noise spikes at 1 Mbps and 2 Mbps if it is powered up without
+ * running a special spike suppression routine. The spike suppression code
+ * takes an average of 30 ms, and I have timed it taking as long as 300 ms.
+ * This is not something you want to use for duty cycling. The solution is
+ * to avoid sending at those two rates. After the transmit routine determines
+ * the rate mac80211 specified, it will call us and we will decide whether
+ * we like that rate. If it is one of our bad rates, then we will bump it
+ * up to a good rate.
+ */
+struct ieee80211_rate *piper_ps_check_rate(struct piper_priv *piperp,
+ struct ieee80211_rate *rate)
+{
+#define BAD_RATE_1MBPS (10)
+#define BAD_RATE_2MBPS (20)
+ if ((piperp->ps.mode == PS_MODE_LOW_POWER) && (rate != NULL)) {
+ if ((rate->bitrate == BAD_RATE_1MBPS)
+ || (rate->bitrate == BAD_RATE_2MBPS)) {
+ rate =
+ (struct ieee80211_rate *) piperp->rf->
+ getRate(AIROHA_55_MBPS_RATE_INDEX);
+ }
+ }
+
+ return rate;
+}
+
+EXPORT_SYMBOL_GPL(piper_ps_check_rate);
+
+
+
+/*
+ * This routine restarts the transmitter after powering back
+ * up, or failing to power down.
+ *
+ * 1) Clear the power management bit so that frames will be
+ * sent indicating that we are poweredup.
+ * 2) Set the flag to allow transmits again.
+ * 3) If we stopped the mac80211 transmitter queue, then start
+ * it back up again.
+ * 4) Notify the AP that we are awake by sending a null-data frame
+ * with the power management bit clear.
+ */
+static void ps_resume_transmits(struct piper_priv *piperp)
+{
+ piperp->ps.power_management = POWERED_UP;
+ piperp->ps.allowTransmits = true;
+ piperp->ps.stopped_tx_queues = false;
+ piper_sendNullDataFrame(piperp, POWERED_UP);
+ ieee80211_wake_queues(piperp->hw);
+}
+
+
+
+
+
+
+/*
+ * This routine sets an event timer. The ps_state_machine routine
+ * will be executed when the event timer expires.
+ */
+static void ps_set_timer_event(struct piper_priv *piperp,
+ enum piper_ps_event next_event,
+ unsigned int timeout_ms)
+{
+ unsigned int delay_in_jiffies = MILLS_TO_JIFFIES(timeout_ms);
+
+ if (delay_in_jiffies == 0)
+ delay_in_jiffies++;
+
+ del_timer_sync(&piperp->ps.timer);
+ piperp->ps.this_event = next_event;
+ piperp->ps.timer.expires = delay_in_jiffies + jiffies;
+ add_timer(&piperp->ps.timer);
+}
+
+
+/*
+ * This routine cancels an event timer set by ps_set_timer_event.
+ */
+static void ps_cancel_timer_event(struct piper_priv *piperp)
+{
+ del_timer_sync(&piperp->ps.timer);
+}
+
+
+#define WANT_STATE_MACHINE_DEBUG (0)
+#if WANT_STATE_MACHINE_DEBUG
+
+struct event_record {
+ enum piper_ps_event event;
+ enum piper_ps_state state;
+};
+
+#define MAX_EVENTS_RECORDED (15)
+static unsigned int debug_events_index = 0;
+static struct event_record debug_events[MAX_EVENTS_RECORDED];
+
+void debug_track_event(enum piper_ps_event event, enum piper_ps_state state)
+{
+ debug_events[debug_events_index].event = event;
+ debug_events[debug_events_index].state = state;
+ if (debug_events_index == MAX_EVENTS_RECORDED) {
+ unsigned int i;
+
+ printk(KERN_ERR);
+ for (i = 0; i < MAX_EVENTS_RECORDED; i++) {
+ printk("(%d,%d)", debug_events[i].event,
+ debug_events[i].state);
+ }
+ printk("\n");
+ debug_events_index = 0;
+ } else {
+ debug_events_index++;
+ }
+}
+
+
+#else
+ #define debug_track_event(event, state)
+#endif
+
+/*
+ * This is the entry point into the duty cycle state machine. It may be called
+ * by:
+ *
+ * 1) piper_ps_handle_beacon when a beacon frame is received.
+ * 2) the receiver task when we receive the ACK for the last pending frame
+ * when we are waiting to power down.
+ * 3) by ps_timer for many timer events.
+ */
+static void ps_state_machine(struct piper_priv *piperp, enum piper_ps_event event)
+{
+ unsigned long flags;
+
+ debug_track_event(event, piperp->ps.state);
+
+ spin_lock_irqsave(&piperp->ps.lock, flags);
+
+
+ switch (event) {
+ case PS_EVENT_BEACON_RECEIVED:
+ /*
+ * We just received a beacon. This is really the driving event in this state
+ * machine. Everything else is synchronized from it.
+ *
+ * We know we are powered up because we just received the beacon. The normal
+ * control path is to set a timer which will expire when we need to start
+ * preparations for powering down again.
+ */
+ ps_cancel_timer_event(piperp); /* cancel missed beacon timer*/
+ stats.receivedBeacons++;
+ if ( (!piperp->areWeAssociated)
+ || (piperp->ps.beacon_int < PS_MINIMUM_BEACON_INT)
+ || (piperp->ps.scan_timer != 0)) {
+ /*
+ * Don't duty cyle while we are trying to associate.
+ */
+ piperp->ps.state = PS_STATE_WAIT_FOR_BEACON;
+ if (piperp->ps.scan_timer) {
+ piperp->ps.scan_timer--;
+ }
+ break;
+ }
+ if (piperp->ps.state == PS_STATE_WAIT_FOR_BEACON) {
+ int timeout;
+ /*
+ * Calculate amount of time to sleep.
+ */
+ piperp->ps.sleep_time = (piperp->ps.beacon_int * (100 - piperp->power_duty)) / 100;
+
+ /*
+ * Now figure out how long we have before it's time to go to sleep. We
+ * have to wake up at least PS_WAKE_BEFORE_BEACON_MS before we expect to
+ * receive the next beacon, and we need to start powering down at least
+ * PS_TRANSMITTER_SHUTDOWN_MS ahead of time.
+ */
+ timeout = piperp->ps.beacon_int - (piperp->ps.sleep_time + PS_TRANSMITTER_SHUTDOWN_MS + PS_WAKE_BEFORE_BEACON_MS);
+ if ((timeout > 0) && (piperp->ps.sleep_time > PS_MINIMUM_SLEEP_TIME)) {
+ /*
+ * If there is enough time left that it makes sense to duty
+ * cycle, then program the timer and advance to the next state.
+ */
+ piperp->ps.state = PS_STATE_WAIT_FOR_STOP_TRANSMIT_EVENT;
+ ps_set_timer_event(piperp, PS_EVENT_STOP_TRANSMIT_TIMER_EXPIRED, timeout);
+
+ break;
+ }
+ } else {
+ if ( (piperp->ps.state == PS_STATE_WAIT_FOR_TRANSMITTER_DONE)
+ || (piperp->ps.state == PS_STATE_WAIT_FOR_TRANSMITTER_DONE_EVENT)) {
+ ps_resume_transmits(piperp);
+ }
+ digi_dbg("*** Beacon event in state %d.\n", piperp->ps.state);
+ }
+ /*
+ * We will come here if we were in the wrong state when we received the
+ * beacon, or if the duty cycle is too short.
+ */
+ piperp->ps.state = PS_STATE_WAIT_FOR_BEACON;
+ ps_set_timer_event(piperp, PS_EVENT_MISSED_BEACON,
+ piperp->ps.beacon_int + PS_BEACON_TIMEOUT_MS);
+ break;
+ case PS_EVENT_STOP_TRANSMIT_TIMER_EXPIRED:
+ /*
+ * This event is hit when it's time to start powering down. Unfortunately, this is
+ * not a simple thing to do. The first things to do are:
+ *
+ * 1. Set the power save on flag. This will cause any transmit frame to be
+ * sent with the power management bit set.
+ * 2. Stop the mac80211 layer from sending us anymore frames.
+ * 3. Signal the AP that we are powering down by sending a null-data frame with the
+ * power management bit set.
+ */
+ if (piperp->ps.state == PS_STATE_WAIT_FOR_STOP_TRANSMIT_EVENT) {
+ if (piperp->ps.scan_timer) {
+ /*
+ * Don't shut down if we are scanning.
+ */
+ piperp->ps.state = PS_STATE_WAIT_FOR_BEACON;
+ break;
+ }
+ piperp->ps.power_management = POWERING_DOWN;
+ piperp->ps.allowTransmits = false;
+ ieee80211_stop_queues(piperp->hw);
+ piperp->ps.stopped_tx_queues = true;
+ piper_sendNullDataFrame(piperp, POWERING_DOWN);
+ piperp->ps.state = PS_STATE_WAIT_FOR_TRANSMITTER_DONE;
+ ps_set_timer_event(piperp, PS_EVENT_TRANSMITTER_DONE_TIMER_TICK,
+ PS_TRANSMIT_TIMER_TICK_MS);
+ } else {
+ /*
+ * This should never happen (famous last words)
+ */
+ digi_dbg("** stop tx event, state = %d.\n", piperp->ps.state);
+ piperp->ps.state = PS_STATE_WAIT_FOR_BEACON;
+ ps_set_timer_event(piperp, PS_EVENT_MISSED_BEACON,
+ piperp->ps.beacon_int + PS_BEACON_TIMEOUT_MS);
+ }
+ break;
+ case PS_EVENT_TRANSMITTER_DONE:
+ /*
+ * This event is triggered when the receive task finishes processing the ACK
+ * from the null-data frame sent by the PS_EVENT_STOP_TRANSMIT_TIMER_EXPIRED event.
+ * We try to power down now.
+ */
+ if (piperp->ps.scan_timer == 0) {
+ if ( (piperp->ps.state == PS_STATE_WAIT_FOR_TRANSMITTER_DONE)
+ || (piperp->ps.state == PS_STATE_WAIT_FOR_TRANSMITTER_DONE_EVENT)) {
+ ps_cancel_timer_event(piperp); /* cancel transmitter done timeout timer*/
+ if (piper_MacEnterSleepMode(piperp, PS_DONT_FORCE) == 0) {
+ piperp->ps.state = PS_STATE_WAIT_FOR_WAKEUP_ALARM;
+ /*
+ * Note that the value PS_EVENT_TRANSMITTER_DONE_TIMER_TICK is
+ * updated as necessary by the PS_EVENT_TRANSMITTER_DONE_TIMER_TICK
+ * event to take into account the amount of time it took for the
+ * transmitter to finish sending the last frame.
+ */
+ ps_set_timer_event(piperp, PS_EVENT_WAKEUP, piperp->ps.sleep_time);
+ break;
+ }
+ } else {
+#ifdef WANT_DEBUG
+ printk(KERN_ERR "couldn't sleep, rxt=%d, AES busy = %d, txfifo=%d, txt=%d, rxfifo=%d\n",
+ (piperp->ps.rxTaskletRunning),((piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_BUSY) != 0),
+ ( (piperp->ac->rd_reg(piperp, BB_GENERAL_CTL)& BB_GENERAL_CTL_TX_FIFO_EMPTY) == 0),
+ (piperp->tx_tasklet_running),
+ ( (piperp->ac->rd_reg(piperp, BB_GENERAL_STAT)
+ & BB_GENERAL_STAT_RX_FIFO_EMPTY) == 0));
+#endif
+ digi_dbg("** PS_EVENT_TRANSMITTER_DONE event, but state == %d.\n", piperp->ps.state);
+ }
+ }
+ /*
+ * If we fall through to here, then either we are in the wrong state, or we were
+ * not able to power down the H/W.
+ */
+ ps_resume_transmits(piperp);
+ piperp->ps.state = PS_STATE_WAIT_FOR_BEACON;
+ ps_set_timer_event(piperp, PS_EVENT_MISSED_BEACON,
+ piperp->ps.beacon_int + PS_BEACON_TIMEOUT_MS);
+ break;
+ case PS_EVENT_TRANSMITTER_DONE_TIMER_TICK:
+ /*
+ * This event is triggered periodically while we are waiting for the
+ * transmitter to finish sending that last packet. We decrement
+ * piperp->ps.sleep_time (which is used by the PS_EVENT_TRANSMITTER_DONE
+ * event). If piperp->ps.sleep_time is still larger than our minimum
+ * required sleep time, then we just restart the timer.
+ */
+ if (piperp->ps.state == PS_STATE_WAIT_FOR_TRANSMITTER_DONE) {
+ piperp->ps.sleep_time -= PS_TRANSMIT_TIMER_TICK_MS;
+ if (piperp->ps.sleep_time >= PS_MINIMUM_SLEEP_TIME) {
+ piperp->ps.state = PS_STATE_WAIT_FOR_TRANSMITTER_DONE;
+ ps_set_timer_event(piperp, PS_EVENT_TRANSMITTER_DONE_TIMER_TICK,
+ PS_TRANSMIT_TIMER_TICK_MS);
+ } else {
+ /*
+ * Transmitter did not shut down in time. Resume normal operations
+ * and stay awake until the next beacon.
+ */
+ ps_resume_transmits(piperp);
+ piperp->ps.state = PS_STATE_WAIT_FOR_BEACON;
+ ps_set_timer_event(piperp, PS_EVENT_MISSED_BEACON,
+ piperp->ps.sleep_time + PS_WAKE_BEFORE_BEACON_MS
+ + PS_BEACON_TIMEOUT_MS);
+ }
+ } else if (piperp->ps.state == PS_STATE_WAIT_FOR_TRANSMITTER_DONE_EVENT) {
+ piperp->ps.sleep_time -= PS_TRANSMIT_TIMER_TICK_MS;
+ /*
+ * The piper_ps_rx_task_exiting routine sets this state just before it
+ * releases the lock on ps.state and calls this event. If we ever
+ * come here, then the timer tick occurred just between the time
+ * piper_ps_rx_task_exiting released the lock and ps_state_machine
+ * reset it. Since the tx done event is in progress, we should ignore
+ * this tick.
+ */
+ break;
+ } else {
+ digi_dbg("** done tick in state %d.\n", piperp->ps.state);
+ }
+ break;
+ case PS_EVENT_WAKEUP:
+ /*
+ * This event is called when we have powered down and it is time
+ * to power back up again.
+ *
+ * 1) Power up the H/W.
+ * 2) Resume normal operations
+ * 3) Update our state.
+ * 4) Set a timeout for receiving the next beacom frame.
+ */
+ if (piperp->ps.state == PS_STATE_WAIT_FOR_WAKEUP_ALARM) {
+ piper_MacEnterActiveMode(piperp, PS_NO_SPIKE_SUPPRESSION);
+ ps_resume_transmits(piperp);
+ piperp->ps.state = PS_STATE_WAIT_FOR_BEACON;
+ ps_set_timer_event(piperp, PS_EVENT_MISSED_BEACON,
+ PS_BEACON_TIMEOUT_MS + PS_WAKE_BEFORE_BEACON_MS);
+ } else {
+ digi_dbg("** wake event in state %d.\n", piperp->ps.state);
+ }
+ break;
+ case PS_EVENT_MISSED_BEACON:
+ /*
+ * This event is called when we miss a beacon. For now just update
+ * our statistics.
+ */
+ piperp->ps.state = PS_STATE_WAIT_FOR_BEACON;
+ if ((piperp->areWeAssociated) && (piperp->ps.scan_timer == 0)) {
+ stats.missedBeacons++;
+ ps_set_timer_event(piperp, PS_EVENT_MISSED_BEACON, piperp->ps.beacon_int);
+ }
+ break;
+ default:
+ digi_dbg("**** ps_state_machine received unknown event %d.\n", event);
+ break;
+ }
+ spin_unlock_irqrestore(&piperp->ps.lock, flags);
+
+}
+
+/*
+ * This routine is called by the receiver task when it exits. We use it to generate
+ * the PS_EVENT_TRANSMITTER_DONE event. The event signifies that the transmitter has
+ * sent our null-data frame and is idle. We determine this by checking frames_pending,
+ * while will be nonzero if a null-data frame is waiting to be sent, and the machine's
+ * state.
+ */
+void piper_ps_rx_task_exiting(struct piper_priv *piperp)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&piperp->ps.lock, flags);
+
+ if ( (piperp->ps.frames_pending == 0)
+ && (piperp->ps.state == PS_STATE_WAIT_FOR_TRANSMITTER_DONE)) {
+ /*
+ * We have a race condition between the transmitter done tick and
+ * this routine. So this routine changes the state to
+ * PS_STATE_WAIT_FOR_TRANSMITTER_DONE_EVENT before it releases the
+ * lock so that we don't get confused.
+ */
+ piperp->ps.state = PS_STATE_WAIT_FOR_TRANSMITTER_DONE_EVENT;
+ spin_unlock_irqrestore(&piperp->ps.lock, flags);
+ ps_state_machine(piperp, PS_EVENT_TRANSMITTER_DONE);
+ } else {
+ spin_unlock_irqrestore(&piperp->ps.lock, flags);
+ }
+}
+
+/*
+ * Called when the event timer expires. Call the state machine to handle
+ * the event.
+ */
+static void ps_timer(unsigned long context)
+{
+ struct piper_priv *piperp = (struct piper_priv *) context;
+
+ ps_state_machine(piperp, piperp->ps.this_event);
+}
+
+
+
+/*
+ * This routine is called when we receive a beacon. We extract the beacon interval
+ * in case it has changed and then call the state machine.
+ */
+static void piper_ps_handle_beacon(struct piper_priv *piperp, struct sk_buff *skb)
+{
+#define BEACON_INT_LSB (8)
+#define BEACON_INT_MSB (9)
+ u32 beacon_int;
+ _80211HeaderType *header = (_80211HeaderType *) skb->data;
+ bool fromOurAp = piperp->areWeAssociated
+ && (memcmp(piperp->bssid, header->addr3, sizeof (header->addr3)) == 0);
+
+ /*
+ * mac80211 does not inform us when the beacon interval changes, so we have
+ * to read this information from the beacon ourselves.
+ */
+
+ if (fromOurAp) {
+ beacon_int = skb->data[sizeof(_80211HeaderType) + BEACON_INT_LSB];
+ beacon_int |= (skb->data[sizeof(_80211HeaderType) + BEACON_INT_MSB] << 8);
+ piperp->ps.beacon_int = beacon_int;
+
+ if (piperp->ps.mode == PS_MODE_LOW_POWER) {
+ ps_state_machine(piperp, PS_EVENT_BEACON_RECEIVED);
+ }
+ }
+}
+
+
+
+/*
+ * This routine is called when mac80211 starts doing things that might indicate it
+ * is attempting to scan or reassociate. Things like changing the channel or
+ * disassociating. When we receive an event like that, we stop duty cycling for
+ * a while since it may interfere with attempts to reassociate with an access point.
+ */
+void piper_ps_scan_event(struct piper_priv *piperp)
+{
+ (void) piperp;
+#if 0
+ /*
+ * It appears that pausing duty cycling during association events may actually
+ * worsen performance I suspect that either the AP or mac80211 is measuring
+ * our throughput and adjusting the load accordingly, and that momentary changes
+ * in performance caused by pausing duty cyling interfere with this.
+ *
+ * TODO: Consider removing this code. I left it in for now in case we decide
+ * to try it again, but if we're not going to use it, it just makes the
+ * driver more confusing and should be removed.
+ */
+ if (piperp->ps.beacon_int != 0) {
+ piperp->ps.scan_timer = PS_SCAN_DELAY / piperp->ps.beacon_int;
+ } else {
+ piperp->ps.scan_timer = PS_SCAN_DELAY / 100;
+ }
+#endif
+}
+
+
+
+/*
+ * This routine is called so we can process incoming frames. We do the
+ * handshaking to receive buffered frames in PS mode here.
+ */
+void piper_ps_process_receive_frame(struct piper_priv *piperp, struct sk_buff *skb)
+{
+ _80211HeaderType *header = (_80211HeaderType *) skb->data;
+
+ if (header->fc.type == TYPE_BEACON) {
+ piper_ps_handle_beacon(piperp, skb);
+ } else if ( (header->fc.type == TYPE_ASSOC_RESP)
+ || (header->fc.type == TYPE_REASSOC_RESP)
+ || (header->fc.type == TYPE_PROBE_RESP)
+ || (header->fc.type == TYPE_DISASSOC)
+ || (header->fc.type == TYPE_DEAUTH)
+ || (header->fc.type == TYPE_ACTION)) {
+ piper_ps_scan_event(piperp);
+ }
+}
+
+EXPORT_SYMBOL_GPL(piper_ps_process_receive_frame);
+
+
+
+/*
+ * This function turns power save mode on or off.
+ */
+void piper_ps_set(struct piper_priv *piperp, bool powerSaveOn)
+{
+#define MAX_SHUTDOWN_TIMEOUT (100)
+ unsigned long flags;
+
+ spin_lock_irqsave(&piperp->ps.lock, flags);
+
+ piper_ps_scan_event(piperp);
+ if (powerSaveOn) {
+ if (piperp->ps.beacon_int >= PS_MINIMUM_BEACON_INT) {
+ if (piperp->ps.mode != PS_MODE_LOW_POWER) {
+ piperp->ps.aid = 0;
+ piperp->ps.mode = PS_MODE_LOW_POWER;
+ piperp->ps.state= PS_STATE_WAIT_FOR_BEACON;
+ piperp->ps.power_management = POWERED_UP;
+ piperp->ps.poweredDown = false;
+ piperp->ps.allowTransmits = true;
+ piperp->ps.stopped_tx_queues = false;
+ stats.receivedBeacons = 0;
+ stats.missedBeacons = 0;
+ stats.modeStart = jiffies;
+ stats.cycleStart = jiffies;
+ stats.jiffiesOff = 0;
+ stats.jiffiesOn = 0;
+ piper_sendNullDataFrame(piperp, POWERED_UP);
+ /*
+ * Will start it the next time we receive a beacon.
+ */
+ }
+ } else {
+ printk(KERN_ERR
+ "\nUnable to set power save mode because the beacon \n"
+ "interval set on this access point less than 100ms.\n");
+ }
+ } else {
+ ps_cancel_timer_event(piperp);
+ if (piperp->ps.mode == PS_MODE_LOW_POWER) {
+ piperp->ps.mode = PS_MODE_FULL_POWER; /* stop duty cycle timer */
+ if (piperp->ps.poweredDown) {
+ /*
+ * If we were powered down, then power up and do the spike suppression.
+ */
+ piper_MacEnterActiveMode(piperp, PS_WANT_SPIKE_SUPPRESSION);
+ } else {
+ unsigned int timeout = 50;
+ int result;
+ /*
+ * If we branch here, then we were already powered up. You would think
+ * that we would be all set, but it's not that easy. Piper has a bug in
+ * it where we have to run a special spike suppression routine when we
+ * power it up. However, this routine takes an average of 30 ms to run,
+ * and I've see it take as long as 300 ms. This is not acceptable when
+ * we are duty cycling every 100 ms. To get around this, we do NOT do
+ * the spike suppression while duty cycling. Instead, we simply avoid
+ * transmitting at those rates which would cause spikes. Now, however,
+ * we are ending duty cycling and returning to normal operations so we
+ * have to do the spike suppression. Since we are powered up, the first
+ * thing to do is to power down.
+ */
+ if (piperp->ps.state != PS_STATE_WAIT_FOR_STOP_TRANSMIT_EVENT) {
+ /*
+ * If we come here, then we did not happen to be trying to power down
+ * just as we got the command from mac80211, so we have to start
+ * the procedure. This is the normal case.
+ *
+ * 1. Set the power save on flag. This will cause frames to be
+ * transmitted with the power management bit on. The reason
+ * for doing that is to tell the AP to stop sending us frames.
+ * 2. Stop the mac80211 layer from sending us more frames by stopping
+ * the transmit queues.
+ * 3. Send a null-data frame to the AP with the power management bit
+ * set. This should cause it to stop sending us frames.
+ */
+ piperp->ps.power_management = POWERING_DOWN;
+ piperp->ps.allowTransmits = false;
+ ieee80211_stop_queues(piperp->hw);
+ piperp->ps.stopped_tx_queues = true;
+ piper_sendNullDataFrame(piperp, POWERING_DOWN);
+ }
+ /*
+ * Now wait for that last frame to go and and then shut down.
+ */
+ result = -1;
+ for (timeout = 0; (timeout < MAX_SHUTDOWN_TIMEOUT) && (result != 0); timeout++) {
+ spin_unlock_irqrestore(&piperp->ps.lock, flags);
+ mdelay(10);
+ spin_lock_irqsave(&piperp->ps.lock, flags);
+ result = piper_MacEnterSleepMode(piperp, PS_DONT_FORCE);
+ }
+ if (result != 0) {
+ /*
+ * This is bad. For some reason we are not able to power down. We
+ * will try to force it now, but this may end up putting the driver
+ * or H/W into a bad state. However, we can't sit in the loop above
+ * forever either.
+ */
+#ifdef WANT_DEBUG
+ printk(KERN_ERR "Forcing Piper to power down\n");
+ printk(KERN_ERR "BB_RSSI_EAS_BUSY = %d\n", piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_BUSY);
+ printk(KERN_ERR "BB_GENERAL_CTL_TX_FIFO_EMPTY = %d\n",
+ piperp->ac->rd_reg(piperp, BB_GENERAL_CTL) & BB_GENERAL_CTL_TX_FIFO_EMPTY);
+ printk(KERN_ERR "BB_GENERAL_STAT_RX_FIFO_EMPTY = %d\n",
+ piperp->ac->rd_reg(piperp, BB_GENERAL_STAT) & BB_GENERAL_STAT_RX_FIFO_EMPTY);
+ digiWifiDumpRegisters(piperp, MAIN_REGS | MAC_REGS);
+#endif
+ piper_MacEnterSleepMode(piperp, PS_FORCE_POWER_DOWN);
+ }
+ /*
+ * Wait a moment and then power the H/W back up and execute the spike suppression
+ * routine.
+ */
+ spin_unlock_irqrestore(&piperp->ps.lock, flags);
+ mdelay(30);
+ spin_lock_irqsave(&piperp->ps.lock, flags);
+ piper_MacEnterActiveMode(piperp, PS_WANT_SPIKE_SUPPRESSION);
+ ps_resume_transmits(piperp);
+ }
+ stats.jiffiesOn += jiffies - stats.cycleStart;
+#define WANT_STATS (0)
+#if WANT_STATS
+ if ((piperp->ps.beacon_int != 0)
+ && ((jiffies - stats.modeStart) != 0)) {
+ printk(KERN_ERR
+ "jiffiesOff = %u, jiffiesOn = %u, total time = %lu\n",
+ stats.jiffiesOff, stats.jiffiesOn,
+ (jiffies - stats.modeStart));
+ printk(KERN_ERR
+ "Powered down %ld percent of the time.\n",
+ (stats.jiffiesOff * 100) / (jiffies - stats.modeStart));
+ printk(KERN_ERR
+ "Received %u of %lu beacons while in powersave mode.\n",
+ stats.receivedBeacons,
+ (jiffies -
+ stats.modeStart) /
+ MILLS_TO_JIFFIES(piperp->ps.beacon_int));
+ printk(KERN_ERR "received %d beacons, missed %d\n",
+ stats.receivedBeacons, stats.missedBeacons);
+ printk(KERN_ERR "allowTransmits = %d, stopped_tx_queues = %d, q_count = %d\n",
+ piperp->ps.allowTransmits, piperp->ps.stopped_tx_queues,
+ piperp->tx_queue_count);
+ if ((stats.receivedBeacons + stats.missedBeacons) != 0)
+ printk(KERN_ERR "%d%% beacons were missed\n",
+ (100 * stats.missedBeacons) / (stats.receivedBeacons + stats.missedBeacons));
+ }
+#endif
+ }
+ piperp->ps.aid = 0;
+ piperp->ps.state= PS_STATE_WAIT_FOR_BEACON;
+ piperp->ps.power_management = POWERED_UP;
+ piperp->ps.poweredDown = false;
+ piperp->ps.allowTransmits = true;
+ piperp->ps.stopped_tx_queues = false;
+ ps_resume_transmits(piperp);
+ piper_sendNullDataFrame(piperp, POWERED_UP);
+ }
+
+ spin_unlock_irqrestore(&piperp->ps.lock, flags);
+}
+
+EXPORT_SYMBOL_GPL(piper_ps_set);
+
+
+
+/*
+ * Called when driver is loaded. Initialize our context.
+ */
+void piper_ps_init(struct piper_priv *piperp)
+{
+ memset(&piperp->ps, 0, sizeof(piperp->ps));
+ piperp->ps.beacon_int = 100;
+ piperp->ps.aid = 0;
+ init_timer(&piperp->ps.timer);
+ piperp->ps.timer.function = ps_timer;
+ piperp->ps.timer.data = (unsigned long) piperp;
+ piperp->ps.mode = PS_MODE_FULL_POWER;
+ piperp->ps.state= PS_STATE_WAIT_FOR_BEACON;
+ spin_lock_init(&piperp->ps.lock);
+ piperp->ps.power_management = POWERED_UP;
+ piperp->ps.poweredDown = false;
+ piperp->ps.rxTaskletRunning;
+ piperp->ps.allowTransmits = true;
+ piperp->ps.stopped_tx_queues = false;
+ piperp->ps.frames_pending = 0;
+}
+
+EXPORT_SYMBOL_GPL(piper_ps_init);
+
+
+/*
+ * Called when driver is unloaded. Make sure the PS
+ * timer is shut down.
+ */
+void piper_ps_deinit(struct piper_priv *piperp)
+{
+ piper_ps_set(piperp, true);
+ piperp->ps.mode = PS_MODE_FULL_POWER;
+ del_timer_sync(&piperp->ps.timer);
+}
+
+EXPORT_SYMBOL_GPL(piper_ps_deinit);
diff --git a/drivers/net/wireless/digiPiper/digiPs.h b/drivers/net/wireless/digiPiper/digiPs.h
new file mode 100644
index 000000000000..1feedd87e228
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/digiPs.h
@@ -0,0 +1,50 @@
+/*
+ * Header file for digiPs.c. Declares functions used for power save mode.
+ */
+
+#ifndef digiPs_h
+
+#define digiPS_h
+
+#include <net/mac80211.h>
+#include "pipermain.h"
+
+enum piper_ps_event {
+ PS_EVENT_BEACON_RECEIVED,
+ PS_EVENT_STOP_TRANSMIT_TIMER_EXPIRED,
+ PS_EVENT_TRANSMITTER_DONE,
+ PS_EVENT_TRANSMITTER_DONE_TIMER_TICK,
+ PS_EVENT_WAKEUP,
+ PS_EVENT_MISSED_BEACON
+};
+
+enum piper_ps_tx_completion_result {
+ PS_RETURN_SKB_TO_MAC80211,
+ PS_DONT_RETURN_SKB_TO_MAC80211
+};
+
+enum piper_ps_active_result {
+ PS_CONTINUE_TRANSMIT,
+ PS_STOP_TRANSMIT
+};
+
+
+/*
+ * Current version of mac80211 doesn't set power management bit in frame headers,
+ * so I guess we have to for now.
+ *
+ * TODO: See if we still have to do this in the next drop.
+ */
+#define piper_ps_set_header_flag(piperp, header) \
+ header->fc.pwrMgt = (piperp->ps.power_management)
+
+int piper_ps_active(struct piper_priv *piperp);
+void piper_ps_process_receive_frame(struct piper_priv *piperp,
+ struct sk_buff *skb);
+void piper_ps_init(struct piper_priv *piperp);
+void piper_ps_deinit(struct piper_priv *piperp);
+void piper_ps_set(struct piper_priv *piperp, bool powerSaveOn);
+struct ieee80211_rate *piper_ps_check_rate(struct piper_priv *piperp,
+ struct ieee80211_rate *rate);
+
+#endif
diff --git a/drivers/net/wireless/digiPiper/digiRx.c b/drivers/net/wireless/digiPiper/digiRx.c
new file mode 100644
index 000000000000..01ff486311b1
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/digiRx.c
@@ -0,0 +1,412 @@
+/*
+ * digiRx.c
+ *
+ * Copyright (C) 2009 by Digi International Inc.
+ * All rights reserved.
+ *
+ * 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 file contains the routines that are related to transmitting
+ * frames.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+
+#include "pipermain.h"
+#include "mac.h"
+#include "phy.h"
+#include "digiPs.h"
+
+#define WANT_RECEIVE_COUNT_SCROLL (0)
+#define AES_TIMEOUT (200)
+#define RX_DEBUG (1)
+
+#if RX_DEBUG
+static int dlevel = DWARNING;
+#define dprintk(level, fmt, arg...) if (level >= dlevel) \
+ printk(KERN_ERR PIPER_DRIVER_NAME \
+ ": %s - " fmt, __func__, ##arg)
+#else
+#define dprintk(level, fmt, arg...) do {} while (0)
+#endif
+
+
+/*
+ * This routine is called to flush the receive and transmit FIFOs. It is used
+ * for error recovery when we detect corrupted data in the FIFO's. It should be
+ * called with the AES lock set.
+ */
+static void reset_fifo(struct piper_priv *piperp)
+{
+ unsigned int i;
+
+ piperp->ac->rd_reg(piperp, BB_AES_CTL);
+ piperp->ac->wr_reg(piperp, BB_AES_CTL, 0, op_write);
+ // clear the TX-FIFO memory
+ for (i = 0; i < 448; i++)
+ piperp->ac->wr_reg(piperp, BB_DATA_FIFO, 0, op_write);
+
+ // clear RX-FIFO memory
+ for (i = 0; i < 512; i++)
+ piperp->ac->rd_reg(piperp, BB_DATA_FIFO);
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, BB_GENERAL_CTL_RXFIFORST, op_or);
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, ~BB_GENERAL_CTL_RXFIFORST, op_and);
+}
+
+/*
+ * This routine is called to receive a frame. The hardware header has
+ * already been read from the FIFO. We need to read out the frame. If
+ * the frame was encrypted with AES and we have the correct key, then
+ * we use the AES H/W encryption engine to decrypt the frame. We also
+ * set up a ieee80211_rx_status structure with the appropriate info.
+ *
+ * Arguments
+ * digi context information
+ * skb empty buffer to receive packet into
+ * length number of bytes in FIFO
+ * fr_ctrl_field buffer to copy frame control header into
+ * status status structure we must write status into
+ *
+ * Returns
+ * true frame was received
+ * false an encryption error was detected
+ */
+static bool receive_packet(struct piper_priv *piperp, struct sk_buff *skb, int length,
+ frameControlFieldType_t * fr_ctrl_field,
+ struct ieee80211_rx_status *status)
+{
+ _80211HeaderType *header;
+ bool result = true;
+ int originalLength = length;
+ int headerlen;
+#if WANT_RECEIVE_COUNT_SCROLL
+ static int packetCount = 0;
+#endif
+
+ headerlen = _80211_HEADER_LENGTH;
+ if (length < _80211_HEADER_LENGTH) {
+ /*
+ * If we branch here, then there is not enough data to make a
+ * complete header. This is possible if this is a control frame.
+ * Adjust our length so that we do not read too much data from
+ * the FIFO.
+ */
+ headerlen = length;
+ }
+
+ /*
+ * Read the frame header. This includes the frame control fields
+ * as well as the 802.11 header.
+ */
+ header = (_80211HeaderType *) skb_put(skb, headerlen);
+ length -= headerlen;
+ piperp->ac->rd_fifo(piperp, BB_DATA_FIFO, (uint8_t *) header, headerlen);
+ memcpy(fr_ctrl_field, &header->fc, sizeof(fr_ctrl_field));
+
+ if (((u32)(skb->tail)) & 0x3) {
+ /* align data */
+ skb_reserve(skb, 4 - ((u32)(skb->tail) & 0x3));
+ }
+
+ if (header->fc.protected) {
+ /*
+ * If we branch here, then the frame is encrypted. We need
+ * to figure out if we should try to decrypt it.
+ */
+ unsigned char *rsnHeader;
+ unsigned int aesDataBlob[AES_BLOB_LENGTH / sizeof(unsigned int)];
+ unsigned int keyIndex;
+
+ rsnHeader = skb_put(skb, PIPER_EXTIV_SIZE);
+
+ /*
+ * Step 1: Read the rest of the unencrypted data, which should
+ * consist of the extiv fields.
+ */
+ piperp->ac->rd_fifo(piperp, BB_DATA_FIFO, rsnHeader, PIPER_EXTIV_SIZE);
+ length -= PIPER_EXTIV_SIZE;
+ keyIndex = rsnHeader[3] >> 6;
+
+ if (piper_prepare_aes_datablob(piperp, keyIndex, (u8 *) aesDataBlob,
+ (unsigned char *)header, originalLength - 12,
+ false)) {
+ /*
+ * If we come here, then we have the correct encryption key for
+ * the frame and will now try to decrypt it.
+ */
+ unsigned int timeout = AES_TIMEOUT;
+ unsigned long flags;
+
+ spin_lock_irqsave(&piperp->aesLock, flags);
+
+ /*
+ * Step 2: Wait for AES to become ready.
+ */
+ while (piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_BUSY) {
+ timeout--;
+ if (timeout == 0) {
+ /*
+ * If we come here, then AES busy appears to be stuck high. It should only be
+ * high for a maximum of about 80 us when it is encrypting a transmit frame.
+ * Our timeout value is high enough to guarantee that the engine has had enough
+ * time to complete the transmit. Apparently there is data stuck in the FIFO
+ * from either a previous transmit or receive.
+ */
+ dprintk(DWARNING, "1st AES busy never became ready\n");
+ digiWifiDumpRegisters(piperp, MAIN_REGS | MAC_REGS);
+ /*
+ * Recover by flushing the FIFO and returning in error.
+ */
+ reset_fifo(piperp);
+#if 0
+ /*
+ * TODO: Figure out why this code snippet doesn't work. I would think
+ * that if we reset the fifo, we should just return in error since we will
+ * have discarded the frame. However, when we do that the system hangs
+ * (after a while). This doesn't make sense.
+ */
+ spin_unlock_irqrestore(&piperp->aesLock, flags);
+ result = false;
+ goto receive_packet_exit;
+#else
+ break;
+#endif
+ }
+ udelay(1);
+ }
+
+ /*
+ * Step 3: Set the AES mode, and then read from the AES control
+ * register to put the AES engine into receive mode.
+ */
+ piperp->ac->rd_reg(piperp, BB_AES_CTL);
+
+ /*
+ * Step 4: Write the expanded AES key into the AES FIFO.
+ */
+ piperp->ac->wr_fifo(piperp, BB_AES_FIFO,
+ (unsigned char *)piperp->key[keyIndex].expandedKey,
+ EXPANDED_KEY_LENGTH);
+
+ /*
+ * Step 5: Write the AES IV and headers into the AES FIFO.
+ */
+ piperp->ac->wr_fifo(piperp, BB_AES_FIFO, (unsigned char *)aesDataBlob,
+ AES_BLOB_LENGTH);
+
+ /*
+ * Step 6: Now, finally, read the unencrypted frame from the
+ * AES FIFO. Adjust the length so that we don't try
+ * to read the MIC or the ICV which the AES engine will
+ * process for us.
+ */
+ length -= MIC_SIZE + ICV_SIZE;
+ piperp->ac->rd_fifo(piperp, BB_AES_FIFO, skb_put(skb, length), length);
+ /*
+ * mac80211 seems to expect there to be a MIC even if the packet
+ * has already been decrypted. It will drop off what it thinks
+ * are the extra MIC bytes, so add some extra bytes that it
+ * can drop off without losing any data.
+ */
+ skb_put(skb, MIC_SIZE); /* add fake MIC */
+
+ /*
+ * Step 7: Wait for AES to become ready.
+ */
+ while (piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_BUSY) {
+ timeout--;
+ if (timeout == 0) {
+ dprintk(DWARNING, "2nd AES busy never became ready\n");
+ digiWifiDumpRegisters(piperp, MAIN_REGS | MAC_REGS);
+ }
+ udelay(1);
+ }
+ result = ((piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_MIC) != 0);
+ timeout = 500;
+ while ((piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_FIFO_EMPTY) == 0) {
+ timeout--;
+ piperp->ac->rd_reg(piperp, BB_AES_FIFO);
+ udelay(1);
+ }
+#ifdef WANT_DEBUG
+ if (piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_BUSY) {
+ digi_dbg("AES busy set at end of rx\n");
+ }
+#endif
+ spin_unlock_irqrestore(&piperp->aesLock, flags);
+
+ /* pad an extra 8 bytes for the MIC which the H/W strips */
+ skb_put(skb, 8);
+ if (result) {
+ status->flag |= RX_FLAG_DECRYPTED;
+ } else {
+ digi_dbg("Error decrypting packet\n");
+ }
+ } else {
+ /*
+ * If we branch here, then we are not able to decrypt the
+ * packet possibly because we don't have the key, or because
+ * the packet was encrypted using TKIP. Read the rest of the
+ * encrypted data. mac80211 will have to decrypt it in software.
+ */
+ piperp->ac->rd_fifo(piperp, BB_DATA_FIFO, skb_put(skb, length),
+ length);
+ }
+ } else {
+ /*
+ * Frame is not encrypted, so just read it.
+ */
+ piperp->ac->rd_fifo(piperp, BB_DATA_FIFO, skb_put(skb, length), length);
+ }
+
+#if WANT_RECEIVE_COUNT_SCROLL
+ if (((++packetCount) & 1023) == 0) {
+ printk(KERN_ERR "\n%d recd, tx_start_count = %d, tx_complete_count = %d.\n",
+ packetCount, piperp->pstats.tx_start_count,
+ piperp->pstats.tx_complete_count);
+ }
+#endif
+#if 0
+receive_packet_exit:
+#endif
+ return result;
+}
+
+/*
+ * This routine is called when we receive an ACK. This should be after
+ * we have transmitted a packet. We need to tell the upper layer we
+ * have a packet by calling ieee80211_tx_status_irqsafe with status
+ * information. The transmit routine also disables queuing whenever we
+ * transmit since we can only transmit one packet at a time, so we need
+ * to reenable to transmit queue too.
+ */
+static inline void handle_ack(struct piper_priv *piperp, int signal_strength)
+{
+ if (piper_tx_getqueue(piperp) && piperp->expectingAck) {
+ struct ieee80211_tx_info *txInfo = IEEE80211_SKB_CB(piper_tx_getqueue(piperp));
+ if ((txInfo->flags & IEEE80211_TX_CTL_NO_ACK) == 0) {
+ piperp->clear_irq_mask_bit(piperp,
+ BB_IRQ_MASK_TX_FIFO_EMPTY |
+ BB_IRQ_MASK_TIMEOUT |
+ BB_IRQ_MASK_TX_ABORT);
+ piperp->tx_signal_strength = signal_strength;
+ piperp->tx_result = RECEIVED_ACK;
+ tasklet_hi_schedule(&piperp->tx_tasklet);
+ }
+ }
+}
+
+
+/*
+ * This is the entry point for the receive tasklet. It is executed
+ * to process receive packets. It allocates an SKB and receives
+ * the packet into it.
+ *
+ * We may be called from the receive ISR if WANT_TO_RECEIVE_FRAMES_IN_ISR
+ * is set.
+ */
+void piper_rx_tasklet(unsigned long context)
+{
+ struct piper_priv *piperp = (struct piper_priv *)context;
+
+ /*
+ * This while loop will keep executing as long as the H/W indicates there
+ * are more frames in the FIFO to be received.
+ */
+
+ piperp->ps.rxTaskletRunning = true;
+
+ while (((piperp->ac->rd_reg(piperp, BB_GENERAL_STAT) &
+ BB_GENERAL_STAT_RX_FIFO_EMPTY) == 0)
+ && (!piperp->ps.poweredDown)) {
+ struct sk_buff *skb = NULL;
+ struct ieee80211_rx_status status = { 0 };
+ struct rx_frame_hdr header;
+ unsigned int length = 0;
+ frameControlFieldType_t fr_ctrl_field;
+
+ /*
+ * Read and process the H/W header. This header is created by
+ * the hardware is is not part of the frame.
+ */
+ piperp->ac->rd_fifo(piperp, BB_DATA_FIFO, (u8 *)&header, sizeof(header));
+ phy_process_plcp(piperp, &header, &status, &length);
+ if ((length == 0) || (length > (RX_FIFO_SIZE - 48))) { /* 48 bytes for padding and related stuff */
+ unsigned long flags;
+
+ dprintk(DERROR, "bogus frame length (%d)\n", length);
+ dprintk(DERROR, "0x%08x 0x%08x\n", *(u32 *)&header, *(((u32 *)&header) + 1));
+ spin_lock_irqsave(&piperp->aesLock, flags);
+ reset_fifo(piperp);
+ spin_unlock_irqrestore(&piperp->aesLock, flags);
+ continue;
+ }
+
+ if (length != 0) {
+
+ skb = __dev_alloc_skb(RX_FIFO_SIZE + 100, GFP_ATOMIC);
+ if (skb == NULL) {
+ /* Oops. Out of memory. Exit the tasklet */
+ dprintk(DERROR, "__dev_alloc_skb failed\n");
+ break;
+ }
+
+ if (receive_packet(piperp, skb, length, &fr_ctrl_field, &status)) {
+
+ if (length >= _80211_HEADER_LENGTH)
+ {
+ /*
+ * If using the Airoha transceiver, then we want to monitor
+ * the receive signal strength and continuously adjust the
+ * receive amplifier so that we get the best possible signal
+ * to noise ratio.
+ */
+ unsigned int rssi = phy_determine_rssi(&header);
+
+ piperp->adjust_max_agc(piperp, rssi, (_80211HeaderType *) skb->data);
+ }
+
+ if (fr_ctrl_field.type == TYPE_ACK)
+ handle_ack(piperp, status.signal);
+
+ if ((fr_ctrl_field.type == TYPE_ACK)
+ || (fr_ctrl_field.type == TYPE_RTS)
+ || (fr_ctrl_field.type == TYPE_CTS)) {
+ /*
+ * Don't pass up RTS, CTS, or ACK frames. They just
+ * confuse the stack
+ */
+ dev_kfree_skb(skb);
+ } else {
+ if (fr_ctrl_field.type == TYPE_BEACON) {
+ piperp->beacon.weSentLastOne = false;
+ }
+
+ piper_ps_process_receive_frame(piperp, skb);
+#if WANT_TO_RECEIVE_FRAMES_IN_ISR
+ ieee80211_rx_irqsafe(piperp->hw, skb, &status);
+#else
+ ieee80211_rx(piperp->hw, skb, &status);
+#endif
+ }
+ } else {
+ /* Frame failed MIC, so discard it */
+ dprintk(DWARNING, "dropping bad frame\n");
+ dev_kfree_skb(skb);
+ }
+ }
+ }
+
+ piperp->ps.rxTaskletRunning = false;
+ piperp->set_irq_mask_bit(piperp, BB_IRQ_MASK_RX_FIFO);
+ piper_ps_rx_task_exiting(piperp);
+}
+EXPORT_SYMBOL_GPL(piper_rx_tasklet);
+
+
+
diff --git a/drivers/net/wireless/digiPiper/digiTx.c b/drivers/net/wireless/digiPiper/digiTx.c
new file mode 100644
index 000000000000..4bea846a42a4
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/digiTx.c
@@ -0,0 +1,604 @@
+/*
+ * digiTx.c
+ *
+ * Copyright (C) 2009 by Digi International Inc.
+ * All rights reserved.
+ *
+ * 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 file contains the routines that are related to transmitting
+ * frames.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/kthread.h>
+#include <linux/jiffies.h>
+#include <linux/timer.h>
+
+#include "pipermain.h"
+#include "mac.h"
+#include "phy.h"
+#include "digiPs.h"
+
+#define FRAME_CONTROL_FIELD_OFFSET (sizeof(struct tx_frame_hdr) + sizeof(struct psk_cck_hdr))
+#define AES_TIMEOUT (200)
+
+#define TX_DEBUG (1)
+
+#if TX_DEBUG
+//static int dlevel = DWARNING;
+#define dprintk(level, fmt, arg...) if (level >= dlevel) \
+ printk(KERN_ERR PIPER_DRIVER_NAME \
+ ": %s - " fmt, __func__, ##arg)
+#else
+#define dprintk(level, fmt, arg...) do {} while (0)
+#endif
+
+/*
+ * Adds an entry into the tx queue.
+ */
+int piper_tx_enqueue(struct piper_priv *piperp, struct sk_buff *skb, tx_skb_return_cb_t skb_return_cb)
+{
+ unsigned long flags;
+ int result = -1;
+
+ spin_lock_irqsave(&piperp->tx_queue_lock, flags);
+ if (NEXT_TX_QUEUE_INDEX(piperp->tx_queue_head) != piperp->tx_queue_tail) {
+ piperp->tx_queue[piperp->tx_queue_head].skb = skb;
+ piperp->tx_queue[piperp->tx_queue_head].skb_return_cb = skb_return_cb;
+ piperp->tx_queue_head = NEXT_TX_QUEUE_INDEX(piperp->tx_queue_head);
+ piperp->tx_queue_count++;
+ result = 0;
+ }
+ spin_unlock_irqrestore(&piperp->tx_queue_lock, flags);
+
+ return result;
+}
+EXPORT_SYMBOL_GPL(piper_tx_enqueue);
+
+
+/*
+ * Returns the skb for the current element.
+ */
+struct sk_buff *piper_tx_getqueue(struct piper_priv *piperp)
+{
+ if (piperp->tx_queue_head == piperp->tx_queue_tail) {
+ return NULL;
+ } else {
+ return piperp->tx_queue[piperp->tx_queue_tail].skb;
+ }
+}
+EXPORT_SYMBOL_GPL(piper_tx_getqueue);
+
+/*
+ * Returns the skb buffer call back for the current element.
+ */
+static inline tx_skb_return_cb_t piper_tx_getqueue_return_fn(struct piper_priv *piperp)
+{
+ return piperp->tx_queue[piperp->tx_queue_tail].skb_return_cb;
+}
+
+
+/*
+ * Called to advance the queue tail.
+ */
+static inline void piper_tx_queue_next(struct piper_priv *piperp)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&piperp->tx_queue_lock, flags);
+ if (piperp->tx_queue_head != piperp->tx_queue_tail) {
+ piperp->tx_queue[piperp->tx_queue_tail].skb = NULL;
+ piperp->tx_queue[piperp->tx_queue_tail].skb_return_cb = NULL;
+ piperp->tx_queue_tail = NEXT_TX_QUEUE_INDEX(piperp->tx_queue_tail);
+ piperp->tx_queue_count--;
+ }
+ spin_unlock_irqrestore(&piperp->tx_queue_lock, flags);
+}
+
+/*
+ * Called when we unload to clear any remaining queue entries.
+ */
+void piper_empty_tx_queue(struct piper_priv *piperp)
+{
+ while (piper_tx_getqueue(piperp)) {
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(piper_tx_getqueue(piperp));
+
+ ieee80211_tx_info_clear_status(info);
+ piper_tx_getqueue_return_fn(piperp)(piperp->hw, piper_tx_getqueue(piperp));
+ piper_tx_queue_next(piperp);
+ }
+}
+EXPORT_SYMBOL_GPL(piper_empty_tx_queue);
+
+
+bool piper_tx_queue_half_full(struct piper_priv *piperp)
+{
+ return (piperp->tx_queue_count >= (PIPER_TX_QUEUE_SIZE >> 1));
+}
+EXPORT_SYMBOL_GPL(piper_tx_queue_half_full);
+
+
+
+/*
+ * This routine writes a frame using H/W AES encryption.
+ *
+ * Arguments
+ * digi context
+ * buffer pointer to start of frame
+ * length number of bytes in frame
+ *
+ * Return Values
+ * 0 success
+ * !0 transmit failed
+ */
+static int piper_write_aes(struct piper_priv *piperp, unsigned char *buffer,
+ unsigned int length)
+{
+ int result;
+ int timeout = AES_TIMEOUT;
+ unsigned long spinLockFlags;
+
+ /*
+ * Step 1: Wait for AES to become ready.
+ */
+ spin_lock_irqsave(&piperp->aesLock, spinLockFlags);
+ while (piperp->ac->rd_reg(piperp, BB_RSSI) & BB_RSSI_EAS_BUSY) {
+ timeout--;
+ if (timeout == 0) {
+ /*
+ * If we come here, then AES busy appears to be stuck high. It should only be
+ * high for a maximum of about 80 us when it is encrypting a transmit frame.
+ * Our timeout value is high enough to guarantee that the engine has had enough
+ * time to complete the transmit. Apparently there is data stuck in the FIFO
+ * from either a previous transmit or receive.
+ */
+ digi_dbg("write AES, AES busy stuck on\n");
+ digiWifiDumpRegisters(piperp, MAIN_REGS | MAC_REGS);
+ /*
+ * We recover by simply continuing on. Step 3 writes to the AES control
+ * register. This will reset the AES engine and clear the error condition.
+ */
+ break;
+ }
+ udelay(1);
+ }
+
+ /*
+ * Step 2: Write the unencrypted part of the frame into the normal
+ * data FIFO.
+ */
+ piperp->ac->wr_fifo(piperp, BB_DATA_FIFO, buffer,
+ _80211_HEADER_LENGTH + TX_HEADER_LENGTH + PIPER_EXTIV_SIZE);
+
+ /*
+ * Step 3: Write to the AES control register. Writing to it puts
+ * AES H/W engine into transmit mode. We also make sure
+ * the AES mode is set correctly.
+ */
+ piperp->ac->wr_reg(piperp, BB_AES_CTL, 0, op_write);
+
+ /*
+ * Step 4: Write the expanded AES key into the AES FIFO.
+ */
+ piperp->ac->wr_fifo(piperp, BB_AES_FIFO,
+ (unsigned char *)piperp->key[piperp->tx_aes_key].expandedKey,
+ EXPANDED_KEY_LENGTH);
+
+ /*
+ * Step 5: Write the AES IV and headers into the AES FIFO.
+ */
+ piperp->ac->wr_fifo(piperp, BB_AES_FIFO, (unsigned char *)piperp->tx_aes_blob,
+ AES_BLOB_LENGTH);
+
+ /*
+ * Step 6: Now, finally, write the part of the frame that needs to
+ * be encrypted into the AES FIFO.
+ */
+ result =
+ piperp->ac->wr_fifo(piperp, BB_AES_FIFO,
+ &buffer[_80211_HEADER_LENGTH + TX_HEADER_LENGTH + PIPER_EXTIV_SIZE],
+ length - (_80211_HEADER_LENGTH + TX_HEADER_LENGTH +
+ PIPER_EXTIV_SIZE));
+
+ spin_unlock_irqrestore(&piperp->aesLock, spinLockFlags);
+
+ return result;
+}
+
+/*
+ * Determine what bit rate the next retry should be sent at.
+ *
+ * The mac80211 library passes us an array of tx bit rates. Each entry
+ * has a rate index and a limit (max number of retries at that rate).
+ * We use the rate index to build the H/W transmit header. The limit
+ * is decremented each time we retry. When it reaches zero, we try the
+ * next rate in the array.
+ */
+static struct ieee80211_rate *get_tx_rate(struct piper_priv *piperp, struct ieee80211_tx_info *info)
+{
+ struct ieee80211_rate *ret = NULL;
+
+ if (piperp->pstats.tx_retry_count[piperp->pstats.tx_retry_index] >=
+ info->control.rates[piperp->pstats.tx_retry_index].count) {
+ piperp->pstats.tx_retry_index++;
+ }
+
+ if (piperp->pstats.tx_retry_index >= IEEE80211_TX_MAX_RATES) {
+ return NULL; /* don't go beyond the end of the rates array */
+ }
+
+ if (piperp->pstats.tx_retry_index == 0) {
+ ret = ieee80211_get_tx_rate(piperp->hw, info);
+ } else {
+ ret = ieee80211_get_alt_retry_rate(piperp->hw, info, piperp->pstats.tx_retry_index - 1);
+ }
+
+ if (ret != NULL) {
+ if (piperp->calibrationTxRate) {
+ ret = piperp->calibrationTxRate;
+ }
+ }
+
+ ret = piper_ps_check_rate(piperp, ret);
+
+ return ret;
+}
+
+/*
+ * This function returns a value for the contention window in microseconds. We
+ * start with the contention window at CW_MIN and double it everytime we have to
+ * retry.
+ */
+static u16 piper_get_cw(struct piper_priv *piperp, bool isFirstTime)
+{
+ static u16 cw = DEFAULT_CW_MIN;
+
+ if (isFirstTime) {
+ cw = DEFAULT_CW_MIN;
+ } else {
+ cw <<= 1;
+ if (cw > DEFAULT_CW_MAX) {
+ cw = DEFAULT_CW_MAX;
+ }
+ }
+ return (cw + (10 * (piperp->rand() & (cw - 1)))) & 0xffff;
+}
+
+/*
+ * This function will prepend an RTS or CTS to self frame ahead of the current
+ * TX frame. This is done if the wantRts or wantCts flag is set. The mac80211
+ * library determines if either of these flags is set.
+ *
+ * The RTS or CTS message is written into the transmit FIFO ahead of the
+ * data frame. Note that RTS and CTS messages are always sent in the clear
+ * so we do not have to worry about encryption.
+ *
+ * Our caller, the master transmit routine, is responsible for setting the
+ * transmit hold bit before calling us and clearing it after the data frame
+ * has been written into the FIFO. This ensures that the RTS/CTS frame is
+ * not transmitted until after the data frame is ready to go.
+ *
+ * Also note that if we are unable to send the RTS/CTS frame, then the H/W
+ * is smart enough to also about the data frame. So we will not send
+ * the data frame without the RTS/CTS frame.
+ */
+static void handle_rts_cts(struct piper_priv *piperp,
+ struct ieee80211_tx_info *txInfo, unsigned int frameType)
+{
+ piperp->tx_rts = false;
+
+ if (frameType == TYPE_DATA) {
+ unsigned int header[2];
+ struct ieee80211_rate *rate = NULL;
+ bool wantCts = (!!(txInfo->control.rates[piperp->pstats.tx_retry_index].flags
+ & IEEE80211_TX_RC_USE_CTS_PROTECT)
+ | piperp->tx_cts);
+ bool wantRts = !!(txInfo->control.rates[piperp->pstats.tx_retry_index].flags
+ & IEEE80211_TX_RC_USE_RTS_CTS);
+
+ if ((wantRts) || (wantCts)) {
+ /*
+ * If we are sending an RTS or a CTS, then get the rate information.
+ */
+ if (piperp->calibrationTxRate) {
+ rate = piperp->calibrationTxRate;
+ } else {
+ rate = ieee80211_get_rts_cts_rate(piperp->hw, txInfo);
+ }
+ if (rate == NULL) {
+ digi_dbg
+ ("ieee80211_get_rts_cts_rate(digi->hw, txInfo) returned NULL!\n");
+ }
+ }
+ if ((wantRts) && (rate)) {
+ /*
+ * We're sending an RTS, so load it into the FIFO.
+ */
+ struct ieee80211_rts rtsFrame;
+
+ ieee80211_rts_get(piperp->hw, txInfo->control.vif,
+ piper_tx_getqueue(piperp)->data + TX_HEADER_LENGTH,
+ piper_tx_getqueue(piperp)->len - TX_HEADER_LENGTH, txInfo,
+ &rtsFrame);
+ /*
+ * If we come here, then we need to send an RTS frame ahead of the
+ * current data frame.
+ */
+ phy_set_plcp((unsigned char *)header, sizeof(struct ieee80211_rts),
+ rate, 0);
+ piperp->ac->wr_fifo(piperp, BB_DATA_FIFO, (unsigned char *)header,
+ TX_HEADER_LENGTH);
+ piperp->ac->wr_fifo(piperp, BB_DATA_FIFO, (unsigned char *)&rtsFrame,
+ sizeof(rtsFrame));
+ if (piperp->pstats.tx_total_tetries != 0) {
+ piperp->pstats.ll_stats.dot11RTSFailureCount++;
+ }
+ piperp->tx_rts = true;
+ } else if ((wantCts) && (rate)) {
+ /*
+ * We're sending a CTS, so load it into the FIFO.
+ */
+ struct ieee80211_cts ctsFrame;
+
+ ieee80211_ctstoself_get(piperp->hw, txInfo->control.vif,
+ piper_tx_getqueue(piperp)->data + TX_HEADER_LENGTH,
+ piper_tx_getqueue(piperp)->len - TX_HEADER_LENGTH,
+ txInfo, &ctsFrame);
+ /*
+ * At the time this code was written, the mac80211 library had
+ * a bug in the ieee80211_ctstoself_get which caused it to copy
+ * the wrong MAC address into the cts frame. So we copy the
+ * right one (ours) in now.
+ */
+ memcpy(piperp->ctsFrame.ra, piperp->hw->wiphy->perm_addr, ETH_ALEN);
+
+ /*
+ * If we come here, then we need to send a CTS to self frame ahead of the
+ * current data frame.
+ */
+ phy_set_plcp((unsigned char *)header, sizeof(struct ieee80211_cts),
+ rate, 0);
+ piperp->ac->wr_fifo(piperp, BB_DATA_FIFO, (unsigned char *)header,
+ TX_HEADER_LENGTH);
+ piperp->ac->wr_fifo(piperp, BB_DATA_FIFO, (unsigned char *)&ctsFrame,
+ sizeof(ctsFrame));
+ }
+ }
+}
+
+/*
+ * This routine is called to report the result of a transmit operation to
+ * mac80211. It is used for both successful transmissions and failures.
+ * It sends the result to the stack, removes the current tx frame from the
+ * queue, and then wakes
+ * up the transmit queue.
+ */
+void packet_tx_done(struct piper_priv *piperp, tx_result_t result,
+ int signal_strength)
+{
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(piper_tx_getqueue(piperp));
+ int i;
+ struct sk_buff *skb;
+ unsigned long flags;
+
+#define WANT_TRANSMIT_RESULT (0)
+#if WANT_TRANSMIT_RESULT
+ const char *resultText[] =
+ {
+ "RECEIVED_ACK",
+ "TX_COMPLETE",
+ "OUT_OF_RETRIES",
+ "TX_NOT_DONE"
+ };
+#endif
+ del_timer_sync(&piperp->tx_timer);
+ piperp->expectingAck = false;
+
+#if WANT_TRANSMIT_RESULT
+ printk(KERN_ERR "Transmit result %s\n", resultText[result]);
+#endif
+ if (piperp->tx_calib_cb)
+ piperp->tx_calib_cb(piperp);
+
+ if (piper_tx_getqueue(piperp) != NULL) {
+ skb_pull(piper_tx_getqueue(piperp), TX_HEADER_LENGTH);
+
+ ieee80211_tx_info_clear_status(info);
+
+ /* prepare statistics and pass them to the stack */
+ for (i = 0; i < IEEE80211_TX_MAX_RATES; i++) {
+ info->status.rates[i].count = piperp->pstats.tx_retry_count[i];
+ if (info->status.rates[i].count == 0)
+ info->status.rates[i].idx = -1;
+ }
+
+ info->status.ack_signal = signal_strength;
+ info->flags |= (result == RECEIVED_ACK) ? IEEE80211_TX_STAT_ACK : 0;
+ piperp->pstats.tx_complete_count++;
+ piperp->pstats.tx_queue.len--;
+ if (piperp->tx_rts)
+ piperp->pstats.ll_stats.dot11RTSSuccessCount++;
+
+ skb = piper_tx_getqueue(piperp);
+ piper_tx_getqueue_return_fn(piperp)(piperp->hw, skb);
+
+ piper_tx_queue_next(piperp);
+
+ if (piper_tx_getqueue(piperp) == NULL) {
+ spin_lock_irqsave(&piperp->tx_tasklet_lock, flags);
+ piperp->tx_tasklet_running = false;
+ spin_unlock_irqrestore(&piperp->tx_tasklet_lock, flags);
+ if (piperp->ps.allowTransmits) {
+ ieee80211_wake_queues(piperp->hw);
+ } else {
+ /*
+ * Do not wake up the mac80211 Tx queue if we are trying to power
+ * down. Make sure we set the stopped_tx_queues flag so that we
+ * know to restart the queues.
+ */
+ piperp->ps.stopped_tx_queues = true;
+ }
+ } else {
+ if (result == OUT_OF_RETRIES) {
+ /*
+ * If we come here, then we are being called from the middle of the
+ * transmit routine and we have to reschedule the transmit task to
+ * start dequeueing the next frame.
+ */
+ tasklet_hi_schedule(&piperp->tx_tasklet);
+ }
+ }
+ } else {
+ digi_dbg("packet_tx_done called with empty queue\n");
+ }
+
+ piperp->tx_result = TX_NOT_DONE;
+}
+EXPORT_SYMBOL_GPL(packet_tx_done);
+
+/*
+ * This function is the entry point for the transmit tasklet. It
+ * is called to transmit frames. It will first be called to transmit
+ * the frame and then to retry if the original transmit fails. So
+ * it does both the first transmit and the subsequent retries.
+ *
+ * Arguments
+ * context context information
+ */
+void piper_tx_tasklet(unsigned long context)
+{
+ struct piper_priv *piperp = (struct piper_priv *)context;
+ frameControlFieldType_t *fc;
+ int err;
+
+ piperp->expectingAck = false;
+ del_timer_sync(&piperp->tx_timer);
+ if ((piperp->tx_result == RECEIVED_ACK) || (piperp->tx_result == TX_COMPLETE)) {
+ /*
+ * We will come here if the receiver task received an ACK, or if we got
+ * a tx fifo empty interrupt. In these cases the receiver thread or ISR
+ * schedule the tx tasklet to handle the event rather than calling
+ * packet_tx_done directly.
+ */
+ packet_tx_done(piperp, piperp->tx_result, piperp->tx_signal_strength);
+ }
+
+
+ /*
+ * Clear flags here to cover ACK case. We do not clear the flags in the ACK
+ * routine since it is possible to receive an ACK after we have started the
+ * next packet. The appropriate interrupts will be reenabled if we decide
+ * to retransmit.
+ */
+ piperp->clear_irq_mask_bit(piperp,
+ BB_IRQ_MASK_TX_FIFO_EMPTY | BB_IRQ_MASK_TIMEOUT |
+ BB_IRQ_MASK_TX_ABORT);
+
+ if (piper_tx_getqueue(piperp) != NULL) {
+ struct ieee80211_tx_info *txInfo = IEEE80211_SKB_CB(piper_tx_getqueue(piperp));
+ struct ieee80211_rate *txRate = get_tx_rate(piperp, txInfo);
+
+ if (txRate != NULL) {
+ fc = (frameControlFieldType_t *)
+ &piper_tx_getqueue(piperp)->data[FRAME_CONTROL_FIELD_OFFSET];
+
+ /* set the retry bit if this is not the first try */
+ if (piperp->pstats.tx_retry_count[0] != 0)
+ fc->retry = 1;
+
+ piperp->ac->wr_reg(piperp, MAC_BACKOFF,
+ piper_get_cw(piperp,
+ (piperp->pstats.tx_retry_count[0] == 0)),
+ op_write);
+
+ /*
+ * Build the H/W transmit header. The transmit header is rebuilt on each
+ * retry because it has the TX rate information which may change for
+ * retries.
+ */
+ phy_set_plcp(piper_tx_getqueue(piperp)->data,
+ piper_tx_getqueue(piperp)->len - TX_HEADER_LENGTH,
+ txRate, piperp->use_hw_aes ? 8 : 0);
+
+ /*
+ * Pause the transmitter so that we don't start transmitting before we
+ * are ready.
+ */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, BB_GENERAL_CTL_TX_HOLD, op_or);
+
+ handle_rts_cts(piperp, txInfo, fc->type);
+
+ if (piperp->use_hw_aes == true && txInfo->control.hw_key != NULL) {
+ err =
+ piper_write_aes(piperp, piper_tx_getqueue(piperp)->data,
+ piper_tx_getqueue(piperp)->len);
+ } else {
+ err =
+ piperp->ac->wr_fifo(piperp, BB_DATA_FIFO, piper_tx_getqueue(piperp)->data,
+ piper_tx_getqueue(piperp)->len);
+ }
+
+ /* Clear any pending TX interrupts */
+ piperp->ac->wr_reg(piperp, BB_IRQ_STAT,
+ BB_IRQ_MASK_TX_FIFO_EMPTY | BB_IRQ_MASK_TIMEOUT |
+ BB_IRQ_MASK_TX_ABORT, op_write);
+
+ /*
+ * Now start the transmitter.
+ */
+ piperp->expectingAck = ((txInfo->flags & IEEE80211_TX_CTL_NO_ACK) == 0);
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, ~BB_GENERAL_CTL_TX_HOLD,
+ op_and);
+
+ /*
+ * Set interrupt flags. Use the timeout interrupt if we expect
+ * an ACK. Use the FIFO empty interrupt if we do not expect an ACK.
+ */
+ if (txInfo->flags & IEEE80211_TX_CTL_NO_ACK) {
+ piperp->set_irq_mask_bit(piperp,
+ BB_IRQ_MASK_TX_FIFO_EMPTY |
+ BB_IRQ_MASK_TX_ABORT);
+ } else {
+ /*
+ * We set up a timer to fire in 1/4 second. We should not need it, but somehow
+ * we seem to miss a timeout interrupt occasionally. Perhaps we encounter a receive
+ * overrun which causes the H/W to discard the ACK packet without generating
+ * a timeout.
+ */
+ piperp->tx_timer.expires = jiffies + (HZ >> 2);
+ add_timer(&piperp->tx_timer);
+
+ /*
+ * Also set the IRQ mask to listen for timeouts and TX aborts. We will receive
+ * an ACK (which is handled by the RX routine) if the TX is successful.
+ */
+ piperp->set_irq_mask_bit(piperp,
+ BB_IRQ_MASK_TIMEOUT |
+ BB_IRQ_MASK_TX_ABORT);
+ }
+ if ((piperp->pstats.tx_total_tetries != 0) &&
+ ((txInfo->flags & IEEE80211_TX_CTL_NO_ACK) == 0)) {
+ piperp->pstats.ll_stats.dot11ACKFailureCount++;
+ }
+ piperp->pstats.tx_retry_count[piperp->pstats.tx_retry_index]++;
+ piperp->pstats.tx_total_tetries++;
+ } else {
+ packet_tx_done(piperp, OUT_OF_RETRIES, 0);
+ }
+ } else {
+ long unsigned int flags;
+
+ spin_lock_irqsave(&piperp->tx_tasklet_lock, flags);
+ piperp->tx_tasklet_running = false;
+ spin_unlock_irqrestore(&piperp->tx_tasklet_lock, flags);
+ }
+}
+EXPORT_SYMBOL_GPL(piper_tx_tasklet);
+
+
+
diff --git a/drivers/net/wireless/digiPiper/mac.h b/drivers/net/wireless/digiPiper/mac.h
new file mode 100644
index 000000000000..892a15a8b67c
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/mac.h
@@ -0,0 +1,375 @@
+#ifndef DIGI_MAC_H_
+#define DIGI_MAC_H_
+
+enum baseband_control_regs {
+ BB_VERSION = 0x00,
+ BB_GENERAL_CTL = 0x04,
+ BB_GENERAL_STAT = 0x08,
+ BB_RSSI = 0x0c,
+ BB_IRQ_MASK = 0x10,
+ BB_IRQ_STAT = 0x14,
+ BB_SPI_DATA = 0x18,
+ BB_SPI_CTRL = 0x1c,
+ BB_DATA_FIFO = 0x20,
+ BB_TRACK_CONTROL = 0x28,
+ BB_CONF_2 = 0x2c,
+ BB_AES_FIFO = 0x30,
+ BB_AES_CTL = 0x38,
+ BB_OUTPUT_CONTROL = 0x3c
+};
+
+#define BB_VERSION_MASK(v) ((v) & 0xffff)
+
+#define BB_GENERAL_CTL_RX_EN (1<<0)
+#define BB_GENERAL_CTL_ANT_DIV (1<<1)
+#define BB_GENERAL_CTL_ANT_SEL (1<<2)
+#define BB_GENERAL_CTL_GEN_5GEN (1<<3) // 5 GHz band enable
+#define BB_GENERAL_CTL_SH_PRE (1<<4)
+#define BB_GENERAL_CTL_RXFIFORST (1<<5)
+#define BB_GENERAL_CTL_TXFIFORST (1<<6)
+#define BB_GENERAL_CTL_TX_HOLD (1<<7)
+#define BB_GENERAL_CTL_BEACON_EN (1<<8)
+#define BB_GENERAL_CTL_FW_LOAD_ENABLE (1 << 9)
+#define BB_GENERAL_CTL_DSP_LOAD_ENABLE (1 << 10)
+#define BB_GENERAL_CTL_MAC_ASSIST_ENABLE (1 << 11)
+#define BB_GENERAL_CTL_TX_FIFO_FULL (1<<15)
+#define BB_GENERAL_CTL_TX_FIFO_EMPTY (1<<14)
+/* TODO: verify max gain value for Piper and Wi9p*/
+#define BB_GENERAL_CTL_MAX_GAIN(g) (((g) & 0x7f)<<16)
+#define BB_GENERAL_CTL_PWR_UP (1<<24)
+#define BB_GENERAL_CTL_ADC_CLK_EN (1<<25)
+#define BB_GENERAL_CTL_BOOT_STAT (1<<28)
+#define BB_GENERAL_CTL_CLK_EN (1<<29)
+#define BB_GENERAL_CTL_SPI_RST (1<<30)
+
+#define BB_GENERAL_CTL_MAX_GAIN_MASK (0x007F0000)
+#define BB_GENERAL_CTL_DEFAULT_MAX_GAIN_A (0x00790000)
+#define BB_GENERAL_CTL_DEFAULT_MAX_GAIN_BG (0x007c0000)
+
+#if defined(CONFIG_PIPER_WIFI)
+#if 0
+#define BB_GENERAL_CTL_INIT (BB_GENERAL_CTL_MAX_GAIN(0x7a) | \
+ BB_GENERAL_CTL_PWR_UP | BB_GENERAL_CTL_ADC_CLK_EN | \
+ BB_GENERAL_CTL_BOOT_STAT | BB_GENERAL_CTL_CLK_EN)
+#define BB_GENERAL_CTL_RESET (BB_GENERAL_CTL_MAX_GAIN(0x7f) | \
+ BB_GENERAL_CTL_ADC_CLK_EN | BB_GENERAL_CTL_BOOT_STAT | \
+ BB_GENERAL_CTL_SPI_RST)
+#else
+#define BB_GENERAL_CTL_INIT (BB_GENERAL_CTL_MAX_GAIN(0x7a)
+
+#define BB_GENERAL_CTL_RESET (BB_GENERAL_CTL_MAX_GAIN(0x7f) | \
+ BB_GENERAL_CTL_SPI_RST
+#endif
+#else
+#define BB_GENERAL_CTL_INIT (BB_GENERAL_CTL_MAX_GAIN(0x7a) | \
+ BB_GENERAL_CTL_PWR_UP | BB_GENERAL_CTL_ADC_CLK_EN | \
+ BB_GENERAL_CTL_BOOT_STAT | BB_GENERAL_CTL_CLK_EN)
+
+#define BB_GENERAL_CTL_RESET (BB_GENERAL_CTL_MAX_GAIN(0x7f) | \
+ BB_GENERAL_CTL_ADC_CLK_EN | BB_GENERAL_CTL_BOOT_STAT | \
+ BB_GENERAL_CTL_SPI_RST)
+#endif
+#define BB_RSSI_LED (1<<8)
+#define BB_RSSI_EAS_FIFO_EMPTY (1 << 16)
+#define BB_RSSI_EAS_FIFO_FULL (1 << 17)
+#define BB_RSSI_EAS_BUSY (1 << 18)
+#define BB_RSSI_EAS_MIC (1 << 19)
+#define BB_RSSI_ANT_MASK (0xff<<24)
+#define BB_RSSI_ANT_NO_DIV_MAP (0x96000000)
+#define BB_RSSI_ANT_DIV_MAP (0x1E000000)
+
+#define BB_GENERAL_STAT_RESET (1<<30)
+/*
+ * STAT_B_EN is a constant that defines a bit in the Wireless Controller FPGA Baseband Control Register
+ * for General Status, which enables the PSK/CCK receiver baseband circuitry (802.11b receiver).
+ * STAT_A_EN is a constant that defines a bit in the Wireless Controller FPGA Baseband Control Register
+ * for General Status, which enables the OFDM receive baseband circuitry (802.11a receiver).
+ */
+
+#define BB_GENERAL_STAT_B_EN 0x10000000 // B EN (PSK/CCK)
+#define BB_GENERAL_STAT_A_EN 0x20000000 // A EN (OFDM)
+#define BB_GENERAL_STAT_RX_FIFO_EMPTY (1 << 4)
+#define BB_GENERAL_STAT_DC_DIS (1 << 24)
+#define BB_GENERAL_STAT_SRC_DIS (1 << 16)
+#define BB_GENERAL_STAT_SPRD_DIS (1 << 17)
+#define BB_GENERAL_STAT_DLL_DIS (1 << 18)
+#define TRACK_TX_B_GAIN_MASK 0xff000000 // Mask word for B_TX_GAIN
+#define TRACK_TX_B_GAIN_NORMAL 0xA0000000 // normal setting for B_TX_GAIN
+#define TRACK_BG_BAND 0x00430000 // Tracking constant for 802.11 b/g frequency band
+#define TRACK_CONSTANT_MASK 0x00ff0000 // mask for tracking constant
+#define TRACK_4920_4980_A_BAND 0x00210000 // Tracking constant for 802.11 a sub-frequency band
+#define TRACK_5150_5350_A_BAND 0x001F0000 // Tracking constant for 802.11 a sub-frequency band
+#define TRACK_5470_5725_A_BAND 0x001D0000 // Tracking constant for 802.11 a sub-frequency band
+#define TRACK_5725_5825_A_BAND 0x001C0000 // Tracking constant for 802.11 a sub-frequency band
+
+
+#define BB_IRQ_MASK_RX_FIFO (1<<0)
+#define BB_IRQ_MASK_TX_FIFO_EMPTY (1<<1)
+#define BB_IRQ_MASK_TIMEOUT (1<<2)
+#define BB_IRQ_MASK_TX_ABORT (1<<3)
+#define BB_IRQ_MASK_TBTT (1<<4)
+#define BB_IRQ_MASK_ATIM (1<<5)
+#define BB_IRQ_MASK_RX_OVERRUN (1<<6)
+
+#define BB_AES_CTL_KEY_LOAD (1<<2)
+#define BB_AES_CTL_AES_MODE (1<<4)
+
+enum mac_control_regs {
+ MAC_STA_ID0 = 0x40,
+ MAC_STA_ID1 = 0x44,
+ MAC_BSS_ID0 = 0x48,
+ MAC_BSS_ID1 = 0x4c,
+ MAC_SSID_LEN = 0x50, /* OFDM_BRS, PSK_BRS, TX_CTL, SSID_LEN */
+ MAC_BACKOFF = 0x54, /* actually 0x56; 2 low order bytes are empty */
+ MAC_DTIM_PERIOD = 0x58,
+ /*MAC_CFP_PERIOD = 0x59,*/
+ /*MAC_LISTEN_INTERVAL = 0x5a,*/
+ MAC_CFP_ATIM = 0x5c, /* beacon interval, CFP/ATIM duration */
+ MAC_STATUS = 0x60,
+ /*MAC_TXP_TIMING = 0x62,*/
+ /*MAC_STATUS = 0x63,*/
+ MAC_CTL = 0x64, /* MAC_AES_KEY_DIS (8 bits), MAC_CTL (8 bits) */
+ MAC_MEASURE = 0x68, /* actually 0x69 */
+ /*MAC_REMAIN_BO = 0x6a,*/
+ MAC_BEACON_FILT = 0x6c, /* actally 0x6d */
+ /*MAC_BEACON_BO = 0x6e,*/
+ MAC_STA2_ID0 = 0xb0,
+ MAC_STA2_ID1 = 0xb4,
+ MAC_STA3_ID0 = 0xb8,
+ MAC_STA3_ID1 = 0xbc,
+
+ MAC_EEPROM_CTL = 0xf0,
+ MAC_EEPROM_DATA = 0xf8,
+
+ MAC_SSID = 0x80,
+
+ BEACON_FIFO = 0x85, /* dummy value used to select data fifo for beacon load */
+};
+
+
+#define MAC_SSID_LEN_MASK (0x000000ff)
+#define MAC_REVISION_MASK(v) (((v) >> 16) & 0xffff)
+
+#define MAC_BEACON_INTERVAL_SHIFT (16)
+#define MAC_BEACON_INTERVAL_MASK (0xffff0000)
+
+#define MAC_ATIM_PERIOD_MASK (0x0000ffff)
+
+#define MAC_LISTEN_INTERVAL_MASK (0x0000ffff)
+
+#define MAC_DTIM_PERIOD_SHIFT (24)
+#define MAC_DTIM_PERIOD_MASK (0xff000000)
+
+#define MAC_DTIM_CFP_SHIFT (16)
+#define MAC_DTIM_CFP_MASK (0x00ff0000)
+
+#define MAC_OFDM_BRS_MASK (0xff000000)
+#define MAC_OFDM_BRS_SHIFT (24)
+#define MAC_PSK_BRS_MASK (0x000f0000)
+#define MAC_PSK_BRS_SHIFT (16)
+
+#define MAC_BEACON_BACKOFF_MASK (0x0000ffff)
+
+#define MAC_BRS_MASK (MAC_OFDM_BRS_MASK | MAC_PSK_BRS_MASK)
+
+#define MAC_CTL_TX_REQ (1)
+#define MAC_CTL_BEACON_TX (1<<2)
+#define MAC_CTL_IBSS (1<<4)
+#define MAC_CTL_AES_DISABLE (1<<5)
+#define MAC_CTL_MAC_FLTR (1<<6)
+#define MAC_CTL_KEY0_DISABLE (1<<8)
+#define MAC_CTL_KEY1_DISABLE (1<<9)
+#define MAC_CTL_KEY2_DISABLE (1<<10)
+#define MAC_CTL_KEY3_DISABLE (1<<11)
+
+#define MAC_EEPROM_CTL_WAIT_MS 21
+
+/*
+ * RX packets look something like:
+ * <custom bus protocol header(s)> - protocol-dependent
+ * <rx_frame_hdr> - 4 bytes
+ * <plcp; either psk_cck_hdr or ofdm_hdr> - 4 bytes
+ * <mac header> - dealt with by the mac80211 stack, not the driver
+ * <data>
+ *
+ * TX packets are similar:
+ * <custom bus protocol header(s)>
+ * <tx_frame_hdr> - 4 bytes
+ * <plcp; either psk_cck_hdr or ofdm_hdr> - 4 bytes
+ * <mac header>
+ * <data>
+ */
+
+
+
+// MAC type field values
+#define TYPE_ASSOC_REQ 0x00 // Association request
+#define TYPE_ASSOC_RESP 0x10 // Association response
+#define TYPE_REASSOC_REQ 0x20 // Reassociation request
+#define TYPE_REASSOC_RESP 0x30 // Reassociation response
+#define TYPE_PROBE_REQ 0x40 // Probe request
+#define TYPE_PROBE_RESP 0x50 // Probe response
+
+#define TYPE_BEACON 0x80 // Beacon
+#define TYPE_ATIM 0x90 // Annoucement traffice indication
+#define TYPE_DISASSOC 0xa0 // Disassociation
+#define TYPE_AUTH 0xb0 // Authentication
+#define TYPE_DEAUTH 0xc0 // Deauthentication
+#define TYPE_ACTION 0xd0 // Action
+
+#define TYPE_RTS 0xb4 // Request to send
+#define TYPE_CTS 0xc4 // Clear to send
+#define TYPE_ACK 0xd4 // Acknowledgement
+#define TYPE_PSPOLL 0xa4 // Power Save(PS)-Poll
+
+#define TYPE_DATA 0x08 // Data
+#define TYPE_NULL_DATA 0x48 // Null Data
+
+struct tx_frame_hdr {
+#if 1
+ unsigned int modulation_type:8;
+ unsigned int length:9;
+ unsigned int pad:15;
+#else
+ uint8_t modulation_type;
+ __le16 length:9;
+ unsigned int pad:15;
+#endif
+} __attribute__((packed));
+
+
+#define MOD_TYPE_PSKCCK 0x00
+#define MOD_TYPE_OFDM 0xee
+
+struct psk_cck_hdr {
+ uint8_t signal; /* x100Kbps */
+ uint8_t service;
+ __le16 length; /* usecs */
+} __attribute__((packed));
+
+/* PSK/CCK PLCP service field bits */
+#define PLCP_SERVICE_LOCKED 0x04 /* locked clocks */
+#define PLCP_SERVICE_MODSEL 0x08 /* modulation selection */
+#define PLCP_SERVICE_LENEXT 0x80 /* length extension */
+
+struct ofdm_hdr {
+ unsigned int rate:4;
+ unsigned int pad_a:1;
+ unsigned int length:12; /* in bytes */
+ unsigned int parity:1;
+ unsigned int pad_b:14;
+} __attribute__((packed));
+
+
+struct rx_frame_hdr {
+ uint8_t modulation_type;
+ unsigned int rssi_variable_gain_attenuator:5;
+ unsigned int rssi_low_noise_amp:2;
+ unsigned int antenna:1;
+ __be16 freq_offset;
+ union
+ {
+ struct psk_cck_hdr psk;
+ struct ofdm_hdr ofdm;
+ } mod;
+} __attribute__((packed));
+
+typedef struct
+{
+ unsigned type :8; // Type, subtype, version
+ unsigned toDS :1; // To distribution service (AP)
+ unsigned fromDS :1; // From distribution service (AP)
+ unsigned moreFrag :1; // More fragments
+ unsigned retry :1; // Retransmission
+ unsigned pwrMgt :1; // Power management state
+ unsigned moreData :1; // More data buffered
+ unsigned protectd :1; // Encrypted
+ unsigned order :1; // Strictly ordered
+} frameControlFieldType_t;
+
+#define WLN_ADDR_SIZE (6)
+
+typedef unsigned char MacAddr[WLN_ADDR_SIZE];
+
+
+#define PACKED_H
+#define PACKED_F __attribute__ ((packed))
+
+typedef PACKED_H struct {
+ unsigned type :8; // Type, subtype, version
+#ifdef BIG_ENDIAN
+ unsigned order :1; // Strictly ordered
+ unsigned protected :1; // Encrypted
+ unsigned moreData :1; // More data buffered
+ unsigned pwrMgt :1; // Power management state
+ unsigned retry :1; // Retransmission
+ unsigned moreFrag :1; // More fragments
+ unsigned fromDS :1; // From distribution service (AP)
+ unsigned toDS :1; // To distribution service (AP)
+#else
+ unsigned toDS :1; // To distribution service (AP)
+ unsigned fromDS :1; // From distribution service (AP)
+ unsigned moreFrag :1; // More fragments
+ unsigned retry :1; // Retransmission
+ unsigned pwrMgt :1; // Power management state
+ unsigned moreData :1; // More data buffered
+ unsigned protected :1; // Encrypted
+ unsigned order :1; // Strictly ordered
+#endif
+} PACKED_F FrameControl_t;
+
+
+// Sequence control field
+// Need to swap bytes on BIG_ENDIAN
+typedef PACKED_H struct {
+#ifdef BIG_ENDIAN
+ unsigned seq :12; // Sequence number
+ unsigned frag :4; // Fragment number
+#else
+ unsigned frag :4; // Fragment number
+ unsigned seq :12; // Sequence number
+#endif
+} PACKED_F SeqControl;
+
+// Union of sequence control types
+typedef PACKED_H union {
+ SeqControl sq; // Sequence control fields
+ unsigned short sq16; // Sequence control as 16-bit int (needs byte swap)
+} PACKED_F SeqControlU;
+
+
+
+typedef PACKED_H struct {
+ FrameControl_t fc; // Frame control
+ unsigned short duration; // Duration/ID (needs byte swap)
+ MacAddr addr1; // Address 1
+ MacAddr addr2; // Address 2
+ MacAddr addr3; // Address 3
+ SeqControlU squ; // Sequence control fields
+} PACKED_F _80211HeaderType;
+
+typedef PACKED_H struct {
+ FrameControl_t fc; // Frame control
+ unsigned short aid; // association identifier
+ MacAddr addr1; // Address 1
+ MacAddr addr2; // Address 2
+} PACKED_F _80211PSPollType;
+
+#define _80211_HEADER_LENGTH (sizeof(_80211HeaderType))
+#define TX_HEADER_LENGTH (sizeof(struct ofdm_hdr) + sizeof(struct tx_frame_hdr))
+/* FIFO sizes in bytes */
+#define TX_FIFO_SIZE 1792
+#define RX_FIFO_SIZE 2048
+
+#define RATE_MASK_BASIC 0x0153
+#define RATE_MASK_OFDM 0x0ff0
+#define RATE_MASK_PSK_CCK 0x000f
+
+#define BEACON_INT 100 /* in TU */
+
+
+#define DEFAULT_CW_MIN 32 // Min contention window size
+#define DEFAULT_CW_MAX 1024 // Max contention window size
+
+#define ASLOT_TIME 20
+#endif
diff --git a/drivers/net/wireless/digiPiper/phy.c b/drivers/net/wireless/digiPiper/phy.c
new file mode 100644
index 000000000000..5a6c53638e81
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/phy.c
@@ -0,0 +1,269 @@
+/*
+ * Linux device driver for Digi's WiWave WLAN card
+ *
+ * Copyright © 2008 Digi International, Inc
+ *
+ * Author: Andres Salomon <dilinger@debian.org>
+ *
+ * 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.
+ */
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <net/mac80211.h>
+
+#include "pipermain.h"
+#include "mac.h"
+#include "airoha.h"
+#include "phy.h"
+
+#define PHY_DEBUG (0)
+
+#if PHY_DEBUG
+static int dlevel = DVERBOSE;
+#define dprintk(level, fmt, arg...) if (level >= dlevel) \
+ printk(KERN_ERR PIPER_DRIVER_NAME \
+ ": %s - " fmt, __func__, ##arg)
+#else
+#define dprintk(level, fmt, arg...) do {} while (0)
+#endif
+
+#define NUMBER_OF_WORD32(x) ((x + 3) >> 2)
+
+static int is_ofdm_rate(int rate)
+{
+ return (rate % 3) == 0;
+}
+
+void phy_set_plcp(unsigned char *frame, unsigned length, struct ieee80211_rate *rate, int aes_len)
+{
+ int ofdm = is_ofdm_rate(rate->bitrate);
+ int plcp_len = length + FCS_LEN + aes_len;
+ struct tx_frame_hdr *hdr;
+
+ if (ofdm) {
+ /* OFDM header */
+ struct ofdm_hdr *ofdm;
+
+ ofdm = (struct ofdm_hdr *) &frame[sizeof(struct tx_frame_hdr)];
+ memset(ofdm, 0, sizeof(*ofdm));
+ ofdm->rate = rate->hw_value;
+ ofdm->length = cpu_to_le16(plcp_len);
+ } else {
+ /* PSK/CCK header */
+ struct psk_cck_hdr *pskcck;
+ int us_len;
+
+ pskcck = (struct psk_cck_hdr *) &frame[sizeof(struct tx_frame_hdr)];
+ pskcck->signal = rate->bitrate;
+ pskcck->service = PLCP_SERVICE_LOCKED;
+
+ /* convert length from bytes to usecs */
+ switch (rate->bitrate) {
+ case 10:
+ us_len = plcp_len * 8;
+ break;
+ case 20:
+ us_len = plcp_len * 4;
+ break;
+ case 55:
+ us_len = (16 * plcp_len + 10) / 11;
+ break;
+ case 110:
+ us_len = (8 * plcp_len + 10) / 11;
+
+ /* set length extension bit if needed */
+ dprintk(DALL, "us_len = %d, plcp_len = %d, (11 * us_len) = %d, \
+ (11 * us_len) / 8 = %d\n", us_len, plcp_len,
+ (11 * us_len), (11 * us_len) / 8);
+
+ if ((11 * us_len) / 8 > plcp_len) {
+ pskcck->service |= PLCP_SERVICE_LENEXT;
+ dprintk(DALL, "Set PLCP_SERVICE_LENEXT, \
+ pskcck->service = 0x%4.4X\n", pskcck->service);
+ } else {
+ dprintk(DALL, "Did not set PLCP_SERVICE_LENEXT, \
+ pskcck->service = 0x%4.4X\n", pskcck->service);
+ }
+ break;
+ default:
+ digi_dbg("rate = %p, rate->bitrate%d\n", rate, rate->bitrate);
+ WARN_ON(1);
+ us_len = 0;
+ }
+
+ pskcck->length = cpu_to_le16(us_len);
+
+ dprintk(DALL, "pskcck .length = %d, signal = %d, service = %d\n",
+ pskcck->length, pskcck->signal, pskcck->service);
+ dprintk(DALL, "rate->bitrate=%x (@%dM), pckcck->length=%d\n",
+ rate->bitrate, rate->bitrate/10, pskcck->length);
+ }
+
+ hdr = (struct tx_frame_hdr *) frame;
+ hdr->pad = 0;
+ hdr->length = NUMBER_OF_WORD32((length + aes_len + TX_HEADER_LENGTH));
+ hdr->modulation_type = ofdm ? MOD_TYPE_OFDM : MOD_TYPE_PSKCCK;
+
+ dprintk(DVVERBOSE, "frame hdr .length = %d, .modulation_type = %d\n",
+ hdr->length, hdr->modulation_type);
+
+ dprintk(DVERBOSE, "TX: %d byte %s packet @ %dmbit\n", length,
+ ofdm ? "OFDM" : "PSK/CCK", rate->bitrate/10);
+}
+EXPORT_SYMBOL_GPL(phy_set_plcp);
+
+
+static int get_signal(struct rx_frame_hdr *hdr, enum ieee80211_band rf_band, int transceiver)
+{
+ int gain;
+ int signal;
+
+ if (transceiver == RF_AIROHA_2236) {
+ const u8 lnaTable_al2236[] =
+ {
+ 0, 0, 20, 36
+ };
+
+ // Map high gain values to dbm
+ const signed char gainTable_al2236[] =
+ {
+ -85, -85, -88, -88, -92
+ };
+ // Convert received signal strength to dbm
+ gain = lnaTable_al2236[hdr->rssi_low_noise_amp] + 2*hdr->rssi_variable_gain_attenuator;
+ if (gain > 92)
+ signal = -96;
+ else if (gain > 87)
+ signal = gainTable_al2236[gain - 88];
+ else
+ signal = 4 - gain;
+ } else {
+ static const u8 lnaTable_al7230_24ghz[] =
+ {
+ 0, 0, 18, 42
+ };
+ static const u8 lnaTable_al7230_50ghz[] =
+ {
+ 0, 0, 17, 37
+ };
+ /* Convert received signal strength to dbm for RF_AIROHA_7230 */
+ if (rf_band == IEEE80211_BAND_2GHZ) {
+ gain = lnaTable_al7230_24ghz[hdr->rssi_low_noise_amp] + 2*hdr->rssi_variable_gain_attenuator;
+ signal = 2 - gain;
+ } else {
+ gain = lnaTable_al7230_50ghz[hdr->rssi_low_noise_amp] + 2*hdr->rssi_variable_gain_attenuator;
+ signal = -5 - gain;
+ }
+ }
+
+ return signal;
+}
+
+
+
+
+/* FIXME, following limtis should depend on the platform */
+#define PIPER_MAX_SIGNAL_DBM (-30)
+#define PIPER_MIN_SIGNAL_DBM (-90)
+
+static int calculate_link_quality(int signal)
+{
+ int quality;
+
+ if (signal < PIPER_MIN_SIGNAL_DBM)
+ quality = 0;
+ else if (signal > PIPER_MAX_SIGNAL_DBM)
+ quality = 100;
+ else
+ quality = (signal - PIPER_MIN_SIGNAL_DBM) * 100 /
+ (PIPER_MAX_SIGNAL_DBM - PIPER_MIN_SIGNAL_DBM);
+
+ dprintk(DVERBOSE, "signal: %d, quality: %d/100\n", signal, quality);
+
+ return quality;
+}
+
+unsigned int phy_determine_rssi(struct rx_frame_hdr *hdr)
+{
+ return (hdr->rssi_low_noise_amp << 5) | hdr->rssi_variable_gain_attenuator;
+}
+EXPORT_SYMBOL_GPL(phy_determine_rssi);
+
+
+
+void phy_process_plcp(struct piper_priv *piper, struct rx_frame_hdr *hdr,
+ struct ieee80211_rx_status *status, unsigned int *length)
+{
+ unsigned rate, i;
+ struct digi_rf_ops *rf = piper->rf;
+
+ memset(status, 0, sizeof(*status));
+ status->band = piper->rf->getBand(piper->channel);
+ status->signal = get_signal(hdr, status->band, piper->pdata->rf_transceiver);
+ status->antenna = hdr->antenna;
+ status->freq = piper->rf->getFrequency(piper->channel);
+ status->qual = calculate_link_quality(status->signal);
+
+ if (hdr->modulation_type == MOD_TYPE_OFDM) {
+ /* OFDM */
+ struct ofdm_hdr *ofdm = &hdr->mod.ofdm;
+ const int ofdm_rate[] = {
+ 480, 240, 120, 60, 540, 360, 180, 90
+ };
+
+ rate = ofdm_rate[ofdm->rate & 0x7];
+ *length = le16_to_cpu(ofdm->length);
+ dprintk(DVVERBOSE, "%d byte OFDM packet @ %dmbit\n",
+ *length, rate/10);
+ } else {
+ /* PSK/CCK */
+ struct psk_cck_hdr *pskcck = &hdr->mod.psk;
+
+ rate = pskcck->signal;
+
+ *length = le16_to_cpu(pskcck->length);
+ switch (rate) {
+ case 10:
+ *length /= 8;
+ break;
+ case 20:
+ *length /= 4;
+ break;
+ case 55:
+ *length = (11 * (*length)) / 16;
+ break;
+ case 110:
+ *length = (11 * (*length)) / 8;
+ if (pskcck->service & PLCP_SERVICE_LENEXT)
+ (*length)--;
+ break;
+ default:
+ /* WARN_ON(1); This happens to often for us to generate that long error message */
+ *length = 0;
+ }
+
+ dprintk(DVVERBOSE, "%d byte PSK/CCK packet @ %dmbit\n",
+ *length, rate/10);
+ }
+
+ /* match rate with the list of bitrates that we supplied the stack */
+ for (i = 0; i < rf->bands[status->band].n_bitrates; i++) {
+ if (rf->bands[status->band].bitrates[i].bitrate == rate)
+ break;
+ }
+
+ if (i != rf->bands[status->band].n_bitrates)
+ status->rate_idx = i;
+ else {
+ *length = 0;
+ digi_dbg(PIPER_DRIVER_NAME
+ ": couldn't find bitrate index for %d?\n",
+ rate);
+ status->flag |= RX_FLAG_FAILED_PLCP_CRC;
+ return;
+ }
+}
+EXPORT_SYMBOL_GPL(phy_process_plcp);
diff --git a/drivers/net/wireless/digiPiper/phy.h b/drivers/net/wireless/digiPiper/phy.h
new file mode 100644
index 000000000000..96500ab92e3e
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/phy.h
@@ -0,0 +1,28 @@
+/*
+ * Linux device driver for Digi's WiWave WLAN card
+ *
+ * Copyright © 2008 Digi International, Inc
+ *
+ * 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.
+ */
+
+#ifndef phy_h_
+#define phy_h_
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/module.h>
+#include <net/mac80211.h>
+
+
+#include "pipermain.h"
+#include "mac.h"
+
+void phy_set_plcp(unsigned char *frame, unsigned length, struct ieee80211_rate *rate, int aes_len);
+void phy_process_plcp(struct piper_priv *piper, struct rx_frame_hdr *hdr,
+ struct ieee80211_rx_status *status, unsigned int *length);
+unsigned int phy_determine_rssi(struct rx_frame_hdr *hdr);
+
+#endif
diff --git a/drivers/net/wireless/digiPiper/piper.c b/drivers/net/wireless/digiPiper/piper.c
new file mode 100644
index 000000000000..a3c24720dd11
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/piper.c
@@ -0,0 +1,1032 @@
+/*
+ * piper.c
+ *
+ * Copyright (C) 2008 by Digi International Inc.
+ * All rights reserved.
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <net/mac80211.h>
+#include <linux/usb.h>
+#include <linux/kthread.h>
+#include <linux/platform_device.h>
+#include <asm/gpio.h>
+#include <linux/timer.h>
+
+#include "pipermain.h"
+#include "mac.h"
+#include "phy.h"
+#include "airoha.h"
+#include "airohaCalibration.h"
+#include "piperDsp.h"
+#include "piperMacAssist.h"
+#include "digiPs.h"
+
+#define WANT_AIROHA_CALIBRATION (1)
+#define WANT_DEBUG_COMMANDS (1)
+
+
+static void piper_clear_irq_mask(struct piper_priv *piperp, unsigned int bits)
+{
+ piperp->ac->wr_reg(piperp, BB_IRQ_MASK, ~bits, op_and);
+}
+
+static void piper_set_irq_mask(struct piper_priv *piperp, unsigned int bits)
+{
+ piperp->ac->wr_reg(piperp, BB_IRQ_MASK, bits, op_or);
+}
+
+/* Generate a random number */
+static int local_rand(void)
+{
+ static unsigned long next = 1;
+
+ /* RAND_MAX assumed to be 32767 */
+ next = next * 1103515245 + 12345;
+ return((unsigned)(next/65536) % 32768);
+}
+
+/*
+ * Load the MAC Assist firmware into the chip. This is done by setting a bit
+ * in the control register to enable MAC Assist firmware download, and then
+ * writing the firmware into the data FIFO.
+ */
+void piper_load_mac_firmware(struct piper_priv *piperp)
+{
+ unsigned int i;
+
+ printk(KERN_DEBUG PIPER_DRIVER_NAME ": loading MAC Assist firmware\n");
+
+ /* Zero out MAC assist SRAM (put into known state before enabling MAC assist) */
+ for (i = 0; i < 0x100; i += 4)
+ piperp->ac->wr_reg(piperp, i, 0, op_write);
+
+ /* Enable download the MAC Assist program RAM */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, BB_GENERAL_CTL_FW_LOAD_ENABLE, op_or);
+
+ /* load MAC Assist data */
+ for (i = 0; i < piper_macassist_data_len; i++)
+ piperp->ac->wr_reg(piperp, BB_DATA_FIFO, piper_wifi_macassist_ucode[i],
+ op_write);
+
+ /* disable MAC Assist download */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, ~BB_GENERAL_CTL_FW_LOAD_ENABLE, op_and);
+}
+
+/*
+ * Load the DSP firmware into the chip. This is done by setting a bit
+ * in the control register to enable DSP firmware download, and then
+ * writing the firmware into the data FIFO.
+ */
+void piper_load_dsp_firmware(struct piper_priv *piperp)
+{
+ unsigned int i;
+
+ printk(KERN_DEBUG PIPER_DRIVER_NAME ": loading DSP firmware\n");
+
+ /* Enable load of DSP firmware */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, BB_GENERAL_CTL_DSP_LOAD_ENABLE, op_or);
+
+ /* load DSP data */
+ for (i = 0; i < piper_dsp_data_len; i++)
+ piperp->ac->wr_reg(piperp, BB_DATA_FIFO, piper_wifi_dsp_ucode[i],
+ op_write);
+
+ /* Disable load of DSP firmware */
+ udelay(10);
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, ~BB_GENERAL_CTL_DSP_LOAD_ENABLE, op_and);
+
+ /* Let her rip */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, BB_GENERAL_CTL_MAC_ASSIST_ENABLE, op_or);
+}
+
+
+/*
+ * This routine corrects a bug in the Piper chip where internal clocks would
+ * be out of sync with each other and cause the chip to generate noise spikes.
+ * This problem should be fixed in the next chip (Chopper).
+ *
+ * I'm not sure exactly what this code is doing. It comes straight from the
+ * guy who designed the chip.
+ */
+int piper_spike_suppression(struct piper_priv *piperp, bool retry)
+{
+ int timeout1 = 300, timeout2 = 300;
+ int ret = 0;
+
+ /*
+ * Initial timing measurement to avoid spike
+ * The new "magic" value is 0x63 at address 0xA62. Bit-0 indicates the
+ * timing measurement is complete. Bit-1 indicates that a second timing
+ * measurment was performed. The upper nibble is the timing measurement
+ * value. This code should eliminate the possibility of spikes at the
+ * beginning of all PSK/CCK frames and eliminate the spikes at the end of
+ * all PSK (1M, 2M) frames.
+ */
+
+ /* reset the timing value */
+ piperp->ac->wr_reg(piperp, MAC_STATUS, 0xffff00ff, op_and);
+
+ while ((piperp->ac->rd_reg(piperp, MAC_STATUS) & 0x0000ff00) != 0x00006300) {
+
+ /* reset the timing value */
+ piperp->ac->wr_reg(piperp, MAC_STATUS, 0xffff00ff, op_and);
+
+ /* issue WiFi soft reset */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_STAT, 0x40000000, op_write);
+
+ /* Set TX_ON Low */
+ piperp->ac->wr_reg(piperp, BB_OUTPUT_CONTROL, 0xffffff3f, op_and);
+ piperp->ac->wr_reg(piperp, BB_OUTPUT_CONTROL, 0x00000080, op_or);
+
+ /* Set PA_2G Low */
+ piperp->ac->wr_reg(piperp, BB_OUTPUT_CONTROL, 0xfffff0ff, op_and);
+ piperp->ac->wr_reg(piperp, BB_OUTPUT_CONTROL, 0x00000a00, op_or);
+
+ /* Set RX_ON low */
+ piperp->ac->wr_reg(piperp, BB_OUTPUT_CONTROL, 0xcfffffff, op_and);
+ piperp->ac->wr_reg(piperp, BB_OUTPUT_CONTROL, 0x20000000, op_or);
+
+ /* start the WiFi mac & dsp */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, 0x37720820, op_write);
+ timeout1 = 500;
+
+ /* Wait for timing measurement to finish */
+ while ((piperp->ac->rd_reg(piperp, MAC_STATUS) & 0x0000ff00) != 0x00000100) {
+ udelay(2);
+ timeout1--;
+ if (!timeout1)
+ break;
+ }
+
+ timeout2--;
+ if (!timeout2) {
+ ret = -EIO;
+ break;
+ }
+
+ if (!retry) {
+ ret = -EIO;
+ break;
+ }
+ }
+
+ /* Set TX_ON/RXHP_ON and RX to normal wifi, restore the reset value to HW_OUT_CTRL */
+ piperp->ac->wr_reg(piperp, BB_OUTPUT_CONTROL, 0x1, op_write);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(piper_spike_suppression);
+
+void piper_reset_mac(struct piper_priv *piperp)
+{
+ int i;
+
+ /* set the TX-hold bit */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, 0x37720080, op_write);
+
+ /* clear the TX-FIFO memory */
+ for (i = 0; i < 448; i++)
+ piperp->ac->wr_reg(piperp, BB_DATA_FIFO, 0, op_write);
+
+ /* reset the TX-FIFO */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, 0x377200C0, op_write);
+
+ /* release the TX-hold and reset */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, 0x37720000, op_write);
+
+/* iowrite32(ioread32(piperp->vbase + MAC_STATUS) & ~0x40000000,
+ piperp->vbase + BB_GENERAL_STAT);*/
+ mdelay(1);
+}
+
+/*
+ * Load the MAC address into the chip. Use the value stored in the
+ * environment, if there is one, otherwise use the default value.
+ */
+void piper_set_macaddr(struct piper_priv *piperp)
+{
+ /* Default MAC Addr used if the nvram parameters are corrupted */
+ u8 mac[6] = {0x00, 0x04, 0xf3, 0x11, 0x43, 0x35};
+ u8 *pmac = piperp->pdata->macaddr;
+ int i;
+ static bool firstTime = true;
+
+ for (i = 0; i < 6; i++) {
+ if (*(pmac + i) != 0xff)
+ break;
+ if (i == 5) {
+ /* There is a problem with the parameters, use default */
+ if (firstTime) {
+ printk(KERN_INFO PIPER_DRIVER_NAME
+ ": invalid mac address, using default\n");
+ }
+ memcpy(piperp->pdata->macaddr, mac, sizeof(piperp->pdata->macaddr));
+ }
+ }
+
+ firstTime = false;
+ memcpy(piperp->hw->wiphy->perm_addr, piperp->pdata->macaddr,
+ sizeof(piperp->hw->wiphy->perm_addr));
+
+ /* configure ethernet address */
+ piperp->ac->wr_reg(piperp, MAC_STA_ID0, *(pmac + 3) | *(pmac + 2) << 8 |
+ *(pmac + 1) << 16 | *(pmac + 0) << 24, op_write);
+ piperp->ac->wr_reg(piperp, MAC_STA_ID1, *(pmac + 5) << 16 | *(pmac + 4) << 24,
+ op_write);
+}
+EXPORT_SYMBOL_GPL(piper_set_macaddr);
+
+
+/* Configure the H/W with the antenna settings */
+static int piper_set_antenna(struct piper_priv *piperp, enum antenna_select sel)
+{
+ if (sel == ANTENNA_BOTH) {
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL,
+ BB_GENERAL_CTL_ANT_DIV, op_or);
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL,
+ ~BB_GENERAL_CTL_ANT_SEL, op_and);
+ } else {
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL,
+ ~BB_GENERAL_CTL_ANT_DIV, op_and);
+ /* select the antenna if !diversity */
+ if (sel == ANTENNA_1)
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL,
+ ~BB_GENERAL_CTL_ANT_SEL, op_and);
+ else
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL,
+ BB_GENERAL_CTL_ANT_SEL, op_or);
+ }
+
+ /* select which antenna to transmit on */
+ piperp->ac->wr_reg(piperp, BB_RSSI, ~BB_RSSI_ANT_MASK, op_and);
+ if (sel == ANTENNA_BOTH)
+ piperp->ac->wr_reg(piperp, BB_RSSI, BB_RSSI_ANT_DIV_MAP, op_or);
+ else
+ piperp->ac->wr_reg(piperp, BB_RSSI, BB_RSSI_ANT_NO_DIV_MAP, op_or);
+
+ return 0;
+}
+
+/*
+ * Compute a beacon backoff time as described in section 11.1.2.2 of 802.11 spec.
+ *
+ */
+static u16 get_next_beacon_backoff(void)
+{
+#define MAX_BEACON_BACKOFF (2 * ASLOT_TIME * DEFAULT_CW_MIN)
+
+ /*
+ * We shift the result of local_rand() by 4 bits because the notes
+ * for the algorithm say that we shouldn't rely on the last few
+ * bits being random. Other than that, we just take the random
+ * value and make sure it is less than MAX_BEACON_BACKOFF.
+ */
+ return (local_rand() >> 4) % MAX_BEACON_BACKOFF;
+}
+
+static int load_beacon(struct piper_priv *digi, unsigned char *buffer,
+ unsigned int length)
+{
+ return digi->ac->wr_fifo(digi, BEACON_FIFO, buffer, length);
+}
+
+static int piper_init_rx_tx(struct piper_priv *piperp)
+{
+ tasklet_init(&piperp->rx_tasklet, piper_rx_tasklet, (unsigned long)piperp);
+ tasklet_disable(&piperp->rx_tasklet);
+ piperp->expectingAck = false;
+
+ spin_lock_init(&piperp->tx_tasklet_lock);
+ spin_lock_init(&piperp->tx_queue_lock);
+ piperp->tx_tasklet_running = false;
+ memset(&piperp->tx_queue, 0, sizeof(piperp->tx_queue));
+ piperp->tx_queue_head = 0;
+ piperp->tx_queue_tail = 0;
+ piperp->tx_queue_count = 0;
+ tasklet_init(&piperp->tx_tasklet, piper_tx_tasklet, (unsigned long)piperp);
+ tasklet_disable(&piperp->tx_tasklet);
+
+ return 0;
+}
+
+static void piper_free_rx_tx(struct piper_priv *piperp)
+{
+ tasklet_disable(&piperp->rx_tasklet);
+ tasklet_kill(&piperp->rx_tasklet);
+ tasklet_disable(&piperp->tx_tasklet);
+ tasklet_kill(&piperp->tx_tasklet);
+ piper_empty_tx_queue(piperp);
+}
+
+/*
+ * This function sets the tracking control according to a channel's
+ * frequency.
+ */
+static int piper_set_tracking_constant(struct piper_priv *piperp, unsigned megahertz)
+{
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, ~TRACK_CONSTANT_MASK, op_and);
+ if (megahertz < 4920)
+ {
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, TRACK_BG_BAND, op_or);
+ }
+ else if (megahertz <= 4980)
+ {
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, TRACK_4920_4980_A_BAND, op_or);
+ }
+ else if (megahertz <= 5350)
+ {
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, TRACK_5150_5350_A_BAND, op_or);
+ }
+ else if (megahertz <= 5725)
+ {
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, TRACK_5470_5725_A_BAND, op_or);
+ }
+ else
+ {
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, TRACK_5725_5825_A_BAND, op_or);
+ }
+
+ return 0;
+}
+
+/*
+ * This function is called to set the value of the B_TX_GAIN field of the
+ * HW_CONF1 mac register. This register must be set to different values depending
+ * on the H/W revision of the board due to changes in the board design.
+ */
+static unsigned int get_b_tx_gain(struct piper_priv *piperp)
+{
+ u16 platform = piperp->pdata->wcd.header.hw_platform & WCD_PLATFORM_MASK;
+ u16 hw_revision = piperp->pdata->wcd.header.hw_platform & WCD_HW_REV_MASK;
+ unsigned int tx_gain = 0;
+
+ switch (platform) {
+ case WCD_CCW9P_PLATFORM:
+ tx_gain = TRACK_TX_B_GAIN_NORMAL;
+ break;
+ case WCD_CCW9M_PLATFORM:
+ switch (hw_revision) {
+ case WCD_HW_REV_PROTOTYPE:
+ case WCD_HW_REV_PILOT:
+ tx_gain = 0xc0000000;
+ break;
+ case WCD_HW_REV_A:
+ default:
+ tx_gain = 0x90000000;
+ break;
+ }
+ break;
+ }
+ return tx_gain;
+}
+
+
+static int piper_init_hw(struct piper_priv *piperp, enum ieee80211_band band)
+{
+ int ret = 0;
+
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, BB_GENERAL_CTL_INIT, op_write);
+
+ /* Initialize baseband general control register for the specific transceiver */
+ if (piperp->pdata->rf_transceiver == RF_AIROHA_7230) {
+ if (band == IEEE80211_BAND_2GHZ) {
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, GEN_INIT_AIROHA_24GHZ, op_write);
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, 0xff00ffff, op_and);
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, TRACK_BG_BAND, op_or);
+ digi_dbg("piper_init_hw Initialized for band B / BG\n");
+ } else {
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, GEN_INIT_AIROHA_50GHZ, op_write);
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, 0xff00ffff, op_and);
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, TRACK_5150_5350_A_BAND, op_or);
+ digi_dbg("piper_init_hw Initialized for band A\n");
+ }
+ piperp->ac->wr_reg(piperp, BB_CONF_2, 0x09325ad4, op_write);
+ /* Initialize the SPI word length */
+ piperp->ac->wr_reg(piperp, BB_SPI_CTRL, SPI_INIT_AIROHA, op_write);
+ } else if (piperp->pdata->rf_transceiver == RF_AIROHA_2236) {
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, GEN_INIT_AIROHA_24GHZ, op_write);
+ piperp->ac->wr_reg(piperp, BB_CONF_2, 0x09325ad4, op_write);
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, 0xff00ffff, op_and);
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, TRACK_BG_BAND, op_or);
+ /* Initialize the SPI word length */
+ piperp->ac->wr_reg(piperp, BB_SPI_CTRL, SPI_INIT_AIROHA2236, op_write);
+ } else {
+ printk(KERN_WARNING PIPER_DRIVER_NAME ": undefined rf transceiver!\n");
+ return -EINVAL;
+ }
+
+ /*
+ *Clear the Intretupt Mask Register before enabling external intretupts.
+ * Also clear out any status bits in the Intretupt Status Register.
+ */
+ piperp->ac->wr_reg(piperp, BB_IRQ_MASK, 0, op_write);
+ piperp->ac->wr_reg(piperp, BB_IRQ_STAT, 0xff, op_write);
+
+ /*
+ * If this firmware supports additional MAC addresses.
+ */
+ if (((piperp->ac->rd_reg(piperp, MAC_STATUS) >> 16) & 0xff) >= 8) {
+ /* Disable additional addresses to start with */
+ piperp->ac->wr_reg(piperp, MAC_CTL, ~MAC_CTL_MAC_FLTR, op_and);
+ piperp->ac->wr_reg(piperp, MAC_STA2_ID0, 0, op_write);
+ piperp->ac->wr_reg(piperp, MAC_STA2_ID1, 0, op_write);
+ piperp->ac->wr_reg(piperp, MAC_STA3_ID0, 0, op_write);
+ piperp->ac->wr_reg(piperp, MAC_STA3_ID1, 0, op_write);
+ }
+ /* TODO: Set this register programatically */
+ piperp->ac->wr_reg(piperp, MAC_DTIM_PERIOD, 0x0, op_write);
+
+ /*
+ * Note that antenna diversity will be set by hw_start, which is the
+ * caller of this function.
+ */
+
+ /* reset RX and TX FIFOs */
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, BB_GENERAL_CTL_RXFIFORST
+ | BB_GENERAL_CTL_TXFIFORST, op_or);
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, ~(BB_GENERAL_CTL_RXFIFORST
+ | BB_GENERAL_CTL_TXFIFORST), op_and);
+
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, 0xC043002C, op_write);
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, ~TRACK_TX_B_GAIN_MASK, op_and);
+ piperp->ac->wr_reg(piperp, BB_TRACK_CONTROL, get_b_tx_gain(piperp), op_or);
+
+ /* Initialize RF transceiver */
+ piperp->rf->init(piperp->hw, band);
+ piperp->ac->wr_reg(piperp, BB_OUTPUT_CONTROL, 0x04000001, op_or);
+ piperp->ac->wr_reg(piperp, MAC_CFP_ATIM, 0x0, op_write);
+ piperp->ac->wr_reg(piperp, BB_GENERAL_STAT, ~(BB_GENERAL_STAT_DC_DIS
+ | BB_GENERAL_STAT_SPRD_DIS), op_and);
+ piperp->ac->wr_reg(piperp, BB_GENERAL_STAT, ~(BB_GENERAL_STAT_SRC_DIS
+ | BB_GENERAL_STAT_DLL_DIS), op_and);
+
+ piperp->ac->wr_reg(piperp, MAC_SSID_LEN, (MAC_OFDM_BRS_MASK | MAC_PSK_BRS_MASK),
+ op_write);
+
+ /*
+ * Set BSSID to the broadcast address so that we receive all packets. The stack
+ * will set a real BSSID when it's ready.
+ */
+ piperp->ac->wr_reg(piperp, MAC_BSS_ID0, 0xffffffff, op_write);
+ piperp->ac->wr_reg(piperp, MAC_BSS_ID1, 0xffffffff, op_write);
+
+ piperp->ps.poweredDown = false;
+
+#if WANT_AIROHA_CALIBRATION
+ digi_dbg("Calling digiWifiInitCalibration()\n");
+ digiWifiInitCalibration(piperp);
+#endif
+
+ return ret;
+}
+
+static int piper_deinit_hw(struct piper_priv *piperp)
+{
+ int ret = 0;
+#if WANT_AIROHA_CALIBRATION
+ digi_dbg("Calling digiWifiDeInitCalibration()\n");
+ digiWifiDeInitCalibration(piperp);
+#endif
+
+ return ret;
+}
+
+
+static void adjust_max_agc(struct piper_priv *piperp, unsigned int rssi, _80211HeaderType *header)
+{
+#define LOWEST_MAXAGC_AL2236 0x76
+#define HIGHEST_MAXAGC_AL2236 0x7B
+#define HIGHEST_MAXAGC_AL7230_24GHZ 0x7c
+#define LOWEST_MAXAGC_AL7230_24GHZ 0x76
+#define HIGHEST_MAXAGC_AL7230_50GHZ 0x79
+#define LOWEST_MAXAGC_AL7230_50GHZ 0x73
+#define RSSI_AVG_COUNT 8
+
+ unsigned char maxgain = 0;
+ static unsigned char lowest = 0, highest = 0;
+ static int k=0, j=0, i =0, tempRssi=0;
+ static unsigned int savedRSSI[RSSI_AVG_COUNT]; /****/
+
+ savedRSSI[k % RSSI_AVG_COUNT] = rssi;
+ if ( (piperp->pdata->rf_transceiver == RF_AIROHA_2236)
+ || (piperp->pdata->rf_transceiver == RF_AIROHA_7230)) {
+
+ if (piperp->pdata->rf_transceiver == RF_AIROHA_2236)
+ {
+ lowest = LOWEST_MAXAGC_AL2236;
+ highest = HIGHEST_MAXAGC_AL2236;
+ }
+ else
+ {
+
+ if (piperp->rf->getBand(piperp->channel) == IEEE80211_BAND_5GHZ) {
+ highest = HIGHEST_MAXAGC_AL7230_50GHZ;
+ lowest = LOWEST_MAXAGC_AL7230_50GHZ;
+ }
+ else {
+ highest = HIGHEST_MAXAGC_AL7230_24GHZ;
+ lowest = LOWEST_MAXAGC_AL7230_24GHZ;
+ }
+ }
+
+ if (piperp->areWeAssociated)
+ {
+
+ if ( (piperp->if_type == NL80211_IFTYPE_ADHOC)
+ || (piperp->if_type == NL80211_IFTYPE_MESH_POINT))
+ {
+ //Monitor the receive signal strength from Ad-hoc network
+ if (memcmp (piperp->bssid, header->addr3, sizeof(piperp->bssid)) == 0)
+ {
+ /* we don't do avareging on all the signals here because it may come from different
+ * unit in that Ad-hoc network. Instead, we do avareging on the signals with higher rssi
+ */
+
+ if ((rssi + 4) > lowest)
+ {
+ k++;
+ tempRssi += rssi;
+
+ if (k >= RSSI_AVG_COUNT)
+ {
+ maxgain = (((tempRssi/k) + 4) > highest)? highest : ((tempRssi/k) + 4) ;
+ k = 0;
+ tempRssi = 0;
+ i =0;
+ }
+ }
+ else
+ {
+ i++;
+ if (i >= (RSSI_AVG_COUNT*4))
+ {
+ maxgain = lowest;
+ i = 0;
+ }
+
+ }
+ }
+ }
+ else
+ {
+ //Monitor the receive signal strength from the frames we received from the associated AP
+ if (memcmp (piperp->bssid, header->addr2, sizeof(piperp->bssid)) == 0)
+ {
+ //averaging all the signals because they come from the same AP
+ k++;
+ tempRssi += rssi;
+
+ if (k >= RSSI_AVG_COUNT*2)
+ {
+ if (((tempRssi/k) + 4) > lowest)
+ maxgain = (((tempRssi/k) + 4) > highest)? highest : ((tempRssi/k) + 4) ;
+ else
+ maxgain = lowest;
+
+ k = 0;
+ tempRssi = 0;
+ }
+ }
+ }
+ j = 0;
+ }
+ else
+ {
+ j++;
+ if (j >= (RSSI_AVG_COUNT*4))
+ {
+ maxgain = highest;
+ j = 0;
+ }
+ k = 0;
+ tempRssi = 0;
+ }
+
+ if( (maxgain != 0)
+ && (maxgain != ((piperp->ac->rd_reg(piperp, BB_GENERAL_CTL) & BB_GENERAL_CTL_MAX_GAIN_MASK) >> 16)))
+ {
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, ~BB_GENERAL_CTL_MAX_GAIN_MASK, op_and);
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, (maxgain << 16) & BB_GENERAL_CTL_MAX_GAIN_MASK, op_or);
+ }
+ }
+}
+
+
+/* Make sure all keys are disabled when we start */
+static void piper_init_keys(struct piper_priv *piperp)
+{
+ unsigned int i;
+
+ for (i = 0; i < PIPER_MAX_KEYS; i++)
+ piperp->key[i].valid = false;
+
+ piperp->aes_key_count = 0;
+}
+
+static void tx_timer_timeout(unsigned long arg)
+{
+ struct piper_priv *piperp = (struct piper_priv *) arg;
+
+ tasklet_hi_schedule(&piperp->tx_tasklet);
+}
+
+/* sysfs entries to get/set antenna mode */
+static ssize_t show_antenna_sel(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct piper_priv *piperp = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", piperp->antenna == ANTENNA_BOTH ? "diversity" :
+ piperp->antenna == ANTENNA_1 ? "primary" : "secondary");
+}
+
+static ssize_t store_antenna_sel(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct piper_priv *piperp = dev_get_drvdata(dev);
+ enum antenna_select ant;
+ size_t len = count;
+ int ret;
+
+ ant = piperp->antenna;
+
+ if (buf[count - 1] == '\n')
+ len--;
+
+ /* TODO check also string length */
+ if (!strncmp("diversity", buf, len))
+ ant = ANTENNA_BOTH;
+ else if (!strncmp("primary", buf, len))
+ ant = ANTENNA_1;
+ else if (!strncmp("secondary", buf, len))
+ ant = ANTENNA_2;
+
+ if (ant != piperp->antenna) {
+ if ((ret = piperp->set_antenna(piperp, ant)) != 0) {
+ printk(KERN_WARNING PIPER_DRIVER_NAME
+ ": error setting antenna to %d (err: %d)\n", ant, ret);
+ } else
+ piperp->antenna = ant;
+ }
+
+ return count;
+}
+static DEVICE_ATTR(antenna_sel, S_IWUSR | S_IRUGO, show_antenna_sel, store_antenna_sel);
+
+static ssize_t show_power_duty(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct piper_priv *piperp = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", piperp->power_duty);
+}
+
+static ssize_t store_power_duty(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+#define MINIMUM_DUTY_CYCLE (33)
+#define LIMIT_LINEAL_DUTY_CYCLE (75)
+ struct piper_priv *piperp = dev_get_drvdata(dev);
+ int pw_duty;
+ ssize_t ret = -EINVAL;
+
+ ret = sscanf(buf, "%d\n", &pw_duty);
+ if (ret > 0) {
+ if (pw_duty < MINIMUM_DUTY_CYCLE) {
+ piperp->power_duty = MINIMUM_DUTY_CYCLE;
+ } else if (pw_duty > LIMIT_LINEAL_DUTY_CYCLE && pw_duty < 100) {
+ piperp->power_duty = LIMIT_LINEAL_DUTY_CYCLE;
+ } else if (pw_duty == 100 ||
+ (pw_duty >= MINIMUM_DUTY_CYCLE && pw_duty <= LIMIT_LINEAL_DUTY_CYCLE)) {
+ piperp->power_duty = pw_duty;
+ }
+ }
+
+ return ret < 0 ? ret : count;
+}
+static DEVICE_ATTR(power_duty, S_IWUSR | S_IRUGO, show_power_duty, store_power_duty);
+
+#if WANT_DEBUG_COMMANDS
+
+static ssize_t show_debug_cmd(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct piper_priv *piperp = dev_get_drvdata(dev);
+
+ return snprintf(buf, PAGE_SIZE, "%s\n", piperp->debug_cmd);
+}
+
+static ssize_t store_debug_cmd(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct piper_priv *piperp = dev_get_drvdata(dev);
+ ssize_t ret = -EINVAL;
+
+ if (strlen(buf) < sizeof(piperp->debug_cmd))
+ {
+ if (strstr(buf, "dump") != NULL) {
+ digiWifiDumpRegisters(piperp, MAIN_REGS | MAC_REGS);
+ ret = 1;
+ } else if (strstr(buf, "ps_state") != NULL) {
+ printk(KERN_ERR "rxTaskletRunning = %d, allowTransmits = %d, stopped_tx_queues = %d\n",
+ piperp->ps.rxTaskletRunning, piperp->ps.allowTransmits, piperp->ps.stopped_tx_queues);
+ ret = 1;
+ } else if (strstr(buf, "rssi_dump") != NULL) {
+ spinlock_t lock;
+ unsigned long flags;
+ unsigned int rssi;
+
+ spin_lock_init(&lock);
+ spin_lock_irqsave(&piperp->ps.lock, flags);
+ piperp->ac->wr_reg(piperp, BB_GENERAL_CTL, ~BB_GENERAL_CTL_RX_EN, op_and);
+ udelay(15);
+ rssi = piperp->ac->rd_reg(piperp, BB_RSSI);
+ printk(KERN_ERR "\n**rssi = 0x%8.8X\n", rssi);
+ digiWifiDumpRegisters(piperp, MAIN_REGS | MAC_REGS);
+ ret = 1;
+ spin_unlock_irqrestore(&lock, flags);
+ } else {
+ strcpy(piperp->debug_cmd, buf);
+ piperp->debug_cmd[strlen(buf)-1] = 0; /* truncate the \n */
+ }
+ ret = count;
+ }
+
+ return ret < 0 ? ret : count;
+}
+static DEVICE_ATTR(debug_cmd, S_IWUSR | S_IRUGO, show_debug_cmd, store_debug_cmd);
+
+#endif
+
+#ifdef CONFIG_PM
+
+static int piper_suspend(struct platform_device *dev, pm_message_t state)
+{
+ struct piper_priv *piperp = platform_get_drvdata(dev);
+ unsigned long flags;
+
+ /* TODO, use in future the ps.lock instead of fully disabling interrupts here */
+ piperp->power_save_was_on_when_suspended = (piperp->ps.mode == PS_MODE_LOW_POWER);
+ if (piperp->power_save_was_on_when_suspended)
+ piper_ps_set(piperp, false);
+ mdelay(10);
+ piper_sendNullDataFrame(piperp, true);
+ ssleep(1);
+
+ local_irq_save(flags);
+ /*
+ * Save power save state and then make sure power save is turned off.
+ */
+ piper_MacEnterSleepMode(piperp, true);
+ local_irq_restore(flags);
+
+ return 0;
+}
+
+static int piper_resume(struct platform_device *dev)
+{
+ struct piper_priv *piperp = platform_get_drvdata(dev);
+ unsigned long flags;
+
+ if (piperp->pdata->early_resume)
+ piperp->pdata->early_resume(piperp);
+
+ /* TODO, use in future the ps.lock instead of fully disabling interrupts here */
+ local_irq_save(flags);
+ piper_MacEnterActiveMode(piperp, true);
+ if (piperp->tx_tasklet_running) {
+ tasklet_hi_schedule(&piperp->tx_tasklet);
+ } else {
+ ieee80211_wake_queues(piperp->hw);
+ }
+ local_irq_restore(flags);
+
+ /*
+ * Restore power save if it was on before
+ */
+ if (piperp->power_save_was_on_when_suspended) {
+ piper_ps_set(piperp, true);
+ } else {
+ piper_sendNullDataFrame(piperp, false);
+ }
+
+ return 0;
+}
+#else
+#define piper_suspend NULL
+#define piper_resume NULL
+#endif
+
+static int __init piper_probe(struct platform_device* pdev)
+{
+ struct piper_pdata *pdata = pdev->dev.platform_data;
+ struct piper_priv *piperp;
+ int ret = 0;
+
+ if (!pdata)
+ return -EINVAL;
+
+ ret = piper_alloc_hw(&piperp, sizeof(*piperp));
+ if (ret) {
+ printk(KERN_ERR PIPER_DRIVER_NAME ": failed to alloc piper_priv\n");
+ return ret;
+ }
+
+ piperp->ac = kzalloc(sizeof(struct access_ops), GFP_KERNEL);
+ if (!piperp->ac){
+ printk(KERN_ERR PIPER_DRIVER_NAME ": failed to alloc memory for ac struct\n");
+ ret = -ENOMEM;
+ goto error_alloc;
+ }
+
+ piperp->drv_name = PIPER_DRIVER_NAME;
+ dev_set_drvdata(&pdev->dev, piperp);
+ piperp->pdata = pdata;
+ pdata->piperp = piperp;
+ spin_lock_init(&piperp->ac->reg_lock);
+ spin_lock_init(&piperp->aesLock);
+
+ piperp->vbase = ioremap(pdev->resource[0].start,
+ pdev[0].resource->end - pdev->resource[0].start);
+
+ if (!piperp->vbase) {
+ printk(KERN_ERR PIPER_DRIVER_NAME ": ioremap base %x, len %x error\n",
+ pdev->resource[0].start, pdev[0].resource->end - pdev->resource[0].start);
+ ret = -ENOMEM;
+ goto error_remap;
+ }
+
+ piperp->pstats.tx_start_count = 0;
+ piperp->pstats.tx_complete_count = 0;
+
+ /*
+ * Platform initialization. This will initialize the hardware, including the load
+ * of the mac and dsp firmware into the piper chip
+ */
+ if (pdata->init) {
+ if ((ret = pdata->init(piperp)) != 0) {
+ printk(KERN_ERR PIPER_DRIVER_NAME
+ ": platform init() returned error (%d)\n", ret);
+ goto error_init;
+ }
+ }
+
+ piper_ps_init(piperp);
+ init_timer(&piperp->tx_timer);
+ piperp->tx_timer.function = tx_timer_timeout;
+ piperp->tx_timer.data = (unsigned long) piperp;
+ piper_init_rx_tx(piperp);
+ piper_init_keys(piperp);
+
+ piperp->init_hw = piper_init_hw;
+ piperp->deinit_hw = piper_deinit_hw;
+ piperp->set_irq_mask_bit = piper_set_irq_mask;
+ piperp->clear_irq_mask_bit = piper_clear_irq_mask;
+ piperp->load_beacon = load_beacon;
+ piperp->rand = local_rand;
+ piperp->get_next_beacon_backoff = get_next_beacon_backoff;
+ piperp->set_antenna = piper_set_antenna;
+ piperp->set_tracking_constant = piper_set_tracking_constant;
+ piperp->antenna = ANTENNA_1;
+ piperp->adjust_max_agc = adjust_max_agc;
+
+ /*
+ * Set the default duty cycle value. Note that duty cycling
+ * is disabled reguardless of what this variable is set to until
+ * the user types "iwconfig wlan0 power on". I just love the
+ * "power on" syntax to turn *down* the power.
+ */
+ piperp->power_duty = 100;
+
+ /* TODO this should be read earlier and actions should be taken
+ * based on different revisions at driver initialization or runtime */
+ piperp->version = piperp->ac->rd_reg(piperp, BB_VERSION);
+
+ piperp->irq = pdev->resource[1].start;
+ piperp->tx_cts = false;
+ piperp->beacon.loaded = false;
+ piperp->beacon.enabled = false;
+ piperp->beacon.weSentLastOne = false;
+
+ ret = request_irq(piperp->irq, piper_irq_handler,
+ IRQF_TRIGGER_HIGH, PIPER_DRIVER_NAME, piperp);
+ if (ret) {
+ printk(KERN_ERR PIPER_DRIVER_NAME ": unable to request irq %d (%d)",
+ piperp->irq, ret);
+ goto retor_irq;
+ }
+
+ disable_irq(piperp->irq);
+
+ ret = piper_register_hw(piperp, &pdev->dev, &al7230_rf_ops);
+ if (ret) {
+ printk(KERN_ERR PIPER_DRIVER_NAME ": failed to register priv\n");
+ goto error_reg_hw;
+ }
+
+ if (pdata->late_init)
+ pdata->late_init(piperp);
+
+ ret = device_create_file(&pdev->dev, &dev_attr_antenna_sel);
+ if (ret) {
+ printk(KERN_ERR PIPER_DRIVER_NAME ": failed to create sysfs file\n");
+ goto error_sysfs;
+ }
+
+ ret = device_create_file(&pdev->dev, &dev_attr_power_duty);
+ if (ret) {
+ printk(KERN_ERR PIPER_DRIVER_NAME ": failed to create sysfs file\n");
+ goto error_sysfs;
+ }
+
+ strcpy(piperp->debug_cmd, "off");
+#if WANT_DEBUG_COMMANDS
+ ret = device_create_file(&pdev->dev, &dev_attr_debug_cmd);
+ if (ret) {
+ printk(KERN_ERR PIPER_DRIVER_NAME ": failed to create sysfs file\n");
+ goto error_sysfs;
+ }
+#endif
+
+ printk(KERN_INFO PIPER_DRIVER_NAME ": driver loaded (fw ver = 0x%08x)\n",
+ piperp->version);
+
+ return 0;
+
+error_sysfs:
+ piper_unregister_hw(piperp);
+error_reg_hw:
+ piper_ps_deinit(piperp);
+ piper_free_rx_tx(piperp);
+retor_irq:
+ free_irq(piperp->irq, piperp);
+error_init:
+ iounmap(piperp->vbase);
+ piperp->vbase = NULL;
+error_remap:
+ release_resource(pdev->resource);
+error_alloc:
+ piper_free_hw(piperp);
+ return ret;
+}
+
+static int piper_remove(struct platform_device *pdev)
+{
+ struct piper_priv *piperp = dev_get_drvdata(&pdev->dev);
+
+ printk(KERN_DEBUG PIPER_DRIVER_NAME " %s\n", __func__);
+
+ device_remove_file(&pdev->dev, &dev_attr_antenna_sel);
+ device_remove_file(&pdev->dev, &dev_attr_power_duty);
+#if WANT_DEBUG_COMMANDS
+ device_remove_file(&pdev->dev, &dev_attr_debug_cmd);
+#endif
+
+ piper_ps_deinit(piperp);
+ piper_unregister_hw(piperp);
+ disable_irq(piperp->irq);
+ piper_clear_irq_mask(piperp, 0xffffffff);
+ free_irq(piperp->irq, piperp);
+ piper_free_rx_tx(piperp);
+ release_resource(pdev->resource);
+ piper_free_hw(piperp);
+
+ return 0;
+}
+
+/* describes the driver */
+static struct platform_driver piper_driver = {
+ .probe = piper_probe,
+ .remove = piper_remove,
+ .suspend = piper_suspend,
+ .resume = piper_resume,
+ .driver = {
+ .name = PIPER_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init piper_init_module(void)
+{
+ return platform_driver_register(&piper_driver);
+}
+
+static void __exit piper_exit_module(void)
+{
+ platform_driver_unregister(&piper_driver);
+}
+
+module_init(piper_init_module);
+module_exit(piper_exit_module);
+
+MODULE_DESCRIPTION("Digi Piper WLAN Driver");
+MODULE_AUTHOR("Contact support@digi.com for questions on this code");
+MODULE_VERSION(DRV_VERS);
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wireless/digiPiper/piperDsp.c b/drivers/net/wireless/digiPiper/piperDsp.c
new file mode 100644
index 000000000000..0f3d0e1b9b5e
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/piperDsp.c
@@ -0,0 +1,285 @@
+/*
+ Copyright (c) 2007-2008 Digi International Inc., All Rights Reserved
+
+ This software contains proprietary and confidential information of Digi
+ International Inc. By accepting transfer of this copy, Recipient agrees
+ to retain this software in confidence, to prevent disclosure to others,
+ and to make no use of this software other than that for which it was
+ delivered. This is an unpublished copyrighted work of Digi International
+ Inc. Except as permitted by federal law, 17 USC 117, copying is strictly
+ prohibited.
+
+ Restricted Rights Legend
+
+ Use, duplication, or disclosure by the Government is subject to
+ restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
+ Technical Data and Computer Software clause at DFARS 252.227-7031 or
+ subparagraphs (c)(1) and (2) of the Commercial Computer Software -
+ Restricted Rights at 48 CFR 52.227-19, as applicable.
+
+ Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
+
+ WiFi DSP Code for Piper
+*/
+
+
+const unsigned long piper_wifi_dsp_ucode[1024] = {
+ 0x32000, 0x3200F, 0x320AB, 0x0403F,
+ 0x0C000, 0x38000, 0x38060, 0x10004,
+ 0x10008, 0x10009, 0x1000A, 0x1000B,
+ 0x10001, 0x10010, 0x08000, 0x08000,
+ 0x10001, 0x08000, 0x08000, 0x08000,
+ 0x08000, 0x08000, 0x08000, 0x08000,
+ 0x32030, 0x39010, 0x39010, 0x30802,
+ 0x2B030, 0x10001, 0x10800, 0x38000,
+ 0x10004, 0x10040, 0x11002, 0x320D1,
+ 0x041AD, 0x0C000, 0x38000, 0x38160,
+ 0x38400, 0x061AD, 0x38100, 0x39171,
+ 0x39571, 0x32072, 0x11002, 0x320E1,
+ 0x38100, 0x39171, 0x39571, 0x36002,
+ 0x29039, 0x38000, 0x11002, 0x38000,
+ 0x38060, 0x10001, 0x083D5, 0x10050,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x10104, 0x10013, 0x321D1, 0x0422D,
+ 0x0E000, 0x38000, 0x38260, 0x38400,
+ 0x0622D, 0x0C000, 0x38200, 0x39271,
+ 0x39671, 0x32002, 0x10003, 0x321E1,
+ 0x38200, 0x39271, 0x39671, 0x10013,
+ 0x321E1, 0x38200, 0x39271, 0x39671,
+ 0x36002, 0x29034, 0x38000, 0x10003,
+ 0x10001, 0x38060, 0x083B1, 0x10050,
+ 0x10104, 0x10040, 0x10213, 0x320D1,
+ 0x0422D, 0x0E000, 0x38000, 0x38260,
+ 0x38400, 0x0622D, 0x0C000, 0x38200,
+ 0x39271, 0x39671, 0x10003, 0x321E1,
+ 0x38200, 0x39271, 0x39671, 0x10013,
+ 0x321E1, 0x38200, 0x39271, 0x39671,
+ 0x10003, 0x10001, 0x38060, 0x10004,
+ 0x10000, 0x08392, 0x10010, 0x08380,
+ 0x30802, 0x29003, 0x37012, 0x082BF,
+ 0x38000, 0x29002, 0x37012, 0x08078,
+ 0x29002, 0x37012, 0x08105, 0x29002,
+ 0x37012, 0x081C2, 0x29002, 0x37172,
+ 0x0822F, 0x04236, 0x10801, 0x29002,
+ 0x370F2, 0x0838A, 0x29002, 0x370F2,
+ 0x083A7, 0x29002, 0x370F2, 0x083C4,
+ 0x38000, 0x08002, 0x38000, 0x38000,
+ 0x30822, 0x10104, 0x10040, 0x10213,
+ 0x320D1, 0x0422D, 0x0E000, 0x38000,
+ 0x38260, 0x38400, 0x0622D, 0x0C000,
+ 0x38200, 0x39271, 0x39671, 0x10003,
+ 0x321E1, 0x38200, 0x39271, 0x39671,
+ 0x10013, 0x321E1, 0x38200, 0x39271,
+ 0x39671, 0x10003, 0x38000, 0x38060,
+ 0x36002, 0x2A005, 0x0422D, 0x38400,
+ 0x0402D, 0x38060, 0x38400, 0x10004,
+ 0x10000, 0x10001, 0x08349, 0x10010,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x3601F, 0x30033, 0x23103, 0x38000,
+ 0x08030, 0x38000, 0x08031, 0x38000,
+ 0x327F0, 0x300F1, 0x37091, 0x21034,
+ 0x30803, 0x300F2, 0x23009, 0x32081,
+ 0x10002, 0x04180, 0x0C100, 0x38000,
+ 0x38040, 0x04400, 0x320A1, 0x38060,
+ 0x04900, 0x0E630, 0x38000, 0x38000,
+ 0x38060, 0x38000, 0x2103E, 0x38060,
+ 0x04000, 0x0C000, 0x300F1, 0x22004,
+ 0x37041, 0x23003, 0x04401, 0x38000,
+ 0x38020, 0x3601F, 0x30033, 0x2310C,
+ 0x38000, 0x082EA, 0x10010, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x38000, 0x10800, 0x38000,
+ 0x300F2, 0x32081, 0x35021, 0x20017,
+ 0x300F2, 0x37052, 0x28036, 0x0440A,
+ 0x0C000, 0x38000, 0x38060, 0x30011,
+ 0x20004, 0x0440A, 0x0E000, 0x2103F,
+ 0x38060, 0x05410, 0x0E000, 0x38000,
+ 0x38060, 0x08005, 0x38000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x04000,
+ 0x10008, 0x05C02, 0x0C000, 0x38000,
+ 0x38020, 0x04C12, 0x0E000, 0x10002,
+ 0x38020, 0x0C209, 0x04914, 0x38000,
+ 0x0C409, 0x38040, 0x38000, 0x38000,
+ 0x38020, 0x32011, 0x36001, 0x0E000,
+ 0x05E02, 0x38000, 0x38120, 0x0E201,
+ 0x04914, 0x38000, 0x0E401, 0x38040,
+ 0x38000, 0x38020, 0x21034, 0x38000,
+ 0x10202, 0x04924, 0x38000, 0x38400,
+ 0x10202, 0x041A4, 0x0C100, 0x38060,
+ 0x04424, 0x38060, 0x04924, 0x0E630,
+ 0x320A1, 0x38000, 0x38060, 0x38000,
+ 0x2103E, 0x38060, 0x04403, 0x0C000,
+ 0x300F0, 0x38020, 0x0E000, 0x37030,
+ 0x39030, 0x39030, 0x0401B, 0x10401,
+ 0x38040, 0x10001, 0x05C1C, 0x38000,
+ 0x38040, 0x08282, 0x10010, 0x00000,
+ 0x10023, 0x10104, 0x0422D, 0x0C080,
+ 0x32180, 0x38260, 0x0E080, 0x38200,
+ 0x39270, 0x39270, 0x05C2E, 0x0C000,
+ 0x38060, 0x100D3, 0x0422D, 0x0C080,
+ 0x32180, 0x38260, 0x0E080, 0x38200,
+ 0x39270, 0x39270, 0x05C2E, 0x0E000,
+ 0x38060, 0x10004, 0x10003, 0x10402,
+ 0x0422D, 0x0C000, 0x323F0, 0x38200,
+ 0x39270, 0x39770, 0x04401, 0x0C000,
+ 0x1000B, 0x38020, 0x06409, 0x38020,
+ 0x38020, 0x38020, 0x1000A, 0x08254,
+ 0x10010, 0x38000, 0x38000, 0x38000,
+ 0x10462, 0x10014, 0x041AD, 0x0C000,
+ 0x32180, 0x38160, 0x0E000, 0x38100,
+ 0x39170, 0x39170, 0x05C2E, 0x0C000,
+ 0x38060, 0x10512, 0x041AD, 0x0C000,
+ 0x32180, 0x38160, 0x0E000, 0x38100,
+ 0x39170, 0x39170, 0x05C2E, 0x0E000,
+ 0x38060, 0x10004, 0x05036, 0x0E008,
+ 0x10212, 0x38060, 0x38400, 0x04427,
+ 0x0C000, 0x38060, 0x0403F, 0x38000,
+ 0x38020, 0x0493F, 0x0E630, 0x32051,
+ 0x38000, 0x38060, 0x38000, 0x2103E,
+ 0x38060, 0x300F1, 0x37061, 0x0443E,
+ 0x0C000, 0x38020, 0x0643F, 0x0C000,
+ 0x38020, 0x38020, 0x38020, 0x0563F,
+ 0x0E000, 0x2000A, 0x38000, 0x21004,
+ 0x38020, 0x04C37, 0x08014, 0x38020,
+ 0x2000C, 0x38000, 0x21006, 0x38000,
+ 0x04637, 0x38020, 0x05C37, 0x0800B,
+ 0x38020, 0x38020, 0x21005, 0x04437,
+ 0x38020, 0x05637, 0x08004, 0x38020,
+ 0x05C37, 0x38020, 0x38000, 0x11102,
+ 0x041B6, 0x0C000, 0x38000, 0x38020,
+ 0x04E37, 0x0E000, 0x38020, 0x05C3F,
+ 0x0C000, 0x38020, 0x04438, 0x0E000,
+ 0x38020, 0x04000, 0x38000, 0x10008,
+ 0x04401, 0x0C000, 0x38020, 0x06439,
+ 0x0E000, 0x38020, 0x38020, 0x38020,
+ 0x38020, 0x38020, 0x38020, 0x04439,
+ 0x38000, 0x38020, 0x04409, 0x0C000,
+ 0x1000B, 0x06409, 0x38020, 0x38020,
+ 0x38020, 0x1000A, 0x10212, 0x04136,
+ 0x0C800, 0x38000, 0x38060, 0x11112,
+ 0x041B6, 0x0E000, 0x38000, 0x38060,
+ 0x25004, 0x38000, 0x10800, 0x38000,
+ 0x38000, 0x10400, 0x081CD, 0x38000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x10422, 0x10023, 0x10114, 0x04BAD,
+ 0x0C080, 0x32190, 0x38300, 0x39370,
+ 0x39F70, 0x104D2, 0x100D3, 0x32190,
+ 0x38300, 0x39370, 0x39F70, 0x38000,
+ 0x10433, 0x0422D, 0x0C000, 0x32030,
+ 0x39270, 0x39A10, 0x10063, 0x10334,
+ 0x32030, 0x39270, 0x39A10, 0x10512,
+ 0x041AD, 0x32020, 0x39170, 0x39510,
+ 0x10782, 0x10114, 0x32020, 0x39170,
+ 0x39510, 0x104D3, 0x0422D, 0x0C070,
+ 0x10334, 0x38260, 0x0E070, 0x32060,
+ 0x39210, 0x39270, 0x107B2, 0x104D3,
+ 0x10020, 0x05BAD, 0x0E060, 0x32190,
+ 0x38300, 0x39370, 0x39B70, 0x13FC3,
+ 0x0422D, 0x0C070, 0x10114, 0x38260,
+ 0x0E070, 0x32060, 0x39210, 0x39270,
+ 0x104E2, 0x13FC3, 0x05BAD, 0x0E060,
+ 0x32190, 0x38300, 0x39370, 0x39B70,
+ 0x10004, 0x08326, 0x10000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x10114, 0x10173, 0x10902, 0x0616D,
+ 0x0C800, 0x321B0, 0x38260, 0x38260,
+ 0x38260, 0x39270, 0x39770, 0x0416D,
+ 0x321B0, 0x10173, 0x10912, 0x38260,
+ 0x38260, 0x38260, 0x39270, 0x39770,
+ 0x10003, 0x10C42, 0x0616D, 0x0C800,
+ 0x321B0, 0x38260, 0x38260, 0x38260,
+ 0x39270, 0x39770, 0x0416D, 0x321B0,
+ 0x10003, 0x10C52, 0x38260, 0x38260,
+ 0x38260, 0x39270, 0x39770, 0x32028,
+ 0x0C070, 0x10973, 0x0423F, 0x38060,
+ 0x10B33, 0x0E070, 0x108E3, 0x38060,
+ 0x38060, 0x0E060, 0x10AA3, 0x38000,
+ 0x38060, 0x0C000, 0x0503F, 0x10222,
+ 0x38400, 0x080C6, 0x38000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x108D3, 0x10104, 0x10140, 0x040F6,
+ 0x0C202, 0x32181, 0x38200, 0x38080,
+ 0x382C0, 0x380A0, 0x216FF, 0x380A0,
+ 0x10823, 0x0C202, 0x32181, 0x38200,
+ 0x38080, 0x382C0, 0x380A0, 0x216FF,
+ 0x380A0, 0x080FA, 0x10010, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x10902, 0x108D3, 0x10114, 0x10140,
+ 0x047F6, 0x0D202, 0x32181, 0x100E1,
+ 0x38280, 0x38040, 0x381A0, 0x216FE,
+ 0x38440, 0x10C42, 0x10823, 0x047F6,
+ 0x0D202, 0x32171, 0x38280, 0x38040,
+ 0x381A0, 0x216FE, 0x38440, 0x381A0,
+ 0x386E0, 0x321A1, 0x2103F, 0x10001,
+ 0x38400, 0x080D2, 0x10010, 0x00000,
+ 0x38000, 0x38000, 0x38000, 0x0423F,
+ 0x10973, 0x0C000, 0x38060, 0x10B33,
+ 0x0E000, 0x108E3, 0x38060, 0x38060,
+ 0x0E030, 0x10AA3, 0x38000, 0x38060,
+ 0x18083, 0x0C0C0, 0x0443F, 0x36018,
+ 0x38060, 0x21002, 0x0C000, 0x32018,
+ 0x0503F, 0x10222, 0x38400, 0x041A4,
+ 0x0C100, 0x38060, 0x04424, 0x38060,
+ 0x048A4, 0x0E630, 0x32061, 0x38000,
+ 0x38060, 0x38000, 0x2103E, 0x38060,
+ 0x10080, 0x04401, 0x0C000, 0x38020,
+ 0x06639, 0x0E000, 0x38020, 0x04409,
+ 0x0C000, 0x1000B, 0x06409, 0x38020,
+ 0x38020, 0x38020, 0x1000A, 0x30811,
+ 0x22007, 0x37021, 0x20003, 0x38000,
+ 0x08383, 0x38000, 0x083A1, 0x38000,
+ 0x10902, 0x108D3, 0x10104, 0x10140,
+ 0x047F6, 0x0D202, 0x32181, 0x100E1,
+ 0x38080, 0x38240, 0x381A0, 0x38460,
+ 0x214FD, 0x38740, 0x10C42, 0x10823,
+ 0x047F6, 0x0D202, 0x32171, 0x38080,
+ 0x38240, 0x381A0, 0x38460, 0x214FD,
+ 0x38740, 0x381A0, 0x38460, 0x384E0,
+ 0x38000, 0x38000, 0x38000, 0x38000,
+ 0x38000, 0x38000, 0x38000, 0x38000,
+ 0x38000, 0x38400, 0x10001, 0x08068,
+ 0x10010, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x041A4, 0x0C100, 0x38000, 0x38060,
+ 0x04424, 0x32061, 0x38060, 0x04924,
+ 0x0E630, 0x38000, 0x38000, 0x38060,
+ 0x38000, 0x2103E, 0x38060, 0x10080,
+ 0x108D3, 0x10104, 0x10040, 0x040F6,
+ 0x0C202, 0x32181, 0x38200, 0x38200,
+ 0x38200, 0x21240, 0x21640, 0x10823,
+ 0x32181, 0x38200, 0x38200, 0x38200,
+ 0x21240, 0x21640, 0x04401, 0x0C000,
+ 0x38020, 0x06639, 0x0E000, 0x38020,
+ 0x04409, 0x0C000, 0x1000B, 0x06409,
+ 0x38020, 0x38020, 0x38020, 0x1000A,
+ 0x0801F, 0x10010, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000
+};
+
+const int piper_dsp_data_len = (sizeof piper_wifi_dsp_ucode)/4;
diff --git a/drivers/net/wireless/digiPiper/piperDsp.h b/drivers/net/wireless/digiPiper/piperDsp.h
new file mode 100644
index 000000000000..836ffcb96341
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/piperDsp.h
@@ -0,0 +1,8 @@
+#ifndef PIPER_DSP_H_
+#define PIPER_DSP_H_
+
+
+extern const unsigned long piper_wifi_dsp_ucode[];
+extern const int piper_dsp_data_len;
+
+#endif
diff --git a/drivers/net/wireless/digiPiper/piperMacAssist.c b/drivers/net/wireless/digiPiper/piperMacAssist.c
new file mode 100644
index 000000000000..1a8ada81b84b
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/piperMacAssist.c
@@ -0,0 +1,285 @@
+/*
+ Copyright (c) 2007-2008 Digi International Inc., All Rights Reserved
+
+ This software contains proprietary and confidential information of Digi
+ International Inc. By accepting transfer of this copy, Recipient agrees
+ to retain this software in confidence, to prevent disclosure to others,
+ and to make no use of this software other than that for which it was
+ delivered. This is an unpublished copyrighted work of Digi International
+ Inc. Except as permitted by federal law, 17 USC 117, copying is strictly
+ prohibited.
+
+ Restricted Rights Legend
+
+ Use, duplication, or disclosure by the Government is subject to
+ restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
+ Technical Data and Computer Software clause at DFARS 252.227-7031 or
+ subparagraphs (c)(1) and (2) of the Commercial Computer Software -
+ Restricted Rights at 48 CFR 52.227-19, as applicable.
+
+ Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
+
+ WiFi MAC assist Code for Piper
+*/
+
+const unsigned long piper_wifi_macassist_ucode[2048] = {
+ 0x00F00, 0x2CF22, 0x3033B, 0x00E20, 0x2CED0, 0x2CFD2, 0x2CFD8, 0x2CFD9,
+ 0x2CF31, 0x2CFDB, 0x00E20, 0x2CEDA, 0x00E64, 0x2CE1D, 0x00EFF, 0x2CEEA,
+ 0x2CEEB, 0x00E01, 0x2CE20, 0x00E10, 0x2CE21, 0x04522, 0x00603, 0x2C6CE,
+ 0x00001, 0x2C040, 0x00000, 0x2C041, 0x2C042, 0x2C043, 0x0016E, 0x2C144,
+ 0x00000, 0x0010A, 0x00200, 0x0A5FF, 0x35027, 0x0010B, 0x00280, 0x2C245,
+ 0x2C146, 0x2C047, 0x00180, 0x2C148, 0x2C049, 0x2C0CE, 0x00000, 0x00100,
+ 0x00223, 0x040C0, 0x302E2, 0x3031A, 0x00030, 0x0011C, 0x00801, 0x042C0,
+ 0x0A204, 0x35037, 0x042C0, 0x0A5FF, 0x35040, 0x00031, 0x00117, 0x00806,
+ 0x30338, 0x1C001, 0x35C40, 0x1C101, 0x35C43, 0x00000, 0x042C0, 0x043C0,
+ 0x044C0, 0x045C0, 0x046C0, 0x047C0, 0x00000, 0x1C801, 0x35C4D, 0x048C1,
+ 0x049C1, 0x04AC1, 0x04BC1, 0x04CC1, 0x04DC1, 0x0D320, 0x0D430, 0x0D540,
+ 0x0D650, 0x0D760, 0x19230, 0x19240, 0x19250, 0x19260, 0x19270, 0x0A880,
+ 0x35062, 0x18208, 0x0A980, 0x35065, 0x18208, 0x0AA80, 0x35068, 0x18208,
+ 0x0AB80, 0x3506B, 0x18208, 0x0AC80, 0x3506E, 0x18208, 0x0AD80, 0x35071,
+ 0x18208, 0x20206, 0x04122, 0x0A1FF, 0x3547B, 0x2C222, 0x0E260, 0x35015,
+ 0x0E260, 0x00151, 0x3407C, 0x00103, 0x19120, 0x2C122, 0x00E00, 0x2CECE,
+ 0x3C001, 0x04EC3, 0x0AE20, 0x04FC2, 0x0AF80, 0x0DEF0, 0x04F30, 0x2CE30,
+ 0x0FFE0, 0x35095, 0x0EE20, 0x35495, 0x3C000, 0x04EC1, 0x0AE80, 0x3548D,
+ 0x04E17, 0x2CED4, 0x04E16, 0x2CED6, 0x3C001, 0x04E27, 0x0AE10, 0x04F33,
+ 0x2CE33, 0x0EF10, 0x0BFE0, 0x350AC, 0x00E00, 0x00FE0, 0x2DEF0, 0x18F01,
+ 0x0EFE8, 0x350A4, 0x0EFE8, 0x3409E, 0x04F1D, 0x00E00, 0x1DEF0, 0x2CEEA,
+ 0x04F1C, 0x00E00, 0x1FEF0, 0x2CEEB, 0x04E27, 0x0AE80, 0x350C8, 0x3C000,
+ 0x00E01, 0x2CECE, 0x04EC1, 0x0AE80, 0x354B2, 0x04EE0, 0x2CE27, 0x04EE1,
+ 0x2CE26, 0x04EE2, 0x2CE25, 0x04EE3, 0x2CE24, 0x04EE4, 0x2CE23, 0x04EE5,
+ 0x2CE22, 0x04EE6, 0x04FE7, 0x2CE21, 0x2CF20, 0x00E00, 0x2CECE, 0x3C001,
+ 0x04E35, 0x04F36, 0x0DEF0, 0x3507E, 0x04E3A, 0x04FEB, 0x15EF0, 0x3547E,
+ 0x04FEA, 0x04E37, 0x15EF0, 0x3547E, 0x3C000, 0x04EC1, 0x0AE80, 0x354D5,
+ 0x04E36, 0x12EFF, 0x354E7, 0x04F35, 0x14F40, 0x35CE7, 0x00E00, 0x2CED8,
+ 0x20F06, 0x20F06, 0x2CFD9, 0x3C001, 0x2CE36, 0x2CE35, 0x3407E, 0x04F35,
+ 0x1CF01, 0x1EE00, 0x2CF35, 0x2CE36, 0x00E0F, 0x2CED8, 0x2CED9, 0x3C001,
+ 0x04E37, 0x04F3A, 0x18E01, 0x1AF00, 0x2CE37, 0x2CF3A, 0x3407E, 0x2CF32,
+ 0x04FC0, 0x20F0E, 0x3190B, 0x20F0E, 0x31BFC, 0x20F0E, 0x3195B, 0x20F0E,
+ 0x31A72, 0x20F0E, 0x31ACA, 0x20F0E, 0x319DC, 0x20F0E, 0x319E3, 0x20F0E,
+ 0x31A63, 0x04F32, 0x38001, 0x04031, 0x0C001, 0x2C031, 0x040C2, 0x0A008,
+ 0x35120, 0x04031, 0x0A004, 0x35517, 0x04031, 0x0C080, 0x2C031, 0x302E2,
+ 0x040C3, 0x0A010, 0x3511C, 0x00016, 0x18011, 0x00100, 0x00200, 0x3031A,
+ 0x302FF, 0x0E3FF, 0x35127, 0x04031, 0x0A0BF, 0x2C031, 0x3412A, 0x04031,
+ 0x0C040, 0x2C031, 0x04244, 0x040C3, 0x0A010, 0x35547, 0x302E8, 0x00400,
+ 0x04510, 0x0A5FF, 0x35534, 0x0C515, 0x13350, 0x3513B, 0x01430, 0x1232F,
+ 0x35544, 0x2030E, 0x34134, 0x1232F, 0x3553F, 0x2030E, 0x34134, 0x124FF,
+ 0x35544, 0x2030E, 0x35D34, 0x00401, 0x302F4, 0x2C33B, 0x2A000, 0x04511,
+ 0x1426E, 0x3514F, 0x14237, 0x35152, 0x14214, 0x35155, 0x34158, 0x0026E,
+ 0x12508, 0x35559, 0x00237, 0x12504, 0x35559, 0x00214, 0x12502, 0x35559,
+ 0x0020A, 0x2C23B, 0x2A000, 0x04127, 0x00001, 0x2C0CE, 0x040C2, 0x0A040,
+ 0x35163, 0x00005, 0x2C0CE, 0x0A110, 0x35192, 0x04048, 0x0E080, 0x3516C,
+ 0x04048, 0x0E050, 0x3516C, 0x34192, 0x00012, 0x04140, 0x0E1EE, 0x35175,
+ 0x041C1, 0x0A120, 0x35574, 0x18020, 0x18007, 0x302FF, 0x00100, 0x19030,
+ 0x1A100, 0x30338, 0x042E0, 0x043E1, 0x044E2, 0x045E3, 0x046E4, 0x047E5,
+ 0x048E6, 0x049E7, 0x19200, 0x1B310, 0x1A400, 0x1A500, 0x1A600, 0x1A700,
+ 0x1A800, 0x1A900, 0x2C260, 0x2C361, 0x2C462, 0x2C563, 0x2C664, 0x2C765,
+ 0x2C866, 0x2C967, 0x0002C, 0x00100, 0x042C2, 0x044C1, 0x0A480, 0x35595,
+ 0x2C0D0, 0x2C1D2, 0x043C2, 0x0A3E0, 0x2C3C2, 0x04140, 0x04348, 0x00000,
+ 0x2C0CE, 0x04031, 0x0A0FB, 0x2C031, 0x0A204, 0x355B6, 0x0A30F, 0x0E304,
+ 0x351B6, 0x04012, 0x0A001, 0x351B6, 0x0E1EE, 0x355AF, 0x0C004, 0x0C00A,
+ 0x2C012, 0x00064, 0x1C001, 0x355B2, 0x00040, 0x2C0C4, 0x040C2, 0x0A040,
+ 0x2B400, 0x0403E, 0x0A0FF, 0x2B400, 0x00001, 0x2C0CE, 0x04040, 0x0A0FF,
+ 0x351C5, 0x0E001, 0x351C5, 0x0E0EF, 0x355D4, 0x04042, 0x0A0FF, 0x351CB,
+ 0x0E001, 0x355D4, 0x341CE, 0x04041, 0x0A0FF, 0x351D4, 0x04043, 0x0A0FF,
+ 0x355D4, 0x00000, 0x2C0CE, 0x2A000, 0x00080, 0x2C0C4, 0x0403F, 0x18001,
+ 0x2C03F, 0x00000, 0x2C0CE, 0x2A000, 0x00008, 0x2C0C4, 0x00100, 0x2C13E,
+ 0x00012, 0x00200, 0x3431A, 0x00101, 0x2C1CE, 0x0402B, 0x12001, 0x35203,
+ 0x04128, 0x121FF, 0x351EE, 0x1C101, 0x2C128, 0x35603, 0x04129, 0x0422D,
+ 0x0432C, 0x0442F, 0x0452E, 0x12002, 0x351F7, 0x2C128, 0x341F9, 0x0A0FE,
+ 0x2C02B, 0x00000, 0x2C0CE, 0x0401D, 0x1D200, 0x0401C, 0x1F300, 0x2C237,
+ 0x2C33A, 0x2C435, 0x2C536, 0x00000, 0x2C0CE, 0x04027, 0x0A010, 0x35236,
+ 0x3033B, 0x040D5, 0x2C02B, 0x040D7, 0x2C02A, 0x00100, 0x0401F, 0x1D100,
+ 0x2C1E8, 0x00100, 0x0401E, 0x1F100, 0x2C1E9, 0x040C2, 0x0A004, 0x3561D,
+ 0x3033B, 0x00004, 0x2C0D0, 0x00000, 0x2C0D2, 0x04027, 0x0A004, 0x3522C,
+ 0x30338, 0x30338, 0x040C2, 0x0C080, 0x2C0C2, 0x040C2, 0x0A040, 0x3522C,
+ 0x0402F, 0x2C0D4, 0x0402E, 0x2C0D6, 0x00110, 0x040C1, 0x0A010, 0x35234,
+ 0x04031, 0x0D010, 0x2C031, 0x34246, 0x2C1C4, 0x34246, 0x00010, 0x2C0C4,
+ 0x04034, 0x0C000, 0x35246, 0x040ED, 0x0C000, 0x35646, 0x3033B, 0x0421F,
+ 0x0431E, 0x2C2D8, 0x2C3D9, 0x04119, 0x1D010, 0x2C0ED, 0x0421D, 0x0431C,
+ 0x00000, 0x00100, 0x1D020, 0x1F130, 0x3033B, 0x2C0EA, 0x2C1EB, 0x040EC,
+ 0x0C000, 0x35655, 0x04118, 0x1D010, 0x2C0EC, 0x3033B, 0x040EE, 0x0C000,
+ 0x2B400, 0x041EF, 0x0C100, 0x2B400, 0x0421B, 0x0431A, 0x1D020, 0x1F130,
+ 0x2C0EE, 0x2C1EF, 0x2A000, 0x3033B, 0x040D5, 0x2C02B, 0x040D7, 0x2C02A,
+ 0x040C1, 0x0A010, 0x3566E, 0x00020, 0x2C0C4, 0x2A000, 0x04031, 0x0C020,
+ 0x2C031, 0x2A000, 0x04012, 0x0A0F7, 0x2C012, 0x00500, 0x00601, 0x00100,
+ 0x00700, 0x04031, 0x0A0F7, 0x2C031, 0x2C53E, 0x040C3, 0x0A0C0, 0x35287,
+ 0x00603, 0x0A080, 0x35684, 0x00605, 0x2C6CE, 0x04440, 0x342A4, 0x00702,
+ 0x2C6CE, 0x04440, 0x0424C, 0x0A201, 0x356A4, 0x04248, 0x0E2B4, 0x35293,
+ 0x04249, 0x0A204, 0x35298, 0x2C5CE, 0x04231, 0x0C208, 0x2C231, 0x2C6CE,
+ 0x00203, 0x04048, 0x2C5CE, 0x0E0C4, 0x352B9, 0x00030, 0x0A4EE, 0x356A2,
+ 0x00080, 0x1805E, 0x00209, 0x342B9, 0x2C5CE, 0x00000, 0x00100, 0x04231,
+ 0x0A240, 0x356AD, 0x042C1, 0x0A201, 0x356B2, 0x0A4EE, 0x356B0, 0x000FE,
+ 0x1803C, 0x1A100, 0x00200, 0x0A4EE, 0x356B7, 0x18016, 0x1A100, 0x18012,
+ 0x1A100, 0x3031A, 0x12702, 0x352C2, 0x0A202, 0x356C2, 0x04131, 0x12108,
+ 0x356C2, 0x0C780, 0x04131, 0x0A130, 0x0D710, 0x2C7C4, 0x04731, 0x0A7CF,
+ 0x2C731, 0x2A000, 0x04531, 0x12504, 0x352D4, 0x3033B, 0x0A5FB, 0x2C531,
+ 0x00500, 0x2C5D8, 0x2C5D9, 0x342DE, 0x04031, 0x0A080, 0x2B400, 0x04131,
+ 0x0A1F7, 0x2C131, 0x00184, 0x2C1C4, 0x00101, 0x2C1CE, 0x04440, 0x302E2,
+ 0x00500, 0x342A4, 0x040C1, 0x0A080, 0x356E2, 0x00020, 0x2C0DB, 0x2A000,
+ 0x00310, 0x12202, 0x352EC, 0x00301, 0x12201, 0x356F0, 0x20306, 0x20306,
+ 0x12204, 0x352F3, 0x20306, 0x2A000, 0x00308, 0x12455, 0x356F8, 0x0030C,
+ 0x12433, 0x352FB, 0x0C301, 0x1240F, 0x352FE, 0x0C302, 0x2A000, 0x04240,
+ 0x0E2EE, 0x35708, 0x04244, 0x003FF, 0x0A208, 0x2B000, 0x00300, 0x2A000,
+ 0x003FF, 0x04244, 0x0E26E, 0x3570D, 0x00311, 0x04244, 0x0E237, 0x35711,
+ 0x00323, 0x04244, 0x0E214, 0x35715, 0x00360, 0x04244, 0x0E20A, 0x2B400,
+ 0x003C0, 0x2A000, 0x0C000, 0x35322, 0x04325, 0x01430, 0x20306, 0x19430,
+ 0x19040, 0x1A100, 0x1C01A, 0x1E100, 0x04AC1, 0x0AA80, 0x35724, 0x043DB,
+ 0x19030, 0x1A100, 0x12180, 0x3532F, 0x00001, 0x00100, 0x34331, 0x18001,
+ 0x1A100, 0x2C0D0, 0x2C1D2, 0x043C2, 0x0A3E0, 0x0D230, 0x2C2C2, 0x2A000,
+ 0x04AC1, 0x0AA80, 0x35338, 0x04AC1, 0x0AA80, 0x3573B, 0x2A000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00001, 0x2C0CF, 0x2A000, 0x340F7,
+ 0x040C3, 0x0A002, 0x35008, 0x300EA, 0x04031, 0x0C002, 0x2C031, 0x343FC,
+ 0x04031, 0x12002, 0x3540E, 0x12001, 0x353FC, 0x300EA, 0x04031, 0x0A0F8,
+ 0x2C031, 0x30058, 0x04031, 0x0A0F7, 0x2C031, 0x04123, 0x01010, 0x0A00C,
+ 0x0E004, 0x35020, 0x04031, 0x0A080, 0x35042, 0x00084, 0x2C0C4, 0x34042,
+ 0x04023, 0x0C010, 0x2C023, 0x04027, 0x0A008, 0x35429, 0x01010, 0x0A003,
+ 0x3501A, 0x04031, 0x0A080, 0x35036, 0x04048, 0x0A0EC, 0x0E0C4, 0x3503A,
+ 0x00084, 0x2C0C4, 0x04031, 0x0A07F, 0x2C031, 0x3403A, 0x04048, 0x0A0EC,
+ 0x0E0C4, 0x35042, 0x040C1, 0x0A002, 0x35040, 0x00040, 0x2C0C4, 0x3401A,
+ 0x00001, 0x2C0C4, 0x04031, 0x0A07F, 0x2C031, 0x04023, 0x0A00C, 0x0E004,
+ 0x357FC, 0x0404C, 0x0A001, 0x3504E, 0x300B0, 0x343FC, 0x04023, 0x0A001,
+ 0x35453, 0x300BF, 0x343FC, 0x04048, 0x0E050, 0x357FC, 0x300DB, 0x343FC,
+ 0x04031, 0x0A040, 0x3505F, 0x04023, 0x0C020, 0x2C023, 0x3406B, 0x040C1,
+ 0x0A001, 0x3506B, 0x04023, 0x0C004, 0x2C023, 0x04048, 0x0A003, 0x35073,
+ 0x04023, 0x0C008, 0x2C023, 0x040C3, 0x0A010, 0x3506F, 0x00054, 0x1804E,
+ 0x00100, 0x00200, 0x34306, 0x040C2, 0x0A004, 0x2B400, 0x040C3, 0x0A010,
+ 0x3507A, 0x00016, 0x18012, 0x00100, 0x00200, 0x30306, 0x0004C, 0x05100,
+ 0x0A101, 0x2B000, 0x04008, 0x0A001, 0x35088, 0x04048, 0x0E080, 0x3509A,
+ 0x04049, 0x0A002, 0x3548C, 0x00008, 0x18050, 0x05100, 0x0A101, 0x35494,
+ 0x00208, 0x3032E, 0x0C400, 0x2B400, 0x04048, 0x0E040, 0x3549A, 0x04027,
+ 0x0A010, 0x2B000, 0x0402D, 0x0A0FF, 0x350AC, 0x04048, 0x0E080, 0x354AC,
+ 0x04063, 0x04139, 0x2C039, 0x0F010, 0x354AC, 0x04062, 0x04138, 0x0422D,
+ 0x0B020, 0x2C038, 0x0F010, 0x2B000, 0x04023, 0x0C002, 0x2C023, 0x2A000,
+ 0x04048, 0x0A0EC, 0x0E0E4, 0x354BC, 0x04036, 0x04135, 0x0D010, 0x2B400,
+ 0x3033C, 0x2C0D8, 0x2C0D9, 0x2A000, 0x04048, 0x0E080, 0x310DB, 0x0414B,
+ 0x0A180, 0x2B400, 0x0414A, 0x0424B, 0x1C10A, 0x1E200, 0x35CC9, 0x00100,
+ 0x00200, 0x30340, 0x2BC00, 0x04048, 0x0E0B4, 0x2B400, 0x2A000, 0x04031,
+ 0x0C004, 0x2C031, 0x00078, 0x00100, 0x042C3, 0x0A210, 0x350D9, 0x0006A,
+ 0x00101, 0x00209, 0x34306, 0x0466D, 0x1866F, 0x05160, 0x19610, 0x0416A,
+ 0x0A10C, 0x2C134, 0x350E9, 0x1860A, 0x05160, 0x18601, 0x05260, 0x30340,
+ 0x1C60B, 0x34213, 0x00000, 0x2C023, 0x04031, 0x0A040, 0x2B400, 0x040C3,
+ 0x0A010, 0x354FB, 0x04046, 0x0A001, 0x354FB, 0x04045, 0x14005, 0x35CFB,
+ 0x040C3, 0x0A002, 0x354F8, 0x04048, 0x0A003, 0x2B400, 0x0004C, 0x05100,
+ 0x0A101, 0x2B400, 0x0424C, 0x0434D, 0x0444E, 0x0454F, 0x04750, 0x04851,
+ 0x04100, 0x0F120, 0x04001, 0x0F030, 0x0D100, 0x04002, 0x0F040, 0x0D100,
+ 0x04003, 0x0F050, 0x0D100, 0x04004, 0x0F070, 0x0D100, 0x04005, 0x0F080,
+ 0x0D100, 0x35146, 0x04027, 0x0A040, 0x35145, 0x00001, 0x2C0CE, 0x04130,
+ 0x0F120, 0x04038, 0x0F200, 0x04031, 0x0F030, 0x0D100, 0x04039, 0x0F030,
+ 0x0D200, 0x04032, 0x0F040, 0x0D100, 0x0403A, 0x0F040, 0x0D200, 0x04033,
+ 0x0F050, 0x0D100, 0x0403B, 0x0F050, 0x0D200, 0x04034, 0x0F070, 0x0D100,
+ 0x0403C, 0x0F070, 0x0D200, 0x04035, 0x0433D, 0x00400, 0x2C4CE, 0x0F080,
+ 0x0D100, 0x35146, 0x0F380, 0x0D230, 0x35146, 0x2A000, 0x04023, 0x0C001,
+ 0x2C023, 0x040C1, 0x0A002, 0x2B400, 0x04048, 0x0A00C, 0x0E004, 0x35564,
+ 0x04048, 0x0A0E0, 0x0E0A0, 0x35164, 0x0E060, 0x2B400, 0x04131, 0x0A108,
+ 0x2B000, 0x3020A, 0x040C3, 0x0A002, 0x3555A, 0x040C1, 0x0A002, 0x35577,
+ 0x040C1, 0x0A001, 0x35177, 0x2A000, 0x3020A, 0x0413B, 0x040C3, 0x0A010,
+ 0x355C5, 0x0C1C0, 0x002EE, 0x00300, 0x00701, 0x00603, 0x040C3, 0x0A002,
+ 0x3556E, 0x040C1, 0x0A002, 0x35577, 0x040C1, 0x0A001, 0x3557C, 0x042C2,
+ 0x0A2E0, 0x0C203, 0x2C2C2, 0x2A000, 0x2C6CE, 0x2C240, 0x2C341, 0x2C342,
+ 0x2C343, 0x2C144, 0x2C745, 0x2C346, 0x2C347, 0x2C3CE, 0x04248, 0x0434A,
+ 0x0444B, 0x01540, 0x0E580, 0x35193, 0x01520, 0x0E5B4, 0x35195, 0x04549,
+ 0x0A504, 0x35595, 0x0A400, 0x0A300, 0x341AB, 0x00528, 0x01010, 0x0A007,
+ 0x351A9, 0x0E004, 0x351A9, 0x0E001, 0x351A9, 0x18504, 0x0E004, 0x351A9,
+ 0x0E007, 0x351A9, 0x18508, 0x0E004, 0x351A9, 0x18504, 0x0E005, 0x351A9,
+ 0x1850C, 0x1D350, 0x1E400, 0x00000, 0x2C6CE, 0x0E2B4, 0x351B1, 0x005D4,
+ 0x341B2, 0x005C4, 0x2C548, 0x2C049, 0x2C34A, 0x2C44B, 0x00306, 0x00152,
+ 0x0024C, 0x2C0CE, 0x05810, 0x2C6CE, 0x2D820, 0x18101, 0x18201, 0x1C301,
+ 0x355B9, 0x2C0CE, 0x00001, 0x2C03E, 0x2A000, 0x045C1, 0x0A540, 0x351CC,
+ 0x01510, 0x0E50A, 0x355CC, 0x00114, 0x01510, 0x00784, 0x00800, 0x0E56E,
+ 0x351D9, 0x00704, 0x0E559, 0x351D8, 0x0E523, 0x351D7, 0x18838, 0x18823,
+ 0x1880A, 0x1880B, 0x00603, 0x00300, 0x040C3, 0x0A002, 0x355DC, 0x040C1,
+ 0x0A002, 0x35577, 0x040C1, 0x0A001, 0x35177, 0x2C6CE, 0x2C340, 0x2C341,
+ 0x2C342, 0x2C343, 0x2C144, 0x2C745, 0x2C846, 0x2C347, 0x2C3CE, 0x04248,
+ 0x0434A, 0x0444B, 0x01540, 0x0E580, 0x351FC, 0x01520, 0x0E5B4, 0x351FE,
+ 0x04549, 0x0A504, 0x355FE, 0x0A400, 0x0A300, 0x341AB, 0x01580, 0x1850A,
+ 0x1D350, 0x1E400, 0x045C1, 0x0A540, 0x35606, 0x185A0, 0x18520, 0x1D350,
+ 0x1E400, 0x341AB, 0x00000, 0x00100, 0x00217, 0x04348, 0x0E3B4, 0x35211,
+ 0x00207, 0x30306, 0x2A000, 0x18603, 0x04027, 0x0A010, 0x0416A, 0x0A102,
+ 0x20106, 0x20106, 0x20106, 0x0F010, 0x2B400, 0x0C100, 0x35237, 0x0426D,
+ 0x04013, 0x0F020, 0x2B400, 0x0006E, 0x00100, 0x0A2FF, 0x2B000, 0x00500,
+ 0x05300, 0x00701, 0x2C7CE, 0x05410, 0x00700, 0x2C7CE, 0x0F340, 0x0D530,
+ 0x18001, 0x18101, 0x1C201, 0x35628, 0x0C500, 0x2B400, 0x34257, 0x00058,
+ 0x00108, 0x00206, 0x00500, 0x05300, 0x05410, 0x0F340, 0x0D530, 0x18001,
+ 0x18101, 0x1C201, 0x3563B, 0x0C500, 0x2B400, 0x04034, 0x0C000, 0x35257,
+ 0x18603, 0x05060, 0x00100, 0x1D100, 0x2C1ED, 0x18601, 0x05060, 0x2C019,
+ 0x18601, 0x05060, 0x2C01F, 0x18601, 0x05060, 0x2C01E, 0x18602, 0x04048,
+ 0x0A0EC, 0x0E080, 0x35676, 0x04023, 0x0C002, 0x2C023, 0x040C2, 0x0A080,
+ 0x35265, 0x040C2, 0x0A05F, 0x2C0C2, 0x2C0C2, 0x18603, 0x04027, 0x0A010,
+ 0x3526F, 0x05060, 0x2C01F, 0x18601, 0x05060, 0x2C01E, 0x34276, 0x05060,
+ 0x00100, 0x1D100, 0x2C1EC, 0x18601, 0x05060, 0x2C018, 0x040C3, 0x0A010,
+ 0x35685, 0x04044, 0x04145, 0x04246, 0x00304, 0x2020E, 0x20108, 0x20008,
+ 0x2010E, 0x20008, 0x1C301, 0x35680, 0x34294, 0x04046, 0x04147, 0x04244,
+ 0x003C0, 0x0E26E, 0x3568C, 0x00311, 0x04244, 0x0E237, 0x35690, 0x00323,
+ 0x04244, 0x0E214, 0x35694, 0x00360, 0x1D030, 0x1E100, 0x30339, 0x1801A,
+ 0x1A100, 0x04260, 0x19200, 0x04361, 0x1B310, 0x04462, 0x1A400, 0x04563,
+ 0x1A500, 0x04664, 0x1A600, 0x04765, 0x1A700, 0x04866, 0x1A800, 0x04967,
+ 0x1A900, 0x0406A, 0x0A002, 0x352BF, 0x040E0, 0x1D020, 0x040E1, 0x1F030,
+ 0x040E2, 0x1F040, 0x040E3, 0x1F050, 0x040E4, 0x1F060, 0x040E5, 0x1F070,
+ 0x040E6, 0x1F080, 0x040E7, 0x1F090, 0x2BC00, 0x00000, 0x342C0, 0x30339,
+ 0x2C2E0, 0x2C3E1, 0x2C4E2, 0x2C5E3, 0x2C6E4, 0x2C7E5, 0x2C8E6, 0x2C9E7,
+ 0x01230, 0x0401D, 0x0411C, 0x00D27, 0x00C01, 0x20006, 0x20100, 0x35AD5,
+ 0x18C01, 0x0EC10, 0x352F4, 0x0EC10, 0x342CD, 0x20108, 0x342D8, 0x2010E,
+ 0x20008, 0x1CC01, 0x352EC, 0x01A80, 0x01B90, 0x1DA00, 0x1FB10, 0x35AD7,
+ 0x018A0, 0x019B0, 0x342D7, 0x1CD01, 0x352F8, 0x20306, 0x20400, 0x20500,
+ 0x20600, 0x20700, 0x20800, 0x20900, 0x01A80, 0x01B90, 0x1DA00, 0x1FB10,
+ 0x35AE3, 0x018A0, 0x019B0, 0x342E3, 0x00100, 0x008FF, 0x009FF, 0x34301,
+ 0x30339, 0x0401D, 0x0411C, 0x04AE1, 0x0FA20, 0x0AAFC, 0x35301, 0x18801,
+ 0x1A900, 0x1D800, 0x1F910, 0x2C8EA, 0x2C9EB, 0x2A000, 0x0442C, 0x0C000,
+ 0x3530D, 0x04325, 0x01430, 0x20306, 0x19430, 0x19040, 0x1A100, 0x043C3,
+ 0x0A310, 0x35717, 0x04344, 0x0A302, 0x35317, 0x0031A, 0x34318, 0x0031E,
+ 0x1D030, 0x1E100, 0x04AC1, 0x0AA80, 0x3571A, 0x04ADB, 0x190A0, 0x1A100,
+ 0x12180, 0x35325, 0x00002, 0x00100, 0x34327, 0x18002, 0x1A100, 0x2C0D0,
+ 0x2C1D2, 0x043C2, 0x0A3E0, 0x0D230, 0x2C2C2, 0x2A000, 0x00506, 0x00400,
+ 0x05100, 0x05320, 0x0F130, 0x0D410, 0x18201, 0x18001, 0x1C501, 0x35730,
+ 0x2A000, 0x04AC1, 0x0AA80, 0x35339, 0x04AC1, 0x0AA80, 0x3573C, 0x2A000,
+ 0x30339, 0x043D8, 0x044D9, 0x18301, 0x1A400, 0x1D310, 0x1F420, 0x2BC00,
+ 0x2C1D8, 0x2C2D9, 0x2A000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x00000,
+ 0x00000, 0x00000, 0x00000, 0x00000, 0x00000, 0x2C0CF, 0x34000, 0x00000
+};
+
+
+const int piper_macassist_data_len = (sizeof piper_wifi_macassist_ucode)/4;
diff --git a/drivers/net/wireless/digiPiper/piperMacAssist.h b/drivers/net/wireless/digiPiper/piperMacAssist.h
new file mode 100644
index 000000000000..27b2b77fb493
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/piperMacAssist.h
@@ -0,0 +1,9 @@
+
+#ifndef PIPER_MACASSIST_H_
+#define PIPER_MACASSIST_H_
+
+extern const unsigned long piper_wifi_macassist_ucode[];
+extern const int piper_macassist_data_len;
+
+
+#endif
diff --git a/drivers/net/wireless/digiPiper/pipermain.h b/drivers/net/wireless/digiPiper/pipermain.h
new file mode 100644
index 000000000000..dba31309e081
--- /dev/null
+++ b/drivers/net/wireless/digiPiper/pipermain.h
@@ -0,0 +1,381 @@
+#ifndef __PIPER_H_
+#define __PIPER_H_
+
+#include <linux/completion.h>
+#include <linux/if_ether.h>
+#include <linux/spinlock.h>
+#include <net/mac80211.h>
+#include <linux/i2c.h>
+#include "mac.h"
+
+
+// #define WANT_DEBUG
+#ifdef WANT_DEBUG
+#define digi_dbg(fmt, arg...) \
+ printk(KERN_ERR PIPER_DRIVER_NAME ": %s - " fmt, __func__, ##arg)
+#else
+#define digi_dbg(fmt, arg...) \
+ do { } while (0)
+#endif
+
+#define ERROR(x) printk(KERN_ALERT x)
+
+/* Debug levels */
+#define DSILENT 0xffff
+#define DERROR 0xfff0
+#define DWARNING 0xffe0
+#define DNORMAL 0xffd0
+#define DVERBOSE 0xffc0
+#define DVVERBOSE 0xffb0
+#define DALL 0xffa0
+
+
+#define PIPER_DRIVER_NAME "piper"
+#define DRV_VERS "0.1"
+
+/* Useful defines for AES */
+#define PIPER_EXTIV_SIZE 8 /* IV and extended IV size */
+#define MIC_SIZE 8 /* Message integrity check size */
+#define ICV_SIZE 4
+#define DATA_SIZE 28 /* Data frame header+FCS size */
+#define CCMP_SIZE (PIPER_EXTIV_SIZE + MIC_SIZE) /* Total CCMP size */
+#define EXPANDED_KEY_LENGTH (176) /* length of expanded AES key */
+#define PIPER_MAX_KEYS (4)
+#define AES_BLOB_LENGTH (48) /* length of AES IV and headers */
+
+/* Calibration constants */
+#define WCD_MAGIC "WCALDATA"
+#define WCD_MAX_CAL_POINTS (8)
+#define WCD_CHANNELS_BG (14)
+#define WCD_CHANNELS_A (35)
+#define WCD_B_CURVE_INDEX (0)
+#define WCD_G_CURVE_INDEX (1)
+
+/*
+ * Set this #define to receive frames in the ISR. This may improve
+ * performance under heavy load at the expense of interrupt latency.
+ */
+#define WANT_TO_RECEIVE_FRAMES_IN_ISR (0)
+
+
+typedef u64 u48;
+
+/*
+ * This enum lists the possible LED states.
+ */
+enum led_states {
+ led_shutdown,
+ led_adhoc,
+ led_not_associated,
+ led_associated
+};
+
+/* Available leds */
+enum wireless_led {
+ STATUS_LED,
+ ACTIVITY_LED,
+};
+
+#define WCD_HW_REV_MASK 0xf000
+#define WCD_HW_REV_PROTOTYPE 0x0000
+#define WCD_HW_REV_PILOT 0x1000
+#define WCD_HW_REV_A 0x2000
+#define WCD_PLATFORM_MASK 0x0ff0
+#define WCD_CCW9P_PLATFORM 0x0010
+#define WCD_CCW9M_PLATFORM 0x0020
+
+
+typedef struct nv_wcd_header {
+ char magic_string[8]; /* WCALDATA */
+ char ver_major; /* Major version in ascii */
+ char ver_minor; /* Minor version in ascii */
+ u16 hw_platform; /* Hardware Platform used for calibration */
+ u8 numcalpoints; /* Number of points per curve */
+ u8 padding[107]; /* Reserved for future use */
+ u32 wcd_len; /* Total length of the data section */
+ u32 wcd_crc; /* Data section crc32 */
+} nv_wcd_header_t;
+
+typedef struct wcd_point {
+ s16 out_power; /* Output Power */
+ u16 adc_val; /* Measured ADC val */
+ u8 power_index; /* Airoha Power Index */
+ u8 reserved[3]; /* For future use */
+} wcd_point_t;
+
+typedef struct wcd_curve {
+ u8 max_adc_value; /* maximum allowed ADC value for this curve */
+ u8 reserved[3]; /* Resered for future use */
+ /* Calibration curve points */
+ wcd_point_t points[WCD_MAX_CAL_POINTS];
+} wcd_curve_t;
+
+typedef struct wcd_data {
+ nv_wcd_header_t header;
+ wcd_curve_t cal_curves_bg[WCD_CHANNELS_BG][2];
+ wcd_curve_t cal_curves_a[WCD_CHANNELS_A];
+} wcd_data_t;
+
+typedef enum {
+ op_write,
+ op_or,
+ op_and
+} reg_op_t;
+
+enum antenna_select {
+ ANTENNA_BOTH = 0,
+ ANTENNA_1,
+ ANTENNA_2,
+};
+
+typedef struct {
+ bool loaded;
+ bool enabled;
+ bool weSentLastOne;
+} piperBeaconInfo_t;
+
+typedef enum {
+ RECEIVED_ACK,
+ TX_COMPLETE,
+ OUT_OF_RETRIES,
+ TX_NOT_DONE
+} tx_result_t;
+
+
+/* Structure that holds the information we need to support H/W AES encryption */
+struct piperKeyInfo {
+ bool valid; /* indicates if this record is valid */
+ u8 addr[ETH_ALEN]; /* MAC address associated with key */
+ u32 expandedKey[EXPANDED_KEY_LENGTH / sizeof(u32)];
+ u48 txPn; /* packet number for transmit */
+ u48 rxPn; /* expected receive packet number */
+};
+
+/* rf */
+struct digi_rf_ops {
+ const char *name;
+ void (*init) (struct ieee80211_hw *, int);
+ int (*stop) (struct ieee80211_hw *);
+ int (*set_chan) (struct ieee80211_hw *, int chan);
+ int (*set_chan_no_rx) (struct ieee80211_hw *, int chan);
+ int (*set_pwr) (struct ieee80211_hw *, uint8_t val);
+ void (*set_pwr_index) (struct ieee80211_hw *, unsigned int val);
+ void (*power_on) (struct ieee80211_hw *, bool want_power_on);
+ void (*getOfdmBrs) (int channel, u64 brsBitMask, unsigned int *ofdm,
+ unsigned int *psk);
+ enum ieee80211_band (*getBand) (int);
+ int (*getFrequency) (int);
+ void (*set_hw_info)(struct ieee80211_hw *, int channel, u16 hw_platform);
+ const struct ieee80211_rate *(*getRate) (unsigned int);
+ int channelChangeTime;
+ s8 maxSignal;
+ struct ieee80211_supported_band *bands;
+ u8 n_bands;
+};
+
+struct piper_stats {
+ u32 rx_overruns;
+ u32 tx_complete_count;
+ u32 tx_start_count;
+ u32 tx_total_tetries;
+ u32 tx_retry_count[IEEE80211_TX_MAX_RATES];
+ u32 tx_retry_index;
+ struct ieee80211_tx_queue_stats tx_queue;
+ struct ieee80211_low_level_stats ll_stats;
+ spinlock_t lock;
+};
+
+enum piper_ps_mode {
+ PS_MODE_LOW_POWER,
+ PS_MODE_FULL_POWER
+};
+
+enum piper_ps_state {
+ PS_STATE_WAIT_FOR_BEACON,
+ PS_STATE_WAIT_FOR_STOP_TRANSMIT_EVENT,
+ PS_STATE_WAIT_FOR_TRANSMITTER_DONE,
+ PS_STATE_WAIT_FOR_WAKEUP_ALARM,
+ PS_STATE_WAIT_FOR_TRANSMITTER_DONE_EVENT
+};
+
+struct piper_priv;
+
+struct piper_ps {
+ u32 beacon_int;
+ u16 aid;
+ volatile unsigned int scan_timer;
+ unsigned int sleep_time;
+ struct timer_list timer;
+ enum piper_ps_mode mode;
+ enum piper_ps_state state;
+ unsigned int this_event;
+ spinlock_t lock;
+ volatile bool power_management;
+ volatile bool poweredDown;
+ volatile bool rxTaskletRunning;
+ volatile bool allowTransmits;
+ volatile bool stopped_tx_queues;
+ volatile unsigned int frames_pending;
+};
+
+typedef void (*tx_skb_return_cb_t)(struct ieee80211_hw *hw,
+ struct sk_buff *skb);
+
+struct piper_queue {
+ struct sk_buff *skb;
+ tx_skb_return_cb_t skb_return_cb;
+};
+
+#define PIPER_TX_QUEUE_SIZE (16)
+#define NEXT_TX_QUEUE_INDEX(x) ((x+1) & 0xf)
+
+struct piper_priv {
+ const char *drv_name;
+ char debug_cmd[32];
+ u32 version;
+ struct piper_pdata *pdata;
+ struct ieee80211_hw *hw;
+ struct ieee80211_key_conf txKeyInfo;
+ struct ieee80211_cts ctsFrame;
+ struct ieee80211_rts rtsFrame;
+ struct ieee80211_rate *calibrationTxRate;
+ struct piper_stats pstats;
+ struct tasklet_struct rx_tasklet;
+ struct tasklet_struct tx_tasklet;
+ spinlock_t tx_tasklet_lock;
+ bool tx_tasklet_running;
+ spinlock_t tx_queue_lock;
+ struct piper_queue tx_queue[PIPER_TX_QUEUE_SIZE];
+ unsigned int tx_queue_head;
+ unsigned int tx_queue_tail;
+ unsigned int tx_queue_count;
+ bool expectingAck;
+ struct timer_list tx_timer;
+ struct timer_list led_timer;
+ enum led_states led_state;
+ struct piper_ps ps;
+ bool power_save_was_on_when_suspended;
+ struct access_ops *ac;
+ spinlock_t aesLock;
+ struct digi_rf_ops *rf;
+ void *__iomem vbase;
+ int irq;
+ tx_result_t tx_result;
+ int tx_signal_strength;
+
+ /* Function callbacks */
+ int (*init_hw) (struct piper_priv *, enum ieee80211_band);
+ int (*deinit_hw) (struct piper_priv *);
+ void (*set_irq_mask_bit) (struct piper_priv *, u32);
+ void (*clear_irq_mask_bit) (struct piper_priv *, u32);
+ u16(*get_next_beacon_backoff) (void);
+ int (*load_beacon) (struct piper_priv *, u8 *, u32);
+ int (*rand) (void);
+ void (*tx_calib_cb) (struct piper_priv *);
+ int (*set_antenna) (struct piper_priv *, enum antenna_select);
+ int (*set_tracking_constant) (struct piper_priv * piperp,
+ unsigned megahertz);
+ void (*adjust_max_agc) (struct piper_priv * piperp, unsigned int rssi,
+ _80211HeaderType * header);
+
+ /* General settings */
+ enum nl80211_iftype if_type;
+ bool areWeAssociated;
+ bool is_radio_on;
+ bool use_short_preamble;
+ int channel;
+ int tx_power;
+ u8 bssid[ETH_ALEN];
+ bool tx_cts;
+ bool tx_rts;
+ enum antenna_select antenna;
+ int power_duty;
+
+ /* AES stuff */
+ bool use_hw_aes;
+ u32 aes_key_count;
+ struct piperKeyInfo key[PIPER_MAX_KEYS];
+ u32 tx_aes_key;
+ u32 tx_aes_blob[AES_BLOB_LENGTH / sizeof(u32)];
+
+ /* IBSS */
+ piperBeaconInfo_t beacon;
+};
+
+struct access_ops {
+ spinlock_t reg_lock;
+ int (*wr_reg) (struct piper_priv *, u8 reg, u32 val, reg_op_t op);
+ u32(*rd_reg) (struct piper_priv *, u8 reg);
+ int (*wr_fifo) (struct piper_priv *, u8 addr, u8 * buf, int len);
+ int (*rd_fifo) (struct piper_priv *, u8 addr, u8 * buf, int len);
+};
+
+struct piper_pdata {
+ u8 macaddr[6];
+ int rst_gpio;
+ int irq_gpio;
+ int status_led_gpio;
+ int rf_transceiver;
+ wcd_data_t wcd;
+ int i2c_adapter_num;
+ struct piper_priv *piperp;
+
+ /* Platform callbacks */
+ void (*reset) (struct piper_priv *, int);
+ int (*init) (struct piper_priv *);
+ int (*late_init) (struct piper_priv *);
+ void (*set_led) (struct piper_priv *, enum wireless_led, int);
+ void (*early_resume) (struct piper_priv *);
+};
+
+/* main */
+int piper_alloc_hw(struct piper_priv **priv, size_t priv_sz);
+void piper_free_hw(struct piper_priv *priv);
+int piper_register_hw(struct piper_priv *priv, struct device *dev,
+ struct digi_rf_ops *rf);
+void piper_unregister_hw(struct piper_priv *priv);
+irqreturn_t piper_irq_handler(int irq, void *dev_id);
+void packet_tx_done(struct piper_priv *piperp,
+ tx_result_t result, int singalstrength);
+void piper_rx_tasklet(unsigned long context);
+void piper_tx_tasklet(unsigned long context);
+bool piper_prepare_aes_datablob(struct piper_priv *digi,
+ unsigned int keyIndex, u8 * aesBlob,
+ u8 * frame, u32 length, bool isTransmit);
+void piper_load_mac_firmware(struct piper_priv *piperp);
+void piper_load_dsp_firmware(struct piper_priv *piperp);
+int piper_spike_suppression(struct piper_priv *piperp, bool retry);
+void piper_reset_mac(struct piper_priv *piperp);
+void piper_set_macaddr(struct piper_priv *piperp);
+int piper_hw_tx_private(struct ieee80211_hw *hw, struct sk_buff *skb, tx_skb_return_cb_t fn);
+void piper_empty_tx_queue(struct piper_priv *piperp);
+int piper_tx_enqueue(struct piper_priv *piperp, struct sk_buff *skb, tx_skb_return_cb_t skb_return_cb);
+struct sk_buff *piper_tx_getqueue(struct piper_priv *piperp);
+bool piper_tx_queue_half_full(struct piper_priv *piperp);
+void piper_set_macaddr(struct piper_priv *piperp);
+void piper_MacEnterActiveMode(struct piper_priv *piperp, bool want_spike_suppression);
+int piper_MacEnterSleepMode(struct piper_priv *piperp, bool force);
+void piper_sendNullDataFrame(struct piper_priv *piperp, bool isPowerSaveOn);
+void piper_ps_rx_task_exiting(struct piper_priv *piperp);
+void piper_ps_scan_event(struct piper_priv *piperp);
+
+/*
+ * Defines for debugging function dumpRegisters
+ */
+#define MAIN_REGS (1)
+#define MAC_REGS (2)
+#define RF_REGS (4)
+#define FRAME_BUFFER_REGS (8)
+#define CTRL_STATUS_REGS (0x10)
+#define FIFO_REGS (0x20)
+#define IRQ_REGS (0x40)
+#define ALL_REGS (0xf)
+
+void digiWifiDumpRegisters(struct piper_priv *digi, unsigned int regs);
+void digiWifiDumpSkb(struct sk_buff *skb);
+
+extern void digiWifiDumpWordsAdd(unsigned int word);
+extern void digiWifiDumpWordsDump(void);
+extern void digiWifiDumpWordsReset(void);
+
+#endif /* __PIPER_H_ */