aboutsummaryrefslogtreecommitdiffstats
path: root/recipes/linux/linux-rp-2.6.26
diff options
context:
space:
mode:
Diffstat (limited to 'recipes/linux/linux-rp-2.6.26')
-rw-r--r--recipes/linux/linux-rp-2.6.26/binutils-buildid-arm.patch16
-rw-r--r--recipes/linux/linux-rp-2.6.26/collie-ucbfix.patch28
-rw-r--r--recipes/linux/linux-rp-2.6.26/collie.patch2601
-rw-r--r--recipes/linux/linux-rp-2.6.26/collie_keymap.patch420
-rw-r--r--recipes/linux/linux-rp-2.6.26/connectplus-prevent-oops-HACK.patch17
-rw-r--r--recipes/linux/linux-rp-2.6.26/connectplus-remove-ide-HACK.patch12
-rw-r--r--recipes/linux/linux-rp-2.6.26/defconfig-akita1799
-rw-r--r--recipes/linux/linux-rp-2.6.26/defconfig-bootcdx861994
-rw-r--r--recipes/linux/linux-rp-2.6.26/defconfig-c7x01809
-rw-r--r--recipes/linux/linux-rp-2.6.26/defconfig-collie1502
-rw-r--r--recipes/linux/linux-rp-2.6.26/defconfig-htcuniversal1308
-rw-r--r--recipes/linux/linux-rp-2.6.26/defconfig-hx20001733
-rw-r--r--recipes/linux/linux-rp-2.6.26/defconfig-poodle1761
-rw-r--r--recipes/linux/linux-rp-2.6.26/defconfig-qemuarm1582
-rw-r--r--recipes/linux/linux-rp-2.6.26/defconfig-qemux861993
-rw-r--r--recipes/linux/linux-rp-2.6.26/defconfig-spitz1799
-rw-r--r--recipes/linux/linux-rp-2.6.26/defconfig-zylonite1740
-rw-r--r--recipes/linux/linux-rp-2.6.26/hostap-monitor-mode.patch209
-rw-r--r--recipes/linux/linux-rp-2.6.26/hrw-hostapcard.patch34
-rw-r--r--recipes/linux/linux-rp-2.6.26/htcuni-acx.patch33527
-rw-r--r--recipes/linux/linux-rp-2.6.26/htcuni.patch7899
-rw-r--r--recipes/linux/linux-rp-2.6.26/pxa-serial-hack.patch90
-rw-r--r--recipes/linux/linux-rp-2.6.26/pxa_fb_overlay.patch26
-rw-r--r--recipes/linux/linux-rp-2.6.26/serial-add-support-for-non-standard-xtals-to-16c950-driver.patch155
-rw-r--r--recipes/linux/linux-rp-2.6.26/sharpsl-rc-r1.patch555
-rw-r--r--recipes/linux/linux-rp-2.6.26/spitz_h_rewrite.patch497
-rw-r--r--recipes/linux/linux-rp-2.6.26/usb-gadget27bp.patch26674
-rw-r--r--recipes/linux/linux-rp-2.6.26/versatile-armv6.patch17
-rw-r--r--recipes/linux/linux-rp-2.6.26/zaurus-i2c-init.patch99
-rw-r--r--recipes/linux/linux-rp-2.6.26/zylonite-boot.patch45
-rw-r--r--recipes/linux/linux-rp-2.6.26/zylonite_keypad-r0.patch1187
-rw-r--r--recipes/linux/linux-rp-2.6.26/zylonite_mtd-r0.patch4093
-rw-r--r--recipes/linux/linux-rp-2.6.26/zylonite_touch-r0.patch1548
33 files changed, 98769 insertions, 0 deletions
diff --git a/recipes/linux/linux-rp-2.6.26/binutils-buildid-arm.patch b/recipes/linux/linux-rp-2.6.26/binutils-buildid-arm.patch
new file mode 100644
index 0000000000..68e35e89e1
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.26/binutils-buildid-arm.patch
@@ -0,0 +1,16 @@
+---
+ arch/arm/kernel/vmlinux.lds.S | 1 +
+ 1 file changed, 1 insertion(+)
+
+Index: linux-2.6.22/arch/arm/kernel/vmlinux.lds.S
+===================================================================
+--- linux-2.6.22.orig/arch/arm/kernel/vmlinux.lds.S 2007-09-11 18:32:29.000000000 +0200
++++ linux-2.6.22/arch/arm/kernel/vmlinux.lds.S 2007-09-11 18:33:42.000000000 +0200
+@@ -94,6 +94,7 @@
+ TEXT_TEXT
+ SCHED_TEXT
+ LOCK_TEXT
++ *(.note.*)
+ #ifdef CONFIG_MMU
+ *(.fixup)
+ #endif
diff --git a/recipes/linux/linux-rp-2.6.26/collie-ucbfix.patch b/recipes/linux/linux-rp-2.6.26/collie-ucbfix.patch
new file mode 100644
index 0000000000..ea87e12197
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.26/collie-ucbfix.patch
@@ -0,0 +1,28 @@
+commit 7f4ecbe6bd6515f65f2720117e5b7d69e1648960
+Author: Thomas Kunze <thommycheck@gmx.de>
+Date: Sat Oct 18 00:27:36 2008 +0200
+
+ fix ucb driver
+
+Index: linux-2.6.26/drivers/mfd/mcp-sa11x0.c
+===================================================================
+--- linux-2.6.26.orig/drivers/mfd/mcp-sa11x0.c 2008-07-13 23:51:29.000000000 +0200
++++ linux-2.6.26/drivers/mfd/mcp-sa11x0.c 2008-10-18 01:48:40.964009879 +0200
+@@ -27,6 +27,7 @@
+ #include <asm/arch/mcp.h>
+
+ #include <asm/arch/assabet.h>
++#include <asm/arch/collie.h>
+
+ #include "mcp.h"
+
+@@ -169,6 +170,9 @@
+ if (machine_is_assabet()) {
+ ASSABET_BCR_set(ASSABET_BCR_CODEC_RST);
+ }
++ if (machine_is_collie()) {
++ GPSR |= COLLIE_GPIO_UCB1x00_RESET;
++ }
+
+ /*
+ * Setup the PPC unit correctly.
diff --git a/recipes/linux/linux-rp-2.6.26/collie.patch b/recipes/linux/linux-rp-2.6.26/collie.patch
new file mode 100644
index 0000000000..750be8ecf3
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.26/collie.patch
@@ -0,0 +1,2601 @@
+Index: linux-2.6.26/arch/arm/Kconfig
+===================================================================
+--- linux-2.6.26.orig/arch/arm/Kconfig 2008-07-13 23:51:29.000000000 +0200
++++ linux-2.6.26/arch/arm/Kconfig 2008-10-17 18:15:31.391792839 +0200
+@@ -967,7 +967,7 @@
+
+ config CPU_FREQ_SA1110
+ bool
+- depends on CPU_FREQ && (SA1100_ASSABET || SA1100_CERF || SA1100_PT_SYSTEM3)
++ depends on CPU_FREQ && (SA1100_ASSABET || SA1100_CERF || SA1100_PT_SYSTEM3 || SA1100_COLLIE)
+ default y
+
+ config CPU_FREQ_INTEGRATOR
+Index: linux-2.6.26/arch/arm/mach-sa1100/collie.c
+===================================================================
+--- linux-2.6.26.orig/arch/arm/mach-sa1100/collie.c 2008-07-13 23:51:29.000000000 +0200
++++ linux-2.6.26/arch/arm/mach-sa1100/collie.c 2008-10-17 18:15:31.391792839 +0200
+@@ -206,7 +206,7 @@
+ }
+
+ static struct flash_platform_data collie_flash_data = {
+- .map_name = "cfi_probe",
++ .map_name = "sharp",
+ .set_vpp = collie_set_vpp,
+ .parts = collie_partitions,
+ .nr_parts = ARRAY_SIZE(collie_partitions),
+Index: linux-2.6.26/arch/arm/mach-sa1100/dma.c
+===================================================================
+--- linux-2.6.26.orig/arch/arm/mach-sa1100/dma.c 2008-07-13 23:51:29.000000000 +0200
++++ linux-2.6.26/arch/arm/mach-sa1100/dma.c 2008-10-17 18:15:31.399789199 +0200
+@@ -39,7 +39,7 @@
+
+ static sa1100_dma_t dma_chan[SA1100_DMA_CHANNELS];
+
+-static spinlock_t dma_list_lock;
++static DEFINE_SPINLOCK(dma_list_lock);
+
+
+ static irqreturn_t dma_irq_handler(int irq, void *dev_id)
+Index: linux-2.6.26/drivers/input/keyboard/locomokbd.c
+===================================================================
+--- linux-2.6.26.orig/drivers/input/keyboard/locomokbd.c 2008-10-17 18:13:16.000000000 +0200
++++ linux-2.6.26/drivers/input/keyboard/locomokbd.c 2008-10-17 18:15:31.403791239 +0200
+@@ -272,6 +272,7 @@
+ for (i = 0; i < LOCOMOKBD_NUMKEYS; i++)
+ set_bit(locomokbd->keycode[i], input_dev->keybit);
+ clear_bit(0, input_dev->keybit);
++ locomo_writel(0, locomokbd->base + LOCOMO_KSC);
+
+ /* attempt to get the interrupt */
+ err = request_irq(dev->irq[0], locomokbd_interrupt, 0, "locomokbd", locomokbd);
+Index: linux-2.6.26/drivers/mfd/Kconfig
+===================================================================
+--- linux-2.6.26.orig/drivers/mfd/Kconfig 2008-10-17 18:13:21.000000000 +0200
++++ linux-2.6.26/drivers/mfd/Kconfig 2008-10-17 18:15:31.403791239 +0200
+@@ -77,4 +77,10 @@
+ tristate "Touchscreen interface support"
+ depends on MCP_UCB1200 && INPUT
+
++config MCP_COLLIE_TS
++ tristate "Touchscreen collie support"
++ depends on MCP_UCB1200 && INPUT && !MCP_UCB1200_TS
++ ---help---
++ Driver for touchscreen on collie - sharp sl-5500.
++
+ endmenu
+Index: linux-2.6.26/drivers/mfd/Makefile
+===================================================================
+--- linux-2.6.26.orig/drivers/mfd/Makefile 2008-10-17 18:13:21.000000000 +0200
++++ linux-2.6.26/drivers/mfd/Makefile 2008-10-17 18:15:31.407791679 +0200
+@@ -14,7 +14,7 @@
+ obj-$(CONFIG_MCP_SA11X0) += mcp-sa11x0.o
+ obj-$(CONFIG_MCP_UCB1200) += ucb1x00-core.o
+ obj-$(CONFIG_MCP_UCB1200_TS) += ucb1x00-ts.o
+-
++obj-$(CONFIG_MCP_COLLIE_TS) += collie-ts.o
+ ifeq ($(CONFIG_SA1100_ASSABET),y)
+ obj-$(CONFIG_MCP_UCB1200) += ucb1x00-assabet.o
+ endif
+Index: linux-2.6.26/drivers/mfd/collie-ts.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26/drivers/mfd/collie-ts.c 2008-10-17 18:15:31.415790559 +0200
+@@ -0,0 +1,449 @@
++/*
++ * Touchscreen driver for UCB1x00-based touchscreens
++ *
++ * Copyright (C) 2001 Russell King, All Rights Reserved.
++ * Copyright (C) 2005 Pavel Machek
++ *
++ * 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.
++ *
++ * 21-Jan-2002 <jco@ict.es> :
++ *
++ * Added support for synchronous A/D mode. This mode is useful to
++ * avoid noise induced in the touchpanel by the LCD, provided that
++ * the UCB1x00 has a valid LCD sync signal routed to its ADCSYNC pin.
++ * It is important to note that the signal connected to the ADCSYNC
++ * pin should provide pulses even when the LCD is blanked, otherwise
++ * a pen touch needed to unblank the LCD will never be read.
++ */
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/init.h>
++#include <linux/smp.h>
++#include <linux/smp_lock.h>
++#include <linux/sched.h>
++#include <linux/completion.h>
++#include <linux/delay.h>
++#include <linux/string.h>
++#include <linux/input.h>
++#include <linux/device.h>
++#include <linux/freezer.h>
++#include <linux/slab.h>
++#include <linux/kthread.h>
++#include <linux/semaphore.h>
++
++#include <asm/dma.h>
++#include <asm/arch/collie.h>
++#include <asm/mach-types.h>
++
++#include "ucb1x00.h"
++
++struct ucb1x00_ts {
++ struct input_dev *idev;
++ struct ucb1x00 *ucb;
++
++ wait_queue_head_t irq_wait;
++ struct task_struct *rtask;
++ u16 x_res;
++ u16 y_res;
++
++ unsigned int adcsync:1;
++};
++
++static int adcsync;
++
++/**********************************
++
++ ................
++ . . = 340
++ . .
++ . ^.
++ . ^.
++ . ^.
++ . ^.
++ . .
++ . X. = 10
++ . <<<<<<<< Y .
++ ................
++ . Sharp =200
++ . .
++ . - O - .
++ . .
++ ................
++
++**********************************/
++
++
++static inline void ucb1x00_ts_evt_add(struct ucb1x00_ts *ts, u16 pressure, u16 x, u16 y)
++{
++ struct input_dev *idev = ts->idev;
++
++ input_report_abs(idev, ABS_X, x);
++ input_report_abs(idev, ABS_Y, y);
++ input_report_abs(idev, ABS_PRESSURE, pressure);
++ input_report_key(idev, BTN_TOUCH, 1);
++ input_sync(idev);
++}
++
++static inline void ucb1x00_ts_event_release(struct ucb1x00_ts *ts)
++{
++ struct input_dev *idev = ts->idev;
++
++ input_report_abs(idev, ABS_PRESSURE, 0);
++ input_report_key(idev, BTN_TOUCH, 0);
++ input_sync(idev);
++}
++
++/*
++ * Switch to interrupt mode. This set touchscreen to interrupt
++ * mode, so that chip is able to send interrupt.
++ */
++static inline void ucb1x00_ts_mode_int(struct ucb1x00_ts *ts)
++{
++ ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
++ UCB_TS_CR_TSMX_POW | UCB_TS_CR_TSPX_POW |
++ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_GND |
++ UCB_TS_CR_MODE_INT);
++}
++
++/*
++ * Switch to pressure mode, and read pressure. We don't need to wait
++ * here, since both plates are being driven.
++ *
++ * set_read_pressure() in sharp code
++ */
++static inline void ucb1x00_ts_read_pressure(struct ucb1x00_ts *ts)
++{
++ ucb1x00_io_write(ts->ucb, COLLIE_TC35143_GPIO_TBL_CHK, 0);
++ ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
++ UCB_TS_CR_TSPX_POW | UCB_TS_CR_TSMX_POW |
++ UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
++
++ ucb1x00_reg_write(ts->ucb, UCB_ADC_CR, ts->ucb->adc_cr |
++ UCB_ADC_INP_AD2 |
++ UCB_ADC_SYNC_ENA);
++ udelay(100);
++ ucb1x00_reg_write(ts->ucb, UCB_ADC_CR, ts->ucb->adc_cr |
++ UCB_ADC_INP_AD2 |
++ UCB_ADC_SYNC_ENA | UCB_ADC_START);
++}
++
++/*
++ * Switch to X position mode and measure Y plate. We switch the plate
++ * configuration in pressure mode, then switch to position mode. This
++ * gives a faster response time. Even so, we need to wait about 55us
++ * for things to stabilise.
++ */
++static inline void ucb1x00_ts_read_xpos(struct ucb1x00_ts *ts)
++{
++ ucb1x00_io_write(ts->ucb, 0, COLLIE_TC35143_GPIO_TBL_CHK);
++ ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
++ UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
++ UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
++
++
++ ucb1x00_reg_write(ts->ucb, UCB_ADC_CR, ts->ucb->adc_cr |
++ UCB_ADC_INP_TSPY | UCB_ADC_SYNC_ENA);
++ udelay(100);
++ ucb1x00_reg_write(ts->ucb, UCB_ADC_CR, ts->ucb->adc_cr |
++ UCB_ADC_INP_TSPY | UCB_ADC_SYNC_ENA |
++ UCB_ADC_START);
++}
++
++/*
++ * Switch to Y position mode and measure X plate. We switch the plate
++ * configuration in pressure mode, then switch to position mode. This
++ * gives a faster response time. Even so, we need to wait about 55us
++ * for things to stabilise.
++ */
++static inline void ucb1x00_ts_read_ypos(struct ucb1x00_ts *ts)
++{
++ ucb1x00_io_write(ts->ucb, 0, COLLIE_TC35143_GPIO_TBL_CHK);
++
++ ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
++ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
++ UCB_TS_CR_MODE_POS | UCB_TS_CR_BIAS_ENA);
++
++
++ ucb1x00_reg_write(ts->ucb, UCB_ADC_CR, ts->ucb->adc_cr |
++ UCB_ADC_INP_TSPX | UCB_ADC_SYNC_ENA);
++ udelay(100);
++ ucb1x00_reg_write(ts->ucb, UCB_ADC_CR, ts->ucb->adc_cr |
++ UCB_ADC_INP_TSPX | UCB_ADC_SYNC_ENA |
++ UCB_ADC_START);
++}
++
++/*
++ * Switch to X plate resistance mode. Set MX to ground, PX to
++ * supply. Measure current.
++ */
++static inline unsigned int ucb1x00_ts_read_xres(struct ucb1x00_ts *ts)
++{
++ ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
++ UCB_TS_CR_TSMX_GND | UCB_TS_CR_TSPX_POW |
++ UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
++ return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync);
++}
++
++/*
++ * Switch to Y plate resistance mode. Set MY to ground, PY to
++ * supply. Measure current.
++ */
++static inline unsigned int ucb1x00_ts_read_yres(struct ucb1x00_ts *ts)
++{
++ ucb1x00_reg_write(ts->ucb, UCB_TS_CR,
++ UCB_TS_CR_TSMY_GND | UCB_TS_CR_TSPY_POW |
++ UCB_TS_CR_MODE_PRES | UCB_TS_CR_BIAS_ENA);
++ return ucb1x00_adc_read(ts->ucb, 0, ts->adcsync);
++}
++
++/*
++ * This is a RT kernel thread that handles the ADC accesses
++ * (mainly so we can use semaphores in the UCB1200 core code
++ * to serialise accesses to the ADC).
++ */
++static int ucb1x00_thread(void *_ts)
++{
++ struct ucb1x00_ts *ts = _ts;
++ struct task_struct *tsk = current;
++ DECLARE_WAITQUEUE(wait, tsk);
++ int state;
++
++ /*
++ * We could run as a real-time thread. However, thus far
++ * this doesn't seem to be necessary.
++ */
++
++ add_wait_queue(&ts->irq_wait, &wait);
++
++ while (!kthread_should_stop()) {
++ unsigned int data[3];
++
++ for (state=0; state<3; state++) {
++
++ ucb1x00_adc_enable(ts->ucb);
++ ucb1x00_enable_irq(ts->ucb, UCB_IRQ_ADC, UCB_FALLING);
++ switch (state) {
++ /* Order matters here; last measurement seems to be more noisy then the
++ rest, and we care about pressure least */
++ case 2: ucb1x00_ts_read_pressure(ts);
++ break;
++ case 0: ucb1x00_ts_read_ypos(ts);
++ break;
++ case 1: ucb1x00_ts_read_xpos(ts);
++ break;
++ }
++ /* wait for adc */
++ try_to_freeze();
++ schedule_timeout(1000 * HZ);
++ ucb1x00_disable_irq(ts->ucb, UCB_IRQ_ADC, UCB_FALLING);
++ data[state] = UCB_ADC_DAT(ucb1x00_reg_read(ts->ucb, UCB_ADC_DATA));
++ ucb1x00_adc_disable(ts->ucb);
++ }
++
++ /* If not pressed any more, try to sleep! */
++ if (data[2] < 300) {
++ set_task_state(tsk, TASK_INTERRUPTIBLE);
++ ucb1x00_enable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_RISING);
++ ucb1x00_ts_mode_int(ts);
++ ucb1x00_disable(ts->ucb);
++ ucb1x00_ts_event_release(ts);
++ try_to_freeze();
++ schedule_timeout(1000 * HZ);
++ ucb1x00_disable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_RISING);
++ ucb1x00_enable(ts->ucb);
++ } else {
++ ucb1x00_ts_evt_add(ts, data[2], data[1], data[0]);
++ }
++ ucb1x00_disable(ts->ucb);
++ msleep(20);
++ ucb1x00_enable(ts->ucb);
++ }
++
++ remove_wait_queue(&ts->irq_wait, &wait);
++
++ ts->rtask = NULL;
++ return 0;
++}
++
++/*
++ * We only detect touch screen _touches_ with this interrupt
++ * handler, and even then we just schedule our task.
++ */
++static void ucb1x00_ts_irq(int idx, void *id)
++{
++ struct ucb1x00_ts *ts = id;
++ wake_up(&ts->irq_wait);
++}
++
++static void ucb1x00_adc_irq(int idx, void *id)
++{
++ struct ucb1x00_ts *ts = id;
++ wake_up(&ts->irq_wait);
++}
++
++static int ucb1x00_ts_open(struct input_dev *idev)
++{
++ struct ucb1x00_ts *ts = input_get_drvdata(idev);
++ int ret = 0;
++
++ BUG_ON(ts->rtask);
++
++ init_waitqueue_head(&ts->irq_wait);
++
++ ret = ucb1x00_hook_irq(ts->ucb, UCB_IRQ_TSPX, ucb1x00_ts_irq, ts);
++ if (ret < 0)
++ return ret;
++
++ ret = ucb1x00_hook_irq(ts->ucb, UCB_IRQ_ADC, ucb1x00_adc_irq, ts);
++ if (ret < 0) {
++ ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts);
++ return ret;
++ }
++
++ ucb1x00_enable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_RISING);
++
++ /*
++ * If we do this at all, we should allow the user to
++ * measure and read the X and Y resistance at any time.
++ */
++ ucb1x00_adc_enable(ts->ucb);
++ ts->x_res = ucb1x00_ts_read_xres(ts);
++ ts->y_res = ucb1x00_ts_read_yres(ts);
++ ucb1x00_adc_disable(ts->ucb);
++
++ if (machine_is_collie()) {
++ ucb1x00_io_set_dir(ts->ucb, 0, COLLIE_TC35143_GPIO_TBL_CHK);
++ }
++
++ ts->rtask = kthread_run(ucb1x00_thread, ts, "ktsd");
++ if (!IS_ERR(ts->rtask)) {
++ ret = 0;
++ } else {
++ ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts);
++ ts->rtask = NULL;
++ ret = -EFAULT;
++ }
++
++ return ret;
++}
++
++/*
++ * Release touchscreen resources. Disable IRQs.
++ */
++static void ucb1x00_ts_close(struct input_dev *idev)
++{
++ struct ucb1x00_ts *ts = input_get_drvdata(idev);
++
++ if (ts->rtask)
++ kthread_stop(ts->rtask);
++
++ ucb1x00_enable(ts->ucb);
++ ucb1x00_free_irq(ts->ucb, UCB_IRQ_TSPX, ts);
++ ucb1x00_free_irq(ts->ucb, UCB_IRQ_ADC, ts);
++ ucb1x00_reg_write(ts->ucb, UCB_TS_CR, 0);
++ ucb1x00_disable(ts->ucb);
++}
++
++#ifdef CONFIG_PM
++static int ucb1x00_ts_resume(struct ucb1x00_dev *dev)
++{
++ struct ucb1x00_ts *ts = dev->priv;
++
++ if (ts->rtask != NULL) {
++ /*
++ * Restart the TS thread to ensure the
++ * TS interrupt mode is set up again
++ * after sleep.
++ */
++ wake_up(&ts->irq_wait);
++ }
++ return 0;
++}
++#else
++#define ucb1x00_ts_resume NULL
++#endif
++
++
++/*
++ * Initialisation.
++ */
++static int ucb1x00_ts_add(struct ucb1x00_dev *dev)
++{
++ struct ucb1x00_ts *ts;
++ struct input_dev *idev;
++ int err;
++
++ ts = kzalloc(sizeof(struct ucb1x00_ts), GFP_KERNEL);
++ idev = input_allocate_device();
++ if (!ts || !idev) {
++ err = -ENOMEM;
++ goto fail;
++ }
++
++ ts->ucb = dev->ucb;
++ ts->idev = idev;
++ ts->adcsync = adcsync ? UCB_SYNC : UCB_NOSYNC;
++
++ input_set_drvdata(idev, ts);
++ idev->name = "Touchscreen panel";
++ idev->id.product = ts->ucb->id;
++ idev->open = ucb1x00_ts_open;
++ idev->close = ucb1x00_ts_close;
++
++ __set_bit(EV_ABS, idev->evbit);
++ __set_bit(ABS_X, idev->absbit);
++ __set_bit(ABS_Y, idev->absbit);
++ __set_bit(ABS_PRESSURE, idev->absbit);
++
++ input_set_abs_params(ts->idev, ABS_X, 0, 450, 0, 0);
++ input_set_abs_params(ts->idev, ABS_Y, 200, 800, 0, 0);
++ input_set_abs_params(ts->idev, ABS_PRESSURE, 400, 800, 0, 0);
++
++
++ err = input_register_device(idev);
++ if (err)
++ goto fail;
++
++ dev->priv = ts;
++
++ return 0;
++
++ fail:
++ input_free_device(idev);
++ kfree(ts);
++ return err;
++}
++
++static void ucb1x00_ts_remove(struct ucb1x00_dev *dev)
++{
++ struct ucb1x00_ts *ts = dev->priv;
++
++ input_unregister_device(ts->idev);
++ kfree(ts);
++}
++
++static struct ucb1x00_driver ucb1x00_ts_driver = {
++ .add = ucb1x00_ts_add,
++ .remove = ucb1x00_ts_remove,
++ .resume = ucb1x00_ts_resume,
++};
++
++static int __init ucb1x00_ts_init(void)
++{
++ return ucb1x00_register_driver(&ucb1x00_ts_driver);
++}
++
++static void __exit ucb1x00_ts_exit(void)
++{
++ ucb1x00_unregister_driver(&ucb1x00_ts_driver);
++}
++
++module_param(adcsync, int, 0444);
++module_init(ucb1x00_ts_init);
++module_exit(ucb1x00_ts_exit);
++
++MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>");
++MODULE_DESCRIPTION("UCB1x00 touchscreen driver");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.26/drivers/mfd/ucb1x00.h
+===================================================================
+--- linux-2.6.26.orig/drivers/mfd/ucb1x00.h 2008-07-13 23:51:29.000000000 +0200
++++ linux-2.6.26/drivers/mfd/ucb1x00.h 2008-10-17 18:15:31.415790559 +0200
+@@ -34,7 +34,10 @@
+ #define UCB_IE_TCLIP (1 << 14)
+ #define UCB_IE_ACLIP (1 << 15)
+
++/* UCB1200 irqs */
++#define UCB_IRQ_ADC 11
+ #define UCB_IRQ_TSPX 12
++#define UCB_IRQ_TSMX 13
+
+ #define UCB_TC_A 0x05
+ #define UCB_TC_A_LOOP (1 << 7) /* UCB1200 */
+Index: linux-2.6.26/drivers/mtd/chips/Kconfig
+===================================================================
+--- linux-2.6.26.orig/drivers/mtd/chips/Kconfig 2008-07-13 23:51:29.000000000 +0200
++++ linux-2.6.26/drivers/mtd/chips/Kconfig 2008-10-17 18:15:31.419791479 +0200
+@@ -239,5 +239,13 @@
+ used for XIP purposes. If you're not sure what this is all about
+ then say N.
+
++config MTD_SHARP
++ tristate "pre-CFI Sharp chip support"
++ depends on MTD
++ help
++ This option enables support for flash chips using Sharp-compatible
++ commands, including some which are not CFI-compatible and hence
++ cannot be used with the CONFIG_MTD_CFI_INTELxxx options.
++
+ endmenu
+
+Index: linux-2.6.26/drivers/mtd/chips/Makefile
+===================================================================
+--- linux-2.6.26.orig/drivers/mtd/chips/Makefile 2008-07-13 23:51:29.000000000 +0200
++++ linux-2.6.26/drivers/mtd/chips/Makefile 2008-10-17 18:15:31.419791479 +0200
+@@ -12,4 +12,5 @@
+ obj-$(CONFIG_MTD_JEDECPROBE) += jedec_probe.o
+ obj-$(CONFIG_MTD_RAM) += map_ram.o
+ obj-$(CONFIG_MTD_ROM) += map_rom.o
++obj-$(CONFIG_MTD_SHARP) += sharp.o
+ obj-$(CONFIG_MTD_ABSENT) += map_absent.o
+Index: linux-2.6.26/drivers/mtd/chips/sharp.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26/drivers/mtd/chips/sharp.c 2008-10-17 18:15:31.423790399 +0200
+@@ -0,0 +1,645 @@
++/*
++ * MTD chip driver for pre-CFI Sharp flash chips
++ *
++ * Copyright 2000,2001 David A. Schleef <ds@schleef.org>
++ * 2000,2001 Lineo, Inc.
++ *
++ * $Id: sharp.c,v 1.17 2005/11/29 14:28:28 gleixner Exp $
++ *
++ * Devices supported:
++ * LH28F016SCT Symmetrical block flash memory, 2Mx8
++ * LH28F008SCT Symmetrical block flash memory, 1Mx8
++ *
++ * Documentation:
++ * http://www.sharpmeg.com/datasheets/memic/flashcmp/
++ * http://www.sharpmeg.com/datasheets/memic/flashcmp/01symf/16m/016sctl9.pdf
++ * 016sctl9.pdf
++ *
++ * Limitations:
++ * This driver only supports 4x1 arrangement of chips.
++ * Not tested on anything but PowerPC.
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/types.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <linux/init.h>
++#include <linux/interrupt.h>
++#include <linux/mtd/map.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/cfi.h>
++#include <linux/delay.h>
++#include <linux/init.h>
++
++#define CMD_RESET 0xffffffff
++#define CMD_READ_ID 0x90909090
++#define CMD_READ_STATUS 0x70707070
++#define CMD_CLEAR_STATUS 0x50505050
++#define CMD_BLOCK_ERASE_1 0x20202020
++#define CMD_BLOCK_ERASE_2 0xd0d0d0d0
++#define CMD_BYTE_WRITE 0x40404040
++#define CMD_SUSPEND 0xb0b0b0b0
++#define CMD_RESUME 0xd0d0d0d0
++#define CMD_SET_BLOCK_LOCK_1 0x60606060
++#define CMD_SET_BLOCK_LOCK_2 0x01010101
++#define CMD_SET_MASTER_LOCK_1 0x60606060
++#define CMD_SET_MASTER_LOCK_2 0xf1f1f1f1
++#define CMD_CLEAR_BLOCK_LOCKS_1 0x60606060
++#define CMD_CLEAR_BLOCK_LOCKS_2 0xd0d0d0d0
++
++#define SR_READY 0x80808080 // 1 = ready
++#define SR_ERASE_SUSPEND 0x40404040 // 1 = block erase suspended
++#define SR_ERROR_ERASE 0x20202020 // 1 = error in block erase or clear lock bits
++#define SR_ERROR_WRITE 0x10101010 // 1 = error in byte write or set lock bit
++#define SR_VPP 0x08080808 // 1 = Vpp is low
++#define SR_WRITE_SUSPEND 0x04040404 // 1 = byte write suspended
++#define SR_PROTECT 0x02020202 // 1 = lock bit set
++#define SR_RESERVED 0x01010101
++
++#define SR_ERRORS (SR_ERROR_ERASE|SR_ERROR_WRITE|SR_VPP|SR_PROTECT)
++
++#define BLOCK_MASK 0xfffe0000
++
++/* Configuration options */
++
++#define AUTOUNLOCK /* automatically unlocks blocks before erasing */
++
++static struct mtd_info *sharp_probe(struct map_info *);
++
++static int sharp_probe_map(struct map_info *map, struct mtd_info *mtd);
++
++static int sharp_read(struct mtd_info *mtd, loff_t from, size_t len,
++ size_t *retlen, u_char *buf);
++static int sharp_write(struct mtd_info *mtd, loff_t from, size_t len,
++ size_t *retlen, const u_char *buf);
++static int sharp_erase(struct mtd_info *mtd, struct erase_info *instr);
++static void sharp_sync(struct mtd_info *mtd);
++static int sharp_suspend(struct mtd_info *mtd);
++static void sharp_resume(struct mtd_info *mtd);
++static void sharp_destroy(struct mtd_info *mtd);
++
++static int sharp_write_oneword(struct map_info *map, struct flchip *chip,
++ unsigned long adr, __u32 datum);
++static int sharp_erase_oneblock(struct map_info *map, struct flchip *chip,
++ unsigned long adr);
++#ifdef AUTOUNLOCK
++static inline void sharp_unlock_oneblock(struct map_info *map, struct flchip *chip,
++ unsigned long adr);
++#endif
++
++
++struct sharp_info{
++ struct flchip *chip;
++ int bogus;
++ int chipshift;
++ int numchips;
++ struct flchip chips[1];
++};
++
++static void sharp_destroy(struct mtd_info *mtd);
++
++static struct mtd_chip_driver sharp_chipdrv = {
++ .probe = sharp_probe,
++ .destroy = sharp_destroy,
++ .name = "sharp",
++ .module = THIS_MODULE
++};
++
++static void sharp_udelay(unsigned long i) {
++ if (in_interrupt()) {
++ udelay(i);
++ } else {
++ schedule();
++ }
++}
++
++static struct mtd_info *sharp_probe(struct map_info *map)
++{
++ struct mtd_info *mtd = NULL;
++ struct sharp_info *sharp = NULL;
++ int width;
++
++ mtd = kzalloc(sizeof(*mtd), GFP_KERNEL);
++ if(!mtd)
++ return NULL;
++
++ sharp = kzalloc(sizeof(*sharp), GFP_KERNEL);
++ if(!sharp) {
++ kfree(mtd);
++ return NULL;
++ }
++
++ width = sharp_probe_map(map,mtd);
++ if(!width){
++ kfree(mtd);
++ kfree(sharp);
++ return NULL;
++ }
++
++ mtd->priv = map;
++ mtd->type = MTD_NORFLASH;
++ mtd->erase = sharp_erase;
++ mtd->read = sharp_read;
++ mtd->write = sharp_write;
++ mtd->sync = sharp_sync;
++ mtd->suspend = sharp_suspend;
++ mtd->resume = sharp_resume;
++ mtd->flags = MTD_CAP_NORFLASH;
++ mtd->writesize = 1;
++ mtd->name = map->name;
++
++ sharp->chipshift = 24;
++ sharp->numchips = 1;
++ sharp->chips[0].start = 0;
++ sharp->chips[0].state = FL_READY;
++ sharp->chips[0].mutex = &sharp->chips[0]._spinlock;
++ sharp->chips[0].word_write_time = 0;
++ init_waitqueue_head(&sharp->chips[0].wq);
++ spin_lock_init(&sharp->chips[0]._spinlock);
++
++ map->fldrv = &sharp_chipdrv;
++ map->fldrv_priv = sharp;
++
++ __module_get(THIS_MODULE);
++ return mtd;
++}
++
++static inline void sharp_send_cmd(struct map_info *map, unsigned long cmd, unsigned long adr)
++{
++ map_word map_cmd;
++ map_cmd.x[0] = cmd;
++ map_write(map, map_cmd, adr);
++}
++
++static int sharp_probe_map(struct map_info *map,struct mtd_info *mtd)
++{
++ map_word tmp, read0, read4;
++ unsigned long base = 0;
++ int width = 4;
++
++ tmp = map_read(map, base+0);
++
++ sharp_send_cmd(map, CMD_READ_ID, base+0);
++
++ read0 = map_read(map, base+0);
++ read4 = map_read(map, base+4);
++ if (read0.x[0] == 0x00b000b0) {
++ printk("Sharp chip, %lx, %lx, width = %d\n", read0.x[0], read4.x[0], width);
++ /* Prints b000b0, b000b0, width = 4 on collie */
++ switch(read4.x[0]){
++ case 0xaaaaaaaa:
++ case 0xa0a0a0a0:
++ /* aa - LH28F016SCT-L95 2Mx8, 32 64k blocks*/
++ /* a0 - LH28F016SCT-Z4 2Mx8, 32 64k blocks*/
++ mtd->erasesize = 0x10000 * width;
++ mtd->size = 0x200000 * width;
++ return width;
++ case 0xa6a6a6a6:
++ /* a6 - LH28F008SCT-L12 1Mx8, 16 64k blocks*/
++ /* a6 - LH28F008SCR-L85 1Mx8, 16 64k blocks*/
++ mtd->erasesize = 0x10000 * width;
++ mtd->size = 0x100000 * width;
++ return width;
++ case 0x00b000b0:
++ /* a6 - LH28F640BFHE 8 64k * 2 chip blocks*/
++ mtd->erasesize = 0x10000 * width / 2;
++ mtd->size = 0x800000 * width / 2;
++ return width;
++ default:
++ printk("Sort-of looks like sharp flash, 0x%08lx 0x%08lx\n",
++ read0.x[0], read4.x[0]);
++ }
++ } else if ((map_read(map, base+0).x[0] == CMD_READ_ID)){
++ /* RAM, probably */
++ printk("Looks like RAM\n");
++ map_write(map, tmp, base+0);
++ }else{
++ printk("Doesn't look like sharp flash, 0x%08lx 0x%08lx\n",
++ read0.x[0], read4.x[0]);
++ }
++
++ return 0;
++}
++
++/* This function returns with the chip->mutex lock held. */
++static int sharp_wait(struct map_info *map, struct flchip *chip)
++{
++ map_word status;
++ unsigned long timeo = jiffies + HZ;
++ DECLARE_WAITQUEUE(wait, current);
++ int adr = 0;
++
++retry:
++ spin_lock_bh(chip->mutex);
++
++ switch (chip->state) {
++ case FL_READY:
++ sharp_send_cmd(map, CMD_READ_STATUS, adr);
++ chip->state = FL_STATUS;
++ case FL_STATUS:
++ status = map_read(map, adr);
++ if ((status.x[0] & SR_READY) == SR_READY)
++ break;
++ spin_unlock_bh(chip->mutex);
++ if (time_after(jiffies, timeo)) {
++ printk("Waiting for chip to be ready timed out in erase\n");
++ return -EIO;
++ }
++ sharp_udelay(1);
++ goto retry;
++ default:
++ set_current_state(TASK_INTERRUPTIBLE);
++ add_wait_queue(&chip->wq, &wait);
++
++ spin_unlock_bh(chip->mutex);
++
++ sharp_udelay(1);
++
++ set_current_state(TASK_RUNNING);
++ remove_wait_queue(&chip->wq, &wait);
++
++ if(signal_pending(current))
++ return -EINTR;
++
++ timeo = jiffies + HZ;
++
++ goto retry;
++ }
++
++ sharp_send_cmd(map, CMD_RESET, adr);
++
++ chip->state = FL_READY;
++
++ return 0;
++}
++
++static void sharp_release(struct flchip *chip)
++{
++ wake_up(&chip->wq);
++ spin_unlock_bh(chip->mutex);
++}
++
++static int sharp_read(struct mtd_info *mtd, loff_t from, size_t len,
++ size_t *retlen, u_char *buf)
++{
++ struct map_info *map = mtd->priv;
++ struct sharp_info *sharp = map->fldrv_priv;
++ int chipnum;
++ int ret = 0;
++ int ofs = 0;
++
++ chipnum = (from >> sharp->chipshift);
++ ofs = from & ((1 << sharp->chipshift)-1);
++
++ *retlen = 0;
++
++ while(len){
++ unsigned long thislen;
++
++ if(chipnum>=sharp->numchips)
++ break;
++
++ thislen = len;
++ if(ofs+thislen >= (1<<sharp->chipshift))
++ thislen = (1<<sharp->chipshift) - ofs;
++
++ ret = sharp_wait(map,&sharp->chips[chipnum]);
++ if(ret<0)
++ break;
++
++ map_copy_from(map,buf,ofs,thislen);
++
++ sharp_release(&sharp->chips[chipnum]);
++
++ *retlen += thislen;
++ len -= thislen;
++ buf += thislen;
++
++ ofs = 0;
++ chipnum++;
++ }
++ return ret;
++}
++
++static int sharp_write(struct mtd_info *mtd, loff_t to, size_t len,
++ size_t *retlen, const u_char *buf)
++{
++ struct map_info *map = mtd->priv;
++ struct sharp_info *sharp = map->fldrv_priv;
++ int ret = 0;
++ int i,j;
++ int chipnum;
++ unsigned long ofs;
++ union { u32 l; unsigned char uc[4]; } tbuf;
++
++ *retlen = 0;
++
++ while(len){
++ tbuf.l = 0xffffffff;
++ chipnum = to >> sharp->chipshift;
++ ofs = to & ((1<<sharp->chipshift)-1);
++
++ j=0;
++ for(i=ofs&3;i<4 && len;i++){
++ tbuf.uc[i] = *buf;
++ buf++;
++ to++;
++ len--;
++ j++;
++ }
++ sharp_write_oneword(map, &sharp->chips[chipnum], ofs&~3, tbuf.l);
++ if(ret<0)
++ return ret;
++ (*retlen)+=j;
++ }
++
++ return 0;
++}
++
++static int sharp_write_oneword(struct map_info *map, struct flchip *chip,
++ unsigned long adr, __u32 datum)
++{
++ int ret;
++ int try;
++ int i;
++ map_word data, status;
++
++ status.x[0] = 0;
++ ret = sharp_wait(map,chip);
++ if (ret < 0)
++ return ret;
++
++ for (try=0; try<10; try++) {
++ long timeo;
++
++ sharp_send_cmd(map, CMD_BYTE_WRITE, adr);
++ /* cpu_to_le32 -> hack to fix the writel be->le conversion */
++ data.x[0] = cpu_to_le32(datum);
++ map_write(map, data, adr);
++
++ chip->state = FL_WRITING;
++ timeo = jiffies + (HZ/2);
++
++ sharp_send_cmd(map, CMD_READ_STATUS, adr);
++ for(i=0;i<100;i++){
++ status = map_read(map, adr);
++ if((status.x[0] & SR_READY) == SR_READY)
++ break;
++ }
++#ifdef AUTOUNLOCK
++ if (status.x[0] & SR_PROTECT) { /* lock block */
++ sharp_send_cmd(map, CMD_CLEAR_STATUS, adr);
++ sharp_unlock_oneblock(map,chip,adr);
++ sharp_send_cmd(map, CMD_CLEAR_STATUS, adr);
++ sharp_send_cmd(map, CMD_RESET, adr);
++ continue;
++ }
++#endif
++ if(i==100){
++ printk("sharp: timed out writing\n");
++ }
++
++ if (!(status.x[0] & SR_ERRORS))
++ break;
++
++ printk("sharp: error writing byte at addr=%08lx status=%08lx\n", adr, status.x[0]);
++
++ sharp_send_cmd(map, CMD_CLEAR_STATUS, adr);
++ }
++ sharp_send_cmd(map, CMD_RESET, adr);
++ chip->state = FL_READY;
++
++ sharp_release(chip);
++
++ return 0;
++}
++
++static int sharp_erase(struct mtd_info *mtd, struct erase_info *instr)
++{
++ struct map_info *map = mtd->priv;
++ struct sharp_info *sharp = map->fldrv_priv;
++ unsigned long adr,len;
++ int chipnum, ret=0;
++
++ if(instr->addr & (mtd->erasesize - 1))
++ return -EINVAL;
++ if(instr->len & (mtd->erasesize - 1))
++ return -EINVAL;
++ if(instr->len + instr->addr > mtd->size)
++ return -EINVAL;
++
++ chipnum = instr->addr >> sharp->chipshift;
++ adr = instr->addr & ((1<<sharp->chipshift)-1);
++ len = instr->len;
++
++ while(len){
++ ret = sharp_erase_oneblock(map, &sharp->chips[chipnum], adr);
++ if(ret)return ret;
++
++ if (adr >= 0xfe0000) {
++ adr += mtd->erasesize / 8;
++ len -= mtd->erasesize / 8;
++ } else {
++ adr += mtd->erasesize;
++ len -= mtd->erasesize;
++ }
++ if(adr >> sharp->chipshift){
++ adr = 0;
++ chipnum++;
++ if(chipnum>=sharp->numchips)
++ break;
++ }
++ }
++
++ instr->state = MTD_ERASE_DONE;
++ mtd_erase_callback(instr);
++
++ return 0;
++}
++
++static inline int sharp_do_wait_for_ready(struct map_info *map, struct flchip *chip,
++ unsigned long adr)
++{
++ int ret;
++ unsigned long timeo;
++ map_word status;
++ DECLARE_WAITQUEUE(wait, current);
++
++ sharp_send_cmd(map, CMD_READ_STATUS, adr);
++ status = map_read(map, adr);
++
++ timeo = jiffies + HZ * 10;
++
++ while (time_before(jiffies, timeo)) {
++ sharp_send_cmd(map, CMD_READ_STATUS, adr);
++ status = map_read(map, adr);
++ if ((status.x[0] & SR_READY) == SR_READY) {
++ ret = 0;
++ goto out;
++ }
++ set_current_state(TASK_INTERRUPTIBLE);
++ add_wait_queue(&chip->wq, &wait);
++
++ spin_unlock_bh(chip->mutex);
++
++ schedule_timeout(1);
++ schedule();
++
++ spin_lock_bh(chip->mutex);
++
++ remove_wait_queue(&chip->wq, &wait);
++ set_current_state(TASK_RUNNING);
++ }
++ ret = -ETIME;
++out:
++ return ret;
++}
++
++static int sharp_erase_oneblock(struct map_info *map, struct flchip *chip,
++ unsigned long adr)
++{
++ int ret;
++ map_word status;
++
++ ret = sharp_wait(map,chip);
++ if (ret < 0)
++ return ret;
++
++#ifdef AUTOUNLOCK
++ /* This seems like a good place to do an unlock */
++ sharp_unlock_oneblock(map,chip,adr);
++#endif
++
++ sharp_send_cmd(map, CMD_BLOCK_ERASE_1, adr);
++ sharp_send_cmd(map, CMD_BLOCK_ERASE_2, adr);
++
++ chip->state = FL_ERASING;
++
++ ret = sharp_do_wait_for_ready(map,chip,adr);
++ if(ret<0) {
++ spin_unlock_bh(chip->mutex);
++ return ret;
++ }
++
++ sharp_send_cmd(map, CMD_READ_STATUS, adr);
++ status = map_read(map, adr);
++
++ if (!(status.x[0] & SR_ERRORS)) {
++ sharp_send_cmd(map, CMD_RESET, adr);
++ chip->state = FL_READY;
++ spin_unlock_bh(chip->mutex);
++ return 0;
++ }
++
++ printk("sharp: error erasing block at addr=%08lx status=%08lx\n", adr, status.x[0]);
++ sharp_send_cmd(map, CMD_CLEAR_STATUS, adr);
++
++ sharp_release(chip);
++
++ return -EIO;
++}
++
++#ifdef AUTOUNLOCK
++static inline void sharp_unlock_oneblock(struct map_info *map, struct flchip *chip,
++ unsigned long adr)
++{
++ map_word status;
++
++ sharp_send_cmd(map, CMD_CLEAR_BLOCK_LOCKS_1, adr & BLOCK_MASK);
++ sharp_send_cmd(map, CMD_CLEAR_BLOCK_LOCKS_2, adr & BLOCK_MASK);
++
++ sharp_do_wait_for_ready(map,chip,adr);
++
++ status = map_read(map, adr);
++
++ if (!(status.x[0] & SR_ERRORS)) {
++ sharp_send_cmd(map, CMD_RESET, adr);
++ chip->state = FL_READY;
++ return;
++ }
++
++ printk("sharp: error unlocking block at addr=%08lx status=%08lx\n", adr, status.x[0]);
++ sharp_send_cmd(map, CMD_CLEAR_STATUS, adr);
++}
++#endif
++
++static void sharp_sync(struct mtd_info *mtd)
++{
++}
++
++static int sharp_suspend(struct mtd_info *mtd)
++{
++ struct map_info *map = mtd->priv;
++ struct sharp_info *sharp = map->fldrv_priv;
++ int i;
++ struct flchip *chip;
++ int ret = 0;
++
++ for (i = 0; !ret && i < sharp->numchips; i++) {
++ chip = &sharp->chips[i];
++ ret = sharp_wait(map,chip);
++
++ if (ret) {
++ ret = -EAGAIN;
++ } else {
++ chip->state = FL_PM_SUSPENDED;
++ spin_unlock_bh(chip->mutex);
++ }
++ }
++ return ret;
++}
++
++static void sharp_resume(struct mtd_info *mtd)
++{
++ struct map_info *map = mtd->priv;
++ struct sharp_info *sharp = map->fldrv_priv;
++ int i;
++ struct flchip *chip;
++
++ for (i = 0; i < sharp->numchips; i++) {
++ chip = &sharp->chips[i];
++
++ spin_lock_bh(chip->mutex);
++
++ if (chip->state == FL_PM_SUSPENDED) {
++ /* We need to force it back to a known state */
++ sharp_send_cmd(map, CMD_RESET, chip->start);
++ chip->state = FL_READY;
++ wake_up(&chip->wq);
++ }
++
++ spin_unlock_bh(chip->mutex);
++ }
++}
++
++static void sharp_destroy(struct mtd_info *mtd)
++{
++ struct map_info *map = mtd->priv;
++ struct sharp_info *sharp = map->fldrv_priv;
++
++ kfree(sharp);
++}
++
++static int __init sharp_probe_init(void)
++{
++ printk("MTD Sharp chip driver <ds@lineo.com>\n");
++
++ register_mtd_chip_driver(&sharp_chipdrv);
++
++ return 0;
++}
++
++static void __exit sharp_probe_exit(void)
++{
++ unregister_mtd_chip_driver(&sharp_chipdrv);
++}
++
++module_init(sharp_probe_init);
++module_exit(sharp_probe_exit);
++
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("David Schleef <ds@schleef.org>");
++MODULE_DESCRIPTION("Old MTD chip driver for pre-CFI Sharp flash chips");
+Index: linux-2.6.26/drivers/mtd/maps/Kconfig
+===================================================================
+--- linux-2.6.26.orig/drivers/mtd/maps/Kconfig 2008-07-13 23:51:29.000000000 +0200
++++ linux-2.6.26/drivers/mtd/maps/Kconfig 2008-10-17 18:15:31.431789839 +0200
+@@ -392,7 +392,7 @@
+
+ config MTD_SA1100
+ tristate "CFI Flash device mapped on StrongARM SA11x0"
+- depends on MTD_CFI && ARCH_SA1100 && MTD_PARTITIONS
++ depends on (MTD_CFI || MTD_SHARP) && ARCH_SA1100 && MTD_PARTITIONS
+ help
+ This enables access to the flash chips on most platforms based on
+ the SA1100 and SA1110, including the Assabet and the Compaq iPAQ.
+Index: linux-2.6.26/drivers/mtd/maps/sa1100-flash.c
+===================================================================
+--- linux-2.6.26.orig/drivers/mtd/maps/sa1100-flash.c 2008-07-13 23:51:29.000000000 +0200
++++ linux-2.6.26/drivers/mtd/maps/sa1100-flash.c 2008-10-17 18:15:31.431789839 +0200
+@@ -210,6 +210,12 @@
+ goto err;
+ }
+ subdev->mtd->owner = THIS_MODULE;
++
++#ifdef CONFIG_SA1100_COLLIE
++ /* collie flash starts locked */
++// if (subdev->mtd->unlock)
++// subdev->mtd->unlock(subdev->mtd, 0xc0000, subdev->mtd->size - 0xc0000);
++#endif
+
+ printk(KERN_INFO "SA1100 flash: CFI device at 0x%08lx, %dMiB, "
+ "%d-bit\n", phys, subdev->mtd->size >> 20,
+Index: linux-2.6.26/drivers/net/wireless/hostap/hostap_cs.c
+===================================================================
+--- linux-2.6.26.orig/drivers/net/wireless/hostap/hostap_cs.c 2008-07-13 23:51:29.000000000 +0200
++++ linux-2.6.26/drivers/net/wireless/hostap/hostap_cs.c 2008-10-17 18:15:31.435790279 +0200
+@@ -35,7 +35,7 @@
+ module_param(ignore_cis_vcc, int, 0444);
+ MODULE_PARM_DESC(ignore_cis_vcc, "Ignore broken CIS VCC entry");
+
+-
++int activar=0;
+ /* struct local_info::hw_priv */
+ struct hostap_cs_priv {
+ dev_node_t node;
+@@ -499,11 +499,13 @@
+
+ PDEBUG(DEBUG_HW, "%s: setting Vcc=33 (constant)\n", dev_info);
+ p_dev->conf.IntType = INT_MEMORY_AND_IO;
+-
++
++ activar=0;
+ ret = prism2_config(p_dev);
+ if (ret) {
+ PDEBUG(DEBUG_EXTRA, "prism2_config() failed\n");
+ }
++ activar=1;
+
+ return ret;
+ }
+Index: linux-2.6.26/drivers/net/wireless/hostap/hostap_hw.c
+===================================================================
+--- linux-2.6.26.orig/drivers/net/wireless/hostap/hostap_hw.c 2008-10-17 18:13:21.000000000 +0200
++++ linux-2.6.26/drivers/net/wireless/hostap/hostap_hw.c 2008-10-17 18:15:31.443789719 +0200
+@@ -54,6 +54,7 @@
+ #include "hostap.h"
+ #include "hostap_ap.h"
+
++extern int activar;
+
+ /* #define final_version */
+
+@@ -1534,6 +1535,8 @@
+ if (local->hw_downloading)
+ return 1;
+
++ activar=1;
++
+ if (prism2_hw_init(dev, initial)) {
+ return local->no_pri ? 0 : 1;
+ }
+@@ -2665,8 +2668,15 @@
+ int events = 0;
+ u16 ev;
+
+- iface = netdev_priv(dev);
+- local = iface->local;
++
++ // Todos los parametros de entrada son correctos (no son nulos). De momento esta es la unica forma que conozco de detectar el problema.
++ if (!activar) {
++ printk("hostap_hw.c: INTERRUPT BEFORE DEVICE INIT!\n");
++ return IRQ_HANDLED;
++ }
++
++ iface = netdev_priv(dev);
++ local = iface->local;
+
+ if(dev->base_addr == 0)
+ {
+Index: linux-2.6.26/drivers/net/wireless/hostap/hostap_pci.c
+===================================================================
+--- linux-2.6.26.orig/drivers/net/wireless/hostap/hostap_pci.c 2008-10-17 18:13:18.000000000 +0200
++++ linux-2.6.26/drivers/net/wireless/hostap/hostap_pci.c 2008-10-17 18:15:31.447790279 +0200
+@@ -19,6 +19,7 @@
+
+ #include "hostap_wlan.h"
+
++int activar=1;
+
+ static char *dev_info = "hostap_pci";
+
+Index: linux-2.6.26/drivers/net/wireless/hostap/hostap_plx.c
+===================================================================
+--- linux-2.6.26.orig/drivers/net/wireless/hostap/hostap_plx.c 2008-10-17 18:13:18.000000000 +0200
++++ linux-2.6.26/drivers/net/wireless/hostap/hostap_plx.c 2008-10-17 18:15:31.451790719 +0200
+@@ -21,7 +21,7 @@
+ #include <asm/io.h>
+
+ #include "hostap_wlan.h"
+-
++int activar=1;
+
+ static char *dev_info = "hostap_plx";
+
+Index: linux-2.6.26/drivers/pcmcia/sa1100_generic.c
+===================================================================
+--- linux-2.6.26.orig/drivers/pcmcia/sa1100_generic.c 2008-07-13 23:51:29.000000000 +0200
++++ linux-2.6.26/drivers/pcmcia/sa1100_generic.c 2008-10-17 18:15:31.459789719 +0200
+@@ -81,13 +81,14 @@
+ return ret;
+ }
+
+-static struct device_driver sa11x0_pcmcia_driver = {
+- .probe = sa11x0_drv_pcmcia_probe,
+- .remove = soc_common_drv_pcmcia_remove,
+- .name = "sa11x0-pcmcia",
+- .bus = &platform_bus_type,
+- .suspend = pcmcia_socket_dev_suspend,
+- .resume = pcmcia_socket_dev_resume,
++static struct platform_driver sa11x0_pcmcia_driver = {
++ .driver = {
++ .name = "sa11x0-pcmcia",
++ .probe = sa11x0_drv_pcmcia_probe,
++ .remove = soc_common_drv_pcmcia_remove,
++ .suspend= pcmcia_socket_dev_suspend,
++ .resume = pcmcia_socket_dev_resume,
++ },
+ };
+
+ /* sa11x0_pcmcia_init()
+@@ -100,7 +101,7 @@
+ */
+ static int __init sa11x0_pcmcia_init(void)
+ {
+- return driver_register(&sa11x0_pcmcia_driver);
++ return platform_driver_register(&sa11x0_pcmcia_driver);
+ }
+
+ /* sa11x0_pcmcia_exit()
+@@ -110,7 +111,7 @@
+ */
+ static void __exit sa11x0_pcmcia_exit(void)
+ {
+- driver_unregister(&sa11x0_pcmcia_driver);
++ platform_driver_unregister(&sa11x0_pcmcia_driver);
+ }
+
+ MODULE_AUTHOR("John Dorsey <john+@cs.cmu.edu>");
+Index: linux-2.6.26/drivers/spi/Kconfig
+===================================================================
+--- linux-2.6.26.orig/drivers/spi/Kconfig 2008-07-13 23:51:29.000000000 +0200
++++ linux-2.6.26/drivers/spi/Kconfig 2008-10-17 18:15:31.463790519 +0200
+@@ -123,6 +123,10 @@
+ This enables using the Freescale MPC52xx Programmable Serial
+ Controller in master SPI mode.
+
++config SPI_LOCOMO
++ tristate "Locomo SPI master"
++ depends on SPI_MASTER && SHARP_LOCOMO && EXPERIMENTAL
++
+ config SPI_MPC83xx
+ tristate "Freescale MPC83xx/QUICC Engine SPI controller"
+ depends on SPI_MASTER && (PPC_83xx || QUICC_ENGINE) && EXPERIMENTAL
+Index: linux-2.6.26/drivers/spi/Makefile
+===================================================================
+--- linux-2.6.26.orig/drivers/spi/Makefile 2008-07-13 23:51:29.000000000 +0200
++++ linux-2.6.26/drivers/spi/Makefile 2008-10-17 18:15:31.463790519 +0200
+@@ -28,6 +28,7 @@
+ obj-$(CONFIG_SPI_TXX9) += spi_txx9.o
+ obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o
+ obj-$(CONFIG_SPI_SH_SCI) += spi_sh_sci.o
++obj-$(CONFIG_SPI_LOCOMO) += locomo_spi.o
+ # ... add above this line ...
+
+ # SPI protocol drivers (device/link on bus)
+Index: linux-2.6.26/drivers/spi/locomo_spi.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26/drivers/spi/locomo_spi.c 2008-10-17 18:15:31.471790439 +0200
+@@ -0,0 +1,1097 @@
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/device.h>
++#include <linux/stat.h>
++#include <linux/delay.h>
++#include <linux/wait.h>
++#include <linux/interrupt.h>
++#include <asm/hardware/locomo.h>
++#include <asm/errno.h>
++#include <linux/mmc/host.h>
++#include <linux/spi/spi.h>
++#include <linux/spi/mmc_spi.h>
++#include <linux/workqueue.h>
++#include <linux/spinlock.h>
++#include <linux/list.h>
++#include "locomo_spi.h"
++static struct locomospi_dev * spidev;
++static struct work_struct transfer_wq;
++int delay;
++
++char* transtxbuf=(char*)NULL;
++char* transrxbuf=(char*)NULL;
++int transfercount=0, transfersize=0;
++static DECLARE_WAIT_QUEUE_HEAD(transferqueue);
++/* MMC_SPI functions *********************************************************/
++
++static int locomommcspi_init(struct device *dev, irqreturn_t (*isr)(int, void*), void *mmc)
++{
++ int result;
++ result=request_irq(IRQ_LOCOMO_CARDDETECT, isr, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, "locomo-spi", mmc);
++ return result;
++}
++
++static void locomommcspi_exit(struct device *dev, void* mmc)
++{
++ free_irq(IRQ_LOCOMO_CARDDETECT, mmc);
++}
++
++static int locomommcspi_getro(struct device *dev)
++{
++ return locomo_gpio_read_level(spidev->ldev->dev.parent,LOCOMO_GPIO_WRITE_PROT) > 0 ? 1 : 0;
++}
++
++static void locomommcspi_setpower(struct device *dev, unsigned int mask)
++{
++ if(!mask && spidev->card_power)
++ locomospi_power(0);
++ else if( !spidev->card_power )
++ locomospi_power(1);
++
++}
++
++
++static struct mmc_spi_platform_data colliemmc ={
++ .init = locomommcspi_init,
++ .exit = locomommcspi_exit,
++ .detect_delay = 200,
++ .get_ro = locomommcspi_getro,
++ .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34,
++ .setpower = locomommcspi_setpower,
++ .powerup_msecs = 200,
++};
++
++/* Utility function **********************************************************/
++
++static void locomospi_power(int on)
++{
++ locomo_gpio_write(spidev->ldev->dev.parent, LOCOMO_GPIO_CARD_POWER, on);
++ spidev->card_power=on;
++ printk(KERN_DEBUG "locomospi: power %d\n",on);
++}
++
++static void locomospi_setclock(unsigned int div, unsigned int clock)
++{
++ u16 r = ioread16(spidev->base+LOCOMO_SPIMD);
++ div &= 0x7;
++ clock &= 0x3;
++ if(clock != spidev->clock_base || div != spidev->clock_div){
++ r &= ~(LOCOMO_SPI_XSEL | LOCOMO_SPI_CLKSEL | LOCOMO_SPI_XEN);
++ iowrite16(r,spidev->base+LOCOMO_SPIMD);
++ r |= (div | (clock <<3) | LOCOMO_SPI_XEN);
++ iowrite16(r,spidev->base+LOCOMO_SPIMD);
++ spidev->clock_div = div;
++ spidev->clock_base = clock;
++ udelay(300);
++ }
++
++}
++// returns 1 if card ist present, 0 otherwise
++static int locomospi_carddetect()
++{
++ return (locomo_gpio_read_level(spidev->ldev->dev.parent,LOCOMO_GPIO_CARD_DETECT)>0)?0:1;
++}
++
++static void locomospi_setcs(int high)
++{
++ u16 r;
++ printk(KERN_DEBUG "locomospi: cs %d\n",high);
++ r = ioread16(spidev->base + LOCOMO_SPICT);
++ if(high)
++ r |= LOCOMO_SPI_CS;
++ else
++ r &= ~LOCOMO_SPI_CS;
++ iowrite16(r, spidev->base + LOCOMO_SPICT);
++}
++
++static void locomospi_reg_open()
++{
++ u16 r;
++ spidev->clock_div = DIV_64;
++ spidev->clock_base = CLOCK_18MHZ;
++ locomospi_power(1);
++ msleep(100);
++// iowrite16( 0xec00 | (CLOCK_18MHZ <<3)|DIV_64, spidev->base+LOCOMO_SPIMD);
++ iowrite16( LOCOMO_SPI_MSB1ST | LOCOMO_SPI_DOSTAT | LOCOMO_SPI_RCPOL | LOCOMO_SPI_TCPOL
++ |(CLOCK_18MHZ <<3) | DIV_64, spidev->base+LOCOMO_SPIMD);
++// if(locomospi_carddetect()){
++ r = ioread16(spidev->base+LOCOMO_SPIMD);
++ r |= LOCOMO_SPI_XON;
++ iowrite16( r, spidev->base+LOCOMO_SPIMD);
++ r = ioread16(spidev->base+LOCOMO_SPIMD);
++ r |= LOCOMO_SPI_XEN;
++ iowrite16( r, spidev->base+LOCOMO_SPIMD);
++// }
++ iowrite16( LOCOMO_SPI_CS, spidev->base+LOCOMO_SPICT);
++ r = ioread16(spidev->base+LOCOMO_SPICT);
++ r |= (LOCOMO_SPI_CEN | LOCOMO_SPI_RXUEN | LOCOMO_SPI_ALIGNEN);
++ iowrite16( r, spidev->base+LOCOMO_SPICT);
++ udelay(200);
++ r = ioread16(spidev->base+LOCOMO_SPICT);
++ iowrite16(r, spidev->base+LOCOMO_SPICT);
++ r = ioread16(spidev->base+LOCOMO_SPICT);
++ r &= ~LOCOMO_SPI_CS;
++ iowrite16(r, spidev->base+LOCOMO_SPICT);
++}
++
++static void locomospi_reg_release()
++{
++ u16 r;
++ r = ioread16(spidev->base+LOCOMO_SPICT);
++ r &= ~LOCOMO_SPI_CEN;
++ iowrite16(r, spidev->base+LOCOMO_SPICT);
++ r = ioread16(spidev->base+LOCOMO_SPIMD);
++ r &= ~LOCOMO_SPI_XEN;
++ iowrite16(r, spidev->base+LOCOMO_SPIMD);
++ r = ioread16(spidev->base+LOCOMO_SPIMD);
++ r &= ~LOCOMO_SPI_XON;
++ iowrite16(r, spidev->base+LOCOMO_SPIMD);
++ r = ioread16(spidev->base+LOCOMO_SPICT);
++ r |= LOCOMO_SPI_XEN;
++ iowrite16(r, spidev->base+LOCOMO_SPICT);
++ locomospi_power(0);
++}
++#if 0
++static int txrx(const char* txbuffer, char* rxbuffer, int size)
++{
++ u16 r = ioread16(spidev->base+LOCOMO_SPICT);
++ r |= LOCOMO_SPI_ALIGNEN;
++ iowrite16(r, spidev->base+LOCOMO_SPICT);
++ printk(KERN_DEBUG "locomospi: %d bytes to prozess\n",size);
++ /* initialize global vars for isr */
++ transfercount=0; transfersize=size;
++ transtxbuf=txbuffer; transrxbuf=rxbuffer;
++
++ /* start transmit and go sleep isr will wake us*/
++ enable_irq(IRQ_LOCOMO_SPI_TEND);
++ iowrite8(txbuffer[0], spidev->base+LOCOMO_SPITD);
++ wait_event(transferqueue, transfercount >= transfersize);
++ disable_irq(IRQ_LOCOMO_SPI_TEND);
++ transrxbuf=NULL; transtxbuf=NULL;
++
++ r = ioread16(spidev->base+LOCOMO_SPICT);
++ r &= ~LOCOMO_SPI_ALIGNEN;
++ iowrite16(r, spidev->base+LOCOMO_SPICT);
++ int i;
++ for(i=0; i< size; i++)
++ printk(KERN_DEBUG "locomospi: sent: %x received: %x \n",txbuffer[i], rxbuffer[i]);
++
++
++ return size;
++}
++
++
++static int tx(const char* txbuffer, int size)
++{
++ printk(KERN_DEBUG "locomospi: %d bytes to send\n",size);
++ /* initialize global vars for isr */
++ transfercount=0; transfersize=size;
++ transtxbuf=txbuffer;
++
++ /* start transmit and go sleep isr will wake us*/
++ enable_irq(IRQ_LOCOMO_SPI_RFW);
++ iowrite8(txbuffer[0], spidev->base+LOCOMO_SPITD);
++ wait_event(transferqueue, transfercount >= transfersize);
++ disable_irq(IRQ_LOCOMO_SPI_RFW);
++ transtxbuf=NULL;
++
++ int i;
++ for(i=0; i< size; i++)
++ printk(KERN_DEBUG "locomospi: sent: %x\n",txbuffer[i]);
++
++
++ return size;
++}
++
++static int rx(char* rxbuffer, int size)
++{
++ printk(KERN_DEBUG "locomospi: %d bytes to read\n",size);
++ /* initialize global vars for isr */
++ transfercount=0; transfersize=size;
++ transrxbuf=rxbuffer;
++
++ /* start transmit and go sleep isr will wake us*/
++ enable_irq(IRQ_LOCOMO_SPI_RFR);
++ rxbuffer[0]=ioread8(spidev->base+LOCOMO_SPIRD);
++ wait_event(transferqueue, transfercount >= transfersize);
++ disable_irq(IRQ_LOCOMO_SPI_RFR);
++ transrxbuf=NULL;
++
++ int i;
++ for(i=0; i< size; i++)
++ printk(KERN_DEBUG "locomospi: received: %x \n", rxbuffer[i]);
++
++
++ return size;
++}
++
++#else
++static int txrx(const char* txbuffer, char* rxbuffer, int size)
++{
++ int i=0,j=0;
++ int wait;
++ u16 r;
++/* char * txback = kmalloc(size * sizeof(char), GFP_KERNEL);
++ memcpy(txback, txbuffer, size);
++*/
++ if(spidev->clock_div == 4)
++ wait = 0x10000;
++ else
++ wait = 8;
++
++// printk(KERN_DEBUG "locomospi: txrx %d bytes to prozess\n",size);
++
++// r = ioread16(spidev->base+LOCOMO_SPICT);
++// r |= LOCOMO_SPI_ALIGNEN;
++// iowrite16(r, spidev->base+LOCOMO_SPICT);
++ //discard first bogus byte
++
++ ioread8(spidev->base+LOCOMO_SPIRD);
++ for(i=0; i<size; i++){
++ for(j=0; j <= wait; j++){
++ if(ioread16(spidev->base+LOCOMO_SPIST) & LOCOMO_SPI_RFW)
++ break;
++ }
++ iowrite8(txbuffer[i], spidev->base+LOCOMO_SPITD);
++ ndelay(delay);
++
++ for(j=0; j <= wait; j++){
++ if(ioread16(spidev->base+LOCOMO_SPIST) & LOCOMO_SPI_RFR)
++ break;
++ }
++ rxbuffer[i] = ioread8(spidev->base+LOCOMO_SPIRD);
++ ndelay(delay);
++ }
++// r = ioread16(spidev->base+LOCOMO_SPICT);
++// r &= ~LOCOMO_SPI_ALIGNEN;
++// iowrite16(r, spidev->base+LOCOMO_SPICT);
++
++/* for(j=0; j< size; j++)
++ printk(KERN_DEBUG "locomospi: sent: %x received: %x \n",txback[j], rxbuffer[j]);
++
++ kfree(txback);
++*/ return i;
++}
++
++static int tx(const char* buffer, int size)
++{
++ int i=0,j=0;
++ int wait;
++ u16 r;
++ if(spidev->clock_div == 4)
++ wait = 0x10000;
++ else
++ wait = 8;
++ r = ioread16(spidev->base+LOCOMO_SPICT);
++ r &= ~LOCOMO_SPI_ALIGNEN;
++ iowrite16(r, spidev->base+LOCOMO_SPICT);
++
++// printk(KERN_DEBUG "locomospi: tx %d bytes to transmit\n",size);
++ for(i=0; i<size; i++){
++ for(j=0; j <= wait; j++){
++ if(ioread16(spidev->base+LOCOMO_SPIST) & LOCOMO_SPI_RFW)
++ break;
++ }
++ iowrite8(buffer[i], spidev->base+LOCOMO_SPITD);
++ ndelay(delay);
++ }
++
++ for(j=0; j <= wait; j++){
++ if(ioread16(spidev->base+LOCOMO_SPIST) & LOCOMO_SPI_TEND)
++ break;
++ }
++
++ r = ioread16(spidev->base+LOCOMO_SPICT);
++ r |= LOCOMO_SPI_ALIGNEN;
++ iowrite16(r, spidev->base+LOCOMO_SPICT);
++
++// for(j=0; j< size; j++)
++// printk(KERN_DEBUG "locomospi: sent: %x \n", buffer[j]);
++// printk(KERN_DEBUG "locomospi: tx %d bytes transmitted\n",i);
++ return i;
++}
++
++static int rx(char* buffer, int size)
++{
++ int i,j;
++ int wait;
++ u16 r;
++ printk(KERN_DEBUG "locomospi: rx %d bytes to receive\n",size);
++ if(spidev->clock_div == 4)
++ wait = 0x10000;
++ else
++ wait = 8;
++ r = ioread16(spidev->base+LOCOMO_SPICT);
++ r &= ~LOCOMO_SPI_ALIGNEN;
++ iowrite16(r, spidev->base+LOCOMO_SPICT);
++
++ for(i=0; i<size; i++){
++
++ for(j=0; j <= wait; j++){
++ if(ioread16(spidev->base+LOCOMO_SPIST) & LOCOMO_SPI_RFR)
++ break;
++ }
++ buffer[i]= ioread8(spidev->base+LOCOMO_SPIRD);
++ ndelay(delay);
++ }
++
++ r = ioread16(spidev->base+LOCOMO_SPICT);
++ r |= LOCOMO_SPI_ALIGNEN;
++ iowrite16(r, spidev->base+LOCOMO_SPICT);
++
++ for(j=0; j< size; j++)
++ printk(KERN_DEBUG "locomospi: received: %x \n", buffer[j]);
++ printk(KERN_DEBUG "locomospi: rx %d bytes received\n",i);
++ return i;
++}
++#endif
++/*
++static irqreturn_t locomospi_rwready(int irq, void *dev_id)
++{
++ struct locomospi_dev* dev=(struct locomospi_dev*) dev_id;
++// dev_dbg(&spidev->sdev->dev, "IRQ: %d\n", irq);
++// printk(KERN_DEBUG "locomospi: IRQ: %d\n", irq);
++ wake_up_interruptible(&dev->waitqueue);
++ return IRQ_HANDLED;
++}
++*/
++static irqreturn_t locomospi_testisr(int irq, void *dev_id)
++{
++ char *buf="";
++ switch(irq){
++ case IRQ_LOCOMO_SPI_RFR: buf="RFR";
++ break;
++ case IRQ_LOCOMO_SPI_RFW: buf="RFW";
++ break;
++ case IRQ_LOCOMO_SPI_REND:buf="REND";
++ break;
++ case IRQ_LOCOMO_SPI_TEND:buf="TEND";
++ break;
++ case IRQ_LOCOMO_CARDDETECT:
++ buf="CARD_DETECT";
++ break;
++ default: return IRQ_NONE;
++ }
++ printk(KERN_DEBUG "locomospi: IRQ: %s\n",buf);
++// dev_dbg(&spidev->sdev->dev, "IRQ: %s\n",buf);
++ return IRQ_HANDLED;
++}
++static irqreturn_t locomospi_txrxisr(int irq, void *dev_id)
++{
++ if(transfercount < transfersize){
++ transrxbuf[transfercount++] = ioread8(spidev->base+LOCOMO_SPIRD);
++ iowrite8(transtxbuf[transfercount], spidev->base+LOCOMO_SPITD);
++ }
++ else{
++ /* transfer complete. wake up txrx */
++ wake_up(&transferqueue);
++ }
++ return IRQ_HANDLED;
++}
++
++static irqreturn_t locomospi_txisr(int irq, void *dev_id)
++{
++ if(transfercount < transfersize){
++ iowrite8(transtxbuf[transfercount++], spidev->base+LOCOMO_SPITD);
++ }
++ else{
++ /* transfer complete. wake up txrx */
++ wake_up(&transferqueue);
++ }
++ return IRQ_HANDLED;
++}
++
++static irqreturn_t locomospi_rxisr(int irq, void *dev_id)
++{
++ if(transfercount < transfersize){
++ transrxbuf[transfercount++] = ioread8(spidev->base+LOCOMO_SPIRD);
++ }
++ else{
++ /* transfer complete. wake up txrx */
++ wake_up(&transferqueue);
++ }
++ return IRQ_HANDLED;
++}
++
++static void locomospi_clock(unsigned int Hz)
++{
++ u16 r;
++ printk(KERN_DEBUG "locomospi: changing clock to: %d\n", Hz);
++ if(Hz == 0){
++ r = ioread16(spidev->base+LOCOMO_SPIMD);
++ r &= ~LOCOMO_SPI_XON;
++ iowrite16(r, spidev->base+LOCOMO_SPIMD);
++ }
++ else if(Hz >= 24576000){
++ r = ioread16(spidev->base+LOCOMO_SPIMD);
++ r |= LOCOMO_SPI_XON;
++ iowrite16(r, spidev->base+LOCOMO_SPIMD);
++ locomospi_setclock(DIV_1, CLOCK_25MHZ);
++ delay=41;
++ }
++ else if(Hz >= 22579200){
++ r = ioread16(spidev->base+LOCOMO_SPIMD);
++ r |= LOCOMO_SPI_XON;
++ iowrite16(r, spidev->base+LOCOMO_SPIMD);
++ locomospi_setclock(DIV_1, CLOCK_22MHZ);
++ delay=45;
++ }
++ else if(Hz >= 18432000){
++ r = ioread16(spidev->base+LOCOMO_SPIMD);
++ r |= LOCOMO_SPI_XON;
++ iowrite16(r, spidev->base+LOCOMO_SPIMD);
++ locomospi_setclock(DIV_1, CLOCK_18MHZ);
++ delay=55;
++ }
++ else if(Hz >= 12288000){
++ r = ioread16(spidev->base+LOCOMO_SPIMD);
++ r |= LOCOMO_SPI_XON;
++ iowrite16(r, spidev->base+LOCOMO_SPIMD);
++ locomospi_setclock(DIV_2, CLOCK_25MHZ);
++ delay=82;
++ }
++ else if(Hz >= 11289600){
++ r = ioread16(spidev->base+LOCOMO_SPIMD);
++ r |= LOCOMO_SPI_XON;
++ iowrite16(r, spidev->base+LOCOMO_SPIMD);
++ locomospi_setclock(DIV_2, CLOCK_22MHZ);
++ delay=89;
++ }
++ else if(Hz >= 9216000){
++ r = ioread16(spidev->base+LOCOMO_SPIMD);
++ r |= LOCOMO_SPI_XON;
++ iowrite16(r, spidev->base+LOCOMO_SPIMD);
++ locomospi_setclock(DIV_2, CLOCK_18MHZ);
++ delay=110;
++ }
++ else if(Hz >= 6144000){
++ r = ioread16(spidev->base+LOCOMO_SPIMD);
++ r |= LOCOMO_SPI_XON;
++ iowrite16(r, spidev->base+LOCOMO_SPIMD);
++ locomospi_setclock(DIV_4, CLOCK_25MHZ);
++ delay=164;
++ }
++ else if(Hz >= 5644800){
++ r = ioread16(spidev->base+LOCOMO_SPIMD);
++ r |= LOCOMO_SPI_XON;
++ iowrite16(r, spidev->base+LOCOMO_SPIMD);
++ locomospi_setclock(DIV_4, CLOCK_22MHZ);
++ delay=178;
++ }
++ else if(Hz >= 4608000){
++ r = ioread16(spidev->base+LOCOMO_SPIMD);
++ r |= LOCOMO_SPI_XON;
++ iowrite16(r, spidev->base+LOCOMO_SPIMD);
++ locomospi_setclock(DIV_4, CLOCK_18MHZ);
++ delay=218;
++ }
++ else if(Hz >= 3072000){
++ r = ioread16(spidev->base+LOCOMO_SPIMD);
++ r |= LOCOMO_SPI_XON;
++ iowrite16(r, spidev->base+LOCOMO_SPIMD);
++ locomospi_setclock(DIV_8, CLOCK_25MHZ);
++ delay=327;
++ }
++ else if(Hz >= 2822400){
++ r = ioread16(spidev->base+LOCOMO_SPIMD);
++ r |= LOCOMO_SPI_XON;
++ iowrite16(r, spidev->base+LOCOMO_SPIMD);
++ locomospi_setclock(DIV_8, CLOCK_22MHZ);
++ delay=355;
++ }
++ else if(Hz >= 2304000){
++ r = ioread16(spidev->base+LOCOMO_SPIMD);
++ r |= LOCOMO_SPI_XON;
++ iowrite16(r, spidev->base+LOCOMO_SPIMD);
++ locomospi_setclock(DIV_8, CLOCK_18MHZ);
++ delay=435;
++ }
++ else if(Hz >= 384000){
++ r = ioread16(spidev->base+LOCOMO_SPIMD);
++ r |= LOCOMO_SPI_XON;
++ iowrite16(r, spidev->base+LOCOMO_SPIMD);
++ locomospi_setclock(DIV_64, CLOCK_25MHZ);
++ delay=2605;
++ }
++ else if(Hz >= 352800){
++ r = ioread16(spidev->base+LOCOMO_SPIMD);
++ r |= LOCOMO_SPI_XON;
++ iowrite16(r, spidev->base+LOCOMO_SPIMD);
++ locomospi_setclock(DIV_64, CLOCK_22MHZ);
++ delay=2834;
++ }
++ else{ /* set to 288 KHz */
++ r = ioread16(spidev->base+LOCOMO_SPIMD);
++ r |= LOCOMO_SPI_XON;
++ iowrite16(r, spidev->base+LOCOMO_SPIMD);
++ locomospi_setclock(DIV_64, CLOCK_18MHZ);
++ delay=3473;
++ }
++ spidev->clock = Hz;
++}
++
++/* sysfs attributes used for debug *******************************************/
++
++/* SPI registers */
++ssize_t locomospi_showspimd(struct device_driver *drv, char *buf)
++{
++ return sprintf(buf, "0x%x\n", ioread16(spidev->base+LOCOMO_SPIMD));
++}
++
++ssize_t locomospi_storespimd(struct device_driver *drv, const char *buf, size_t count)
++{
++ iowrite16(simple_strtoul(buf, NULL, 16), spidev->base+LOCOMO_SPIMD);
++ return count;
++}
++static DRIVER_ATTR(spimd, S_IWUSR | S_IRUGO, locomospi_showspimd, locomospi_storespimd);
++
++ssize_t locomospi_showspict(struct device_driver *drv, char *buf)
++{
++ return sprintf(buf, "0x%x\n", ioread16(spidev->base+LOCOMO_SPICT));
++}
++
++ssize_t locomospi_storespict(struct device_driver *drv, const char *buf, size_t count)
++{
++ iowrite16(simple_strtoul(buf, NULL, 16), spidev->base+LOCOMO_SPICT);
++ return count;
++}
++static DRIVER_ATTR(spict, S_IWUSR | S_IRUGO, locomospi_showspict, locomospi_storespict);
++
++ssize_t locomospi_showspist(struct device_driver *drv, char *buf)
++{
++ return sprintf(buf, "0x%x\n", ioread16(spidev->base+LOCOMO_SPIST));
++}
++
++ssize_t locomospi_storespist(struct device_driver *drv, const char *buf, size_t count)
++{
++ iowrite16(simple_strtoul(buf, NULL, 16), spidev->base+LOCOMO_SPIST);
++ return count;
++}
++static DRIVER_ATTR(spist, S_IWUSR | S_IRUGO, locomospi_showspist, locomospi_storespist);
++
++ssize_t locomospi_showspitd(struct device_driver *drv, char *buf)
++{
++ return sprintf(buf, "0x%x\n", ioread16(spidev->base+LOCOMO_SPITD));
++}
++
++ssize_t locomospi_storespitd(struct device_driver *drv, const char *buf, size_t count)
++{
++ iowrite16(simple_strtoul(buf, NULL, 16), spidev->base+LOCOMO_SPITD);
++ return count;
++}
++static DRIVER_ATTR(spitd, S_IWUSR | S_IRUGO, locomospi_showspitd, locomospi_storespitd);
++
++ssize_t locomospi_showspird(struct device_driver *drv, char *buf)
++{
++ return sprintf(buf, "0x%x\n", ioread16(spidev->base+LOCOMO_SPIRD));
++}
++
++ssize_t locomospi_storespird(struct device_driver *drv, const char *buf, size_t count)
++{
++ iowrite16(simple_strtoul(buf, NULL, 16), spidev->base+LOCOMO_SPIRD);
++ return count;
++}
++static DRIVER_ATTR(spird, S_IWUSR | S_IRUGO, locomospi_showspird, locomospi_storespird);
++
++ssize_t locomospi_showspits(struct device_driver *drv, char *buf)
++{
++ return sprintf(buf, "0x%x\n", ioread16(spidev->base+LOCOMO_SPITS));
++}
++
++ssize_t locomospi_storespits(struct device_driver *drv, const char *buf, size_t count)
++{
++ iowrite16(simple_strtoul(buf, NULL, 16), spidev->base+LOCOMO_SPITS);
++ return count;
++}
++static DRIVER_ATTR(spits, S_IWUSR | S_IRUGO, locomospi_showspits, locomospi_storespits);
++
++ssize_t locomospi_showspirs(struct device_driver *drv, char *buf)
++{
++ return sprintf(buf, "0x%x\n", ioread16(spidev->base+LOCOMO_SPIRS));
++}
++
++ssize_t locomospi_storespirs(struct device_driver *drv, const char *buf, size_t count)
++{
++ iowrite16(simple_strtoul(buf, NULL, 16), spidev->base+LOCOMO_SPIRS);
++ return count;
++}
++static DRIVER_ATTR(spirs, S_IWUSR | S_IRUGO, locomospi_showspirs, locomospi_storespirs);
++
++/* MMC Card status */
++
++ssize_t locomospi_showpower(struct device_driver *drv, char *buf)
++{
++ return sprintf(buf, "%d\n", spidev->card_power);
++}
++
++ssize_t locomospi_storepower(struct device_driver *drv, const char *buf, size_t count)
++{
++ locomospi_power(simple_strtoul(buf, NULL, 10));
++ return count;
++}
++static DRIVER_ATTR(cardpower, S_IWUSR | S_IRUGO, locomospi_showpower, locomospi_storepower);
++
++ssize_t locomospi_detectcard(struct device_driver *drv, char *buf)
++{
++ return sprintf(buf, "%d\n",(locomo_gpio_read_level(spidev->ldev->dev.parent,LOCOMO_GPIO_CARD_DETECT)>0)?0:1);
++}
++static DRIVER_ATTR(carddetect, S_IRUGO, locomospi_detectcard, NULL);
++
++ssize_t locomospi_writeprotect(struct device_driver *drv, char *buf)
++{
++ return sprintf(buf, "%d\n",(locomo_gpio_read_level(spidev->ldev->dev.parent,LOCOMO_GPIO_WRITE_PROT)>0)?1:0);
++}
++static DRIVER_ATTR(cardwriteprotect, S_IRUGO, locomospi_writeprotect, NULL);
++
++
++ssize_t locomospi_showclock(struct device_driver *drv, char *buf)
++{
++ return sprintf(buf, "%d\n", spidev->clock);
++}
++
++ssize_t locomospi_storeclock(struct device_driver *drv, const char *buf, size_t count)
++{
++ locomospi_clock(simple_strtoul(buf, NULL, 10));
++ return count;
++}
++static DRIVER_ATTR(clock, S_IWUSR | S_IRUGO, locomospi_showclock, locomospi_storeclock);
++
++/* debug */
++ssize_t locomospi_showdelay(struct device_driver *drv, char *buf)
++{
++ return sprintf(buf, "%d\n", delay);
++}
++
++ssize_t locomospi_storedelay(struct device_driver *drv, const char *buf, size_t count)
++{
++ delay=simple_strtoul(buf,NULL,10);
++ return count;
++}
++static DRIVER_ATTR(delay, S_IWUSR | S_IRUGO, locomospi_showdelay, locomospi_storedelay);
++
++ssize_t locomospi_reset(struct device_driver *drv, const char *buf, size_t count)
++{
++ int choice = simple_strtoul(buf, NULL, 10);
++ char buff[100];
++ u16 r;
++ switch(choice){
++ case 0: locomospi_reg_release();
++ schedule_timeout(2*HZ);
++ locomospi_reg_open();
++ break;
++ case 1: {
++ char b1[] = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff";
++ char b2[] = "\xff\x40\x00\x00\x00\x00\x95\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff";
++ locomospi_setcs(1);
++ txrx(b1,b1,17);
++ locomospi_setcs(0);
++ txrx(b2,b2,18);
++
++ }
++ break;
++ case 2: locomospi_setcs(1);
++ txrx("\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff",buff,18);
++ locomospi_setcs(0);
++ txrx("\xff\x40\x00\x00\x00\x00\x95\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff",buff,17);
++ break;
++ case 3:
++ r = ioread16(spidev->base+LOCOMO_SPIMD);
++ r |= LOCOMO_SPI_LOOPBACK;
++ iowrite16(r, spidev->base+LOCOMO_SPIMD);
++ txrx("X",buff,1);
++ txrx("abcdefghijklmnopqrstuvwxyz1234567890",buff,36);
++ txrx("Y",buff,1);
++ udelay(100);
++ txrx("Z",buff,1);
++ schedule_timeout(HZ);
++ txrx("abcdefghijklmnopqrstuvwxyz1234567890",buff,36);
++
++ r = ioread16(spidev->base+LOCOMO_SPIMD);
++ r &= ~LOCOMO_SPI_LOOPBACK;
++ iowrite16(r, spidev->base+LOCOMO_SPIMD);
++ break;
++ default: /* do nothing */;
++ }
++ return count;
++}
++static DRIVER_ATTR(reset, S_IWUSR, NULL, locomospi_reset);
++
++typedef struct locomo_reg_entry {
++ u32 addr;
++ char* name;
++} locomo_reg_entry_t;
++#define LCM (sizeof(locomo_regs)/sizeof(locomo_reg_entry_t))
++static locomo_reg_entry_t locomo_regs[] =
++{
++/* { addr, name, description } */
++ { 0x00, "VER" },
++ { 0x04, "ST" },
++ { 0x08, "C32K" },
++ { 0x0C, "ICR" },
++ { 0x10, "MCSX0" },
++ { 0x14, "MCSX1" },
++ { 0x18, "MCSX2" },
++ { 0x1C, "MCSX3" },
++ { 0x20, "ASD" },
++ { 0x28, "HSD" },
++ { 0x2C, "HSC" },
++ { 0x30, "TADC" },
++ { 0x38, "TC" },
++ { 0x3C, "CPSD" },
++ { 0x40, "KIB" },
++ { 0x44, "KSC" },
++ { 0x48, "KCMD" },
++ { 0x4C, "KIC" },
++ { 0x54, "ACC" },
++ { 0x60, "SPIMD" },
++ { 0x64, "SPICT" },
++ { 0x68, "SPIST" },
++ { 0x70, "SPIIS" },
++ { 0x74, "SPIWE" },
++ { 0x78, "SPIIE" },
++ { 0x7C, "SPIIR" },
++ { 0x80, "SPITD" },
++ { 0x84, "SPIRD" },
++ { 0x88, "SPITS" },
++ { 0x8C, "SPIRS" },
++ { 0x90, "GPD" },
++ { 0x94, "GPE" },
++ { 0x98, "GPL" },
++ { 0x9C, "GPO" },
++ { 0xa0, "GRIE" },
++ { 0xa4, "GFIE" },
++ { 0xa8, "GIS" },
++ { 0xac, "GWE" },
++ { 0xb0, "GIE" },
++ { 0xb4, "GIR" },
++ { 0xc8, "ALC" },
++ { 0xcc, "ALR" },
++ { 0xd0, "PAIF" },
++ { 0xd8, "LTC" },
++ { 0xdc, "LTINT" },
++ { 0xe0, "DAC" },
++ { 0xe8, "LPT0" },
++ { 0xec, "LPT1" },
++ { 0xfc, "TCR" },
++};
++
++static ssize_t lcm_show(struct device *dev, struct device_attribute *attr, char *buf)
++{
++ int base = spidev->base - LOCOMO_SPI;
++ char b[4000]="";
++ char c[30];
++ int i;
++ for(i=0; i<LCM; i++){
++ sprintf(c,"%s:\t\t 0x%x\n",locomo_regs[i].name, ioread16(base + locomo_regs[i].addr));
++ strcat(b,c);
++ }
++ return sprintf(buf,"%s",b);
++}
++
++static DRIVER_ATTR(regs, 0444, lcm_show, NULL);
++
++
++/* SPI functions *************************************************************/
++
++static void locomospi_do_transfer(struct work_struct *wrk)
++{
++ struct list_head *mptr, *tptr, *mptr2;
++ struct spi_transfer *entry;
++ struct spi_message *msg;
++
++ list_for_each_safe(mptr, mptr2, &spidev->message_list){
++ msg = list_entry(mptr, struct spi_message, queue);
++
++ msg->status = 0;
++ msg->actual_length = 0;
++ list_for_each(tptr, &msg->transfers){
++ entry = list_entry(tptr, struct spi_transfer, transfer_list);
++ if(entry->tx_buf && entry->rx_buf){ //duplex
++ txrx((char*) entry->tx_buf, (char*) entry->rx_buf, entry->len);
++ msg->actual_length += entry->len;
++ } else if(entry->tx_buf && !entry->rx_buf){ //write
++ tx((char*) entry->tx_buf, entry->len);
++ msg->actual_length += entry->len;
++ } else if(!entry->tx_buf && entry->rx_buf){ //read
++ rx((char*) entry->rx_buf, entry->len);
++ msg->actual_length += entry->len;
++ } else if(!entry->tx_buf && !entry->rx_buf){ //error
++ dev_err(&spidev->sdev->dev, "do_transfer: no buffers allocated\n");
++ msg->status = -EFAULT;
++ }
++ }
++ spin_lock(&spidev->message_lock);
++ list_del(mptr);
++ spin_unlock(&spidev->message_lock);
++ msg->complete(msg->context);
++ }
++}
++
++static int locomospi_setup(struct spi_device *spi)
++{
++ if((spi->mode & SPI_CS_HIGH) != (spidev->spimode & SPI_CS_HIGH))
++ locomospi_setcs(spi->mode & SPI_CS_HIGH ? 1 : 0 );
++ if(spidev->clock != spi->max_speed_hz){
++ locomospi_clock(spi->max_speed_hz);
++ }
++ spidev->spimode = spi->mode;
++
++ return 0;
++}
++
++static int locomospi_transfer(struct spi_device *spi, struct spi_message *msg)
++{
++
++ spin_lock(&spidev->message_lock);
++ list_add_tail(&msg->queue, &spidev->message_list);
++ spin_unlock(&spidev->message_lock);
++ schedule_work(&transfer_wq);
++ return 0;
++}
++
++static struct locomo_driver locomo_spi_driver = {
++ .drv = {
++ .name = "locomo-spi",
++ },
++ .devid = LOCOMO_DEVID_SPI,
++ .probe = locomospi_probe,
++ .remove = locomospi_remove,
++#ifdef CONFIG_PM
++ .suspend = locomospi_suspend,
++ .resume = locomospi_resume,
++#endif
++};
++
++static struct spi_board_info board = {
++ .modalias = "mmc_spi",
++ .platform_data = (void*) &colliemmc,
++ .controller_data= NULL,
++ .irq = 0,
++ .max_speed_hz = 25000000,
++ .bus_num = 0,
++ .chip_select = 0,
++ .mode = 0,
++};
++
++#ifdef CONFIG_PM
++static int locomospi_suspend(struct locomo_dev *dev, pm_message_t state)
++{
++ disable_irq(IRQ_LOCOMO_CARDDETECT);
++ return 0;
++}
++
++static int locomospi_resume(struct locomo_dev *dev)
++{
++ enable_irq(IRQ_LOCOMO_CARDDETECT);
++ return 0;
++}
++#endif
++
++static int locomospi_probe(struct locomo_dev *dev)
++{
++ int result=0;
++ printk(KERN_DEBUG "Collie MMC over SPI Driver\n");
++ spidev=kmalloc(sizeof(struct locomospi_dev),GFP_KERNEL);
++ if(!spidev){
++ return -ENOMEM;
++ }
++ spidev->ldev = dev;
++ spidev->card_power = 1;
++ spidev->spimode = 0;
++
++ if(!request_mem_region((unsigned long) dev->mapbase, dev->length, LOCOMO_DRIVER_NAME(dev))) {
++ dev_err(&dev->dev, " Can't aquire access to io memory\n");
++ return -EBUSY;
++ }
++ spidev->base=(unsigned long) dev->mapbase;
++ locomospi_reg_open();
++
++ locomo_gpio_set_dir(dev->dev.parent, LOCOMO_GPIO_CARD_POWER, 0);
++ locomo_gpio_set_dir(dev->dev.parent, LOCOMO_GPIO_CARD_DETECT, 1);
++ locomo_gpio_set_dir(dev->dev.parent, LOCOMO_GPIO_WRITE_PROT, 1);
++
++ result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_cardpower);
++ if(result){
++ dev_err(&dev->dev, "error creating driver attribute\n");
++ goto region;
++ }
++ result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_carddetect);
++ if(result){
++ dev_err(&dev->dev,"error creating driver attribute\n");
++ goto region;
++ }
++ result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_cardwriteprotect);
++ if(result){
++ dev_err(&dev->dev, "error creating driver attribute\n");
++ goto region;
++ }
++ result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_spimd);
++ if(result){
++ dev_err(&dev->dev, "error creating driver attribute\n");
++ goto region;
++ }
++ result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_spict);
++ if(result){
++ dev_err(&dev->dev, "error creating driver attribute\n");
++ goto region;
++ }
++ result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_spist);
++ if(result){
++ dev_err(&dev->dev, "error creating driver attribute\n");
++ goto region;
++ }
++ result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_spitd);
++ if(result){
++ dev_err(&dev->dev, "error creating driver attribute\n");
++ goto region;
++ }
++ result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_spird);
++ if(result){
++ dev_err(&dev->dev, "error creating driver attribute\n");
++ goto region;
++ }
++ result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_spits);
++ if(result){
++ dev_err(&dev->dev, "error creating driver attribute\n");
++ goto region;
++ }
++ result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_spirs);
++ if(result){
++ dev_err(&dev->dev, "error creating driver attribute\n");
++ goto region;
++ }
++ result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_clock);
++ if(result){
++ dev_err(&dev->dev, "error creating driver attribute\n");
++ goto region;
++ }
++ result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_delay);
++ if(result){
++ dev_err(&dev->dev, "error creating driver attribute\n");
++ goto region;
++ }
++ result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_reset);
++ if(result){
++ dev_err(&dev->dev, "error creating driver attribute\n");
++ goto region;
++ }
++ result=driver_create_file(&locomo_spi_driver.drv, &driver_attr_regs);
++ if(result){
++ dev_err(&dev->dev, "error creating driver attribute\n");
++ goto region;
++ }
++ INIT_WORK(&transfer_wq, locomospi_do_transfer);
++ INIT_LIST_HEAD(&spidev->message_list);
++ spin_lock_init(&spidev->message_lock);
++ init_waitqueue_head(&spidev->waitqueue);
++ spidev->master=spi_alloc_master(&dev->dev,0);
++ if(!spidev->master){
++ result=-ENOMEM;
++ goto region;
++ }
++ spidev->master->bus_num = 0;
++ spidev->master->num_chipselect = 1;
++ spidev->master->setup = locomospi_setup;
++ spidev->master->transfer = locomospi_transfer;
++ spidev->sdev = spi_new_device(spidev->master, &board);
++ if(!spidev->sdev){
++ dev_err(&dev->dev, "failed to register spi device\n");
++ result = -EINVAL;
++ goto master;
++ }
++/* result=request_irq(IRQ_LOCOMO_SPI_RFR, locomospi_testisr, IRQF_SHARED, "locomo-spi", (void*) spidev);
++ if(result) {
++ dev_err(&dev->dev, "Could not get IRQ: RFR\n");
++ goto regdev;
++ }
++ //disable_irq(IRQ_LOCOMO_SPI_RFR);
++*//* result=request_irq(IRQ_LOCOMO_SPI_RFW, locomospi_testisr, IRQF_SHARED, "locomo-spi", (void*) spidev);
++ if(result) {
++ dev_err(&dev->dev, "Could not get IRQ: RFW\n");
++ goto irq1;
++ }
++ //disable_irq(IRQ_LOCOMO_SPI_RFW);
++*//* result=request_irq(IRQ_LOCOMO_SPI_REND, locomospi_testisr, IRQF_SHARED, "locomo-spi", (void*) spidev);
++ if(result) {
++ dev_err(&dev->dev, "Could not get IRQ: REND\n");
++ goto irq2;
++ }
++*//* result=request_irq(IRQ_LOCOMO_SPI_TEND, locomospi_testisr, IRQF_SHARED, "locomo-spi", (void*) spidev);
++ if(result) {
++ dev_err(&dev->dev, "Could not get IRQ: TEND\n");
++ goto irq3;
++ }
++ //disable_irq(IRQ_LOCOMO_SPI_TEND);
++*/ spidev->workqueue = create_singlethread_workqueue("locomo-spi");
++ if(!spidev->workqueue){
++ dev_err(&dev->dev, "failed to create workqueue\n");
++ goto irq4;
++ }
++ result=spi_register_master(spidev->master);
++ if(result){
++ dev_err(&dev->dev, "failed to register spimaster\n");
++ goto wq;
++ }
++ return 0;
++wq:
++ destroy_workqueue(spidev->workqueue);
++irq4:
++// free_irq(IRQ_LOCOMO_SPI_TEND, (void*) spidev);
++irq3:
++// free_irq(IRQ_LOCOMO_SPI_REND, (void*) spidev);
++irq2:
++// free_irq(IRQ_LOCOMO_SPI_RFW, (void*) spidev);
++irq1:
++// free_irq(IRQ_LOCOMO_SPI_RFR, (void*) spidev);
++regdev:
++ spi_unregister_device(spidev->sdev);
++master:
++ spi_master_put(spidev->master);
++region:
++ release_mem_region((unsigned long) dev->mapbase, dev->length);
++ kfree(spidev);
++ return result;
++
++}
++
++static int locomospi_remove(struct locomo_dev *dev)
++{
++ spi_unregister_device(spidev->sdev);
++ spi_unregister_master(spidev->master);
++ destroy_workqueue(spidev->workqueue);
++ locomospi_reg_release();
++// free_irq(IRQ_LOCOMO_SPI_TEND, (void*) spidev);
++// free_irq(IRQ_LOCOMO_SPI_REND, (void*) spidev);
++// free_irq(IRQ_LOCOMO_SPI_RFW, (void*) spidev);
++// free_irq(IRQ_LOCOMO_SPI_RFR, (void*) spidev);
++ spi_master_put(spidev->master);
++ release_mem_region((unsigned long) dev->mapbase, dev->length);
++ kfree(spidev);
++ return 0;
++}
++
++
++
++static int __init locomospi_init(void)
++{
++ int ret = locomo_driver_register(&locomo_spi_driver);
++ if (ret)
++ return ret;
++
++
++ return 0;
++}
++
++static void __exit locomospi_exit(void)
++{
++ locomo_driver_unregister(&locomo_spi_driver);
++}
++
++module_init(locomospi_init);
++module_exit(locomospi_exit);
++
++MODULE_AUTHOR("Thomas Kunze thommy@tabao.de");
++MODULE_DESCRIPTION("Collie mmc driver");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.26/drivers/spi/locomo_spi.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26/drivers/spi/locomo_spi.h 2008-10-17 18:15:31.471790439 +0200
+@@ -0,0 +1,75 @@
++#include <asm/hardware/locomo.h>
++#ifndef __LOCOMO_SPI_H__
++#define __LOCOMO_SPI_H__
++
++/* locomo-spi status register LOCOMO_SPIST */
++#define LOCOMO_SPI_TEND (1 << 3) /* Transfer end bit */
++#define LOCOMO_SPI_REND (1 << 2) /* Receive end bit */
++#define LOCOMO_SPI_RFW (1 << 1) /* write buffer bit */
++#define LOCOMO_SPI_RFR (1) /* read buffer bit */
++
++/* locomo-spi mode register LOCOMO_SPIMD */
++#define LOCOMO_SPI_LOOPBACK (1 << 15) /* loopback tx to rx */
++#define LOCOMO_SPI_MSB1ST (1 << 14) /* send MSB first */
++#define LOCOMO_SPI_DOSTAT (1 << 13) /* transmit line is idle high */
++#define LOCOMO_SPI_TCPOL (1 << 11) /* transmit CPOL (maybe affects CPHA too) */
++#define LOCOMO_SPI_RCPOL (1 << 10) /* receive CPOL (maybe affects CPHA too) */
++#define LOCOMO_SPI_TDINV (1 << 9) /* invert transmit line */
++#define LOCOMO_SPI_RDINV (1 << 8) /* invert receive line */
++#define LOCOMO_SPI_XON (1 << 7) /* enable spi controller clock */
++#define LOCOMO_SPI_XEN (1 << 6) /* clock bit write enable xon must be off, wait 300 us before xon->1 */
++#define LOCOMO_SPI_XSEL 0x0018 /* clock select */
++#define CLOCK_18MHZ 0 /* 18,432 MHz clock */
++#define CLOCK_22MHZ 1 /* 22,5792 MHz clock */
++#define CLOCK_25MHZ 2 /* 24,576 MHz clock */
++#define LOCOMO_SPI_CLKSEL 0x7
++#define DIV_1 0 /* don't divide clock */
++#define DIV_2 1 /* divide clock by two */
++#define DIV_4 2 /* divide clock by four */
++#define DIV_8 3 /* divide clock by eight*/
++#define DIV_64 4 /* divide clock by 64 */
++
++/* locomo-spi control register LOCOMO_SPICT */
++#define LOCOMO_SPI_CRC16_7_B (1 << 15) /* 0: crc16 1: crc7 */
++#define LOCOMO_SPI_CRCRX_TX_B (1 << 14)
++#define LOCOMO_SPI_CRCRESET_B (1 << 13)
++#define LOCOMO_SPI_CEN (1 << 7) /* ?? enable */
++#define LOCOMO_SPI_CS (1 << 6) /* chip select */
++#define LOCOMO_SPI_UNIT16 (1 << 5) /* 0: 8 bit units, 1: 16 bit unit */
++#define LOCOMO_SPI_ALIGNEN (1 << 2) /* align transfer enable */
++#define LOCOMO_SPI_RXWEN (1 << 1) /* continous receive */
++#define LOCOMO_SPI_RXUEN (1 << 0) /* aligned receive */
++
++#define IRQ_LOCOMO_CARDDETECT IRQ_LOCOMO_GPIO13
++
++
++struct locomospi_dev {
++ struct locomo_dev *ldev;
++ struct spi_master *master;
++ struct spi_device *sdev;
++ int card_power;
++ int clock_base;
++ int clock_div;
++ int clock;
++ unsigned long base;
++ u8 spimode;
++ wait_queue_head_t waitqueue;
++ struct workqueue_struct *workqueue;
++ struct list_head message_list;
++ spinlock_t message_lock;
++};
++
++
++static irqreturn_t locomospi_cardisr(int, void*);
++static int locomospi_probe(struct locomo_dev*);
++static int locomospi_remove(struct locomo_dev*);
++static int locomospi_carddetect(void);
++static void locomospi_reg_open(void);
++static void locomospi_reg_release(void);
++static int tx(const char*, int);
++static int rx(char *, int);
++static void locomospi_power(int on);
++static int locomospi_suspend(struct locomo_dev *dev, pm_message_t state);
++static int locomospi_resume(struct locomo_dev *dev);
++static void locomospi_setcs(int high);
++#endif
diff --git a/recipes/linux/linux-rp-2.6.26/collie_keymap.patch b/recipes/linux/linux-rp-2.6.26/collie_keymap.patch
new file mode 100644
index 0000000000..648821f577
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.26/collie_keymap.patch
@@ -0,0 +1,420 @@
+
+#
+# Patch managed by http://www.holgerschurig.de/patcher.html
+#
+
+Index: linux-2.6.26/drivers/char/defkeymap.map
+===================================================================
+--- linux-2.6.26.orig/drivers/char/defkeymap.map 2008-07-13 23:51:29.000000000 +0200
++++ linux-2.6.26/drivers/char/defkeymap.map 2008-07-24 22:09:38.663284352 +0200
+@@ -1,264 +1,170 @@
+-# Default kernel keymap. This uses 7 modifier combinations.
+-keymaps 0-2,4-5,8,12
+-# Change the above line into
+-# keymaps 0-2,4-6,8,12
+-# in case you want the entries
+-# altgr control keycode 83 = Boot
+-# altgr control keycode 111 = Boot
+-# below.
+-#
+-# In fact AltGr is used very little, and one more keymap can
+-# be saved by mapping AltGr to Alt (and adapting a few entries):
+-# keycode 100 = Alt
+-#
+-keycode 1 = Escape Escape
+- alt keycode 1 = Meta_Escape
+-keycode 2 = one exclam
+- alt keycode 2 = Meta_one
+-keycode 3 = two at at
+- control keycode 3 = nul
+- shift control keycode 3 = nul
+- alt keycode 3 = Meta_two
+-keycode 4 = three numbersign
+- control keycode 4 = Escape
+- alt keycode 4 = Meta_three
+-keycode 5 = four dollar dollar
+- control keycode 5 = Control_backslash
+- alt keycode 5 = Meta_four
+-keycode 6 = five percent
+- control keycode 6 = Control_bracketright
+- alt keycode 6 = Meta_five
+-keycode 7 = six asciicircum
+- control keycode 7 = Control_asciicircum
+- alt keycode 7 = Meta_six
+-keycode 8 = seven ampersand braceleft
+- control keycode 8 = Control_underscore
+- alt keycode 8 = Meta_seven
+-keycode 9 = eight asterisk bracketleft
+- control keycode 9 = Delete
+- alt keycode 9 = Meta_eight
+-keycode 10 = nine parenleft bracketright
+- alt keycode 10 = Meta_nine
+-keycode 11 = zero parenright braceright
+- alt keycode 11 = Meta_zero
+-keycode 12 = minus underscore backslash
+- control keycode 12 = Control_underscore
+- shift control keycode 12 = Control_underscore
+- alt keycode 12 = Meta_minus
+-keycode 13 = equal plus
+- alt keycode 13 = Meta_equal
+-keycode 14 = Delete Delete
+- control keycode 14 = BackSpace
+- alt keycode 14 = Meta_Delete
++# Note:
++# The way in which the modifiers are handled are quite different
++# than how they were handled in the 2.4.6-rmk1-np2-embedix kernel.
++#
++# Here, we simply pass up Fn as Control, and the german accent key
++# as Altgr, and simply use a proper keymap. Said keymap is as
++# follows.
++# keymaps 0-2,4-5,8,12,20
++keycode 14 = BackSpace
++ shift keycode 14 = BackSpace
++ control keycode 14 = Delete
++ shiftl control keycode 14 = bracketleft
++ control shiftr keycode 14 = bracketleft
+ keycode 15 = Tab Tab
+- alt keycode 15 = Meta_Tab
++ shift keycode 15 = backslash
++ control keycode 15 = Caps_Lock
++ shiftl control keycode 15 = Caps_Lock
++ control shiftr keycode 15 = Caps_Lock
+ keycode 16 = q
++ control keycode 16 = one
++ shiftl control keycode 16 = Control_q
++ control shiftr keycode 16 = Meta_q
+ keycode 17 = w
+-keycode 18 = e
+- altgr keycode 18 = Hex_E
++ control keycode 17 = two
++ shiftl control keycode 17 = Control_w
++ control shiftr keycode 17 = Meta_w
++keycode 18 = e
++ control keycode 18 = three
++ shiftl control keycode 18 = Control_e
++ control shiftr keycode 18 = Meta_e
+ keycode 19 = r
++ control keycode 19 = four
++ shiftr control keycode 19 = Control_r
++ control shiftl keycode 19 = Meta_r
+ keycode 20 = t
++ control keycode 20 = five
++ shiftl control keycode 20 = Control_t
++ control shiftr keycode 20 = Meta_t
+ keycode 21 = y
++ control keycode 21 = six
++ shiftl control keycode 21 = Control_y
++ control shiftr keycode 21 = Meta_y
+ keycode 22 = u
++ control keycode 22 = seven
++ shiftl control keycode 22 = Control_u
++ control shiftr keycode 22 = Meta_u
+ keycode 23 = i
++ control keycode 23 = eight
++ shiftl control keycode 23 = Control_i
++ control shiftr keycode 23 = Meta_i
+ keycode 24 = o
++ control keycode 24 = nine
++ shiftl control keycode 24 = Control_o
++ control shiftr keycode 24 = Meta_o
+ keycode 25 = p
+-keycode 26 = bracketleft braceleft
+- control keycode 26 = Escape
+- alt keycode 26 = Meta_bracketleft
+-keycode 27 = bracketright braceright asciitilde
+- control keycode 27 = Control_bracketright
+- alt keycode 27 = Meta_bracketright
++ control keycode 25 = zero
++ shiftl control keycode 25 = Control_p
++ control shiftr keycode 25 = Meta_p
+ keycode 28 = Return
+- alt keycode 28 = Meta_Control_m
++ control keycode 28 = greater
++ shiftl control keycode 28 = braceright
++ control shiftr keycode 28 = braceright
+ keycode 29 = Control
+-keycode 30 = a
+- altgr keycode 30 = Hex_A
++keycode 30 = a
++ control keycode 30 = exclam
++ shiftl control keycode 30 = Control_a
++ control shiftr keycode 30 = Meta_a
+ keycode 31 = s
+-keycode 32 = d
+- altgr keycode 32 = Hex_D
+-keycode 33 = f
+- altgr keycode 33 = Hex_F
++ control keycode 31 = at
++ shiftl control keycode 31 = Control_s
++ control shiftr keycode 31 = Meta_s
++keycode 32 = d
++ control keycode 32 = numbersign
++ shiftl control keycode 32 = Control_d
++ control shiftr keycode 32 = Meta_d
++keycode 33 = f
++ control keycode 33 = dollar
++ shiftl control keycode 33 = Control_f
++ control shiftr keycode 33 = Meta_f
+ keycode 34 = g
++ control keycode 34 = percent
++ shiftl control keycode 34 = Control_g
++ control shiftr keycode 34 = Meta_g
+ keycode 35 = h
++ control keycode 35 = underscore
++ shiftl control keycode 35 = BackSpace
++ control shiftr keycode 35 = BackSpace
+ keycode 36 = j
++ control keycode 36 = ampersand
++ shiftl control keycode 36 = Linefeed
++ control shiftr keycode 36 = Linefeed
+ keycode 37 = k
++ control keycode 37 = asterisk
++ shiftl control keycode 37 = Control_k
++ control shiftr keycode 37 = Meta_k
+ keycode 38 = l
+-keycode 39 = semicolon colon
+- alt keycode 39 = Meta_semicolon
+-keycode 40 = apostrophe quotedbl
+- control keycode 40 = Control_g
+- alt keycode 40 = Meta_apostrophe
+-keycode 41 = grave asciitilde
+- control keycode 41 = nul
+- alt keycode 41 = Meta_grave
++ control keycode 38 = bracketleft
++ shiftl control keycode 51 = parenleft
++ control shiftr keycode 51 = parenleft
++keycode 40 = apostrophe quotedbl
++ control keycode 40 = asciitilde
++ shiftl control keycode 40 = asciicircum
++ control shiftr keycode 40 = asciicircum
+ keycode 42 = Shift
+-keycode 43 = backslash bar
+- control keycode 43 = Control_backslash
+- alt keycode 43 = Meta_backslash
+ keycode 44 = z
++ control keycode 44 = Control_z
++ shiftl control keycode 44 = Control_z
++ control shiftr keycode 44 = Meta_z
+ keycode 45 = x
+-keycode 46 = c
+- altgr keycode 46 = Hex_C
++ control keycode 45 = Control_x
++ shiftl control keycode 45 = Control_x
++ control shiftr keycode 45 = Meta_x
++keycode 46 = c
++ control keycode 46 = Control_c
++ shiftl control keycode 46 = Control_c
++ control shiftr keycode 46 = Meta_c
+ keycode 47 = v
+-keycode 48 = b
+- altgr keycode 48 = Hex_B
++ control keycode 47 = Control_v
++ shiftl control keycode 47 = Control_v
++ control shiftr keycode 47 = Meta_v
++## current location ##
++keycode 48 = b
++ control keycode 48 = minus
++ shiftl control keycode 48 = Control_b
++ control shiftr keycode 48 = Meta_b
+ keycode 49 = n
++ control keycode 49 = plus
++ shiftl control keycode 49 = Control_n
++ control shiftr keycode 49 = Meta_n
+ keycode 50 = m
+-keycode 51 = comma less
+- alt keycode 51 = Meta_comma
+-keycode 52 = period greater
+- control keycode 52 = Compose
+- alt keycode 52 = Meta_period
+-keycode 53 = slash question
+- control keycode 53 = Delete
+- alt keycode 53 = Meta_slash
+-keycode 54 = Shift
++ control keycode 50 = equal
++ shiftl control keycode 50 = Control_m
++ control shiftr keycode 50 = Meta_m
++keycode 51 = comma
++ shift keycode 51 = semicolon
++ control keycode 51 = bracketright
++ shiftl control keycode 51 = parenright
++ control shiftr keycode 51 = parenright
++keycode 52 = period
++ shift keycode 52 = colon
++ control keycode 52 = less
++ shiftl control keycode 52 = braceleft
++ control shiftr keycode 52 = braceleft
++keycode 53 = slash
++ shift keycode 53 = question
++ control keycode 53 = Num_Lock
++ shiftl control keycode 53 = Num_Lock
++ control shiftr keycode 53 = Num_Lock
++keycode 54 = AltGr
+ keycode 55 = KP_Multiply
+ keycode 56 = Alt
+-keycode 57 = space space
+- control keycode 57 = nul
+- alt keycode 57 = Meta_space
+-keycode 58 = Caps_Lock
+-keycode 59 = F1 F11 Console_13
+- control keycode 59 = F1
+- alt keycode 59 = Console_1
+- control alt keycode 59 = Console_1
+-keycode 60 = F2 F12 Console_14
+- control keycode 60 = F2
+- alt keycode 60 = Console_2
+- control alt keycode 60 = Console_2
+-keycode 61 = F3 F13 Console_15
+- control keycode 61 = F3
+- alt keycode 61 = Console_3
+- control alt keycode 61 = Console_3
+-keycode 62 = F4 F14 Console_16
+- control keycode 62 = F4
+- alt keycode 62 = Console_4
+- control alt keycode 62 = Console_4
+-keycode 63 = F5 F15 Console_17
+- control keycode 63 = F5
+- alt keycode 63 = Console_5
+- control alt keycode 63 = Console_5
+-keycode 64 = F6 F16 Console_18
+- control keycode 64 = F6
+- alt keycode 64 = Console_6
+- control alt keycode 64 = Console_6
+-keycode 65 = F7 F17 Console_19
+- control keycode 65 = F7
+- alt keycode 65 = Console_7
+- control alt keycode 65 = Console_7
+-keycode 66 = F8 F18 Console_20
+- control keycode 66 = F8
+- alt keycode 66 = Console_8
+- control alt keycode 66 = Console_8
+-keycode 67 = F9 F19 Console_21
+- control keycode 67 = F9
+- alt keycode 67 = Console_9
+- control alt keycode 67 = Console_9
+-keycode 68 = F10 F20 Console_22
+- control keycode 68 = F10
+- alt keycode 68 = Console_10
+- control alt keycode 68 = Console_10
+-keycode 69 = Num_Lock
+- shift keycode 69 = Bare_Num_Lock
+-keycode 70 = Scroll_Lock Show_Memory Show_Registers
+- control keycode 70 = Show_State
+- alt keycode 70 = Scroll_Lock
+-keycode 71 = KP_7
+- alt keycode 71 = Ascii_7
+- altgr keycode 71 = Hex_7
+-keycode 72 = KP_8
+- alt keycode 72 = Ascii_8
+- altgr keycode 72 = Hex_8
+-keycode 73 = KP_9
+- alt keycode 73 = Ascii_9
+- altgr keycode 73 = Hex_9
+-keycode 74 = KP_Subtract
+-keycode 75 = KP_4
+- alt keycode 75 = Ascii_4
+- altgr keycode 75 = Hex_4
+-keycode 76 = KP_5
+- alt keycode 76 = Ascii_5
+- altgr keycode 76 = Hex_5
+-keycode 77 = KP_6
+- alt keycode 77 = Ascii_6
+- altgr keycode 77 = Hex_6
+-keycode 78 = KP_Add
+-keycode 79 = KP_1
+- alt keycode 79 = Ascii_1
+- altgr keycode 79 = Hex_1
+-keycode 80 = KP_2
+- alt keycode 80 = Ascii_2
+- altgr keycode 80 = Hex_2
+-keycode 81 = KP_3
+- alt keycode 81 = Ascii_3
+- altgr keycode 81 = Hex_3
+-keycode 82 = KP_0
+- alt keycode 82 = Ascii_0
+- altgr keycode 82 = Hex_0
+-keycode 83 = KP_Period
+-# altgr control keycode 83 = Boot
+- control alt keycode 83 = Boot
+-keycode 84 = Last_Console
+-keycode 85 =
+-keycode 86 = less greater bar
+- alt keycode 86 = Meta_less
+-keycode 87 = F11 F11 Console_23
+- control keycode 87 = F11
+- alt keycode 87 = Console_11
+- control alt keycode 87 = Console_11
+-keycode 88 = F12 F12 Console_24
+- control keycode 88 = F12
+- alt keycode 88 = Console_12
+- control alt keycode 88 = Console_12
+-keycode 89 =
+-keycode 90 =
+-keycode 91 =
+-keycode 92 =
+-keycode 93 =
+-keycode 94 =
+-keycode 95 =
+-keycode 96 = KP_Enter
+-keycode 97 = Control
+-keycode 98 = KP_Divide
++keycode 57 = space
++ shift keycode 57 = bar
++ control keycode 57 = nul
++ shiftl control keycode 57 = grave
++ control shiftr keycode 57 = grave
++keycode 67 = Control
++keycode 87 = Return
+ keycode 99 = Control_backslash
+ control keycode 99 = Control_backslash
+- alt keycode 99 = Control_backslash
+ keycode 100 = AltGr
+-keycode 101 = Break
+-keycode 102 = Find
+ keycode 103 = Up
+-keycode 104 = Prior
+- shift keycode 104 = Scroll_Backward
+ keycode 105 = Left
+- alt keycode 105 = Decr_Console
+ keycode 106 = Right
+- alt keycode 106 = Incr_Console
+ keycode 107 = Select
+ keycode 108 = Down
+-keycode 109 = Next
+- shift keycode 109 = Scroll_Forward
+-keycode 110 = Insert
+-keycode 111 = Remove
+-# altgr control keycode 111 = Boot
+- control alt keycode 111 = Boot
+-keycode 112 = Macro
+-keycode 113 = F13
+-keycode 114 = F14
+-keycode 115 = Help
+-keycode 116 = Do
+-keycode 117 = F17
+-keycode 118 = KP_MinPlus
+-keycode 119 = Pause
+-keycode 120 =
+-keycode 121 =
+-keycode 122 =
+-keycode 123 =
+-keycode 124 =
+-keycode 125 =
+-keycode 126 =
+-keycode 127 =
++keycode 116 = Escape
+ string F1 = "\033[[A"
+ string F2 = "\033[[B"
+ string F3 = "\033[[C"
+Index: linux-2.6.26/drivers/char/Makefile
+===================================================================
+--- linux-2.6.26.orig/drivers/char/Makefile 2008-07-13 23:51:29.000000000 +0200
++++ linux-2.6.26/drivers/char/Makefile 2008-07-24 21:57:12.744280423 +0200
+@@ -127,7 +127,7 @@
+ # Uncomment if you're changing the keymap and have an appropriate
+ # loadkeys version for the map. By default, we'll use the shipped
+ # versions.
+-# GENERATE_KEYMAP := 1
++ GENERATE_KEYMAP := 1
+
+ ifdef GENERATE_KEYMAP
+
diff --git a/recipes/linux/linux-rp-2.6.26/connectplus-prevent-oops-HACK.patch b/recipes/linux/linux-rp-2.6.26/connectplus-prevent-oops-HACK.patch
new file mode 100644
index 0000000000..b5439c62e7
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.26/connectplus-prevent-oops-HACK.patch
@@ -0,0 +1,17 @@
+Index: linux-2.6.21/drivers/net/wireless/hostap/hostap_hw.c
+===================================================================
+--- linux-2.6.21.orig/drivers/net/wireless/hostap/hostap_hw.c 2007-07-07 12:45:39.000000000 +0100
++++ linux-2.6.21/drivers/net/wireless/hostap/hostap_hw.c 2007-07-07 12:47:30.000000000 +0100
+@@ -2666,6 +2666,12 @@
+ iface = netdev_priv(dev);
+ local = iface->local;
+
++ if(dev->base_addr == 0)
++ {
++ printk(KERN_DEBUG "%s: IRQ before base_addr set\n", dev->name);
++ return IRQ_HANDLED;
++ }
++
+ prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INTERRUPT, 0, 0);
+
+ if (local->func->card_present && !local->func->card_present(local)) {
diff --git a/recipes/linux/linux-rp-2.6.26/connectplus-remove-ide-HACK.patch b/recipes/linux/linux-rp-2.6.26/connectplus-remove-ide-HACK.patch
new file mode 100644
index 0000000000..4414b21191
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.26/connectplus-remove-ide-HACK.patch
@@ -0,0 +1,12 @@
+Index: linux-2.6.13/drivers/ide/legacy/ide-cs.c
+===================================================================
+--- linux-2.6.13.orig/drivers/ide/legacy/ide-cs.c 2005-09-01 22:43:46.000000000 +0100
++++ linux-2.6.13/drivers/ide/legacy/ide-cs.c 2005-09-01 22:45:46.000000000 +0100
+@@ -488,7 +488,6 @@
+ PCMCIA_DEVICE_PROD_ID123("KODAK Picture Card ", "KODAK ", "V100K", 0x94a0d8f3, 0xe4fc3ea0, 0xe5e7eed4),
+ PCMCIA_DEVICE_PROD_ID1("STI Flash", 0xe4a13209),
+ PCMCIA_DEVICE_PROD_ID12("STI", "Flash 5.0", 0xbf2df18d, 0x8cb57a0e),
+- PCMCIA_MFC_DEVICE_PROD_ID12(1, "SanDisk", "ConnectPlus", 0x7a954bd9, 0x74be00c6),
+ PCMCIA_DEVICE_NULL,
+ };
+ MODULE_DEVICE_TABLE(pcmcia, ide_ids);
diff --git a/recipes/linux/linux-rp-2.6.26/defconfig-akita b/recipes/linux/linux-rp-2.6.26/defconfig-akita
new file mode 100644
index 0000000000..f863b28483
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.26/defconfig-akita
@@ -0,0 +1,1799 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.26
+# Tue Feb 3 01:08:10 2009
+#
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+CONFIG_GENERIC_GPIO=y
+CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_MMU=y
+# CONFIG_NO_IOPORT is not set
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+# CONFIG_ARCH_HAS_ILOG2_U32 is not set
+# CONFIG_ARCH_HAS_ILOG2_U64 is not set
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_ARCH_SUPPORTS_AOUT=y
+CONFIG_ZONE_DMA=y
+CONFIG_ARCH_MTD_XIP=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_LOCK_KERNEL=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+# CONFIG_POSIX_MQUEUE is not set
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_BSD_PROCESS_ACCT_V3=y
+# CONFIG_TASKSTATS is not set
+# CONFIG_AUDIT is not set
+# CONFIG_IKCONFIG is not set
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_CGROUPS is not set
+# CONFIG_GROUP_SCHED is not set
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+# CONFIG_RELAY is not set
+# CONFIG_NAMESPACES is not set
+# CONFIG_BLK_DEV_INITRD is not set
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+CONFIG_EMBEDDED=y
+CONFIG_UID16=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_SYSCTL_SYSCALL_CHECK=y
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_COMPAT_BRK=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_ANON_INODES=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLAB=y
+# CONFIG_SLUB is not set
+# CONFIG_SLOB is not set
+CONFIG_PROFILING=y
+# CONFIG_MARKERS is not set
+CONFIG_OPROFILE=m
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_KPROBES is not set
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+# CONFIG_HAVE_DMA_ATTRS is not set
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+# CONFIG_TINY_SHMEM is not set
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+# CONFIG_MODULE_FORCE_LOAD is not set
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+CONFIG_BLOCK=y
+# CONFIG_LBD is not set
+# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_LSF is not set
+# CONFIG_BLK_DEV_BSG is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=m
+CONFIG_IOSCHED_CFQ=m
+CONFIG_DEFAULT_AS=y
+# CONFIG_DEFAULT_DEADLINE is not set
+# CONFIG_DEFAULT_CFQ is not set
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="anticipatory"
+CONFIG_CLASSIC_RCU=y
+
+#
+# System Type
+#
+# CONFIG_ARCH_AAEC2000 is not set
+# CONFIG_ARCH_INTEGRATOR is not set
+# CONFIG_ARCH_REALVIEW is not set
+# CONFIG_ARCH_VERSATILE is not set
+# CONFIG_ARCH_AT91 is not set
+# CONFIG_ARCH_CLPS7500 is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_CO285 is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE is not set
+# CONFIG_ARCH_NETX is not set
+# CONFIG_ARCH_H720X is not set
+# CONFIG_ARCH_IMX is not set
+# CONFIG_ARCH_IOP13XX is not set
+# CONFIG_ARCH_IOP32X is not set
+# CONFIG_ARCH_IOP33X is not set
+# CONFIG_ARCH_IXP23XX is not set
+# CONFIG_ARCH_IXP2000 is not set
+# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_L7200 is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_NS9XXX is not set
+# CONFIG_ARCH_MXC is not set
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_PNX4008 is not set
+CONFIG_ARCH_PXA=y
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP is not set
+# CONFIG_ARCH_MSM7X00A is not set
+
+#
+# Intel PXA2xx/PXA3xx Implementations
+#
+# CONFIG_ARCH_GUMSTIX is not set
+# CONFIG_ARCH_LUBBOCK is not set
+# CONFIG_MACH_LOGICPD_PXA270 is not set
+# CONFIG_MACH_MAINSTONE is not set
+# CONFIG_ARCH_PXA_IDP is not set
+CONFIG_PXA_SHARPSL=y
+# CONFIG_ARCH_PXA_ESERIES is not set
+# CONFIG_MACH_TRIZEPS4 is not set
+# CONFIG_MACH_HX2750 is not set
+# CONFIG_MACH_EM_X270 is not set
+# CONFIG_MACH_COLIBRI is not set
+# CONFIG_MACH_ZYLONITE is not set
+# CONFIG_MACH_LITTLETON is not set
+# CONFIG_MACH_ARMCORE is not set
+# CONFIG_MACH_MAGICIAN is not set
+# CONFIG_MACH_PCM027 is not set
+# CONFIG_MACH_HTCUNIVERSAL is not set
+# CONFIG_PXA_SHARPSL_25x is not set
+CONFIG_PXA_SHARPSL_27x=y
+CONFIG_MACH_AKITA=y
+CONFIG_MACH_SPITZ=y
+CONFIG_MACH_BORZOI=y
+CONFIG_PXA27x=y
+CONFIG_PXA_SHARP_Cxx00=y
+CONFIG_PXA_SSP=y
+# CONFIG_PXA_KEYS is not set
+
+#
+# Boot options
+#
+
+#
+# Power management
+#
+
+#
+# Processor Type
+#
+CONFIG_CPU_32=y
+CONFIG_CPU_XSCALE=y
+CONFIG_CPU_32v5=y
+CONFIG_CPU_ABRT_EV5T=y
+CONFIG_CPU_PABRT_NOIFAR=y
+CONFIG_CPU_CACHE_VIVT=y
+CONFIG_CPU_TLB_V4WBI=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+
+#
+# Processor Features
+#
+CONFIG_ARM_THUMB=y
+# CONFIG_CPU_DCACHE_DISABLE is not set
+# CONFIG_OUTER_CACHE is not set
+CONFIG_IWMMXT=y
+CONFIG_XSCALE_PMU=y
+CONFIG_SHARP_PARAM=y
+CONFIG_SHARPSL_PM=y
+CONFIG_SHARP_SCOOP=y
+
+#
+# Bus support
+#
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+CONFIG_PCCARD=y
+# CONFIG_PCMCIA_DEBUG is not set
+CONFIG_PCMCIA=y
+CONFIG_PCMCIA_LOAD_CIS=y
+CONFIG_PCMCIA_IOCTL=y
+
+#
+# PC-card bridges
+#
+CONFIG_PCMCIA_PXA2XX=y
+
+#
+# Kernel Features
+#
+CONFIG_TICK_ONESHOT=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_PREEMPT=y
+CONFIG_HZ=100
+CONFIG_AEABI=y
+# CONFIG_OABI_COMPAT is not set
+# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+# CONFIG_SPARSEMEM_STATIC is not set
+# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4096
+# CONFIG_RESOURCES_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=1
+CONFIG_BOUNCE=y
+CONFIG_VIRT_TO_BUS=y
+CONFIG_ALIGNMENT_TRAP=y
+
+#
+# Boot options
+#
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="console=ttyS0,115200n8 console=tty1 noinitrd root=/dev/mtdblock2 rootfstype=jffs2 fbcon=rotate:1 dyntick=enable debug"
+# CONFIG_XIP_KERNEL is not set
+CONFIG_KEXEC=y
+CONFIG_ATAGS_PROC=y
+
+#
+# CPU Frequency scaling
+#
+# CONFIG_CPU_FREQ is not set
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+
+#
+# Userspace binary formats
+#
+CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_AOUT=m
+CONFIG_BINFMT_MISC=m
+
+#
+# Power management options
+#
+CONFIG_PM=y
+# CONFIG_PM_DEBUG is not set
+CONFIG_PM_SLEEP=y
+CONFIG_SUSPEND=y
+CONFIG_SUSPEND_FREEZER=y
+CONFIG_APM_EMULATION=y
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+
+#
+# Networking
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=m
+CONFIG_PACKET_MMAP=y
+CONFIG_UNIX=y
+CONFIG_XFRM=y
+CONFIG_XFRM_USER=m
+# CONFIG_XFRM_SUB_POLICY is not set
+# CONFIG_XFRM_MIGRATE is not set
+# CONFIG_XFRM_STATISTICS is not set
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+# CONFIG_IP_PNP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_ARPD is not set
+CONFIG_SYN_COOKIES=y
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+CONFIG_INET_TUNNEL=m
+CONFIG_INET_XFRM_MODE_TRANSPORT=m
+CONFIG_INET_XFRM_MODE_TUNNEL=m
+CONFIG_INET_XFRM_MODE_BEET=m
+# CONFIG_INET_LRO is not set
+CONFIG_INET_DIAG=m
+CONFIG_INET_TCP_DIAG=m
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+# CONFIG_IP_VS is not set
+CONFIG_IPV6=m
+# CONFIG_IPV6_PRIVACY is not set
+# CONFIG_IPV6_ROUTER_PREF is not set
+# CONFIG_IPV6_OPTIMISTIC_DAD is not set
+CONFIG_INET6_AH=m
+CONFIG_INET6_ESP=m
+CONFIG_INET6_IPCOMP=m
+# CONFIG_IPV6_MIP6 is not set
+CONFIG_INET6_XFRM_TUNNEL=m
+CONFIG_INET6_TUNNEL=m
+CONFIG_INET6_XFRM_MODE_TRANSPORT=m
+CONFIG_INET6_XFRM_MODE_TUNNEL=m
+CONFIG_INET6_XFRM_MODE_BEET=m
+# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set
+CONFIG_IPV6_SIT=m
+CONFIG_IPV6_NDISC_NODETYPE=y
+CONFIG_IPV6_TUNNEL=m
+# CONFIG_IPV6_MULTIPLE_TABLES is not set
+# CONFIG_IPV6_MROUTE is not set
+# CONFIG_NETWORK_SECMARK is not set
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+CONFIG_NETFILTER_ADVANCED=y
+
+#
+# Core Netfilter Configuration
+#
+# CONFIG_NETFILTER_NETLINK_QUEUE is not set
+# CONFIG_NETFILTER_NETLINK_LOG is not set
+# CONFIG_NF_CONNTRACK is not set
+CONFIG_NETFILTER_XTABLES=m
+# CONFIG_NETFILTER_XT_TARGET_CLASSIFY is not set
+# CONFIG_NETFILTER_XT_TARGET_DSCP is not set
+# CONFIG_NETFILTER_XT_TARGET_MARK is not set
+# CONFIG_NETFILTER_XT_TARGET_NFQUEUE is not set
+# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set
+# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set
+# CONFIG_NETFILTER_XT_TARGET_TRACE is not set
+# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set
+# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set
+# CONFIG_NETFILTER_XT_MATCH_COMMENT is not set
+# CONFIG_NETFILTER_XT_MATCH_DCCP is not set
+# CONFIG_NETFILTER_XT_MATCH_DSCP is not set
+# CONFIG_NETFILTER_XT_MATCH_ESP is not set
+# CONFIG_NETFILTER_XT_MATCH_IPRANGE is not set
+# CONFIG_NETFILTER_XT_MATCH_LENGTH is not set
+# CONFIG_NETFILTER_XT_MATCH_LIMIT is not set
+# CONFIG_NETFILTER_XT_MATCH_MAC is not set
+# CONFIG_NETFILTER_XT_MATCH_MARK is not set
+# CONFIG_NETFILTER_XT_MATCH_OWNER is not set
+# CONFIG_NETFILTER_XT_MATCH_POLICY is not set
+# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set
+# CONFIG_NETFILTER_XT_MATCH_PKTTYPE is not set
+# CONFIG_NETFILTER_XT_MATCH_QUOTA is not set
+# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set
+# CONFIG_NETFILTER_XT_MATCH_REALM is not set
+# CONFIG_NETFILTER_XT_MATCH_SCTP is not set
+# CONFIG_NETFILTER_XT_MATCH_STATISTIC is not set
+# CONFIG_NETFILTER_XT_MATCH_STRING is not set
+# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set
+# CONFIG_NETFILTER_XT_MATCH_TIME is not set
+# CONFIG_NETFILTER_XT_MATCH_U32 is not set
+# CONFIG_NETFILTER_XT_MATCH_HASHLIMIT is not set
+
+#
+# IP: Netfilter Configuration
+#
+CONFIG_IP_NF_QUEUE=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_RECENT=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_AH=m
+CONFIG_IP_NF_MATCH_TTL=m
+CONFIG_IP_NF_MATCH_ADDRTYPE=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_LOG=m
+CONFIG_IP_NF_TARGET_ULOG=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_TTL=m
+CONFIG_IP_NF_RAW=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+
+#
+# IPv6: Netfilter Configuration
+#
+# CONFIG_IP6_NF_QUEUE is not set
+# CONFIG_IP6_NF_IPTABLES is not set
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_SCTP is not set
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_SCHED is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_CAN is not set
+CONFIG_IRDA=m
+
+#
+# IrDA protocols
+#
+CONFIG_IRLAN=m
+CONFIG_IRNET=m
+CONFIG_IRCOMM=m
+# CONFIG_IRDA_ULTRA is not set
+
+#
+# IrDA options
+#
+# CONFIG_IRDA_CACHE_LAST_LSAP is not set
+# CONFIG_IRDA_FAST_RR is not set
+# CONFIG_IRDA_DEBUG is not set
+
+#
+# Infrared-port device drivers
+#
+
+#
+# SIR device drivers
+#
+# CONFIG_IRTTY_SIR is not set
+
+#
+# Dongle support
+#
+# CONFIG_KINGSUN_DONGLE is not set
+# CONFIG_KSDAZZLE_DONGLE is not set
+# CONFIG_KS959_DONGLE is not set
+
+#
+# FIR device drivers
+#
+# CONFIG_USB_IRDA is not set
+# CONFIG_SIGMATEL_FIR is not set
+CONFIG_PXA_FICP=m
+# CONFIG_MCS_FIR is not set
+CONFIG_BT=m
+CONFIG_BT_L2CAP=m
+CONFIG_BT_SCO=m
+CONFIG_BT_RFCOMM=m
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=m
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+CONFIG_BT_HIDP=m
+
+#
+# Bluetooth device drivers
+#
+CONFIG_BT_HCIUSB=m
+# CONFIG_BT_HCIUSB_SCO is not set
+# CONFIG_BT_HCIBTSDIO is not set
+CONFIG_BT_HCIUART=m
+CONFIG_BT_HCIUART_H4=y
+CONFIG_BT_HCIUART_BCSP=y
+# CONFIG_BT_HCIUART_LL is not set
+CONFIG_BT_HCIBCM203X=m
+CONFIG_BT_HCIBPA10X=m
+CONFIG_BT_HCIBFUSB=m
+CONFIG_BT_HCIDTL1=m
+CONFIG_BT_HCIBT3C=m
+CONFIG_BT_HCIBLUECARD=m
+CONFIG_BT_HCIBTUART=m
+CONFIG_BT_HCIVHCI=m
+# CONFIG_AF_RXRPC is not set
+
+#
+# Wireless
+#
+# CONFIG_CFG80211 is not set
+CONFIG_WIRELESS_EXT=y
+# CONFIG_MAC80211 is not set
+CONFIG_IEEE80211=m
+# CONFIG_IEEE80211_DEBUG is not set
+CONFIG_IEEE80211_CRYPT_WEP=m
+CONFIG_IEEE80211_CRYPT_CCMP=m
+CONFIG_IEEE80211_CRYPT_TKIP=m
+# CONFIG_RFKILL is not set
+# CONFIG_NET_9P is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_CONNECTOR is not set
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+# CONFIG_MTD_CONCAT is not set
+CONFIG_MTD_PARTITIONS=y
+# CONFIG_MTD_REDBOOT_PARTS is not set
+CONFIG_MTD_CMDLINE_PARTS=y
+# CONFIG_MTD_AFS_PARTS is not set
+# CONFIG_MTD_AR7_PARTS is not set
+
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+# CONFIG_MTD_OOPS is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+# CONFIG_MTD_CFI is not set
+# CONFIG_MTD_JEDECPROBE is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+# CONFIG_MTD_RAM is not set
+CONFIG_MTD_ROM=y
+# CONFIG_MTD_ABSENT is not set
+
+#
+# Mapping drivers for chip access
+#
+CONFIG_MTD_COMPLEX_MAPPINGS=y
+# CONFIG_MTD_PHYSMAP is not set
+CONFIG_MTD_SHARP_SL=y
+# CONFIG_MTD_PLATRAM is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLOCK2MTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_VERIFY_WRITE=y
+# CONFIG_MTD_NAND_ECC_SMC is not set
+# CONFIG_MTD_NAND_MUSEUM_IDS is not set
+# CONFIG_MTD_NAND_H1900 is not set
+CONFIG_MTD_NAND_IDS=y
+# CONFIG_MTD_NAND_DISKONCHIP is not set
+CONFIG_MTD_NAND_SHARPSL=y
+# CONFIG_MTD_NAND_NANDSIM is not set
+# CONFIG_MTD_NAND_PLATFORM is not set
+# CONFIG_MTD_ALAUDA is not set
+# CONFIG_MTD_ONENAND is not set
+
+#
+# UBI - Unsorted block images
+#
+# CONFIG_MTD_UBI is not set
+# CONFIG_PARPORT is not set
+CONFIG_BLK_DEV=y
+# CONFIG_BLK_DEV_COW_COMMON is not set
+CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_CRYPTOLOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_UB is not set
+# CONFIG_BLK_DEV_RAM is not set
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+CONFIG_MISC_DEVICES=y
+# CONFIG_EEPROM_93CX6 is not set
+# CONFIG_ENCLOSURE_SERVICES is not set
+CONFIG_HAVE_IDE=y
+CONFIG_IDE=y
+CONFIG_IDE_MAX_HWIFS=4
+CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_IDE_SATA is not set
+CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_IDEDISK_MULTI_MODE is not set
+CONFIG_BLK_DEV_IDECS=y
+# CONFIG_BLK_DEV_IDECD is not set
+# CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
+# CONFIG_BLK_DEV_IDESCSI is not set
+# CONFIG_IDE_TASK_IOCTL is not set
+CONFIG_IDE_PROC_FS=y
+
+#
+# IDE chipset support/bugfixes
+#
+# CONFIG_BLK_DEV_PLATFORM is not set
+# CONFIG_BLK_DEV_IDEDMA is not set
+# CONFIG_BLK_DEV_HD is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+CONFIG_SCSI=m
+CONFIG_SCSI_DMA=y
+# CONFIG_SCSI_TGT is not set
+# CONFIG_SCSI_NETLINK is not set
+CONFIG_SCSI_PROC_FS=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=m
+CONFIG_CHR_DEV_ST=m
+CONFIG_CHR_DEV_OSST=m
+CONFIG_BLK_DEV_SR=m
+# CONFIG_BLK_DEV_SR_VENDOR is not set
+CONFIG_CHR_DEV_SG=m
+# CONFIG_CHR_DEV_SCH is not set
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+CONFIG_SCSI_MULTI_LUN=y
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
+# CONFIG_SCSI_SCAN_ASYNC is not set
+CONFIG_SCSI_WAIT_SCAN=m
+
+#
+# SCSI Transports
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+# CONFIG_SCSI_ISCSI_ATTRS is not set
+# CONFIG_SCSI_SAS_LIBSAS is not set
+# CONFIG_SCSI_SRP_ATTRS is not set
+CONFIG_SCSI_LOWLEVEL=y
+# CONFIG_ISCSI_TCP is not set
+# CONFIG_SCSI_DEBUG is not set
+# CONFIG_SCSI_LOWLEVEL_PCMCIA is not set
+# CONFIG_ATA is not set
+CONFIG_MD=y
+# CONFIG_BLK_DEV_MD is not set
+CONFIG_BLK_DEV_DM=m
+# CONFIG_DM_DEBUG is not set
+CONFIG_DM_CRYPT=m
+CONFIG_DM_SNAPSHOT=m
+CONFIG_DM_MIRROR=m
+CONFIG_DM_ZERO=m
+CONFIG_DM_MULTIPATH=m
+CONFIG_DM_MULTIPATH_EMC=m
+# CONFIG_DM_MULTIPATH_RDAC is not set
+# CONFIG_DM_MULTIPATH_HP is not set
+# CONFIG_DM_DELAY is not set
+# CONFIG_DM_UEVENT is not set
+CONFIG_NETDEVICES=y
+# CONFIG_NETDEVICES_MULTIQUEUE is not set
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_MACVLAN is not set
+# CONFIG_EQUALIZER is not set
+CONFIG_TUN=m
+# CONFIG_VETH is not set
+# CONFIG_PHYLIB is not set
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=m
+# CONFIG_AX88796 is not set
+# CONFIG_SMC91X is not set
+# CONFIG_DM9000 is not set
+# CONFIG_SMC911X is not set
+# CONFIG_IBM_NEW_EMAC_ZMII is not set
+# CONFIG_IBM_NEW_EMAC_RGMII is not set
+# CONFIG_IBM_NEW_EMAC_TAH is not set
+# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
+# CONFIG_B44 is not set
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
+
+#
+# Wireless LAN
+#
+# CONFIG_WLAN_PRE80211 is not set
+CONFIG_WLAN_80211=y
+# CONFIG_PCMCIA_RAYCS is not set
+CONFIG_LIBERTAS=m
+CONFIG_LIBERTAS_USB=m
+CONFIG_LIBERTAS_CS=m
+# CONFIG_LIBERTAS_SDIO is not set
+# CONFIG_LIBERTAS_DEBUG is not set
+CONFIG_HERMES=m
+CONFIG_PCMCIA_HERMES=m
+CONFIG_PCMCIA_SPECTRUM=m
+# CONFIG_ATMEL is not set
+CONFIG_AIRO_CS=m
+# CONFIG_PCMCIA_WL3501 is not set
+# CONFIG_USB_ZD1201 is not set
+# CONFIG_USB_NET_RNDIS_WLAN is not set
+# CONFIG_IWLWIFI_LEDS is not set
+CONFIG_HOSTAP=m
+CONFIG_HOSTAP_FIRMWARE=y
+# CONFIG_HOSTAP_FIRMWARE_NVRAM is not set
+CONFIG_HOSTAP_CS=m
+
+#
+# USB Network Adapters
+#
+CONFIG_USB_CATC=m
+CONFIG_USB_KAWETH=m
+CONFIG_USB_PEGASUS=m
+CONFIG_USB_RTL8150=m
+CONFIG_USB_USBNET=m
+CONFIG_USB_NET_AX8817X=m
+CONFIG_USB_NET_CDCETHER=m
+CONFIG_USB_NET_DM9601=m
+# CONFIG_USB_NET_GL620A is not set
+CONFIG_USB_NET_NET1080=m
+# CONFIG_USB_NET_PLUSB is not set
+# CONFIG_USB_NET_MCS7830 is not set
+# CONFIG_USB_NET_RNDIS_HOST is not set
+# CONFIG_USB_NET_CDC_SUBSET is not set
+CONFIG_USB_NET_ZAURUS=m
+CONFIG_NET_PCMCIA=y
+# CONFIG_PCMCIA_3C589 is not set
+# CONFIG_PCMCIA_3C574 is not set
+# CONFIG_PCMCIA_FMVJ18X is not set
+CONFIG_PCMCIA_PCNET=m
+# CONFIG_PCMCIA_NMCLAN is not set
+# CONFIG_PCMCIA_SMC91C92 is not set
+# CONFIG_PCMCIA_XIRC2PS is not set
+# CONFIG_PCMCIA_AXNET is not set
+# CONFIG_WAN is not set
+CONFIG_PPP=m
+# CONFIG_PPP_MULTILINK is not set
+# CONFIG_PPP_FILTER is not set
+CONFIG_PPP_ASYNC=m
+# CONFIG_PPP_SYNC_TTY is not set
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_BSDCOMP=m
+# CONFIG_PPP_MPPE is not set
+# CONFIG_PPPOE is not set
+# CONFIG_PPPOL2TP is not set
+# CONFIG_SLIP is not set
+CONFIG_SLHC=m
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_ISDN is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=m
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=640
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=480
+# CONFIG_INPUT_JOYDEV is not set
+CONFIG_INPUT_EVDEV=y
+# CONFIG_INPUT_EVBUG is not set
+CONFIG_INPUT_APMPOWER=y
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+# CONFIG_KEYBOARD_ATKBD is not set
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+# CONFIG_KEYBOARD_XTKBD is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+# CONFIG_KEYBOARD_CORGI is not set
+CONFIG_KEYBOARD_SPITZ=y
+CONFIG_SHARPSL_RC=y
+# CONFIG_KEYBOARD_PXA27x is not set
+# CONFIG_KEYBOARD_GPIO is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_CORGI=y
+# CONFIG_TOUCHSCREEN_FUJITSU is not set
+# CONFIG_TOUCHSCREEN_GUNZE is not set
+# CONFIG_TOUCHSCREEN_ELO is not set
+# CONFIG_TOUCHSCREEN_MTOUCH is not set
+# CONFIG_TOUCHSCREEN_MK712 is not set
+# CONFIG_TOUCHSCREEN_PENMOUNT is not set
+# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set
+# CONFIG_TOUCHSCREEN_TOUCHWIN is not set
+# CONFIG_TOUCHSCREEN_UCB1400 is not set
+# CONFIG_TOUCHSCREEN_WM97XX is not set
+# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set
+CONFIG_INPUT_MISC=y
+# CONFIG_INPUT_ATI_REMOTE is not set
+# CONFIG_INPUT_ATI_REMOTE2 is not set
+# CONFIG_INPUT_KEYSPAN_REMOTE is not set
+# CONFIG_INPUT_POWERMATE is not set
+# CONFIG_INPUT_YEALINK is not set
+CONFIG_INPUT_UINPUT=m
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+# CONFIG_VT_HW_CONSOLE_BINDING is not set
+CONFIG_DEVKMEM=y
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+CONFIG_SERIAL_8250=m
+CONFIG_SERIAL_8250_CS=m
+CONFIG_SERIAL_8250_NR_UARTS=4
+CONFIG_SERIAL_8250_RUNTIME_UARTS=4
+# CONFIG_SERIAL_8250_EXTENDED is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_PXA=y
+CONFIG_SERIAL_PXA_CONSOLE=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_IPMI_HANDLER is not set
+CONFIG_HW_RANDOM=m
+# CONFIG_NVRAM is not set
+# CONFIG_R3964 is not set
+
+#
+# PCMCIA character devices
+#
+# CONFIG_SYNCLINK_CS is not set
+# CONFIG_CARDMAN_4000 is not set
+# CONFIG_CARDMAN_4040 is not set
+# CONFIG_IPWIRELESS is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+# CONFIG_I2C_CHARDEV is not set
+
+#
+# I2C Hardware Bus support
+#
+# CONFIG_I2C_GPIO is not set
+CONFIG_I2C_PXA=y
+# CONFIG_I2C_PXA_SLAVE is not set
+# CONFIG_I2C_OCORES is not set
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_SIMTEC is not set
+# CONFIG_I2C_TAOS_EVM is not set
+# CONFIG_I2C_STUB is not set
+# CONFIG_I2C_TINY_USB is not set
+# CONFIG_I2C_PCA_PLATFORM is not set
+
+#
+# Miscellaneous I2C Chip support
+#
+# CONFIG_DS1682 is not set
+# CONFIG_SENSORS_EEPROM is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_PCF8575 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_TPS65010 is not set
+# CONFIG_SENSORS_MAX6875 is not set
+# CONFIG_SENSORS_TSL2550 is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_I2C_DEBUG_CHIP is not set
+# CONFIG_SPI is not set
+CONFIG_HAVE_GPIO_LIB=y
+
+#
+# GPIO Support
+#
+
+#
+# I2C GPIO expanders:
+#
+# CONFIG_GPIO_PCA953X is not set
+# CONFIG_GPIO_PCF857X is not set
+
+#
+# SPI GPIO expanders:
+#
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+# CONFIG_WATCHDOG is not set
+
+#
+# Sonics Silicon Backplane
+#
+CONFIG_SSB_POSSIBLE=y
+# CONFIG_SSB is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_SM501 is not set
+# CONFIG_MFD_ASIC3 is not set
+# CONFIG_HTC_EGPIO is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_HTC_ASIC3 is not set
+# CONFIG_HTC_ASIC3_DS1WM is not set
+
+#
+# Multimedia devices
+#
+
+#
+# Multimedia core support
+#
+CONFIG_VIDEO_DEV=m
+CONFIG_VIDEO_V4L2_COMMON=m
+CONFIG_VIDEO_ALLOW_V4L1=y
+CONFIG_VIDEO_V4L1_COMPAT=y
+# CONFIG_DVB_CORE is not set
+CONFIG_VIDEO_MEDIA=m
+
+#
+# Multimedia drivers
+#
+# CONFIG_MEDIA_ATTACH is not set
+CONFIG_MEDIA_TUNER=m
+# CONFIG_MEDIA_TUNER_CUSTOMIZE is not set
+CONFIG_MEDIA_TUNER_SIMPLE=m
+CONFIG_MEDIA_TUNER_TDA8290=m
+CONFIG_MEDIA_TUNER_TDA9887=m
+CONFIG_MEDIA_TUNER_TEA5761=m
+CONFIG_MEDIA_TUNER_TEA5767=m
+CONFIG_MEDIA_TUNER_MT20XX=m
+CONFIG_MEDIA_TUNER_XC2028=m
+CONFIG_MEDIA_TUNER_XC5000=m
+CONFIG_VIDEO_V4L2=m
+CONFIG_VIDEO_V4L1=m
+CONFIG_VIDEO_CAPTURE_DRIVERS=y
+# CONFIG_VIDEO_ADV_DEBUG is not set
+CONFIG_VIDEO_HELPER_CHIPS_AUTO=y
+# CONFIG_VIDEO_VIVI is not set
+# CONFIG_VIDEO_CPIA is not set
+# CONFIG_VIDEO_CPIA2 is not set
+# CONFIG_VIDEO_SAA5246A is not set
+# CONFIG_VIDEO_SAA5249 is not set
+# CONFIG_TUNER_3036 is not set
+CONFIG_V4L_USB_DRIVERS=y
+# CONFIG_USB_VIDEO_CLASS is not set
+# CONFIG_VIDEO_PVRUSB2 is not set
+# CONFIG_VIDEO_EM28XX is not set
+# CONFIG_VIDEO_USBVISION is not set
+CONFIG_VIDEO_USBVIDEO=m
+CONFIG_USB_VICAM=m
+CONFIG_USB_IBMCAM=m
+CONFIG_USB_KONICAWC=m
+# CONFIG_USB_QUICKCAM_MESSENGER is not set
+# CONFIG_USB_ET61X251 is not set
+# CONFIG_VIDEO_OVCAMCHIP is not set
+# CONFIG_USB_W9968CF is not set
+CONFIG_USB_OV511=m
+CONFIG_USB_SE401=m
+CONFIG_USB_SN9C102=m
+CONFIG_USB_STV680=m
+# CONFIG_USB_ZC0301 is not set
+# CONFIG_USB_PWC is not set
+# CONFIG_USB_ZR364XX is not set
+# CONFIG_USB_STKWEBCAM is not set
+# CONFIG_SOC_CAMERA is not set
+# CONFIG_VIDEO_PXA27x is not set
+CONFIG_RADIO_ADAPTERS=y
+CONFIG_USB_DSBR=m
+# CONFIG_USB_SI470X is not set
+CONFIG_DAB=y
+CONFIG_USB_DABUSB=m
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+CONFIG_VIDEO_OUTPUT_CONTROL=m
+CONFIG_FB=y
+# CONFIG_FIRMWARE_EDID is not set
+# CONFIG_FB_DDC is not set
+CONFIG_FB_CFB_FILLRECT=y
+CONFIG_FB_CFB_COPYAREA=y
+CONFIG_FB_CFB_IMAGEBLIT=y
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+# CONFIG_FB_SYS_FILLRECT is not set
+# CONFIG_FB_SYS_COPYAREA is not set
+# CONFIG_FB_SYS_IMAGEBLIT is not set
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+# CONFIG_FB_SYS_FOPS is not set
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_BACKLIGHT is not set
+# CONFIG_FB_MODE_HELPERS is not set
+# CONFIG_FB_TILEBLITTING is not set
+
+#
+# Frame buffer hardware drivers
+#
+# CONFIG_FB_S1D13XXX is not set
+CONFIG_FB_PXA=y
+# CONFIG_FB_PXA_SMARTPANEL is not set
+# CONFIG_FB_PXA_PARAMETERS is not set
+# CONFIG_FB_MBX is not set
+# CONFIG_FB_W100 is not set
+# CONFIG_FB_AM200EPD is not set
+# CONFIG_FB_VIRTUAL is not set
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+# CONFIG_LCD_CLASS_DEVICE is not set
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+CONFIG_BACKLIGHT_CORGI=y
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
+CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y
+CONFIG_FONTS=y
+# CONFIG_FONT_8x8 is not set
+CONFIG_FONT_8x16=y
+# CONFIG_FONT_6x11 is not set
+# CONFIG_FONT_7x14 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+# CONFIG_FONT_ACORN_8x8 is not set
+# CONFIG_FONT_MINI_4x6 is not set
+# CONFIG_FONT_SUN8x16 is not set
+# CONFIG_FONT_SUN12x22 is not set
+# CONFIG_FONT_10x18 is not set
+# CONFIG_LOGO is not set
+
+#
+# Sound
+#
+CONFIG_SOUND=m
+
+#
+# Advanced Linux Sound Architecture
+#
+CONFIG_SND=m
+CONFIG_SND_TIMER=m
+CONFIG_SND_PCM=m
+CONFIG_SND_HWDEP=m
+CONFIG_SND_RAWMIDI=m
+CONFIG_SND_SEQUENCER=m
+# CONFIG_SND_SEQ_DUMMY is not set
+CONFIG_SND_OSSEMUL=y
+CONFIG_SND_MIXER_OSS=m
+CONFIG_SND_PCM_OSS=m
+CONFIG_SND_PCM_OSS_PLUGINS=y
+# CONFIG_SND_SEQUENCER_OSS is not set
+# CONFIG_SND_DYNAMIC_MINORS is not set
+CONFIG_SND_SUPPORT_OLD_API=y
+CONFIG_SND_VERBOSE_PROCFS=y
+# CONFIG_SND_VERBOSE_PRINTK is not set
+# CONFIG_SND_DEBUG is not set
+
+#
+# Generic devices
+#
+CONFIG_SND_AC97_CODEC=m
+# CONFIG_SND_DUMMY is not set
+# CONFIG_SND_VIRMIDI is not set
+# CONFIG_SND_MTPAV is not set
+# CONFIG_SND_SERIAL_U16550 is not set
+# CONFIG_SND_MPU401 is not set
+
+#
+# ALSA ARM devices
+#
+CONFIG_SND_PXA2XX_PCM=m
+CONFIG_SND_PXA2XX_AC97=m
+
+#
+# USB devices
+#
+CONFIG_SND_USB_AUDIO=m
+# CONFIG_SND_USB_CAIAQ is not set
+
+#
+# PCMCIA devices
+#
+# CONFIG_SND_VXPOCKET is not set
+# CONFIG_SND_PDAUDIOCF is not set
+
+#
+# System on Chip audio support
+#
+CONFIG_SND_SOC=m
+CONFIG_SND_PXA2XX_SOC=m
+CONFIG_SND_PXA2XX_SOC_I2S=m
+CONFIG_SND_PXA2XX_SOC_SPITZ=m
+
+#
+# ALSA SoC audio for Freescale SOCs
+#
+
+#
+# SoC Audio for the Texas Instruments OMAP
+#
+CONFIG_SND_SOC_WM8750=m
+
+#
+# Open Sound System
+#
+# CONFIG_SOUND_PRIME is not set
+CONFIG_AC97_BUS=m
+CONFIG_HID_SUPPORT=y
+CONFIG_HID=m
+# CONFIG_HID_DEBUG is not set
+# CONFIG_HIDRAW is not set
+
+#
+# USB Input Devices
+#
+CONFIG_USB_HID=m
+# CONFIG_USB_HIDINPUT_POWERBOOK is not set
+# CONFIG_HID_FF is not set
+# CONFIG_USB_HIDDEV is not set
+
+#
+# USB HID Boot Protocol drivers
+#
+CONFIG_USB_KBD=m
+CONFIG_USB_MOUSE=m
+CONFIG_USB_SUPPORT=y
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARCH_HAS_OHCI=y
+# CONFIG_USB_ARCH_HAS_EHCI is not set
+CONFIG_USB=m
+# CONFIG_USB_DEBUG is not set
+# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set
+
+#
+# Miscellaneous USB options
+#
+CONFIG_USB_DEVICEFS=y
+CONFIG_USB_DEVICE_CLASS=y
+# CONFIG_USB_DYNAMIC_MINORS is not set
+# CONFIG_USB_SUSPEND is not set
+# CONFIG_USB_OTG is not set
+# CONFIG_USB_OTG_WHITELIST is not set
+# CONFIG_USB_OTG_BLACKLIST_HUB is not set
+
+#
+# USB Host Controller Drivers
+#
+# CONFIG_USB_C67X00_HCD is not set
+# CONFIG_USB_ISP116X_HCD is not set
+# CONFIG_USB_ISP1760_HCD is not set
+CONFIG_USB_OHCI_HCD=m
+# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set
+# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set
+CONFIG_USB_OHCI_LITTLE_ENDIAN=y
+CONFIG_USB_SL811_HCD=m
+CONFIG_USB_SL811_CS=m
+# CONFIG_USB_R8A66597_HCD is not set
+
+#
+# USB Device Class drivers
+#
+CONFIG_USB_ACM=m
+CONFIG_USB_PRINTER=m
+# CONFIG_USB_WDM is not set
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
+#
+
+#
+# may also be needed; see USB_STORAGE Help for more information
+#
+CONFIG_USB_STORAGE=m
+# CONFIG_USB_STORAGE_DEBUG is not set
+# CONFIG_USB_STORAGE_DATAFAB is not set
+# CONFIG_USB_STORAGE_FREECOM is not set
+# CONFIG_USB_STORAGE_ISD200 is not set
+# CONFIG_USB_STORAGE_DPCM is not set
+# CONFIG_USB_STORAGE_USBAT is not set
+# CONFIG_USB_STORAGE_SDDR09 is not set
+# CONFIG_USB_STORAGE_SDDR55 is not set
+# CONFIG_USB_STORAGE_JUMPSHOT is not set
+# CONFIG_USB_STORAGE_ALAUDA is not set
+# CONFIG_USB_STORAGE_ONETOUCH is not set
+# CONFIG_USB_STORAGE_KARMA is not set
+# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set
+# CONFIG_USB_LIBUSUAL is not set
+
+#
+# USB Imaging devices
+#
+CONFIG_USB_MDC800=m
+CONFIG_USB_MICROTEK=m
+CONFIG_USB_MON=y
+
+#
+# USB port drivers
+#
+CONFIG_USB_SERIAL=m
+CONFIG_USB_EZUSB=y
+CONFIG_USB_SERIAL_GENERIC=y
+# CONFIG_USB_SERIAL_AIRCABLE is not set
+# CONFIG_USB_SERIAL_AIRPRIME is not set
+# CONFIG_USB_SERIAL_ARK3116 is not set
+CONFIG_USB_SERIAL_BELKIN=m
+# CONFIG_USB_SERIAL_CH341 is not set
+# CONFIG_USB_SERIAL_WHITEHEAT is not set
+CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m
+CONFIG_USB_SERIAL_CP2101=m
+CONFIG_USB_SERIAL_CYPRESS_M8=m
+CONFIG_USB_SERIAL_EMPEG=m
+CONFIG_USB_SERIAL_FTDI_SIO=m
+# CONFIG_USB_SERIAL_FUNSOFT is not set
+CONFIG_USB_SERIAL_VISOR=m
+CONFIG_USB_SERIAL_IPAQ=m
+CONFIG_USB_SERIAL_IR=m
+CONFIG_USB_SERIAL_EDGEPORT=m
+CONFIG_USB_SERIAL_EDGEPORT_TI=m
+CONFIG_USB_SERIAL_GARMIN=m
+CONFIG_USB_SERIAL_IPW=m
+# CONFIG_USB_SERIAL_IUU is not set
+CONFIG_USB_SERIAL_KEYSPAN_PDA=m
+CONFIG_USB_SERIAL_KEYSPAN=m
+# CONFIG_USB_SERIAL_KEYSPAN_MPR is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19QW is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19QI is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA49WLC is not set
+CONFIG_USB_SERIAL_KLSI=m
+CONFIG_USB_SERIAL_KOBIL_SCT=m
+CONFIG_USB_SERIAL_MCT_U232=m
+# CONFIG_USB_SERIAL_MOS7720 is not set
+# CONFIG_USB_SERIAL_MOS7840 is not set
+# CONFIG_USB_SERIAL_MOTOROLA is not set
+# CONFIG_USB_SERIAL_NAVMAN is not set
+CONFIG_USB_SERIAL_PL2303=m
+# CONFIG_USB_SERIAL_OTI6858 is not set
+# CONFIG_USB_SERIAL_SPCP8X5 is not set
+# CONFIG_USB_SERIAL_HP4X is not set
+CONFIG_USB_SERIAL_SAFE=m
+# CONFIG_USB_SERIAL_SAFE_PADDED is not set
+# CONFIG_USB_SERIAL_SIERRAWIRELESS is not set
+CONFIG_USB_SERIAL_TI=m
+CONFIG_USB_SERIAL_CYBERJACK=m
+CONFIG_USB_SERIAL_XIRCOM=m
+# CONFIG_USB_SERIAL_OPTION is not set
+CONFIG_USB_SERIAL_OMNINET=m
+# CONFIG_USB_SERIAL_DEBUG is not set
+
+#
+# USB Miscellaneous drivers
+#
+CONFIG_USB_EMI62=m
+CONFIG_USB_EMI26=m
+# CONFIG_USB_ADUTUX is not set
+CONFIG_USB_AUERSWALD=m
+CONFIG_USB_RIO500=m
+CONFIG_USB_LEGOTOWER=m
+CONFIG_USB_LCD=m
+# CONFIG_USB_BERRY_CHARGE is not set
+CONFIG_USB_LED=m
+# CONFIG_USB_CYPRESS_CY7C63 is not set
+CONFIG_USB_CYTHERM=m
+# CONFIG_USB_PHIDGET is not set
+CONFIG_USB_IDMOUSE=m
+# CONFIG_USB_FTDI_ELAN is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+# CONFIG_USB_LD is not set
+# CONFIG_USB_TRANCEVIBRATOR is not set
+# CONFIG_USB_IOWARRIOR is not set
+# CONFIG_USB_TEST is not set
+# CONFIG_USB_ISIGHTFW is not set
+CONFIG_USB_GADGET=m
+# CONFIG_USB_GADGET_DEBUG_FILES is not set
+CONFIG_USB_GADGET_SELECTED=y
+# CONFIG_USB_GADGET_AMD5536UDC is not set
+# CONFIG_USB_GADGET_ATMEL_USBA is not set
+# CONFIG_USB_GADGET_FSL_USB2 is not set
+# CONFIG_USB_GADGET_NET2280 is not set
+# CONFIG_USB_GADGET_PXA2XX is not set
+# CONFIG_USB_GADGET_M66592 is not set
+CONFIG_USB_GADGET_PXA27X=y
+CONFIG_USB_PXA27X=m
+# CONFIG_USB_GADGET_GOKU is not set
+# CONFIG_USB_GADGET_LH7A40X is not set
+# CONFIG_USB_GADGET_OMAP is not set
+# CONFIG_USB_GADGET_S3C2410 is not set
+# CONFIG_USB_GADGET_AT91 is not set
+# CONFIG_USB_GADGET_DUMMY_HCD is not set
+# CONFIG_USB_GADGET_DUALSPEED is not set
+CONFIG_USB_ZERO=m
+CONFIG_USB_ETH=m
+CONFIG_USB_ETH_RNDIS=y
+CONFIG_USB_GADGETFS=m
+CONFIG_USB_FILE_STORAGE=m
+# CONFIG_USB_FILE_STORAGE_TEST is not set
+CONFIG_USB_G_SERIAL=m
+# CONFIG_USB_MIDI_GADGET is not set
+# CONFIG_USB_G_PRINTER is not set
+CONFIG_MMC=y
+# CONFIG_MMC_DEBUG is not set
+CONFIG_MMC_UNSAFE_RESUME=y
+
+#
+# MMC/SD Card Drivers
+#
+CONFIG_MMC_BLOCK=y
+CONFIG_MMC_BLOCK_BOUNCE=y
+# CONFIG_SDIO_UART is not set
+# CONFIG_MMC_TEST is not set
+
+#
+# MMC/SD Host Controller Drivers
+#
+CONFIG_MMC_PXA=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+
+#
+# LED drivers
+#
+CONFIG_LEDS_SPITZ=y
+# CONFIG_LEDS_GPIO is not set
+
+#
+# LED Triggers
+#
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=y
+CONFIG_LEDS_TRIGGER_IDE_DISK=y
+# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set
+# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set
+CONFIG_RTC_LIB=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_HCTOSYS=y
+CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
+# CONFIG_RTC_DEBUG is not set
+
+#
+# RTC interfaces
+#
+CONFIG_RTC_INTF_SYSFS=y
+CONFIG_RTC_INTF_PROC=y
+CONFIG_RTC_INTF_DEV=y
+# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
+# CONFIG_RTC_DRV_TEST is not set
+
+#
+# I2C RTC drivers
+#
+# CONFIG_RTC_DRV_DS1307 is not set
+# CONFIG_RTC_DRV_DS1374 is not set
+# CONFIG_RTC_DRV_DS1672 is not set
+# CONFIG_RTC_DRV_MAX6900 is not set
+# CONFIG_RTC_DRV_RS5C372 is not set
+# CONFIG_RTC_DRV_ISL1208 is not set
+# CONFIG_RTC_DRV_X1205 is not set
+# CONFIG_RTC_DRV_PCF8563 is not set
+# CONFIG_RTC_DRV_PCF8583 is not set
+# CONFIG_RTC_DRV_M41T80 is not set
+# CONFIG_RTC_DRV_S35390A is not set
+# CONFIG_RTC_DRV_FM3130 is not set
+
+#
+# SPI RTC drivers
+#
+
+#
+# Platform RTC drivers
+#
+# CONFIG_RTC_DRV_CMOS is not set
+# CONFIG_RTC_DRV_DS1511 is not set
+# CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_DS1742 is not set
+# CONFIG_RTC_DRV_STK17TA8 is not set
+# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_M48T59 is not set
+# CONFIG_RTC_DRV_V3020 is not set
+
+#
+# on-CPU RTC drivers
+#
+CONFIG_RTC_DRV_SA1100=y
+# CONFIG_UIO is not set
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+# CONFIG_EXT2_FS_XIP is not set
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_FS_XATTR is not set
+# CONFIG_EXT4DEV_FS is not set
+CONFIG_JBD=y
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+CONFIG_FS_POSIX_ACL=y
+# CONFIG_XFS_FS is not set
+# CONFIG_OCFS2_FS is not set
+CONFIG_DNOTIFY=y
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+# CONFIG_MSDOS_FS is not set
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_DEBUG=0
+CONFIG_JFFS2_FS_WRITEBUFFER=y
+# CONFIG_JFFS2_FS_WBUF_VERIFY is not set
+CONFIG_JFFS2_SUMMARY=y
+# CONFIG_JFFS2_FS_XATTR is not set
+# CONFIG_JFFS2_SYSFS is not set
+CONFIG_JFFS2_COMPRESSION_OPTIONS=y
+CONFIG_JFFS2_ZLIB=y
+CONFIG_JFFS2_LZO=y
+CONFIG_JFFS2_RTIME=y
+CONFIG_JFFS2_RUBIN=y
+# CONFIG_JFFS2_CMODE_NONE is not set
+CONFIG_JFFS2_CMODE_PRIORITY=y
+# CONFIG_JFFS2_CMODE_SIZE is not set
+# CONFIG_JFFS2_CMODE_FAVOURLZO is not set
+CONFIG_CRAMFS=m
+# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+CONFIG_NFS_FS=m
+CONFIG_NFS_V3=y
+# CONFIG_NFS_V3_ACL is not set
+CONFIG_NFS_V4=y
+CONFIG_NFSD=m
+CONFIG_NFSD_V3=y
+# CONFIG_NFSD_V3_ACL is not set
+CONFIG_NFSD_V4=y
+CONFIG_LOCKD=m
+CONFIG_LOCKD_V4=y
+CONFIG_EXPORTFS=m
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=m
+CONFIG_SUNRPC_GSS=m
+# CONFIG_SUNRPC_BIND34 is not set
+CONFIG_RPCSEC_GSS_KRB5=m
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+CONFIG_SMB_FS=m
+CONFIG_SMB_NLS_DEFAULT=y
+CONFIG_SMB_NLS_REMOTE="cp437"
+CONFIG_CIFS=m
+# CONFIG_CIFS_STATS is not set
+# CONFIG_CIFS_WEAK_PW_HASH is not set
+# CONFIG_CIFS_XATTR is not set
+# CONFIG_CIFS_DEBUG2 is not set
+# CONFIG_CIFS_EXPERIMENTAL is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_MINIX_SUBPARTITION is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_UNIXWARE_DISKLABEL is not set
+# CONFIG_LDM_PARTITION is not set
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_ULTRIX_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_KARMA_PARTITION is not set
+# CONFIG_EFI_PARTITION is not set
+# CONFIG_SYSV68_PARTITION is not set
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="cp437"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_737=m
+CONFIG_NLS_CODEPAGE_775=m
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
+CONFIG_NLS_CODEPAGE_855=m
+CONFIG_NLS_CODEPAGE_857=m
+CONFIG_NLS_CODEPAGE_860=m
+CONFIG_NLS_CODEPAGE_861=m
+CONFIG_NLS_CODEPAGE_862=m
+CONFIG_NLS_CODEPAGE_863=m
+CONFIG_NLS_CODEPAGE_864=m
+CONFIG_NLS_CODEPAGE_865=m
+CONFIG_NLS_CODEPAGE_866=m
+CONFIG_NLS_CODEPAGE_869=m
+CONFIG_NLS_CODEPAGE_936=m
+CONFIG_NLS_CODEPAGE_950=m
+CONFIG_NLS_CODEPAGE_932=m
+CONFIG_NLS_CODEPAGE_949=m
+CONFIG_NLS_CODEPAGE_874=m
+CONFIG_NLS_ISO8859_8=m
+CONFIG_NLS_CODEPAGE_1250=m
+CONFIG_NLS_CODEPAGE_1251=m
+CONFIG_NLS_ASCII=m
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_ISO8859_2=m
+CONFIG_NLS_ISO8859_3=m
+CONFIG_NLS_ISO8859_4=m
+CONFIG_NLS_ISO8859_5=m
+CONFIG_NLS_ISO8859_6=m
+CONFIG_NLS_ISO8859_7=m
+CONFIG_NLS_ISO8859_9=m
+CONFIG_NLS_ISO8859_13=m
+CONFIG_NLS_ISO8859_14=m
+CONFIG_NLS_ISO8859_15=m
+CONFIG_NLS_KOI8_R=m
+CONFIG_NLS_KOI8_U=m
+CONFIG_NLS_UTF8=y
+# CONFIG_DLM is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_PRINTK_TIME is not set
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_FRAME_WARN=1024
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_DEBUG_FS is not set
+# CONFIG_HEADERS_CHECK is not set
+# CONFIG_DEBUG_KERNEL is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+CONFIG_FRAME_POINTER=y
+# CONFIG_SAMPLES is not set
+# CONFIG_DEBUG_USER is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+CONFIG_CRYPTO=y
+
+#
+# Crypto core or helper
+#
+CONFIG_CRYPTO_ALGAPI=m
+CONFIG_CRYPTO_AEAD=m
+CONFIG_CRYPTO_BLKCIPHER=m
+CONFIG_CRYPTO_HASH=m
+CONFIG_CRYPTO_MANAGER=m
+# CONFIG_CRYPTO_GF128MUL is not set
+CONFIG_CRYPTO_NULL=m
+# CONFIG_CRYPTO_CRYPTD is not set
+CONFIG_CRYPTO_AUTHENC=m
+CONFIG_CRYPTO_TEST=m
+
+#
+# Authenticated Encryption with Associated Data
+#
+# CONFIG_CRYPTO_CCM is not set
+# CONFIG_CRYPTO_GCM is not set
+# CONFIG_CRYPTO_SEQIV is not set
+
+#
+# Block modes
+#
+CONFIG_CRYPTO_CBC=m
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+CONFIG_CRYPTO_ECB=m
+# CONFIG_CRYPTO_LRW is not set
+CONFIG_CRYPTO_PCBC=m
+# CONFIG_CRYPTO_XTS is not set
+
+#
+# Hash modes
+#
+CONFIG_CRYPTO_HMAC=m
+# CONFIG_CRYPTO_XCBC is not set
+
+#
+# Digest
+#
+CONFIG_CRYPTO_CRC32C=m
+CONFIG_CRYPTO_MD4=m
+CONFIG_CRYPTO_MD5=m
+CONFIG_CRYPTO_MICHAEL_MIC=m
+CONFIG_CRYPTO_SHA1=m
+CONFIG_CRYPTO_SHA256=m
+CONFIG_CRYPTO_SHA512=m
+# CONFIG_CRYPTO_TGR192 is not set
+CONFIG_CRYPTO_WP512=m
+
+#
+# Ciphers
+#
+CONFIG_CRYPTO_AES=m
+CONFIG_CRYPTO_ANUBIS=m
+CONFIG_CRYPTO_ARC4=m
+CONFIG_CRYPTO_BLOWFISH=m
+CONFIG_CRYPTO_CAMELLIA=m
+CONFIG_CRYPTO_CAST5=m
+CONFIG_CRYPTO_CAST6=m
+CONFIG_CRYPTO_DES=m
+# CONFIG_CRYPTO_FCRYPT is not set
+CONFIG_CRYPTO_KHAZAD=m
+# CONFIG_CRYPTO_SALSA20 is not set
+# CONFIG_CRYPTO_SEED is not set
+CONFIG_CRYPTO_SERPENT=m
+CONFIG_CRYPTO_TEA=m
+CONFIG_CRYPTO_TWOFISH=m
+CONFIG_CRYPTO_TWOFISH_COMMON=m
+
+#
+# Compression
+#
+CONFIG_CRYPTO_DEFLATE=m
+CONFIG_CRYPTO_LZO=m
+# CONFIG_CRYPTO_HW is not set
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+# CONFIG_GENERIC_FIND_FIRST_BIT is not set
+# CONFIG_GENERIC_FIND_NEXT_BIT is not set
+CONFIG_CRC_CCITT=y
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
+CONFIG_LIBCRC32C=m
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_LZO_COMPRESS=y
+CONFIG_LZO_DECOMPRESS=y
+CONFIG_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
diff --git a/recipes/linux/linux-rp-2.6.26/defconfig-bootcdx86 b/recipes/linux/linux-rp-2.6.26/defconfig-bootcdx86
new file mode 100644
index 0000000000..397468a366
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.26/defconfig-bootcdx86
@@ -0,0 +1,1994 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.24-rc8
+# Sun Jan 20 18:34:58 2008
+#
+# CONFIG_64BIT is not set
+CONFIG_X86_32=y
+# CONFIG_X86_64 is not set
+CONFIG_X86=y
+CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_CMOS_UPDATE=y
+CONFIG_CLOCKSOURCE_WATCHDOG=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_SEMAPHORE_SLEEPERS=y
+CONFIG_MMU=y
+CONFIG_ZONE_DMA=y
+CONFIG_QUICKLIST=y
+CONFIG_GENERIC_ISA_DMA=y
+CONFIG_GENERIC_IOMAP=y
+CONFIG_GENERIC_BUG=y
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_ARCH_MAY_HAVE_PC_FDC=y
+CONFIG_DMI=y
+# CONFIG_RWSEM_GENERIC_SPINLOCK is not set
+CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+# CONFIG_ARCH_HAS_ILOG2_U32 is not set
+# CONFIG_ARCH_HAS_ILOG2_U64 is not set
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+# CONFIG_GENERIC_TIME_VSYSCALL is not set
+CONFIG_ARCH_SUPPORTS_OPROFILE=y
+# CONFIG_ZONE_DMA32 is not set
+CONFIG_ARCH_POPULATES_NODE_MAP=y
+# CONFIG_AUDIT_ARCH is not set
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_GENERIC_PENDING_IRQ=y
+CONFIG_X86_SMP=y
+CONFIG_X86_HT=y
+CONFIG_X86_BIOS_REBOOT=y
+CONFIG_X86_TRAMPOLINE=y
+CONFIG_KTIME_SCALAR=y
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_LOCK_KERNEL=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+CONFIG_POSIX_MQUEUE=y
+# CONFIG_BSD_PROCESS_ACCT is not set
+# CONFIG_TASKSTATS is not set
+# CONFIG_USER_NS is not set
+# CONFIG_PID_NS is not set
+CONFIG_AUDIT=y
+CONFIG_AUDITSYSCALL=y
+CONFIG_AUDIT_TREE=y
+# CONFIG_IKCONFIG is not set
+CONFIG_LOG_BUF_SHIFT=15
+# CONFIG_CGROUPS is not set
+CONFIG_FAIR_GROUP_SCHED=y
+CONFIG_FAIR_USER_SCHED=y
+# CONFIG_FAIR_CGROUP_SCHED is not set
+CONFIG_SYSFS_DEPRECATED=y
+# CONFIG_RELAY is not set
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SYSCTL=y
+CONFIG_EMBEDDED=y
+CONFIG_UID16=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_ANON_INODES=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLAB=y
+# CONFIG_SLUB is not set
+# CONFIG_SLOB is not set
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+# CONFIG_TINY_SHMEM is not set
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+CONFIG_STOP_MACHINE=y
+CONFIG_BLOCK=y
+CONFIG_LBD=y
+# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_LSF is not set
+# CONFIG_BLK_DEV_BSG is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=m
+CONFIG_IOSCHED_CFQ=m
+CONFIG_DEFAULT_AS=y
+# CONFIG_DEFAULT_DEADLINE is not set
+# CONFIG_DEFAULT_CFQ is not set
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="anticipatory"
+
+#
+# Processor type and features
+#
+CONFIG_TICK_ONESHOT=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_SMP=y
+CONFIG_X86_PC=y
+# CONFIG_X86_ELAN is not set
+# CONFIG_X86_VOYAGER is not set
+# CONFIG_X86_NUMAQ is not set
+# CONFIG_X86_SUMMIT is not set
+# CONFIG_X86_BIGSMP is not set
+# CONFIG_X86_VISWS is not set
+# CONFIG_X86_GENERICARCH is not set
+# CONFIG_X86_ES7000 is not set
+# CONFIG_X86_VSMP is not set
+CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y
+# CONFIG_PARAVIRT_GUEST is not set
+# CONFIG_M386 is not set
+# CONFIG_M486 is not set
+# CONFIG_M586 is not set
+# CONFIG_M586TSC is not set
+# CONFIG_M586MMX is not set
+CONFIG_M686=y
+# CONFIG_MPENTIUMII is not set
+# CONFIG_MPENTIUMIII is not set
+# CONFIG_MPENTIUMM is not set
+# CONFIG_MPENTIUM4 is not set
+# CONFIG_MK6 is not set
+# CONFIG_MK7 is not set
+# CONFIG_MK8 is not set
+# CONFIG_MCRUSOE is not set
+# CONFIG_MEFFICEON is not set
+# CONFIG_MWINCHIPC6 is not set
+# CONFIG_MWINCHIP2 is not set
+# CONFIG_MWINCHIP3D is not set
+# CONFIG_MGEODEGX1 is not set
+# CONFIG_MGEODE_LX is not set
+# CONFIG_MCYRIXIII is not set
+# CONFIG_MVIAC3_2 is not set
+# CONFIG_MVIAC7 is not set
+# CONFIG_MPSC is not set
+# CONFIG_MCORE2 is not set
+# CONFIG_GENERIC_CPU is not set
+CONFIG_X86_GENERIC=y
+CONFIG_X86_CMPXCHG=y
+CONFIG_X86_L1_CACHE_SHIFT=7
+CONFIG_X86_XADD=y
+CONFIG_X86_PPRO_FENCE=y
+CONFIG_X86_WP_WORKS_OK=y
+CONFIG_X86_INVLPG=y
+CONFIG_X86_BSWAP=y
+CONFIG_X86_POPAD_OK=y
+CONFIG_X86_GOOD_APIC=y
+CONFIG_X86_INTEL_USERCOPY=y
+CONFIG_X86_USE_PPRO_CHECKSUM=y
+CONFIG_X86_TSC=y
+CONFIG_X86_CMOV=y
+CONFIG_X86_MINIMUM_CPU_FAMILY=4
+CONFIG_HPET_TIMER=y
+CONFIG_NR_CPUS=8
+CONFIG_SCHED_SMT=y
+CONFIG_SCHED_MC=y
+CONFIG_PREEMPT_NONE=y
+# CONFIG_PREEMPT_VOLUNTARY is not set
+# CONFIG_PREEMPT is not set
+CONFIG_PREEMPT_BKL=y
+CONFIG_X86_LOCAL_APIC=y
+CONFIG_X86_IO_APIC=y
+CONFIG_X86_MCE=y
+CONFIG_X86_MCE_NONFATAL=y
+CONFIG_X86_MCE_P4THERMAL=y
+CONFIG_VM86=y
+# CONFIG_TOSHIBA is not set
+# CONFIG_I8K is not set
+# CONFIG_X86_REBOOTFIXUPS is not set
+# CONFIG_MICROCODE is not set
+# CONFIG_X86_MSR is not set
+# CONFIG_X86_CPUID is not set
+CONFIG_NOHIGHMEM=y
+# CONFIG_HIGHMEM4G is not set
+# CONFIG_HIGHMEM64G is not set
+CONFIG_VMSPLIT_3G=y
+# CONFIG_VMSPLIT_3G_OPT is not set
+# CONFIG_VMSPLIT_2G is not set
+# CONFIG_VMSPLIT_2G_OPT is not set
+# CONFIG_VMSPLIT_1G is not set
+CONFIG_PAGE_OFFSET=0xC0000000
+# CONFIG_X86_PAE is not set
+CONFIG_ARCH_FLATMEM_ENABLE=y
+CONFIG_ARCH_SPARSEMEM_ENABLE=y
+CONFIG_ARCH_SELECT_MEMORY_MODEL=y
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+CONFIG_SPARSEMEM_STATIC=y
+# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set
+CONFIG_SPLIT_PTLOCK_CPUS=4
+# CONFIG_RESOURCES_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=1
+CONFIG_BOUNCE=y
+CONFIG_NR_QUICK=1
+CONFIG_VIRT_TO_BUS=y
+# CONFIG_MATH_EMULATION is not set
+CONFIG_MTRR=y
+# CONFIG_EFI is not set
+CONFIG_IRQBALANCE=y
+CONFIG_SECCOMP=y
+# CONFIG_HZ_100 is not set
+CONFIG_HZ_250=y
+# CONFIG_HZ_300 is not set
+# CONFIG_HZ_1000 is not set
+CONFIG_HZ=250
+CONFIG_KEXEC=y
+CONFIG_PHYSICAL_START=0x100000
+# CONFIG_RELOCATABLE is not set
+CONFIG_PHYSICAL_ALIGN=0x100000
+CONFIG_HOTPLUG_CPU=y
+CONFIG_COMPAT_VDSO=y
+
+#
+# Power management options
+#
+CONFIG_PM=y
+CONFIG_PM_LEGACY=y
+# CONFIG_PM_DEBUG is not set
+CONFIG_PM_SLEEP_SMP=y
+CONFIG_PM_SLEEP=y
+CONFIG_SUSPEND_SMP_POSSIBLE=y
+CONFIG_SUSPEND=y
+CONFIG_HIBERNATION_SMP_POSSIBLE=y
+# CONFIG_HIBERNATION is not set
+CONFIG_ACPI=y
+CONFIG_ACPI_SLEEP=y
+# CONFIG_ACPI_PROCFS is not set
+CONFIG_ACPI_PROCFS_POWER=y
+CONFIG_ACPI_SYSFS_POWER=y
+CONFIG_ACPI_PROC_EVENT=y
+CONFIG_ACPI_AC=y
+CONFIG_ACPI_BATTERY=y
+CONFIG_ACPI_BUTTON=y
+# CONFIG_ACPI_VIDEO is not set
+CONFIG_ACPI_FAN=y
+# CONFIG_ACPI_DOCK is not set
+CONFIG_ACPI_PROCESSOR=y
+CONFIG_ACPI_HOTPLUG_CPU=y
+CONFIG_ACPI_THERMAL=y
+# CONFIG_ACPI_ASUS is not set
+# CONFIG_ACPI_TOSHIBA is not set
+CONFIG_ACPI_BLACKLIST_YEAR=0
+# CONFIG_ACPI_DEBUG is not set
+CONFIG_ACPI_EC=y
+CONFIG_ACPI_POWER=y
+CONFIG_ACPI_SYSTEM=y
+CONFIG_X86_PM_TIMER=y
+CONFIG_ACPI_CONTAINER=y
+# CONFIG_ACPI_SBS is not set
+# CONFIG_APM is not set
+
+#
+# CPU Frequency scaling
+#
+# CONFIG_CPU_FREQ is not set
+# CONFIG_CPU_IDLE is not set
+
+#
+# Bus options (PCI etc.)
+#
+CONFIG_PCI=y
+# CONFIG_PCI_GOBIOS is not set
+# CONFIG_PCI_GOMMCONFIG is not set
+# CONFIG_PCI_GODIRECT is not set
+CONFIG_PCI_GOANY=y
+CONFIG_PCI_BIOS=y
+CONFIG_PCI_DIRECT=y
+CONFIG_PCI_MMCONFIG=y
+CONFIG_PCI_DOMAINS=y
+CONFIG_PCIEPORTBUS=y
+CONFIG_PCIEAER=y
+CONFIG_ARCH_SUPPORTS_MSI=y
+CONFIG_PCI_MSI=y
+CONFIG_PCI_LEGACY=y
+CONFIG_HT_IRQ=y
+CONFIG_ISA_DMA_API=y
+CONFIG_ISA=y
+# CONFIG_EISA is not set
+# CONFIG_MCA is not set
+# CONFIG_SCx200 is not set
+CONFIG_K8_NB=y
+# CONFIG_PCCARD is not set
+# CONFIG_HOTPLUG_PCI is not set
+
+#
+# Executable file formats / Emulations
+#
+CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_AOUT=m
+CONFIG_BINFMT_MISC=m
+
+#
+# Networking
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=m
+CONFIG_PACKET_MMAP=y
+CONFIG_UNIX=y
+CONFIG_XFRM=y
+# CONFIG_XFRM_USER is not set
+# CONFIG_XFRM_SUB_POLICY is not set
+# CONFIG_XFRM_MIGRATE is not set
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_ARPD is not set
+CONFIG_SYN_COOKIES=y
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+CONFIG_INET_TUNNEL=m
+CONFIG_INET_XFRM_MODE_TRANSPORT=m
+CONFIG_INET_XFRM_MODE_TUNNEL=m
+CONFIG_INET_XFRM_MODE_BEET=m
+# CONFIG_INET_LRO is not set
+CONFIG_INET_DIAG=m
+CONFIG_INET_TCP_DIAG=m
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+# CONFIG_IP_VS is not set
+CONFIG_IPV6=m
+# CONFIG_IPV6_PRIVACY is not set
+# CONFIG_IPV6_ROUTER_PREF is not set
+# CONFIG_IPV6_OPTIMISTIC_DAD is not set
+CONFIG_INET6_AH=m
+CONFIG_INET6_ESP=m
+CONFIG_INET6_IPCOMP=m
+# CONFIG_IPV6_MIP6 is not set
+CONFIG_INET6_XFRM_TUNNEL=m
+CONFIG_INET6_TUNNEL=m
+CONFIG_INET6_XFRM_MODE_TRANSPORT=m
+CONFIG_INET6_XFRM_MODE_TUNNEL=m
+CONFIG_INET6_XFRM_MODE_BEET=m
+# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set
+CONFIG_IPV6_SIT=m
+CONFIG_IPV6_TUNNEL=m
+# CONFIG_IPV6_MULTIPLE_TABLES is not set
+# CONFIG_NETWORK_SECMARK is not set
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+
+#
+# Core Netfilter Configuration
+#
+# CONFIG_NETFILTER_NETLINK is not set
+# CONFIG_NF_CONNTRACK_ENABLED is not set
+# CONFIG_NF_CONNTRACK is not set
+CONFIG_NETFILTER_XTABLES=m
+# CONFIG_NETFILTER_XT_TARGET_CLASSIFY is not set
+# CONFIG_NETFILTER_XT_TARGET_DSCP is not set
+# CONFIG_NETFILTER_XT_TARGET_MARK is not set
+# CONFIG_NETFILTER_XT_TARGET_NFQUEUE is not set
+# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set
+# CONFIG_NETFILTER_XT_TARGET_TRACE is not set
+# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set
+# CONFIG_NETFILTER_XT_MATCH_COMMENT is not set
+# CONFIG_NETFILTER_XT_MATCH_DCCP is not set
+# CONFIG_NETFILTER_XT_MATCH_DSCP is not set
+# CONFIG_NETFILTER_XT_MATCH_ESP is not set
+# CONFIG_NETFILTER_XT_MATCH_LENGTH is not set
+# CONFIG_NETFILTER_XT_MATCH_LIMIT is not set
+# CONFIG_NETFILTER_XT_MATCH_MAC is not set
+# CONFIG_NETFILTER_XT_MATCH_MARK is not set
+# CONFIG_NETFILTER_XT_MATCH_POLICY is not set
+# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set
+# CONFIG_NETFILTER_XT_MATCH_PKTTYPE is not set
+# CONFIG_NETFILTER_XT_MATCH_QUOTA is not set
+# CONFIG_NETFILTER_XT_MATCH_REALM is not set
+# CONFIG_NETFILTER_XT_MATCH_SCTP is not set
+# CONFIG_NETFILTER_XT_MATCH_STATISTIC is not set
+# CONFIG_NETFILTER_XT_MATCH_STRING is not set
+# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set
+# CONFIG_NETFILTER_XT_MATCH_TIME is not set
+# CONFIG_NETFILTER_XT_MATCH_U32 is not set
+# CONFIG_NETFILTER_XT_MATCH_HASHLIMIT is not set
+
+#
+# IP: Netfilter Configuration
+#
+CONFIG_IP_NF_QUEUE=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_IPRANGE=m
+CONFIG_IP_NF_MATCH_TOS=m
+CONFIG_IP_NF_MATCH_RECENT=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_AH=m
+CONFIG_IP_NF_MATCH_TTL=m
+CONFIG_IP_NF_MATCH_OWNER=m
+CONFIG_IP_NF_MATCH_ADDRTYPE=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_LOG=m
+CONFIG_IP_NF_TARGET_ULOG=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_TOS=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_TTL=m
+CONFIG_IP_NF_RAW=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+
+#
+# IPv6: Netfilter Configuration (EXPERIMENTAL)
+#
+# CONFIG_IP6_NF_QUEUE is not set
+# CONFIG_IP6_NF_IPTABLES is not set
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_SCTP is not set
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_SCHED is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+CONFIG_IRDA=m
+
+#
+# IrDA protocols
+#
+CONFIG_IRLAN=m
+CONFIG_IRNET=m
+CONFIG_IRCOMM=m
+# CONFIG_IRDA_ULTRA is not set
+
+#
+# IrDA options
+#
+# CONFIG_IRDA_CACHE_LAST_LSAP is not set
+# CONFIG_IRDA_FAST_RR is not set
+# CONFIG_IRDA_DEBUG is not set
+
+#
+# Infrared-port device drivers
+#
+
+#
+# SIR device drivers
+#
+# CONFIG_IRTTY_SIR is not set
+
+#
+# Dongle support
+#
+# CONFIG_KINGSUN_DONGLE is not set
+# CONFIG_KSDAZZLE_DONGLE is not set
+# CONFIG_KS959_DONGLE is not set
+
+#
+# Old SIR device drivers
+#
+
+#
+# Old Serial dongle support
+#
+
+#
+# FIR device drivers
+#
+# CONFIG_USB_IRDA is not set
+# CONFIG_SIGMATEL_FIR is not set
+# CONFIG_NSC_FIR is not set
+# CONFIG_WINBOND_FIR is not set
+# CONFIG_TOSHIBA_FIR is not set
+# CONFIG_SMC_IRCC_FIR is not set
+# CONFIG_ALI_FIR is not set
+# CONFIG_VLSI_FIR is not set
+# CONFIG_VIA_FIR is not set
+# CONFIG_MCS_FIR is not set
+CONFIG_BT=m
+CONFIG_BT_L2CAP=m
+CONFIG_BT_SCO=m
+CONFIG_BT_RFCOMM=m
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=m
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+CONFIG_BT_HIDP=m
+
+#
+# Bluetooth device drivers
+#
+CONFIG_BT_HCIUSB=m
+# CONFIG_BT_HCIUSB_SCO is not set
+CONFIG_BT_HCIUART=m
+CONFIG_BT_HCIUART_H4=y
+CONFIG_BT_HCIUART_BCSP=y
+# CONFIG_BT_HCIUART_LL is not set
+CONFIG_BT_HCIBCM203X=m
+CONFIG_BT_HCIBPA10X=m
+CONFIG_BT_HCIBFUSB=m
+CONFIG_BT_HCIVHCI=m
+# CONFIG_AF_RXRPC is not set
+
+#
+# Wireless
+#
+# CONFIG_CFG80211 is not set
+CONFIG_WIRELESS_EXT=y
+# CONFIG_MAC80211 is not set
+CONFIG_IEEE80211=m
+# CONFIG_IEEE80211_DEBUG is not set
+CONFIG_IEEE80211_CRYPT_WEP=m
+CONFIG_IEEE80211_CRYPT_CCMP=m
+CONFIG_IEEE80211_CRYPT_TKIP=m
+CONFIG_IEEE80211_SOFTMAC=m
+# CONFIG_IEEE80211_SOFTMAC_DEBUG is not set
+# CONFIG_RFKILL is not set
+# CONFIG_NET_9P is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+# CONFIG_SYS_HYPERVISOR is not set
+CONFIG_CONNECTOR=y
+CONFIG_PROC_EVENTS=y
+# CONFIG_MTD is not set
+CONFIG_PARPORT=y
+CONFIG_PARPORT_PC=y
+# CONFIG_PARPORT_SERIAL is not set
+# CONFIG_PARPORT_PC_FIFO is not set
+# CONFIG_PARPORT_PC_SUPERIO is not set
+# CONFIG_PARPORT_GSC is not set
+# CONFIG_PARPORT_AX88796 is not set
+# CONFIG_PARPORT_1284 is not set
+CONFIG_PNP=y
+# CONFIG_PNP_DEBUG is not set
+
+#
+# Protocols
+#
+# CONFIG_ISAPNP is not set
+# CONFIG_PNPBIOS is not set
+CONFIG_PNPACPI=y
+CONFIG_BLK_DEV=y
+CONFIG_BLK_DEV_FD=y
+# CONFIG_BLK_DEV_XD is not set
+# CONFIG_PARIDE is not set
+# CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_CPQ_CISS_DA is not set
+# CONFIG_BLK_DEV_DAC960 is not set
+# CONFIG_BLK_DEV_UMEM is not set
+# CONFIG_BLK_DEV_COW_COMMON is not set
+CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_CRYPTOLOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_SX8 is not set
+# CONFIG_BLK_DEV_UB is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=65536
+CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+CONFIG_MISC_DEVICES=y
+# CONFIG_IBM_ASM is not set
+# CONFIG_PHANTOM is not set
+# CONFIG_EEPROM_93CX6 is not set
+# CONFIG_SGI_IOC4 is not set
+# CONFIG_TIFM_CORE is not set
+# CONFIG_ASUS_LAPTOP is not set
+# CONFIG_FUJITSU_LAPTOP is not set
+# CONFIG_MSI_LAPTOP is not set
+# CONFIG_SONY_LAPTOP is not set
+# CONFIG_THINKPAD_ACPI is not set
+CONFIG_IDE=y
+CONFIG_IDE_MAX_HWIFS=4
+CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_IDE_SATA is not set
+# CONFIG_BLK_DEV_HD_IDE is not set
+CONFIG_BLK_DEV_IDEDISK=y
+CONFIG_IDEDISK_MULTI_MODE=y
+CONFIG_BLK_DEV_IDECD=y
+# CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
+# CONFIG_BLK_DEV_IDESCSI is not set
+# CONFIG_BLK_DEV_IDEACPI is not set
+# CONFIG_IDE_TASK_IOCTL is not set
+CONFIG_IDE_PROC_FS=y
+
+#
+# IDE chipset support/bugfixes
+#
+CONFIG_IDE_GENERIC=y
+# CONFIG_BLK_DEV_PLATFORM is not set
+CONFIG_BLK_DEV_CMD640=y
+# CONFIG_BLK_DEV_CMD640_ENHANCED is not set
+# CONFIG_BLK_DEV_IDEPNP is not set
+
+#
+# PCI IDE chipsets support
+#
+CONFIG_BLK_DEV_IDEPCI=y
+CONFIG_IDEPCI_SHARE_IRQ=y
+CONFIG_IDEPCI_PCIBUS_ORDER=y
+# CONFIG_BLK_DEV_OFFBOARD is not set
+CONFIG_BLK_DEV_GENERIC=y
+# CONFIG_BLK_DEV_OPTI621 is not set
+CONFIG_BLK_DEV_RZ1000=y
+CONFIG_BLK_DEV_IDEDMA_PCI=y
+# CONFIG_BLK_DEV_AEC62XX is not set
+# CONFIG_BLK_DEV_ALI15X3 is not set
+# CONFIG_BLK_DEV_AMD74XX is not set
+# CONFIG_BLK_DEV_ATIIXP is not set
+# CONFIG_BLK_DEV_CMD64X is not set
+# CONFIG_BLK_DEV_TRIFLEX is not set
+# CONFIG_BLK_DEV_CY82C693 is not set
+# CONFIG_BLK_DEV_CS5520 is not set
+# CONFIG_BLK_DEV_CS5530 is not set
+# CONFIG_BLK_DEV_CS5535 is not set
+# CONFIG_BLK_DEV_HPT34X is not set
+# CONFIG_BLK_DEV_HPT366 is not set
+# CONFIG_BLK_DEV_JMICRON is not set
+# CONFIG_BLK_DEV_SC1200 is not set
+CONFIG_BLK_DEV_PIIX=y
+# CONFIG_BLK_DEV_IT8213 is not set
+# CONFIG_BLK_DEV_IT821X is not set
+# CONFIG_BLK_DEV_NS87415 is not set
+# CONFIG_BLK_DEV_PDC202XX_OLD is not set
+# CONFIG_BLK_DEV_PDC202XX_NEW is not set
+# CONFIG_BLK_DEV_SVWKS is not set
+# CONFIG_BLK_DEV_SIIMAGE is not set
+# CONFIG_BLK_DEV_SIS5513 is not set
+# CONFIG_BLK_DEV_SLC90E66 is not set
+# CONFIG_BLK_DEV_TRM290 is not set
+# CONFIG_BLK_DEV_VIA82CXXX is not set
+# CONFIG_BLK_DEV_TC86C001 is not set
+# CONFIG_IDE_ARM is not set
+
+#
+# Other IDE chipsets support
+#
+
+#
+# Note: most of these also require special kernel boot parameters
+#
+# CONFIG_BLK_DEV_4DRIVES is not set
+# CONFIG_BLK_DEV_ALI14XX is not set
+# CONFIG_BLK_DEV_DTC2278 is not set
+# CONFIG_BLK_DEV_HT6560B is not set
+# CONFIG_BLK_DEV_QD65XX is not set
+# CONFIG_BLK_DEV_UMC8672 is not set
+CONFIG_BLK_DEV_IDEDMA=y
+CONFIG_IDE_ARCH_OBSOLETE_INIT=y
+# CONFIG_BLK_DEV_HD is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+CONFIG_SCSI=y
+CONFIG_SCSI_DMA=y
+# CONFIG_SCSI_TGT is not set
+# CONFIG_SCSI_NETLINK is not set
+CONFIG_SCSI_PROC_FS=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=y
+# CONFIG_CHR_DEV_ST is not set
+# CONFIG_CHR_DEV_OSST is not set
+# CONFIG_BLK_DEV_SR is not set
+CONFIG_CHR_DEV_SG=y
+# CONFIG_CHR_DEV_SCH is not set
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+CONFIG_SCSI_MULTI_LUN=y
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
+# CONFIG_SCSI_SCAN_ASYNC is not set
+CONFIG_SCSI_WAIT_SCAN=m
+
+#
+# SCSI Transports
+#
+CONFIG_SCSI_SPI_ATTRS=y
+# CONFIG_SCSI_FC_ATTRS is not set
+# CONFIG_SCSI_ISCSI_ATTRS is not set
+# CONFIG_SCSI_SAS_LIBSAS is not set
+# CONFIG_SCSI_SRP_ATTRS is not set
+CONFIG_SCSI_LOWLEVEL=y
+# CONFIG_ISCSI_TCP is not set
+# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
+# CONFIG_SCSI_3W_9XXX is not set
+# CONFIG_SCSI_7000FASST is not set
+# CONFIG_SCSI_ACARD is not set
+# CONFIG_SCSI_AHA152X is not set
+# CONFIG_SCSI_AHA1542 is not set
+# CONFIG_SCSI_AACRAID is not set
+# CONFIG_SCSI_AIC7XXX is not set
+# CONFIG_SCSI_AIC7XXX_OLD is not set
+# CONFIG_SCSI_AIC79XX is not set
+# CONFIG_SCSI_AIC94XX is not set
+CONFIG_SCSI_DPT_I2O=m
+# CONFIG_SCSI_ADVANSYS is not set
+# CONFIG_SCSI_IN2000 is not set
+# CONFIG_SCSI_ARCMSR is not set
+# CONFIG_MEGARAID_NEWGEN is not set
+# CONFIG_MEGARAID_LEGACY is not set
+# CONFIG_MEGARAID_SAS is not set
+# CONFIG_SCSI_HPTIOP is not set
+# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_DMX3191D is not set
+# CONFIG_SCSI_DTC3280 is not set
+# CONFIG_SCSI_EATA is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GDTH is not set
+# CONFIG_SCSI_GENERIC_NCR5380 is not set
+# CONFIG_SCSI_GENERIC_NCR5380_MMIO is not set
+# CONFIG_SCSI_IPS is not set
+# CONFIG_SCSI_INITIO is not set
+# CONFIG_SCSI_INIA100 is not set
+# CONFIG_SCSI_PPA is not set
+# CONFIG_SCSI_IMM is not set
+# CONFIG_SCSI_NCR53C406A is not set
+# CONFIG_SCSI_STEX is not set
+# CONFIG_SCSI_SYM53C8XX_2 is not set
+# CONFIG_SCSI_PAS16 is not set
+# CONFIG_SCSI_PSI240I is not set
+# CONFIG_SCSI_QLOGIC_FAS is not set
+# CONFIG_SCSI_QLOGIC_1280 is not set
+# CONFIG_SCSI_QLA_FC is not set
+# CONFIG_SCSI_QLA_ISCSI is not set
+# CONFIG_SCSI_LPFC is not set
+# CONFIG_SCSI_SEAGATE is not set
+# CONFIG_SCSI_SYM53C416 is not set
+# CONFIG_SCSI_DC395x is not set
+# CONFIG_SCSI_DC390T is not set
+# CONFIG_SCSI_T128 is not set
+# CONFIG_SCSI_U14_34F is not set
+# CONFIG_SCSI_ULTRASTOR is not set
+# CONFIG_SCSI_NSP32 is not set
+# CONFIG_SCSI_DEBUG is not set
+# CONFIG_SCSI_SRP is not set
+# CONFIG_ATA is not set
+CONFIG_MD=y
+# CONFIG_BLK_DEV_MD is not set
+CONFIG_BLK_DEV_DM=m
+# CONFIG_DM_DEBUG is not set
+CONFIG_DM_CRYPT=m
+CONFIG_DM_SNAPSHOT=m
+CONFIG_DM_MIRROR=m
+CONFIG_DM_ZERO=m
+CONFIG_DM_MULTIPATH=m
+CONFIG_DM_MULTIPATH_EMC=m
+# CONFIG_DM_MULTIPATH_RDAC is not set
+# CONFIG_DM_MULTIPATH_HP is not set
+# CONFIG_DM_DELAY is not set
+# CONFIG_DM_UEVENT is not set
+# CONFIG_FUSION is not set
+
+#
+# IEEE 1394 (FireWire) support
+#
+# CONFIG_FIREWIRE is not set
+# CONFIG_IEEE1394 is not set
+# CONFIG_I2O is not set
+# CONFIG_MACINTOSH_DRIVERS is not set
+CONFIG_NETDEVICES=y
+# CONFIG_NETDEVICES_MULTIQUEUE is not set
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_MACVLAN is not set
+# CONFIG_EQUALIZER is not set
+CONFIG_TUN=m
+# CONFIG_VETH is not set
+# CONFIG_NET_SB1000 is not set
+# CONFIG_ARCNET is not set
+# CONFIG_PHYLIB is not set
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=y
+# CONFIG_HAPPYMEAL is not set
+# CONFIG_SUNGEM is not set
+# CONFIG_CASSINI is not set
+CONFIG_NET_VENDOR_3COM=y
+# CONFIG_EL1 is not set
+# CONFIG_EL2 is not set
+# CONFIG_ELPLUS is not set
+# CONFIG_EL16 is not set
+# CONFIG_EL3 is not set
+# CONFIG_3C515 is not set
+CONFIG_VORTEX=m
+CONFIG_TYPHOON=m
+CONFIG_LANCE=m
+CONFIG_NET_VENDOR_SMC=y
+CONFIG_WD80x3=m
+CONFIG_ULTRA=m
+CONFIG_SMC9194=m
+# CONFIG_NET_VENDOR_RACAL is not set
+CONFIG_NET_TULIP=y
+# CONFIG_DE2104X is not set
+CONFIG_TULIP=m
+CONFIG_TULIP_MWI=y
+CONFIG_TULIP_MMIO=y
+CONFIG_TULIP_NAPI=y
+CONFIG_TULIP_NAPI_HW_MITIGATION=y
+CONFIG_DE4X5=m
+CONFIG_WINBOND_840=m
+CONFIG_DM9102=m
+CONFIG_ULI526X=m
+CONFIG_AT1700=m
+CONFIG_DEPCA=m
+CONFIG_HP100=m
+CONFIG_NET_ISA=y
+# CONFIG_E2100 is not set
+# CONFIG_EWRK3 is not set
+# CONFIG_EEXPRESS is not set
+# CONFIG_EEXPRESS_PRO is not set
+# CONFIG_HPLAN_PLUS is not set
+# CONFIG_HPLAN is not set
+# CONFIG_LP486E is not set
+# CONFIG_ETH16I is not set
+CONFIG_NE2000=y
+# CONFIG_ZNET is not set
+# CONFIG_SEEQ8005 is not set
+# CONFIG_IBM_NEW_EMAC_ZMII is not set
+# CONFIG_IBM_NEW_EMAC_RGMII is not set
+# CONFIG_IBM_NEW_EMAC_TAH is not set
+# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
+CONFIG_NET_PCI=y
+CONFIG_PCNET32=m
+CONFIG_PCNET32_NAPI=y
+CONFIG_AMD8111_ETH=m
+CONFIG_AMD8111E_NAPI=y
+CONFIG_ADAPTEC_STARFIRE=m
+CONFIG_ADAPTEC_STARFIRE_NAPI=y
+CONFIG_AC3200=m
+CONFIG_APRICOT=m
+CONFIG_B44=m
+CONFIG_B44_PCI_AUTOSELECT=y
+CONFIG_B44_PCICORE_AUTOSELECT=y
+CONFIG_B44_PCI=y
+CONFIG_FORCEDETH=m
+CONFIG_FORCEDETH_NAPI=y
+CONFIG_CS89x0=m
+CONFIG_EEPRO100=m
+CONFIG_E100=m
+CONFIG_FEALNX=m
+CONFIG_NATSEMI=m
+CONFIG_NE2K_PCI=y
+CONFIG_8139CP=m
+CONFIG_8139TOO=m
+CONFIG_8139TOO_PIO=y
+CONFIG_8139TOO_TUNE_TWISTER=y
+CONFIG_8139TOO_8129=y
+# CONFIG_8139_OLD_RX_RESET is not set
+CONFIG_SIS900=m
+CONFIG_EPIC100=m
+CONFIG_SUNDANCE=m
+CONFIG_SUNDANCE_MMIO=y
+CONFIG_TLAN=m
+CONFIG_VIA_RHINE=m
+CONFIG_VIA_RHINE_MMIO=y
+CONFIG_VIA_RHINE_NAPI=y
+CONFIG_SC92031=m
+# CONFIG_NET_POCKET is not set
+CONFIG_NETDEV_1000=y
+CONFIG_ACENIC=m
+CONFIG_ACENIC_OMIT_TIGON_I=y
+CONFIG_DL2K=m
+CONFIG_E1000=m
+CONFIG_E1000_NAPI=y
+# CONFIG_E1000_DISABLE_PACKET_SPLIT is not set
+# CONFIG_E1000E is not set
+# CONFIG_IP1000 is not set
+CONFIG_NS83820=m
+CONFIG_HAMACHI=m
+CONFIG_YELLOWFIN=m
+CONFIG_R8169=m
+CONFIG_R8169_NAPI=y
+CONFIG_SIS190=m
+CONFIG_SKGE=m
+CONFIG_SKY2=m
+CONFIG_SK98LIN=m
+CONFIG_VIA_VELOCITY=m
+CONFIG_TIGON3=m
+CONFIG_BNX2=m
+CONFIG_QLA3XXX=m
+CONFIG_ATL1=m
+CONFIG_NETDEV_10000=y
+# CONFIG_CHELSIO_T1 is not set
+# CONFIG_CHELSIO_T3 is not set
+# CONFIG_IXGBE is not set
+# CONFIG_IXGB is not set
+CONFIG_S2IO=m
+# CONFIG_S2IO_NAPI is not set
+# CONFIG_MYRI10GE is not set
+# CONFIG_NETXEN_NIC is not set
+# CONFIG_NIU is not set
+# CONFIG_MLX4_CORE is not set
+# CONFIG_TEHUTI is not set
+# CONFIG_TR is not set
+
+#
+# Wireless LAN
+#
+# CONFIG_WLAN_PRE80211 is not set
+CONFIG_WLAN_80211=y
+# CONFIG_IPW2100 is not set
+# CONFIG_IPW2200 is not set
+# CONFIG_LIBERTAS is not set
+# CONFIG_AIRO is not set
+CONFIG_HERMES=m
+# CONFIG_PLX_HERMES is not set
+# CONFIG_TMD_HERMES is not set
+# CONFIG_NORTEL_HERMES is not set
+# CONFIG_PCI_HERMES is not set
+# CONFIG_ATMEL is not set
+# CONFIG_PRISM54 is not set
+# CONFIG_USB_ZD1201 is not set
+CONFIG_HOSTAP=m
+CONFIG_HOSTAP_FIRMWARE=y
+# CONFIG_HOSTAP_FIRMWARE_NVRAM is not set
+# CONFIG_HOSTAP_PLX is not set
+# CONFIG_HOSTAP_PCI is not set
+# CONFIG_BCM43XX is not set
+# CONFIG_ZD1211RW is not set
+
+#
+# USB Network Adapters
+#
+# CONFIG_USB_CATC is not set
+# CONFIG_USB_KAWETH is not set
+# CONFIG_USB_PEGASUS is not set
+# CONFIG_USB_RTL8150 is not set
+# CONFIG_USB_USBNET is not set
+# CONFIG_WAN is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PLIP is not set
+CONFIG_PPP=m
+# CONFIG_PPP_MULTILINK is not set
+# CONFIG_PPP_FILTER is not set
+CONFIG_PPP_ASYNC=m
+# CONFIG_PPP_SYNC_TTY is not set
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_BSDCOMP=m
+# CONFIG_PPP_MPPE is not set
+# CONFIG_PPPOE is not set
+# CONFIG_PPPOL2TP is not set
+# CONFIG_SLIP is not set
+CONFIG_SLHC=m
+# CONFIG_NET_FC is not set
+# CONFIG_SHAPER is not set
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_ISDN is not set
+# CONFIG_PHONE is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=m
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+# CONFIG_INPUT_JOYDEV is not set
+CONFIG_INPUT_EVDEV=y
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+CONFIG_KEYBOARD_ATKBD=y
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+# CONFIG_KEYBOARD_XTKBD is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+CONFIG_INPUT_MOUSE=y
+CONFIG_MOUSE_PS2=y
+CONFIG_MOUSE_PS2_ALPS=y
+CONFIG_MOUSE_PS2_LOGIPS2PP=y
+CONFIG_MOUSE_PS2_SYNAPTICS=y
+CONFIG_MOUSE_PS2_LIFEBOOK=y
+CONFIG_MOUSE_PS2_TRACKPOINT=y
+# CONFIG_MOUSE_PS2_TOUCHKIT is not set
+# CONFIG_MOUSE_SERIAL is not set
+# CONFIG_MOUSE_APPLETOUCH is not set
+# CONFIG_MOUSE_INPORT is not set
+# CONFIG_MOUSE_LOGIBM is not set
+# CONFIG_MOUSE_PC110PAD is not set
+# CONFIG_MOUSE_VSXXXAA is not set
+# CONFIG_INPUT_JOYSTICK is not set
+CONFIG_INPUT_TABLET=y
+# CONFIG_TABLET_USB_ACECAD is not set
+# CONFIG_TABLET_USB_AIPTEK is not set
+# CONFIG_TABLET_USB_GTCO is not set
+# CONFIG_TABLET_USB_KBTAB is not set
+CONFIG_TABLET_USB_WACOM=y
+# CONFIG_INPUT_TOUCHSCREEN is not set
+CONFIG_INPUT_MISC=y
+# CONFIG_INPUT_PCSPKR is not set
+# CONFIG_INPUT_WISTRON_BTNS is not set
+# CONFIG_INPUT_ATLAS_BTNS is not set
+# CONFIG_INPUT_ATI_REMOTE is not set
+# CONFIG_INPUT_ATI_REMOTE2 is not set
+# CONFIG_INPUT_KEYSPAN_REMOTE is not set
+# CONFIG_INPUT_POWERMATE is not set
+# CONFIG_INPUT_YEALINK is not set
+CONFIG_INPUT_UINPUT=m
+
+#
+# Hardware I/O ports
+#
+CONFIG_SERIO=y
+CONFIG_SERIO_I8042=y
+# CONFIG_SERIO_SERPORT is not set
+# CONFIG_SERIO_CT82C710 is not set
+# CONFIG_SERIO_PARKBD is not set
+# CONFIG_SERIO_PCIPS2 is not set
+CONFIG_SERIO_LIBPS2=y
+# CONFIG_SERIO_RAW is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+# CONFIG_VT_HW_CONSOLE_BINDING is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+CONFIG_SERIAL_8250=y
+# CONFIG_SERIAL_8250_CONSOLE is not set
+CONFIG_FIX_EARLYCON_MEM=y
+CONFIG_SERIAL_8250_PCI=y
+CONFIG_SERIAL_8250_PNP=y
+CONFIG_SERIAL_8250_NR_UARTS=4
+CONFIG_SERIAL_8250_RUNTIME_UARTS=4
+# CONFIG_SERIAL_8250_EXTENDED is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_CORE=y
+# CONFIG_SERIAL_JSM is not set
+CONFIG_UNIX98_PTYS=y
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_PRINTER=y
+# CONFIG_LP_CONSOLE is not set
+# CONFIG_PPDEV is not set
+# CONFIG_IPMI_HANDLER is not set
+# CONFIG_HW_RANDOM is not set
+# CONFIG_NVRAM is not set
+# CONFIG_RTC is not set
+# CONFIG_GEN_RTC is not set
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+# CONFIG_APPLICOM is not set
+# CONFIG_SONYPI is not set
+# CONFIG_MWAVE is not set
+# CONFIG_PC8736x_GPIO is not set
+# CONFIG_NSC_GPIO is not set
+# CONFIG_CS5535_GPIO is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_HPET is not set
+# CONFIG_HANGCHECK_TIMER is not set
+# CONFIG_TCG_TPM is not set
+# CONFIG_TELCLOCK is not set
+CONFIG_DEVPORT=y
+# CONFIG_I2C is not set
+
+#
+# SPI support
+#
+# CONFIG_SPI is not set
+# CONFIG_SPI_MASTER is not set
+# CONFIG_W1 is not set
+CONFIG_POWER_SUPPLY=y
+# CONFIG_POWER_SUPPLY_DEBUG is not set
+# CONFIG_PDA_POWER is not set
+# CONFIG_BATTERY_DS2760 is not set
+# CONFIG_HWMON is not set
+# CONFIG_WATCHDOG is not set
+
+#
+# Sonics Silicon Backplane
+#
+CONFIG_SSB_POSSIBLE=y
+CONFIG_SSB=m
+CONFIG_SSB_PCIHOST_POSSIBLE=y
+CONFIG_SSB_PCIHOST=y
+# CONFIG_SSB_SILENT is not set
+# CONFIG_SSB_DEBUG is not set
+CONFIG_SSB_DRIVER_PCICORE_POSSIBLE=y
+CONFIG_SSB_DRIVER_PCICORE=y
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_SM501 is not set
+# CONFIG_HTC_ASIC3 is not set
+# CONFIG_HTC_ASIC3_DS1WM is not set
+
+#
+# Multimedia devices
+#
+CONFIG_VIDEO_DEV=m
+CONFIG_VIDEO_V4L1=y
+CONFIG_VIDEO_V4L1_COMPAT=y
+CONFIG_VIDEO_V4L2=y
+CONFIG_VIDEO_CAPTURE_DRIVERS=y
+# CONFIG_VIDEO_ADV_DEBUG is not set
+CONFIG_VIDEO_HELPER_CHIPS_AUTO=y
+# CONFIG_VIDEO_VIVI is not set
+# CONFIG_VIDEO_PMS is not set
+# CONFIG_VIDEO_BWQCAM is not set
+# CONFIG_VIDEO_CQCAM is not set
+# CONFIG_VIDEO_CPIA is not set
+# CONFIG_VIDEO_CPIA2 is not set
+# CONFIG_VIDEO_STRADIS is not set
+CONFIG_V4L_USB_DRIVERS=y
+# CONFIG_USB_VICAM is not set
+# CONFIG_USB_IBMCAM is not set
+# CONFIG_USB_KONICAWC is not set
+# CONFIG_USB_QUICKCAM_MESSENGER is not set
+# CONFIG_USB_ET61X251 is not set
+CONFIG_USB_OV511=m
+CONFIG_USB_SE401=m
+CONFIG_USB_SN9C102=m
+CONFIG_USB_STV680=m
+# CONFIG_USB_ZC0301 is not set
+# CONFIG_USB_PWC is not set
+# CONFIG_USB_ZR364XX is not set
+CONFIG_RADIO_ADAPTERS=y
+# CONFIG_RADIO_CADET is not set
+# CONFIG_RADIO_RTRACK is not set
+# CONFIG_RADIO_RTRACK2 is not set
+# CONFIG_RADIO_AZTECH is not set
+# CONFIG_RADIO_GEMTEK is not set
+# CONFIG_RADIO_GEMTEK_PCI is not set
+# CONFIG_RADIO_MAXIRADIO is not set
+# CONFIG_RADIO_MAESTRO is not set
+# CONFIG_RADIO_SF16FMI is not set
+# CONFIG_RADIO_SF16FMR2 is not set
+# CONFIG_RADIO_TERRATEC is not set
+# CONFIG_RADIO_TRUST is not set
+# CONFIG_RADIO_TYPHOON is not set
+# CONFIG_RADIO_ZOLTRIX is not set
+CONFIG_USB_DSBR=m
+# CONFIG_DVB_CORE is not set
+CONFIG_DAB=y
+# CONFIG_USB_DABUSB is not set
+
+#
+# Graphics support
+#
+CONFIG_AGP=m
+CONFIG_AGP_ALI=m
+CONFIG_AGP_ATI=m
+CONFIG_AGP_AMD=m
+CONFIG_AGP_AMD64=m
+CONFIG_AGP_INTEL=m
+CONFIG_AGP_NVIDIA=m
+CONFIG_AGP_SIS=m
+CONFIG_AGP_SWORKS=m
+CONFIG_AGP_VIA=m
+CONFIG_AGP_EFFICEON=m
+CONFIG_DRM=m
+# CONFIG_DRM_TDFX is not set
+# CONFIG_DRM_R128 is not set
+# CONFIG_DRM_RADEON is not set
+# CONFIG_DRM_I810 is not set
+# CONFIG_DRM_I830 is not set
+# CONFIG_DRM_I915 is not set
+# CONFIG_DRM_MGA is not set
+# CONFIG_DRM_SIS is not set
+# CONFIG_DRM_VIA is not set
+# CONFIG_DRM_SAVAGE is not set
+# CONFIG_VGASTATE is not set
+CONFIG_VIDEO_OUTPUT_CONTROL=m
+CONFIG_FB=y
+# CONFIG_FIRMWARE_EDID is not set
+# CONFIG_FB_DDC is not set
+CONFIG_FB_CFB_FILLRECT=m
+CONFIG_FB_CFB_COPYAREA=m
+CONFIG_FB_CFB_IMAGEBLIT=m
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+# CONFIG_FB_SYS_FILLRECT is not set
+# CONFIG_FB_SYS_COPYAREA is not set
+# CONFIG_FB_SYS_IMAGEBLIT is not set
+# CONFIG_FB_SYS_FOPS is not set
+# CONFIG_FB_DEFERRED_IO is not set
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_BACKLIGHT is not set
+CONFIG_FB_MODE_HELPERS=y
+# CONFIG_FB_TILEBLITTING is not set
+
+#
+# Frame buffer hardware drivers
+#
+# CONFIG_FB_CIRRUS is not set
+# CONFIG_FB_PM2 is not set
+# CONFIG_FB_CYBER2000 is not set
+# CONFIG_FB_ARC is not set
+# CONFIG_FB_ASILIANT is not set
+# CONFIG_FB_IMSTT is not set
+# CONFIG_FB_VGA16 is not set
+CONFIG_FB_UVESA=m
+# CONFIG_FB_VESA is not set
+# CONFIG_FB_EFI is not set
+# CONFIG_FB_HECUBA is not set
+# CONFIG_FB_HGA is not set
+# CONFIG_FB_S1D13XXX is not set
+# CONFIG_FB_NVIDIA is not set
+# CONFIG_FB_RIVA is not set
+# CONFIG_FB_I810 is not set
+# CONFIG_FB_LE80578 is not set
+# CONFIG_FB_INTEL is not set
+# CONFIG_FB_MATROX is not set
+# CONFIG_FB_RADEON is not set
+# CONFIG_FB_ATY128 is not set
+# CONFIG_FB_ATY is not set
+# CONFIG_FB_S3 is not set
+# CONFIG_FB_SAVAGE is not set
+# CONFIG_FB_SIS is not set
+# CONFIG_FB_NEOMAGIC is not set
+# CONFIG_FB_KYRO is not set
+# CONFIG_FB_3DFX is not set
+# CONFIG_FB_VOODOO1 is not set
+# CONFIG_FB_VT8623 is not set
+# CONFIG_FB_CYBLA is not set
+# CONFIG_FB_TRIDENT is not set
+# CONFIG_FB_ARK is not set
+# CONFIG_FB_PM3 is not set
+# CONFIG_FB_GEODE is not set
+# CONFIG_FB_VIRTUAL is not set
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_LCD_CLASS_DEVICE=m
+CONFIG_BACKLIGHT_CLASS_DEVICE=m
+CONFIG_BACKLIGHT_CORGI=m
+# CONFIG_BACKLIGHT_PROGEAR is not set
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+CONFIG_VGA_CONSOLE=y
+# CONFIG_VGACON_SOFT_SCROLLBACK is not set
+CONFIG_VIDEO_SELECT=y
+# CONFIG_MDA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
+CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y
+CONFIG_FONTS=y
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+# CONFIG_FONT_6x11 is not set
+# CONFIG_FONT_7x14 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+# CONFIG_FONT_ACORN_8x8 is not set
+# CONFIG_FONT_MINI_4x6 is not set
+# CONFIG_FONT_SUN8x16 is not set
+# CONFIG_FONT_SUN12x22 is not set
+# CONFIG_FONT_10x18 is not set
+CONFIG_LOGO=y
+CONFIG_LOGO_LINUX_MONO=y
+CONFIG_LOGO_LINUX_VGA16=y
+# CONFIG_LOGO_LINUX_CLUT224 is not set
+CONFIG_LOGO_OHAND_CLUT224=y
+
+#
+# Sound
+#
+CONFIG_SOUND=y
+
+#
+# Advanced Linux Sound Architecture
+#
+CONFIG_SND=y
+CONFIG_SND_TIMER=y
+CONFIG_SND_PCM=y
+CONFIG_SND_SEQUENCER=y
+# CONFIG_SND_SEQ_DUMMY is not set
+CONFIG_SND_OSSEMUL=y
+CONFIG_SND_MIXER_OSS=y
+CONFIG_SND_PCM_OSS=y
+CONFIG_SND_PCM_OSS_PLUGINS=y
+CONFIG_SND_SEQUENCER_OSS=y
+# CONFIG_SND_DYNAMIC_MINORS is not set
+CONFIG_SND_SUPPORT_OLD_API=y
+CONFIG_SND_VERBOSE_PROCFS=y
+# CONFIG_SND_VERBOSE_PRINTK is not set
+# CONFIG_SND_DEBUG is not set
+
+#
+# Generic devices
+#
+CONFIG_SND_AC97_CODEC=y
+# CONFIG_SND_DUMMY is not set
+# CONFIG_SND_VIRMIDI is not set
+# CONFIG_SND_MTPAV is not set
+# CONFIG_SND_MTS64 is not set
+# CONFIG_SND_SERIAL_U16550 is not set
+# CONFIG_SND_MPU401 is not set
+# CONFIG_SND_PORTMAN2X4 is not set
+
+#
+# ISA devices
+#
+# CONFIG_SND_ADLIB is not set
+# CONFIG_SND_AD1816A is not set
+# CONFIG_SND_AD1848 is not set
+# CONFIG_SND_ALS100 is not set
+# CONFIG_SND_AZT2320 is not set
+# CONFIG_SND_CMI8330 is not set
+# CONFIG_SND_CS4231 is not set
+# CONFIG_SND_CS4232 is not set
+# CONFIG_SND_CS4236 is not set
+# CONFIG_SND_DT019X is not set
+# CONFIG_SND_ES968 is not set
+# CONFIG_SND_ES1688 is not set
+# CONFIG_SND_ES18XX is not set
+# CONFIG_SND_SC6000 is not set
+# CONFIG_SND_GUSCLASSIC is not set
+# CONFIG_SND_GUSEXTREME is not set
+# CONFIG_SND_GUSMAX is not set
+# CONFIG_SND_INTERWAVE is not set
+# CONFIG_SND_INTERWAVE_STB is not set
+# CONFIG_SND_OPL3SA2 is not set
+# CONFIG_SND_OPTI92X_AD1848 is not set
+# CONFIG_SND_OPTI92X_CS4231 is not set
+# CONFIG_SND_OPTI93X is not set
+# CONFIG_SND_MIRO is not set
+# CONFIG_SND_SB8 is not set
+# CONFIG_SND_SB16 is not set
+# CONFIG_SND_SBAWE is not set
+# CONFIG_SND_SGALAXY is not set
+# CONFIG_SND_SSCAPE is not set
+# CONFIG_SND_WAVEFRONT is not set
+
+#
+# PCI devices
+#
+# CONFIG_SND_AD1889 is not set
+# CONFIG_SND_ALS300 is not set
+# CONFIG_SND_ALS4000 is not set
+# CONFIG_SND_ALI5451 is not set
+# CONFIG_SND_ATIIXP is not set
+# CONFIG_SND_ATIIXP_MODEM is not set
+# CONFIG_SND_AU8810 is not set
+# CONFIG_SND_AU8820 is not set
+# CONFIG_SND_AU8830 is not set
+# CONFIG_SND_AZT3328 is not set
+# CONFIG_SND_BT87X is not set
+# CONFIG_SND_CA0106 is not set
+# CONFIG_SND_CMIPCI is not set
+# CONFIG_SND_CS4281 is not set
+# CONFIG_SND_CS46XX is not set
+# CONFIG_SND_CS5530 is not set
+# CONFIG_SND_CS5535AUDIO is not set
+# CONFIG_SND_DARLA20 is not set
+# CONFIG_SND_GINA20 is not set
+# CONFIG_SND_LAYLA20 is not set
+# CONFIG_SND_DARLA24 is not set
+# CONFIG_SND_GINA24 is not set
+# CONFIG_SND_LAYLA24 is not set
+# CONFIG_SND_MONA is not set
+# CONFIG_SND_MIA is not set
+# CONFIG_SND_ECHO3G is not set
+# CONFIG_SND_INDIGO is not set
+# CONFIG_SND_INDIGOIO is not set
+# CONFIG_SND_INDIGODJ is not set
+# CONFIG_SND_EMU10K1 is not set
+# CONFIG_SND_EMU10K1X is not set
+# CONFIG_SND_ENS1370 is not set
+# CONFIG_SND_ENS1371 is not set
+# CONFIG_SND_ES1938 is not set
+# CONFIG_SND_ES1968 is not set
+# CONFIG_SND_FM801 is not set
+# CONFIG_SND_HDA_INTEL is not set
+# CONFIG_SND_HDSP is not set
+# CONFIG_SND_HDSPM is not set
+# CONFIG_SND_ICE1712 is not set
+# CONFIG_SND_ICE1724 is not set
+CONFIG_SND_INTEL8X0=y
+# CONFIG_SND_INTEL8X0M is not set
+# CONFIG_SND_KORG1212 is not set
+# CONFIG_SND_MAESTRO3 is not set
+# CONFIG_SND_MIXART is not set
+# CONFIG_SND_NM256 is not set
+# CONFIG_SND_PCXHR is not set
+# CONFIG_SND_RIPTIDE is not set
+# CONFIG_SND_RME32 is not set
+# CONFIG_SND_RME96 is not set
+# CONFIG_SND_RME9652 is not set
+# CONFIG_SND_SONICVIBES is not set
+# CONFIG_SND_TRIDENT is not set
+# CONFIG_SND_VIA82XX is not set
+# CONFIG_SND_VIA82XX_MODEM is not set
+# CONFIG_SND_VX222 is not set
+# CONFIG_SND_YMFPCI is not set
+# CONFIG_SND_AC97_POWER_SAVE is not set
+
+#
+# USB devices
+#
+# CONFIG_SND_USB_AUDIO is not set
+# CONFIG_SND_USB_USX2Y is not set
+# CONFIG_SND_USB_CAIAQ is not set
+
+#
+# System on Chip audio support
+#
+# CONFIG_SND_SOC is not set
+
+#
+# SoC Audio support for SuperH
+#
+
+#
+# Open Sound System
+#
+# CONFIG_SOUND_PRIME is not set
+CONFIG_AC97_BUS=y
+CONFIG_HID_SUPPORT=y
+CONFIG_HID=y
+# CONFIG_HID_DEBUG is not set
+# CONFIG_HIDRAW is not set
+
+#
+# USB Input Devices
+#
+CONFIG_USB_HID=y
+# CONFIG_USB_HIDINPUT_POWERBOOK is not set
+# CONFIG_HID_FF is not set
+# CONFIG_USB_HIDDEV is not set
+CONFIG_USB_SUPPORT=y
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARCH_HAS_OHCI=y
+CONFIG_USB_ARCH_HAS_EHCI=y
+CONFIG_USB=y
+# CONFIG_USB_DEBUG is not set
+
+#
+# Miscellaneous USB options
+#
+CONFIG_USB_DEVICEFS=y
+CONFIG_USB_DEVICE_CLASS=y
+# CONFIG_USB_DYNAMIC_MINORS is not set
+CONFIG_USB_SUSPEND=y
+# CONFIG_USB_PERSIST is not set
+# CONFIG_USB_OTG is not set
+
+#
+# USB Host Controller Drivers
+#
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_SPLIT_ISO=y
+# CONFIG_USB_EHCI_ROOT_HUB_TT is not set
+# CONFIG_USB_EHCI_TT_NEWSCHED is not set
+# CONFIG_USB_ISP116X_HCD is not set
+CONFIG_USB_OHCI_HCD=y
+# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set
+# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set
+CONFIG_USB_OHCI_LITTLE_ENDIAN=y
+CONFIG_USB_UHCI_HCD=y
+CONFIG_USB_SL811_HCD=m
+# CONFIG_USB_R8A66597_HCD is not set
+
+#
+# USB Device Class drivers
+#
+CONFIG_USB_ACM=m
+CONFIG_USB_PRINTER=m
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
+#
+
+#
+# may also be needed; see USB_STORAGE Help for more information
+#
+CONFIG_USB_STORAGE=m
+# CONFIG_USB_STORAGE_DEBUG is not set
+# CONFIG_USB_STORAGE_DATAFAB is not set
+# CONFIG_USB_STORAGE_FREECOM is not set
+# CONFIG_USB_STORAGE_ISD200 is not set
+# CONFIG_USB_STORAGE_DPCM is not set
+# CONFIG_USB_STORAGE_USBAT is not set
+# CONFIG_USB_STORAGE_SDDR09 is not set
+# CONFIG_USB_STORAGE_SDDR55 is not set
+# CONFIG_USB_STORAGE_JUMPSHOT is not set
+# CONFIG_USB_STORAGE_ALAUDA is not set
+# CONFIG_USB_STORAGE_KARMA is not set
+# CONFIG_USB_LIBUSUAL is not set
+
+#
+# USB Imaging devices
+#
+CONFIG_USB_MDC800=m
+CONFIG_USB_MICROTEK=m
+CONFIG_USB_MON=y
+
+#
+# USB port drivers
+#
+# CONFIG_USB_USS720 is not set
+
+#
+# USB Serial Converter support
+#
+CONFIG_USB_SERIAL=m
+CONFIG_USB_SERIAL_GENERIC=y
+# CONFIG_USB_SERIAL_AIRCABLE is not set
+# CONFIG_USB_SERIAL_AIRPRIME is not set
+# CONFIG_USB_SERIAL_ARK3116 is not set
+CONFIG_USB_SERIAL_BELKIN=m
+# CONFIG_USB_SERIAL_CH341 is not set
+# CONFIG_USB_SERIAL_WHITEHEAT is not set
+CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m
+CONFIG_USB_SERIAL_CP2101=m
+CONFIG_USB_SERIAL_CYPRESS_M8=m
+CONFIG_USB_SERIAL_EMPEG=m
+CONFIG_USB_SERIAL_FTDI_SIO=m
+# CONFIG_USB_SERIAL_FUNSOFT is not set
+CONFIG_USB_SERIAL_VISOR=m
+CONFIG_USB_SERIAL_IPAQ=m
+CONFIG_USB_SERIAL_IR=m
+CONFIG_USB_SERIAL_EDGEPORT=m
+CONFIG_USB_SERIAL_EDGEPORT_TI=m
+CONFIG_USB_SERIAL_GARMIN=m
+CONFIG_USB_SERIAL_IPW=m
+CONFIG_USB_SERIAL_KEYSPAN_PDA=m
+CONFIG_USB_SERIAL_KEYSPAN=m
+# CONFIG_USB_SERIAL_KEYSPAN_MPR is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19QW is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19QI is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA49WLC is not set
+CONFIG_USB_SERIAL_KLSI=m
+CONFIG_USB_SERIAL_KOBIL_SCT=m
+CONFIG_USB_SERIAL_MCT_U232=m
+# CONFIG_USB_SERIAL_MOS7720 is not set
+# CONFIG_USB_SERIAL_MOS7840 is not set
+# CONFIG_USB_SERIAL_NAVMAN is not set
+CONFIG_USB_SERIAL_PL2303=m
+# CONFIG_USB_SERIAL_OTI6858 is not set
+# CONFIG_USB_SERIAL_HP4X is not set
+CONFIG_USB_SERIAL_SAFE=m
+# CONFIG_USB_SERIAL_SAFE_PADDED is not set
+# CONFIG_USB_SERIAL_SIERRAWIRELESS is not set
+CONFIG_USB_SERIAL_TI=m
+CONFIG_USB_SERIAL_CYBERJACK=m
+CONFIG_USB_SERIAL_XIRCOM=m
+# CONFIG_USB_SERIAL_OPTION is not set
+CONFIG_USB_SERIAL_OMNINET=m
+# CONFIG_USB_SERIAL_DEBUG is not set
+CONFIG_USB_EZUSB=y
+
+#
+# USB Miscellaneous drivers
+#
+CONFIG_USB_EMI62=m
+CONFIG_USB_EMI26=m
+# CONFIG_USB_ADUTUX is not set
+CONFIG_USB_AUERSWALD=m
+CONFIG_USB_RIO500=m
+CONFIG_USB_LEGOTOWER=m
+CONFIG_USB_LCD=m
+# CONFIG_USB_BERRY_CHARGE is not set
+CONFIG_USB_LED=m
+# CONFIG_USB_CYPRESS_CY7C63 is not set
+CONFIG_USB_CYTHERM=m
+# CONFIG_USB_PHIDGET is not set
+CONFIG_USB_IDMOUSE=m
+# CONFIG_USB_FTDI_ELAN is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+# CONFIG_USB_SISUSBVGA is not set
+# CONFIG_USB_LD is not set
+# CONFIG_USB_TRANCEVIBRATOR is not set
+# CONFIG_USB_IOWARRIOR is not set
+# CONFIG_USB_TEST is not set
+
+#
+# USB DSL modem support
+#
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
+# CONFIG_MMC is not set
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+
+#
+# LED drivers
+#
+
+#
+# LED Triggers
+#
+# CONFIG_LEDS_TRIGGERS is not set
+# CONFIG_INFINIBAND is not set
+# CONFIG_EDAC is not set
+# CONFIG_RTC_CLASS is not set
+# CONFIG_DMADEVICES is not set
+# CONFIG_AUXDISPLAY is not set
+CONFIG_VIRTUALIZATION=y
+# CONFIG_KVM is not set
+# CONFIG_LGUEST is not set
+
+#
+# Userspace I/O
+#
+# CONFIG_UIO is not set
+
+#
+# Firmware Drivers
+#
+# CONFIG_EDD is not set
+# CONFIG_DELL_RBU is not set
+# CONFIG_DCDBAS is not set
+CONFIG_DMIID=y
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+# CONFIG_EXT2_FS_XIP is not set
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_FS_XATTR is not set
+# CONFIG_EXT4DEV_FS is not set
+CONFIG_JBD=y
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_FS_POSIX_ACL is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_GFS2_FS is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_ROMFS_FS is not set
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_QUOTA is not set
+CONFIG_DNOTIFY=y
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+CONFIG_ISO9660_FS=y
+CONFIG_JOLIET=y
+# CONFIG_ZISOFS is not set
+CONFIG_UDF_FS=y
+CONFIG_UDF_NLS=y
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+# CONFIG_MSDOS_FS is not set
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+# CONFIG_HUGETLBFS is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_CRAMFS=m
+CONFIG_SQUASHFS=m
+# CONFIG_SQUASHFS_EMBEDDED is not set
+CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3
+# CONFIG_VXFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+# CONFIG_NFS_V3_ACL is not set
+# CONFIG_NFS_V4 is not set
+# CONFIG_NFS_DIRECTIO is not set
+CONFIG_NFSD=y
+CONFIG_NFSD_V3=y
+# CONFIG_NFSD_V3_ACL is not set
+# CONFIG_NFSD_V4 is not set
+# CONFIG_NFSD_TCP is not set
+CONFIG_ROOT_NFS=y
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_EXPORTFS=y
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=y
+# CONFIG_SUNRPC_BIND34 is not set
+# CONFIG_RPCSEC_GSS_KRB5 is not set
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+CONFIG_SMB_FS=y
+# CONFIG_SMB_NLS_DEFAULT is not set
+CONFIG_CIFS=m
+# CONFIG_CIFS_STATS is not set
+# CONFIG_CIFS_WEAK_PW_HASH is not set
+# CONFIG_CIFS_XATTR is not set
+# CONFIG_CIFS_DEBUG2 is not set
+# CONFIG_CIFS_EXPERIMENTAL is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_MINIX_SUBPARTITION is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_UNIXWARE_DISKLABEL is not set
+# CONFIG_LDM_PARTITION is not set
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_ULTRIX_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_KARMA_PARTITION is not set
+# CONFIG_EFI_PARTITION is not set
+# CONFIG_SYSV68_PARTITION is not set
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="cp437"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_737=m
+CONFIG_NLS_CODEPAGE_775=m
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
+CONFIG_NLS_CODEPAGE_855=m
+CONFIG_NLS_CODEPAGE_857=m
+CONFIG_NLS_CODEPAGE_860=m
+CONFIG_NLS_CODEPAGE_861=m
+CONFIG_NLS_CODEPAGE_862=m
+CONFIG_NLS_CODEPAGE_863=m
+CONFIG_NLS_CODEPAGE_864=m
+CONFIG_NLS_CODEPAGE_865=m
+CONFIG_NLS_CODEPAGE_866=m
+CONFIG_NLS_CODEPAGE_869=m
+CONFIG_NLS_CODEPAGE_936=m
+CONFIG_NLS_CODEPAGE_950=m
+CONFIG_NLS_CODEPAGE_932=m
+CONFIG_NLS_CODEPAGE_949=m
+CONFIG_NLS_CODEPAGE_874=m
+CONFIG_NLS_ISO8859_8=m
+CONFIG_NLS_CODEPAGE_1250=m
+CONFIG_NLS_CODEPAGE_1251=m
+CONFIG_NLS_ASCII=m
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_ISO8859_2=m
+CONFIG_NLS_ISO8859_3=m
+CONFIG_NLS_ISO8859_4=m
+CONFIG_NLS_ISO8859_5=m
+CONFIG_NLS_ISO8859_6=m
+CONFIG_NLS_ISO8859_7=m
+CONFIG_NLS_ISO8859_9=m
+CONFIG_NLS_ISO8859_13=m
+CONFIG_NLS_ISO8859_14=m
+CONFIG_NLS_ISO8859_15=m
+CONFIG_NLS_KOI8_R=m
+CONFIG_NLS_KOI8_U=m
+CONFIG_NLS_UTF8=y
+# CONFIG_DLM is not set
+CONFIG_INSTRUMENTATION=y
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=m
+# CONFIG_KPROBES is not set
+# CONFIG_MARKERS is not set
+
+#
+# Kernel hacking
+#
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+# CONFIG_PRINTK_TIME is not set
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_DEBUG_FS is not set
+# CONFIG_HEADERS_CHECK is not set
+# CONFIG_DEBUG_KERNEL is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+# CONFIG_SAMPLES is not set
+CONFIG_EARLY_PRINTK=y
+CONFIG_X86_FIND_SMP_CONFIG=y
+CONFIG_X86_MPPARSE=y
+CONFIG_DOUBLEFAULT=y
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+CONFIG_CRYPTO=y
+CONFIG_CRYPTO_ALGAPI=m
+CONFIG_CRYPTO_BLKCIPHER=m
+CONFIG_CRYPTO_HASH=m
+CONFIG_CRYPTO_MANAGER=m
+CONFIG_CRYPTO_HMAC=m
+# CONFIG_CRYPTO_XCBC is not set
+CONFIG_CRYPTO_NULL=m
+CONFIG_CRYPTO_MD4=m
+CONFIG_CRYPTO_MD5=m
+CONFIG_CRYPTO_SHA1=m
+CONFIG_CRYPTO_SHA256=m
+CONFIG_CRYPTO_SHA512=m
+CONFIG_CRYPTO_WP512=m
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_GF128MUL is not set
+CONFIG_CRYPTO_ECB=m
+CONFIG_CRYPTO_CBC=m
+CONFIG_CRYPTO_PCBC=m
+# CONFIG_CRYPTO_LRW is not set
+# CONFIG_CRYPTO_XTS is not set
+# CONFIG_CRYPTO_CRYPTD is not set
+CONFIG_CRYPTO_DES=m
+# CONFIG_CRYPTO_FCRYPT is not set
+CONFIG_CRYPTO_BLOWFISH=m
+CONFIG_CRYPTO_TWOFISH=m
+CONFIG_CRYPTO_TWOFISH_COMMON=m
+# CONFIG_CRYPTO_TWOFISH_586 is not set
+CONFIG_CRYPTO_SERPENT=m
+CONFIG_CRYPTO_AES=m
+# CONFIG_CRYPTO_AES_586 is not set
+CONFIG_CRYPTO_CAST5=m
+CONFIG_CRYPTO_CAST6=m
+CONFIG_CRYPTO_TEA=m
+CONFIG_CRYPTO_ARC4=m
+CONFIG_CRYPTO_KHAZAD=m
+CONFIG_CRYPTO_ANUBIS=m
+# CONFIG_CRYPTO_SEED is not set
+CONFIG_CRYPTO_DEFLATE=m
+CONFIG_CRYPTO_LZO=m
+CONFIG_CRYPTO_MICHAEL_MIC=m
+CONFIG_CRYPTO_CRC32C=m
+CONFIG_CRYPTO_CAMELLIA=m
+CONFIG_CRYPTO_TEST=m
+# CONFIG_CRYPTO_AUTHENC is not set
+# CONFIG_CRYPTO_HW is not set
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+CONFIG_CRC_CCITT=y
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
+CONFIG_LIBCRC32C=m
+CONFIG_AUDIT_GENERIC=y
+CONFIG_ZLIB_INFLATE=m
+CONFIG_ZLIB_DEFLATE=m
+CONFIG_LZO_COMPRESS=m
+CONFIG_LZO_DECOMPRESS=m
+CONFIG_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
diff --git a/recipes/linux/linux-rp-2.6.26/defconfig-c7x0 b/recipes/linux/linux-rp-2.6.26/defconfig-c7x0
new file mode 100644
index 0000000000..8b62decdce
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.26/defconfig-c7x0
@@ -0,0 +1,1809 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.26
+# Tue Feb 3 01:27:59 2009
+#
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+CONFIG_GENERIC_GPIO=y
+CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_MMU=y
+# CONFIG_NO_IOPORT is not set
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+# CONFIG_ARCH_HAS_ILOG2_U32 is not set
+# CONFIG_ARCH_HAS_ILOG2_U64 is not set
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_ARCH_SUPPORTS_AOUT=y
+CONFIG_ZONE_DMA=y
+CONFIG_ARCH_MTD_XIP=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_LOCK_KERNEL=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+# CONFIG_POSIX_MQUEUE is not set
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_BSD_PROCESS_ACCT_V3=y
+# CONFIG_TASKSTATS is not set
+# CONFIG_AUDIT is not set
+# CONFIG_IKCONFIG is not set
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_CGROUPS is not set
+# CONFIG_GROUP_SCHED is not set
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+# CONFIG_RELAY is not set
+# CONFIG_NAMESPACES is not set
+# CONFIG_BLK_DEV_INITRD is not set
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+CONFIG_EMBEDDED=y
+CONFIG_UID16=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_SYSCTL_SYSCALL_CHECK=y
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_COMPAT_BRK=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_ANON_INODES=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLAB=y
+# CONFIG_SLUB is not set
+# CONFIG_SLOB is not set
+CONFIG_PROFILING=y
+# CONFIG_MARKERS is not set
+CONFIG_OPROFILE=m
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_KPROBES is not set
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+# CONFIG_HAVE_DMA_ATTRS is not set
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+# CONFIG_TINY_SHMEM is not set
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+# CONFIG_MODULE_FORCE_LOAD is not set
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+CONFIG_BLOCK=y
+# CONFIG_LBD is not set
+# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_LSF is not set
+# CONFIG_BLK_DEV_BSG is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=m
+CONFIG_IOSCHED_CFQ=m
+CONFIG_DEFAULT_AS=y
+# CONFIG_DEFAULT_DEADLINE is not set
+# CONFIG_DEFAULT_CFQ is not set
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="anticipatory"
+CONFIG_CLASSIC_RCU=y
+
+#
+# System Type
+#
+# CONFIG_ARCH_AAEC2000 is not set
+# CONFIG_ARCH_INTEGRATOR is not set
+# CONFIG_ARCH_REALVIEW is not set
+# CONFIG_ARCH_VERSATILE is not set
+# CONFIG_ARCH_AT91 is not set
+# CONFIG_ARCH_CLPS7500 is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_CO285 is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE is not set
+# CONFIG_ARCH_NETX is not set
+# CONFIG_ARCH_H720X is not set
+# CONFIG_ARCH_IMX is not set
+# CONFIG_ARCH_IOP13XX is not set
+# CONFIG_ARCH_IOP32X is not set
+# CONFIG_ARCH_IOP33X is not set
+# CONFIG_ARCH_IXP23XX is not set
+# CONFIG_ARCH_IXP2000 is not set
+# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_L7200 is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_NS9XXX is not set
+# CONFIG_ARCH_MXC is not set
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_PNX4008 is not set
+CONFIG_ARCH_PXA=y
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP is not set
+# CONFIG_ARCH_MSM7X00A is not set
+
+#
+# Intel PXA2xx/PXA3xx Implementations
+#
+# CONFIG_ARCH_GUMSTIX is not set
+# CONFIG_ARCH_LUBBOCK is not set
+# CONFIG_MACH_LOGICPD_PXA270 is not set
+# CONFIG_MACH_MAINSTONE is not set
+# CONFIG_ARCH_PXA_IDP is not set
+CONFIG_PXA_SHARPSL=y
+# CONFIG_ARCH_PXA_ESERIES is not set
+# CONFIG_MACH_TRIZEPS4 is not set
+# CONFIG_MACH_HX2750 is not set
+# CONFIG_MACH_EM_X270 is not set
+# CONFIG_MACH_COLIBRI is not set
+# CONFIG_MACH_ZYLONITE is not set
+# CONFIG_MACH_LITTLETON is not set
+# CONFIG_MACH_ARMCORE is not set
+# CONFIG_MACH_MAGICIAN is not set
+# CONFIG_MACH_PCM027 is not set
+# CONFIG_MACH_HTCUNIVERSAL is not set
+CONFIG_PXA_SHARPSL_25x=y
+# CONFIG_PXA_SHARPSL_27x is not set
+# CONFIG_MACH_POODLE is not set
+CONFIG_MACH_CORGI=y
+CONFIG_MACH_SHEPHERD=y
+CONFIG_MACH_HUSKY=y
+# CONFIG_MACH_TOSA is not set
+CONFIG_PXA25x=y
+CONFIG_PXA_SHARP_C7xx=y
+CONFIG_PXA_SSP=y
+# CONFIG_PXA_KEYS is not set
+
+#
+# Boot options
+#
+
+#
+# Power management
+#
+
+#
+# Processor Type
+#
+CONFIG_CPU_32=y
+CONFIG_CPU_XSCALE=y
+CONFIG_CPU_32v5=y
+CONFIG_CPU_ABRT_EV5T=y
+CONFIG_CPU_PABRT_NOIFAR=y
+CONFIG_CPU_CACHE_VIVT=y
+CONFIG_CPU_TLB_V4WBI=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+
+#
+# Processor Features
+#
+CONFIG_ARM_THUMB=y
+# CONFIG_CPU_DCACHE_DISABLE is not set
+# CONFIG_OUTER_CACHE is not set
+# CONFIG_IWMMXT is not set
+CONFIG_XSCALE_PMU=y
+CONFIG_SHARP_PARAM=y
+CONFIG_SHARPSL_PM=y
+CONFIG_SHARP_SCOOP=y
+
+#
+# Bus support
+#
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+CONFIG_PCCARD=y
+# CONFIG_PCMCIA_DEBUG is not set
+CONFIG_PCMCIA=y
+CONFIG_PCMCIA_LOAD_CIS=y
+CONFIG_PCMCIA_IOCTL=y
+
+#
+# PC-card bridges
+#
+CONFIG_PCMCIA_PXA2XX=y
+
+#
+# Kernel Features
+#
+CONFIG_TICK_ONESHOT=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_PREEMPT=y
+CONFIG_HZ=100
+CONFIG_AEABI=y
+# CONFIG_OABI_COMPAT is not set
+# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+# CONFIG_SPARSEMEM_STATIC is not set
+# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4096
+# CONFIG_RESOURCES_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=1
+CONFIG_BOUNCE=y
+CONFIG_VIRT_TO_BUS=y
+CONFIG_ALIGNMENT_TRAP=y
+
+#
+# Boot options
+#
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="console=ttyS0,115200n8 console=tty1 noinitrd root=/dev/mtdblock2 rootfstype=jffs2 dyntick=enable debug"
+# CONFIG_XIP_KERNEL is not set
+CONFIG_KEXEC=y
+CONFIG_ATAGS_PROC=y
+
+#
+# CPU Frequency scaling
+#
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_TABLE=y
+CONFIG_CPU_FREQ_DEBUG=y
+CONFIG_CPU_FREQ_STAT=y
+# CONFIG_CPU_FREQ_STAT_DETAILS is not set
+CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_CPU_FREQ_PXA=y
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+
+#
+# Userspace binary formats
+#
+CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_AOUT=m
+CONFIG_BINFMT_MISC=m
+
+#
+# Power management options
+#
+CONFIG_PM=y
+# CONFIG_PM_DEBUG is not set
+CONFIG_PM_SLEEP=y
+CONFIG_SUSPEND=y
+CONFIG_SUSPEND_FREEZER=y
+CONFIG_APM_EMULATION=y
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+
+#
+# Networking
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=m
+CONFIG_PACKET_MMAP=y
+CONFIG_UNIX=y
+CONFIG_XFRM=y
+CONFIG_XFRM_USER=m
+# CONFIG_XFRM_SUB_POLICY is not set
+# CONFIG_XFRM_MIGRATE is not set
+# CONFIG_XFRM_STATISTICS is not set
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+# CONFIG_IP_PNP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_ARPD is not set
+CONFIG_SYN_COOKIES=y
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+CONFIG_INET_TUNNEL=m
+CONFIG_INET_XFRM_MODE_TRANSPORT=m
+CONFIG_INET_XFRM_MODE_TUNNEL=m
+CONFIG_INET_XFRM_MODE_BEET=m
+# CONFIG_INET_LRO is not set
+CONFIG_INET_DIAG=m
+CONFIG_INET_TCP_DIAG=m
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+# CONFIG_IP_VS is not set
+CONFIG_IPV6=m
+# CONFIG_IPV6_PRIVACY is not set
+# CONFIG_IPV6_ROUTER_PREF is not set
+# CONFIG_IPV6_OPTIMISTIC_DAD is not set
+CONFIG_INET6_AH=m
+CONFIG_INET6_ESP=m
+CONFIG_INET6_IPCOMP=m
+# CONFIG_IPV6_MIP6 is not set
+CONFIG_INET6_XFRM_TUNNEL=m
+CONFIG_INET6_TUNNEL=m
+CONFIG_INET6_XFRM_MODE_TRANSPORT=m
+CONFIG_INET6_XFRM_MODE_TUNNEL=m
+CONFIG_INET6_XFRM_MODE_BEET=m
+# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set
+CONFIG_IPV6_SIT=m
+CONFIG_IPV6_NDISC_NODETYPE=y
+CONFIG_IPV6_TUNNEL=m
+# CONFIG_IPV6_MULTIPLE_TABLES is not set
+# CONFIG_IPV6_MROUTE is not set
+# CONFIG_NETWORK_SECMARK is not set
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+CONFIG_NETFILTER_ADVANCED=y
+
+#
+# Core Netfilter Configuration
+#
+# CONFIG_NETFILTER_NETLINK_QUEUE is not set
+# CONFIG_NETFILTER_NETLINK_LOG is not set
+# CONFIG_NF_CONNTRACK is not set
+CONFIG_NETFILTER_XTABLES=m
+# CONFIG_NETFILTER_XT_TARGET_CLASSIFY is not set
+# CONFIG_NETFILTER_XT_TARGET_DSCP is not set
+# CONFIG_NETFILTER_XT_TARGET_MARK is not set
+# CONFIG_NETFILTER_XT_TARGET_NFQUEUE is not set
+# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set
+# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set
+# CONFIG_NETFILTER_XT_TARGET_TRACE is not set
+# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set
+# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set
+# CONFIG_NETFILTER_XT_MATCH_COMMENT is not set
+# CONFIG_NETFILTER_XT_MATCH_DCCP is not set
+# CONFIG_NETFILTER_XT_MATCH_DSCP is not set
+# CONFIG_NETFILTER_XT_MATCH_ESP is not set
+# CONFIG_NETFILTER_XT_MATCH_IPRANGE is not set
+# CONFIG_NETFILTER_XT_MATCH_LENGTH is not set
+# CONFIG_NETFILTER_XT_MATCH_LIMIT is not set
+# CONFIG_NETFILTER_XT_MATCH_MAC is not set
+# CONFIG_NETFILTER_XT_MATCH_MARK is not set
+# CONFIG_NETFILTER_XT_MATCH_OWNER is not set
+# CONFIG_NETFILTER_XT_MATCH_POLICY is not set
+# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set
+# CONFIG_NETFILTER_XT_MATCH_PKTTYPE is not set
+# CONFIG_NETFILTER_XT_MATCH_QUOTA is not set
+# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set
+# CONFIG_NETFILTER_XT_MATCH_REALM is not set
+# CONFIG_NETFILTER_XT_MATCH_SCTP is not set
+# CONFIG_NETFILTER_XT_MATCH_STATISTIC is not set
+# CONFIG_NETFILTER_XT_MATCH_STRING is not set
+# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set
+# CONFIG_NETFILTER_XT_MATCH_TIME is not set
+# CONFIG_NETFILTER_XT_MATCH_U32 is not set
+# CONFIG_NETFILTER_XT_MATCH_HASHLIMIT is not set
+
+#
+# IP: Netfilter Configuration
+#
+CONFIG_IP_NF_QUEUE=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_RECENT=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_AH=m
+CONFIG_IP_NF_MATCH_TTL=m
+CONFIG_IP_NF_MATCH_ADDRTYPE=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_LOG=m
+CONFIG_IP_NF_TARGET_ULOG=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_TTL=m
+CONFIG_IP_NF_RAW=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+
+#
+# IPv6: Netfilter Configuration
+#
+# CONFIG_IP6_NF_QUEUE is not set
+# CONFIG_IP6_NF_IPTABLES is not set
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_SCTP is not set
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_SCHED is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_CAN is not set
+CONFIG_IRDA=m
+
+#
+# IrDA protocols
+#
+CONFIG_IRLAN=m
+CONFIG_IRNET=m
+CONFIG_IRCOMM=m
+# CONFIG_IRDA_ULTRA is not set
+
+#
+# IrDA options
+#
+# CONFIG_IRDA_CACHE_LAST_LSAP is not set
+# CONFIG_IRDA_FAST_RR is not set
+# CONFIG_IRDA_DEBUG is not set
+
+#
+# Infrared-port device drivers
+#
+
+#
+# SIR device drivers
+#
+# CONFIG_IRTTY_SIR is not set
+
+#
+# Dongle support
+#
+# CONFIG_KINGSUN_DONGLE is not set
+# CONFIG_KSDAZZLE_DONGLE is not set
+# CONFIG_KS959_DONGLE is not set
+
+#
+# FIR device drivers
+#
+# CONFIG_USB_IRDA is not set
+# CONFIG_SIGMATEL_FIR is not set
+CONFIG_PXA_FICP=m
+# CONFIG_MCS_FIR is not set
+CONFIG_BT=m
+CONFIG_BT_L2CAP=m
+CONFIG_BT_SCO=m
+CONFIG_BT_RFCOMM=m
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=m
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+CONFIG_BT_HIDP=m
+
+#
+# Bluetooth device drivers
+#
+CONFIG_BT_HCIUSB=m
+# CONFIG_BT_HCIUSB_SCO is not set
+# CONFIG_BT_HCIBTSDIO is not set
+CONFIG_BT_HCIUART=m
+CONFIG_BT_HCIUART_H4=y
+CONFIG_BT_HCIUART_BCSP=y
+# CONFIG_BT_HCIUART_LL is not set
+CONFIG_BT_HCIBCM203X=m
+CONFIG_BT_HCIBPA10X=m
+CONFIG_BT_HCIBFUSB=m
+CONFIG_BT_HCIDTL1=m
+CONFIG_BT_HCIBT3C=m
+CONFIG_BT_HCIBLUECARD=m
+CONFIG_BT_HCIBTUART=m
+CONFIG_BT_HCIVHCI=m
+# CONFIG_AF_RXRPC is not set
+
+#
+# Wireless
+#
+# CONFIG_CFG80211 is not set
+CONFIG_WIRELESS_EXT=y
+# CONFIG_MAC80211 is not set
+CONFIG_IEEE80211=m
+# CONFIG_IEEE80211_DEBUG is not set
+CONFIG_IEEE80211_CRYPT_WEP=m
+CONFIG_IEEE80211_CRYPT_CCMP=m
+CONFIG_IEEE80211_CRYPT_TKIP=m
+# CONFIG_RFKILL is not set
+# CONFIG_NET_9P is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_CONNECTOR is not set
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+# CONFIG_MTD_CONCAT is not set
+CONFIG_MTD_PARTITIONS=y
+# CONFIG_MTD_REDBOOT_PARTS is not set
+CONFIG_MTD_CMDLINE_PARTS=y
+# CONFIG_MTD_AFS_PARTS is not set
+# CONFIG_MTD_AR7_PARTS is not set
+
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+# CONFIG_MTD_OOPS is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+# CONFIG_MTD_CFI is not set
+# CONFIG_MTD_JEDECPROBE is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+# CONFIG_MTD_RAM is not set
+CONFIG_MTD_ROM=y
+# CONFIG_MTD_ABSENT is not set
+
+#
+# Mapping drivers for chip access
+#
+CONFIG_MTD_COMPLEX_MAPPINGS=y
+# CONFIG_MTD_PHYSMAP is not set
+CONFIG_MTD_SHARP_SL=y
+# CONFIG_MTD_PLATRAM is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLOCK2MTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_VERIFY_WRITE=y
+# CONFIG_MTD_NAND_ECC_SMC is not set
+# CONFIG_MTD_NAND_MUSEUM_IDS is not set
+# CONFIG_MTD_NAND_H1900 is not set
+CONFIG_MTD_NAND_IDS=y
+# CONFIG_MTD_NAND_DISKONCHIP is not set
+CONFIG_MTD_NAND_SHARPSL=y
+# CONFIG_MTD_NAND_NANDSIM is not set
+# CONFIG_MTD_NAND_PLATFORM is not set
+# CONFIG_MTD_ALAUDA is not set
+# CONFIG_MTD_ONENAND is not set
+
+#
+# UBI - Unsorted block images
+#
+# CONFIG_MTD_UBI is not set
+# CONFIG_PARPORT is not set
+CONFIG_BLK_DEV=y
+# CONFIG_BLK_DEV_COW_COMMON is not set
+CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_CRYPTOLOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_UB is not set
+# CONFIG_BLK_DEV_RAM is not set
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+CONFIG_MISC_DEVICES=y
+# CONFIG_EEPROM_93CX6 is not set
+# CONFIG_ENCLOSURE_SERVICES is not set
+CONFIG_HAVE_IDE=y
+CONFIG_IDE=y
+CONFIG_IDE_MAX_HWIFS=4
+CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_IDE_SATA is not set
+CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_IDEDISK_MULTI_MODE is not set
+CONFIG_BLK_DEV_IDECS=y
+# CONFIG_BLK_DEV_IDECD is not set
+# CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
+# CONFIG_BLK_DEV_IDESCSI is not set
+# CONFIG_IDE_TASK_IOCTL is not set
+CONFIG_IDE_PROC_FS=y
+
+#
+# IDE chipset support/bugfixes
+#
+# CONFIG_BLK_DEV_PLATFORM is not set
+# CONFIG_BLK_DEV_IDEDMA is not set
+# CONFIG_BLK_DEV_HD is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+CONFIG_SCSI=m
+CONFIG_SCSI_DMA=y
+# CONFIG_SCSI_TGT is not set
+# CONFIG_SCSI_NETLINK is not set
+CONFIG_SCSI_PROC_FS=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=m
+CONFIG_CHR_DEV_ST=m
+CONFIG_CHR_DEV_OSST=m
+CONFIG_BLK_DEV_SR=m
+# CONFIG_BLK_DEV_SR_VENDOR is not set
+CONFIG_CHR_DEV_SG=m
+# CONFIG_CHR_DEV_SCH is not set
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+CONFIG_SCSI_MULTI_LUN=y
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
+# CONFIG_SCSI_SCAN_ASYNC is not set
+CONFIG_SCSI_WAIT_SCAN=m
+
+#
+# SCSI Transports
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+# CONFIG_SCSI_ISCSI_ATTRS is not set
+# CONFIG_SCSI_SAS_LIBSAS is not set
+# CONFIG_SCSI_SRP_ATTRS is not set
+CONFIG_SCSI_LOWLEVEL=y
+# CONFIG_ISCSI_TCP is not set
+# CONFIG_SCSI_DEBUG is not set
+# CONFIG_SCSI_LOWLEVEL_PCMCIA is not set
+# CONFIG_ATA is not set
+CONFIG_MD=y
+# CONFIG_BLK_DEV_MD is not set
+CONFIG_BLK_DEV_DM=m
+# CONFIG_DM_DEBUG is not set
+CONFIG_DM_CRYPT=m
+CONFIG_DM_SNAPSHOT=m
+CONFIG_DM_MIRROR=m
+CONFIG_DM_ZERO=m
+CONFIG_DM_MULTIPATH=m
+CONFIG_DM_MULTIPATH_EMC=m
+# CONFIG_DM_MULTIPATH_RDAC is not set
+# CONFIG_DM_MULTIPATH_HP is not set
+# CONFIG_DM_DELAY is not set
+# CONFIG_DM_UEVENT is not set
+CONFIG_NETDEVICES=y
+# CONFIG_NETDEVICES_MULTIQUEUE is not set
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_MACVLAN is not set
+# CONFIG_EQUALIZER is not set
+CONFIG_TUN=m
+# CONFIG_VETH is not set
+# CONFIG_PHYLIB is not set
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=m
+# CONFIG_AX88796 is not set
+# CONFIG_SMC91X is not set
+# CONFIG_DM9000 is not set
+# CONFIG_SMC911X is not set
+# CONFIG_IBM_NEW_EMAC_ZMII is not set
+# CONFIG_IBM_NEW_EMAC_RGMII is not set
+# CONFIG_IBM_NEW_EMAC_TAH is not set
+# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
+# CONFIG_B44 is not set
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
+
+#
+# Wireless LAN
+#
+# CONFIG_WLAN_PRE80211 is not set
+CONFIG_WLAN_80211=y
+# CONFIG_PCMCIA_RAYCS is not set
+CONFIG_LIBERTAS=m
+# CONFIG_LIBERTAS_USB is not set
+CONFIG_LIBERTAS_CS=m
+# CONFIG_LIBERTAS_SDIO is not set
+# CONFIG_LIBERTAS_DEBUG is not set
+CONFIG_HERMES=m
+CONFIG_PCMCIA_HERMES=m
+CONFIG_PCMCIA_SPECTRUM=m
+# CONFIG_ATMEL is not set
+CONFIG_AIRO_CS=m
+# CONFIG_PCMCIA_WL3501 is not set
+# CONFIG_USB_ZD1201 is not set
+# CONFIG_USB_NET_RNDIS_WLAN is not set
+# CONFIG_IWLWIFI_LEDS is not set
+CONFIG_HOSTAP=m
+CONFIG_HOSTAP_FIRMWARE=y
+# CONFIG_HOSTAP_FIRMWARE_NVRAM is not set
+CONFIG_HOSTAP_CS=m
+
+#
+# USB Network Adapters
+#
+CONFIG_USB_CATC=m
+CONFIG_USB_KAWETH=m
+CONFIG_USB_PEGASUS=m
+CONFIG_USB_RTL8150=m
+CONFIG_USB_USBNET=m
+CONFIG_USB_NET_AX8817X=m
+CONFIG_USB_NET_CDCETHER=m
+CONFIG_USB_NET_DM9601=m
+# CONFIG_USB_NET_GL620A is not set
+CONFIG_USB_NET_NET1080=m
+# CONFIG_USB_NET_PLUSB is not set
+# CONFIG_USB_NET_MCS7830 is not set
+# CONFIG_USB_NET_RNDIS_HOST is not set
+# CONFIG_USB_NET_CDC_SUBSET is not set
+CONFIG_USB_NET_ZAURUS=m
+CONFIG_NET_PCMCIA=y
+# CONFIG_PCMCIA_3C589 is not set
+# CONFIG_PCMCIA_3C574 is not set
+# CONFIG_PCMCIA_FMVJ18X is not set
+CONFIG_PCMCIA_PCNET=m
+# CONFIG_PCMCIA_NMCLAN is not set
+# CONFIG_PCMCIA_SMC91C92 is not set
+# CONFIG_PCMCIA_XIRC2PS is not set
+# CONFIG_PCMCIA_AXNET is not set
+# CONFIG_WAN is not set
+CONFIG_PPP=m
+# CONFIG_PPP_MULTILINK is not set
+# CONFIG_PPP_FILTER is not set
+CONFIG_PPP_ASYNC=m
+# CONFIG_PPP_SYNC_TTY is not set
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_BSDCOMP=m
+# CONFIG_PPP_MPPE is not set
+# CONFIG_PPPOE is not set
+# CONFIG_PPPOL2TP is not set
+# CONFIG_SLIP is not set
+CONFIG_SLHC=m
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_ISDN is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=m
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=640
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=480
+# CONFIG_INPUT_JOYDEV is not set
+CONFIG_INPUT_EVDEV=y
+# CONFIG_INPUT_EVBUG is not set
+CONFIG_INPUT_APMPOWER=y
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+# CONFIG_KEYBOARD_ATKBD is not set
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+# CONFIG_KEYBOARD_XTKBD is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+CONFIG_KEYBOARD_CORGI=y
+# CONFIG_KEYBOARD_SPITZ is not set
+CONFIG_SHARPSL_RC=y
+# CONFIG_KEYBOARD_GPIO is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_CORGI=y
+# CONFIG_TOUCHSCREEN_FUJITSU is not set
+# CONFIG_TOUCHSCREEN_GUNZE is not set
+# CONFIG_TOUCHSCREEN_ELO is not set
+# CONFIG_TOUCHSCREEN_MTOUCH is not set
+# CONFIG_TOUCHSCREEN_MK712 is not set
+# CONFIG_TOUCHSCREEN_PENMOUNT is not set
+# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set
+# CONFIG_TOUCHSCREEN_TOUCHWIN is not set
+# CONFIG_TOUCHSCREEN_UCB1400 is not set
+# CONFIG_TOUCHSCREEN_WM97XX is not set
+# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set
+CONFIG_INPUT_MISC=y
+# CONFIG_INPUT_ATI_REMOTE is not set
+# CONFIG_INPUT_ATI_REMOTE2 is not set
+# CONFIG_INPUT_KEYSPAN_REMOTE is not set
+# CONFIG_INPUT_POWERMATE is not set
+# CONFIG_INPUT_YEALINK is not set
+CONFIG_INPUT_UINPUT=m
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+# CONFIG_VT_HW_CONSOLE_BINDING is not set
+CONFIG_DEVKMEM=y
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+CONFIG_SERIAL_8250=m
+CONFIG_SERIAL_8250_CS=m
+CONFIG_SERIAL_8250_NR_UARTS=4
+CONFIG_SERIAL_8250_RUNTIME_UARTS=4
+# CONFIG_SERIAL_8250_EXTENDED is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_PXA=y
+CONFIG_SERIAL_PXA_CONSOLE=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_IPMI_HANDLER is not set
+CONFIG_HW_RANDOM=m
+# CONFIG_NVRAM is not set
+# CONFIG_R3964 is not set
+
+#
+# PCMCIA character devices
+#
+# CONFIG_SYNCLINK_CS is not set
+# CONFIG_CARDMAN_4000 is not set
+# CONFIG_CARDMAN_4040 is not set
+# CONFIG_IPWIRELESS is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+# CONFIG_I2C_CHARDEV is not set
+
+#
+# I2C Hardware Bus support
+#
+# CONFIG_I2C_GPIO is not set
+CONFIG_I2C_PXA=y
+# CONFIG_I2C_PXA_SLAVE is not set
+# CONFIG_I2C_OCORES is not set
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_SIMTEC is not set
+# CONFIG_I2C_TAOS_EVM is not set
+# CONFIG_I2C_STUB is not set
+# CONFIG_I2C_TINY_USB is not set
+# CONFIG_I2C_PCA_PLATFORM is not set
+
+#
+# Miscellaneous I2C Chip support
+#
+# CONFIG_DS1682 is not set
+# CONFIG_SENSORS_EEPROM is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_PCF8575 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_TPS65010 is not set
+# CONFIG_SENSORS_MAX6875 is not set
+# CONFIG_SENSORS_TSL2550 is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_I2C_DEBUG_CHIP is not set
+# CONFIG_SPI is not set
+CONFIG_HAVE_GPIO_LIB=y
+
+#
+# GPIO Support
+#
+
+#
+# I2C GPIO expanders:
+#
+# CONFIG_GPIO_PCA953X is not set
+# CONFIG_GPIO_PCF857X is not set
+
+#
+# SPI GPIO expanders:
+#
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+# CONFIG_WATCHDOG is not set
+
+#
+# Sonics Silicon Backplane
+#
+CONFIG_SSB_POSSIBLE=y
+# CONFIG_SSB is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_SM501 is not set
+# CONFIG_MFD_ASIC3 is not set
+# CONFIG_HTC_EGPIO is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_HTC_ASIC3 is not set
+# CONFIG_HTC_ASIC3_DS1WM is not set
+
+#
+# Multimedia devices
+#
+
+#
+# Multimedia core support
+#
+CONFIG_VIDEO_DEV=m
+CONFIG_VIDEO_V4L2_COMMON=m
+CONFIG_VIDEO_ALLOW_V4L1=y
+CONFIG_VIDEO_V4L1_COMPAT=y
+# CONFIG_DVB_CORE is not set
+CONFIG_VIDEO_MEDIA=m
+
+#
+# Multimedia drivers
+#
+# CONFIG_MEDIA_ATTACH is not set
+CONFIG_MEDIA_TUNER=m
+# CONFIG_MEDIA_TUNER_CUSTOMIZE is not set
+CONFIG_MEDIA_TUNER_SIMPLE=m
+CONFIG_MEDIA_TUNER_TDA8290=m
+CONFIG_MEDIA_TUNER_TDA9887=m
+CONFIG_MEDIA_TUNER_TEA5761=m
+CONFIG_MEDIA_TUNER_TEA5767=m
+CONFIG_MEDIA_TUNER_MT20XX=m
+CONFIG_MEDIA_TUNER_XC2028=m
+CONFIG_MEDIA_TUNER_XC5000=m
+CONFIG_VIDEO_V4L2=m
+CONFIG_VIDEO_V4L1=m
+CONFIG_VIDEO_CAPTURE_DRIVERS=y
+# CONFIG_VIDEO_ADV_DEBUG is not set
+CONFIG_VIDEO_HELPER_CHIPS_AUTO=y
+# CONFIG_VIDEO_VIVI is not set
+# CONFIG_VIDEO_CPIA is not set
+# CONFIG_VIDEO_CPIA2 is not set
+# CONFIG_VIDEO_SAA5246A is not set
+# CONFIG_VIDEO_SAA5249 is not set
+# CONFIG_TUNER_3036 is not set
+CONFIG_V4L_USB_DRIVERS=y
+# CONFIG_USB_VIDEO_CLASS is not set
+# CONFIG_VIDEO_PVRUSB2 is not set
+# CONFIG_VIDEO_EM28XX is not set
+# CONFIG_VIDEO_USBVISION is not set
+CONFIG_VIDEO_USBVIDEO=m
+CONFIG_USB_VICAM=m
+CONFIG_USB_IBMCAM=m
+CONFIG_USB_KONICAWC=m
+# CONFIG_USB_QUICKCAM_MESSENGER is not set
+# CONFIG_USB_ET61X251 is not set
+# CONFIG_VIDEO_OVCAMCHIP is not set
+# CONFIG_USB_W9968CF is not set
+CONFIG_USB_OV511=m
+CONFIG_USB_SE401=m
+CONFIG_USB_SN9C102=m
+CONFIG_USB_STV680=m
+# CONFIG_USB_ZC0301 is not set
+# CONFIG_USB_PWC is not set
+# CONFIG_USB_ZR364XX is not set
+# CONFIG_USB_STKWEBCAM is not set
+# CONFIG_SOC_CAMERA is not set
+CONFIG_RADIO_ADAPTERS=y
+CONFIG_USB_DSBR=m
+# CONFIG_USB_SI470X is not set
+CONFIG_DAB=y
+CONFIG_USB_DABUSB=m
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+CONFIG_VIDEO_OUTPUT_CONTROL=m
+CONFIG_FB=y
+# CONFIG_FIRMWARE_EDID is not set
+# CONFIG_FB_DDC is not set
+CONFIG_FB_CFB_FILLRECT=y
+CONFIG_FB_CFB_COPYAREA=y
+CONFIG_FB_CFB_IMAGEBLIT=y
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+# CONFIG_FB_SYS_FILLRECT is not set
+# CONFIG_FB_SYS_COPYAREA is not set
+# CONFIG_FB_SYS_IMAGEBLIT is not set
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+# CONFIG_FB_SYS_FOPS is not set
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_BACKLIGHT is not set
+# CONFIG_FB_MODE_HELPERS is not set
+# CONFIG_FB_TILEBLITTING is not set
+
+#
+# Frame buffer hardware drivers
+#
+# CONFIG_FB_S1D13XXX is not set
+# CONFIG_FB_PXA is not set
+# CONFIG_FB_MBX is not set
+CONFIG_FB_W100=y
+# CONFIG_FB_AM200EPD is not set
+# CONFIG_FB_VIRTUAL is not set
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+# CONFIG_LCD_CLASS_DEVICE is not set
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+CONFIG_BACKLIGHT_CORGI=y
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
+# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
+CONFIG_FONTS=y
+# CONFIG_FONT_8x8 is not set
+CONFIG_FONT_8x16=y
+# CONFIG_FONT_6x11 is not set
+# CONFIG_FONT_7x14 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+# CONFIG_FONT_ACORN_8x8 is not set
+# CONFIG_FONT_MINI_4x6 is not set
+# CONFIG_FONT_SUN8x16 is not set
+# CONFIG_FONT_SUN12x22 is not set
+# CONFIG_FONT_10x18 is not set
+# CONFIG_LOGO is not set
+
+#
+# Sound
+#
+CONFIG_SOUND=m
+
+#
+# Advanced Linux Sound Architecture
+#
+CONFIG_SND=m
+CONFIG_SND_TIMER=m
+CONFIG_SND_PCM=m
+CONFIG_SND_HWDEP=m
+CONFIG_SND_RAWMIDI=m
+CONFIG_SND_SEQUENCER=m
+# CONFIG_SND_SEQ_DUMMY is not set
+CONFIG_SND_OSSEMUL=y
+CONFIG_SND_MIXER_OSS=m
+CONFIG_SND_PCM_OSS=m
+CONFIG_SND_PCM_OSS_PLUGINS=y
+# CONFIG_SND_SEQUENCER_OSS is not set
+# CONFIG_SND_DYNAMIC_MINORS is not set
+CONFIG_SND_SUPPORT_OLD_API=y
+CONFIG_SND_VERBOSE_PROCFS=y
+# CONFIG_SND_VERBOSE_PRINTK is not set
+# CONFIG_SND_DEBUG is not set
+
+#
+# Generic devices
+#
+CONFIG_SND_AC97_CODEC=m
+# CONFIG_SND_DUMMY is not set
+# CONFIG_SND_VIRMIDI is not set
+# CONFIG_SND_MTPAV is not set
+# CONFIG_SND_SERIAL_U16550 is not set
+# CONFIG_SND_MPU401 is not set
+
+#
+# ALSA ARM devices
+#
+CONFIG_SND_PXA2XX_PCM=m
+CONFIG_SND_PXA2XX_AC97=m
+
+#
+# USB devices
+#
+CONFIG_SND_USB_AUDIO=m
+# CONFIG_SND_USB_CAIAQ is not set
+
+#
+# PCMCIA devices
+#
+# CONFIG_SND_VXPOCKET is not set
+# CONFIG_SND_PDAUDIOCF is not set
+
+#
+# System on Chip audio support
+#
+CONFIG_SND_SOC=m
+CONFIG_SND_PXA2XX_SOC=m
+CONFIG_SND_PXA2XX_SOC_I2S=m
+CONFIG_SND_PXA2XX_SOC_CORGI=m
+
+#
+# ALSA SoC audio for Freescale SOCs
+#
+
+#
+# SoC Audio for the Texas Instruments OMAP
+#
+CONFIG_SND_SOC_WM8731=m
+
+#
+# Open Sound System
+#
+# CONFIG_SOUND_PRIME is not set
+CONFIG_AC97_BUS=m
+CONFIG_HID_SUPPORT=y
+CONFIG_HID=m
+# CONFIG_HID_DEBUG is not set
+# CONFIG_HIDRAW is not set
+
+#
+# USB Input Devices
+#
+CONFIG_USB_HID=m
+# CONFIG_USB_HIDINPUT_POWERBOOK is not set
+# CONFIG_HID_FF is not set
+# CONFIG_USB_HIDDEV is not set
+
+#
+# USB HID Boot Protocol drivers
+#
+CONFIG_USB_KBD=m
+CONFIG_USB_MOUSE=m
+CONFIG_USB_SUPPORT=y
+CONFIG_USB_ARCH_HAS_HCD=y
+# CONFIG_USB_ARCH_HAS_OHCI is not set
+# CONFIG_USB_ARCH_HAS_EHCI is not set
+CONFIG_USB=m
+# CONFIG_USB_DEBUG is not set
+# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set
+
+#
+# Miscellaneous USB options
+#
+CONFIG_USB_DEVICEFS=y
+CONFIG_USB_DEVICE_CLASS=y
+# CONFIG_USB_DYNAMIC_MINORS is not set
+# CONFIG_USB_SUSPEND is not set
+# CONFIG_USB_OTG is not set
+# CONFIG_USB_OTG_WHITELIST is not set
+# CONFIG_USB_OTG_BLACKLIST_HUB is not set
+
+#
+# USB Host Controller Drivers
+#
+# CONFIG_USB_C67X00_HCD is not set
+# CONFIG_USB_ISP116X_HCD is not set
+# CONFIG_USB_ISP1760_HCD is not set
+CONFIG_USB_SL811_HCD=m
+CONFIG_USB_SL811_CS=m
+# CONFIG_USB_R8A66597_HCD is not set
+
+#
+# USB Device Class drivers
+#
+CONFIG_USB_ACM=m
+CONFIG_USB_PRINTER=m
+# CONFIG_USB_WDM is not set
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
+#
+
+#
+# may also be needed; see USB_STORAGE Help for more information
+#
+CONFIG_USB_STORAGE=m
+# CONFIG_USB_STORAGE_DEBUG is not set
+# CONFIG_USB_STORAGE_DATAFAB is not set
+# CONFIG_USB_STORAGE_FREECOM is not set
+# CONFIG_USB_STORAGE_ISD200 is not set
+# CONFIG_USB_STORAGE_DPCM is not set
+# CONFIG_USB_STORAGE_USBAT is not set
+# CONFIG_USB_STORAGE_SDDR09 is not set
+# CONFIG_USB_STORAGE_SDDR55 is not set
+# CONFIG_USB_STORAGE_JUMPSHOT is not set
+# CONFIG_USB_STORAGE_ALAUDA is not set
+# CONFIG_USB_STORAGE_ONETOUCH is not set
+# CONFIG_USB_STORAGE_KARMA is not set
+# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set
+# CONFIG_USB_LIBUSUAL is not set
+
+#
+# USB Imaging devices
+#
+CONFIG_USB_MDC800=m
+CONFIG_USB_MICROTEK=m
+CONFIG_USB_MON=y
+
+#
+# USB port drivers
+#
+CONFIG_USB_SERIAL=m
+CONFIG_USB_EZUSB=y
+CONFIG_USB_SERIAL_GENERIC=y
+# CONFIG_USB_SERIAL_AIRCABLE is not set
+# CONFIG_USB_SERIAL_AIRPRIME is not set
+# CONFIG_USB_SERIAL_ARK3116 is not set
+CONFIG_USB_SERIAL_BELKIN=m
+# CONFIG_USB_SERIAL_CH341 is not set
+# CONFIG_USB_SERIAL_WHITEHEAT is not set
+CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m
+CONFIG_USB_SERIAL_CP2101=m
+CONFIG_USB_SERIAL_CYPRESS_M8=m
+CONFIG_USB_SERIAL_EMPEG=m
+CONFIG_USB_SERIAL_FTDI_SIO=m
+# CONFIG_USB_SERIAL_FUNSOFT is not set
+CONFIG_USB_SERIAL_VISOR=m
+CONFIG_USB_SERIAL_IPAQ=m
+CONFIG_USB_SERIAL_IR=m
+CONFIG_USB_SERIAL_EDGEPORT=m
+CONFIG_USB_SERIAL_EDGEPORT_TI=m
+CONFIG_USB_SERIAL_GARMIN=m
+CONFIG_USB_SERIAL_IPW=m
+# CONFIG_USB_SERIAL_IUU is not set
+CONFIG_USB_SERIAL_KEYSPAN_PDA=m
+CONFIG_USB_SERIAL_KEYSPAN=m
+# CONFIG_USB_SERIAL_KEYSPAN_MPR is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19QW is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19QI is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA49WLC is not set
+CONFIG_USB_SERIAL_KLSI=m
+CONFIG_USB_SERIAL_KOBIL_SCT=m
+CONFIG_USB_SERIAL_MCT_U232=m
+# CONFIG_USB_SERIAL_MOS7720 is not set
+# CONFIG_USB_SERIAL_MOS7840 is not set
+# CONFIG_USB_SERIAL_MOTOROLA is not set
+# CONFIG_USB_SERIAL_NAVMAN is not set
+CONFIG_USB_SERIAL_PL2303=m
+# CONFIG_USB_SERIAL_OTI6858 is not set
+# CONFIG_USB_SERIAL_SPCP8X5 is not set
+# CONFIG_USB_SERIAL_HP4X is not set
+CONFIG_USB_SERIAL_SAFE=m
+# CONFIG_USB_SERIAL_SAFE_PADDED is not set
+# CONFIG_USB_SERIAL_SIERRAWIRELESS is not set
+CONFIG_USB_SERIAL_TI=m
+CONFIG_USB_SERIAL_CYBERJACK=m
+CONFIG_USB_SERIAL_XIRCOM=m
+# CONFIG_USB_SERIAL_OPTION is not set
+CONFIG_USB_SERIAL_OMNINET=m
+# CONFIG_USB_SERIAL_DEBUG is not set
+
+#
+# USB Miscellaneous drivers
+#
+CONFIG_USB_EMI62=m
+CONFIG_USB_EMI26=m
+# CONFIG_USB_ADUTUX is not set
+CONFIG_USB_AUERSWALD=m
+CONFIG_USB_RIO500=m
+CONFIG_USB_LEGOTOWER=m
+CONFIG_USB_LCD=m
+# CONFIG_USB_BERRY_CHARGE is not set
+CONFIG_USB_LED=m
+# CONFIG_USB_CYPRESS_CY7C63 is not set
+CONFIG_USB_CYTHERM=m
+# CONFIG_USB_PHIDGET is not set
+CONFIG_USB_IDMOUSE=m
+# CONFIG_USB_FTDI_ELAN is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+# CONFIG_USB_LD is not set
+# CONFIG_USB_TRANCEVIBRATOR is not set
+# CONFIG_USB_IOWARRIOR is not set
+# CONFIG_USB_TEST is not set
+# CONFIG_USB_ISIGHTFW is not set
+CONFIG_USB_GADGET=y
+# CONFIG_USB_GADGET_DEBUG_FILES is not set
+CONFIG_USB_GADGET_SELECTED=y
+# CONFIG_USB_GADGET_AMD5536UDC is not set
+# CONFIG_USB_GADGET_ATMEL_USBA is not set
+# CONFIG_USB_GADGET_FSL_USB2 is not set
+# CONFIG_USB_GADGET_NET2280 is not set
+CONFIG_USB_GADGET_PXA2XX=y
+CONFIG_USB_PXA2XX=y
+# CONFIG_USB_PXA2XX_SMALL is not set
+# CONFIG_USB_GADGET_M66592 is not set
+# CONFIG_USB_GADGET_PXA27X is not set
+# CONFIG_USB_GADGET_GOKU is not set
+# CONFIG_USB_GADGET_LH7A40X is not set
+# CONFIG_USB_GADGET_OMAP is not set
+# CONFIG_USB_GADGET_S3C2410 is not set
+# CONFIG_USB_GADGET_AT91 is not set
+# CONFIG_USB_GADGET_DUMMY_HCD is not set
+# CONFIG_USB_GADGET_DUALSPEED is not set
+CONFIG_USB_ZERO=m
+CONFIG_USB_ETH=m
+CONFIG_USB_ETH_RNDIS=y
+CONFIG_USB_GADGETFS=m
+CONFIG_USB_FILE_STORAGE=m
+# CONFIG_USB_FILE_STORAGE_TEST is not set
+CONFIG_USB_G_SERIAL=m
+# CONFIG_USB_MIDI_GADGET is not set
+# CONFIG_USB_G_PRINTER is not set
+CONFIG_MMC=y
+# CONFIG_MMC_DEBUG is not set
+CONFIG_MMC_UNSAFE_RESUME=y
+
+#
+# MMC/SD Card Drivers
+#
+CONFIG_MMC_BLOCK=y
+CONFIG_MMC_BLOCK_BOUNCE=y
+# CONFIG_SDIO_UART is not set
+# CONFIG_MMC_TEST is not set
+
+#
+# MMC/SD Host Controller Drivers
+#
+CONFIG_MMC_PXA=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+
+#
+# LED drivers
+#
+CONFIG_LEDS_CORGI=y
+# CONFIG_LEDS_GPIO is not set
+
+#
+# LED Triggers
+#
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=y
+CONFIG_LEDS_TRIGGER_IDE_DISK=y
+# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set
+# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set
+CONFIG_RTC_LIB=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_HCTOSYS=y
+CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
+# CONFIG_RTC_DEBUG is not set
+
+#
+# RTC interfaces
+#
+CONFIG_RTC_INTF_SYSFS=y
+CONFIG_RTC_INTF_PROC=y
+CONFIG_RTC_INTF_DEV=y
+# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
+# CONFIG_RTC_DRV_TEST is not set
+
+#
+# I2C RTC drivers
+#
+# CONFIG_RTC_DRV_DS1307 is not set
+# CONFIG_RTC_DRV_DS1374 is not set
+# CONFIG_RTC_DRV_DS1672 is not set
+# CONFIG_RTC_DRV_MAX6900 is not set
+# CONFIG_RTC_DRV_RS5C372 is not set
+# CONFIG_RTC_DRV_ISL1208 is not set
+# CONFIG_RTC_DRV_X1205 is not set
+# CONFIG_RTC_DRV_PCF8563 is not set
+# CONFIG_RTC_DRV_PCF8583 is not set
+# CONFIG_RTC_DRV_M41T80 is not set
+# CONFIG_RTC_DRV_S35390A is not set
+# CONFIG_RTC_DRV_FM3130 is not set
+
+#
+# SPI RTC drivers
+#
+
+#
+# Platform RTC drivers
+#
+# CONFIG_RTC_DRV_CMOS is not set
+# CONFIG_RTC_DRV_DS1511 is not set
+# CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_DS1742 is not set
+# CONFIG_RTC_DRV_STK17TA8 is not set
+# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_M48T59 is not set
+# CONFIG_RTC_DRV_V3020 is not set
+
+#
+# on-CPU RTC drivers
+#
+CONFIG_RTC_DRV_SA1100=y
+# CONFIG_UIO is not set
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+# CONFIG_EXT2_FS_XIP is not set
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_FS_XATTR is not set
+# CONFIG_EXT4DEV_FS is not set
+CONFIG_JBD=y
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+CONFIG_FS_POSIX_ACL=y
+# CONFIG_XFS_FS is not set
+# CONFIG_OCFS2_FS is not set
+CONFIG_DNOTIFY=y
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+# CONFIG_MSDOS_FS is not set
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_DEBUG=0
+CONFIG_JFFS2_FS_WRITEBUFFER=y
+# CONFIG_JFFS2_FS_WBUF_VERIFY is not set
+CONFIG_JFFS2_SUMMARY=y
+# CONFIG_JFFS2_FS_XATTR is not set
+# CONFIG_JFFS2_SYSFS is not set
+CONFIG_JFFS2_COMPRESSION_OPTIONS=y
+CONFIG_JFFS2_ZLIB=y
+CONFIG_JFFS2_LZO=y
+CONFIG_JFFS2_RTIME=y
+CONFIG_JFFS2_RUBIN=y
+# CONFIG_JFFS2_CMODE_NONE is not set
+CONFIG_JFFS2_CMODE_PRIORITY=y
+# CONFIG_JFFS2_CMODE_SIZE is not set
+# CONFIG_JFFS2_CMODE_FAVOURLZO is not set
+CONFIG_CRAMFS=m
+# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+CONFIG_NFS_FS=m
+CONFIG_NFS_V3=y
+# CONFIG_NFS_V3_ACL is not set
+CONFIG_NFS_V4=y
+CONFIG_NFSD=m
+CONFIG_NFSD_V3=y
+# CONFIG_NFSD_V3_ACL is not set
+CONFIG_NFSD_V4=y
+CONFIG_LOCKD=m
+CONFIG_LOCKD_V4=y
+CONFIG_EXPORTFS=m
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=m
+CONFIG_SUNRPC_GSS=m
+# CONFIG_SUNRPC_BIND34 is not set
+CONFIG_RPCSEC_GSS_KRB5=m
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+CONFIG_SMB_FS=m
+CONFIG_SMB_NLS_DEFAULT=y
+CONFIG_SMB_NLS_REMOTE="cp437"
+CONFIG_CIFS=m
+# CONFIG_CIFS_STATS is not set
+# CONFIG_CIFS_WEAK_PW_HASH is not set
+# CONFIG_CIFS_XATTR is not set
+# CONFIG_CIFS_DEBUG2 is not set
+# CONFIG_CIFS_EXPERIMENTAL is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_MINIX_SUBPARTITION is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_UNIXWARE_DISKLABEL is not set
+# CONFIG_LDM_PARTITION is not set
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_ULTRIX_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_KARMA_PARTITION is not set
+# CONFIG_EFI_PARTITION is not set
+# CONFIG_SYSV68_PARTITION is not set
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="cp437"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_737=m
+CONFIG_NLS_CODEPAGE_775=m
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
+CONFIG_NLS_CODEPAGE_855=m
+CONFIG_NLS_CODEPAGE_857=m
+CONFIG_NLS_CODEPAGE_860=m
+CONFIG_NLS_CODEPAGE_861=m
+CONFIG_NLS_CODEPAGE_862=m
+CONFIG_NLS_CODEPAGE_863=m
+CONFIG_NLS_CODEPAGE_864=m
+CONFIG_NLS_CODEPAGE_865=m
+CONFIG_NLS_CODEPAGE_866=m
+CONFIG_NLS_CODEPAGE_869=m
+CONFIG_NLS_CODEPAGE_936=m
+CONFIG_NLS_CODEPAGE_950=m
+CONFIG_NLS_CODEPAGE_932=m
+CONFIG_NLS_CODEPAGE_949=m
+CONFIG_NLS_CODEPAGE_874=m
+CONFIG_NLS_ISO8859_8=m
+CONFIG_NLS_CODEPAGE_1250=m
+CONFIG_NLS_CODEPAGE_1251=m
+CONFIG_NLS_ASCII=m
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_ISO8859_2=m
+CONFIG_NLS_ISO8859_3=m
+CONFIG_NLS_ISO8859_4=m
+CONFIG_NLS_ISO8859_5=m
+CONFIG_NLS_ISO8859_6=m
+CONFIG_NLS_ISO8859_7=m
+CONFIG_NLS_ISO8859_9=m
+CONFIG_NLS_ISO8859_13=m
+CONFIG_NLS_ISO8859_14=m
+CONFIG_NLS_ISO8859_15=m
+CONFIG_NLS_KOI8_R=m
+CONFIG_NLS_KOI8_U=m
+CONFIG_NLS_UTF8=y
+# CONFIG_DLM is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_PRINTK_TIME is not set
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_FRAME_WARN=1024
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_DEBUG_FS is not set
+# CONFIG_HEADERS_CHECK is not set
+# CONFIG_DEBUG_KERNEL is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+CONFIG_FRAME_POINTER=y
+# CONFIG_SAMPLES is not set
+# CONFIG_DEBUG_USER is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+CONFIG_CRYPTO=y
+
+#
+# Crypto core or helper
+#
+CONFIG_CRYPTO_ALGAPI=m
+CONFIG_CRYPTO_AEAD=m
+CONFIG_CRYPTO_BLKCIPHER=m
+CONFIG_CRYPTO_HASH=m
+CONFIG_CRYPTO_MANAGER=m
+# CONFIG_CRYPTO_GF128MUL is not set
+CONFIG_CRYPTO_NULL=m
+# CONFIG_CRYPTO_CRYPTD is not set
+CONFIG_CRYPTO_AUTHENC=m
+CONFIG_CRYPTO_TEST=m
+
+#
+# Authenticated Encryption with Associated Data
+#
+# CONFIG_CRYPTO_CCM is not set
+# CONFIG_CRYPTO_GCM is not set
+# CONFIG_CRYPTO_SEQIV is not set
+
+#
+# Block modes
+#
+CONFIG_CRYPTO_CBC=m
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+CONFIG_CRYPTO_ECB=m
+# CONFIG_CRYPTO_LRW is not set
+CONFIG_CRYPTO_PCBC=m
+# CONFIG_CRYPTO_XTS is not set
+
+#
+# Hash modes
+#
+CONFIG_CRYPTO_HMAC=m
+# CONFIG_CRYPTO_XCBC is not set
+
+#
+# Digest
+#
+CONFIG_CRYPTO_CRC32C=m
+CONFIG_CRYPTO_MD4=m
+CONFIG_CRYPTO_MD5=m
+CONFIG_CRYPTO_MICHAEL_MIC=m
+CONFIG_CRYPTO_SHA1=m
+CONFIG_CRYPTO_SHA256=m
+CONFIG_CRYPTO_SHA512=m
+# CONFIG_CRYPTO_TGR192 is not set
+CONFIG_CRYPTO_WP512=m
+
+#
+# Ciphers
+#
+CONFIG_CRYPTO_AES=m
+CONFIG_CRYPTO_ANUBIS=m
+CONFIG_CRYPTO_ARC4=m
+CONFIG_CRYPTO_BLOWFISH=m
+CONFIG_CRYPTO_CAMELLIA=m
+CONFIG_CRYPTO_CAST5=m
+CONFIG_CRYPTO_CAST6=m
+CONFIG_CRYPTO_DES=m
+# CONFIG_CRYPTO_FCRYPT is not set
+CONFIG_CRYPTO_KHAZAD=m
+# CONFIG_CRYPTO_SALSA20 is not set
+# CONFIG_CRYPTO_SEED is not set
+CONFIG_CRYPTO_SERPENT=m
+CONFIG_CRYPTO_TEA=m
+CONFIG_CRYPTO_TWOFISH=m
+CONFIG_CRYPTO_TWOFISH_COMMON=m
+
+#
+# Compression
+#
+CONFIG_CRYPTO_DEFLATE=m
+CONFIG_CRYPTO_LZO=m
+# CONFIG_CRYPTO_HW is not set
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+# CONFIG_GENERIC_FIND_FIRST_BIT is not set
+# CONFIG_GENERIC_FIND_NEXT_BIT is not set
+CONFIG_CRC_CCITT=y
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
+CONFIG_LIBCRC32C=m
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_LZO_COMPRESS=y
+CONFIG_LZO_DECOMPRESS=y
+CONFIG_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
diff --git a/recipes/linux/linux-rp-2.6.26/defconfig-collie b/recipes/linux/linux-rp-2.6.26/defconfig-collie
new file mode 100644
index 0000000000..10163d814d
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.26/defconfig-collie
@@ -0,0 +1,1502 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.26
+# Mon Oct 20 01:48:37 2008
+#
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+CONFIG_GENERIC_GPIO=y
+CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_MMU=y
+# CONFIG_NO_IOPORT is not set
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+# CONFIG_ARCH_HAS_ILOG2_U32 is not set
+# CONFIG_ARCH_HAS_ILOG2_U64 is not set
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_ARCH_SUPPORTS_AOUT=y
+CONFIG_ZONE_DMA=y
+CONFIG_ARCH_MTD_XIP=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_LOCK_KERNEL=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+# CONFIG_POSIX_MQUEUE is not set
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_BSD_PROCESS_ACCT_V3=y
+# CONFIG_TASKSTATS is not set
+# CONFIG_AUDIT is not set
+# CONFIG_IKCONFIG is not set
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_CGROUPS is not set
+# CONFIG_GROUP_SCHED is not set
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+# CONFIG_RELAY is not set
+# CONFIG_NAMESPACES is not set
+# CONFIG_BLK_DEV_INITRD is not set
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+CONFIG_EMBEDDED=y
+CONFIG_UID16=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_SYSCTL_SYSCALL_CHECK=y
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_COMPAT_BRK=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_ANON_INODES=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLAB=y
+# CONFIG_SLUB is not set
+# CONFIG_SLOB is not set
+CONFIG_PROFILING=y
+# CONFIG_MARKERS is not set
+CONFIG_OPROFILE=m
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_KPROBES is not set
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+# CONFIG_HAVE_DMA_ATTRS is not set
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+# CONFIG_TINY_SHMEM is not set
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+# CONFIG_MODULE_FORCE_LOAD is not set
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+CONFIG_BLOCK=y
+# CONFIG_LBD is not set
+# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_LSF is not set
+# CONFIG_BLK_DEV_BSG is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=m
+CONFIG_IOSCHED_CFQ=m
+CONFIG_DEFAULT_AS=y
+# CONFIG_DEFAULT_DEADLINE is not set
+# CONFIG_DEFAULT_CFQ is not set
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="anticipatory"
+CONFIG_CLASSIC_RCU=y
+
+#
+# System Type
+#
+# CONFIG_ARCH_AAEC2000 is not set
+# CONFIG_ARCH_INTEGRATOR is not set
+# CONFIG_ARCH_REALVIEW is not set
+# CONFIG_ARCH_VERSATILE is not set
+# CONFIG_ARCH_AT91 is not set
+# CONFIG_ARCH_CLPS7500 is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_CO285 is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE is not set
+# CONFIG_ARCH_NETX is not set
+# CONFIG_ARCH_H720X is not set
+# CONFIG_ARCH_IMX is not set
+# CONFIG_ARCH_IOP13XX is not set
+# CONFIG_ARCH_IOP32X is not set
+# CONFIG_ARCH_IOP33X is not set
+# CONFIG_ARCH_IXP23XX is not set
+# CONFIG_ARCH_IXP2000 is not set
+# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_L7200 is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_NS9XXX is not set
+# CONFIG_ARCH_MXC is not set
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_PNX4008 is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_RPC is not set
+CONFIG_ARCH_SA1100=y
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP is not set
+# CONFIG_ARCH_MSM7X00A is not set
+
+#
+# SA11x0 Implementations
+#
+# CONFIG_SA1100_ASSABET is not set
+# CONFIG_SA1100_CERF is not set
+CONFIG_SA1100_COLLIE=y
+# CONFIG_SA1100_H3100 is not set
+# CONFIG_SA1100_H3600 is not set
+# CONFIG_SA1100_H3800 is not set
+# CONFIG_SA1100_BADGE4 is not set
+# CONFIG_SA1100_JORNADA720 is not set
+# CONFIG_SA1100_HACKKIT is not set
+# CONFIG_SA1100_LART is not set
+# CONFIG_SA1100_PLEB is not set
+# CONFIG_SA1100_SHANNON is not set
+# CONFIG_SA1100_SIMPAD is not set
+# CONFIG_SA1100_SSP is not set
+
+#
+# Boot options
+#
+
+#
+# Power management
+#
+
+#
+# Processor Type
+#
+CONFIG_CPU_32=y
+CONFIG_CPU_SA1100=y
+CONFIG_CPU_32v4=y
+CONFIG_CPU_ABRT_EV4=y
+CONFIG_CPU_PABRT_NOIFAR=y
+CONFIG_CPU_CACHE_V4WB=y
+CONFIG_CPU_CACHE_VIVT=y
+CONFIG_CPU_TLB_V4WB=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+
+#
+# Processor Features
+#
+# CONFIG_CPU_ICACHE_DISABLE is not set
+# CONFIG_CPU_DCACHE_DISABLE is not set
+# CONFIG_OUTER_CACHE is not set
+CONFIG_SHARP_LOCOMO=y
+CONFIG_SHARP_PARAM=y
+CONFIG_SHARP_SCOOP=y
+
+#
+# Bus support
+#
+CONFIG_ISA=y
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+CONFIG_PCCARD=y
+# CONFIG_PCMCIA_DEBUG is not set
+CONFIG_PCMCIA=y
+CONFIG_PCMCIA_LOAD_CIS=y
+CONFIG_PCMCIA_IOCTL=y
+
+#
+# PC-card bridges
+#
+# CONFIG_I82365 is not set
+# CONFIG_TCIC is not set
+CONFIG_PCMCIA_SA1100=y
+
+#
+# Kernel Features
+#
+CONFIG_TICK_ONESHOT=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_PREEMPT=y
+CONFIG_HZ=100
+# CONFIG_AEABI is not set
+CONFIG_ARCH_DISCONTIGMEM_ENABLE=y
+CONFIG_ARCH_SPARSEMEM_ENABLE=y
+CONFIG_ARCH_SELECT_MEMORY_MODEL=y
+CONFIG_NODES_SHIFT=2
+CONFIG_SELECT_MEMORY_MODEL=y
+# CONFIG_FLATMEM_MANUAL is not set
+CONFIG_DISCONTIGMEM_MANUAL=y
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_DISCONTIGMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+CONFIG_NEED_MULTIPLE_NODES=y
+# CONFIG_SPARSEMEM_STATIC is not set
+# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4096
+# CONFIG_RESOURCES_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=1
+CONFIG_BOUNCE=y
+CONFIG_VIRT_TO_BUS=y
+# CONFIG_LEDS is not set
+CONFIG_ALIGNMENT_TRAP=y
+
+#
+# Boot options
+#
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="console=ttySA0,115200n8 console=tty1 noinitrd root=/dev/mmcblk0p1 rootfstype=ext2 rootdelay=3 rw mem=64M fbcon=rotate:1 dyntick=enable debug"
+# CONFIG_XIP_KERNEL is not set
+CONFIG_KEXEC=y
+CONFIG_ATAGS_PROC=y
+
+#
+# CPU Frequency scaling
+#
+# CONFIG_CPU_FREQ is not set
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+CONFIG_FPE_NWFPE=y
+# CONFIG_FPE_NWFPE_XP is not set
+# CONFIG_FPE_FASTFPE is not set
+
+#
+# Userspace binary formats
+#
+CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_AOUT=m
+CONFIG_BINFMT_MISC=m
+# CONFIG_ARTHUR is not set
+
+#
+# Power management options
+#
+# CONFIG_PM is not set
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+
+#
+# Networking
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=m
+CONFIG_PACKET_MMAP=y
+CONFIG_UNIX=y
+CONFIG_XFRM=y
+CONFIG_XFRM_USER=m
+# CONFIG_XFRM_SUB_POLICY is not set
+# CONFIG_XFRM_MIGRATE is not set
+# CONFIG_XFRM_STATISTICS is not set
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+# CONFIG_IP_PNP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_ARPD is not set
+CONFIG_SYN_COOKIES=y
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+CONFIG_INET_TUNNEL=m
+CONFIG_INET_XFRM_MODE_TRANSPORT=m
+CONFIG_INET_XFRM_MODE_TUNNEL=m
+CONFIG_INET_XFRM_MODE_BEET=m
+# CONFIG_INET_LRO is not set
+CONFIG_INET_DIAG=m
+CONFIG_INET_TCP_DIAG=m
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+# CONFIG_IP_VS is not set
+CONFIG_IPV6=m
+# CONFIG_IPV6_PRIVACY is not set
+# CONFIG_IPV6_ROUTER_PREF is not set
+# CONFIG_IPV6_OPTIMISTIC_DAD is not set
+CONFIG_INET6_AH=m
+CONFIG_INET6_ESP=m
+CONFIG_INET6_IPCOMP=m
+# CONFIG_IPV6_MIP6 is not set
+CONFIG_INET6_XFRM_TUNNEL=m
+CONFIG_INET6_TUNNEL=m
+CONFIG_INET6_XFRM_MODE_TRANSPORT=m
+CONFIG_INET6_XFRM_MODE_TUNNEL=m
+CONFIG_INET6_XFRM_MODE_BEET=m
+# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set
+CONFIG_IPV6_SIT=m
+CONFIG_IPV6_NDISC_NODETYPE=y
+CONFIG_IPV6_TUNNEL=m
+# CONFIG_IPV6_MULTIPLE_TABLES is not set
+# CONFIG_IPV6_MROUTE is not set
+# CONFIG_NETWORK_SECMARK is not set
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+CONFIG_NETFILTER_ADVANCED=y
+
+#
+# Core Netfilter Configuration
+#
+# CONFIG_NETFILTER_NETLINK_QUEUE is not set
+# CONFIG_NETFILTER_NETLINK_LOG is not set
+# CONFIG_NF_CONNTRACK is not set
+CONFIG_NETFILTER_XTABLES=m
+# CONFIG_NETFILTER_XT_TARGET_CLASSIFY is not set
+# CONFIG_NETFILTER_XT_TARGET_DSCP is not set
+# CONFIG_NETFILTER_XT_TARGET_MARK is not set
+# CONFIG_NETFILTER_XT_TARGET_NFQUEUE is not set
+# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set
+# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set
+# CONFIG_NETFILTER_XT_TARGET_TRACE is not set
+# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set
+# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set
+# CONFIG_NETFILTER_XT_MATCH_COMMENT is not set
+# CONFIG_NETFILTER_XT_MATCH_DCCP is not set
+# CONFIG_NETFILTER_XT_MATCH_DSCP is not set
+# CONFIG_NETFILTER_XT_MATCH_ESP is not set
+# CONFIG_NETFILTER_XT_MATCH_IPRANGE is not set
+# CONFIG_NETFILTER_XT_MATCH_LENGTH is not set
+# CONFIG_NETFILTER_XT_MATCH_LIMIT is not set
+# CONFIG_NETFILTER_XT_MATCH_MAC is not set
+# CONFIG_NETFILTER_XT_MATCH_MARK is not set
+# CONFIG_NETFILTER_XT_MATCH_OWNER is not set
+# CONFIG_NETFILTER_XT_MATCH_POLICY is not set
+# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set
+# CONFIG_NETFILTER_XT_MATCH_PKTTYPE is not set
+# CONFIG_NETFILTER_XT_MATCH_QUOTA is not set
+# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set
+# CONFIG_NETFILTER_XT_MATCH_REALM is not set
+# CONFIG_NETFILTER_XT_MATCH_SCTP is not set
+# CONFIG_NETFILTER_XT_MATCH_STATISTIC is not set
+# CONFIG_NETFILTER_XT_MATCH_STRING is not set
+# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set
+# CONFIG_NETFILTER_XT_MATCH_TIME is not set
+# CONFIG_NETFILTER_XT_MATCH_U32 is not set
+# CONFIG_NETFILTER_XT_MATCH_HASHLIMIT is not set
+
+#
+# IP: Netfilter Configuration
+#
+CONFIG_IP_NF_QUEUE=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_RECENT=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_AH=m
+CONFIG_IP_NF_MATCH_TTL=m
+CONFIG_IP_NF_MATCH_ADDRTYPE=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_LOG=m
+CONFIG_IP_NF_TARGET_ULOG=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_TTL=m
+CONFIG_IP_NF_RAW=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+
+#
+# IPv6: Netfilter Configuration
+#
+# CONFIG_IP6_NF_QUEUE is not set
+# CONFIG_IP6_NF_IPTABLES is not set
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_SCTP is not set
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_SCHED is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_CAN is not set
+CONFIG_IRDA=m
+
+#
+# IrDA protocols
+#
+CONFIG_IRLAN=m
+CONFIG_IRNET=m
+CONFIG_IRCOMM=m
+# CONFIG_IRDA_ULTRA is not set
+
+#
+# IrDA options
+#
+# CONFIG_IRDA_CACHE_LAST_LSAP is not set
+# CONFIG_IRDA_FAST_RR is not set
+# CONFIG_IRDA_DEBUG is not set
+
+#
+# Infrared-port device drivers
+#
+
+#
+# SIR device drivers
+#
+# CONFIG_IRTTY_SIR is not set
+
+#
+# Dongle support
+#
+
+#
+# FIR device drivers
+#
+# CONFIG_SA1100_FIR is not set
+CONFIG_BT=m
+CONFIG_BT_L2CAP=m
+CONFIG_BT_SCO=m
+CONFIG_BT_RFCOMM=m
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=m
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+CONFIG_BT_HIDP=m
+
+#
+# Bluetooth device drivers
+#
+# CONFIG_BT_HCIBTSDIO is not set
+CONFIG_BT_HCIUART=m
+CONFIG_BT_HCIUART_H4=y
+CONFIG_BT_HCIUART_BCSP=y
+# CONFIG_BT_HCIUART_LL is not set
+CONFIG_BT_HCIDTL1=m
+CONFIG_BT_HCIBT3C=m
+CONFIG_BT_HCIBLUECARD=m
+CONFIG_BT_HCIBTUART=m
+CONFIG_BT_HCIVHCI=m
+# CONFIG_AF_RXRPC is not set
+
+#
+# Wireless
+#
+# CONFIG_CFG80211 is not set
+CONFIG_WIRELESS_EXT=y
+# CONFIG_MAC80211 is not set
+CONFIG_IEEE80211=m
+# CONFIG_IEEE80211_DEBUG is not set
+CONFIG_IEEE80211_CRYPT_WEP=m
+CONFIG_IEEE80211_CRYPT_CCMP=m
+CONFIG_IEEE80211_CRYPT_TKIP=m
+# CONFIG_RFKILL is not set
+# CONFIG_NET_9P is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+# CONFIG_DEBUG_DRIVER is not set
+# CONFIG_DEBUG_DEVRES is not set
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_CONNECTOR is not set
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+# CONFIG_MTD_CONCAT is not set
+CONFIG_MTD_PARTITIONS=y
+# CONFIG_MTD_REDBOOT_PARTS is not set
+# CONFIG_MTD_CMDLINE_PARTS is not set
+# CONFIG_MTD_AFS_PARTS is not set
+# CONFIG_MTD_AR7_PARTS is not set
+
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_CHAR=m
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+# CONFIG_MTD_OOPS is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+# CONFIG_MTD_CFI is not set
+# CONFIG_MTD_JEDECPROBE is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+# CONFIG_MTD_RAM is not set
+# CONFIG_MTD_ROM is not set
+# CONFIG_MTD_ABSENT is not set
+CONFIG_MTD_SHARP=y
+
+#
+# Mapping drivers for chip access
+#
+CONFIG_MTD_COMPLEX_MAPPINGS=y
+CONFIG_MTD_SA1100=y
+# CONFIG_MTD_PLATRAM is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_DATAFLASH is not set
+# CONFIG_MTD_M25P80 is not set
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLOCK2MTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+# CONFIG_MTD_NAND is not set
+# CONFIG_MTD_ONENAND is not set
+
+#
+# UBI - Unsorted block images
+#
+# CONFIG_MTD_UBI is not set
+# CONFIG_PARPORT is not set
+# CONFIG_PNP is not set
+CONFIG_BLK_DEV=y
+# CONFIG_BLK_DEV_COW_COMMON is not set
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_CRYPTOLOOP=m
+CONFIG_BLK_DEV_NBD=m
+# CONFIG_BLK_DEV_RAM is not set
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+CONFIG_MISC_DEVICES=y
+# CONFIG_EEPROM_93CX6 is not set
+# CONFIG_ENCLOSURE_SERVICES is not set
+CONFIG_HAVE_IDE=y
+CONFIG_IDE=y
+CONFIG_IDE_MAX_HWIFS=4
+CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_IDE_SATA is not set
+CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_IDEDISK_MULTI_MODE is not set
+CONFIG_BLK_DEV_IDECS=y
+# CONFIG_BLK_DEV_IDECD is not set
+# CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
+# CONFIG_BLK_DEV_IDESCSI is not set
+# CONFIG_IDE_TASK_IOCTL is not set
+CONFIG_IDE_PROC_FS=y
+
+#
+# IDE chipset support/bugfixes
+#
+# CONFIG_BLK_DEV_PLATFORM is not set
+# CONFIG_BLK_DEV_IDEDMA is not set
+# CONFIG_BLK_DEV_HD is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+CONFIG_SCSI=m
+CONFIG_SCSI_DMA=y
+# CONFIG_SCSI_TGT is not set
+# CONFIG_SCSI_NETLINK is not set
+CONFIG_SCSI_PROC_FS=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=m
+CONFIG_CHR_DEV_ST=m
+CONFIG_CHR_DEV_OSST=m
+CONFIG_BLK_DEV_SR=m
+# CONFIG_BLK_DEV_SR_VENDOR is not set
+CONFIG_CHR_DEV_SG=m
+# CONFIG_CHR_DEV_SCH is not set
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+CONFIG_SCSI_MULTI_LUN=y
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
+# CONFIG_SCSI_SCAN_ASYNC is not set
+CONFIG_SCSI_WAIT_SCAN=m
+
+#
+# SCSI Transports
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+# CONFIG_SCSI_ISCSI_ATTRS is not set
+# CONFIG_SCSI_SAS_LIBSAS is not set
+# CONFIG_SCSI_SRP_ATTRS is not set
+CONFIG_SCSI_LOWLEVEL=y
+# CONFIG_ISCSI_TCP is not set
+# CONFIG_SCSI_AHA152X is not set
+# CONFIG_SCSI_AIC7XXX_OLD is not set
+# CONFIG_SCSI_ADVANSYS is not set
+# CONFIG_SCSI_IN2000 is not set
+# CONFIG_SCSI_DTC3280 is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GENERIC_NCR5380 is not set
+# CONFIG_SCSI_GENERIC_NCR5380_MMIO is not set
+# CONFIG_SCSI_NCR53C406A is not set
+# CONFIG_SCSI_PAS16 is not set
+# CONFIG_SCSI_QLOGIC_FAS is not set
+# CONFIG_SCSI_SYM53C416 is not set
+# CONFIG_SCSI_T128 is not set
+# CONFIG_SCSI_DEBUG is not set
+# CONFIG_SCSI_LOWLEVEL_PCMCIA is not set
+# CONFIG_ATA is not set
+CONFIG_MD=y
+# CONFIG_BLK_DEV_MD is not set
+CONFIG_BLK_DEV_DM=m
+# CONFIG_DM_DEBUG is not set
+CONFIG_DM_CRYPT=m
+CONFIG_DM_SNAPSHOT=m
+CONFIG_DM_MIRROR=m
+CONFIG_DM_ZERO=m
+CONFIG_DM_MULTIPATH=m
+CONFIG_DM_MULTIPATH_EMC=m
+# CONFIG_DM_MULTIPATH_RDAC is not set
+# CONFIG_DM_MULTIPATH_HP is not set
+# CONFIG_DM_DELAY is not set
+# CONFIG_DM_UEVENT is not set
+CONFIG_NETDEVICES=y
+# CONFIG_NETDEVICES_MULTIQUEUE is not set
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_MACVLAN is not set
+# CONFIG_EQUALIZER is not set
+CONFIG_TUN=m
+# CONFIG_VETH is not set
+# CONFIG_ARCNET is not set
+# CONFIG_PHYLIB is not set
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=m
+# CONFIG_AX88796 is not set
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_SMC91X is not set
+# CONFIG_DM9000 is not set
+# CONFIG_ENC28J60 is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_AT1700 is not set
+# CONFIG_DEPCA is not set
+# CONFIG_HP100 is not set
+# CONFIG_NET_ISA is not set
+# CONFIG_IBM_NEW_EMAC_ZMII is not set
+# CONFIG_IBM_NEW_EMAC_RGMII is not set
+# CONFIG_IBM_NEW_EMAC_TAH is not set
+# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
+# CONFIG_NET_PCI is not set
+# CONFIG_B44 is not set
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
+# CONFIG_TR is not set
+
+#
+# Wireless LAN
+#
+# CONFIG_WLAN_PRE80211 is not set
+CONFIG_WLAN_80211=y
+# CONFIG_PCMCIA_RAYCS is not set
+CONFIG_LIBERTAS=m
+# CONFIG_LIBERTAS_USB is not set
+CONFIG_LIBERTAS_CS=m
+# CONFIG_LIBERTAS_SDIO is not set
+# CONFIG_LIBERTAS_DEBUG is not set
+CONFIG_HERMES=m
+CONFIG_PCMCIA_HERMES=m
+CONFIG_PCMCIA_SPECTRUM=m
+# CONFIG_ATMEL is not set
+CONFIG_AIRO_CS=m
+# CONFIG_PCMCIA_WL3501 is not set
+# CONFIG_IWLWIFI_LEDS is not set
+CONFIG_HOSTAP=m
+CONFIG_HOSTAP_FIRMWARE=y
+# CONFIG_HOSTAP_FIRMWARE_NVRAM is not set
+CONFIG_HOSTAP_CS=m
+CONFIG_NET_PCMCIA=y
+# CONFIG_PCMCIA_3C589 is not set
+# CONFIG_PCMCIA_3C574 is not set
+# CONFIG_PCMCIA_FMVJ18X is not set
+CONFIG_PCMCIA_PCNET=m
+# CONFIG_PCMCIA_NMCLAN is not set
+# CONFIG_PCMCIA_SMC91C92 is not set
+# CONFIG_PCMCIA_XIRC2PS is not set
+# CONFIG_PCMCIA_AXNET is not set
+# CONFIG_WAN is not set
+CONFIG_PPP=m
+# CONFIG_PPP_MULTILINK is not set
+# CONFIG_PPP_FILTER is not set
+CONFIG_PPP_ASYNC=m
+# CONFIG_PPP_SYNC_TTY is not set
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_BSDCOMP=m
+# CONFIG_PPP_MPPE is not set
+# CONFIG_PPPOE is not set
+# CONFIG_PPPOL2TP is not set
+# CONFIG_SLIP is not set
+CONFIG_SLHC=m
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_ISDN is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=m
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=640
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=480
+# CONFIG_INPUT_JOYDEV is not set
+CONFIG_INPUT_EVDEV=y
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+# CONFIG_KEYBOARD_ATKBD is not set
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+CONFIG_KEYBOARD_LOCOMO=y
+# CONFIG_KEYBOARD_XTKBD is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+# CONFIG_KEYBOARD_GPIO is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+CONFIG_INPUT_TOUCHSCREEN=y
+# CONFIG_TOUCHSCREEN_ADS7846 is not set
+# CONFIG_TOUCHSCREEN_FUJITSU is not set
+# CONFIG_TOUCHSCREEN_GUNZE is not set
+# CONFIG_TOUCHSCREEN_ELO is not set
+# CONFIG_TOUCHSCREEN_MTOUCH is not set
+# CONFIG_TOUCHSCREEN_MK712 is not set
+# CONFIG_TOUCHSCREEN_PENMOUNT is not set
+# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set
+# CONFIG_TOUCHSCREEN_TOUCHWIN is not set
+# CONFIG_TOUCHSCREEN_UCB1400 is not set
+# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set
+CONFIG_INPUT_MISC=y
+# CONFIG_INPUT_ATI_REMOTE is not set
+# CONFIG_INPUT_ATI_REMOTE2 is not set
+# CONFIG_INPUT_KEYSPAN_REMOTE is not set
+# CONFIG_INPUT_POWERMATE is not set
+# CONFIG_INPUT_YEALINK is not set
+CONFIG_INPUT_UINPUT=m
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+# CONFIG_VT_HW_CONSOLE_BINDING is not set
+CONFIG_DEVKMEM=y
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+CONFIG_SERIAL_8250=m
+CONFIG_SERIAL_8250_CS=m
+CONFIG_SERIAL_8250_NR_UARTS=4
+CONFIG_SERIAL_8250_RUNTIME_UARTS=4
+# CONFIG_SERIAL_8250_EXTENDED is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_SA1100=y
+CONFIG_SERIAL_SA1100_CONSOLE=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_IPMI_HANDLER is not set
+CONFIG_HW_RANDOM=m
+# CONFIG_NVRAM is not set
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+
+#
+# PCMCIA character devices
+#
+CONFIG_SYNCLINK_CS=m
+CONFIG_CARDMAN_4000=m
+CONFIG_CARDMAN_4040=m
+CONFIG_IPWIRELESS=m
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+CONFIG_DEVPORT=y
+# CONFIG_I2C is not set
+CONFIG_SPI=y
+# CONFIG_SPI_DEBUG is not set
+CONFIG_SPI_MASTER=y
+
+#
+# SPI Master Controller Drivers
+#
+# CONFIG_SPI_BITBANG is not set
+CONFIG_SPI_LOCOMO=y
+
+#
+# SPI Protocol Masters
+#
+# CONFIG_SPI_AT25 is not set
+# CONFIG_SPI_SPIDEV is not set
+# CONFIG_SPI_TLE62X0 is not set
+CONFIG_HAVE_GPIO_LIB=y
+
+#
+# GPIO Support
+#
+# CONFIG_DEBUG_GPIO is not set
+
+#
+# I2C GPIO expanders:
+#
+
+#
+# SPI GPIO expanders:
+#
+# CONFIG_GPIO_MCP23S08 is not set
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+# CONFIG_WATCHDOG is not set
+
+#
+# Sonics Silicon Backplane
+#
+CONFIG_SSB_POSSIBLE=y
+# CONFIG_SSB is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_SM501 is not set
+# CONFIG_MFD_ASIC3 is not set
+# CONFIG_HTC_EGPIO is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_HTC_ASIC3 is not set
+# CONFIG_HTC_ASIC3_DS1WM is not set
+
+#
+# Multimedia Capabilities Port drivers
+#
+CONFIG_MCP=y
+CONFIG_MCP_SA11X0=y
+CONFIG_MCP_UCB1200=y
+# CONFIG_MCP_UCB1200_TS is not set
+CONFIG_MCP_COLLIE_TS=y
+
+#
+# Multimedia devices
+#
+
+#
+# Multimedia core support
+#
+# CONFIG_VIDEO_DEV is not set
+# CONFIG_DVB_CORE is not set
+# CONFIG_VIDEO_MEDIA is not set
+
+#
+# Multimedia drivers
+#
+# CONFIG_DAB is not set
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+CONFIG_FB=y
+# CONFIG_FIRMWARE_EDID is not set
+# CONFIG_FB_DDC is not set
+CONFIG_FB_CFB_FILLRECT=y
+CONFIG_FB_CFB_COPYAREA=y
+CONFIG_FB_CFB_IMAGEBLIT=y
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+# CONFIG_FB_SYS_FILLRECT is not set
+# CONFIG_FB_SYS_COPYAREA is not set
+# CONFIG_FB_SYS_IMAGEBLIT is not set
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+# CONFIG_FB_SYS_FOPS is not set
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_BACKLIGHT is not set
+# CONFIG_FB_MODE_HELPERS is not set
+# CONFIG_FB_TILEBLITTING is not set
+
+#
+# Frame buffer hardware drivers
+#
+CONFIG_FB_SA1100=y
+# CONFIG_FB_S1D13XXX is not set
+# CONFIG_FB_VIRTUAL is not set
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+# CONFIG_LCD_CLASS_DEVICE is not set
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+# CONFIG_BACKLIGHT_CORGI is not set
+CONFIG_BACKLIGHT_LOCOMO=y
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+# CONFIG_MDA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
+CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y
+CONFIG_FONTS=y
+CONFIG_FONT_8x8=y
+# CONFIG_FONT_8x16 is not set
+# CONFIG_FONT_6x11 is not set
+# CONFIG_FONT_7x14 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+# CONFIG_FONT_ACORN_8x8 is not set
+# CONFIG_FONT_MINI_4x6 is not set
+# CONFIG_FONT_SUN8x16 is not set
+# CONFIG_FONT_SUN12x22 is not set
+# CONFIG_FONT_10x18 is not set
+# CONFIG_LOGO is not set
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+# CONFIG_HID_SUPPORT is not set
+CONFIG_HID=m
+CONFIG_USB_SUPPORT=y
+CONFIG_USB_ARCH_HAS_HCD=y
+# CONFIG_USB_ARCH_HAS_OHCI is not set
+# CONFIG_USB_ARCH_HAS_EHCI is not set
+# CONFIG_USB is not set
+# CONFIG_USB_OTG_WHITELIST is not set
+# CONFIG_USB_OTG_BLACKLIST_HUB is not set
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
+#
+CONFIG_USB_GADGET=y
+# CONFIG_USB_GADGET_DEBUG is not set
+# CONFIG_USB_GADGET_DEBUG_FILES is not set
+CONFIG_USB_GADGET_SELECTED=y
+# CONFIG_USB_GADGET_AMD5536UDC is not set
+# CONFIG_USB_GADGET_ATMEL_USBA is not set
+# CONFIG_USB_GADGET_FSL_USB2 is not set
+# CONFIG_USB_GADGET_NET2280 is not set
+# CONFIG_USB_GADGET_PXA2XX is not set
+# CONFIG_USB_GADGET_M66592 is not set
+# CONFIG_USB_GADGET_PXA27X is not set
+# CONFIG_USB_GADGET_GOKU is not set
+# CONFIG_USB_GADGET_LH7A40X is not set
+# CONFIG_USB_GADGET_OMAP is not set
+# CONFIG_USB_GADGET_S3C2410 is not set
+# CONFIG_USB_GADGET_AT91 is not set
+CONFIG_USB_GADGET_SA1100=y
+CONFIG_USB_SA1100=y
+# CONFIG_USB_GADGET_DUMMY_HCD is not set
+# CONFIG_USB_GADGET_DUALSPEED is not set
+# CONFIG_USB_ZERO is not set
+CONFIG_USB_ETH=m
+# CONFIG_USB_ETH_RNDIS is not set
+# CONFIG_USB_ETH_NO_MDLM is not set
+# CONFIG_USB_GADGETFS is not set
+CONFIG_USB_FILE_STORAGE=m
+# CONFIG_USB_FILE_STORAGE_TEST is not set
+CONFIG_USB_G_SERIAL=m
+# CONFIG_USB_MIDI_GADGET is not set
+CONFIG_USB_G_PRINTER=m
+CONFIG_MMC=y
+# CONFIG_MMC_DEBUG is not set
+CONFIG_MMC_UNSAFE_RESUME=y
+
+#
+# MMC/SD Card Drivers
+#
+CONFIG_MMC_BLOCK=y
+CONFIG_MMC_BLOCK_BOUNCE=y
+# CONFIG_SDIO_UART is not set
+# CONFIG_MMC_TEST is not set
+
+#
+# MMC/SD Host Controller Drivers
+#
+CONFIG_MMC_SPI=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+
+#
+# LED drivers
+#
+CONFIG_LEDS_LOCOMO=y
+# CONFIG_LEDS_GPIO is not set
+
+#
+# LED Triggers
+#
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=y
+CONFIG_LEDS_TRIGGER_IDE_DISK=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
+CONFIG_RTC_LIB=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_HCTOSYS=y
+CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
+# CONFIG_RTC_DEBUG is not set
+
+#
+# RTC interfaces
+#
+CONFIG_RTC_INTF_SYSFS=y
+CONFIG_RTC_INTF_PROC=y
+CONFIG_RTC_INTF_DEV=y
+# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
+# CONFIG_RTC_DRV_TEST is not set
+
+#
+# SPI RTC drivers
+#
+# CONFIG_RTC_DRV_MAX6902 is not set
+# CONFIG_RTC_DRV_R9701 is not set
+# CONFIG_RTC_DRV_RS5C348 is not set
+
+#
+# Platform RTC drivers
+#
+# CONFIG_RTC_DRV_CMOS is not set
+# CONFIG_RTC_DRV_DS1511 is not set
+# CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_DS1742 is not set
+# CONFIG_RTC_DRV_STK17TA8 is not set
+# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_M48T59 is not set
+# CONFIG_RTC_DRV_V3020 is not set
+
+#
+# on-CPU RTC drivers
+#
+CONFIG_RTC_DRV_SA1100=y
+CONFIG_UIO=m
+# CONFIG_UIO_SMX is not set
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+# CONFIG_EXT2_FS_XIP is not set
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_FS_XATTR is not set
+# CONFIG_EXT4DEV_FS is not set
+CONFIG_JBD=y
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+CONFIG_FS_POSIX_ACL=y
+# CONFIG_XFS_FS is not set
+# CONFIG_OCFS2_FS is not set
+CONFIG_DNOTIFY=y
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+# CONFIG_MSDOS_FS is not set
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_DEBUG=0
+CONFIG_JFFS2_FS_WRITEBUFFER=y
+# CONFIG_JFFS2_FS_WBUF_VERIFY is not set
+# CONFIG_JFFS2_SUMMARY is not set
+# CONFIG_JFFS2_FS_XATTR is not set
+# CONFIG_JFFS2_SYSFS is not set
+CONFIG_JFFS2_COMPRESSION_OPTIONS=y
+CONFIG_JFFS2_ZLIB=y
+CONFIG_JFFS2_LZO=y
+CONFIG_JFFS2_RTIME=y
+CONFIG_JFFS2_RUBIN=y
+# CONFIG_JFFS2_CMODE_NONE is not set
+CONFIG_JFFS2_CMODE_PRIORITY=y
+# CONFIG_JFFS2_CMODE_SIZE is not set
+# CONFIG_JFFS2_CMODE_FAVOURLZO is not set
+CONFIG_CRAMFS=m
+# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+CONFIG_NFS_FS=m
+CONFIG_NFS_V3=y
+# CONFIG_NFS_V3_ACL is not set
+CONFIG_NFS_V4=y
+CONFIG_NFSD=m
+CONFIG_NFSD_V3=y
+# CONFIG_NFSD_V3_ACL is not set
+CONFIG_NFSD_V4=y
+CONFIG_LOCKD=m
+CONFIG_LOCKD_V4=y
+CONFIG_EXPORTFS=m
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=m
+CONFIG_SUNRPC_GSS=m
+# CONFIG_SUNRPC_BIND34 is not set
+CONFIG_RPCSEC_GSS_KRB5=m
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+CONFIG_SMB_FS=m
+CONFIG_SMB_NLS_DEFAULT=y
+CONFIG_SMB_NLS_REMOTE="cp437"
+CONFIG_CIFS=m
+# CONFIG_CIFS_STATS is not set
+# CONFIG_CIFS_WEAK_PW_HASH is not set
+# CONFIG_CIFS_XATTR is not set
+# CONFIG_CIFS_DEBUG2 is not set
+# CONFIG_CIFS_EXPERIMENTAL is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_MINIX_SUBPARTITION is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_UNIXWARE_DISKLABEL is not set
+# CONFIG_LDM_PARTITION is not set
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_ULTRIX_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_KARMA_PARTITION is not set
+# CONFIG_EFI_PARTITION is not set
+# CONFIG_SYSV68_PARTITION is not set
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="cp437"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_737=m
+CONFIG_NLS_CODEPAGE_775=m
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
+CONFIG_NLS_CODEPAGE_855=m
+CONFIG_NLS_CODEPAGE_857=m
+CONFIG_NLS_CODEPAGE_860=m
+CONFIG_NLS_CODEPAGE_861=m
+CONFIG_NLS_CODEPAGE_862=m
+CONFIG_NLS_CODEPAGE_863=m
+CONFIG_NLS_CODEPAGE_864=m
+CONFIG_NLS_CODEPAGE_865=m
+CONFIG_NLS_CODEPAGE_866=m
+CONFIG_NLS_CODEPAGE_869=m
+CONFIG_NLS_CODEPAGE_936=m
+CONFIG_NLS_CODEPAGE_950=m
+CONFIG_NLS_CODEPAGE_932=m
+CONFIG_NLS_CODEPAGE_949=m
+CONFIG_NLS_CODEPAGE_874=m
+CONFIG_NLS_ISO8859_8=m
+CONFIG_NLS_CODEPAGE_1250=m
+CONFIG_NLS_CODEPAGE_1251=m
+CONFIG_NLS_ASCII=m
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_ISO8859_2=m
+CONFIG_NLS_ISO8859_3=m
+CONFIG_NLS_ISO8859_4=m
+CONFIG_NLS_ISO8859_5=m
+CONFIG_NLS_ISO8859_6=m
+CONFIG_NLS_ISO8859_7=m
+CONFIG_NLS_ISO8859_9=m
+CONFIG_NLS_ISO8859_13=m
+CONFIG_NLS_ISO8859_14=m
+CONFIG_NLS_ISO8859_15=m
+CONFIG_NLS_KOI8_R=m
+CONFIG_NLS_KOI8_U=m
+CONFIG_NLS_UTF8=y
+# CONFIG_DLM is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_PRINTK_TIME is not set
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_FRAME_WARN=1024
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_DEBUG_FS is not set
+# CONFIG_HEADERS_CHECK is not set
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_DEBUG_SHIRQ is not set
+# CONFIG_DETECT_SOFTLOCKUP is not set
+# CONFIG_SCHED_DEBUG is not set
+# CONFIG_SCHEDSTATS is not set
+CONFIG_TIMER_STATS=y
+# CONFIG_DEBUG_OBJECTS is not set
+# CONFIG_DEBUG_SLAB is not set
+# CONFIG_DEBUG_PREEMPT is not set
+# CONFIG_DEBUG_RT_MUTEXES is not set
+# CONFIG_RT_MUTEX_TESTER is not set
+# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_DEBUG_MUTEXES is not set
+# CONFIG_DEBUG_LOCK_ALLOC is not set
+# CONFIG_PROVE_LOCKING is not set
+# CONFIG_LOCK_STAT is not set
+# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
+# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
+# CONFIG_DEBUG_KOBJECT is not set
+CONFIG_DEBUG_BUGVERBOSE=y
+# CONFIG_DEBUG_INFO is not set
+# CONFIG_DEBUG_VM is not set
+# CONFIG_DEBUG_WRITECOUNT is not set
+# CONFIG_DEBUG_LIST is not set
+# CONFIG_DEBUG_SG is not set
+CONFIG_FRAME_POINTER=y
+# CONFIG_BOOT_PRINTK_DELAY is not set
+# CONFIG_RCU_TORTURE_TEST is not set
+# CONFIG_BACKTRACE_SELF_TEST is not set
+# CONFIG_FAULT_INJECTION is not set
+# CONFIG_SAMPLES is not set
+# CONFIG_DEBUG_USER is not set
+CONFIG_DEBUG_ERRORS=y
+# CONFIG_DEBUG_STACK_USAGE is not set
+# CONFIG_DEBUG_LL is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+CONFIG_CRYPTO=y
+
+#
+# Crypto core or helper
+#
+CONFIG_CRYPTO_ALGAPI=m
+CONFIG_CRYPTO_AEAD=m
+CONFIG_CRYPTO_BLKCIPHER=m
+CONFIG_CRYPTO_HASH=m
+CONFIG_CRYPTO_MANAGER=m
+# CONFIG_CRYPTO_GF128MUL is not set
+CONFIG_CRYPTO_NULL=m
+# CONFIG_CRYPTO_CRYPTD is not set
+CONFIG_CRYPTO_AUTHENC=m
+CONFIG_CRYPTO_TEST=m
+
+#
+# Authenticated Encryption with Associated Data
+#
+# CONFIG_CRYPTO_CCM is not set
+# CONFIG_CRYPTO_GCM is not set
+# CONFIG_CRYPTO_SEQIV is not set
+
+#
+# Block modes
+#
+CONFIG_CRYPTO_CBC=m
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+CONFIG_CRYPTO_ECB=m
+# CONFIG_CRYPTO_LRW is not set
+CONFIG_CRYPTO_PCBC=m
+# CONFIG_CRYPTO_XTS is not set
+
+#
+# Hash modes
+#
+CONFIG_CRYPTO_HMAC=m
+# CONFIG_CRYPTO_XCBC is not set
+
+#
+# Digest
+#
+CONFIG_CRYPTO_CRC32C=m
+CONFIG_CRYPTO_MD4=m
+CONFIG_CRYPTO_MD5=m
+CONFIG_CRYPTO_MICHAEL_MIC=m
+CONFIG_CRYPTO_SHA1=m
+CONFIG_CRYPTO_SHA256=m
+CONFIG_CRYPTO_SHA512=m
+# CONFIG_CRYPTO_TGR192 is not set
+CONFIG_CRYPTO_WP512=m
+
+#
+# Ciphers
+#
+CONFIG_CRYPTO_AES=m
+CONFIG_CRYPTO_ANUBIS=m
+CONFIG_CRYPTO_ARC4=m
+CONFIG_CRYPTO_BLOWFISH=m
+CONFIG_CRYPTO_CAMELLIA=m
+CONFIG_CRYPTO_CAST5=m
+CONFIG_CRYPTO_CAST6=m
+CONFIG_CRYPTO_DES=m
+# CONFIG_CRYPTO_FCRYPT is not set
+CONFIG_CRYPTO_KHAZAD=m
+# CONFIG_CRYPTO_SALSA20 is not set
+# CONFIG_CRYPTO_SEED is not set
+CONFIG_CRYPTO_SERPENT=m
+CONFIG_CRYPTO_TEA=m
+CONFIG_CRYPTO_TWOFISH=m
+CONFIG_CRYPTO_TWOFISH_COMMON=m
+
+#
+# Compression
+#
+CONFIG_CRYPTO_DEFLATE=m
+CONFIG_CRYPTO_LZO=m
+# CONFIG_CRYPTO_HW is not set
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+# CONFIG_GENERIC_FIND_FIRST_BIT is not set
+# CONFIG_GENERIC_FIND_NEXT_BIT is not set
+CONFIG_CRC_CCITT=y
+# CONFIG_CRC16 is not set
+CONFIG_CRC_ITU_T=y
+CONFIG_CRC32=y
+CONFIG_CRC7=y
+CONFIG_LIBCRC32C=m
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_LZO_COMPRESS=y
+CONFIG_LZO_DECOMPRESS=y
+CONFIG_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
diff --git a/recipes/linux/linux-rp-2.6.26/defconfig-htcuniversal b/recipes/linux/linux-rp-2.6.26/defconfig-htcuniversal
new file mode 100644
index 0000000000..8b2a31dbb0
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.26/defconfig-htcuniversal
@@ -0,0 +1,1308 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.24-rc8
+# Sun Jan 20 21:46:02 2008
+#
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+CONFIG_GENERIC_GPIO=y
+CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_MMU=y
+# CONFIG_NO_IOPORT is not set
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+# CONFIG_ARCH_HAS_ILOG2_U32 is not set
+# CONFIG_ARCH_HAS_ILOG2_U64 is not set
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_ZONE_DMA=y
+CONFIG_ARCH_MTD_XIP=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_LOCK_KERNEL=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+# CONFIG_POSIX_MQUEUE is not set
+# CONFIG_BSD_PROCESS_ACCT is not set
+# CONFIG_TASKSTATS is not set
+# CONFIG_USER_NS is not set
+# CONFIG_PID_NS is not set
+# CONFIG_AUDIT is not set
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=16
+# CONFIG_CGROUPS is not set
+CONFIG_FAIR_GROUP_SCHED=y
+CONFIG_FAIR_USER_SCHED=y
+# CONFIG_FAIR_CGROUP_SCHED is not set
+CONFIG_SYSFS_DEPRECATED=y
+# CONFIG_RELAY is not set
+# CONFIG_BLK_DEV_INITRD is not set
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+CONFIG_EMBEDDED=y
+# CONFIG_UID16 is not set
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_ANON_INODES=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLAB=y
+# CONFIG_SLUB is not set
+# CONFIG_SLOB is not set
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+# CONFIG_TINY_SHMEM is not set
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+CONFIG_BLOCK=y
+# CONFIG_LBD is not set
+# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_LSF is not set
+# CONFIG_BLK_DEV_BSG is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+# CONFIG_IOSCHED_CFQ is not set
+CONFIG_DEFAULT_AS=y
+# CONFIG_DEFAULT_DEADLINE is not set
+# CONFIG_DEFAULT_CFQ is not set
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="anticipatory"
+
+#
+# System Type
+#
+# CONFIG_ARCH_AAEC2000 is not set
+# CONFIG_ARCH_INTEGRATOR is not set
+# CONFIG_ARCH_REALVIEW is not set
+# CONFIG_ARCH_VERSATILE is not set
+# CONFIG_ARCH_AT91 is not set
+# CONFIG_ARCH_CLPS7500 is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_CO285 is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE is not set
+# CONFIG_ARCH_NETX is not set
+# CONFIG_ARCH_H720X is not set
+# CONFIG_ARCH_IMX is not set
+# CONFIG_ARCH_IOP13XX is not set
+# CONFIG_ARCH_IOP32X is not set
+# CONFIG_ARCH_IOP33X is not set
+# CONFIG_ARCH_IXP23XX is not set
+# CONFIG_ARCH_IXP2000 is not set
+# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_L7200 is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_NS9XXX is not set
+# CONFIG_ARCH_MXC is not set
+# CONFIG_ARCH_PNX4008 is not set
+CONFIG_ARCH_PXA=y
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP is not set
+CONFIG_DMABOUNCE=y
+
+#
+# Intel PXA2xx/PXA3xx Implementations
+#
+CONFIG_ARCH_LUBBOCK=y
+# CONFIG_MACH_LOGICPD_PXA270 is not set
+# CONFIG_MACH_MAINSTONE is not set
+# CONFIG_ARCH_PXA_IDP is not set
+# CONFIG_PXA_SHARPSL is not set
+# CONFIG_MACH_TRIZEPS4 is not set
+# CONFIG_MACH_EM_X270 is not set
+# CONFIG_MACH_ZYLONITE is not set
+# CONFIG_MACH_ARMCORE is not set
+CONFIG_PXA25x=y
+# CONFIG_PXA_KEYS is not set
+
+#
+# Boot options
+#
+
+#
+# Power management
+#
+
+#
+# Processor Type
+#
+CONFIG_CPU_32=y
+CONFIG_CPU_XSCALE=y
+CONFIG_CPU_32v5=y
+CONFIG_CPU_ABRT_EV5T=y
+CONFIG_CPU_CACHE_VIVT=y
+CONFIG_CPU_TLB_V4WBI=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+
+#
+# Processor Features
+#
+CONFIG_ARM_THUMB=y
+# CONFIG_CPU_DCACHE_DISABLE is not set
+# CONFIG_OUTER_CACHE is not set
+CONFIG_IWMMXT=y
+CONFIG_XSCALE_PMU=y
+CONFIG_SA1111=y
+CONFIG_FORCE_MAX_ZONEORDER=9
+
+#
+# Bus support
+#
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+# CONFIG_PCCARD is not set
+
+#
+# Kernel Features
+#
+# CONFIG_TICK_ONESHOT is not set
+# CONFIG_NO_HZ is not set
+# CONFIG_HIGH_RES_TIMERS is not set
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_PREEMPT=y
+CONFIG_HZ=100
+CONFIG_AEABI=y
+CONFIG_OABI_COMPAT=y
+# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+# CONFIG_SPARSEMEM_STATIC is not set
+# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set
+CONFIG_SPLIT_PTLOCK_CPUS=4096
+# CONFIG_RESOURCES_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=1
+CONFIG_BOUNCE=y
+CONFIG_VIRT_TO_BUS=y
+# CONFIG_LEDS is not set
+CONFIG_ALIGNMENT_TRAP=y
+
+#
+# Boot options
+#
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="console=ttyS0,115200n8 console=tty1 noinitrd root=/dev/mtdblock2 rootfstype=jffs2 dyntick=enable debug"
+# CONFIG_XIP_KERNEL is not set
+CONFIG_KEXEC=y
+CONFIG_ATAGS_PROC=y
+CONFIG_CPU_FREQ_PXA25x=y
+
+#
+# CPU Frequency scaling
+#
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_TABLE=y
+CONFIG_CPU_FREQ_DEBUG=y
+CONFIG_CPU_FREQ_STAT=y
+CONFIG_CPU_FREQ_STAT_DETAILS=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set
+CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+CONFIG_FPE_NWFPE=y
+# CONFIG_FPE_NWFPE_XP is not set
+# CONFIG_FPE_FASTFPE is not set
+
+#
+# Userspace binary formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_AOUT is not set
+# CONFIG_BINFMT_MISC is not set
+
+#
+# Power management options
+#
+CONFIG_PM=y
+# CONFIG_PM_LEGACY is not set
+# CONFIG_PM_DEBUG is not set
+CONFIG_SUSPEND_UP_POSSIBLE=y
+# CONFIG_SUSPEND is not set
+CONFIG_APM_EMULATION=y
+CONFIG_INPUT_APMPOWER=y
+
+#
+# Networking
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=m
+CONFIG_PACKET_MMAP=y
+CONFIG_UNIX=y
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+CONFIG_IP_PNP=y
+# CONFIG_IP_PNP_DHCP is not set
+# CONFIG_IP_PNP_BOOTP is not set
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_MROUTE is not set
+# CONFIG_ARPD is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+# CONFIG_INET_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+# CONFIG_INET_DIAG is not set
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+# CONFIG_IP_VS is not set
+# CONFIG_IPV6 is not set
+# CONFIG_INET6_XFRM_TUNNEL is not set
+# CONFIG_INET6_TUNNEL is not set
+# CONFIG_NETWORK_SECMARK is not set
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+
+#
+# Core Netfilter Configuration
+#
+# CONFIG_NETFILTER_NETLINK is not set
+# CONFIG_NF_CONNTRACK_ENABLED is not set
+# CONFIG_NF_CONNTRACK is not set
+CONFIG_NETFILTER_XTABLES=m
+# CONFIG_NETFILTER_XT_TARGET_CLASSIFY is not set
+# CONFIG_NETFILTER_XT_TARGET_DSCP is not set
+# CONFIG_NETFILTER_XT_TARGET_MARK is not set
+# CONFIG_NETFILTER_XT_TARGET_NFQUEUE is not set
+# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set
+# CONFIG_NETFILTER_XT_TARGET_TRACE is not set
+# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set
+# CONFIG_NETFILTER_XT_MATCH_COMMENT is not set
+# CONFIG_NETFILTER_XT_MATCH_DCCP is not set
+# CONFIG_NETFILTER_XT_MATCH_DSCP is not set
+# CONFIG_NETFILTER_XT_MATCH_ESP is not set
+# CONFIG_NETFILTER_XT_MATCH_LENGTH is not set
+# CONFIG_NETFILTER_XT_MATCH_LIMIT is not set
+# CONFIG_NETFILTER_XT_MATCH_MAC is not set
+# CONFIG_NETFILTER_XT_MATCH_MARK is not set
+# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set
+# CONFIG_NETFILTER_XT_MATCH_PKTTYPE is not set
+# CONFIG_NETFILTER_XT_MATCH_QUOTA is not set
+# CONFIG_NETFILTER_XT_MATCH_REALM is not set
+# CONFIG_NETFILTER_XT_MATCH_SCTP is not set
+# CONFIG_NETFILTER_XT_MATCH_STATISTIC is not set
+# CONFIG_NETFILTER_XT_MATCH_STRING is not set
+# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set
+# CONFIG_NETFILTER_XT_MATCH_TIME is not set
+# CONFIG_NETFILTER_XT_MATCH_U32 is not set
+# CONFIG_NETFILTER_XT_MATCH_HASHLIMIT is not set
+
+#
+# IP: Netfilter Configuration
+#
+CONFIG_IP_NF_QUEUE=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_IPRANGE=m
+CONFIG_IP_NF_MATCH_TOS=m
+CONFIG_IP_NF_MATCH_RECENT=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_AH=m
+CONFIG_IP_NF_MATCH_TTL=m
+CONFIG_IP_NF_MATCH_OWNER=m
+CONFIG_IP_NF_MATCH_ADDRTYPE=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_LOG=m
+CONFIG_IP_NF_TARGET_ULOG=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_TOS=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_TTL=m
+CONFIG_IP_NF_RAW=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_SCTP is not set
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_SCHED is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+CONFIG_IRDA=y
+
+#
+# IrDA protocols
+#
+CONFIG_IRLAN=y
+# CONFIG_IRNET is not set
+CONFIG_IRCOMM=y
+CONFIG_IRDA_ULTRA=y
+
+#
+# IrDA options
+#
+CONFIG_IRDA_CACHE_LAST_LSAP=y
+CONFIG_IRDA_FAST_RR=y
+CONFIG_IRDA_DEBUG=y
+
+#
+# Infrared-port device drivers
+#
+
+#
+# SIR device drivers
+#
+CONFIG_IRTTY_SIR=y
+
+#
+# Dongle support
+#
+# CONFIG_DONGLE is not set
+
+#
+# Old SIR device drivers
+#
+# CONFIG_IRPORT_SIR is not set
+
+#
+# Old Serial dongle support
+#
+
+#
+# FIR device drivers
+#
+CONFIG_PXA_FICP=y
+CONFIG_BT=m
+CONFIG_BT_L2CAP=m
+CONFIG_BT_SCO=m
+CONFIG_BT_RFCOMM=m
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=m
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+CONFIG_BT_HIDP=m
+
+#
+# Bluetooth device drivers
+#
+# CONFIG_BT_HCIBTSDIO is not set
+CONFIG_BT_HCIUART=m
+CONFIG_BT_HCIUART_H4=y
+CONFIG_BT_HCIUART_BCSP=y
+# CONFIG_BT_HCIUART_LL is not set
+# CONFIG_BT_HCIVHCI is not set
+# CONFIG_AF_RXRPC is not set
+
+#
+# Wireless
+#
+# CONFIG_CFG80211 is not set
+CONFIG_WIRELESS_EXT=y
+# CONFIG_MAC80211 is not set
+# CONFIG_IEEE80211 is not set
+# CONFIG_RFKILL is not set
+# CONFIG_NET_9P is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+# CONFIG_DEBUG_DRIVER is not set
+# CONFIG_DEBUG_DEVRES is not set
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_CONNECTOR is not set
+CONFIG_MTD=y
+CONFIG_MTD_DEBUG=y
+CONFIG_MTD_DEBUG_VERBOSE=0
+# CONFIG_MTD_CONCAT is not set
+CONFIG_MTD_PARTITIONS=y
+# CONFIG_MTD_REDBOOT_PARTS is not set
+# CONFIG_MTD_CMDLINE_PARTS is not set
+# CONFIG_MTD_AFS_PARTS is not set
+
+#
+# User Modules And Translation Layers
+#
+# CONFIG_MTD_CHAR is not set
+# CONFIG_MTD_BLKDEVS is not set
+# CONFIG_MTD_BLOCK is not set
+# CONFIG_MTD_BLOCK_RO is not set
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+# CONFIG_MTD_OOPS is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+# CONFIG_MTD_CFI is not set
+# CONFIG_MTD_JEDECPROBE is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+# CONFIG_MTD_RAM is not set
+# CONFIG_MTD_ROM is not set
+# CONFIG_MTD_ABSENT is not set
+
+#
+# Mapping drivers for chip access
+#
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+# CONFIG_MTD_SHARP_SL is not set
+# CONFIG_MTD_PLATRAM is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
+CONFIG_MTD_MTDRAM=m
+CONFIG_MTDRAM_TOTAL_SIZE=4096
+CONFIG_MTDRAM_ERASE_SIZE=128
+# CONFIG_MTD_BLOCK2MTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+# CONFIG_MTD_NAND is not set
+# CONFIG_MTD_ONENAND is not set
+
+#
+# UBI - Unsorted block images
+#
+# CONFIG_MTD_UBI is not set
+# CONFIG_PARPORT is not set
+CONFIG_BLK_DEV=y
+# CONFIG_BLK_DEV_COW_COMMON is not set
+# CONFIG_BLK_DEV_LOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_RAM is not set
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+CONFIG_MISC_DEVICES=y
+# CONFIG_EEPROM_93CX6 is not set
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+# CONFIG_SCSI is not set
+# CONFIG_SCSI_DMA is not set
+# CONFIG_SCSI_NETLINK is not set
+# CONFIG_ATA is not set
+# CONFIG_MD is not set
+CONFIG_NETDEVICES=y
+# CONFIG_NETDEVICES_MULTIQUEUE is not set
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_MACVLAN is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_TUN is not set
+# CONFIG_VETH is not set
+# CONFIG_NET_ETHERNET is not set
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
+
+#
+# Wireless LAN
+#
+CONFIG_NET_RADIO=y
+# CONFIG_NET_WIRELESS_RTNETLINK is not set
+# CONFIG_WLAN_PRE80211 is not set
+# CONFIG_WLAN_80211 is not set
+CONFIG_ACX=m
+CONFIG_ACX_MEM=y
+# CONFIG_ACX_CS is not set
+# CONFIG_WAN is not set
+CONFIG_PPP=m
+# CONFIG_PPP_MULTILINK is not set
+# CONFIG_PPP_FILTER is not set
+CONFIG_PPP_ASYNC=m
+# CONFIG_PPP_SYNC_TTY is not set
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_BSDCOMP=m
+CONFIG_PPP_MPPE=m
+# CONFIG_PPPOE is not set
+# CONFIG_PPPOL2TP is not set
+# CONFIG_SLIP is not set
+CONFIG_SLHC=m
+# CONFIG_SHAPER is not set
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_ISDN is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+
+#
+# Userland interfaces
+#
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_JOYDEV is not set
+CONFIG_INPUT_EVDEV=y
+# CONFIG_INPUT_EVBUG is not set
+# CONFIG_INPUT_POWER is not set
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+# CONFIG_KEYBOARD_ATKBD is not set
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+# CONFIG_KEYBOARD_XTKBD is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+CONFIG_KEYBOARD_GPIO=y
+CONFIG_KEYBOARD_ASIC3=y
+CONFIG_INPUT_MOUSE=y
+# CONFIG_MOUSE_PS2 is not set
+# CONFIG_MOUSE_SERIAL is not set
+# CONFIG_MOUSE_APPLETOUCH is not set
+# CONFIG_MOUSE_VSXXXAA is not set
+# CONFIG_MOUSE_GPIO is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+CONFIG_INPUT_TOUCHSCREEN=y
+# CONFIG_TOUCHSCREEN_FUJITSU is not set
+# CONFIG_TOUCHSCREEN_GUNZE is not set
+# CONFIG_TOUCHSCREEN_ELO is not set
+# CONFIG_TOUCHSCREEN_MTOUCH is not set
+# CONFIG_TOUCHSCREEN_MK712 is not set
+# CONFIG_TOUCHSCREEN_PENMOUNT is not set
+# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set
+# CONFIG_TOUCHSCREEN_TOUCHWIN is not set
+# CONFIG_TOUCHSCREEN_UCB1400 is not set
+# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set
+CONFIG_INPUT_MISC=y
+# CONFIG_INPUT_ATI_REMOTE is not set
+# CONFIG_INPUT_ATI_REMOTE2 is not set
+# CONFIG_INPUT_KEYSPAN_REMOTE is not set
+# CONFIG_INPUT_POWERMATE is not set
+# CONFIG_INPUT_YEALINK is not set
+CONFIG_INPUT_UINPUT=m
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+# CONFIG_VT_HW_CONSOLE_BINDING is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_PXA=y
+CONFIG_SERIAL_PXA_CONSOLE=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+CONFIG_LEGACY_PTYS=y
+CONFIG_LEGACY_PTY_COUNT=32
+# CONFIG_IPMI_HANDLER is not set
+# CONFIG_HW_RANDOM is not set
+# CONFIG_NVRAM is not set
+# CONFIG_R3964 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+CONFIG_I2C=m
+CONFIG_I2C_BOARDINFO=y
+# CONFIG_I2C_CHARDEV is not set
+
+#
+# I2C Algorithms
+#
+# CONFIG_I2C_ALGOBIT is not set
+# CONFIG_I2C_ALGOPCF is not set
+# CONFIG_I2C_ALGOPCA is not set
+
+#
+# I2C Hardware Bus support
+#
+# CONFIG_I2C_GPIO is not set
+CONFIG_I2C_PXA=m
+# CONFIG_I2C_PXA_SLAVE is not set
+# CONFIG_I2C_OCORES is not set
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_SIMTEC is not set
+# CONFIG_I2C_TAOS_EVM is not set
+# CONFIG_I2C_STUB is not set
+
+#
+# Miscellaneous I2C Chip support
+#
+# CONFIG_SENSORS_DS1337 is not set
+# CONFIG_SENSORS_DS1374 is not set
+# CONFIG_DS1682 is not set
+# CONFIG_SENSORS_EEPROM is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_SENSORS_PCA9539 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_SENSORS_MAX6875 is not set
+# CONFIG_SENSORS_TSL2550 is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_I2C_DEBUG_CHIP is not set
+
+#
+# SPI support
+#
+# CONFIG_SPI is not set
+# CONFIG_SPI_MASTER is not set
+CONFIG_W1=y
+
+#
+# 1-wire Bus Masters
+#
+# CONFIG_W1_MASTER_DS2482 is not set
+CONFIG_W1_MASTER_DS1WM=y
+
+#
+# 1-wire Slaves
+#
+# CONFIG_W1_SLAVE_THERM is not set
+# CONFIG_W1_SLAVE_SMEM is not set
+# CONFIG_W1_SLAVE_DS2433 is not set
+CONFIG_W1_SLAVE_DS2760=y
+CONFIG_POWER_SUPPLY=y
+# CONFIG_POWER_SUPPLY_DEBUG is not set
+CONFIG_PDA_POWER=y
+CONFIG_APM_POWER=y
+CONFIG_BATTERY_DS2760=y
+# CONFIG_HWMON is not set
+# CONFIG_WATCHDOG is not set
+
+#
+# Sonics Silicon Backplane
+#
+CONFIG_SSB_POSSIBLE=y
+# CONFIG_SSB is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_SM501 is not set
+CONFIG_HTC_ASIC3=y
+CONFIG_HTC_ASIC3_DS1WM=y
+
+#
+# Multimedia devices
+#
+# CONFIG_VIDEO_DEV is not set
+# CONFIG_DVB_CORE is not set
+CONFIG_DAB=y
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+CONFIG_VIDEO_OUTPUT_CONTROL=m
+CONFIG_FB=y
+# CONFIG_FIRMWARE_EDID is not set
+# CONFIG_FB_DDC is not set
+CONFIG_FB_CFB_FILLRECT=y
+CONFIG_FB_CFB_COPYAREA=y
+CONFIG_FB_CFB_IMAGEBLIT=y
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+# CONFIG_FB_SYS_FILLRECT is not set
+# CONFIG_FB_SYS_COPYAREA is not set
+# CONFIG_FB_SYS_IMAGEBLIT is not set
+# CONFIG_FB_SYS_FOPS is not set
+# CONFIG_FB_DEFERRED_IO is not set
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_BACKLIGHT is not set
+# CONFIG_FB_MODE_HELPERS is not set
+# CONFIG_FB_TILEBLITTING is not set
+
+#
+# Frame buffer hardware drivers
+#
+# CONFIG_FB_S1D13XXX is not set
+CONFIG_FB_PXA=y
+CONFIG_FB_PXA_LCD_QVGA=y
+# CONFIG_FB_PXA_LCD_VGA is not set
+# CONFIG_FB_PXA_OVERLAY is not set
+# CONFIG_FB_PXA_PARAMETERS is not set
+# CONFIG_FB_MBX is not set
+# CONFIG_FB_VIRTUAL is not set
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_LCD_CLASS_DEVICE=y
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+CONFIG_BACKLIGHT_CORGI=y
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
+CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y
+CONFIG_FONTS=y
+CONFIG_FONT_8x8=y
+# CONFIG_FONT_8x16 is not set
+# CONFIG_FONT_6x11 is not set
+# CONFIG_FONT_7x14 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+# CONFIG_FONT_ACORN_8x8 is not set
+# CONFIG_FONT_MINI_4x6 is not set
+# CONFIG_FONT_SUN8x16 is not set
+# CONFIG_FONT_SUN12x22 is not set
+# CONFIG_FONT_10x18 is not set
+CONFIG_LOGO=y
+# CONFIG_LOGO_LINUX_MONO is not set
+# CONFIG_LOGO_LINUX_VGA16 is not set
+# CONFIG_LOGO_LINUX_CLUT224 is not set
+CONFIG_LOGO_OHAND_CLUT224=y
+
+#
+# Sound
+#
+CONFIG_SOUND=y
+
+#
+# Advanced Linux Sound Architecture
+#
+CONFIG_SND=y
+CONFIG_SND_TIMER=y
+CONFIG_SND_PCM=y
+# CONFIG_SND_SEQUENCER is not set
+CONFIG_SND_OSSEMUL=y
+CONFIG_SND_MIXER_OSS=y
+CONFIG_SND_PCM_OSS=y
+CONFIG_SND_PCM_OSS_PLUGINS=y
+# CONFIG_SND_DYNAMIC_MINORS is not set
+CONFIG_SND_SUPPORT_OLD_API=y
+CONFIG_SND_VERBOSE_PROCFS=y
+# CONFIG_SND_VERBOSE_PRINTK is not set
+# CONFIG_SND_DEBUG is not set
+
+#
+# Generic devices
+#
+# CONFIG_SND_DUMMY is not set
+# CONFIG_SND_MTPAV is not set
+# CONFIG_SND_SERIAL_U16550 is not set
+# CONFIG_SND_MPU401 is not set
+
+#
+# ALSA ARM devices
+#
+# CONFIG_SND_PXA2XX_AC97 is not set
+
+#
+# System on Chip audio support
+#
+# CONFIG_SND_SOC is not set
+
+#
+# SoC Audio support for SuperH
+#
+
+#
+# Open Sound System
+#
+# CONFIG_SOUND_PRIME is not set
+CONFIG_HID_SUPPORT=y
+CONFIG_HID=m
+# CONFIG_HID_DEBUG is not set
+# CONFIG_HIDRAW is not set
+CONFIG_USB_SUPPORT=y
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARCH_HAS_OHCI=y
+# CONFIG_USB_ARCH_HAS_EHCI is not set
+# CONFIG_USB is not set
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
+#
+
+#
+# USB Gadget Support
+#
+CONFIG_USB_GADGET=y
+# CONFIG_USB_GADGET_DEBUG is not set
+# CONFIG_USB_GADGET_DEBUG_FILES is not set
+CONFIG_USB_GADGET_SELECTED=y
+# CONFIG_USB_GADGET_AMD5536UDC is not set
+# CONFIG_USB_GADGET_ATMEL_USBA is not set
+# CONFIG_USB_GADGET_FSL_USB2 is not set
+# CONFIG_USB_GADGET_NET2280 is not set
+CONFIG_USB_GADGET_PXA2XX=y
+CONFIG_USB_PXA2XX=y
+# CONFIG_USB_PXA2XX_SMALL is not set
+# CONFIG_USB_GADGET_M66592 is not set
+# CONFIG_USB_GADGET_PXA27X is not set
+# CONFIG_USB_GADGET_GOKU is not set
+# CONFIG_USB_GADGET_LH7A40X is not set
+# CONFIG_USB_GADGET_OMAP is not set
+# CONFIG_USB_GADGET_S3C2410 is not set
+# CONFIG_USB_GADGET_AT91 is not set
+# CONFIG_USB_GADGET_DUMMY_HCD is not set
+# CONFIG_USB_GADGET_DUALSPEED is not set
+# CONFIG_USB_ZERO is not set
+CONFIG_USB_ETH=y
+CONFIG_USB_ETH_RNDIS=y
+# CONFIG_USB_GADGETFS is not set
+# CONFIG_USB_FILE_STORAGE is not set
+# CONFIG_USB_G_SERIAL is not set
+# CONFIG_USB_MIDI_GADGET is not set
+CONFIG_MMC=y
+# CONFIG_MMC_DEBUG is not set
+CONFIG_MMC_UNSAFE_RESUME=y
+
+#
+# MMC/SD Card Drivers
+#
+CONFIG_MMC_BLOCK=y
+CONFIG_MMC_BLOCK_BOUNCE=y
+# CONFIG_SDIO_UART is not set
+
+#
+# MMC/SD Host Controller Drivers
+#
+# CONFIG_MMC_PXA is not set
+CONFIG_MMC_ASIC3=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+
+#
+# LED drivers
+#
+# CONFIG_LEDS_GPIO is not set
+CONFIG_LEDS_ASIC3=y
+
+#
+# LED Triggers
+#
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=y
+# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set
+CONFIG_RTC_LIB=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_HCTOSYS=y
+CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
+CONFIG_RTC_DEBUG=y
+
+#
+# RTC interfaces
+#
+CONFIG_RTC_INTF_SYSFS=y
+CONFIG_RTC_INTF_PROC=y
+CONFIG_RTC_INTF_DEV=y
+# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
+# CONFIG_RTC_DRV_TEST is not set
+
+#
+# I2C RTC drivers
+#
+# CONFIG_RTC_DRV_DS1307 is not set
+# CONFIG_RTC_DRV_DS1374 is not set
+# CONFIG_RTC_DRV_DS1672 is not set
+# CONFIG_RTC_DRV_MAX6900 is not set
+# CONFIG_RTC_DRV_RS5C372 is not set
+# CONFIG_RTC_DRV_ISL1208 is not set
+# CONFIG_RTC_DRV_X1205 is not set
+# CONFIG_RTC_DRV_PCF8563 is not set
+# CONFIG_RTC_DRV_PCF8583 is not set
+# CONFIG_RTC_DRV_M41T80 is not set
+
+#
+# SPI RTC drivers
+#
+
+#
+# Platform RTC drivers
+#
+# CONFIG_RTC_DRV_CMOS is not set
+# CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_STK17TA8 is not set
+# CONFIG_RTC_DRV_DS1742 is not set
+# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_M48T59 is not set
+# CONFIG_RTC_DRV_V3020 is not set
+
+#
+# on-CPU RTC drivers
+#
+CONFIG_RTC_DRV_SA1100=y
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+# CONFIG_EXT2_FS_XIP is not set
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_FS_XATTR is not set
+# CONFIG_EXT4DEV_FS is not set
+CONFIG_JBD=y
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_FS_POSIX_ACL is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_GFS2_FS is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_ROMFS_FS is not set
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_QUOTA is not set
+CONFIG_DNOTIFY=y
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_JFFS2_FS is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_SQUASHFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+# CONFIG_NFS_V3_ACL is not set
+# CONFIG_NFS_V4 is not set
+# CONFIG_NFS_DIRECTIO is not set
+# CONFIG_NFSD is not set
+CONFIG_ROOT_NFS=y
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=y
+# CONFIG_SUNRPC_BIND34 is not set
+# CONFIG_RPCSEC_GSS_KRB5 is not set
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+# CONFIG_SMB_FS is not set
+# CONFIG_CIFS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_CODEPAGE_437=y
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+CONFIG_NLS_CODEPAGE_1250=y
+CONFIG_NLS_CODEPAGE_1251=y
+# CONFIG_NLS_ASCII is not set
+CONFIG_NLS_ISO8859_1=y
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+CONFIG_NLS_UTF8=y
+# CONFIG_DLM is not set
+CONFIG_INSTRUMENTATION=y
+# CONFIG_PROFILING is not set
+# CONFIG_MARKERS is not set
+
+#
+# Kernel hacking
+#
+CONFIG_PRINTK_TIME=y
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_DEBUG_FS is not set
+# CONFIG_HEADERS_CHECK is not set
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_DEBUG_SHIRQ is not set
+CONFIG_DETECT_SOFTLOCKUP=y
+CONFIG_SCHED_DEBUG=y
+# CONFIG_SCHEDSTATS is not set
+# CONFIG_TIMER_STATS is not set
+# CONFIG_DEBUG_SLAB is not set
+CONFIG_DEBUG_PREEMPT=y
+# CONFIG_DEBUG_RT_MUTEXES is not set
+# CONFIG_RT_MUTEX_TESTER is not set
+# CONFIG_DEBUG_SPINLOCK is not set
+CONFIG_DEBUG_MUTEXES=y
+# CONFIG_DEBUG_LOCK_ALLOC is not set
+# CONFIG_PROVE_LOCKING is not set
+# CONFIG_LOCK_STAT is not set
+# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
+# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
+# CONFIG_DEBUG_KOBJECT is not set
+CONFIG_DEBUG_BUGVERBOSE=y
+# CONFIG_DEBUG_INFO is not set
+CONFIG_DEBUG_VM=y
+# CONFIG_DEBUG_LIST is not set
+# CONFIG_DEBUG_SG is not set
+CONFIG_FRAME_POINTER=y
+CONFIG_FORCED_INLINING=y
+# CONFIG_BOOT_PRINTK_DELAY is not set
+# CONFIG_RCU_TORTURE_TEST is not set
+# CONFIG_FAULT_INJECTION is not set
+# CONFIG_SAMPLES is not set
+CONFIG_DEBUG_USER=y
+CONFIG_DEBUG_ERRORS=y
+CONFIG_DEBUG_LL=y
+# CONFIG_DEBUG_ICEDCC is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+CONFIG_CRYPTO=y
+CONFIG_CRYPTO_ALGAPI=y
+CONFIG_CRYPTO_BLKCIPHER=m
+CONFIG_CRYPTO_HASH=y
+CONFIG_CRYPTO_MANAGER=y
+CONFIG_CRYPTO_HMAC=y
+# CONFIG_CRYPTO_XCBC is not set
+# CONFIG_CRYPTO_NULL is not set
+# CONFIG_CRYPTO_MD4 is not set
+CONFIG_CRYPTO_MD5=y
+CONFIG_CRYPTO_SHA1=y
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_WP512 is not set
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_GF128MUL is not set
+CONFIG_CRYPTO_ECB=m
+# CONFIG_CRYPTO_CBC is not set
+CONFIG_CRYPTO_PCBC=m
+# CONFIG_CRYPTO_LRW is not set
+# CONFIG_CRYPTO_XTS is not set
+# CONFIG_CRYPTO_CRYPTD is not set
+CONFIG_CRYPTO_DES=y
+# CONFIG_CRYPTO_FCRYPT is not set
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_TWOFISH is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_AES is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+# CONFIG_CRYPTO_TEA is not set
+CONFIG_CRYPTO_ARC4=m
+# CONFIG_CRYPTO_KHAZAD is not set
+# CONFIG_CRYPTO_ANUBIS is not set
+# CONFIG_CRYPTO_SEED is not set
+CONFIG_CRYPTO_DEFLATE=y
+# CONFIG_CRYPTO_LZO is not set
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_TEST is not set
+# CONFIG_CRYPTO_AUTHENC is not set
+CONFIG_CRYPTO_HW=y
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+CONFIG_CRC_CCITT=y
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
+# CONFIG_LIBCRC32C is not set
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
diff --git a/recipes/linux/linux-rp-2.6.26/defconfig-hx2000 b/recipes/linux/linux-rp-2.6.26/defconfig-hx2000
new file mode 100644
index 0000000000..92fe72c2c0
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.26/defconfig-hx2000
@@ -0,0 +1,1733 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.24-rc8
+# Sun Jan 20 18:10:53 2008
+#
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+CONFIG_GENERIC_GPIO=y
+CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_MMU=y
+# CONFIG_NO_IOPORT is not set
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+# CONFIG_ARCH_HAS_ILOG2_U32 is not set
+# CONFIG_ARCH_HAS_ILOG2_U64 is not set
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_ZONE_DMA=y
+CONFIG_ARCH_MTD_XIP=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_LOCK_KERNEL=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+# CONFIG_POSIX_MQUEUE is not set
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_BSD_PROCESS_ACCT_V3=y
+# CONFIG_TASKSTATS is not set
+# CONFIG_USER_NS is not set
+# CONFIG_PID_NS is not set
+# CONFIG_AUDIT is not set
+# CONFIG_IKCONFIG is not set
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_CGROUPS is not set
+CONFIG_FAIR_GROUP_SCHED=y
+CONFIG_FAIR_USER_SCHED=y
+# CONFIG_FAIR_CGROUP_SCHED is not set
+CONFIG_SYSFS_DEPRECATED=y
+# CONFIG_RELAY is not set
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+CONFIG_EMBEDDED=y
+CONFIG_UID16=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_ANON_INODES=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLAB=y
+# CONFIG_SLUB is not set
+# CONFIG_SLOB is not set
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+# CONFIG_TINY_SHMEM is not set
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+CONFIG_BLOCK=y
+# CONFIG_LBD is not set
+# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_LSF is not set
+# CONFIG_BLK_DEV_BSG is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=m
+CONFIG_IOSCHED_CFQ=m
+CONFIG_DEFAULT_AS=y
+# CONFIG_DEFAULT_DEADLINE is not set
+# CONFIG_DEFAULT_CFQ is not set
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="anticipatory"
+
+#
+# System Type
+#
+# CONFIG_ARCH_AAEC2000 is not set
+# CONFIG_ARCH_INTEGRATOR is not set
+# CONFIG_ARCH_REALVIEW is not set
+# CONFIG_ARCH_VERSATILE is not set
+# CONFIG_ARCH_AT91 is not set
+# CONFIG_ARCH_CLPS7500 is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_CO285 is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE is not set
+# CONFIG_ARCH_NETX is not set
+# CONFIG_ARCH_H720X is not set
+# CONFIG_ARCH_IMX is not set
+# CONFIG_ARCH_IOP13XX is not set
+# CONFIG_ARCH_IOP32X is not set
+# CONFIG_ARCH_IOP33X is not set
+# CONFIG_ARCH_IXP23XX is not set
+# CONFIG_ARCH_IXP2000 is not set
+# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_L7200 is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_NS9XXX is not set
+# CONFIG_ARCH_MXC is not set
+# CONFIG_ARCH_PNX4008 is not set
+CONFIG_ARCH_PXA=y
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP is not set
+
+#
+# Intel PXA2xx/PXA3xx Implementations
+#
+# CONFIG_ARCH_LUBBOCK is not set
+# CONFIG_MACH_LOGICPD_PXA270 is not set
+# CONFIG_MACH_MAINSTONE is not set
+# CONFIG_ARCH_PXA_IDP is not set
+# CONFIG_PXA_SHARPSL is not set
+# CONFIG_MACH_TRIZEPS4 is not set
+CONFIG_MACH_HX2750=y
+# CONFIG_MACH_EM_X270 is not set
+# CONFIG_MACH_ZYLONITE is not set
+# CONFIG_MACH_ARMCORE is not set
+CONFIG_PXA27x=y
+CONFIG_PXA_SSP=y
+CONFIG_PXA_KEYS=y
+
+#
+# Boot options
+#
+
+#
+# Power management
+#
+
+#
+# Processor Type
+#
+CONFIG_CPU_32=y
+CONFIG_CPU_XSCALE=y
+CONFIG_CPU_32v5=y
+CONFIG_CPU_ABRT_EV5T=y
+CONFIG_CPU_CACHE_VIVT=y
+CONFIG_CPU_TLB_V4WBI=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+
+#
+# Processor Features
+#
+CONFIG_ARM_THUMB=y
+# CONFIG_CPU_DCACHE_DISABLE is not set
+# CONFIG_OUTER_CACHE is not set
+CONFIG_IWMMXT=y
+CONFIG_XSCALE_PMU=y
+
+#
+# Bus support
+#
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+CONFIG_PCCARD=y
+# CONFIG_PCMCIA_DEBUG is not set
+CONFIG_PCMCIA=y
+CONFIG_PCMCIA_LOAD_CIS=y
+CONFIG_PCMCIA_IOCTL=y
+
+#
+# PC-card bridges
+#
+CONFIG_PCMCIA_PXA2XX=y
+
+#
+# Kernel Features
+#
+# CONFIG_TICK_ONESHOT is not set
+# CONFIG_NO_HZ is not set
+# CONFIG_HIGH_RES_TIMERS is not set
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_PREEMPT=y
+CONFIG_HZ=100
+CONFIG_AEABI=y
+CONFIG_OABI_COMPAT=y
+# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+# CONFIG_SPARSEMEM_STATIC is not set
+# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set
+CONFIG_SPLIT_PTLOCK_CPUS=4096
+# CONFIG_RESOURCES_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=1
+CONFIG_BOUNCE=y
+CONFIG_VIRT_TO_BUS=y
+CONFIG_ALIGNMENT_TRAP=y
+
+#
+# Boot options
+#
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="console=ttyS0,115200n8 console=tty1 noinitrd root=/dev/mtdblock2 rootfstype=jffs2 dyntick=enable debug"
+# CONFIG_XIP_KERNEL is not set
+CONFIG_KEXEC=y
+CONFIG_ATAGS_PROC=y
+
+#
+# CPU Frequency scaling
+#
+# CONFIG_CPU_FREQ is not set
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+CONFIG_FPE_NWFPE=y
+# CONFIG_FPE_NWFPE_XP is not set
+# CONFIG_FPE_FASTFPE is not set
+
+#
+# Userspace binary formats
+#
+CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_AOUT=m
+CONFIG_BINFMT_MISC=m
+
+#
+# Power management options
+#
+CONFIG_PM=y
+# CONFIG_PM_LEGACY is not set
+# CONFIG_PM_DEBUG is not set
+CONFIG_PM_SLEEP=y
+CONFIG_SUSPEND_UP_POSSIBLE=y
+CONFIG_SUSPEND=y
+CONFIG_APM_EMULATION=y
+
+#
+# Networking
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=m
+CONFIG_PACKET_MMAP=y
+CONFIG_UNIX=y
+CONFIG_XFRM=y
+# CONFIG_XFRM_USER is not set
+# CONFIG_XFRM_SUB_POLICY is not set
+# CONFIG_XFRM_MIGRATE is not set
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+# CONFIG_IP_PNP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_ARPD is not set
+CONFIG_SYN_COOKIES=y
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+CONFIG_INET_TUNNEL=m
+CONFIG_INET_XFRM_MODE_TRANSPORT=m
+CONFIG_INET_XFRM_MODE_TUNNEL=m
+CONFIG_INET_XFRM_MODE_BEET=m
+# CONFIG_INET_LRO is not set
+CONFIG_INET_DIAG=m
+CONFIG_INET_TCP_DIAG=m
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+# CONFIG_IP_VS is not set
+CONFIG_IPV6=m
+# CONFIG_IPV6_PRIVACY is not set
+# CONFIG_IPV6_ROUTER_PREF is not set
+# CONFIG_IPV6_OPTIMISTIC_DAD is not set
+CONFIG_INET6_AH=m
+CONFIG_INET6_ESP=m
+CONFIG_INET6_IPCOMP=m
+# CONFIG_IPV6_MIP6 is not set
+CONFIG_INET6_XFRM_TUNNEL=m
+CONFIG_INET6_TUNNEL=m
+CONFIG_INET6_XFRM_MODE_TRANSPORT=m
+CONFIG_INET6_XFRM_MODE_TUNNEL=m
+CONFIG_INET6_XFRM_MODE_BEET=m
+# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set
+CONFIG_IPV6_SIT=m
+CONFIG_IPV6_TUNNEL=m
+# CONFIG_IPV6_MULTIPLE_TABLES is not set
+# CONFIG_NETWORK_SECMARK is not set
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+
+#
+# Core Netfilter Configuration
+#
+# CONFIG_NETFILTER_NETLINK is not set
+# CONFIG_NF_CONNTRACK_ENABLED is not set
+# CONFIG_NF_CONNTRACK is not set
+CONFIG_NETFILTER_XTABLES=m
+# CONFIG_NETFILTER_XT_TARGET_CLASSIFY is not set
+# CONFIG_NETFILTER_XT_TARGET_DSCP is not set
+# CONFIG_NETFILTER_XT_TARGET_MARK is not set
+# CONFIG_NETFILTER_XT_TARGET_NFQUEUE is not set
+# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set
+# CONFIG_NETFILTER_XT_TARGET_TRACE is not set
+# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set
+# CONFIG_NETFILTER_XT_MATCH_COMMENT is not set
+# CONFIG_NETFILTER_XT_MATCH_DCCP is not set
+# CONFIG_NETFILTER_XT_MATCH_DSCP is not set
+# CONFIG_NETFILTER_XT_MATCH_ESP is not set
+# CONFIG_NETFILTER_XT_MATCH_LENGTH is not set
+# CONFIG_NETFILTER_XT_MATCH_LIMIT is not set
+# CONFIG_NETFILTER_XT_MATCH_MAC is not set
+# CONFIG_NETFILTER_XT_MATCH_MARK is not set
+# CONFIG_NETFILTER_XT_MATCH_POLICY is not set
+# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set
+# CONFIG_NETFILTER_XT_MATCH_PKTTYPE is not set
+# CONFIG_NETFILTER_XT_MATCH_QUOTA is not set
+# CONFIG_NETFILTER_XT_MATCH_REALM is not set
+# CONFIG_NETFILTER_XT_MATCH_SCTP is not set
+# CONFIG_NETFILTER_XT_MATCH_STATISTIC is not set
+# CONFIG_NETFILTER_XT_MATCH_STRING is not set
+# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set
+# CONFIG_NETFILTER_XT_MATCH_TIME is not set
+# CONFIG_NETFILTER_XT_MATCH_U32 is not set
+# CONFIG_NETFILTER_XT_MATCH_HASHLIMIT is not set
+
+#
+# IP: Netfilter Configuration
+#
+CONFIG_IP_NF_QUEUE=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_IPRANGE=m
+CONFIG_IP_NF_MATCH_TOS=m
+CONFIG_IP_NF_MATCH_RECENT=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_AH=m
+CONFIG_IP_NF_MATCH_TTL=m
+CONFIG_IP_NF_MATCH_OWNER=m
+CONFIG_IP_NF_MATCH_ADDRTYPE=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_LOG=m
+CONFIG_IP_NF_TARGET_ULOG=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_TOS=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_TTL=m
+CONFIG_IP_NF_RAW=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+
+#
+# IPv6: Netfilter Configuration (EXPERIMENTAL)
+#
+# CONFIG_IP6_NF_QUEUE is not set
+# CONFIG_IP6_NF_IPTABLES is not set
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_SCTP is not set
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_SCHED is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+CONFIG_IRDA=m
+
+#
+# IrDA protocols
+#
+CONFIG_IRLAN=m
+CONFIG_IRNET=m
+CONFIG_IRCOMM=m
+# CONFIG_IRDA_ULTRA is not set
+
+#
+# IrDA options
+#
+# CONFIG_IRDA_CACHE_LAST_LSAP is not set
+# CONFIG_IRDA_FAST_RR is not set
+# CONFIG_IRDA_DEBUG is not set
+
+#
+# Infrared-port device drivers
+#
+
+#
+# SIR device drivers
+#
+# CONFIG_IRTTY_SIR is not set
+
+#
+# Dongle support
+#
+# CONFIG_KINGSUN_DONGLE is not set
+# CONFIG_KSDAZZLE_DONGLE is not set
+# CONFIG_KS959_DONGLE is not set
+
+#
+# Old SIR device drivers
+#
+# CONFIG_IRPORT_SIR is not set
+
+#
+# Old Serial dongle support
+#
+
+#
+# FIR device drivers
+#
+# CONFIG_USB_IRDA is not set
+# CONFIG_SIGMATEL_FIR is not set
+CONFIG_PXA_FICP=m
+# CONFIG_MCS_FIR is not set
+CONFIG_BT=m
+CONFIG_BT_L2CAP=m
+CONFIG_BT_SCO=m
+CONFIG_BT_RFCOMM=m
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=m
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+CONFIG_BT_HIDP=m
+
+#
+# Bluetooth device drivers
+#
+CONFIG_BT_HCIUSB=m
+# CONFIG_BT_HCIUSB_SCO is not set
+# CONFIG_BT_HCIBTSDIO is not set
+CONFIG_BT_HCIUART=m
+CONFIG_BT_HCIUART_H4=y
+CONFIG_BT_HCIUART_BCSP=y
+# CONFIG_BT_HCIUART_LL is not set
+CONFIG_BT_HCIBCM203X=m
+CONFIG_BT_HCIBPA10X=m
+CONFIG_BT_HCIBFUSB=m
+CONFIG_BT_HCIDTL1=m
+CONFIG_BT_HCIBT3C=m
+CONFIG_BT_HCIBLUECARD=m
+CONFIG_BT_HCIBTUART=m
+CONFIG_BT_HCIVHCI=m
+# CONFIG_AF_RXRPC is not set
+
+#
+# Wireless
+#
+# CONFIG_CFG80211 is not set
+CONFIG_WIRELESS_EXT=y
+# CONFIG_MAC80211 is not set
+CONFIG_IEEE80211=m
+# CONFIG_IEEE80211_DEBUG is not set
+CONFIG_IEEE80211_CRYPT_WEP=m
+CONFIG_IEEE80211_CRYPT_CCMP=m
+CONFIG_IEEE80211_CRYPT_TKIP=m
+CONFIG_IEEE80211_SOFTMAC=m
+# CONFIG_IEEE80211_SOFTMAC_DEBUG is not set
+# CONFIG_RFKILL is not set
+# CONFIG_NET_9P is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+# CONFIG_DEBUG_DRIVER is not set
+# CONFIG_DEBUG_DEVRES is not set
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_CONNECTOR is not set
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+# CONFIG_MTD_CONCAT is not set
+CONFIG_MTD_PARTITIONS=y
+# CONFIG_MTD_REDBOOT_PARTS is not set
+CONFIG_MTD_CMDLINE_PARTS=y
+# CONFIG_MTD_AFS_PARTS is not set
+
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+# CONFIG_MTD_OOPS is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+CONFIG_MTD_CFI=y
+# CONFIG_MTD_JEDECPROBE is not set
+CONFIG_MTD_GEN_PROBE=y
+CONFIG_MTD_CFI_ADV_OPTIONS=y
+CONFIG_MTD_CFI_NOSWAP=y
+# CONFIG_MTD_CFI_BE_BYTE_SWAP is not set
+# CONFIG_MTD_CFI_LE_BYTE_SWAP is not set
+CONFIG_MTD_CFI_GEOMETRY=y
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+# CONFIG_MTD_OTP is not set
+CONFIG_MTD_CFI_INTELEXT=y
+# CONFIG_MTD_CFI_AMDSTD is not set
+# CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
+# CONFIG_MTD_RAM is not set
+# CONFIG_MTD_ROM is not set
+# CONFIG_MTD_ABSENT is not set
+# CONFIG_MTD_XIP is not set
+
+#
+# Mapping drivers for chip access
+#
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+# CONFIG_MTD_PHYSMAP is not set
+# CONFIG_MTD_PXA2XX is not set
+# CONFIG_MTD_ARM_INTEGRATOR is not set
+# CONFIG_MTD_SHARP_SL is not set
+# CONFIG_MTD_PLATRAM is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLOCK2MTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+# CONFIG_MTD_NAND is not set
+# CONFIG_MTD_ONENAND is not set
+
+#
+# UBI - Unsorted block images
+#
+# CONFIG_MTD_UBI is not set
+# CONFIG_PARPORT is not set
+CONFIG_BLK_DEV=y
+# CONFIG_BLK_DEV_COW_COMMON is not set
+CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_CRYPTOLOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_UB is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=8192
+CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+CONFIG_MISC_DEVICES=y
+# CONFIG_EEPROM_93CX6 is not set
+CONFIG_IDE=y
+CONFIG_IDE_MAX_HWIFS=4
+CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_IDE_SATA is not set
+CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_IDEDISK_MULTI_MODE is not set
+CONFIG_BLK_DEV_IDECS=y
+# CONFIG_BLK_DEV_IDECD is not set
+# CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
+# CONFIG_BLK_DEV_IDESCSI is not set
+# CONFIG_IDE_TASK_IOCTL is not set
+CONFIG_IDE_PROC_FS=y
+
+#
+# IDE chipset support/bugfixes
+#
+CONFIG_IDE_GENERIC=y
+# CONFIG_BLK_DEV_PLATFORM is not set
+# CONFIG_IDE_ARM is not set
+# CONFIG_BLK_DEV_IDEDMA is not set
+CONFIG_IDE_ARCH_OBSOLETE_INIT=y
+# CONFIG_BLK_DEV_HD is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+CONFIG_SCSI=m
+CONFIG_SCSI_DMA=y
+# CONFIG_SCSI_TGT is not set
+# CONFIG_SCSI_NETLINK is not set
+CONFIG_SCSI_PROC_FS=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=m
+CONFIG_CHR_DEV_ST=m
+CONFIG_CHR_DEV_OSST=m
+CONFIG_BLK_DEV_SR=m
+# CONFIG_BLK_DEV_SR_VENDOR is not set
+CONFIG_CHR_DEV_SG=m
+# CONFIG_CHR_DEV_SCH is not set
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+CONFIG_SCSI_MULTI_LUN=y
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
+# CONFIG_SCSI_SCAN_ASYNC is not set
+CONFIG_SCSI_WAIT_SCAN=m
+
+#
+# SCSI Transports
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+# CONFIG_SCSI_ISCSI_ATTRS is not set
+# CONFIG_SCSI_SAS_LIBSAS is not set
+# CONFIG_SCSI_SRP_ATTRS is not set
+CONFIG_SCSI_LOWLEVEL=y
+# CONFIG_ISCSI_TCP is not set
+# CONFIG_SCSI_DEBUG is not set
+# CONFIG_SCSI_LOWLEVEL_PCMCIA is not set
+# CONFIG_ATA is not set
+CONFIG_MD=y
+# CONFIG_BLK_DEV_MD is not set
+CONFIG_BLK_DEV_DM=m
+# CONFIG_DM_DEBUG is not set
+CONFIG_DM_CRYPT=m
+CONFIG_DM_SNAPSHOT=m
+CONFIG_DM_MIRROR=m
+CONFIG_DM_ZERO=m
+CONFIG_DM_MULTIPATH=m
+CONFIG_DM_MULTIPATH_EMC=m
+# CONFIG_DM_MULTIPATH_RDAC is not set
+# CONFIG_DM_MULTIPATH_HP is not set
+# CONFIG_DM_DELAY is not set
+# CONFIG_DM_UEVENT is not set
+CONFIG_NETDEVICES=y
+# CONFIG_NETDEVICES_MULTIQUEUE is not set
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_MACVLAN is not set
+# CONFIG_EQUALIZER is not set
+CONFIG_TUN=m
+# CONFIG_VETH is not set
+# CONFIG_PHYLIB is not set
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=m
+# CONFIG_AX88796 is not set
+# CONFIG_SMC91X is not set
+# CONFIG_DM9000 is not set
+# CONFIG_SMC911X is not set
+# CONFIG_IBM_NEW_EMAC_ZMII is not set
+# CONFIG_IBM_NEW_EMAC_RGMII is not set
+# CONFIG_IBM_NEW_EMAC_TAH is not set
+# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
+# CONFIG_B44 is not set
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
+
+#
+# Wireless LAN
+#
+# CONFIG_WLAN_PRE80211 is not set
+CONFIG_WLAN_80211=y
+# CONFIG_PCMCIA_RAYCS is not set
+# CONFIG_LIBERTAS is not set
+CONFIG_HERMES=m
+CONFIG_PCMCIA_HERMES=m
+CONFIG_PCMCIA_SPECTRUM=m
+# CONFIG_ATMEL is not set
+CONFIG_AIRO_CS=m
+# CONFIG_PCMCIA_WL3501 is not set
+# CONFIG_USB_ZD1201 is not set
+CONFIG_HOSTAP=m
+CONFIG_HOSTAP_FIRMWARE=y
+# CONFIG_HOSTAP_FIRMWARE_NVRAM is not set
+CONFIG_HOSTAP_CS=m
+# CONFIG_ZD1211RW is not set
+
+#
+# USB Network Adapters
+#
+CONFIG_USB_CATC=m
+CONFIG_USB_KAWETH=m
+CONFIG_USB_PEGASUS=m
+CONFIG_USB_RTL8150=m
+CONFIG_USB_USBNET=m
+CONFIG_USB_NET_AX8817X=m
+CONFIG_USB_NET_CDCETHER=m
+CONFIG_USB_NET_DM9601=m
+# CONFIG_USB_NET_GL620A is not set
+CONFIG_USB_NET_NET1080=m
+# CONFIG_USB_NET_PLUSB is not set
+# CONFIG_USB_NET_MCS7830 is not set
+# CONFIG_USB_NET_RNDIS_HOST is not set
+# CONFIG_USB_NET_CDC_SUBSET is not set
+CONFIG_USB_NET_ZAURUS=m
+CONFIG_NET_PCMCIA=y
+# CONFIG_PCMCIA_3C589 is not set
+# CONFIG_PCMCIA_3C574 is not set
+# CONFIG_PCMCIA_FMVJ18X is not set
+CONFIG_PCMCIA_PCNET=m
+# CONFIG_PCMCIA_NMCLAN is not set
+# CONFIG_PCMCIA_SMC91C92 is not set
+# CONFIG_PCMCIA_XIRC2PS is not set
+# CONFIG_PCMCIA_AXNET is not set
+# CONFIG_WAN is not set
+CONFIG_PPP=m
+# CONFIG_PPP_MULTILINK is not set
+# CONFIG_PPP_FILTER is not set
+CONFIG_PPP_ASYNC=m
+# CONFIG_PPP_SYNC_TTY is not set
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_BSDCOMP=m
+# CONFIG_PPP_MPPE is not set
+# CONFIG_PPPOE is not set
+# CONFIG_PPPOL2TP is not set
+# CONFIG_SLIP is not set
+CONFIG_SLHC=m
+# CONFIG_SHAPER is not set
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_ISDN is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=m
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=240
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=320
+# CONFIG_INPUT_JOYDEV is not set
+CONFIG_INPUT_EVDEV=y
+# CONFIG_INPUT_EVBUG is not set
+CONFIG_INPUT_POWER=y
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+# CONFIG_KEYBOARD_ATKBD is not set
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+# CONFIG_KEYBOARD_XTKBD is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+CONFIG_KEYBOARD_PXA27x=m
+# CONFIG_KEYBOARD_GPIO is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+CONFIG_INPUT_TOUCHSCREEN=y
+# CONFIG_TOUCHSCREEN_FUJITSU is not set
+# CONFIG_TOUCHSCREEN_GUNZE is not set
+# CONFIG_TOUCHSCREEN_ELO is not set
+# CONFIG_TOUCHSCREEN_MTOUCH is not set
+# CONFIG_TOUCHSCREEN_MK712 is not set
+# CONFIG_TOUCHSCREEN_PENMOUNT is not set
+# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set
+# CONFIG_TOUCHSCREEN_TOUCHWIN is not set
+# CONFIG_TOUCHSCREEN_UCB1400 is not set
+# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set
+CONFIG_TOUCHSCREEN_TSC2101=y
+CONFIG_INPUT_MISC=y
+# CONFIG_INPUT_ATI_REMOTE is not set
+# CONFIG_INPUT_ATI_REMOTE2 is not set
+# CONFIG_INPUT_KEYSPAN_REMOTE is not set
+# CONFIG_INPUT_POWERMATE is not set
+# CONFIG_INPUT_YEALINK is not set
+CONFIG_INPUT_UINPUT=m
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+# CONFIG_VT_HW_CONSOLE_BINDING is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+CONFIG_SERIAL_8250=m
+CONFIG_SERIAL_8250_CS=m
+CONFIG_SERIAL_8250_NR_UARTS=4
+CONFIG_SERIAL_8250_RUNTIME_UARTS=4
+# CONFIG_SERIAL_8250_EXTENDED is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_PXA=y
+CONFIG_SERIAL_PXA_CONSOLE=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_IPMI_HANDLER is not set
+CONFIG_HW_RANDOM=m
+# CONFIG_NVRAM is not set
+# CONFIG_R3964 is not set
+
+#
+# PCMCIA character devices
+#
+# CONFIG_SYNCLINK_CS is not set
+# CONFIG_CARDMAN_4000 is not set
+# CONFIG_CARDMAN_4040 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+# CONFIG_I2C_CHARDEV is not set
+
+#
+# I2C Algorithms
+#
+CONFIG_I2C_ALGOBIT=y
+# CONFIG_I2C_ALGOPCF is not set
+# CONFIG_I2C_ALGOPCA is not set
+
+#
+# I2C Hardware Bus support
+#
+# CONFIG_I2C_GPIO is not set
+CONFIG_I2C_PXA=y
+# CONFIG_I2C_PXA_SLAVE is not set
+# CONFIG_I2C_OCORES is not set
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_SIMTEC is not set
+# CONFIG_I2C_TAOS_EVM is not set
+# CONFIG_I2C_STUB is not set
+# CONFIG_I2C_TINY_USB is not set
+
+#
+# Miscellaneous I2C Chip support
+#
+# CONFIG_SENSORS_DS1337 is not set
+# CONFIG_SENSORS_DS1374 is not set
+# CONFIG_DS1682 is not set
+# CONFIG_SENSORS_EEPROM is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_SENSORS_PCA9539 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_SENSORS_MAX6875 is not set
+# CONFIG_SENSORS_TSL2550 is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_I2C_DEBUG_CHIP is not set
+
+#
+# SPI support
+#
+# CONFIG_SPI is not set
+# CONFIG_SPI_MASTER is not set
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+# CONFIG_WATCHDOG is not set
+
+#
+# Sonics Silicon Backplane
+#
+CONFIG_SSB_POSSIBLE=y
+# CONFIG_SSB is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_SM501 is not set
+CONFIG_MFD_TSC2101=y
+# CONFIG_HTC_ASIC3 is not set
+# CONFIG_HTC_ASIC3_DS1WM is not set
+
+#
+# Multimedia devices
+#
+CONFIG_VIDEO_DEV=m
+CONFIG_VIDEO_V4L1=y
+CONFIG_VIDEO_V4L1_COMPAT=y
+CONFIG_VIDEO_V4L2=y
+CONFIG_VIDEO_CAPTURE_DRIVERS=y
+# CONFIG_VIDEO_ADV_DEBUG is not set
+CONFIG_VIDEO_HELPER_CHIPS_AUTO=y
+# CONFIG_VIDEO_VIVI is not set
+# CONFIG_VIDEO_CPIA is not set
+# CONFIG_VIDEO_CPIA2 is not set
+# CONFIG_VIDEO_SAA5246A is not set
+# CONFIG_VIDEO_SAA5249 is not set
+# CONFIG_TUNER_3036 is not set
+CONFIG_V4L_USB_DRIVERS=y
+# CONFIG_VIDEO_PVRUSB2 is not set
+# CONFIG_VIDEO_EM28XX is not set
+# CONFIG_VIDEO_USBVISION is not set
+CONFIG_VIDEO_USBVIDEO=m
+CONFIG_USB_VICAM=m
+CONFIG_USB_IBMCAM=m
+CONFIG_USB_KONICAWC=m
+# CONFIG_USB_QUICKCAM_MESSENGER is not set
+# CONFIG_USB_ET61X251 is not set
+# CONFIG_VIDEO_OVCAMCHIP is not set
+# CONFIG_USB_W9968CF is not set
+CONFIG_USB_OV511=m
+CONFIG_USB_SE401=m
+CONFIG_USB_SN9C102=m
+CONFIG_USB_STV680=m
+# CONFIG_USB_ZC0301 is not set
+# CONFIG_USB_PWC is not set
+# CONFIG_USB_ZR364XX is not set
+CONFIG_RADIO_ADAPTERS=y
+CONFIG_USB_DSBR=m
+# CONFIG_DVB_CORE is not set
+CONFIG_DAB=y
+CONFIG_USB_DABUSB=m
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+CONFIG_VIDEO_OUTPUT_CONTROL=m
+CONFIG_FB=y
+# CONFIG_FIRMWARE_EDID is not set
+# CONFIG_FB_DDC is not set
+CONFIG_FB_CFB_FILLRECT=y
+CONFIG_FB_CFB_COPYAREA=y
+CONFIG_FB_CFB_IMAGEBLIT=y
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+# CONFIG_FB_SYS_FILLRECT is not set
+# CONFIG_FB_SYS_COPYAREA is not set
+# CONFIG_FB_SYS_IMAGEBLIT is not set
+# CONFIG_FB_SYS_FOPS is not set
+# CONFIG_FB_DEFERRED_IO is not set
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_BACKLIGHT is not set
+# CONFIG_FB_MODE_HELPERS is not set
+# CONFIG_FB_TILEBLITTING is not set
+
+#
+# Frame buffer hardware drivers
+#
+# CONFIG_FB_S1D13XXX is not set
+CONFIG_FB_PXA=y
+CONFIG_FB_PXA_LCD_QVGA=y
+# CONFIG_FB_PXA_LCD_VGA is not set
+CONFIG_FB_PXA_OVERLAY=y
+# CONFIG_FB_PXA_PARAMETERS is not set
+# CONFIG_FB_MBX is not set
+# CONFIG_FB_VIRTUAL is not set
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+# CONFIG_LCD_CLASS_DEVICE is not set
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+CONFIG_BACKLIGHT_CORGI=y
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
+CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y
+CONFIG_FONTS=y
+# CONFIG_FONT_8x8 is not set
+# CONFIG_FONT_8x16 is not set
+# CONFIG_FONT_6x11 is not set
+# CONFIG_FONT_7x14 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+# CONFIG_FONT_ACORN_8x8 is not set
+CONFIG_FONT_MINI_4x6=y
+# CONFIG_FONT_SUN8x16 is not set
+# CONFIG_FONT_SUN12x22 is not set
+# CONFIG_FONT_10x18 is not set
+CONFIG_LOGO=y
+# CONFIG_LOGO_LINUX_MONO is not set
+# CONFIG_LOGO_LINUX_VGA16 is not set
+# CONFIG_LOGO_LINUX_CLUT224 is not set
+CONFIG_LOGO_OHAND_CLUT224=y
+
+#
+# Sound
+#
+CONFIG_SOUND=m
+
+#
+# Advanced Linux Sound Architecture
+#
+CONFIG_SND=m
+CONFIG_SND_TIMER=m
+CONFIG_SND_PCM=m
+CONFIG_SND_HWDEP=m
+CONFIG_SND_RAWMIDI=m
+CONFIG_SND_SEQUENCER=m
+# CONFIG_SND_SEQ_DUMMY is not set
+CONFIG_SND_OSSEMUL=y
+CONFIG_SND_MIXER_OSS=m
+CONFIG_SND_PCM_OSS=m
+CONFIG_SND_PCM_OSS_PLUGINS=y
+# CONFIG_SND_SEQUENCER_OSS is not set
+# CONFIG_SND_DYNAMIC_MINORS is not set
+CONFIG_SND_SUPPORT_OLD_API=y
+CONFIG_SND_VERBOSE_PROCFS=y
+# CONFIG_SND_VERBOSE_PRINTK is not set
+# CONFIG_SND_DEBUG is not set
+
+#
+# Generic devices
+#
+CONFIG_SND_AC97_CODEC=m
+# CONFIG_SND_DUMMY is not set
+# CONFIG_SND_VIRMIDI is not set
+# CONFIG_SND_MTPAV is not set
+# CONFIG_SND_SERIAL_U16550 is not set
+# CONFIG_SND_MPU401 is not set
+
+#
+# ALSA ARM devices
+#
+CONFIG_SND_PXA2XX_PCM=m
+CONFIG_SND_PXA2XX_AC97=m
+
+#
+# USB devices
+#
+CONFIG_SND_USB_AUDIO=m
+# CONFIG_SND_USB_CAIAQ is not set
+
+#
+# PCMCIA devices
+#
+# CONFIG_SND_VXPOCKET is not set
+# CONFIG_SND_PDAUDIOCF is not set
+
+#
+# System on Chip audio support
+#
+CONFIG_SND_SOC=m
+CONFIG_SND_PXA2XX_SOC=m
+
+#
+# SoC Audio support for SuperH
+#
+
+#
+# Open Sound System
+#
+# CONFIG_SOUND_PRIME is not set
+CONFIG_AC97_BUS=m
+CONFIG_HID_SUPPORT=y
+CONFIG_HID=m
+# CONFIG_HID_DEBUG is not set
+# CONFIG_HIDRAW is not set
+
+#
+# USB Input Devices
+#
+CONFIG_USB_HID=m
+# CONFIG_USB_HIDINPUT_POWERBOOK is not set
+# CONFIG_HID_FF is not set
+# CONFIG_USB_HIDDEV is not set
+
+#
+# USB HID Boot Protocol drivers
+#
+CONFIG_USB_KBD=m
+CONFIG_USB_MOUSE=m
+CONFIG_USB_SUPPORT=y
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARCH_HAS_OHCI=y
+# CONFIG_USB_ARCH_HAS_EHCI is not set
+CONFIG_USB=m
+# CONFIG_USB_DEBUG is not set
+
+#
+# Miscellaneous USB options
+#
+CONFIG_USB_DEVICEFS=y
+CONFIG_USB_DEVICE_CLASS=y
+# CONFIG_USB_DYNAMIC_MINORS is not set
+# CONFIG_USB_SUSPEND is not set
+# CONFIG_USB_PERSIST is not set
+# CONFIG_USB_OTG is not set
+
+#
+# USB Host Controller Drivers
+#
+# CONFIG_USB_ISP116X_HCD is not set
+CONFIG_USB_OHCI_HCD=m
+# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set
+# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set
+CONFIG_USB_OHCI_LITTLE_ENDIAN=y
+CONFIG_USB_SL811_HCD=m
+CONFIG_USB_SL811_CS=m
+# CONFIG_USB_R8A66597_HCD is not set
+
+#
+# USB Device Class drivers
+#
+CONFIG_USB_ACM=m
+CONFIG_USB_PRINTER=m
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
+#
+
+#
+# may also be needed; see USB_STORAGE Help for more information
+#
+CONFIG_USB_STORAGE=m
+# CONFIG_USB_STORAGE_DEBUG is not set
+# CONFIG_USB_STORAGE_DATAFAB is not set
+# CONFIG_USB_STORAGE_FREECOM is not set
+# CONFIG_USB_STORAGE_ISD200 is not set
+# CONFIG_USB_STORAGE_DPCM is not set
+# CONFIG_USB_STORAGE_USBAT is not set
+# CONFIG_USB_STORAGE_SDDR09 is not set
+# CONFIG_USB_STORAGE_SDDR55 is not set
+# CONFIG_USB_STORAGE_JUMPSHOT is not set
+# CONFIG_USB_STORAGE_ALAUDA is not set
+# CONFIG_USB_STORAGE_KARMA is not set
+# CONFIG_USB_LIBUSUAL is not set
+
+#
+# USB Imaging devices
+#
+CONFIG_USB_MDC800=m
+CONFIG_USB_MICROTEK=m
+CONFIG_USB_MON=y
+
+#
+# USB port drivers
+#
+
+#
+# USB Serial Converter support
+#
+CONFIG_USB_SERIAL=m
+CONFIG_USB_SERIAL_GENERIC=y
+# CONFIG_USB_SERIAL_AIRCABLE is not set
+# CONFIG_USB_SERIAL_AIRPRIME is not set
+# CONFIG_USB_SERIAL_ARK3116 is not set
+CONFIG_USB_SERIAL_BELKIN=m
+# CONFIG_USB_SERIAL_CH341 is not set
+# CONFIG_USB_SERIAL_WHITEHEAT is not set
+CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m
+CONFIG_USB_SERIAL_CP2101=m
+CONFIG_USB_SERIAL_CYPRESS_M8=m
+CONFIG_USB_SERIAL_EMPEG=m
+CONFIG_USB_SERIAL_FTDI_SIO=m
+# CONFIG_USB_SERIAL_FUNSOFT is not set
+CONFIG_USB_SERIAL_VISOR=m
+CONFIG_USB_SERIAL_IPAQ=m
+CONFIG_USB_SERIAL_IR=m
+CONFIG_USB_SERIAL_EDGEPORT=m
+CONFIG_USB_SERIAL_EDGEPORT_TI=m
+CONFIG_USB_SERIAL_GARMIN=m
+CONFIG_USB_SERIAL_IPW=m
+CONFIG_USB_SERIAL_KEYSPAN_PDA=m
+CONFIG_USB_SERIAL_KEYSPAN=m
+# CONFIG_USB_SERIAL_KEYSPAN_MPR is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19QW is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19QI is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA49WLC is not set
+CONFIG_USB_SERIAL_KLSI=m
+CONFIG_USB_SERIAL_KOBIL_SCT=m
+CONFIG_USB_SERIAL_MCT_U232=m
+# CONFIG_USB_SERIAL_MOS7720 is not set
+# CONFIG_USB_SERIAL_MOS7840 is not set
+# CONFIG_USB_SERIAL_NAVMAN is not set
+CONFIG_USB_SERIAL_PL2303=m
+# CONFIG_USB_SERIAL_OTI6858 is not set
+# CONFIG_USB_SERIAL_HP4X is not set
+CONFIG_USB_SERIAL_SAFE=m
+# CONFIG_USB_SERIAL_SAFE_PADDED is not set
+# CONFIG_USB_SERIAL_SIERRAWIRELESS is not set
+CONFIG_USB_SERIAL_TI=m
+CONFIG_USB_SERIAL_CYBERJACK=m
+CONFIG_USB_SERIAL_XIRCOM=m
+# CONFIG_USB_SERIAL_OPTION is not set
+CONFIG_USB_SERIAL_OMNINET=m
+# CONFIG_USB_SERIAL_DEBUG is not set
+CONFIG_USB_EZUSB=y
+
+#
+# USB Miscellaneous drivers
+#
+CONFIG_USB_EMI62=m
+CONFIG_USB_EMI26=m
+# CONFIG_USB_ADUTUX is not set
+CONFIG_USB_AUERSWALD=m
+CONFIG_USB_RIO500=m
+CONFIG_USB_LEGOTOWER=m
+CONFIG_USB_LCD=m
+# CONFIG_USB_BERRY_CHARGE is not set
+CONFIG_USB_LED=m
+# CONFIG_USB_CYPRESS_CY7C63 is not set
+CONFIG_USB_CYTHERM=m
+# CONFIG_USB_PHIDGET is not set
+CONFIG_USB_IDMOUSE=m
+# CONFIG_USB_FTDI_ELAN is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+# CONFIG_USB_LD is not set
+# CONFIG_USB_TRANCEVIBRATOR is not set
+# CONFIG_USB_IOWARRIOR is not set
+# CONFIG_USB_TEST is not set
+
+#
+# USB DSL modem support
+#
+
+#
+# USB Gadget Support
+#
+CONFIG_USB_GADGET=y
+# CONFIG_USB_GADGET_DEBUG is not set
+# CONFIG_USB_GADGET_DEBUG_FILES is not set
+CONFIG_USB_GADGET_SELECTED=y
+# CONFIG_USB_GADGET_AMD5536UDC is not set
+# CONFIG_USB_GADGET_ATMEL_USBA is not set
+# CONFIG_USB_GADGET_FSL_USB2 is not set
+# CONFIG_USB_GADGET_NET2280 is not set
+# CONFIG_USB_GADGET_PXA2XX is not set
+# CONFIG_USB_GADGET_M66592 is not set
+CONFIG_USB_GADGET_PXA27X=y
+CONFIG_USB_PXA27X=y
+# CONFIG_USB_GADGET_GOKU is not set
+# CONFIG_USB_GADGET_LH7A40X is not set
+# CONFIG_USB_GADGET_OMAP is not set
+# CONFIG_USB_GADGET_S3C2410 is not set
+# CONFIG_USB_GADGET_AT91 is not set
+# CONFIG_USB_GADGET_DUMMY_HCD is not set
+# CONFIG_USB_GADGET_DUALSPEED is not set
+CONFIG_USB_ZERO=m
+CONFIG_USB_ETH=m
+CONFIG_USB_ETH_RNDIS=y
+CONFIG_USB_GADGETFS=m
+CONFIG_USB_FILE_STORAGE=m
+# CONFIG_USB_FILE_STORAGE_TEST is not set
+CONFIG_USB_G_SERIAL=m
+# CONFIG_USB_MIDI_GADGET is not set
+CONFIG_MMC=y
+# CONFIG_MMC_DEBUG is not set
+CONFIG_MMC_UNSAFE_RESUME=y
+
+#
+# MMC/SD Card Drivers
+#
+CONFIG_MMC_BLOCK=y
+CONFIG_MMC_BLOCK_BOUNCE=y
+# CONFIG_SDIO_UART is not set
+
+#
+# MMC/SD Host Controller Drivers
+#
+CONFIG_MMC_PXA=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+
+#
+# LED drivers
+#
+# CONFIG_LEDS_GPIO is not set
+
+#
+# LED Triggers
+#
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=y
+CONFIG_LEDS_TRIGGER_IDE_DISK=y
+# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set
+CONFIG_RTC_LIB=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_HCTOSYS=y
+CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
+# CONFIG_RTC_DEBUG is not set
+
+#
+# RTC interfaces
+#
+CONFIG_RTC_INTF_SYSFS=y
+CONFIG_RTC_INTF_PROC=y
+CONFIG_RTC_INTF_DEV=y
+# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
+# CONFIG_RTC_DRV_TEST is not set
+
+#
+# I2C RTC drivers
+#
+# CONFIG_RTC_DRV_DS1307 is not set
+# CONFIG_RTC_DRV_DS1374 is not set
+# CONFIG_RTC_DRV_DS1672 is not set
+# CONFIG_RTC_DRV_MAX6900 is not set
+# CONFIG_RTC_DRV_RS5C372 is not set
+# CONFIG_RTC_DRV_ISL1208 is not set
+# CONFIG_RTC_DRV_X1205 is not set
+# CONFIG_RTC_DRV_PCF8563 is not set
+# CONFIG_RTC_DRV_PCF8583 is not set
+# CONFIG_RTC_DRV_M41T80 is not set
+
+#
+# SPI RTC drivers
+#
+
+#
+# Platform RTC drivers
+#
+# CONFIG_RTC_DRV_CMOS is not set
+# CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_STK17TA8 is not set
+# CONFIG_RTC_DRV_DS1742 is not set
+# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_M48T59 is not set
+# CONFIG_RTC_DRV_V3020 is not set
+
+#
+# on-CPU RTC drivers
+#
+CONFIG_RTC_DRV_SA1100=y
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+# CONFIG_EXT2_FS_XIP is not set
+CONFIG_EXT3_FS=m
+# CONFIG_EXT3_FS_XATTR is not set
+# CONFIG_EXT4DEV_FS is not set
+CONFIG_JBD=m
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+CONFIG_FS_POSIX_ACL=y
+# CONFIG_XFS_FS is not set
+# CONFIG_GFS2_FS is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_ROMFS_FS is not set
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_QUOTA is not set
+CONFIG_DNOTIFY=y
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+# CONFIG_MSDOS_FS is not set
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_DEBUG=0
+CONFIG_JFFS2_FS_WRITEBUFFER=y
+# CONFIG_JFFS2_FS_WBUF_VERIFY is not set
+CONFIG_JFFS2_SUMMARY=y
+# CONFIG_JFFS2_FS_XATTR is not set
+# CONFIG_JFFS2_SYSFS is not set
+CONFIG_JFFS2_COMPRESSION_OPTIONS=y
+CONFIG_JFFS2_ZLIB=y
+CONFIG_JFFS2_LZO=y
+CONFIG_JFFS2_RTIME=y
+CONFIG_JFFS2_RUBIN=y
+# CONFIG_JFFS2_CMODE_NONE is not set
+CONFIG_JFFS2_CMODE_PRIORITY=y
+# CONFIG_JFFS2_CMODE_SIZE is not set
+# CONFIG_JFFS2_CMODE_FAVOURLZO is not set
+CONFIG_CRAMFS=m
+CONFIG_SQUASHFS=m
+# CONFIG_SQUASHFS_EMBEDDED is not set
+CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3
+# CONFIG_VXFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+CONFIG_NFS_FS=m
+CONFIG_NFS_V3=y
+# CONFIG_NFS_V3_ACL is not set
+CONFIG_NFS_V4=y
+# CONFIG_NFS_DIRECTIO is not set
+CONFIG_NFSD=m
+CONFIG_NFSD_V3=y
+# CONFIG_NFSD_V3_ACL is not set
+CONFIG_NFSD_V4=y
+CONFIG_NFSD_TCP=y
+CONFIG_LOCKD=m
+CONFIG_LOCKD_V4=y
+CONFIG_EXPORTFS=m
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=m
+CONFIG_SUNRPC_GSS=m
+# CONFIG_SUNRPC_BIND34 is not set
+CONFIG_RPCSEC_GSS_KRB5=m
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+CONFIG_SMB_FS=m
+CONFIG_SMB_NLS_DEFAULT=y
+CONFIG_SMB_NLS_REMOTE="cp437"
+CONFIG_CIFS=m
+# CONFIG_CIFS_STATS is not set
+# CONFIG_CIFS_WEAK_PW_HASH is not set
+# CONFIG_CIFS_XATTR is not set
+# CONFIG_CIFS_DEBUG2 is not set
+# CONFIG_CIFS_EXPERIMENTAL is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_MINIX_SUBPARTITION is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_UNIXWARE_DISKLABEL is not set
+# CONFIG_LDM_PARTITION is not set
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_ULTRIX_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_KARMA_PARTITION is not set
+# CONFIG_EFI_PARTITION is not set
+# CONFIG_SYSV68_PARTITION is not set
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="cp437"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_737=m
+CONFIG_NLS_CODEPAGE_775=m
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
+CONFIG_NLS_CODEPAGE_855=m
+CONFIG_NLS_CODEPAGE_857=m
+CONFIG_NLS_CODEPAGE_860=m
+CONFIG_NLS_CODEPAGE_861=m
+CONFIG_NLS_CODEPAGE_862=m
+CONFIG_NLS_CODEPAGE_863=m
+CONFIG_NLS_CODEPAGE_864=m
+CONFIG_NLS_CODEPAGE_865=m
+CONFIG_NLS_CODEPAGE_866=m
+CONFIG_NLS_CODEPAGE_869=m
+CONFIG_NLS_CODEPAGE_936=m
+CONFIG_NLS_CODEPAGE_950=m
+CONFIG_NLS_CODEPAGE_932=m
+CONFIG_NLS_CODEPAGE_949=m
+CONFIG_NLS_CODEPAGE_874=m
+CONFIG_NLS_ISO8859_8=m
+CONFIG_NLS_CODEPAGE_1250=m
+CONFIG_NLS_CODEPAGE_1251=m
+CONFIG_NLS_ASCII=m
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_ISO8859_2=m
+CONFIG_NLS_ISO8859_3=m
+CONFIG_NLS_ISO8859_4=m
+CONFIG_NLS_ISO8859_5=m
+CONFIG_NLS_ISO8859_6=m
+CONFIG_NLS_ISO8859_7=m
+CONFIG_NLS_ISO8859_9=m
+CONFIG_NLS_ISO8859_13=m
+CONFIG_NLS_ISO8859_14=m
+CONFIG_NLS_ISO8859_15=m
+CONFIG_NLS_KOI8_R=m
+CONFIG_NLS_KOI8_U=m
+CONFIG_NLS_UTF8=y
+# CONFIG_DLM is not set
+CONFIG_INSTRUMENTATION=y
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=m
+# CONFIG_MARKERS is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_PRINTK_TIME is not set
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_DEBUG_FS is not set
+# CONFIG_HEADERS_CHECK is not set
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_DEBUG_SHIRQ is not set
+# CONFIG_DETECT_SOFTLOCKUP is not set
+# CONFIG_SCHED_DEBUG is not set
+# CONFIG_SCHEDSTATS is not set
+CONFIG_TIMER_STATS=y
+# CONFIG_DEBUG_SLAB is not set
+# CONFIG_DEBUG_PREEMPT is not set
+# CONFIG_DEBUG_RT_MUTEXES is not set
+# CONFIG_RT_MUTEX_TESTER is not set
+# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_DEBUG_MUTEXES is not set
+# CONFIG_DEBUG_LOCK_ALLOC is not set
+# CONFIG_PROVE_LOCKING is not set
+# CONFIG_LOCK_STAT is not set
+# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
+# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
+# CONFIG_DEBUG_KOBJECT is not set
+CONFIG_DEBUG_BUGVERBOSE=y
+# CONFIG_DEBUG_INFO is not set
+# CONFIG_DEBUG_VM is not set
+# CONFIG_DEBUG_LIST is not set
+# CONFIG_DEBUG_SG is not set
+CONFIG_FRAME_POINTER=y
+# CONFIG_FORCED_INLINING is not set
+# CONFIG_BOOT_PRINTK_DELAY is not set
+# CONFIG_RCU_TORTURE_TEST is not set
+# CONFIG_FAULT_INJECTION is not set
+# CONFIG_SAMPLES is not set
+# CONFIG_DEBUG_USER is not set
+CONFIG_DEBUG_ERRORS=y
+# CONFIG_DEBUG_LL is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+CONFIG_CRYPTO=y
+CONFIG_CRYPTO_ALGAPI=m
+CONFIG_CRYPTO_BLKCIPHER=m
+CONFIG_CRYPTO_HASH=m
+CONFIG_CRYPTO_MANAGER=m
+CONFIG_CRYPTO_HMAC=m
+# CONFIG_CRYPTO_XCBC is not set
+CONFIG_CRYPTO_NULL=m
+CONFIG_CRYPTO_MD4=m
+CONFIG_CRYPTO_MD5=m
+CONFIG_CRYPTO_SHA1=m
+CONFIG_CRYPTO_SHA256=m
+CONFIG_CRYPTO_SHA512=m
+CONFIG_CRYPTO_WP512=m
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_GF128MUL is not set
+CONFIG_CRYPTO_ECB=m
+CONFIG_CRYPTO_CBC=m
+CONFIG_CRYPTO_PCBC=m
+# CONFIG_CRYPTO_LRW is not set
+# CONFIG_CRYPTO_XTS is not set
+# CONFIG_CRYPTO_CRYPTD is not set
+CONFIG_CRYPTO_DES=m
+# CONFIG_CRYPTO_FCRYPT is not set
+CONFIG_CRYPTO_BLOWFISH=m
+CONFIG_CRYPTO_TWOFISH=m
+CONFIG_CRYPTO_TWOFISH_COMMON=m
+CONFIG_CRYPTO_SERPENT=m
+CONFIG_CRYPTO_AES=m
+CONFIG_CRYPTO_CAST5=m
+CONFIG_CRYPTO_CAST6=m
+CONFIG_CRYPTO_TEA=m
+CONFIG_CRYPTO_ARC4=m
+CONFIG_CRYPTO_KHAZAD=m
+CONFIG_CRYPTO_ANUBIS=m
+# CONFIG_CRYPTO_SEED is not set
+CONFIG_CRYPTO_DEFLATE=m
+CONFIG_CRYPTO_LZO=m
+CONFIG_CRYPTO_MICHAEL_MIC=m
+CONFIG_CRYPTO_CRC32C=m
+CONFIG_CRYPTO_CAMELLIA=m
+CONFIG_CRYPTO_TEST=m
+# CONFIG_CRYPTO_AUTHENC is not set
+# CONFIG_CRYPTO_HW is not set
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+CONFIG_CRC_CCITT=y
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
+CONFIG_LIBCRC32C=m
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_LZO_COMPRESS=y
+CONFIG_LZO_DECOMPRESS=y
+CONFIG_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
diff --git a/recipes/linux/linux-rp-2.6.26/defconfig-poodle b/recipes/linux/linux-rp-2.6.26/defconfig-poodle
new file mode 100644
index 0000000000..9d5e340114
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.26/defconfig-poodle
@@ -0,0 +1,1761 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.24-rc8
+# Sun Jan 20 22:47:47 2008
+#
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+CONFIG_GENERIC_GPIO=y
+CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_MMU=y
+# CONFIG_NO_IOPORT is not set
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+# CONFIG_ARCH_HAS_ILOG2_U32 is not set
+# CONFIG_ARCH_HAS_ILOG2_U64 is not set
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_ZONE_DMA=y
+CONFIG_ARCH_MTD_XIP=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_LOCK_KERNEL=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+# CONFIG_POSIX_MQUEUE is not set
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_BSD_PROCESS_ACCT_V3=y
+# CONFIG_TASKSTATS is not set
+# CONFIG_USER_NS is not set
+# CONFIG_PID_NS is not set
+# CONFIG_AUDIT is not set
+# CONFIG_IKCONFIG is not set
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_CGROUPS is not set
+CONFIG_FAIR_GROUP_SCHED=y
+CONFIG_FAIR_USER_SCHED=y
+# CONFIG_FAIR_CGROUP_SCHED is not set
+CONFIG_SYSFS_DEPRECATED=y
+# CONFIG_RELAY is not set
+# CONFIG_BLK_DEV_INITRD is not set
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+CONFIG_EMBEDDED=y
+CONFIG_UID16=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_ANON_INODES=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLAB=y
+# CONFIG_SLUB is not set
+# CONFIG_SLOB is not set
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+# CONFIG_TINY_SHMEM is not set
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+CONFIG_BLOCK=y
+# CONFIG_LBD is not set
+# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_LSF is not set
+# CONFIG_BLK_DEV_BSG is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=m
+CONFIG_IOSCHED_CFQ=m
+CONFIG_DEFAULT_AS=y
+# CONFIG_DEFAULT_DEADLINE is not set
+# CONFIG_DEFAULT_CFQ is not set
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="anticipatory"
+
+#
+# System Type
+#
+# CONFIG_ARCH_AAEC2000 is not set
+# CONFIG_ARCH_INTEGRATOR is not set
+# CONFIG_ARCH_REALVIEW is not set
+# CONFIG_ARCH_VERSATILE is not set
+# CONFIG_ARCH_AT91 is not set
+# CONFIG_ARCH_CLPS7500 is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_CO285 is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE is not set
+# CONFIG_ARCH_NETX is not set
+# CONFIG_ARCH_H720X is not set
+# CONFIG_ARCH_IMX is not set
+# CONFIG_ARCH_IOP13XX is not set
+# CONFIG_ARCH_IOP32X is not set
+# CONFIG_ARCH_IOP33X is not set
+# CONFIG_ARCH_IXP23XX is not set
+# CONFIG_ARCH_IXP2000 is not set
+# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_L7200 is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_NS9XXX is not set
+# CONFIG_ARCH_MXC is not set
+# CONFIG_ARCH_PNX4008 is not set
+CONFIG_ARCH_PXA=y
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP is not set
+
+#
+# Intel PXA2xx/PXA3xx Implementations
+#
+# CONFIG_ARCH_LUBBOCK is not set
+# CONFIG_MACH_LOGICPD_PXA270 is not set
+# CONFIG_MACH_MAINSTONE is not set
+# CONFIG_ARCH_PXA_IDP is not set
+CONFIG_PXA_SHARPSL=y
+# CONFIG_MACH_TRIZEPS4 is not set
+# CONFIG_MACH_EM_X270 is not set
+# CONFIG_MACH_ZYLONITE is not set
+# CONFIG_MACH_ARMCORE is not set
+CONFIG_PXA_SHARPSL_25x=y
+# CONFIG_PXA_SHARPSL_27x is not set
+# CONFIG_MACH_HX2750 is not set
+# CONFIG_MACH_HTCUNIVERSAL is not set
+CONFIG_MACH_POODLE=y
+# CONFIG_MACH_CORGI is not set
+# CONFIG_MACH_SHEPHERD is not set
+# CONFIG_MACH_HUSKY is not set
+# CONFIG_MACH_TOSA is not set
+CONFIG_PXA25x=y
+CONFIG_PXA_SSP=y
+# CONFIG_PXA_KEYS is not set
+
+#
+# Boot options
+#
+
+#
+# Power management
+#
+
+#
+# Processor Type
+#
+CONFIG_CPU_32=y
+CONFIG_CPU_XSCALE=y
+CONFIG_CPU_32v5=y
+CONFIG_CPU_ABRT_EV5T=y
+CONFIG_CPU_CACHE_VIVT=y
+CONFIG_CPU_TLB_V4WBI=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+
+#
+# Processor Features
+#
+CONFIG_ARM_THUMB=y
+# CONFIG_CPU_DCACHE_DISABLE is not set
+# CONFIG_OUTER_CACHE is not set
+# CONFIG_IWMMXT is not set
+CONFIG_XSCALE_PMU=y
+CONFIG_SHARP_LOCOMO=y
+CONFIG_SHARP_PARAM=y
+CONFIG_SHARPSL_PM=y
+CONFIG_SHARP_SCOOP=y
+
+#
+# Bus support
+#
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+CONFIG_PCCARD=y
+# CONFIG_PCMCIA_DEBUG is not set
+CONFIG_PCMCIA=y
+CONFIG_PCMCIA_LOAD_CIS=y
+CONFIG_PCMCIA_IOCTL=y
+
+#
+# PC-card bridges
+#
+CONFIG_PCMCIA_PXA2XX=y
+
+#
+# Kernel Features
+#
+CONFIG_TICK_ONESHOT=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_PREEMPT=y
+CONFIG_HZ=100
+CONFIG_AEABI=y
+CONFIG_OABI_COMPAT=y
+# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+# CONFIG_SPARSEMEM_STATIC is not set
+# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set
+CONFIG_SPLIT_PTLOCK_CPUS=4096
+# CONFIG_RESOURCES_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=1
+CONFIG_BOUNCE=y
+CONFIG_VIRT_TO_BUS=y
+CONFIG_ALIGNMENT_TRAP=y
+
+#
+# Boot options
+#
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="console=ttyS0,115200n8 console=tty1 noinitrd root=/dev/mtdblock2 rootfstype=jffs2 fbcon=rotate:1 dyntick=enable debug"
+# CONFIG_XIP_KERNEL is not set
+CONFIG_KEXEC=y
+CONFIG_ATAGS_PROC=y
+CONFIG_CPU_FREQ_PXA25x=y
+
+#
+# CPU Frequency scaling
+#
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_TABLE=y
+CONFIG_CPU_FREQ_DEBUG=y
+CONFIG_CPU_FREQ_STAT=y
+# CONFIG_CPU_FREQ_STAT_DETAILS is not set
+CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+CONFIG_FPE_NWFPE=y
+# CONFIG_FPE_NWFPE_XP is not set
+# CONFIG_FPE_FASTFPE is not set
+
+#
+# Userspace binary formats
+#
+CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_AOUT=m
+CONFIG_BINFMT_MISC=m
+
+#
+# Power management options
+#
+CONFIG_PM=y
+# CONFIG_PM_LEGACY is not set
+# CONFIG_PM_DEBUG is not set
+CONFIG_PM_SLEEP=y
+CONFIG_SUSPEND_UP_POSSIBLE=y
+CONFIG_SUSPEND=y
+CONFIG_APM_EMULATION=y
+CONFIG_INPUT_APMPOWER=y
+
+#
+# Networking
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=m
+CONFIG_PACKET_MMAP=y
+CONFIG_UNIX=y
+CONFIG_XFRM=y
+CONFIG_XFRM_USER=m
+# CONFIG_XFRM_SUB_POLICY is not set
+# CONFIG_XFRM_MIGRATE is not set
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+# CONFIG_IP_PNP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_ARPD is not set
+CONFIG_SYN_COOKIES=y
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+CONFIG_INET_TUNNEL=m
+CONFIG_INET_XFRM_MODE_TRANSPORT=m
+CONFIG_INET_XFRM_MODE_TUNNEL=m
+CONFIG_INET_XFRM_MODE_BEET=m
+# CONFIG_INET_LRO is not set
+CONFIG_INET_DIAG=m
+CONFIG_INET_TCP_DIAG=m
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+# CONFIG_IP_VS is not set
+CONFIG_IPV6=m
+# CONFIG_IPV6_PRIVACY is not set
+# CONFIG_IPV6_ROUTER_PREF is not set
+# CONFIG_IPV6_OPTIMISTIC_DAD is not set
+CONFIG_INET6_AH=m
+CONFIG_INET6_ESP=m
+CONFIG_INET6_IPCOMP=m
+# CONFIG_IPV6_MIP6 is not set
+CONFIG_INET6_XFRM_TUNNEL=m
+CONFIG_INET6_TUNNEL=m
+CONFIG_INET6_XFRM_MODE_TRANSPORT=m
+CONFIG_INET6_XFRM_MODE_TUNNEL=m
+CONFIG_INET6_XFRM_MODE_BEET=m
+# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set
+CONFIG_IPV6_SIT=m
+CONFIG_IPV6_TUNNEL=m
+# CONFIG_IPV6_MULTIPLE_TABLES is not set
+# CONFIG_NETWORK_SECMARK is not set
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+
+#
+# Core Netfilter Configuration
+#
+# CONFIG_NETFILTER_NETLINK is not set
+# CONFIG_NF_CONNTRACK_ENABLED is not set
+# CONFIG_NF_CONNTRACK is not set
+CONFIG_NETFILTER_XTABLES=m
+# CONFIG_NETFILTER_XT_TARGET_CLASSIFY is not set
+# CONFIG_NETFILTER_XT_TARGET_DSCP is not set
+# CONFIG_NETFILTER_XT_TARGET_MARK is not set
+# CONFIG_NETFILTER_XT_TARGET_NFQUEUE is not set
+# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set
+# CONFIG_NETFILTER_XT_TARGET_TRACE is not set
+# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set
+# CONFIG_NETFILTER_XT_MATCH_COMMENT is not set
+# CONFIG_NETFILTER_XT_MATCH_DCCP is not set
+# CONFIG_NETFILTER_XT_MATCH_DSCP is not set
+# CONFIG_NETFILTER_XT_MATCH_ESP is not set
+# CONFIG_NETFILTER_XT_MATCH_LENGTH is not set
+# CONFIG_NETFILTER_XT_MATCH_LIMIT is not set
+# CONFIG_NETFILTER_XT_MATCH_MAC is not set
+# CONFIG_NETFILTER_XT_MATCH_MARK is not set
+# CONFIG_NETFILTER_XT_MATCH_POLICY is not set
+# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set
+# CONFIG_NETFILTER_XT_MATCH_PKTTYPE is not set
+# CONFIG_NETFILTER_XT_MATCH_QUOTA is not set
+# CONFIG_NETFILTER_XT_MATCH_REALM is not set
+# CONFIG_NETFILTER_XT_MATCH_SCTP is not set
+# CONFIG_NETFILTER_XT_MATCH_STATISTIC is not set
+# CONFIG_NETFILTER_XT_MATCH_STRING is not set
+# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set
+# CONFIG_NETFILTER_XT_MATCH_TIME is not set
+# CONFIG_NETFILTER_XT_MATCH_U32 is not set
+# CONFIG_NETFILTER_XT_MATCH_HASHLIMIT is not set
+
+#
+# IP: Netfilter Configuration
+#
+CONFIG_IP_NF_QUEUE=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_IPRANGE=m
+CONFIG_IP_NF_MATCH_TOS=m
+CONFIG_IP_NF_MATCH_RECENT=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_AH=m
+CONFIG_IP_NF_MATCH_TTL=m
+CONFIG_IP_NF_MATCH_OWNER=m
+CONFIG_IP_NF_MATCH_ADDRTYPE=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_LOG=m
+CONFIG_IP_NF_TARGET_ULOG=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_TOS=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_TTL=m
+CONFIG_IP_NF_RAW=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+
+#
+# IPv6: Netfilter Configuration (EXPERIMENTAL)
+#
+# CONFIG_IP6_NF_QUEUE is not set
+# CONFIG_IP6_NF_IPTABLES is not set
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_SCTP is not set
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_SCHED is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+CONFIG_IRDA=m
+
+#
+# IrDA protocols
+#
+CONFIG_IRLAN=m
+CONFIG_IRNET=m
+CONFIG_IRCOMM=m
+# CONFIG_IRDA_ULTRA is not set
+
+#
+# IrDA options
+#
+# CONFIG_IRDA_CACHE_LAST_LSAP is not set
+# CONFIG_IRDA_FAST_RR is not set
+# CONFIG_IRDA_DEBUG is not set
+
+#
+# Infrared-port device drivers
+#
+
+#
+# SIR device drivers
+#
+# CONFIG_IRTTY_SIR is not set
+
+#
+# Dongle support
+#
+# CONFIG_KINGSUN_DONGLE is not set
+# CONFIG_KSDAZZLE_DONGLE is not set
+# CONFIG_KS959_DONGLE is not set
+
+#
+# Old SIR device drivers
+#
+# CONFIG_IRPORT_SIR is not set
+
+#
+# Old Serial dongle support
+#
+
+#
+# FIR device drivers
+#
+# CONFIG_USB_IRDA is not set
+# CONFIG_SIGMATEL_FIR is not set
+CONFIG_PXA_FICP=m
+# CONFIG_MCS_FIR is not set
+CONFIG_BT=m
+CONFIG_BT_L2CAP=m
+CONFIG_BT_SCO=m
+CONFIG_BT_RFCOMM=m
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=m
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+CONFIG_BT_HIDP=m
+
+#
+# Bluetooth device drivers
+#
+CONFIG_BT_HCIUSB=m
+# CONFIG_BT_HCIUSB_SCO is not set
+# CONFIG_BT_HCIBTSDIO is not set
+CONFIG_BT_HCIUART=m
+CONFIG_BT_HCIUART_H4=y
+CONFIG_BT_HCIUART_BCSP=y
+# CONFIG_BT_HCIUART_LL is not set
+CONFIG_BT_HCIBCM203X=m
+CONFIG_BT_HCIBPA10X=m
+CONFIG_BT_HCIBFUSB=m
+CONFIG_BT_HCIDTL1=m
+CONFIG_BT_HCIBT3C=m
+CONFIG_BT_HCIBLUECARD=m
+CONFIG_BT_HCIBTUART=m
+CONFIG_BT_HCIVHCI=m
+# CONFIG_AF_RXRPC is not set
+
+#
+# Wireless
+#
+# CONFIG_CFG80211 is not set
+CONFIG_WIRELESS_EXT=y
+# CONFIG_MAC80211 is not set
+CONFIG_IEEE80211=m
+# CONFIG_IEEE80211_DEBUG is not set
+CONFIG_IEEE80211_CRYPT_WEP=m
+CONFIG_IEEE80211_CRYPT_CCMP=m
+CONFIG_IEEE80211_CRYPT_TKIP=m
+CONFIG_IEEE80211_SOFTMAC=m
+# CONFIG_IEEE80211_SOFTMAC_DEBUG is not set
+# CONFIG_RFKILL is not set
+# CONFIG_NET_9P is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+# CONFIG_DEBUG_DRIVER is not set
+# CONFIG_DEBUG_DEVRES is not set
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_CONNECTOR is not set
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+# CONFIG_MTD_CONCAT is not set
+CONFIG_MTD_PARTITIONS=y
+# CONFIG_MTD_REDBOOT_PARTS is not set
+CONFIG_MTD_CMDLINE_PARTS=y
+# CONFIG_MTD_AFS_PARTS is not set
+
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+# CONFIG_MTD_OOPS is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+# CONFIG_MTD_CFI is not set
+# CONFIG_MTD_JEDECPROBE is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+# CONFIG_MTD_RAM is not set
+CONFIG_MTD_ROM=y
+# CONFIG_MTD_ABSENT is not set
+
+#
+# Mapping drivers for chip access
+#
+CONFIG_MTD_COMPLEX_MAPPINGS=y
+# CONFIG_MTD_PHYSMAP is not set
+CONFIG_MTD_SHARP_SL=y
+# CONFIG_MTD_PLATRAM is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLOCK2MTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_VERIFY_WRITE=y
+# CONFIG_MTD_NAND_ECC_SMC is not set
+# CONFIG_MTD_NAND_MUSEUM_IDS is not set
+# CONFIG_MTD_NAND_H1900 is not set
+CONFIG_MTD_NAND_IDS=y
+# CONFIG_MTD_NAND_DISKONCHIP is not set
+CONFIG_MTD_NAND_SHARPSL=y
+# CONFIG_MTD_NAND_NANDSIM is not set
+# CONFIG_MTD_NAND_PLATFORM is not set
+# CONFIG_MTD_ALAUDA is not set
+# CONFIG_MTD_ONENAND is not set
+
+#
+# UBI - Unsorted block images
+#
+# CONFIG_MTD_UBI is not set
+# CONFIG_PARPORT is not set
+CONFIG_BLK_DEV=y
+# CONFIG_BLK_DEV_COW_COMMON is not set
+CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_CRYPTOLOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_UB is not set
+# CONFIG_BLK_DEV_RAM is not set
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+CONFIG_MISC_DEVICES=y
+# CONFIG_EEPROM_93CX6 is not set
+CONFIG_IDE=y
+CONFIG_IDE_MAX_HWIFS=4
+CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_IDE_SATA is not set
+CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_IDEDISK_MULTI_MODE is not set
+CONFIG_BLK_DEV_IDECS=y
+# CONFIG_BLK_DEV_IDECD is not set
+# CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
+# CONFIG_BLK_DEV_IDESCSI is not set
+# CONFIG_IDE_TASK_IOCTL is not set
+CONFIG_IDE_PROC_FS=y
+
+#
+# IDE chipset support/bugfixes
+#
+CONFIG_IDE_GENERIC=y
+# CONFIG_BLK_DEV_PLATFORM is not set
+# CONFIG_IDE_ARM is not set
+# CONFIG_BLK_DEV_IDEDMA is not set
+CONFIG_IDE_ARCH_OBSOLETE_INIT=y
+# CONFIG_BLK_DEV_HD is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+CONFIG_SCSI=m
+CONFIG_SCSI_DMA=y
+# CONFIG_SCSI_TGT is not set
+# CONFIG_SCSI_NETLINK is not set
+CONFIG_SCSI_PROC_FS=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=m
+CONFIG_CHR_DEV_ST=m
+CONFIG_CHR_DEV_OSST=m
+CONFIG_BLK_DEV_SR=m
+# CONFIG_BLK_DEV_SR_VENDOR is not set
+CONFIG_CHR_DEV_SG=m
+# CONFIG_CHR_DEV_SCH is not set
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+CONFIG_SCSI_MULTI_LUN=y
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
+# CONFIG_SCSI_SCAN_ASYNC is not set
+CONFIG_SCSI_WAIT_SCAN=m
+
+#
+# SCSI Transports
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+# CONFIG_SCSI_ISCSI_ATTRS is not set
+# CONFIG_SCSI_SAS_LIBSAS is not set
+# CONFIG_SCSI_SRP_ATTRS is not set
+CONFIG_SCSI_LOWLEVEL=y
+# CONFIG_ISCSI_TCP is not set
+# CONFIG_SCSI_DEBUG is not set
+# CONFIG_SCSI_LOWLEVEL_PCMCIA is not set
+# CONFIG_ATA is not set
+CONFIG_MD=y
+# CONFIG_BLK_DEV_MD is not set
+CONFIG_BLK_DEV_DM=m
+# CONFIG_DM_DEBUG is not set
+CONFIG_DM_CRYPT=m
+CONFIG_DM_SNAPSHOT=m
+CONFIG_DM_MIRROR=m
+CONFIG_DM_ZERO=m
+CONFIG_DM_MULTIPATH=m
+CONFIG_DM_MULTIPATH_EMC=m
+# CONFIG_DM_MULTIPATH_RDAC is not set
+# CONFIG_DM_MULTIPATH_HP is not set
+# CONFIG_DM_DELAY is not set
+# CONFIG_DM_UEVENT is not set
+CONFIG_NETDEVICES=y
+# CONFIG_NETDEVICES_MULTIQUEUE is not set
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_MACVLAN is not set
+# CONFIG_EQUALIZER is not set
+CONFIG_TUN=m
+# CONFIG_VETH is not set
+# CONFIG_PHYLIB is not set
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=m
+# CONFIG_AX88796 is not set
+# CONFIG_SMC91X is not set
+# CONFIG_DM9000 is not set
+# CONFIG_SMC911X is not set
+# CONFIG_IBM_NEW_EMAC_ZMII is not set
+# CONFIG_IBM_NEW_EMAC_RGMII is not set
+# CONFIG_IBM_NEW_EMAC_TAH is not set
+# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
+# CONFIG_B44 is not set
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
+
+#
+# Wireless LAN
+#
+# CONFIG_WLAN_PRE80211 is not set
+CONFIG_WLAN_80211=y
+# CONFIG_PCMCIA_RAYCS is not set
+CONFIG_LIBERTAS=m
+# CONFIG_LIBERTAS_USB is not set
+CONFIG_LIBERTAS_CS=m
+# CONFIG_LIBERTAS_SDIO is not set
+# CONFIG_LIBERTAS_DEBUG is not set
+CONFIG_HERMES=m
+CONFIG_PCMCIA_HERMES=m
+CONFIG_PCMCIA_SPECTRUM=m
+# CONFIG_ATMEL is not set
+CONFIG_AIRO_CS=m
+# CONFIG_PCMCIA_WL3501 is not set
+# CONFIG_USB_ZD1201 is not set
+CONFIG_HOSTAP=m
+CONFIG_HOSTAP_FIRMWARE=y
+# CONFIG_HOSTAP_FIRMWARE_NVRAM is not set
+CONFIG_HOSTAP_CS=m
+# CONFIG_ZD1211RW is not set
+
+#
+# USB Network Adapters
+#
+CONFIG_USB_CATC=m
+CONFIG_USB_KAWETH=m
+CONFIG_USB_PEGASUS=m
+CONFIG_USB_RTL8150=m
+CONFIG_USB_USBNET=m
+CONFIG_USB_NET_AX8817X=m
+CONFIG_USB_NET_CDCETHER=m
+CONFIG_USB_NET_DM9601=m
+# CONFIG_USB_NET_GL620A is not set
+CONFIG_USB_NET_NET1080=m
+# CONFIG_USB_NET_PLUSB is not set
+# CONFIG_USB_NET_MCS7830 is not set
+# CONFIG_USB_NET_RNDIS_HOST is not set
+# CONFIG_USB_NET_CDC_SUBSET is not set
+CONFIG_USB_NET_ZAURUS=m
+CONFIG_NET_PCMCIA=y
+# CONFIG_PCMCIA_3C589 is not set
+# CONFIG_PCMCIA_3C574 is not set
+# CONFIG_PCMCIA_FMVJ18X is not set
+CONFIG_PCMCIA_PCNET=m
+# CONFIG_PCMCIA_NMCLAN is not set
+# CONFIG_PCMCIA_SMC91C92 is not set
+# CONFIG_PCMCIA_XIRC2PS is not set
+# CONFIG_PCMCIA_AXNET is not set
+# CONFIG_WAN is not set
+CONFIG_PPP=m
+# CONFIG_PPP_MULTILINK is not set
+# CONFIG_PPP_FILTER is not set
+CONFIG_PPP_ASYNC=m
+# CONFIG_PPP_SYNC_TTY is not set
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_BSDCOMP=m
+# CONFIG_PPP_MPPE is not set
+# CONFIG_PPPOE is not set
+# CONFIG_PPPOL2TP is not set
+# CONFIG_SLIP is not set
+CONFIG_SLHC=m
+# CONFIG_SHAPER is not set
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_ISDN is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=m
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=240
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=320
+# CONFIG_INPUT_JOYDEV is not set
+CONFIG_INPUT_EVDEV=y
+# CONFIG_INPUT_EVBUG is not set
+CONFIG_INPUT_POWER=y
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+# CONFIG_KEYBOARD_ATKBD is not set
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+CONFIG_KEYBOARD_LOCOMO=y
+# CONFIG_KEYBOARD_XTKBD is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+# CONFIG_KEYBOARD_CORGI is not set
+# CONFIG_KEYBOARD_SPITZ is not set
+# CONFIG_KEYBOARD_GPIO is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_CORGI=y
+# CONFIG_TOUCHSCREEN_FUJITSU is not set
+# CONFIG_TOUCHSCREEN_GUNZE is not set
+# CONFIG_TOUCHSCREEN_ELO is not set
+# CONFIG_TOUCHSCREEN_MTOUCH is not set
+# CONFIG_TOUCHSCREEN_MK712 is not set
+# CONFIG_TOUCHSCREEN_PENMOUNT is not set
+# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set
+# CONFIG_TOUCHSCREEN_TOUCHWIN is not set
+# CONFIG_TOUCHSCREEN_UCB1400 is not set
+# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set
+CONFIG_INPUT_MISC=y
+# CONFIG_INPUT_ATI_REMOTE is not set
+# CONFIG_INPUT_ATI_REMOTE2 is not set
+# CONFIG_INPUT_KEYSPAN_REMOTE is not set
+# CONFIG_INPUT_POWERMATE is not set
+# CONFIG_INPUT_YEALINK is not set
+CONFIG_INPUT_UINPUT=m
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+# CONFIG_VT_HW_CONSOLE_BINDING is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+CONFIG_SERIAL_8250=m
+CONFIG_SERIAL_8250_CS=m
+CONFIG_SERIAL_8250_NR_UARTS=4
+CONFIG_SERIAL_8250_RUNTIME_UARTS=4
+# CONFIG_SERIAL_8250_EXTENDED is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_PXA=y
+CONFIG_SERIAL_PXA_CONSOLE=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_IPMI_HANDLER is not set
+CONFIG_HW_RANDOM=m
+# CONFIG_NVRAM is not set
+# CONFIG_R3964 is not set
+
+#
+# PCMCIA character devices
+#
+# CONFIG_SYNCLINK_CS is not set
+# CONFIG_CARDMAN_4000 is not set
+# CONFIG_CARDMAN_4040 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+# CONFIG_I2C_CHARDEV is not set
+
+#
+# I2C Algorithms
+#
+CONFIG_I2C_ALGOBIT=y
+# CONFIG_I2C_ALGOPCF is not set
+# CONFIG_I2C_ALGOPCA is not set
+
+#
+# I2C Hardware Bus support
+#
+# CONFIG_I2C_GPIO is not set
+CONFIG_I2C_PXA=y
+# CONFIG_I2C_PXA_SLAVE is not set
+# CONFIG_I2C_OCORES is not set
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_SIMTEC is not set
+# CONFIG_I2C_TAOS_EVM is not set
+# CONFIG_I2C_STUB is not set
+# CONFIG_I2C_TINY_USB is not set
+
+#
+# Miscellaneous I2C Chip support
+#
+# CONFIG_SENSORS_DS1337 is not set
+# CONFIG_SENSORS_DS1374 is not set
+# CONFIG_DS1682 is not set
+# CONFIG_SENSORS_EEPROM is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_SENSORS_PCA9539 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_SENSORS_MAX6875 is not set
+# CONFIG_SENSORS_TSL2550 is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_I2C_DEBUG_CHIP is not set
+
+#
+# SPI support
+#
+# CONFIG_SPI is not set
+# CONFIG_SPI_MASTER is not set
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+# CONFIG_WATCHDOG is not set
+
+#
+# Sonics Silicon Backplane
+#
+CONFIG_SSB_POSSIBLE=y
+# CONFIG_SSB is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_SM501 is not set
+# CONFIG_HTC_ASIC3 is not set
+# CONFIG_HTC_ASIC3_DS1WM is not set
+
+#
+# Multimedia devices
+#
+CONFIG_VIDEO_DEV=m
+CONFIG_VIDEO_V4L1=y
+CONFIG_VIDEO_V4L1_COMPAT=y
+CONFIG_VIDEO_V4L2=y
+CONFIG_VIDEO_CAPTURE_DRIVERS=y
+# CONFIG_VIDEO_ADV_DEBUG is not set
+CONFIG_VIDEO_HELPER_CHIPS_AUTO=y
+# CONFIG_VIDEO_VIVI is not set
+# CONFIG_VIDEO_CPIA is not set
+# CONFIG_VIDEO_CPIA2 is not set
+# CONFIG_VIDEO_SAA5246A is not set
+# CONFIG_VIDEO_SAA5249 is not set
+# CONFIG_TUNER_3036 is not set
+CONFIG_V4L_USB_DRIVERS=y
+# CONFIG_VIDEO_PVRUSB2 is not set
+# CONFIG_VIDEO_EM28XX is not set
+# CONFIG_VIDEO_USBVISION is not set
+CONFIG_VIDEO_USBVIDEO=m
+CONFIG_USB_VICAM=m
+CONFIG_USB_IBMCAM=m
+CONFIG_USB_KONICAWC=m
+# CONFIG_USB_QUICKCAM_MESSENGER is not set
+# CONFIG_USB_ET61X251 is not set
+# CONFIG_VIDEO_OVCAMCHIP is not set
+# CONFIG_USB_W9968CF is not set
+CONFIG_USB_OV511=m
+CONFIG_USB_SE401=m
+CONFIG_USB_SN9C102=m
+CONFIG_USB_STV680=m
+# CONFIG_USB_ZC0301 is not set
+# CONFIG_USB_PWC is not set
+# CONFIG_USB_ZR364XX is not set
+CONFIG_RADIO_ADAPTERS=y
+CONFIG_USB_DSBR=m
+# CONFIG_DVB_CORE is not set
+CONFIG_DAB=y
+CONFIG_USB_DABUSB=m
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+CONFIG_VIDEO_OUTPUT_CONTROL=m
+CONFIG_FB=y
+# CONFIG_FIRMWARE_EDID is not set
+# CONFIG_FB_DDC is not set
+CONFIG_FB_CFB_FILLRECT=y
+CONFIG_FB_CFB_COPYAREA=y
+CONFIG_FB_CFB_IMAGEBLIT=y
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+# CONFIG_FB_SYS_FILLRECT is not set
+# CONFIG_FB_SYS_COPYAREA is not set
+# CONFIG_FB_SYS_IMAGEBLIT is not set
+# CONFIG_FB_SYS_FOPS is not set
+# CONFIG_FB_DEFERRED_IO is not set
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_BACKLIGHT is not set
+# CONFIG_FB_MODE_HELPERS is not set
+# CONFIG_FB_TILEBLITTING is not set
+
+#
+# Frame buffer hardware drivers
+#
+# CONFIG_FB_S1D13XXX is not set
+CONFIG_FB_PXA=y
+CONFIG_FB_PXA_LCD_QVGA=y
+# CONFIG_FB_PXA_LCD_VGA is not set
+CONFIG_FB_PXA_OVERLAY=y
+# CONFIG_FB_PXA_PARAMETERS is not set
+# CONFIG_FB_MBX is not set
+# CONFIG_FB_W100 is not set
+# CONFIG_FB_VIRTUAL is not set
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+# CONFIG_LCD_CLASS_DEVICE is not set
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+# CONFIG_BACKLIGHT_CORGI is not set
+CONFIG_BACKLIGHT_LOCOMO=y
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
+CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y
+CONFIG_FONTS=y
+# CONFIG_FONT_8x8 is not set
+# CONFIG_FONT_8x16 is not set
+# CONFIG_FONT_6x11 is not set
+# CONFIG_FONT_7x14 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+# CONFIG_FONT_ACORN_8x8 is not set
+CONFIG_FONT_MINI_4x6=y
+# CONFIG_FONT_SUN8x16 is not set
+# CONFIG_FONT_SUN12x22 is not set
+# CONFIG_FONT_10x18 is not set
+CONFIG_LOGO=y
+# CONFIG_LOGO_LINUX_MONO is not set
+# CONFIG_LOGO_LINUX_VGA16 is not set
+# CONFIG_LOGO_LINUX_CLUT224 is not set
+CONFIG_LOGO_OHAND_CLUT224=y
+
+#
+# Sound
+#
+CONFIG_SOUND=m
+
+#
+# Advanced Linux Sound Architecture
+#
+CONFIG_SND=m
+CONFIG_SND_TIMER=m
+CONFIG_SND_PCM=m
+CONFIG_SND_HWDEP=m
+CONFIG_SND_RAWMIDI=m
+CONFIG_SND_SEQUENCER=m
+# CONFIG_SND_SEQ_DUMMY is not set
+CONFIG_SND_OSSEMUL=y
+CONFIG_SND_MIXER_OSS=m
+CONFIG_SND_PCM_OSS=m
+CONFIG_SND_PCM_OSS_PLUGINS=y
+# CONFIG_SND_SEQUENCER_OSS is not set
+# CONFIG_SND_DYNAMIC_MINORS is not set
+CONFIG_SND_SUPPORT_OLD_API=y
+CONFIG_SND_VERBOSE_PROCFS=y
+# CONFIG_SND_VERBOSE_PRINTK is not set
+# CONFIG_SND_DEBUG is not set
+
+#
+# Generic devices
+#
+# CONFIG_SND_AC97_CODEC is not set
+# CONFIG_SND_DUMMY is not set
+# CONFIG_SND_VIRMIDI is not set
+# CONFIG_SND_MTPAV is not set
+# CONFIG_SND_SERIAL_U16550 is not set
+# CONFIG_SND_MPU401 is not set
+
+#
+# ALSA ARM devices
+#
+CONFIG_SND_PXA2XX_PCM=m
+CONFIG_SND_PXA2XX_AC97=m
+
+#
+# USB devices
+#
+CONFIG_SND_USB_AUDIO=m
+# CONFIG_SND_USB_CAIAQ is not set
+
+#
+# PCMCIA devices
+#
+# CONFIG_SND_VXPOCKET is not set
+# CONFIG_SND_PDAUDIOCF is not set
+
+#
+# System on Chip audio support
+#
+CONFIG_SND_SOC=m
+CONFIG_SND_PXA2XX_SOC=m
+CONFIG_SND_PXA2XX_SOC_I2S=m
+CONFIG_SND_PXA2XX_SOC_POODLE=m
+
+#
+# SoC Audio support for SuperH
+#
+CONFIG_SND_SOC_WM8731=m
+
+#
+# Open Sound System
+#
+# CONFIG_SOUND_PRIME is not set
+# CONFIG_AC97_BUS is not set
+CONFIG_HID_SUPPORT=y
+CONFIG_HID=m
+# CONFIG_HID_DEBUG is not set
+# CONFIG_HIDRAW is not set
+
+#
+# USB Input Devices
+#
+CONFIG_USB_HID=m
+# CONFIG_USB_HIDINPUT_POWERBOOK is not set
+# CONFIG_HID_FF is not set
+# CONFIG_USB_HIDDEV is not set
+
+#
+# USB HID Boot Protocol drivers
+#
+CONFIG_USB_KBD=m
+CONFIG_USB_MOUSE=m
+CONFIG_USB_SUPPORT=y
+CONFIG_USB_ARCH_HAS_HCD=y
+# CONFIG_USB_ARCH_HAS_OHCI is not set
+# CONFIG_USB_ARCH_HAS_EHCI is not set
+CONFIG_USB=m
+# CONFIG_USB_DEBUG is not set
+
+#
+# Miscellaneous USB options
+#
+CONFIG_USB_DEVICEFS=y
+CONFIG_USB_DEVICE_CLASS=y
+# CONFIG_USB_DYNAMIC_MINORS is not set
+# CONFIG_USB_SUSPEND is not set
+# CONFIG_USB_PERSIST is not set
+# CONFIG_USB_OTG is not set
+
+#
+# USB Host Controller Drivers
+#
+# CONFIG_USB_ISP116X_HCD is not set
+CONFIG_USB_SL811_HCD=m
+CONFIG_USB_SL811_CS=m
+# CONFIG_USB_R8A66597_HCD is not set
+
+#
+# USB Device Class drivers
+#
+CONFIG_USB_ACM=m
+CONFIG_USB_PRINTER=m
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
+#
+
+#
+# may also be needed; see USB_STORAGE Help for more information
+#
+CONFIG_USB_STORAGE=m
+# CONFIG_USB_STORAGE_DEBUG is not set
+# CONFIG_USB_STORAGE_DATAFAB is not set
+# CONFIG_USB_STORAGE_FREECOM is not set
+# CONFIG_USB_STORAGE_ISD200 is not set
+# CONFIG_USB_STORAGE_DPCM is not set
+# CONFIG_USB_STORAGE_USBAT is not set
+# CONFIG_USB_STORAGE_SDDR09 is not set
+# CONFIG_USB_STORAGE_SDDR55 is not set
+# CONFIG_USB_STORAGE_JUMPSHOT is not set
+# CONFIG_USB_STORAGE_ALAUDA is not set
+# CONFIG_USB_STORAGE_KARMA is not set
+# CONFIG_USB_LIBUSUAL is not set
+
+#
+# USB Imaging devices
+#
+CONFIG_USB_MDC800=m
+CONFIG_USB_MICROTEK=m
+CONFIG_USB_MON=y
+
+#
+# USB port drivers
+#
+
+#
+# USB Serial Converter support
+#
+CONFIG_USB_SERIAL=m
+CONFIG_USB_SERIAL_GENERIC=y
+# CONFIG_USB_SERIAL_AIRCABLE is not set
+# CONFIG_USB_SERIAL_AIRPRIME is not set
+# CONFIG_USB_SERIAL_ARK3116 is not set
+CONFIG_USB_SERIAL_BELKIN=m
+# CONFIG_USB_SERIAL_CH341 is not set
+# CONFIG_USB_SERIAL_WHITEHEAT is not set
+CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m
+CONFIG_USB_SERIAL_CP2101=m
+CONFIG_USB_SERIAL_CYPRESS_M8=m
+CONFIG_USB_SERIAL_EMPEG=m
+CONFIG_USB_SERIAL_FTDI_SIO=m
+# CONFIG_USB_SERIAL_FUNSOFT is not set
+CONFIG_USB_SERIAL_VISOR=m
+CONFIG_USB_SERIAL_IPAQ=m
+CONFIG_USB_SERIAL_IR=m
+CONFIG_USB_SERIAL_EDGEPORT=m
+CONFIG_USB_SERIAL_EDGEPORT_TI=m
+CONFIG_USB_SERIAL_GARMIN=m
+CONFIG_USB_SERIAL_IPW=m
+CONFIG_USB_SERIAL_KEYSPAN_PDA=m
+CONFIG_USB_SERIAL_KEYSPAN=m
+# CONFIG_USB_SERIAL_KEYSPAN_MPR is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19QW is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19QI is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA49WLC is not set
+CONFIG_USB_SERIAL_KLSI=m
+CONFIG_USB_SERIAL_KOBIL_SCT=m
+CONFIG_USB_SERIAL_MCT_U232=m
+# CONFIG_USB_SERIAL_MOS7720 is not set
+# CONFIG_USB_SERIAL_MOS7840 is not set
+# CONFIG_USB_SERIAL_NAVMAN is not set
+CONFIG_USB_SERIAL_PL2303=m
+# CONFIG_USB_SERIAL_OTI6858 is not set
+# CONFIG_USB_SERIAL_HP4X is not set
+CONFIG_USB_SERIAL_SAFE=m
+# CONFIG_USB_SERIAL_SAFE_PADDED is not set
+# CONFIG_USB_SERIAL_SIERRAWIRELESS is not set
+CONFIG_USB_SERIAL_TI=m
+CONFIG_USB_SERIAL_CYBERJACK=m
+CONFIG_USB_SERIAL_XIRCOM=m
+# CONFIG_USB_SERIAL_OPTION is not set
+CONFIG_USB_SERIAL_OMNINET=m
+# CONFIG_USB_SERIAL_DEBUG is not set
+CONFIG_USB_EZUSB=y
+
+#
+# USB Miscellaneous drivers
+#
+CONFIG_USB_EMI62=m
+CONFIG_USB_EMI26=m
+# CONFIG_USB_ADUTUX is not set
+CONFIG_USB_AUERSWALD=m
+CONFIG_USB_RIO500=m
+CONFIG_USB_LEGOTOWER=m
+CONFIG_USB_LCD=m
+# CONFIG_USB_BERRY_CHARGE is not set
+CONFIG_USB_LED=m
+# CONFIG_USB_CYPRESS_CY7C63 is not set
+CONFIG_USB_CYTHERM=m
+# CONFIG_USB_PHIDGET is not set
+CONFIG_USB_IDMOUSE=m
+# CONFIG_USB_FTDI_ELAN is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+# CONFIG_USB_LD is not set
+# CONFIG_USB_TRANCEVIBRATOR is not set
+# CONFIG_USB_IOWARRIOR is not set
+# CONFIG_USB_TEST is not set
+
+#
+# USB DSL modem support
+#
+
+#
+# USB Gadget Support
+#
+CONFIG_USB_GADGET=y
+# CONFIG_USB_GADGET_DEBUG is not set
+# CONFIG_USB_GADGET_DEBUG_FILES is not set
+CONFIG_USB_GADGET_SELECTED=y
+# CONFIG_USB_GADGET_AMD5536UDC is not set
+# CONFIG_USB_GADGET_ATMEL_USBA is not set
+# CONFIG_USB_GADGET_FSL_USB2 is not set
+# CONFIG_USB_GADGET_NET2280 is not set
+CONFIG_USB_GADGET_PXA2XX=y
+CONFIG_USB_PXA2XX=y
+# CONFIG_USB_PXA2XX_SMALL is not set
+# CONFIG_USB_GADGET_M66592 is not set
+# CONFIG_USB_GADGET_PXA27X is not set
+# CONFIG_USB_GADGET_GOKU is not set
+# CONFIG_USB_GADGET_LH7A40X is not set
+# CONFIG_USB_GADGET_OMAP is not set
+# CONFIG_USB_GADGET_S3C2410 is not set
+# CONFIG_USB_GADGET_AT91 is not set
+# CONFIG_USB_GADGET_DUMMY_HCD is not set
+# CONFIG_USB_GADGET_DUALSPEED is not set
+CONFIG_USB_ZERO=m
+CONFIG_USB_ETH=m
+CONFIG_USB_ETH_RNDIS=y
+CONFIG_USB_GADGETFS=m
+CONFIG_USB_FILE_STORAGE=m
+# CONFIG_USB_FILE_STORAGE_TEST is not set
+CONFIG_USB_G_SERIAL=m
+# CONFIG_USB_MIDI_GADGET is not set
+CONFIG_MMC=y
+# CONFIG_MMC_DEBUG is not set
+CONFIG_MMC_UNSAFE_RESUME=y
+
+#
+# MMC/SD Card Drivers
+#
+CONFIG_MMC_BLOCK=y
+CONFIG_MMC_BLOCK_BOUNCE=y
+# CONFIG_SDIO_UART is not set
+
+#
+# MMC/SD Host Controller Drivers
+#
+CONFIG_MMC_PXA=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+
+#
+# LED drivers
+#
+CONFIG_LEDS_LOCOMO=y
+# CONFIG_LEDS_TOSA is not set
+# CONFIG_LEDS_GPIO is not set
+
+#
+# LED Triggers
+#
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=y
+CONFIG_LEDS_TRIGGER_IDE_DISK=y
+# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set
+CONFIG_RTC_LIB=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_HCTOSYS=y
+CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
+# CONFIG_RTC_DEBUG is not set
+
+#
+# RTC interfaces
+#
+CONFIG_RTC_INTF_SYSFS=y
+CONFIG_RTC_INTF_PROC=y
+CONFIG_RTC_INTF_DEV=y
+# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
+# CONFIG_RTC_DRV_TEST is not set
+
+#
+# I2C RTC drivers
+#
+# CONFIG_RTC_DRV_DS1307 is not set
+# CONFIG_RTC_DRV_DS1374 is not set
+# CONFIG_RTC_DRV_DS1672 is not set
+# CONFIG_RTC_DRV_MAX6900 is not set
+# CONFIG_RTC_DRV_RS5C372 is not set
+# CONFIG_RTC_DRV_ISL1208 is not set
+# CONFIG_RTC_DRV_X1205 is not set
+# CONFIG_RTC_DRV_PCF8563 is not set
+# CONFIG_RTC_DRV_PCF8583 is not set
+# CONFIG_RTC_DRV_M41T80 is not set
+
+#
+# SPI RTC drivers
+#
+
+#
+# Platform RTC drivers
+#
+# CONFIG_RTC_DRV_CMOS is not set
+# CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_STK17TA8 is not set
+# CONFIG_RTC_DRV_DS1742 is not set
+# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_M48T59 is not set
+# CONFIG_RTC_DRV_V3020 is not set
+
+#
+# on-CPU RTC drivers
+#
+CONFIG_RTC_DRV_SA1100=y
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+# CONFIG_EXT2_FS_XIP is not set
+CONFIG_EXT3_FS=m
+# CONFIG_EXT3_FS_XATTR is not set
+# CONFIG_EXT4DEV_FS is not set
+CONFIG_JBD=m
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+CONFIG_FS_POSIX_ACL=y
+# CONFIG_XFS_FS is not set
+# CONFIG_GFS2_FS is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_ROMFS_FS is not set
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_QUOTA is not set
+CONFIG_DNOTIFY=y
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+# CONFIG_MSDOS_FS is not set
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_DEBUG=0
+CONFIG_JFFS2_FS_WRITEBUFFER=y
+# CONFIG_JFFS2_FS_WBUF_VERIFY is not set
+CONFIG_JFFS2_SUMMARY=y
+# CONFIG_JFFS2_FS_XATTR is not set
+# CONFIG_JFFS2_SYSFS is not set
+CONFIG_JFFS2_COMPRESSION_OPTIONS=y
+CONFIG_JFFS2_ZLIB=y
+CONFIG_JFFS2_LZO=y
+CONFIG_JFFS2_RTIME=y
+CONFIG_JFFS2_RUBIN=y
+# CONFIG_JFFS2_CMODE_NONE is not set
+CONFIG_JFFS2_CMODE_PRIORITY=y
+# CONFIG_JFFS2_CMODE_SIZE is not set
+# CONFIG_JFFS2_CMODE_FAVOURLZO is not set
+CONFIG_CRAMFS=m
+CONFIG_SQUASHFS=m
+# CONFIG_SQUASHFS_EMBEDDED is not set
+CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3
+# CONFIG_VXFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+CONFIG_NFS_FS=m
+CONFIG_NFS_V3=y
+# CONFIG_NFS_V3_ACL is not set
+CONFIG_NFS_V4=y
+# CONFIG_NFS_DIRECTIO is not set
+CONFIG_NFSD=m
+CONFIG_NFSD_V3=y
+# CONFIG_NFSD_V3_ACL is not set
+CONFIG_NFSD_V4=y
+CONFIG_NFSD_TCP=y
+CONFIG_LOCKD=m
+CONFIG_LOCKD_V4=y
+CONFIG_EXPORTFS=m
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=m
+CONFIG_SUNRPC_GSS=m
+# CONFIG_SUNRPC_BIND34 is not set
+CONFIG_RPCSEC_GSS_KRB5=m
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+CONFIG_SMB_FS=m
+CONFIG_SMB_NLS_DEFAULT=y
+CONFIG_SMB_NLS_REMOTE="cp437"
+CONFIG_CIFS=m
+# CONFIG_CIFS_STATS is not set
+# CONFIG_CIFS_WEAK_PW_HASH is not set
+# CONFIG_CIFS_XATTR is not set
+# CONFIG_CIFS_DEBUG2 is not set
+# CONFIG_CIFS_EXPERIMENTAL is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_MINIX_SUBPARTITION is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_UNIXWARE_DISKLABEL is not set
+# CONFIG_LDM_PARTITION is not set
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_ULTRIX_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_KARMA_PARTITION is not set
+# CONFIG_EFI_PARTITION is not set
+# CONFIG_SYSV68_PARTITION is not set
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="cp437"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_737=m
+CONFIG_NLS_CODEPAGE_775=m
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
+CONFIG_NLS_CODEPAGE_855=m
+CONFIG_NLS_CODEPAGE_857=m
+CONFIG_NLS_CODEPAGE_860=m
+CONFIG_NLS_CODEPAGE_861=m
+CONFIG_NLS_CODEPAGE_862=m
+CONFIG_NLS_CODEPAGE_863=m
+CONFIG_NLS_CODEPAGE_864=m
+CONFIG_NLS_CODEPAGE_865=m
+CONFIG_NLS_CODEPAGE_866=m
+CONFIG_NLS_CODEPAGE_869=m
+CONFIG_NLS_CODEPAGE_936=m
+CONFIG_NLS_CODEPAGE_950=m
+CONFIG_NLS_CODEPAGE_932=m
+CONFIG_NLS_CODEPAGE_949=m
+CONFIG_NLS_CODEPAGE_874=m
+CONFIG_NLS_ISO8859_8=m
+CONFIG_NLS_CODEPAGE_1250=m
+CONFIG_NLS_CODEPAGE_1251=m
+CONFIG_NLS_ASCII=m
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_ISO8859_2=m
+CONFIG_NLS_ISO8859_3=m
+CONFIG_NLS_ISO8859_4=m
+CONFIG_NLS_ISO8859_5=m
+CONFIG_NLS_ISO8859_6=m
+CONFIG_NLS_ISO8859_7=m
+CONFIG_NLS_ISO8859_9=m
+CONFIG_NLS_ISO8859_13=m
+CONFIG_NLS_ISO8859_14=m
+CONFIG_NLS_ISO8859_15=m
+CONFIG_NLS_KOI8_R=m
+CONFIG_NLS_KOI8_U=m
+CONFIG_NLS_UTF8=y
+# CONFIG_DLM is not set
+CONFIG_INSTRUMENTATION=y
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=m
+# CONFIG_MARKERS is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_PRINTK_TIME is not set
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_DEBUG_FS is not set
+# CONFIG_HEADERS_CHECK is not set
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_DEBUG_SHIRQ is not set
+# CONFIG_DETECT_SOFTLOCKUP is not set
+# CONFIG_SCHED_DEBUG is not set
+# CONFIG_SCHEDSTATS is not set
+CONFIG_TIMER_STATS=y
+# CONFIG_DEBUG_SLAB is not set
+# CONFIG_DEBUG_PREEMPT is not set
+# CONFIG_DEBUG_RT_MUTEXES is not set
+# CONFIG_RT_MUTEX_TESTER is not set
+# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_DEBUG_MUTEXES is not set
+# CONFIG_DEBUG_LOCK_ALLOC is not set
+# CONFIG_PROVE_LOCKING is not set
+# CONFIG_LOCK_STAT is not set
+# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
+# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
+# CONFIG_DEBUG_KOBJECT is not set
+CONFIG_DEBUG_BUGVERBOSE=y
+# CONFIG_DEBUG_INFO is not set
+# CONFIG_DEBUG_VM is not set
+# CONFIG_DEBUG_LIST is not set
+# CONFIG_DEBUG_SG is not set
+CONFIG_FRAME_POINTER=y
+# CONFIG_FORCED_INLINING is not set
+# CONFIG_BOOT_PRINTK_DELAY is not set
+# CONFIG_RCU_TORTURE_TEST is not set
+# CONFIG_FAULT_INJECTION is not set
+# CONFIG_SAMPLES is not set
+# CONFIG_DEBUG_USER is not set
+CONFIG_DEBUG_ERRORS=y
+# CONFIG_DEBUG_LL is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+CONFIG_CRYPTO=y
+CONFIG_CRYPTO_ALGAPI=m
+CONFIG_CRYPTO_BLKCIPHER=m
+CONFIG_CRYPTO_HASH=m
+CONFIG_CRYPTO_MANAGER=m
+CONFIG_CRYPTO_HMAC=m
+# CONFIG_CRYPTO_XCBC is not set
+CONFIG_CRYPTO_NULL=m
+CONFIG_CRYPTO_MD4=m
+CONFIG_CRYPTO_MD5=m
+CONFIG_CRYPTO_SHA1=m
+CONFIG_CRYPTO_SHA256=m
+CONFIG_CRYPTO_SHA512=m
+CONFIG_CRYPTO_WP512=m
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_GF128MUL is not set
+CONFIG_CRYPTO_ECB=m
+CONFIG_CRYPTO_CBC=m
+CONFIG_CRYPTO_PCBC=m
+# CONFIG_CRYPTO_LRW is not set
+# CONFIG_CRYPTO_XTS is not set
+# CONFIG_CRYPTO_CRYPTD is not set
+CONFIG_CRYPTO_DES=m
+# CONFIG_CRYPTO_FCRYPT is not set
+CONFIG_CRYPTO_BLOWFISH=m
+CONFIG_CRYPTO_TWOFISH=m
+CONFIG_CRYPTO_TWOFISH_COMMON=m
+CONFIG_CRYPTO_SERPENT=m
+CONFIG_CRYPTO_AES=m
+CONFIG_CRYPTO_CAST5=m
+CONFIG_CRYPTO_CAST6=m
+CONFIG_CRYPTO_TEA=m
+CONFIG_CRYPTO_ARC4=m
+CONFIG_CRYPTO_KHAZAD=m
+CONFIG_CRYPTO_ANUBIS=m
+# CONFIG_CRYPTO_SEED is not set
+CONFIG_CRYPTO_DEFLATE=m
+CONFIG_CRYPTO_LZO=m
+CONFIG_CRYPTO_MICHAEL_MIC=m
+CONFIG_CRYPTO_CRC32C=m
+CONFIG_CRYPTO_CAMELLIA=m
+CONFIG_CRYPTO_TEST=m
+# CONFIG_CRYPTO_AUTHENC is not set
+# CONFIG_CRYPTO_HW is not set
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+CONFIG_CRC_CCITT=y
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
+CONFIG_LIBCRC32C=m
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_LZO_COMPRESS=y
+CONFIG_LZO_DECOMPRESS=y
+CONFIG_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
diff --git a/recipes/linux/linux-rp-2.6.26/defconfig-qemuarm b/recipes/linux/linux-rp-2.6.26/defconfig-qemuarm
new file mode 100644
index 0000000000..ae7ef52d31
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.26/defconfig-qemuarm
@@ -0,0 +1,1582 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.24-rc8
+# Sun Jan 20 22:28:17 2008
+#
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+# CONFIG_GENERIC_GPIO is not set
+CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_MMU=y
+# CONFIG_NO_IOPORT is not set
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+# CONFIG_ARCH_HAS_ILOG2_U32 is not set
+# CONFIG_ARCH_HAS_ILOG2_U64 is not set
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_ZONE_DMA=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_LOCK_KERNEL=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+# CONFIG_POSIX_MQUEUE is not set
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_BSD_PROCESS_ACCT_V3=y
+# CONFIG_TASKSTATS is not set
+# CONFIG_USER_NS is not set
+# CONFIG_PID_NS is not set
+# CONFIG_AUDIT is not set
+# CONFIG_IKCONFIG is not set
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_CGROUPS is not set
+CONFIG_FAIR_GROUP_SCHED=y
+CONFIG_FAIR_USER_SCHED=y
+# CONFIG_FAIR_CGROUP_SCHED is not set
+CONFIG_SYSFS_DEPRECATED=y
+# CONFIG_RELAY is not set
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+CONFIG_EMBEDDED=y
+CONFIG_UID16=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_ANON_INODES=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLAB=y
+# CONFIG_SLUB is not set
+# CONFIG_SLOB is not set
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+# CONFIG_TINY_SHMEM is not set
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+CONFIG_BLOCK=y
+# CONFIG_LBD is not set
+# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_LSF is not set
+# CONFIG_BLK_DEV_BSG is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=m
+CONFIG_IOSCHED_CFQ=m
+CONFIG_DEFAULT_AS=y
+# CONFIG_DEFAULT_DEADLINE is not set
+# CONFIG_DEFAULT_CFQ is not set
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="anticipatory"
+
+#
+# System Type
+#
+# CONFIG_ARCH_AAEC2000 is not set
+# CONFIG_ARCH_INTEGRATOR is not set
+# CONFIG_ARCH_REALVIEW is not set
+CONFIG_ARCH_VERSATILE=y
+# CONFIG_ARCH_AT91 is not set
+# CONFIG_ARCH_CLPS7500 is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_CO285 is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE is not set
+# CONFIG_ARCH_NETX is not set
+# CONFIG_ARCH_H720X is not set
+# CONFIG_ARCH_IMX is not set
+# CONFIG_ARCH_IOP13XX is not set
+# CONFIG_ARCH_IOP32X is not set
+# CONFIG_ARCH_IOP33X is not set
+# CONFIG_ARCH_IXP23XX is not set
+# CONFIG_ARCH_IXP2000 is not set
+# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_L7200 is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_NS9XXX is not set
+# CONFIG_ARCH_MXC is not set
+# CONFIG_ARCH_PNX4008 is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP is not set
+
+#
+# Boot options
+#
+
+#
+# Power management
+#
+
+#
+# Versatile platform type
+#
+CONFIG_ARCH_VERSATILE_PB=y
+# CONFIG_MACH_VERSATILE_AB is not set
+
+#
+# Processor Type
+#
+CONFIG_CPU_32=y
+CONFIG_CPU_ARM926T=y
+# CONFIG_CPU_V6 is not set
+CONFIG_CPU_32v5=y
+CONFIG_CPU_ABRT_EV5TJ=y
+CONFIG_CPU_CACHE_VIVT=y
+CONFIG_CPU_COPY_V4WB=y
+CONFIG_CPU_TLB_V4WBI=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+
+#
+# Processor Features
+#
+CONFIG_ARM_THUMB=y
+# CONFIG_CPU_ICACHE_DISABLE is not set
+# CONFIG_CPU_DCACHE_DISABLE is not set
+# CONFIG_CPU_DCACHE_WRITETHROUGH is not set
+# CONFIG_CPU_CACHE_ROUND_ROBIN is not set
+# CONFIG_OUTER_CACHE is not set
+CONFIG_ARM_VIC=y
+CONFIG_ICST307=y
+
+#
+# Bus support
+#
+CONFIG_ARM_AMBA=y
+CONFIG_PCI=y
+CONFIG_PCI_SYSCALL=y
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+CONFIG_PCI_LEGACY=y
+# CONFIG_PCI_DEBUG is not set
+# CONFIG_PCCARD is not set
+
+#
+# Kernel Features
+#
+# CONFIG_TICK_ONESHOT is not set
+# CONFIG_NO_HZ is not set
+# CONFIG_HIGH_RES_TIMERS is not set
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_PREEMPT=y
+CONFIG_HZ=100
+CONFIG_AEABI=y
+CONFIG_OABI_COMPAT=y
+# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+# CONFIG_SPARSEMEM_STATIC is not set
+# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set
+CONFIG_SPLIT_PTLOCK_CPUS=4096
+# CONFIG_RESOURCES_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=1
+CONFIG_BOUNCE=y
+CONFIG_VIRT_TO_BUS=y
+CONFIG_LEDS=y
+CONFIG_LEDS_CPU=y
+CONFIG_ALIGNMENT_TRAP=y
+
+#
+# Boot options
+#
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="console=ttyAMA0,115200n8 console=tty1 noinitrd root=/dev/mtdblock2 rootfstype=jffs2 dyntick=enable debug"
+# CONFIG_XIP_KERNEL is not set
+CONFIG_KEXEC=y
+CONFIG_ATAGS_PROC=y
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+CONFIG_FPE_NWFPE=y
+# CONFIG_FPE_NWFPE_XP is not set
+# CONFIG_FPE_FASTFPE is not set
+CONFIG_VFP=y
+
+#
+# Userspace binary formats
+#
+CONFIG_BINFMT_ELF=y
+# CONFIG_BINFMT_AOUT is not set
+# CONFIG_BINFMT_MISC is not set
+
+#
+# Power management options
+#
+CONFIG_PM=y
+# CONFIG_PM_LEGACY is not set
+# CONFIG_PM_DEBUG is not set
+CONFIG_PM_SLEEP=y
+CONFIG_SUSPEND_UP_POSSIBLE=y
+CONFIG_SUSPEND=y
+# CONFIG_APM_EMULATION is not set
+
+#
+# Networking
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=m
+CONFIG_PACKET_MMAP=y
+CONFIG_UNIX=y
+CONFIG_XFRM=y
+# CONFIG_XFRM_USER is not set
+# CONFIG_XFRM_SUB_POLICY is not set
+# CONFIG_XFRM_MIGRATE is not set
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_ARPD is not set
+CONFIG_SYN_COOKIES=y
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+CONFIG_INET_TUNNEL=m
+CONFIG_INET_XFRM_MODE_TRANSPORT=m
+CONFIG_INET_XFRM_MODE_TUNNEL=m
+CONFIG_INET_XFRM_MODE_BEET=m
+# CONFIG_INET_LRO is not set
+CONFIG_INET_DIAG=m
+CONFIG_INET_TCP_DIAG=m
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+# CONFIG_IP_VS is not set
+CONFIG_IPV6=m
+# CONFIG_IPV6_PRIVACY is not set
+# CONFIG_IPV6_ROUTER_PREF is not set
+# CONFIG_IPV6_OPTIMISTIC_DAD is not set
+CONFIG_INET6_AH=m
+CONFIG_INET6_ESP=m
+CONFIG_INET6_IPCOMP=m
+# CONFIG_IPV6_MIP6 is not set
+CONFIG_INET6_XFRM_TUNNEL=m
+CONFIG_INET6_TUNNEL=m
+CONFIG_INET6_XFRM_MODE_TRANSPORT=m
+CONFIG_INET6_XFRM_MODE_TUNNEL=m
+CONFIG_INET6_XFRM_MODE_BEET=m
+# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set
+CONFIG_IPV6_SIT=m
+CONFIG_IPV6_TUNNEL=m
+# CONFIG_IPV6_MULTIPLE_TABLES is not set
+# CONFIG_NETWORK_SECMARK is not set
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+
+#
+# Core Netfilter Configuration
+#
+# CONFIG_NETFILTER_NETLINK is not set
+# CONFIG_NF_CONNTRACK_ENABLED is not set
+# CONFIG_NF_CONNTRACK is not set
+CONFIG_NETFILTER_XTABLES=m
+# CONFIG_NETFILTER_XT_TARGET_CLASSIFY is not set
+# CONFIG_NETFILTER_XT_TARGET_DSCP is not set
+# CONFIG_NETFILTER_XT_TARGET_MARK is not set
+# CONFIG_NETFILTER_XT_TARGET_NFQUEUE is not set
+# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set
+# CONFIG_NETFILTER_XT_TARGET_TRACE is not set
+# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set
+# CONFIG_NETFILTER_XT_MATCH_COMMENT is not set
+# CONFIG_NETFILTER_XT_MATCH_DCCP is not set
+# CONFIG_NETFILTER_XT_MATCH_DSCP is not set
+# CONFIG_NETFILTER_XT_MATCH_ESP is not set
+# CONFIG_NETFILTER_XT_MATCH_LENGTH is not set
+# CONFIG_NETFILTER_XT_MATCH_LIMIT is not set
+# CONFIG_NETFILTER_XT_MATCH_MAC is not set
+# CONFIG_NETFILTER_XT_MATCH_MARK is not set
+# CONFIG_NETFILTER_XT_MATCH_POLICY is not set
+# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set
+# CONFIG_NETFILTER_XT_MATCH_PKTTYPE is not set
+# CONFIG_NETFILTER_XT_MATCH_QUOTA is not set
+# CONFIG_NETFILTER_XT_MATCH_REALM is not set
+# CONFIG_NETFILTER_XT_MATCH_SCTP is not set
+# CONFIG_NETFILTER_XT_MATCH_STATISTIC is not set
+# CONFIG_NETFILTER_XT_MATCH_STRING is not set
+# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set
+# CONFIG_NETFILTER_XT_MATCH_TIME is not set
+# CONFIG_NETFILTER_XT_MATCH_U32 is not set
+# CONFIG_NETFILTER_XT_MATCH_HASHLIMIT is not set
+
+#
+# IP: Netfilter Configuration
+#
+CONFIG_IP_NF_QUEUE=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_IPRANGE=m
+CONFIG_IP_NF_MATCH_TOS=m
+CONFIG_IP_NF_MATCH_RECENT=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_AH=m
+CONFIG_IP_NF_MATCH_TTL=m
+CONFIG_IP_NF_MATCH_OWNER=m
+CONFIG_IP_NF_MATCH_ADDRTYPE=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_LOG=m
+CONFIG_IP_NF_TARGET_ULOG=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_TOS=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_TTL=m
+CONFIG_IP_NF_RAW=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+
+#
+# IPv6: Netfilter Configuration (EXPERIMENTAL)
+#
+# CONFIG_IP6_NF_QUEUE is not set
+# CONFIG_IP6_NF_IPTABLES is not set
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_SCTP is not set
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_SCHED is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+CONFIG_IRDA=m
+
+#
+# IrDA protocols
+#
+CONFIG_IRLAN=m
+CONFIG_IRNET=m
+CONFIG_IRCOMM=m
+# CONFIG_IRDA_ULTRA is not set
+
+#
+# IrDA options
+#
+# CONFIG_IRDA_CACHE_LAST_LSAP is not set
+# CONFIG_IRDA_FAST_RR is not set
+# CONFIG_IRDA_DEBUG is not set
+
+#
+# Infrared-port device drivers
+#
+
+#
+# SIR device drivers
+#
+# CONFIG_IRTTY_SIR is not set
+
+#
+# Dongle support
+#
+# CONFIG_KINGSUN_DONGLE is not set
+# CONFIG_KSDAZZLE_DONGLE is not set
+# CONFIG_KS959_DONGLE is not set
+
+#
+# Old SIR device drivers
+#
+# CONFIG_IRPORT_SIR is not set
+
+#
+# Old Serial dongle support
+#
+
+#
+# FIR device drivers
+#
+# CONFIG_USB_IRDA is not set
+# CONFIG_SIGMATEL_FIR is not set
+# CONFIG_TOSHIBA_FIR is not set
+# CONFIG_VLSI_FIR is not set
+# CONFIG_MCS_FIR is not set
+CONFIG_BT=m
+CONFIG_BT_L2CAP=m
+CONFIG_BT_SCO=m
+CONFIG_BT_RFCOMM=m
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=m
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+CONFIG_BT_HIDP=m
+
+#
+# Bluetooth device drivers
+#
+CONFIG_BT_HCIUSB=m
+# CONFIG_BT_HCIUSB_SCO is not set
+CONFIG_BT_HCIUART=m
+CONFIG_BT_HCIUART_H4=y
+CONFIG_BT_HCIUART_BCSP=y
+# CONFIG_BT_HCIUART_LL is not set
+CONFIG_BT_HCIBCM203X=m
+CONFIG_BT_HCIBPA10X=m
+CONFIG_BT_HCIBFUSB=m
+CONFIG_BT_HCIVHCI=m
+# CONFIG_AF_RXRPC is not set
+
+#
+# Wireless
+#
+# CONFIG_CFG80211 is not set
+CONFIG_WIRELESS_EXT=y
+# CONFIG_MAC80211 is not set
+CONFIG_IEEE80211=m
+# CONFIG_IEEE80211_DEBUG is not set
+CONFIG_IEEE80211_CRYPT_WEP=m
+CONFIG_IEEE80211_CRYPT_CCMP=m
+CONFIG_IEEE80211_CRYPT_TKIP=m
+CONFIG_IEEE80211_SOFTMAC=m
+# CONFIG_IEEE80211_SOFTMAC_DEBUG is not set
+# CONFIG_RFKILL is not set
+# CONFIG_NET_9P is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+# CONFIG_DEBUG_DRIVER is not set
+# CONFIG_DEBUG_DEVRES is not set
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_CONNECTOR is not set
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+# CONFIG_MTD_CONCAT is not set
+CONFIG_MTD_PARTITIONS=y
+# CONFIG_MTD_REDBOOT_PARTS is not set
+CONFIG_MTD_CMDLINE_PARTS=y
+CONFIG_MTD_AFS_PARTS=y
+
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+# CONFIG_MTD_OOPS is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+CONFIG_MTD_CFI=y
+# CONFIG_MTD_JEDECPROBE is not set
+CONFIG_MTD_GEN_PROBE=y
+CONFIG_MTD_CFI_ADV_OPTIONS=y
+CONFIG_MTD_CFI_NOSWAP=y
+# CONFIG_MTD_CFI_BE_BYTE_SWAP is not set
+# CONFIG_MTD_CFI_LE_BYTE_SWAP is not set
+# CONFIG_MTD_CFI_GEOMETRY is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+# CONFIG_MTD_OTP is not set
+CONFIG_MTD_CFI_INTELEXT=y
+# CONFIG_MTD_CFI_AMDSTD is not set
+# CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
+# CONFIG_MTD_RAM is not set
+# CONFIG_MTD_ROM is not set
+# CONFIG_MTD_ABSENT is not set
+
+#
+# Mapping drivers for chip access
+#
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+# CONFIG_MTD_PHYSMAP is not set
+# CONFIG_MTD_ARM_INTEGRATOR is not set
+# CONFIG_MTD_INTEL_VR_NOR is not set
+# CONFIG_MTD_PLATRAM is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_PMC551 is not set
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLOCK2MTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+CONFIG_MTD_NAND=m
+CONFIG_MTD_NAND_VERIFY_WRITE=y
+# CONFIG_MTD_NAND_ECC_SMC is not set
+# CONFIG_MTD_NAND_MUSEUM_IDS is not set
+CONFIG_MTD_NAND_IDS=m
+# CONFIG_MTD_NAND_DISKONCHIP is not set
+# CONFIG_MTD_NAND_CAFE is not set
+# CONFIG_MTD_NAND_NANDSIM is not set
+# CONFIG_MTD_NAND_PLATFORM is not set
+# CONFIG_MTD_ALAUDA is not set
+# CONFIG_MTD_ONENAND is not set
+
+#
+# UBI - Unsorted block images
+#
+# CONFIG_MTD_UBI is not set
+# CONFIG_PARPORT is not set
+CONFIG_BLK_DEV=y
+# CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_CPQ_CISS_DA is not set
+# CONFIG_BLK_DEV_DAC960 is not set
+# CONFIG_BLK_DEV_UMEM is not set
+# CONFIG_BLK_DEV_COW_COMMON is not set
+CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_CRYPTOLOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_SX8 is not set
+# CONFIG_BLK_DEV_UB is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=51200
+CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+CONFIG_MISC_DEVICES=y
+# CONFIG_PHANTOM is not set
+# CONFIG_EEPROM_93CX6 is not set
+# CONFIG_SGI_IOC4 is not set
+# CONFIG_TIFM_CORE is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+CONFIG_SCSI=y
+CONFIG_SCSI_DMA=y
+# CONFIG_SCSI_TGT is not set
+# CONFIG_SCSI_NETLINK is not set
+CONFIG_SCSI_PROC_FS=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=y
+# CONFIG_CHR_DEV_ST is not set
+# CONFIG_CHR_DEV_OSST is not set
+# CONFIG_BLK_DEV_SR is not set
+# CONFIG_CHR_DEV_SG is not set
+# CONFIG_CHR_DEV_SCH is not set
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+CONFIG_SCSI_MULTI_LUN=y
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
+# CONFIG_SCSI_SCAN_ASYNC is not set
+CONFIG_SCSI_WAIT_SCAN=m
+
+#
+# SCSI Transports
+#
+CONFIG_SCSI_SPI_ATTRS=y
+# CONFIG_SCSI_FC_ATTRS is not set
+# CONFIG_SCSI_ISCSI_ATTRS is not set
+# CONFIG_SCSI_SAS_LIBSAS is not set
+# CONFIG_SCSI_SRP_ATTRS is not set
+CONFIG_SCSI_LOWLEVEL=y
+# CONFIG_ISCSI_TCP is not set
+# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
+# CONFIG_SCSI_3W_9XXX is not set
+# CONFIG_SCSI_ACARD is not set
+# CONFIG_SCSI_AACRAID is not set
+# CONFIG_SCSI_AIC7XXX is not set
+# CONFIG_SCSI_AIC7XXX_OLD is not set
+# CONFIG_SCSI_AIC79XX is not set
+# CONFIG_SCSI_AIC94XX is not set
+# CONFIG_SCSI_DPT_I2O is not set
+# CONFIG_SCSI_ADVANSYS is not set
+# CONFIG_SCSI_ARCMSR is not set
+# CONFIG_MEGARAID_NEWGEN is not set
+# CONFIG_MEGARAID_LEGACY is not set
+# CONFIG_MEGARAID_SAS is not set
+# CONFIG_SCSI_HPTIOP is not set
+# CONFIG_SCSI_DMX3191D is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_IPS is not set
+# CONFIG_SCSI_INITIO is not set
+# CONFIG_SCSI_INIA100 is not set
+# CONFIG_SCSI_STEX is not set
+CONFIG_SCSI_SYM53C8XX_2=y
+CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=1
+CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16
+CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64
+CONFIG_SCSI_SYM53C8XX_MMIO=y
+# CONFIG_SCSI_QLOGIC_1280 is not set
+# CONFIG_SCSI_QLA_FC is not set
+# CONFIG_SCSI_QLA_ISCSI is not set
+# CONFIG_SCSI_LPFC is not set
+# CONFIG_SCSI_DC395x is not set
+# CONFIG_SCSI_DC390T is not set
+# CONFIG_SCSI_NSP32 is not set
+# CONFIG_SCSI_DEBUG is not set
+# CONFIG_SCSI_SRP is not set
+# CONFIG_ATA is not set
+CONFIG_MD=y
+# CONFIG_BLK_DEV_MD is not set
+CONFIG_BLK_DEV_DM=m
+# CONFIG_DM_DEBUG is not set
+CONFIG_DM_CRYPT=m
+CONFIG_DM_SNAPSHOT=m
+CONFIG_DM_MIRROR=m
+CONFIG_DM_ZERO=m
+CONFIG_DM_MULTIPATH=m
+CONFIG_DM_MULTIPATH_EMC=m
+# CONFIG_DM_MULTIPATH_RDAC is not set
+# CONFIG_DM_MULTIPATH_HP is not set
+# CONFIG_DM_DELAY is not set
+# CONFIG_DM_UEVENT is not set
+# CONFIG_FUSION is not set
+
+#
+# IEEE 1394 (FireWire) support
+#
+# CONFIG_FIREWIRE is not set
+# CONFIG_IEEE1394 is not set
+# CONFIG_I2O is not set
+CONFIG_NETDEVICES=y
+# CONFIG_NETDEVICES_MULTIQUEUE is not set
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_MACVLAN is not set
+# CONFIG_EQUALIZER is not set
+CONFIG_TUN=m
+# CONFIG_VETH is not set
+# CONFIG_ARCNET is not set
+# CONFIG_PHYLIB is not set
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=y
+# CONFIG_AX88796 is not set
+# CONFIG_HAPPYMEAL is not set
+# CONFIG_SUNGEM is not set
+# CONFIG_CASSINI is not set
+# CONFIG_NET_VENDOR_3COM is not set
+CONFIG_SMC91X=y
+# CONFIG_DM9000 is not set
+# CONFIG_NET_TULIP is not set
+# CONFIG_HP100 is not set
+# CONFIG_IBM_NEW_EMAC_ZMII is not set
+# CONFIG_IBM_NEW_EMAC_RGMII is not set
+# CONFIG_IBM_NEW_EMAC_TAH is not set
+# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
+# CONFIG_NET_PCI is not set
+# CONFIG_B44 is not set
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
+# CONFIG_TR is not set
+
+#
+# Wireless LAN
+#
+# CONFIG_WLAN_PRE80211 is not set
+CONFIG_WLAN_80211=y
+# CONFIG_IPW2100 is not set
+# CONFIG_IPW2200 is not set
+# CONFIG_LIBERTAS is not set
+CONFIG_HERMES=m
+# CONFIG_PLX_HERMES is not set
+# CONFIG_TMD_HERMES is not set
+# CONFIG_NORTEL_HERMES is not set
+# CONFIG_PCI_HERMES is not set
+# CONFIG_ATMEL is not set
+# CONFIG_PRISM54 is not set
+# CONFIG_USB_ZD1201 is not set
+CONFIG_HOSTAP=m
+CONFIG_HOSTAP_FIRMWARE=y
+# CONFIG_HOSTAP_FIRMWARE_NVRAM is not set
+# CONFIG_HOSTAP_PLX is not set
+# CONFIG_HOSTAP_PCI is not set
+# CONFIG_BCM43XX is not set
+# CONFIG_ZD1211RW is not set
+
+#
+# USB Network Adapters
+#
+# CONFIG_USB_CATC is not set
+# CONFIG_USB_KAWETH is not set
+# CONFIG_USB_PEGASUS is not set
+# CONFIG_USB_RTL8150 is not set
+# CONFIG_USB_USBNET is not set
+# CONFIG_WAN is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+CONFIG_PPP=m
+# CONFIG_PPP_MULTILINK is not set
+# CONFIG_PPP_FILTER is not set
+CONFIG_PPP_ASYNC=m
+# CONFIG_PPP_SYNC_TTY is not set
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_BSDCOMP=m
+# CONFIG_PPP_MPPE is not set
+# CONFIG_PPPOE is not set
+# CONFIG_PPPOL2TP is not set
+# CONFIG_SLIP is not set
+CONFIG_SLHC=m
+# CONFIG_NET_FC is not set
+# CONFIG_SHAPER is not set
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_ISDN is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=m
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=640
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=480
+# CONFIG_INPUT_JOYDEV is not set
+CONFIG_INPUT_EVDEV=y
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+CONFIG_KEYBOARD_ATKBD=y
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+# CONFIG_KEYBOARD_XTKBD is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+CONFIG_INPUT_TABLET=y
+# CONFIG_TABLET_USB_ACECAD is not set
+# CONFIG_TABLET_USB_AIPTEK is not set
+# CONFIG_TABLET_USB_GTCO is not set
+# CONFIG_TABLET_USB_KBTAB is not set
+CONFIG_TABLET_USB_WACOM=y
+# CONFIG_INPUT_TOUCHSCREEN is not set
+CONFIG_INPUT_MISC=y
+# CONFIG_INPUT_ATI_REMOTE is not set
+# CONFIG_INPUT_ATI_REMOTE2 is not set
+# CONFIG_INPUT_KEYSPAN_REMOTE is not set
+# CONFIG_INPUT_POWERMATE is not set
+# CONFIG_INPUT_YEALINK is not set
+CONFIG_INPUT_UINPUT=m
+
+#
+# Hardware I/O ports
+#
+CONFIG_SERIO=y
+# CONFIG_SERIO_SERPORT is not set
+CONFIG_SERIO_AMBAKMI=y
+# CONFIG_SERIO_PCIPS2 is not set
+CONFIG_SERIO_LIBPS2=y
+# CONFIG_SERIO_RAW is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+# CONFIG_VT_HW_CONSOLE_BINDING is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_AMBA_PL011=y
+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+# CONFIG_SERIAL_JSM is not set
+CONFIG_UNIX98_PTYS=y
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_IPMI_HANDLER is not set
+CONFIG_HW_RANDOM=m
+# CONFIG_NVRAM is not set
+# CONFIG_R3964 is not set
+# CONFIG_APPLICOM is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+CONFIG_DEVPORT=y
+# CONFIG_I2C is not set
+
+#
+# SPI support
+#
+# CONFIG_SPI is not set
+# CONFIG_SPI_MASTER is not set
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+# CONFIG_WATCHDOG is not set
+
+#
+# Sonics Silicon Backplane
+#
+CONFIG_SSB_POSSIBLE=y
+# CONFIG_SSB is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_SM501 is not set
+# CONFIG_HTC_ASIC3 is not set
+# CONFIG_HTC_ASIC3_DS1WM is not set
+
+#
+# Multimedia devices
+#
+CONFIG_VIDEO_DEV=m
+CONFIG_VIDEO_V4L1=y
+CONFIG_VIDEO_V4L1_COMPAT=y
+CONFIG_VIDEO_V4L2=y
+CONFIG_VIDEO_CAPTURE_DRIVERS=y
+# CONFIG_VIDEO_ADV_DEBUG is not set
+CONFIG_VIDEO_HELPER_CHIPS_AUTO=y
+# CONFIG_VIDEO_VIVI is not set
+# CONFIG_VIDEO_CPIA is not set
+# CONFIG_VIDEO_CPIA2 is not set
+# CONFIG_VIDEO_STRADIS is not set
+CONFIG_V4L_USB_DRIVERS=y
+# CONFIG_USB_VICAM is not set
+# CONFIG_USB_IBMCAM is not set
+# CONFIG_USB_KONICAWC is not set
+# CONFIG_USB_QUICKCAM_MESSENGER is not set
+# CONFIG_USB_ET61X251 is not set
+CONFIG_USB_OV511=m
+CONFIG_USB_SE401=m
+CONFIG_USB_SN9C102=m
+CONFIG_USB_STV680=m
+# CONFIG_USB_ZC0301 is not set
+# CONFIG_USB_PWC is not set
+# CONFIG_USB_ZR364XX is not set
+CONFIG_RADIO_ADAPTERS=y
+# CONFIG_RADIO_GEMTEK_PCI is not set
+# CONFIG_RADIO_MAXIRADIO is not set
+# CONFIG_RADIO_MAESTRO is not set
+CONFIG_USB_DSBR=m
+# CONFIG_DVB_CORE is not set
+CONFIG_DAB=y
+CONFIG_USB_DABUSB=m
+
+#
+# Graphics support
+#
+# CONFIG_DRM is not set
+# CONFIG_VGASTATE is not set
+CONFIG_VIDEO_OUTPUT_CONTROL=m
+CONFIG_FB=y
+# CONFIG_FIRMWARE_EDID is not set
+# CONFIG_FB_DDC is not set
+CONFIG_FB_CFB_FILLRECT=y
+CONFIG_FB_CFB_COPYAREA=y
+CONFIG_FB_CFB_IMAGEBLIT=y
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+# CONFIG_FB_SYS_FILLRECT is not set
+# CONFIG_FB_SYS_COPYAREA is not set
+# CONFIG_FB_SYS_IMAGEBLIT is not set
+# CONFIG_FB_SYS_FOPS is not set
+# CONFIG_FB_DEFERRED_IO is not set
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_BACKLIGHT is not set
+# CONFIG_FB_MODE_HELPERS is not set
+# CONFIG_FB_TILEBLITTING is not set
+
+#
+# Frame buffer hardware drivers
+#
+# CONFIG_FB_CIRRUS is not set
+# CONFIG_FB_PM2 is not set
+CONFIG_FB_ARMCLCD=y
+# CONFIG_FB_CYBER2000 is not set
+# CONFIG_FB_ASILIANT is not set
+# CONFIG_FB_IMSTT is not set
+# CONFIG_FB_S1D13XXX is not set
+# CONFIG_FB_NVIDIA is not set
+# CONFIG_FB_RIVA is not set
+# CONFIG_FB_MATROX is not set
+# CONFIG_FB_RADEON is not set
+# CONFIG_FB_ATY128 is not set
+# CONFIG_FB_ATY is not set
+# CONFIG_FB_S3 is not set
+# CONFIG_FB_SAVAGE is not set
+# CONFIG_FB_SIS is not set
+# CONFIG_FB_NEOMAGIC is not set
+# CONFIG_FB_KYRO is not set
+# CONFIG_FB_3DFX is not set
+# CONFIG_FB_VOODOO1 is not set
+# CONFIG_FB_VT8623 is not set
+# CONFIG_FB_TRIDENT is not set
+# CONFIG_FB_ARK is not set
+# CONFIG_FB_PM3 is not set
+# CONFIG_FB_VIRTUAL is not set
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_LCD_CLASS_DEVICE=m
+CONFIG_BACKLIGHT_CLASS_DEVICE=m
+CONFIG_BACKLIGHT_CORGI=m
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
+CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y
+CONFIG_FONTS=y
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+# CONFIG_FONT_6x11 is not set
+# CONFIG_FONT_7x14 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+# CONFIG_FONT_ACORN_8x8 is not set
+# CONFIG_FONT_MINI_4x6 is not set
+# CONFIG_FONT_SUN8x16 is not set
+# CONFIG_FONT_SUN12x22 is not set
+# CONFIG_FONT_10x18 is not set
+CONFIG_LOGO=y
+CONFIG_LOGO_LINUX_MONO=y
+CONFIG_LOGO_LINUX_VGA16=y
+# CONFIG_LOGO_LINUX_CLUT224 is not set
+CONFIG_LOGO_OHAND_CLUT224=y
+
+#
+# Sound
+#
+# CONFIG_SOUND is not set
+CONFIG_HID_SUPPORT=y
+CONFIG_HID=m
+# CONFIG_HID_DEBUG is not set
+# CONFIG_HIDRAW is not set
+
+#
+# USB Input Devices
+#
+CONFIG_USB_HID=m
+# CONFIG_USB_HIDINPUT_POWERBOOK is not set
+# CONFIG_HID_FF is not set
+# CONFIG_USB_HIDDEV is not set
+
+#
+# USB HID Boot Protocol drivers
+#
+CONFIG_USB_KBD=m
+CONFIG_USB_MOUSE=m
+CONFIG_USB_SUPPORT=y
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARCH_HAS_OHCI=y
+CONFIG_USB_ARCH_HAS_EHCI=y
+CONFIG_USB=y
+# CONFIG_USB_DEBUG is not set
+
+#
+# Miscellaneous USB options
+#
+CONFIG_USB_DEVICEFS=y
+CONFIG_USB_DEVICE_CLASS=y
+# CONFIG_USB_DYNAMIC_MINORS is not set
+# CONFIG_USB_SUSPEND is not set
+# CONFIG_USB_PERSIST is not set
+# CONFIG_USB_OTG is not set
+
+#
+# USB Host Controller Drivers
+#
+# CONFIG_USB_EHCI_HCD is not set
+# CONFIG_USB_ISP116X_HCD is not set
+CONFIG_USB_OHCI_HCD=y
+# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set
+# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set
+CONFIG_USB_OHCI_LITTLE_ENDIAN=y
+# CONFIG_USB_UHCI_HCD is not set
+CONFIG_USB_SL811_HCD=m
+# CONFIG_USB_R8A66597_HCD is not set
+
+#
+# USB Device Class drivers
+#
+CONFIG_USB_ACM=m
+CONFIG_USB_PRINTER=m
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
+#
+
+#
+# may also be needed; see USB_STORAGE Help for more information
+#
+CONFIG_USB_STORAGE=m
+# CONFIG_USB_STORAGE_DEBUG is not set
+# CONFIG_USB_STORAGE_DATAFAB is not set
+# CONFIG_USB_STORAGE_FREECOM is not set
+# CONFIG_USB_STORAGE_ISD200 is not set
+# CONFIG_USB_STORAGE_DPCM is not set
+# CONFIG_USB_STORAGE_USBAT is not set
+# CONFIG_USB_STORAGE_SDDR09 is not set
+# CONFIG_USB_STORAGE_SDDR55 is not set
+# CONFIG_USB_STORAGE_JUMPSHOT is not set
+# CONFIG_USB_STORAGE_ALAUDA is not set
+# CONFIG_USB_STORAGE_KARMA is not set
+# CONFIG_USB_LIBUSUAL is not set
+
+#
+# USB Imaging devices
+#
+CONFIG_USB_MDC800=m
+CONFIG_USB_MICROTEK=m
+CONFIG_USB_MON=y
+
+#
+# USB port drivers
+#
+
+#
+# USB Serial Converter support
+#
+CONFIG_USB_SERIAL=m
+CONFIG_USB_SERIAL_GENERIC=y
+# CONFIG_USB_SERIAL_AIRCABLE is not set
+# CONFIG_USB_SERIAL_AIRPRIME is not set
+# CONFIG_USB_SERIAL_ARK3116 is not set
+CONFIG_USB_SERIAL_BELKIN=m
+# CONFIG_USB_SERIAL_CH341 is not set
+# CONFIG_USB_SERIAL_WHITEHEAT is not set
+CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m
+CONFIG_USB_SERIAL_CP2101=m
+CONFIG_USB_SERIAL_CYPRESS_M8=m
+CONFIG_USB_SERIAL_EMPEG=m
+CONFIG_USB_SERIAL_FTDI_SIO=m
+# CONFIG_USB_SERIAL_FUNSOFT is not set
+CONFIG_USB_SERIAL_VISOR=m
+CONFIG_USB_SERIAL_IPAQ=m
+CONFIG_USB_SERIAL_IR=m
+CONFIG_USB_SERIAL_EDGEPORT=m
+CONFIG_USB_SERIAL_EDGEPORT_TI=m
+CONFIG_USB_SERIAL_GARMIN=m
+CONFIG_USB_SERIAL_IPW=m
+CONFIG_USB_SERIAL_KEYSPAN_PDA=m
+CONFIG_USB_SERIAL_KEYSPAN=m
+# CONFIG_USB_SERIAL_KEYSPAN_MPR is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19QW is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19QI is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA49WLC is not set
+CONFIG_USB_SERIAL_KLSI=m
+CONFIG_USB_SERIAL_KOBIL_SCT=m
+CONFIG_USB_SERIAL_MCT_U232=m
+# CONFIG_USB_SERIAL_MOS7720 is not set
+# CONFIG_USB_SERIAL_MOS7840 is not set
+# CONFIG_USB_SERIAL_NAVMAN is not set
+CONFIG_USB_SERIAL_PL2303=m
+# CONFIG_USB_SERIAL_OTI6858 is not set
+# CONFIG_USB_SERIAL_HP4X is not set
+CONFIG_USB_SERIAL_SAFE=m
+# CONFIG_USB_SERIAL_SAFE_PADDED is not set
+# CONFIG_USB_SERIAL_SIERRAWIRELESS is not set
+CONFIG_USB_SERIAL_TI=m
+CONFIG_USB_SERIAL_CYBERJACK=m
+CONFIG_USB_SERIAL_XIRCOM=m
+# CONFIG_USB_SERIAL_OPTION is not set
+CONFIG_USB_SERIAL_OMNINET=m
+# CONFIG_USB_SERIAL_DEBUG is not set
+CONFIG_USB_EZUSB=y
+
+#
+# USB Miscellaneous drivers
+#
+CONFIG_USB_EMI62=m
+CONFIG_USB_EMI26=m
+# CONFIG_USB_ADUTUX is not set
+CONFIG_USB_AUERSWALD=m
+CONFIG_USB_RIO500=m
+CONFIG_USB_LEGOTOWER=m
+CONFIG_USB_LCD=m
+# CONFIG_USB_BERRY_CHARGE is not set
+CONFIG_USB_LED=m
+# CONFIG_USB_CYPRESS_CY7C63 is not set
+CONFIG_USB_CYTHERM=m
+# CONFIG_USB_PHIDGET is not set
+CONFIG_USB_IDMOUSE=m
+# CONFIG_USB_FTDI_ELAN is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+# CONFIG_USB_LD is not set
+# CONFIG_USB_TRANCEVIBRATOR is not set
+# CONFIG_USB_IOWARRIOR is not set
+# CONFIG_USB_TEST is not set
+
+#
+# USB DSL modem support
+#
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
+# CONFIG_MMC is not set
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+
+#
+# LED drivers
+#
+
+#
+# LED Triggers
+#
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=y
+# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set
+CONFIG_RTC_LIB=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_HCTOSYS=y
+CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
+# CONFIG_RTC_DEBUG is not set
+
+#
+# RTC interfaces
+#
+CONFIG_RTC_INTF_SYSFS=y
+CONFIG_RTC_INTF_PROC=y
+CONFIG_RTC_INTF_DEV=y
+# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
+# CONFIG_RTC_DRV_TEST is not set
+
+#
+# SPI RTC drivers
+#
+
+#
+# Platform RTC drivers
+#
+# CONFIG_RTC_DRV_CMOS is not set
+# CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_STK17TA8 is not set
+# CONFIG_RTC_DRV_DS1742 is not set
+# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_M48T59 is not set
+# CONFIG_RTC_DRV_V3020 is not set
+
+#
+# on-CPU RTC drivers
+#
+# CONFIG_RTC_DRV_PL031 is not set
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+# CONFIG_EXT2_FS_XIP is not set
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_FS_XATTR is not set
+# CONFIG_EXT4DEV_FS is not set
+CONFIG_JBD=y
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_FS_POSIX_ACL is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_GFS2_FS is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_ROMFS_FS is not set
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_QUOTA is not set
+CONFIG_DNOTIFY=y
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+# CONFIG_MSDOS_FS is not set
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_DEBUG=0
+CONFIG_JFFS2_FS_WRITEBUFFER=y
+# CONFIG_JFFS2_FS_WBUF_VERIFY is not set
+CONFIG_JFFS2_SUMMARY=y
+# CONFIG_JFFS2_FS_XATTR is not set
+# CONFIG_JFFS2_SYSFS is not set
+CONFIG_JFFS2_COMPRESSION_OPTIONS=y
+CONFIG_JFFS2_ZLIB=y
+CONFIG_JFFS2_LZO=y
+CONFIG_JFFS2_RTIME=y
+CONFIG_JFFS2_RUBIN=y
+# CONFIG_JFFS2_CMODE_NONE is not set
+CONFIG_JFFS2_CMODE_PRIORITY=y
+# CONFIG_JFFS2_CMODE_SIZE is not set
+# CONFIG_JFFS2_CMODE_FAVOURLZO is not set
+CONFIG_CRAMFS=m
+CONFIG_SQUASHFS=m
+# CONFIG_SQUASHFS_EMBEDDED is not set
+CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3
+# CONFIG_VXFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+# CONFIG_NFS_V3_ACL is not set
+# CONFIG_NFS_V4 is not set
+# CONFIG_NFS_DIRECTIO is not set
+CONFIG_NFSD=y
+CONFIG_NFSD_V3=y
+# CONFIG_NFSD_V3_ACL is not set
+# CONFIG_NFSD_V4 is not set
+# CONFIG_NFSD_TCP is not set
+CONFIG_ROOT_NFS=y
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_EXPORTFS=y
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=y
+# CONFIG_SUNRPC_BIND34 is not set
+# CONFIG_RPCSEC_GSS_KRB5 is not set
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+CONFIG_SMB_FS=y
+# CONFIG_SMB_NLS_DEFAULT is not set
+CONFIG_CIFS=m
+# CONFIG_CIFS_STATS is not set
+# CONFIG_CIFS_WEAK_PW_HASH is not set
+# CONFIG_CIFS_XATTR is not set
+# CONFIG_CIFS_DEBUG2 is not set
+# CONFIG_CIFS_EXPERIMENTAL is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_MINIX_SUBPARTITION is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_UNIXWARE_DISKLABEL is not set
+# CONFIG_LDM_PARTITION is not set
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_ULTRIX_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_KARMA_PARTITION is not set
+# CONFIG_EFI_PARTITION is not set
+# CONFIG_SYSV68_PARTITION is not set
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="cp437"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_737=m
+CONFIG_NLS_CODEPAGE_775=m
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
+CONFIG_NLS_CODEPAGE_855=m
+CONFIG_NLS_CODEPAGE_857=m
+CONFIG_NLS_CODEPAGE_860=m
+CONFIG_NLS_CODEPAGE_861=m
+CONFIG_NLS_CODEPAGE_862=m
+CONFIG_NLS_CODEPAGE_863=m
+CONFIG_NLS_CODEPAGE_864=m
+CONFIG_NLS_CODEPAGE_865=m
+CONFIG_NLS_CODEPAGE_866=m
+CONFIG_NLS_CODEPAGE_869=m
+CONFIG_NLS_CODEPAGE_936=m
+CONFIG_NLS_CODEPAGE_950=m
+CONFIG_NLS_CODEPAGE_932=m
+CONFIG_NLS_CODEPAGE_949=m
+CONFIG_NLS_CODEPAGE_874=m
+CONFIG_NLS_ISO8859_8=m
+CONFIG_NLS_CODEPAGE_1250=m
+CONFIG_NLS_CODEPAGE_1251=m
+CONFIG_NLS_ASCII=m
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_ISO8859_2=m
+CONFIG_NLS_ISO8859_3=m
+CONFIG_NLS_ISO8859_4=m
+CONFIG_NLS_ISO8859_5=m
+CONFIG_NLS_ISO8859_6=m
+CONFIG_NLS_ISO8859_7=m
+CONFIG_NLS_ISO8859_9=m
+CONFIG_NLS_ISO8859_13=m
+CONFIG_NLS_ISO8859_14=m
+CONFIG_NLS_ISO8859_15=m
+CONFIG_NLS_KOI8_R=m
+CONFIG_NLS_KOI8_U=m
+CONFIG_NLS_UTF8=y
+# CONFIG_DLM is not set
+CONFIG_INSTRUMENTATION=y
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=m
+# CONFIG_MARKERS is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_PRINTK_TIME is not set
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_DEBUG_FS is not set
+# CONFIG_HEADERS_CHECK is not set
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_DEBUG_SHIRQ is not set
+# CONFIG_DETECT_SOFTLOCKUP is not set
+# CONFIG_SCHED_DEBUG is not set
+# CONFIG_SCHEDSTATS is not set
+CONFIG_TIMER_STATS=y
+# CONFIG_DEBUG_SLAB is not set
+# CONFIG_DEBUG_PREEMPT is not set
+# CONFIG_DEBUG_RT_MUTEXES is not set
+# CONFIG_RT_MUTEX_TESTER is not set
+# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_DEBUG_MUTEXES is not set
+# CONFIG_DEBUG_LOCK_ALLOC is not set
+# CONFIG_PROVE_LOCKING is not set
+# CONFIG_LOCK_STAT is not set
+# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
+# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
+# CONFIG_DEBUG_KOBJECT is not set
+CONFIG_DEBUG_BUGVERBOSE=y
+# CONFIG_DEBUG_INFO is not set
+# CONFIG_DEBUG_VM is not set
+# CONFIG_DEBUG_LIST is not set
+# CONFIG_DEBUG_SG is not set
+CONFIG_FRAME_POINTER=y
+# CONFIG_FORCED_INLINING is not set
+# CONFIG_BOOT_PRINTK_DELAY is not set
+# CONFIG_RCU_TORTURE_TEST is not set
+# CONFIG_FAULT_INJECTION is not set
+# CONFIG_SAMPLES is not set
+# CONFIG_DEBUG_USER is not set
+CONFIG_DEBUG_ERRORS=y
+# CONFIG_DEBUG_LL is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+CONFIG_CRYPTO=y
+CONFIG_CRYPTO_ALGAPI=m
+CONFIG_CRYPTO_BLKCIPHER=m
+CONFIG_CRYPTO_HASH=m
+CONFIG_CRYPTO_MANAGER=m
+CONFIG_CRYPTO_HMAC=m
+# CONFIG_CRYPTO_XCBC is not set
+CONFIG_CRYPTO_NULL=m
+CONFIG_CRYPTO_MD4=m
+CONFIG_CRYPTO_MD5=m
+CONFIG_CRYPTO_SHA1=m
+CONFIG_CRYPTO_SHA256=m
+CONFIG_CRYPTO_SHA512=m
+CONFIG_CRYPTO_WP512=m
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_GF128MUL is not set
+CONFIG_CRYPTO_ECB=m
+CONFIG_CRYPTO_CBC=m
+CONFIG_CRYPTO_PCBC=m
+# CONFIG_CRYPTO_LRW is not set
+# CONFIG_CRYPTO_XTS is not set
+# CONFIG_CRYPTO_CRYPTD is not set
+CONFIG_CRYPTO_DES=m
+# CONFIG_CRYPTO_FCRYPT is not set
+CONFIG_CRYPTO_BLOWFISH=m
+CONFIG_CRYPTO_TWOFISH=m
+CONFIG_CRYPTO_TWOFISH_COMMON=m
+CONFIG_CRYPTO_SERPENT=m
+CONFIG_CRYPTO_AES=m
+CONFIG_CRYPTO_CAST5=m
+CONFIG_CRYPTO_CAST6=m
+CONFIG_CRYPTO_TEA=m
+CONFIG_CRYPTO_ARC4=m
+CONFIG_CRYPTO_KHAZAD=m
+CONFIG_CRYPTO_ANUBIS=m
+# CONFIG_CRYPTO_SEED is not set
+CONFIG_CRYPTO_DEFLATE=m
+CONFIG_CRYPTO_LZO=m
+CONFIG_CRYPTO_MICHAEL_MIC=m
+CONFIG_CRYPTO_CRC32C=m
+CONFIG_CRYPTO_CAMELLIA=m
+CONFIG_CRYPTO_TEST=m
+# CONFIG_CRYPTO_AUTHENC is not set
+# CONFIG_CRYPTO_HW is not set
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+CONFIG_CRC_CCITT=y
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
+CONFIG_LIBCRC32C=m
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_LZO_COMPRESS=y
+CONFIG_LZO_DECOMPRESS=y
+CONFIG_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
diff --git a/recipes/linux/linux-rp-2.6.26/defconfig-qemux86 b/recipes/linux/linux-rp-2.6.26/defconfig-qemux86
new file mode 100644
index 0000000000..975dbdd85b
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.26/defconfig-qemux86
@@ -0,0 +1,1993 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.24-rc8
+# Sun Jan 20 18:20:37 2008
+#
+# CONFIG_64BIT is not set
+CONFIG_X86_32=y
+# CONFIG_X86_64 is not set
+CONFIG_X86=y
+CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_CMOS_UPDATE=y
+CONFIG_CLOCKSOURCE_WATCHDOG=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_SEMAPHORE_SLEEPERS=y
+CONFIG_MMU=y
+CONFIG_ZONE_DMA=y
+CONFIG_QUICKLIST=y
+CONFIG_GENERIC_ISA_DMA=y
+CONFIG_GENERIC_IOMAP=y
+CONFIG_GENERIC_BUG=y
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_ARCH_MAY_HAVE_PC_FDC=y
+CONFIG_DMI=y
+# CONFIG_RWSEM_GENERIC_SPINLOCK is not set
+CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+# CONFIG_ARCH_HAS_ILOG2_U32 is not set
+# CONFIG_ARCH_HAS_ILOG2_U64 is not set
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+# CONFIG_GENERIC_TIME_VSYSCALL is not set
+CONFIG_ARCH_SUPPORTS_OPROFILE=y
+# CONFIG_ZONE_DMA32 is not set
+CONFIG_ARCH_POPULATES_NODE_MAP=y
+# CONFIG_AUDIT_ARCH is not set
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_GENERIC_PENDING_IRQ=y
+CONFIG_X86_SMP=y
+CONFIG_X86_HT=y
+CONFIG_X86_BIOS_REBOOT=y
+CONFIG_X86_TRAMPOLINE=y
+CONFIG_KTIME_SCALAR=y
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_LOCK_KERNEL=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+CONFIG_POSIX_MQUEUE=y
+# CONFIG_BSD_PROCESS_ACCT is not set
+# CONFIG_TASKSTATS is not set
+# CONFIG_USER_NS is not set
+# CONFIG_PID_NS is not set
+CONFIG_AUDIT=y
+CONFIG_AUDITSYSCALL=y
+CONFIG_AUDIT_TREE=y
+# CONFIG_IKCONFIG is not set
+CONFIG_LOG_BUF_SHIFT=15
+# CONFIG_CGROUPS is not set
+CONFIG_FAIR_GROUP_SCHED=y
+CONFIG_FAIR_USER_SCHED=y
+# CONFIG_FAIR_CGROUP_SCHED is not set
+CONFIG_SYSFS_DEPRECATED=y
+# CONFIG_RELAY is not set
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SYSCTL=y
+CONFIG_EMBEDDED=y
+CONFIG_UID16=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_ANON_INODES=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLAB=y
+# CONFIG_SLUB is not set
+# CONFIG_SLOB is not set
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+# CONFIG_TINY_SHMEM is not set
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+CONFIG_STOP_MACHINE=y
+CONFIG_BLOCK=y
+CONFIG_LBD=y
+# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_LSF is not set
+# CONFIG_BLK_DEV_BSG is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=m
+CONFIG_IOSCHED_CFQ=m
+CONFIG_DEFAULT_AS=y
+# CONFIG_DEFAULT_DEADLINE is not set
+# CONFIG_DEFAULT_CFQ is not set
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="anticipatory"
+
+#
+# Processor type and features
+#
+CONFIG_TICK_ONESHOT=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_SMP=y
+CONFIG_X86_PC=y
+# CONFIG_X86_ELAN is not set
+# CONFIG_X86_VOYAGER is not set
+# CONFIG_X86_NUMAQ is not set
+# CONFIG_X86_SUMMIT is not set
+# CONFIG_X86_BIGSMP is not set
+# CONFIG_X86_VISWS is not set
+# CONFIG_X86_GENERICARCH is not set
+# CONFIG_X86_ES7000 is not set
+# CONFIG_X86_VSMP is not set
+CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER=y
+# CONFIG_PARAVIRT_GUEST is not set
+# CONFIG_M386 is not set
+# CONFIG_M486 is not set
+# CONFIG_M586 is not set
+# CONFIG_M586TSC is not set
+# CONFIG_M586MMX is not set
+CONFIG_M686=y
+# CONFIG_MPENTIUMII is not set
+# CONFIG_MPENTIUMIII is not set
+# CONFIG_MPENTIUMM is not set
+# CONFIG_MPENTIUM4 is not set
+# CONFIG_MK6 is not set
+# CONFIG_MK7 is not set
+# CONFIG_MK8 is not set
+# CONFIG_MCRUSOE is not set
+# CONFIG_MEFFICEON is not set
+# CONFIG_MWINCHIPC6 is not set
+# CONFIG_MWINCHIP2 is not set
+# CONFIG_MWINCHIP3D is not set
+# CONFIG_MGEODEGX1 is not set
+# CONFIG_MGEODE_LX is not set
+# CONFIG_MCYRIXIII is not set
+# CONFIG_MVIAC3_2 is not set
+# CONFIG_MVIAC7 is not set
+# CONFIG_MPSC is not set
+# CONFIG_MCORE2 is not set
+# CONFIG_GENERIC_CPU is not set
+CONFIG_X86_GENERIC=y
+CONFIG_X86_CMPXCHG=y
+CONFIG_X86_L1_CACHE_SHIFT=7
+CONFIG_X86_XADD=y
+CONFIG_X86_PPRO_FENCE=y
+CONFIG_X86_WP_WORKS_OK=y
+CONFIG_X86_INVLPG=y
+CONFIG_X86_BSWAP=y
+CONFIG_X86_POPAD_OK=y
+CONFIG_X86_GOOD_APIC=y
+CONFIG_X86_INTEL_USERCOPY=y
+CONFIG_X86_USE_PPRO_CHECKSUM=y
+CONFIG_X86_TSC=y
+CONFIG_X86_CMOV=y
+CONFIG_X86_MINIMUM_CPU_FAMILY=4
+# CONFIG_HPET_TIMER is not set
+CONFIG_NR_CPUS=8
+CONFIG_SCHED_SMT=y
+CONFIG_SCHED_MC=y
+CONFIG_PREEMPT_NONE=y
+# CONFIG_PREEMPT_VOLUNTARY is not set
+# CONFIG_PREEMPT is not set
+CONFIG_PREEMPT_BKL=y
+CONFIG_X86_LOCAL_APIC=y
+CONFIG_X86_IO_APIC=y
+CONFIG_X86_MCE=y
+CONFIG_X86_MCE_NONFATAL=y
+CONFIG_X86_MCE_P4THERMAL=y
+CONFIG_VM86=y
+# CONFIG_TOSHIBA is not set
+# CONFIG_I8K is not set
+# CONFIG_X86_REBOOTFIXUPS is not set
+# CONFIG_MICROCODE is not set
+# CONFIG_X86_MSR is not set
+# CONFIG_X86_CPUID is not set
+CONFIG_NOHIGHMEM=y
+# CONFIG_HIGHMEM4G is not set
+# CONFIG_HIGHMEM64G is not set
+CONFIG_VMSPLIT_3G=y
+# CONFIG_VMSPLIT_3G_OPT is not set
+# CONFIG_VMSPLIT_2G is not set
+# CONFIG_VMSPLIT_2G_OPT is not set
+# CONFIG_VMSPLIT_1G is not set
+CONFIG_PAGE_OFFSET=0xC0000000
+# CONFIG_X86_PAE is not set
+CONFIG_ARCH_FLATMEM_ENABLE=y
+CONFIG_ARCH_SPARSEMEM_ENABLE=y
+CONFIG_ARCH_SELECT_MEMORY_MODEL=y
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+CONFIG_SPARSEMEM_STATIC=y
+# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set
+CONFIG_SPLIT_PTLOCK_CPUS=4
+# CONFIG_RESOURCES_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=1
+CONFIG_BOUNCE=y
+CONFIG_NR_QUICK=1
+CONFIG_VIRT_TO_BUS=y
+# CONFIG_MATH_EMULATION is not set
+CONFIG_MTRR=y
+# CONFIG_EFI is not set
+CONFIG_IRQBALANCE=y
+CONFIG_SECCOMP=y
+# CONFIG_HZ_100 is not set
+CONFIG_HZ_250=y
+# CONFIG_HZ_300 is not set
+# CONFIG_HZ_1000 is not set
+CONFIG_HZ=250
+CONFIG_KEXEC=y
+CONFIG_PHYSICAL_START=0x100000
+# CONFIG_RELOCATABLE is not set
+CONFIG_PHYSICAL_ALIGN=0x100000
+CONFIG_HOTPLUG_CPU=y
+CONFIG_COMPAT_VDSO=y
+
+#
+# Power management options
+#
+CONFIG_PM=y
+CONFIG_PM_LEGACY=y
+# CONFIG_PM_DEBUG is not set
+CONFIG_PM_SLEEP_SMP=y
+CONFIG_PM_SLEEP=y
+CONFIG_SUSPEND_SMP_POSSIBLE=y
+CONFIG_SUSPEND=y
+CONFIG_HIBERNATION_SMP_POSSIBLE=y
+# CONFIG_HIBERNATION is not set
+CONFIG_ACPI=y
+CONFIG_ACPI_SLEEP=y
+# CONFIG_ACPI_PROCFS is not set
+CONFIG_ACPI_PROCFS_POWER=y
+CONFIG_ACPI_SYSFS_POWER=y
+CONFIG_ACPI_PROC_EVENT=y
+CONFIG_ACPI_AC=y
+CONFIG_ACPI_BATTERY=y
+CONFIG_ACPI_BUTTON=y
+# CONFIG_ACPI_VIDEO is not set
+CONFIG_ACPI_FAN=y
+# CONFIG_ACPI_DOCK is not set
+CONFIG_ACPI_PROCESSOR=y
+CONFIG_ACPI_HOTPLUG_CPU=y
+CONFIG_ACPI_THERMAL=y
+# CONFIG_ACPI_ASUS is not set
+# CONFIG_ACPI_TOSHIBA is not set
+CONFIG_ACPI_BLACKLIST_YEAR=0
+# CONFIG_ACPI_DEBUG is not set
+CONFIG_ACPI_EC=y
+CONFIG_ACPI_POWER=y
+CONFIG_ACPI_SYSTEM=y
+CONFIG_X86_PM_TIMER=y
+CONFIG_ACPI_CONTAINER=y
+# CONFIG_ACPI_SBS is not set
+# CONFIG_APM is not set
+
+#
+# CPU Frequency scaling
+#
+# CONFIG_CPU_FREQ is not set
+# CONFIG_CPU_IDLE is not set
+
+#
+# Bus options (PCI etc.)
+#
+CONFIG_PCI=y
+# CONFIG_PCI_GOBIOS is not set
+# CONFIG_PCI_GOMMCONFIG is not set
+# CONFIG_PCI_GODIRECT is not set
+CONFIG_PCI_GOANY=y
+CONFIG_PCI_BIOS=y
+CONFIG_PCI_DIRECT=y
+CONFIG_PCI_MMCONFIG=y
+CONFIG_PCI_DOMAINS=y
+# CONFIG_PCIEPORTBUS is not set
+CONFIG_ARCH_SUPPORTS_MSI=y
+# CONFIG_PCI_MSI is not set
+CONFIG_PCI_LEGACY=y
+CONFIG_HT_IRQ=y
+CONFIG_ISA_DMA_API=y
+CONFIG_ISA=y
+# CONFIG_EISA is not set
+# CONFIG_MCA is not set
+# CONFIG_SCx200 is not set
+CONFIG_K8_NB=y
+# CONFIG_PCCARD is not set
+# CONFIG_HOTPLUG_PCI is not set
+
+#
+# Executable file formats / Emulations
+#
+CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_AOUT=m
+CONFIG_BINFMT_MISC=m
+
+#
+# Networking
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=m
+CONFIG_PACKET_MMAP=y
+CONFIG_UNIX=y
+CONFIG_XFRM=y
+# CONFIG_XFRM_USER is not set
+# CONFIG_XFRM_SUB_POLICY is not set
+# CONFIG_XFRM_MIGRATE is not set
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_ARPD is not set
+CONFIG_SYN_COOKIES=y
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+CONFIG_INET_TUNNEL=m
+CONFIG_INET_XFRM_MODE_TRANSPORT=m
+CONFIG_INET_XFRM_MODE_TUNNEL=m
+CONFIG_INET_XFRM_MODE_BEET=m
+# CONFIG_INET_LRO is not set
+CONFIG_INET_DIAG=m
+CONFIG_INET_TCP_DIAG=m
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+# CONFIG_IP_VS is not set
+CONFIG_IPV6=m
+# CONFIG_IPV6_PRIVACY is not set
+# CONFIG_IPV6_ROUTER_PREF is not set
+# CONFIG_IPV6_OPTIMISTIC_DAD is not set
+CONFIG_INET6_AH=m
+CONFIG_INET6_ESP=m
+CONFIG_INET6_IPCOMP=m
+# CONFIG_IPV6_MIP6 is not set
+CONFIG_INET6_XFRM_TUNNEL=m
+CONFIG_INET6_TUNNEL=m
+CONFIG_INET6_XFRM_MODE_TRANSPORT=m
+CONFIG_INET6_XFRM_MODE_TUNNEL=m
+CONFIG_INET6_XFRM_MODE_BEET=m
+# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set
+CONFIG_IPV6_SIT=m
+CONFIG_IPV6_TUNNEL=m
+# CONFIG_IPV6_MULTIPLE_TABLES is not set
+# CONFIG_NETWORK_SECMARK is not set
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+
+#
+# Core Netfilter Configuration
+#
+# CONFIG_NETFILTER_NETLINK is not set
+# CONFIG_NF_CONNTRACK_ENABLED is not set
+# CONFIG_NF_CONNTRACK is not set
+CONFIG_NETFILTER_XTABLES=m
+# CONFIG_NETFILTER_XT_TARGET_CLASSIFY is not set
+# CONFIG_NETFILTER_XT_TARGET_DSCP is not set
+# CONFIG_NETFILTER_XT_TARGET_MARK is not set
+# CONFIG_NETFILTER_XT_TARGET_NFQUEUE is not set
+# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set
+# CONFIG_NETFILTER_XT_TARGET_TRACE is not set
+# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set
+# CONFIG_NETFILTER_XT_MATCH_COMMENT is not set
+# CONFIG_NETFILTER_XT_MATCH_DCCP is not set
+# CONFIG_NETFILTER_XT_MATCH_DSCP is not set
+# CONFIG_NETFILTER_XT_MATCH_ESP is not set
+# CONFIG_NETFILTER_XT_MATCH_LENGTH is not set
+# CONFIG_NETFILTER_XT_MATCH_LIMIT is not set
+# CONFIG_NETFILTER_XT_MATCH_MAC is not set
+# CONFIG_NETFILTER_XT_MATCH_MARK is not set
+# CONFIG_NETFILTER_XT_MATCH_POLICY is not set
+# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set
+# CONFIG_NETFILTER_XT_MATCH_PKTTYPE is not set
+# CONFIG_NETFILTER_XT_MATCH_QUOTA is not set
+# CONFIG_NETFILTER_XT_MATCH_REALM is not set
+# CONFIG_NETFILTER_XT_MATCH_SCTP is not set
+# CONFIG_NETFILTER_XT_MATCH_STATISTIC is not set
+# CONFIG_NETFILTER_XT_MATCH_STRING is not set
+# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set
+# CONFIG_NETFILTER_XT_MATCH_TIME is not set
+# CONFIG_NETFILTER_XT_MATCH_U32 is not set
+# CONFIG_NETFILTER_XT_MATCH_HASHLIMIT is not set
+
+#
+# IP: Netfilter Configuration
+#
+CONFIG_IP_NF_QUEUE=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_IPRANGE=m
+CONFIG_IP_NF_MATCH_TOS=m
+CONFIG_IP_NF_MATCH_RECENT=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_AH=m
+CONFIG_IP_NF_MATCH_TTL=m
+CONFIG_IP_NF_MATCH_OWNER=m
+CONFIG_IP_NF_MATCH_ADDRTYPE=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_LOG=m
+CONFIG_IP_NF_TARGET_ULOG=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_TOS=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_TTL=m
+CONFIG_IP_NF_RAW=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+
+#
+# IPv6: Netfilter Configuration (EXPERIMENTAL)
+#
+# CONFIG_IP6_NF_QUEUE is not set
+# CONFIG_IP6_NF_IPTABLES is not set
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_SCTP is not set
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_SCHED is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+CONFIG_IRDA=m
+
+#
+# IrDA protocols
+#
+CONFIG_IRLAN=m
+CONFIG_IRNET=m
+CONFIG_IRCOMM=m
+# CONFIG_IRDA_ULTRA is not set
+
+#
+# IrDA options
+#
+# CONFIG_IRDA_CACHE_LAST_LSAP is not set
+# CONFIG_IRDA_FAST_RR is not set
+# CONFIG_IRDA_DEBUG is not set
+
+#
+# Infrared-port device drivers
+#
+
+#
+# SIR device drivers
+#
+# CONFIG_IRTTY_SIR is not set
+
+#
+# Dongle support
+#
+# CONFIG_KINGSUN_DONGLE is not set
+# CONFIG_KSDAZZLE_DONGLE is not set
+# CONFIG_KS959_DONGLE is not set
+
+#
+# Old SIR device drivers
+#
+
+#
+# Old Serial dongle support
+#
+
+#
+# FIR device drivers
+#
+# CONFIG_USB_IRDA is not set
+# CONFIG_SIGMATEL_FIR is not set
+# CONFIG_NSC_FIR is not set
+# CONFIG_WINBOND_FIR is not set
+# CONFIG_TOSHIBA_FIR is not set
+# CONFIG_SMC_IRCC_FIR is not set
+# CONFIG_ALI_FIR is not set
+# CONFIG_VLSI_FIR is not set
+# CONFIG_VIA_FIR is not set
+# CONFIG_MCS_FIR is not set
+CONFIG_BT=m
+CONFIG_BT_L2CAP=m
+CONFIG_BT_SCO=m
+CONFIG_BT_RFCOMM=m
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=m
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+CONFIG_BT_HIDP=m
+
+#
+# Bluetooth device drivers
+#
+CONFIG_BT_HCIUSB=m
+# CONFIG_BT_HCIUSB_SCO is not set
+CONFIG_BT_HCIUART=m
+CONFIG_BT_HCIUART_H4=y
+CONFIG_BT_HCIUART_BCSP=y
+# CONFIG_BT_HCIUART_LL is not set
+CONFIG_BT_HCIBCM203X=m
+CONFIG_BT_HCIBPA10X=m
+CONFIG_BT_HCIBFUSB=m
+CONFIG_BT_HCIVHCI=m
+# CONFIG_AF_RXRPC is not set
+
+#
+# Wireless
+#
+# CONFIG_CFG80211 is not set
+CONFIG_WIRELESS_EXT=y
+# CONFIG_MAC80211 is not set
+CONFIG_IEEE80211=m
+# CONFIG_IEEE80211_DEBUG is not set
+CONFIG_IEEE80211_CRYPT_WEP=m
+CONFIG_IEEE80211_CRYPT_CCMP=m
+CONFIG_IEEE80211_CRYPT_TKIP=m
+CONFIG_IEEE80211_SOFTMAC=m
+# CONFIG_IEEE80211_SOFTMAC_DEBUG is not set
+# CONFIG_RFKILL is not set
+# CONFIG_NET_9P is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+# CONFIG_SYS_HYPERVISOR is not set
+CONFIG_CONNECTOR=y
+CONFIG_PROC_EVENTS=y
+# CONFIG_MTD is not set
+CONFIG_PARPORT=y
+CONFIG_PARPORT_PC=y
+# CONFIG_PARPORT_SERIAL is not set
+# CONFIG_PARPORT_PC_FIFO is not set
+# CONFIG_PARPORT_PC_SUPERIO is not set
+# CONFIG_PARPORT_GSC is not set
+# CONFIG_PARPORT_AX88796 is not set
+# CONFIG_PARPORT_1284 is not set
+CONFIG_PNP=y
+# CONFIG_PNP_DEBUG is not set
+
+#
+# Protocols
+#
+# CONFIG_ISAPNP is not set
+# CONFIG_PNPBIOS is not set
+CONFIG_PNPACPI=y
+CONFIG_BLK_DEV=y
+CONFIG_BLK_DEV_FD=y
+# CONFIG_BLK_DEV_XD is not set
+# CONFIG_PARIDE is not set
+# CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_CPQ_CISS_DA is not set
+# CONFIG_BLK_DEV_DAC960 is not set
+# CONFIG_BLK_DEV_UMEM is not set
+# CONFIG_BLK_DEV_COW_COMMON is not set
+CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_CRYPTOLOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_SX8 is not set
+# CONFIG_BLK_DEV_UB is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=65536
+CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+CONFIG_MISC_DEVICES=y
+# CONFIG_IBM_ASM is not set
+# CONFIG_PHANTOM is not set
+# CONFIG_EEPROM_93CX6 is not set
+# CONFIG_SGI_IOC4 is not set
+# CONFIG_TIFM_CORE is not set
+# CONFIG_ASUS_LAPTOP is not set
+# CONFIG_FUJITSU_LAPTOP is not set
+# CONFIG_MSI_LAPTOP is not set
+# CONFIG_SONY_LAPTOP is not set
+# CONFIG_THINKPAD_ACPI is not set
+CONFIG_IDE=y
+CONFIG_IDE_MAX_HWIFS=4
+CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_IDE_SATA is not set
+# CONFIG_BLK_DEV_HD_IDE is not set
+CONFIG_BLK_DEV_IDEDISK=y
+CONFIG_IDEDISK_MULTI_MODE=y
+CONFIG_BLK_DEV_IDECD=y
+# CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
+# CONFIG_BLK_DEV_IDESCSI is not set
+# CONFIG_BLK_DEV_IDEACPI is not set
+# CONFIG_IDE_TASK_IOCTL is not set
+CONFIG_IDE_PROC_FS=y
+
+#
+# IDE chipset support/bugfixes
+#
+CONFIG_IDE_GENERIC=y
+# CONFIG_BLK_DEV_PLATFORM is not set
+CONFIG_BLK_DEV_CMD640=y
+# CONFIG_BLK_DEV_CMD640_ENHANCED is not set
+# CONFIG_BLK_DEV_IDEPNP is not set
+
+#
+# PCI IDE chipsets support
+#
+CONFIG_BLK_DEV_IDEPCI=y
+CONFIG_IDEPCI_SHARE_IRQ=y
+CONFIG_IDEPCI_PCIBUS_ORDER=y
+# CONFIG_BLK_DEV_OFFBOARD is not set
+CONFIG_BLK_DEV_GENERIC=y
+# CONFIG_BLK_DEV_OPTI621 is not set
+CONFIG_BLK_DEV_RZ1000=y
+CONFIG_BLK_DEV_IDEDMA_PCI=y
+# CONFIG_BLK_DEV_AEC62XX is not set
+# CONFIG_BLK_DEV_ALI15X3 is not set
+# CONFIG_BLK_DEV_AMD74XX is not set
+# CONFIG_BLK_DEV_ATIIXP is not set
+# CONFIG_BLK_DEV_CMD64X is not set
+# CONFIG_BLK_DEV_TRIFLEX is not set
+# CONFIG_BLK_DEV_CY82C693 is not set
+# CONFIG_BLK_DEV_CS5520 is not set
+# CONFIG_BLK_DEV_CS5530 is not set
+# CONFIG_BLK_DEV_CS5535 is not set
+# CONFIG_BLK_DEV_HPT34X is not set
+# CONFIG_BLK_DEV_HPT366 is not set
+# CONFIG_BLK_DEV_JMICRON is not set
+# CONFIG_BLK_DEV_SC1200 is not set
+CONFIG_BLK_DEV_PIIX=y
+# CONFIG_BLK_DEV_IT8213 is not set
+# CONFIG_BLK_DEV_IT821X is not set
+# CONFIG_BLK_DEV_NS87415 is not set
+# CONFIG_BLK_DEV_PDC202XX_OLD is not set
+# CONFIG_BLK_DEV_PDC202XX_NEW is not set
+# CONFIG_BLK_DEV_SVWKS is not set
+# CONFIG_BLK_DEV_SIIMAGE is not set
+# CONFIG_BLK_DEV_SIS5513 is not set
+# CONFIG_BLK_DEV_SLC90E66 is not set
+# CONFIG_BLK_DEV_TRM290 is not set
+# CONFIG_BLK_DEV_VIA82CXXX is not set
+# CONFIG_BLK_DEV_TC86C001 is not set
+# CONFIG_IDE_ARM is not set
+
+#
+# Other IDE chipsets support
+#
+
+#
+# Note: most of these also require special kernel boot parameters
+#
+# CONFIG_BLK_DEV_4DRIVES is not set
+# CONFIG_BLK_DEV_ALI14XX is not set
+# CONFIG_BLK_DEV_DTC2278 is not set
+# CONFIG_BLK_DEV_HT6560B is not set
+# CONFIG_BLK_DEV_QD65XX is not set
+# CONFIG_BLK_DEV_UMC8672 is not set
+CONFIG_BLK_DEV_IDEDMA=y
+CONFIG_IDE_ARCH_OBSOLETE_INIT=y
+# CONFIG_BLK_DEV_HD is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+CONFIG_SCSI=y
+CONFIG_SCSI_DMA=y
+# CONFIG_SCSI_TGT is not set
+# CONFIG_SCSI_NETLINK is not set
+CONFIG_SCSI_PROC_FS=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=y
+# CONFIG_CHR_DEV_ST is not set
+# CONFIG_CHR_DEV_OSST is not set
+# CONFIG_BLK_DEV_SR is not set
+CONFIG_CHR_DEV_SG=y
+# CONFIG_CHR_DEV_SCH is not set
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+CONFIG_SCSI_MULTI_LUN=y
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
+# CONFIG_SCSI_SCAN_ASYNC is not set
+CONFIG_SCSI_WAIT_SCAN=m
+
+#
+# SCSI Transports
+#
+CONFIG_SCSI_SPI_ATTRS=y
+# CONFIG_SCSI_FC_ATTRS is not set
+# CONFIG_SCSI_ISCSI_ATTRS is not set
+# CONFIG_SCSI_SAS_LIBSAS is not set
+# CONFIG_SCSI_SRP_ATTRS is not set
+CONFIG_SCSI_LOWLEVEL=y
+# CONFIG_ISCSI_TCP is not set
+# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
+# CONFIG_SCSI_3W_9XXX is not set
+# CONFIG_SCSI_7000FASST is not set
+# CONFIG_SCSI_ACARD is not set
+# CONFIG_SCSI_AHA152X is not set
+# CONFIG_SCSI_AHA1542 is not set
+# CONFIG_SCSI_AACRAID is not set
+# CONFIG_SCSI_AIC7XXX is not set
+# CONFIG_SCSI_AIC7XXX_OLD is not set
+# CONFIG_SCSI_AIC79XX is not set
+# CONFIG_SCSI_AIC94XX is not set
+CONFIG_SCSI_DPT_I2O=m
+# CONFIG_SCSI_ADVANSYS is not set
+# CONFIG_SCSI_IN2000 is not set
+# CONFIG_SCSI_ARCMSR is not set
+# CONFIG_MEGARAID_NEWGEN is not set
+# CONFIG_MEGARAID_LEGACY is not set
+# CONFIG_MEGARAID_SAS is not set
+# CONFIG_SCSI_HPTIOP is not set
+# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_DMX3191D is not set
+# CONFIG_SCSI_DTC3280 is not set
+# CONFIG_SCSI_EATA is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GDTH is not set
+# CONFIG_SCSI_GENERIC_NCR5380 is not set
+# CONFIG_SCSI_GENERIC_NCR5380_MMIO is not set
+# CONFIG_SCSI_IPS is not set
+# CONFIG_SCSI_INITIO is not set
+# CONFIG_SCSI_INIA100 is not set
+# CONFIG_SCSI_PPA is not set
+# CONFIG_SCSI_IMM is not set
+# CONFIG_SCSI_NCR53C406A is not set
+# CONFIG_SCSI_STEX is not set
+# CONFIG_SCSI_SYM53C8XX_2 is not set
+# CONFIG_SCSI_PAS16 is not set
+# CONFIG_SCSI_PSI240I is not set
+# CONFIG_SCSI_QLOGIC_FAS is not set
+# CONFIG_SCSI_QLOGIC_1280 is not set
+# CONFIG_SCSI_QLA_FC is not set
+# CONFIG_SCSI_QLA_ISCSI is not set
+# CONFIG_SCSI_LPFC is not set
+# CONFIG_SCSI_SEAGATE is not set
+# CONFIG_SCSI_SYM53C416 is not set
+# CONFIG_SCSI_DC395x is not set
+# CONFIG_SCSI_DC390T is not set
+# CONFIG_SCSI_T128 is not set
+# CONFIG_SCSI_U14_34F is not set
+# CONFIG_SCSI_ULTRASTOR is not set
+# CONFIG_SCSI_NSP32 is not set
+# CONFIG_SCSI_DEBUG is not set
+# CONFIG_SCSI_SRP is not set
+# CONFIG_ATA is not set
+CONFIG_MD=y
+# CONFIG_BLK_DEV_MD is not set
+CONFIG_BLK_DEV_DM=m
+# CONFIG_DM_DEBUG is not set
+CONFIG_DM_CRYPT=m
+CONFIG_DM_SNAPSHOT=m
+CONFIG_DM_MIRROR=m
+CONFIG_DM_ZERO=m
+CONFIG_DM_MULTIPATH=m
+CONFIG_DM_MULTIPATH_EMC=m
+# CONFIG_DM_MULTIPATH_RDAC is not set
+# CONFIG_DM_MULTIPATH_HP is not set
+# CONFIG_DM_DELAY is not set
+# CONFIG_DM_UEVENT is not set
+# CONFIG_FUSION is not set
+
+#
+# IEEE 1394 (FireWire) support
+#
+# CONFIG_FIREWIRE is not set
+# CONFIG_IEEE1394 is not set
+# CONFIG_I2O is not set
+# CONFIG_MACINTOSH_DRIVERS is not set
+CONFIG_NETDEVICES=y
+# CONFIG_NETDEVICES_MULTIQUEUE is not set
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_MACVLAN is not set
+# CONFIG_EQUALIZER is not set
+CONFIG_TUN=m
+# CONFIG_VETH is not set
+# CONFIG_NET_SB1000 is not set
+# CONFIG_ARCNET is not set
+# CONFIG_PHYLIB is not set
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=y
+# CONFIG_HAPPYMEAL is not set
+# CONFIG_SUNGEM is not set
+# CONFIG_CASSINI is not set
+CONFIG_NET_VENDOR_3COM=y
+# CONFIG_EL1 is not set
+# CONFIG_EL2 is not set
+# CONFIG_ELPLUS is not set
+# CONFIG_EL16 is not set
+# CONFIG_EL3 is not set
+# CONFIG_3C515 is not set
+CONFIG_VORTEX=m
+CONFIG_TYPHOON=m
+CONFIG_LANCE=m
+CONFIG_NET_VENDOR_SMC=y
+CONFIG_WD80x3=m
+CONFIG_ULTRA=m
+CONFIG_SMC9194=m
+# CONFIG_NET_VENDOR_RACAL is not set
+CONFIG_NET_TULIP=y
+# CONFIG_DE2104X is not set
+CONFIG_TULIP=m
+CONFIG_TULIP_MWI=y
+CONFIG_TULIP_MMIO=y
+CONFIG_TULIP_NAPI=y
+CONFIG_TULIP_NAPI_HW_MITIGATION=y
+CONFIG_DE4X5=m
+CONFIG_WINBOND_840=m
+CONFIG_DM9102=m
+CONFIG_ULI526X=m
+CONFIG_AT1700=m
+CONFIG_DEPCA=m
+CONFIG_HP100=m
+CONFIG_NET_ISA=y
+# CONFIG_E2100 is not set
+# CONFIG_EWRK3 is not set
+# CONFIG_EEXPRESS is not set
+# CONFIG_EEXPRESS_PRO is not set
+# CONFIG_HPLAN_PLUS is not set
+# CONFIG_HPLAN is not set
+# CONFIG_LP486E is not set
+# CONFIG_ETH16I is not set
+CONFIG_NE2000=y
+# CONFIG_ZNET is not set
+# CONFIG_SEEQ8005 is not set
+# CONFIG_IBM_NEW_EMAC_ZMII is not set
+# CONFIG_IBM_NEW_EMAC_RGMII is not set
+# CONFIG_IBM_NEW_EMAC_TAH is not set
+# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
+CONFIG_NET_PCI=y
+CONFIG_PCNET32=m
+CONFIG_PCNET32_NAPI=y
+CONFIG_AMD8111_ETH=m
+CONFIG_AMD8111E_NAPI=y
+CONFIG_ADAPTEC_STARFIRE=m
+CONFIG_ADAPTEC_STARFIRE_NAPI=y
+CONFIG_AC3200=m
+CONFIG_APRICOT=m
+CONFIG_B44=m
+CONFIG_B44_PCI_AUTOSELECT=y
+CONFIG_B44_PCICORE_AUTOSELECT=y
+CONFIG_B44_PCI=y
+CONFIG_FORCEDETH=m
+CONFIG_FORCEDETH_NAPI=y
+CONFIG_CS89x0=m
+CONFIG_EEPRO100=m
+CONFIG_E100=m
+CONFIG_FEALNX=m
+CONFIG_NATSEMI=m
+CONFIG_NE2K_PCI=y
+# CONFIG_8139CP is not set
+CONFIG_8139TOO=y
+CONFIG_8139TOO_PIO=y
+# CONFIG_8139TOO_TUNE_TWISTER is not set
+# CONFIG_8139TOO_8129 is not set
+# CONFIG_8139_OLD_RX_RESET is not set
+CONFIG_SIS900=m
+CONFIG_EPIC100=m
+CONFIG_SUNDANCE=m
+CONFIG_SUNDANCE_MMIO=y
+CONFIG_TLAN=m
+CONFIG_VIA_RHINE=m
+CONFIG_VIA_RHINE_MMIO=y
+CONFIG_VIA_RHINE_NAPI=y
+CONFIG_SC92031=m
+# CONFIG_NET_POCKET is not set
+CONFIG_NETDEV_1000=y
+CONFIG_ACENIC=m
+CONFIG_ACENIC_OMIT_TIGON_I=y
+CONFIG_DL2K=m
+CONFIG_E1000=m
+CONFIG_E1000_NAPI=y
+# CONFIG_E1000_DISABLE_PACKET_SPLIT is not set
+# CONFIG_E1000E is not set
+# CONFIG_IP1000 is not set
+CONFIG_NS83820=m
+CONFIG_HAMACHI=m
+CONFIG_YELLOWFIN=m
+CONFIG_R8169=m
+CONFIG_R8169_NAPI=y
+CONFIG_SIS190=m
+CONFIG_SKGE=m
+CONFIG_SKY2=m
+CONFIG_SK98LIN=m
+CONFIG_VIA_VELOCITY=m
+CONFIG_TIGON3=m
+CONFIG_BNX2=m
+CONFIG_QLA3XXX=m
+CONFIG_ATL1=m
+CONFIG_NETDEV_10000=y
+# CONFIG_CHELSIO_T1 is not set
+# CONFIG_CHELSIO_T3 is not set
+# CONFIG_IXGBE is not set
+# CONFIG_IXGB is not set
+CONFIG_S2IO=m
+# CONFIG_S2IO_NAPI is not set
+# CONFIG_MYRI10GE is not set
+# CONFIG_NETXEN_NIC is not set
+# CONFIG_NIU is not set
+# CONFIG_MLX4_CORE is not set
+# CONFIG_TEHUTI is not set
+# CONFIG_TR is not set
+
+#
+# Wireless LAN
+#
+# CONFIG_WLAN_PRE80211 is not set
+CONFIG_WLAN_80211=y
+# CONFIG_IPW2100 is not set
+# CONFIG_IPW2200 is not set
+# CONFIG_LIBERTAS is not set
+# CONFIG_AIRO is not set
+CONFIG_HERMES=m
+# CONFIG_PLX_HERMES is not set
+# CONFIG_TMD_HERMES is not set
+# CONFIG_NORTEL_HERMES is not set
+# CONFIG_PCI_HERMES is not set
+# CONFIG_ATMEL is not set
+# CONFIG_PRISM54 is not set
+# CONFIG_USB_ZD1201 is not set
+CONFIG_HOSTAP=m
+CONFIG_HOSTAP_FIRMWARE=y
+# CONFIG_HOSTAP_FIRMWARE_NVRAM is not set
+# CONFIG_HOSTAP_PLX is not set
+# CONFIG_HOSTAP_PCI is not set
+# CONFIG_BCM43XX is not set
+# CONFIG_ZD1211RW is not set
+
+#
+# USB Network Adapters
+#
+# CONFIG_USB_CATC is not set
+# CONFIG_USB_KAWETH is not set
+# CONFIG_USB_PEGASUS is not set
+# CONFIG_USB_RTL8150 is not set
+# CONFIG_USB_USBNET is not set
+# CONFIG_WAN is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PLIP is not set
+CONFIG_PPP=m
+# CONFIG_PPP_MULTILINK is not set
+# CONFIG_PPP_FILTER is not set
+CONFIG_PPP_ASYNC=m
+# CONFIG_PPP_SYNC_TTY is not set
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_BSDCOMP=m
+# CONFIG_PPP_MPPE is not set
+# CONFIG_PPPOE is not set
+# CONFIG_PPPOL2TP is not set
+# CONFIG_SLIP is not set
+CONFIG_SLHC=m
+# CONFIG_NET_FC is not set
+# CONFIG_SHAPER is not set
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_ISDN is not set
+# CONFIG_PHONE is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=m
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=640
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=480
+# CONFIG_INPUT_JOYDEV is not set
+CONFIG_INPUT_EVDEV=y
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+CONFIG_KEYBOARD_ATKBD=y
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+# CONFIG_KEYBOARD_XTKBD is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+CONFIG_INPUT_MOUSE=y
+CONFIG_MOUSE_PS2=m
+CONFIG_MOUSE_PS2_ALPS=y
+CONFIG_MOUSE_PS2_LOGIPS2PP=y
+CONFIG_MOUSE_PS2_SYNAPTICS=y
+CONFIG_MOUSE_PS2_LIFEBOOK=y
+CONFIG_MOUSE_PS2_TRACKPOINT=y
+# CONFIG_MOUSE_PS2_TOUCHKIT is not set
+# CONFIG_MOUSE_SERIAL is not set
+# CONFIG_MOUSE_APPLETOUCH is not set
+# CONFIG_MOUSE_INPORT is not set
+# CONFIG_MOUSE_LOGIBM is not set
+# CONFIG_MOUSE_PC110PAD is not set
+# CONFIG_MOUSE_VSXXXAA is not set
+# CONFIG_INPUT_JOYSTICK is not set
+CONFIG_INPUT_TABLET=y
+# CONFIG_TABLET_USB_ACECAD is not set
+# CONFIG_TABLET_USB_AIPTEK is not set
+# CONFIG_TABLET_USB_GTCO is not set
+# CONFIG_TABLET_USB_KBTAB is not set
+CONFIG_TABLET_USB_WACOM=y
+# CONFIG_INPUT_TOUCHSCREEN is not set
+CONFIG_INPUT_MISC=y
+# CONFIG_INPUT_PCSPKR is not set
+# CONFIG_INPUT_WISTRON_BTNS is not set
+# CONFIG_INPUT_ATLAS_BTNS is not set
+# CONFIG_INPUT_ATI_REMOTE is not set
+# CONFIG_INPUT_ATI_REMOTE2 is not set
+# CONFIG_INPUT_KEYSPAN_REMOTE is not set
+# CONFIG_INPUT_POWERMATE is not set
+# CONFIG_INPUT_YEALINK is not set
+CONFIG_INPUT_UINPUT=m
+
+#
+# Hardware I/O ports
+#
+CONFIG_SERIO=y
+CONFIG_SERIO_I8042=y
+# CONFIG_SERIO_SERPORT is not set
+# CONFIG_SERIO_CT82C710 is not set
+# CONFIG_SERIO_PARKBD is not set
+# CONFIG_SERIO_PCIPS2 is not set
+CONFIG_SERIO_LIBPS2=y
+# CONFIG_SERIO_RAW is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+# CONFIG_VT_HW_CONSOLE_BINDING is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+CONFIG_SERIAL_8250=y
+# CONFIG_SERIAL_8250_CONSOLE is not set
+CONFIG_FIX_EARLYCON_MEM=y
+CONFIG_SERIAL_8250_PCI=y
+CONFIG_SERIAL_8250_PNP=y
+CONFIG_SERIAL_8250_NR_UARTS=4
+CONFIG_SERIAL_8250_RUNTIME_UARTS=4
+# CONFIG_SERIAL_8250_EXTENDED is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_CORE=y
+# CONFIG_SERIAL_JSM is not set
+CONFIG_UNIX98_PTYS=y
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_PRINTER=y
+# CONFIG_LP_CONSOLE is not set
+# CONFIG_PPDEV is not set
+# CONFIG_IPMI_HANDLER is not set
+# CONFIG_HW_RANDOM is not set
+# CONFIG_NVRAM is not set
+# CONFIG_RTC is not set
+# CONFIG_GEN_RTC is not set
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+# CONFIG_APPLICOM is not set
+# CONFIG_SONYPI is not set
+# CONFIG_MWAVE is not set
+# CONFIG_PC8736x_GPIO is not set
+# CONFIG_NSC_GPIO is not set
+# CONFIG_CS5535_GPIO is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_HPET is not set
+# CONFIG_HANGCHECK_TIMER is not set
+# CONFIG_TCG_TPM is not set
+# CONFIG_TELCLOCK is not set
+CONFIG_DEVPORT=y
+# CONFIG_I2C is not set
+
+#
+# SPI support
+#
+# CONFIG_SPI is not set
+# CONFIG_SPI_MASTER is not set
+# CONFIG_W1 is not set
+CONFIG_POWER_SUPPLY=y
+# CONFIG_POWER_SUPPLY_DEBUG is not set
+# CONFIG_PDA_POWER is not set
+# CONFIG_BATTERY_DS2760 is not set
+# CONFIG_HWMON is not set
+# CONFIG_WATCHDOG is not set
+
+#
+# Sonics Silicon Backplane
+#
+CONFIG_SSB_POSSIBLE=y
+CONFIG_SSB=m
+CONFIG_SSB_PCIHOST_POSSIBLE=y
+CONFIG_SSB_PCIHOST=y
+# CONFIG_SSB_SILENT is not set
+# CONFIG_SSB_DEBUG is not set
+CONFIG_SSB_DRIVER_PCICORE_POSSIBLE=y
+CONFIG_SSB_DRIVER_PCICORE=y
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_SM501 is not set
+# CONFIG_HTC_ASIC3 is not set
+# CONFIG_HTC_ASIC3_DS1WM is not set
+
+#
+# Multimedia devices
+#
+CONFIG_VIDEO_DEV=m
+CONFIG_VIDEO_V4L1=y
+CONFIG_VIDEO_V4L1_COMPAT=y
+CONFIG_VIDEO_V4L2=y
+CONFIG_VIDEO_CAPTURE_DRIVERS=y
+# CONFIG_VIDEO_ADV_DEBUG is not set
+CONFIG_VIDEO_HELPER_CHIPS_AUTO=y
+# CONFIG_VIDEO_VIVI is not set
+# CONFIG_VIDEO_PMS is not set
+# CONFIG_VIDEO_BWQCAM is not set
+# CONFIG_VIDEO_CQCAM is not set
+# CONFIG_VIDEO_CPIA is not set
+# CONFIG_VIDEO_CPIA2 is not set
+# CONFIG_VIDEO_STRADIS is not set
+CONFIG_V4L_USB_DRIVERS=y
+# CONFIG_USB_VICAM is not set
+# CONFIG_USB_IBMCAM is not set
+# CONFIG_USB_KONICAWC is not set
+# CONFIG_USB_QUICKCAM_MESSENGER is not set
+# CONFIG_USB_ET61X251 is not set
+CONFIG_USB_OV511=m
+CONFIG_USB_SE401=m
+CONFIG_USB_SN9C102=m
+CONFIG_USB_STV680=m
+# CONFIG_USB_ZC0301 is not set
+# CONFIG_USB_PWC is not set
+# CONFIG_USB_ZR364XX is not set
+CONFIG_RADIO_ADAPTERS=y
+# CONFIG_RADIO_CADET is not set
+# CONFIG_RADIO_RTRACK is not set
+# CONFIG_RADIO_RTRACK2 is not set
+# CONFIG_RADIO_AZTECH is not set
+# CONFIG_RADIO_GEMTEK is not set
+# CONFIG_RADIO_GEMTEK_PCI is not set
+# CONFIG_RADIO_MAXIRADIO is not set
+# CONFIG_RADIO_MAESTRO is not set
+# CONFIG_RADIO_SF16FMI is not set
+# CONFIG_RADIO_SF16FMR2 is not set
+# CONFIG_RADIO_TERRATEC is not set
+# CONFIG_RADIO_TRUST is not set
+# CONFIG_RADIO_TYPHOON is not set
+# CONFIG_RADIO_ZOLTRIX is not set
+CONFIG_USB_DSBR=m
+# CONFIG_DVB_CORE is not set
+CONFIG_DAB=y
+# CONFIG_USB_DABUSB is not set
+
+#
+# Graphics support
+#
+CONFIG_AGP=m
+CONFIG_AGP_ALI=m
+CONFIG_AGP_ATI=m
+CONFIG_AGP_AMD=m
+CONFIG_AGP_AMD64=m
+CONFIG_AGP_INTEL=m
+CONFIG_AGP_NVIDIA=m
+CONFIG_AGP_SIS=m
+CONFIG_AGP_SWORKS=m
+CONFIG_AGP_VIA=m
+CONFIG_AGP_EFFICEON=m
+CONFIG_DRM=m
+# CONFIG_DRM_TDFX is not set
+# CONFIG_DRM_R128 is not set
+# CONFIG_DRM_RADEON is not set
+# CONFIG_DRM_I810 is not set
+# CONFIG_DRM_I830 is not set
+# CONFIG_DRM_I915 is not set
+# CONFIG_DRM_MGA is not set
+# CONFIG_DRM_SIS is not set
+# CONFIG_DRM_VIA is not set
+# CONFIG_DRM_SAVAGE is not set
+# CONFIG_VGASTATE is not set
+CONFIG_VIDEO_OUTPUT_CONTROL=m
+CONFIG_FB=y
+# CONFIG_FIRMWARE_EDID is not set
+# CONFIG_FB_DDC is not set
+CONFIG_FB_CFB_FILLRECT=m
+CONFIG_FB_CFB_COPYAREA=m
+CONFIG_FB_CFB_IMAGEBLIT=m
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+# CONFIG_FB_SYS_FILLRECT is not set
+# CONFIG_FB_SYS_COPYAREA is not set
+# CONFIG_FB_SYS_IMAGEBLIT is not set
+# CONFIG_FB_SYS_FOPS is not set
+# CONFIG_FB_DEFERRED_IO is not set
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_BACKLIGHT is not set
+CONFIG_FB_MODE_HELPERS=y
+# CONFIG_FB_TILEBLITTING is not set
+
+#
+# Frame buffer hardware drivers
+#
+# CONFIG_FB_CIRRUS is not set
+# CONFIG_FB_PM2 is not set
+# CONFIG_FB_CYBER2000 is not set
+# CONFIG_FB_ARC is not set
+# CONFIG_FB_ASILIANT is not set
+# CONFIG_FB_IMSTT is not set
+# CONFIG_FB_VGA16 is not set
+CONFIG_FB_UVESA=m
+# CONFIG_FB_VESA is not set
+# CONFIG_FB_EFI is not set
+# CONFIG_FB_HECUBA is not set
+# CONFIG_FB_HGA is not set
+# CONFIG_FB_S1D13XXX is not set
+# CONFIG_FB_NVIDIA is not set
+# CONFIG_FB_RIVA is not set
+# CONFIG_FB_I810 is not set
+# CONFIG_FB_LE80578 is not set
+# CONFIG_FB_INTEL is not set
+# CONFIG_FB_MATROX is not set
+# CONFIG_FB_RADEON is not set
+# CONFIG_FB_ATY128 is not set
+# CONFIG_FB_ATY is not set
+# CONFIG_FB_S3 is not set
+# CONFIG_FB_SAVAGE is not set
+# CONFIG_FB_SIS is not set
+# CONFIG_FB_NEOMAGIC is not set
+# CONFIG_FB_KYRO is not set
+# CONFIG_FB_3DFX is not set
+# CONFIG_FB_VOODOO1 is not set
+# CONFIG_FB_VT8623 is not set
+# CONFIG_FB_CYBLA is not set
+# CONFIG_FB_TRIDENT is not set
+# CONFIG_FB_ARK is not set
+# CONFIG_FB_PM3 is not set
+# CONFIG_FB_GEODE is not set
+# CONFIG_FB_VIRTUAL is not set
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_LCD_CLASS_DEVICE=m
+CONFIG_BACKLIGHT_CLASS_DEVICE=m
+CONFIG_BACKLIGHT_CORGI=m
+# CONFIG_BACKLIGHT_PROGEAR is not set
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+CONFIG_VGA_CONSOLE=y
+# CONFIG_VGACON_SOFT_SCROLLBACK is not set
+CONFIG_VIDEO_SELECT=y
+# CONFIG_MDA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
+CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y
+CONFIG_FONTS=y
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+# CONFIG_FONT_6x11 is not set
+# CONFIG_FONT_7x14 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+# CONFIG_FONT_ACORN_8x8 is not set
+# CONFIG_FONT_MINI_4x6 is not set
+# CONFIG_FONT_SUN8x16 is not set
+# CONFIG_FONT_SUN12x22 is not set
+# CONFIG_FONT_10x18 is not set
+CONFIG_LOGO=y
+CONFIG_LOGO_LINUX_MONO=y
+CONFIG_LOGO_LINUX_VGA16=y
+# CONFIG_LOGO_LINUX_CLUT224 is not set
+CONFIG_LOGO_OHAND_CLUT224=y
+
+#
+# Sound
+#
+CONFIG_SOUND=y
+
+#
+# Advanced Linux Sound Architecture
+#
+CONFIG_SND=y
+CONFIG_SND_TIMER=y
+CONFIG_SND_PCM=y
+CONFIG_SND_SEQUENCER=y
+# CONFIG_SND_SEQ_DUMMY is not set
+CONFIG_SND_OSSEMUL=y
+CONFIG_SND_MIXER_OSS=y
+CONFIG_SND_PCM_OSS=y
+CONFIG_SND_PCM_OSS_PLUGINS=y
+CONFIG_SND_SEQUENCER_OSS=y
+# CONFIG_SND_DYNAMIC_MINORS is not set
+CONFIG_SND_SUPPORT_OLD_API=y
+CONFIG_SND_VERBOSE_PROCFS=y
+# CONFIG_SND_VERBOSE_PRINTK is not set
+# CONFIG_SND_DEBUG is not set
+
+#
+# Generic devices
+#
+CONFIG_SND_AC97_CODEC=y
+# CONFIG_SND_DUMMY is not set
+# CONFIG_SND_VIRMIDI is not set
+# CONFIG_SND_MTPAV is not set
+# CONFIG_SND_MTS64 is not set
+# CONFIG_SND_SERIAL_U16550 is not set
+# CONFIG_SND_MPU401 is not set
+# CONFIG_SND_PORTMAN2X4 is not set
+
+#
+# ISA devices
+#
+# CONFIG_SND_ADLIB is not set
+# CONFIG_SND_AD1816A is not set
+# CONFIG_SND_AD1848 is not set
+# CONFIG_SND_ALS100 is not set
+# CONFIG_SND_AZT2320 is not set
+# CONFIG_SND_CMI8330 is not set
+# CONFIG_SND_CS4231 is not set
+# CONFIG_SND_CS4232 is not set
+# CONFIG_SND_CS4236 is not set
+# CONFIG_SND_DT019X is not set
+# CONFIG_SND_ES968 is not set
+# CONFIG_SND_ES1688 is not set
+# CONFIG_SND_ES18XX is not set
+# CONFIG_SND_SC6000 is not set
+# CONFIG_SND_GUSCLASSIC is not set
+# CONFIG_SND_GUSEXTREME is not set
+# CONFIG_SND_GUSMAX is not set
+# CONFIG_SND_INTERWAVE is not set
+# CONFIG_SND_INTERWAVE_STB is not set
+# CONFIG_SND_OPL3SA2 is not set
+# CONFIG_SND_OPTI92X_AD1848 is not set
+# CONFIG_SND_OPTI92X_CS4231 is not set
+# CONFIG_SND_OPTI93X is not set
+# CONFIG_SND_MIRO is not set
+# CONFIG_SND_SB8 is not set
+# CONFIG_SND_SB16 is not set
+# CONFIG_SND_SBAWE is not set
+# CONFIG_SND_SGALAXY is not set
+# CONFIG_SND_SSCAPE is not set
+# CONFIG_SND_WAVEFRONT is not set
+
+#
+# PCI devices
+#
+# CONFIG_SND_AD1889 is not set
+# CONFIG_SND_ALS300 is not set
+# CONFIG_SND_ALS4000 is not set
+# CONFIG_SND_ALI5451 is not set
+# CONFIG_SND_ATIIXP is not set
+# CONFIG_SND_ATIIXP_MODEM is not set
+# CONFIG_SND_AU8810 is not set
+# CONFIG_SND_AU8820 is not set
+# CONFIG_SND_AU8830 is not set
+# CONFIG_SND_AZT3328 is not set
+# CONFIG_SND_BT87X is not set
+# CONFIG_SND_CA0106 is not set
+# CONFIG_SND_CMIPCI is not set
+# CONFIG_SND_CS4281 is not set
+# CONFIG_SND_CS46XX is not set
+# CONFIG_SND_CS5530 is not set
+# CONFIG_SND_CS5535AUDIO is not set
+# CONFIG_SND_DARLA20 is not set
+# CONFIG_SND_GINA20 is not set
+# CONFIG_SND_LAYLA20 is not set
+# CONFIG_SND_DARLA24 is not set
+# CONFIG_SND_GINA24 is not set
+# CONFIG_SND_LAYLA24 is not set
+# CONFIG_SND_MONA is not set
+# CONFIG_SND_MIA is not set
+# CONFIG_SND_ECHO3G is not set
+# CONFIG_SND_INDIGO is not set
+# CONFIG_SND_INDIGOIO is not set
+# CONFIG_SND_INDIGODJ is not set
+# CONFIG_SND_EMU10K1 is not set
+# CONFIG_SND_EMU10K1X is not set
+# CONFIG_SND_ENS1370 is not set
+# CONFIG_SND_ENS1371 is not set
+# CONFIG_SND_ES1938 is not set
+# CONFIG_SND_ES1968 is not set
+# CONFIG_SND_FM801 is not set
+# CONFIG_SND_HDA_INTEL is not set
+# CONFIG_SND_HDSP is not set
+# CONFIG_SND_HDSPM is not set
+# CONFIG_SND_ICE1712 is not set
+# CONFIG_SND_ICE1724 is not set
+CONFIG_SND_INTEL8X0=y
+# CONFIG_SND_INTEL8X0M is not set
+# CONFIG_SND_KORG1212 is not set
+# CONFIG_SND_MAESTRO3 is not set
+# CONFIG_SND_MIXART is not set
+# CONFIG_SND_NM256 is not set
+# CONFIG_SND_PCXHR is not set
+# CONFIG_SND_RIPTIDE is not set
+# CONFIG_SND_RME32 is not set
+# CONFIG_SND_RME96 is not set
+# CONFIG_SND_RME9652 is not set
+# CONFIG_SND_SONICVIBES is not set
+# CONFIG_SND_TRIDENT is not set
+# CONFIG_SND_VIA82XX is not set
+# CONFIG_SND_VIA82XX_MODEM is not set
+# CONFIG_SND_VX222 is not set
+# CONFIG_SND_YMFPCI is not set
+# CONFIG_SND_AC97_POWER_SAVE is not set
+
+#
+# USB devices
+#
+# CONFIG_SND_USB_AUDIO is not set
+# CONFIG_SND_USB_USX2Y is not set
+# CONFIG_SND_USB_CAIAQ is not set
+
+#
+# System on Chip audio support
+#
+# CONFIG_SND_SOC is not set
+
+#
+# SoC Audio support for SuperH
+#
+
+#
+# Open Sound System
+#
+# CONFIG_SOUND_PRIME is not set
+CONFIG_AC97_BUS=y
+CONFIG_HID_SUPPORT=y
+CONFIG_HID=y
+# CONFIG_HID_DEBUG is not set
+# CONFIG_HIDRAW is not set
+
+#
+# USB Input Devices
+#
+CONFIG_USB_HID=y
+# CONFIG_USB_HIDINPUT_POWERBOOK is not set
+# CONFIG_HID_FF is not set
+# CONFIG_USB_HIDDEV is not set
+CONFIG_USB_SUPPORT=y
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARCH_HAS_OHCI=y
+CONFIG_USB_ARCH_HAS_EHCI=y
+CONFIG_USB=y
+# CONFIG_USB_DEBUG is not set
+
+#
+# Miscellaneous USB options
+#
+CONFIG_USB_DEVICEFS=y
+CONFIG_USB_DEVICE_CLASS=y
+# CONFIG_USB_DYNAMIC_MINORS is not set
+CONFIG_USB_SUSPEND=y
+# CONFIG_USB_PERSIST is not set
+# CONFIG_USB_OTG is not set
+
+#
+# USB Host Controller Drivers
+#
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_SPLIT_ISO=y
+# CONFIG_USB_EHCI_ROOT_HUB_TT is not set
+# CONFIG_USB_EHCI_TT_NEWSCHED is not set
+# CONFIG_USB_ISP116X_HCD is not set
+CONFIG_USB_OHCI_HCD=y
+# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set
+# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set
+CONFIG_USB_OHCI_LITTLE_ENDIAN=y
+CONFIG_USB_UHCI_HCD=y
+CONFIG_USB_SL811_HCD=m
+# CONFIG_USB_R8A66597_HCD is not set
+
+#
+# USB Device Class drivers
+#
+CONFIG_USB_ACM=m
+CONFIG_USB_PRINTER=m
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
+#
+
+#
+# may also be needed; see USB_STORAGE Help for more information
+#
+CONFIG_USB_STORAGE=m
+# CONFIG_USB_STORAGE_DEBUG is not set
+# CONFIG_USB_STORAGE_DATAFAB is not set
+# CONFIG_USB_STORAGE_FREECOM is not set
+# CONFIG_USB_STORAGE_ISD200 is not set
+# CONFIG_USB_STORAGE_DPCM is not set
+# CONFIG_USB_STORAGE_USBAT is not set
+# CONFIG_USB_STORAGE_SDDR09 is not set
+# CONFIG_USB_STORAGE_SDDR55 is not set
+# CONFIG_USB_STORAGE_JUMPSHOT is not set
+# CONFIG_USB_STORAGE_ALAUDA is not set
+# CONFIG_USB_STORAGE_KARMA is not set
+# CONFIG_USB_LIBUSUAL is not set
+
+#
+# USB Imaging devices
+#
+CONFIG_USB_MDC800=m
+CONFIG_USB_MICROTEK=m
+CONFIG_USB_MON=y
+
+#
+# USB port drivers
+#
+# CONFIG_USB_USS720 is not set
+
+#
+# USB Serial Converter support
+#
+CONFIG_USB_SERIAL=m
+CONFIG_USB_SERIAL_GENERIC=y
+# CONFIG_USB_SERIAL_AIRCABLE is not set
+# CONFIG_USB_SERIAL_AIRPRIME is not set
+# CONFIG_USB_SERIAL_ARK3116 is not set
+CONFIG_USB_SERIAL_BELKIN=m
+# CONFIG_USB_SERIAL_CH341 is not set
+# CONFIG_USB_SERIAL_WHITEHEAT is not set
+CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m
+CONFIG_USB_SERIAL_CP2101=m
+CONFIG_USB_SERIAL_CYPRESS_M8=m
+CONFIG_USB_SERIAL_EMPEG=m
+CONFIG_USB_SERIAL_FTDI_SIO=m
+# CONFIG_USB_SERIAL_FUNSOFT is not set
+CONFIG_USB_SERIAL_VISOR=m
+CONFIG_USB_SERIAL_IPAQ=m
+CONFIG_USB_SERIAL_IR=m
+CONFIG_USB_SERIAL_EDGEPORT=m
+CONFIG_USB_SERIAL_EDGEPORT_TI=m
+CONFIG_USB_SERIAL_GARMIN=m
+CONFIG_USB_SERIAL_IPW=m
+CONFIG_USB_SERIAL_KEYSPAN_PDA=m
+CONFIG_USB_SERIAL_KEYSPAN=m
+# CONFIG_USB_SERIAL_KEYSPAN_MPR is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19QW is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19QI is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA49WLC is not set
+CONFIG_USB_SERIAL_KLSI=m
+CONFIG_USB_SERIAL_KOBIL_SCT=m
+CONFIG_USB_SERIAL_MCT_U232=m
+# CONFIG_USB_SERIAL_MOS7720 is not set
+# CONFIG_USB_SERIAL_MOS7840 is not set
+# CONFIG_USB_SERIAL_NAVMAN is not set
+CONFIG_USB_SERIAL_PL2303=m
+# CONFIG_USB_SERIAL_OTI6858 is not set
+# CONFIG_USB_SERIAL_HP4X is not set
+CONFIG_USB_SERIAL_SAFE=m
+# CONFIG_USB_SERIAL_SAFE_PADDED is not set
+# CONFIG_USB_SERIAL_SIERRAWIRELESS is not set
+CONFIG_USB_SERIAL_TI=m
+CONFIG_USB_SERIAL_CYBERJACK=m
+CONFIG_USB_SERIAL_XIRCOM=m
+# CONFIG_USB_SERIAL_OPTION is not set
+CONFIG_USB_SERIAL_OMNINET=m
+# CONFIG_USB_SERIAL_DEBUG is not set
+CONFIG_USB_EZUSB=y
+
+#
+# USB Miscellaneous drivers
+#
+CONFIG_USB_EMI62=m
+CONFIG_USB_EMI26=m
+# CONFIG_USB_ADUTUX is not set
+CONFIG_USB_AUERSWALD=m
+CONFIG_USB_RIO500=m
+CONFIG_USB_LEGOTOWER=m
+CONFIG_USB_LCD=m
+# CONFIG_USB_BERRY_CHARGE is not set
+CONFIG_USB_LED=m
+# CONFIG_USB_CYPRESS_CY7C63 is not set
+CONFIG_USB_CYTHERM=m
+# CONFIG_USB_PHIDGET is not set
+CONFIG_USB_IDMOUSE=m
+# CONFIG_USB_FTDI_ELAN is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+# CONFIG_USB_SISUSBVGA is not set
+# CONFIG_USB_LD is not set
+# CONFIG_USB_TRANCEVIBRATOR is not set
+# CONFIG_USB_IOWARRIOR is not set
+# CONFIG_USB_TEST is not set
+
+#
+# USB DSL modem support
+#
+
+#
+# USB Gadget Support
+#
+# CONFIG_USB_GADGET is not set
+# CONFIG_MMC is not set
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+
+#
+# LED drivers
+#
+
+#
+# LED Triggers
+#
+# CONFIG_LEDS_TRIGGERS is not set
+# CONFIG_INFINIBAND is not set
+# CONFIG_EDAC is not set
+# CONFIG_RTC_CLASS is not set
+# CONFIG_DMADEVICES is not set
+# CONFIG_AUXDISPLAY is not set
+CONFIG_VIRTUALIZATION=y
+# CONFIG_KVM is not set
+# CONFIG_LGUEST is not set
+
+#
+# Userspace I/O
+#
+# CONFIG_UIO is not set
+
+#
+# Firmware Drivers
+#
+# CONFIG_EDD is not set
+# CONFIG_DELL_RBU is not set
+# CONFIG_DCDBAS is not set
+CONFIG_DMIID=y
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+# CONFIG_EXT2_FS_XIP is not set
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_FS_XATTR is not set
+# CONFIG_EXT4DEV_FS is not set
+CONFIG_JBD=y
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_FS_POSIX_ACL is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_GFS2_FS is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_ROMFS_FS is not set
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_QUOTA is not set
+CONFIG_DNOTIFY=y
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+CONFIG_ISO9660_FS=y
+CONFIG_JOLIET=y
+# CONFIG_ZISOFS is not set
+CONFIG_UDF_FS=y
+CONFIG_UDF_NLS=y
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+# CONFIG_MSDOS_FS is not set
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+# CONFIG_HUGETLBFS is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_CRAMFS=m
+CONFIG_SQUASHFS=m
+# CONFIG_SQUASHFS_EMBEDDED is not set
+CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3
+# CONFIG_VXFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+# CONFIG_NFS_V3_ACL is not set
+# CONFIG_NFS_V4 is not set
+# CONFIG_NFS_DIRECTIO is not set
+CONFIG_NFSD=y
+CONFIG_NFSD_V3=y
+# CONFIG_NFSD_V3_ACL is not set
+# CONFIG_NFSD_V4 is not set
+# CONFIG_NFSD_TCP is not set
+CONFIG_ROOT_NFS=y
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+CONFIG_EXPORTFS=y
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=y
+# CONFIG_SUNRPC_BIND34 is not set
+# CONFIG_RPCSEC_GSS_KRB5 is not set
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+CONFIG_SMB_FS=y
+# CONFIG_SMB_NLS_DEFAULT is not set
+CONFIG_CIFS=m
+# CONFIG_CIFS_STATS is not set
+# CONFIG_CIFS_WEAK_PW_HASH is not set
+# CONFIG_CIFS_XATTR is not set
+# CONFIG_CIFS_DEBUG2 is not set
+# CONFIG_CIFS_EXPERIMENTAL is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_MINIX_SUBPARTITION is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_UNIXWARE_DISKLABEL is not set
+# CONFIG_LDM_PARTITION is not set
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_ULTRIX_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_KARMA_PARTITION is not set
+# CONFIG_EFI_PARTITION is not set
+# CONFIG_SYSV68_PARTITION is not set
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="cp437"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_737=m
+CONFIG_NLS_CODEPAGE_775=m
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
+CONFIG_NLS_CODEPAGE_855=m
+CONFIG_NLS_CODEPAGE_857=m
+CONFIG_NLS_CODEPAGE_860=m
+CONFIG_NLS_CODEPAGE_861=m
+CONFIG_NLS_CODEPAGE_862=m
+CONFIG_NLS_CODEPAGE_863=m
+CONFIG_NLS_CODEPAGE_864=m
+CONFIG_NLS_CODEPAGE_865=m
+CONFIG_NLS_CODEPAGE_866=m
+CONFIG_NLS_CODEPAGE_869=m
+CONFIG_NLS_CODEPAGE_936=m
+CONFIG_NLS_CODEPAGE_950=m
+CONFIG_NLS_CODEPAGE_932=m
+CONFIG_NLS_CODEPAGE_949=m
+CONFIG_NLS_CODEPAGE_874=m
+CONFIG_NLS_ISO8859_8=m
+CONFIG_NLS_CODEPAGE_1250=m
+CONFIG_NLS_CODEPAGE_1251=m
+CONFIG_NLS_ASCII=m
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_ISO8859_2=m
+CONFIG_NLS_ISO8859_3=m
+CONFIG_NLS_ISO8859_4=m
+CONFIG_NLS_ISO8859_5=m
+CONFIG_NLS_ISO8859_6=m
+CONFIG_NLS_ISO8859_7=m
+CONFIG_NLS_ISO8859_9=m
+CONFIG_NLS_ISO8859_13=m
+CONFIG_NLS_ISO8859_14=m
+CONFIG_NLS_ISO8859_15=m
+CONFIG_NLS_KOI8_R=m
+CONFIG_NLS_KOI8_U=m
+CONFIG_NLS_UTF8=y
+# CONFIG_DLM is not set
+CONFIG_INSTRUMENTATION=y
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=m
+# CONFIG_KPROBES is not set
+# CONFIG_MARKERS is not set
+
+#
+# Kernel hacking
+#
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+# CONFIG_PRINTK_TIME is not set
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_DEBUG_FS is not set
+# CONFIG_HEADERS_CHECK is not set
+# CONFIG_DEBUG_KERNEL is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+# CONFIG_SAMPLES is not set
+CONFIG_EARLY_PRINTK=y
+CONFIG_X86_FIND_SMP_CONFIG=y
+CONFIG_X86_MPPARSE=y
+CONFIG_DOUBLEFAULT=y
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+CONFIG_CRYPTO=y
+CONFIG_CRYPTO_ALGAPI=m
+CONFIG_CRYPTO_BLKCIPHER=m
+CONFIG_CRYPTO_HASH=m
+CONFIG_CRYPTO_MANAGER=m
+CONFIG_CRYPTO_HMAC=m
+# CONFIG_CRYPTO_XCBC is not set
+CONFIG_CRYPTO_NULL=m
+CONFIG_CRYPTO_MD4=m
+CONFIG_CRYPTO_MD5=m
+CONFIG_CRYPTO_SHA1=m
+CONFIG_CRYPTO_SHA256=m
+CONFIG_CRYPTO_SHA512=m
+CONFIG_CRYPTO_WP512=m
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_GF128MUL is not set
+CONFIG_CRYPTO_ECB=m
+CONFIG_CRYPTO_CBC=m
+CONFIG_CRYPTO_PCBC=m
+# CONFIG_CRYPTO_LRW is not set
+# CONFIG_CRYPTO_XTS is not set
+# CONFIG_CRYPTO_CRYPTD is not set
+CONFIG_CRYPTO_DES=m
+# CONFIG_CRYPTO_FCRYPT is not set
+CONFIG_CRYPTO_BLOWFISH=m
+CONFIG_CRYPTO_TWOFISH=m
+CONFIG_CRYPTO_TWOFISH_COMMON=m
+# CONFIG_CRYPTO_TWOFISH_586 is not set
+CONFIG_CRYPTO_SERPENT=m
+CONFIG_CRYPTO_AES=m
+# CONFIG_CRYPTO_AES_586 is not set
+CONFIG_CRYPTO_CAST5=m
+CONFIG_CRYPTO_CAST6=m
+CONFIG_CRYPTO_TEA=m
+CONFIG_CRYPTO_ARC4=m
+CONFIG_CRYPTO_KHAZAD=m
+CONFIG_CRYPTO_ANUBIS=m
+# CONFIG_CRYPTO_SEED is not set
+CONFIG_CRYPTO_DEFLATE=m
+CONFIG_CRYPTO_LZO=m
+CONFIG_CRYPTO_MICHAEL_MIC=m
+CONFIG_CRYPTO_CRC32C=m
+CONFIG_CRYPTO_CAMELLIA=m
+CONFIG_CRYPTO_TEST=m
+# CONFIG_CRYPTO_AUTHENC is not set
+# CONFIG_CRYPTO_HW is not set
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+CONFIG_CRC_CCITT=y
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
+CONFIG_LIBCRC32C=m
+CONFIG_AUDIT_GENERIC=y
+CONFIG_ZLIB_INFLATE=m
+CONFIG_ZLIB_DEFLATE=m
+CONFIG_LZO_COMPRESS=m
+CONFIG_LZO_DECOMPRESS=m
+CONFIG_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
diff --git a/recipes/linux/linux-rp-2.6.26/defconfig-spitz b/recipes/linux/linux-rp-2.6.26/defconfig-spitz
new file mode 100644
index 0000000000..fd2285d4fb
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.26/defconfig-spitz
@@ -0,0 +1,1799 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.26
+# Tue Feb 3 01:16:06 2009
+#
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+CONFIG_GENERIC_GPIO=y
+CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_MMU=y
+# CONFIG_NO_IOPORT is not set
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+# CONFIG_ARCH_HAS_ILOG2_U32 is not set
+# CONFIG_ARCH_HAS_ILOG2_U64 is not set
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_ARCH_SUPPORTS_AOUT=y
+CONFIG_ZONE_DMA=y
+CONFIG_ARCH_MTD_XIP=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_LOCK_KERNEL=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+# CONFIG_POSIX_MQUEUE is not set
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_BSD_PROCESS_ACCT_V3=y
+# CONFIG_TASKSTATS is not set
+# CONFIG_AUDIT is not set
+# CONFIG_IKCONFIG is not set
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_CGROUPS is not set
+# CONFIG_GROUP_SCHED is not set
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_SYSFS_DEPRECATED_V2=y
+# CONFIG_RELAY is not set
+# CONFIG_NAMESPACES is not set
+# CONFIG_BLK_DEV_INITRD is not set
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+CONFIG_EMBEDDED=y
+CONFIG_UID16=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_SYSCTL_SYSCALL_CHECK=y
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_COMPAT_BRK=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_ANON_INODES=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLAB=y
+# CONFIG_SLUB is not set
+# CONFIG_SLOB is not set
+CONFIG_PROFILING=y
+# CONFIG_MARKERS is not set
+CONFIG_OPROFILE=m
+CONFIG_HAVE_OPROFILE=y
+# CONFIG_KPROBES is not set
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+# CONFIG_HAVE_DMA_ATTRS is not set
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+# CONFIG_TINY_SHMEM is not set
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+# CONFIG_MODULE_FORCE_LOAD is not set
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+CONFIG_BLOCK=y
+# CONFIG_LBD is not set
+# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_LSF is not set
+# CONFIG_BLK_DEV_BSG is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=m
+CONFIG_IOSCHED_CFQ=m
+CONFIG_DEFAULT_AS=y
+# CONFIG_DEFAULT_DEADLINE is not set
+# CONFIG_DEFAULT_CFQ is not set
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="anticipatory"
+CONFIG_CLASSIC_RCU=y
+
+#
+# System Type
+#
+# CONFIG_ARCH_AAEC2000 is not set
+# CONFIG_ARCH_INTEGRATOR is not set
+# CONFIG_ARCH_REALVIEW is not set
+# CONFIG_ARCH_VERSATILE is not set
+# CONFIG_ARCH_AT91 is not set
+# CONFIG_ARCH_CLPS7500 is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_CO285 is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE is not set
+# CONFIG_ARCH_NETX is not set
+# CONFIG_ARCH_H720X is not set
+# CONFIG_ARCH_IMX is not set
+# CONFIG_ARCH_IOP13XX is not set
+# CONFIG_ARCH_IOP32X is not set
+# CONFIG_ARCH_IOP33X is not set
+# CONFIG_ARCH_IXP23XX is not set
+# CONFIG_ARCH_IXP2000 is not set
+# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_L7200 is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_NS9XXX is not set
+# CONFIG_ARCH_MXC is not set
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_PNX4008 is not set
+CONFIG_ARCH_PXA=y
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP is not set
+# CONFIG_ARCH_MSM7X00A is not set
+
+#
+# Intel PXA2xx/PXA3xx Implementations
+#
+# CONFIG_ARCH_GUMSTIX is not set
+# CONFIG_ARCH_LUBBOCK is not set
+# CONFIG_MACH_LOGICPD_PXA270 is not set
+# CONFIG_MACH_MAINSTONE is not set
+# CONFIG_ARCH_PXA_IDP is not set
+CONFIG_PXA_SHARPSL=y
+# CONFIG_ARCH_PXA_ESERIES is not set
+# CONFIG_MACH_TRIZEPS4 is not set
+# CONFIG_MACH_HX2750 is not set
+# CONFIG_MACH_EM_X270 is not set
+# CONFIG_MACH_COLIBRI is not set
+# CONFIG_MACH_ZYLONITE is not set
+# CONFIG_MACH_LITTLETON is not set
+# CONFIG_MACH_ARMCORE is not set
+# CONFIG_MACH_MAGICIAN is not set
+# CONFIG_MACH_PCM027 is not set
+# CONFIG_MACH_HTCUNIVERSAL is not set
+# CONFIG_PXA_SHARPSL_25x is not set
+CONFIG_PXA_SHARPSL_27x=y
+CONFIG_MACH_AKITA=y
+CONFIG_MACH_SPITZ=y
+CONFIG_MACH_BORZOI=y
+CONFIG_PXA27x=y
+CONFIG_PXA_SHARP_Cxx00=y
+CONFIG_PXA_SSP=y
+# CONFIG_PXA_KEYS is not set
+
+#
+# Boot options
+#
+
+#
+# Power management
+#
+
+#
+# Processor Type
+#
+CONFIG_CPU_32=y
+CONFIG_CPU_XSCALE=y
+CONFIG_CPU_32v5=y
+CONFIG_CPU_ABRT_EV5T=y
+CONFIG_CPU_PABRT_NOIFAR=y
+CONFIG_CPU_CACHE_VIVT=y
+CONFIG_CPU_TLB_V4WBI=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+
+#
+# Processor Features
+#
+CONFIG_ARM_THUMB=y
+# CONFIG_CPU_DCACHE_DISABLE is not set
+# CONFIG_OUTER_CACHE is not set
+CONFIG_IWMMXT=y
+CONFIG_XSCALE_PMU=y
+CONFIG_SHARP_PARAM=y
+CONFIG_SHARPSL_PM=y
+CONFIG_SHARP_SCOOP=y
+
+#
+# Bus support
+#
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+CONFIG_PCCARD=y
+# CONFIG_PCMCIA_DEBUG is not set
+CONFIG_PCMCIA=y
+CONFIG_PCMCIA_LOAD_CIS=y
+CONFIG_PCMCIA_IOCTL=y
+
+#
+# PC-card bridges
+#
+CONFIG_PCMCIA_PXA2XX=y
+
+#
+# Kernel Features
+#
+CONFIG_TICK_ONESHOT=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_PREEMPT=y
+CONFIG_HZ=100
+CONFIG_AEABI=y
+# CONFIG_OABI_COMPAT is not set
+# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+# CONFIG_SPARSEMEM_STATIC is not set
+# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4096
+# CONFIG_RESOURCES_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=1
+CONFIG_BOUNCE=y
+CONFIG_VIRT_TO_BUS=y
+CONFIG_ALIGNMENT_TRAP=y
+
+#
+# Boot options
+#
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="console=ttyS0,115200n8 console=tty1 noinitrd root=/dev/hda1 rootfstype=ext3 rootdelay=1 rw fbcon=rotate:1 dyntick=enable debug"
+# CONFIG_XIP_KERNEL is not set
+CONFIG_KEXEC=y
+CONFIG_ATAGS_PROC=y
+
+#
+# CPU Frequency scaling
+#
+# CONFIG_CPU_FREQ is not set
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+
+#
+# Userspace binary formats
+#
+CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_AOUT=m
+CONFIG_BINFMT_MISC=m
+
+#
+# Power management options
+#
+CONFIG_PM=y
+# CONFIG_PM_DEBUG is not set
+CONFIG_PM_SLEEP=y
+CONFIG_SUSPEND=y
+CONFIG_SUSPEND_FREEZER=y
+CONFIG_APM_EMULATION=y
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+
+#
+# Networking
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=m
+CONFIG_PACKET_MMAP=y
+CONFIG_UNIX=y
+CONFIG_XFRM=y
+CONFIG_XFRM_USER=m
+# CONFIG_XFRM_SUB_POLICY is not set
+# CONFIG_XFRM_MIGRATE is not set
+# CONFIG_XFRM_STATISTICS is not set
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+# CONFIG_IP_PNP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_ARPD is not set
+CONFIG_SYN_COOKIES=y
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+CONFIG_INET_TUNNEL=m
+CONFIG_INET_XFRM_MODE_TRANSPORT=m
+CONFIG_INET_XFRM_MODE_TUNNEL=m
+CONFIG_INET_XFRM_MODE_BEET=m
+# CONFIG_INET_LRO is not set
+CONFIG_INET_DIAG=m
+CONFIG_INET_TCP_DIAG=m
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+# CONFIG_IP_VS is not set
+CONFIG_IPV6=m
+# CONFIG_IPV6_PRIVACY is not set
+# CONFIG_IPV6_ROUTER_PREF is not set
+# CONFIG_IPV6_OPTIMISTIC_DAD is not set
+CONFIG_INET6_AH=m
+CONFIG_INET6_ESP=m
+CONFIG_INET6_IPCOMP=m
+# CONFIG_IPV6_MIP6 is not set
+CONFIG_INET6_XFRM_TUNNEL=m
+CONFIG_INET6_TUNNEL=m
+CONFIG_INET6_XFRM_MODE_TRANSPORT=m
+CONFIG_INET6_XFRM_MODE_TUNNEL=m
+CONFIG_INET6_XFRM_MODE_BEET=m
+# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set
+CONFIG_IPV6_SIT=m
+CONFIG_IPV6_NDISC_NODETYPE=y
+CONFIG_IPV6_TUNNEL=m
+# CONFIG_IPV6_MULTIPLE_TABLES is not set
+# CONFIG_IPV6_MROUTE is not set
+# CONFIG_NETWORK_SECMARK is not set
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+CONFIG_NETFILTER_ADVANCED=y
+
+#
+# Core Netfilter Configuration
+#
+# CONFIG_NETFILTER_NETLINK_QUEUE is not set
+# CONFIG_NETFILTER_NETLINK_LOG is not set
+# CONFIG_NF_CONNTRACK is not set
+CONFIG_NETFILTER_XTABLES=m
+# CONFIG_NETFILTER_XT_TARGET_CLASSIFY is not set
+# CONFIG_NETFILTER_XT_TARGET_DSCP is not set
+# CONFIG_NETFILTER_XT_TARGET_MARK is not set
+# CONFIG_NETFILTER_XT_TARGET_NFQUEUE is not set
+# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set
+# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set
+# CONFIG_NETFILTER_XT_TARGET_TRACE is not set
+# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set
+# CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP is not set
+# CONFIG_NETFILTER_XT_MATCH_COMMENT is not set
+# CONFIG_NETFILTER_XT_MATCH_DCCP is not set
+# CONFIG_NETFILTER_XT_MATCH_DSCP is not set
+# CONFIG_NETFILTER_XT_MATCH_ESP is not set
+# CONFIG_NETFILTER_XT_MATCH_IPRANGE is not set
+# CONFIG_NETFILTER_XT_MATCH_LENGTH is not set
+# CONFIG_NETFILTER_XT_MATCH_LIMIT is not set
+# CONFIG_NETFILTER_XT_MATCH_MAC is not set
+# CONFIG_NETFILTER_XT_MATCH_MARK is not set
+# CONFIG_NETFILTER_XT_MATCH_OWNER is not set
+# CONFIG_NETFILTER_XT_MATCH_POLICY is not set
+# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set
+# CONFIG_NETFILTER_XT_MATCH_PKTTYPE is not set
+# CONFIG_NETFILTER_XT_MATCH_QUOTA is not set
+# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set
+# CONFIG_NETFILTER_XT_MATCH_REALM is not set
+# CONFIG_NETFILTER_XT_MATCH_SCTP is not set
+# CONFIG_NETFILTER_XT_MATCH_STATISTIC is not set
+# CONFIG_NETFILTER_XT_MATCH_STRING is not set
+# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set
+# CONFIG_NETFILTER_XT_MATCH_TIME is not set
+# CONFIG_NETFILTER_XT_MATCH_U32 is not set
+# CONFIG_NETFILTER_XT_MATCH_HASHLIMIT is not set
+
+#
+# IP: Netfilter Configuration
+#
+CONFIG_IP_NF_QUEUE=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_RECENT=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_AH=m
+CONFIG_IP_NF_MATCH_TTL=m
+CONFIG_IP_NF_MATCH_ADDRTYPE=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_LOG=m
+CONFIG_IP_NF_TARGET_ULOG=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_TTL=m
+CONFIG_IP_NF_RAW=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+
+#
+# IPv6: Netfilter Configuration
+#
+# CONFIG_IP6_NF_QUEUE is not set
+# CONFIG_IP6_NF_IPTABLES is not set
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_SCTP is not set
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_SCHED is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_CAN is not set
+CONFIG_IRDA=m
+
+#
+# IrDA protocols
+#
+CONFIG_IRLAN=m
+CONFIG_IRNET=m
+CONFIG_IRCOMM=m
+# CONFIG_IRDA_ULTRA is not set
+
+#
+# IrDA options
+#
+# CONFIG_IRDA_CACHE_LAST_LSAP is not set
+# CONFIG_IRDA_FAST_RR is not set
+# CONFIG_IRDA_DEBUG is not set
+
+#
+# Infrared-port device drivers
+#
+
+#
+# SIR device drivers
+#
+# CONFIG_IRTTY_SIR is not set
+
+#
+# Dongle support
+#
+# CONFIG_KINGSUN_DONGLE is not set
+# CONFIG_KSDAZZLE_DONGLE is not set
+# CONFIG_KS959_DONGLE is not set
+
+#
+# FIR device drivers
+#
+# CONFIG_USB_IRDA is not set
+# CONFIG_SIGMATEL_FIR is not set
+CONFIG_PXA_FICP=m
+# CONFIG_MCS_FIR is not set
+CONFIG_BT=m
+CONFIG_BT_L2CAP=m
+CONFIG_BT_SCO=m
+CONFIG_BT_RFCOMM=m
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=m
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+CONFIG_BT_HIDP=m
+
+#
+# Bluetooth device drivers
+#
+CONFIG_BT_HCIUSB=m
+# CONFIG_BT_HCIUSB_SCO is not set
+# CONFIG_BT_HCIBTSDIO is not set
+CONFIG_BT_HCIUART=m
+CONFIG_BT_HCIUART_H4=y
+CONFIG_BT_HCIUART_BCSP=y
+# CONFIG_BT_HCIUART_LL is not set
+CONFIG_BT_HCIBCM203X=m
+CONFIG_BT_HCIBPA10X=m
+CONFIG_BT_HCIBFUSB=m
+CONFIG_BT_HCIDTL1=m
+CONFIG_BT_HCIBT3C=m
+CONFIG_BT_HCIBLUECARD=m
+CONFIG_BT_HCIBTUART=m
+CONFIG_BT_HCIVHCI=m
+# CONFIG_AF_RXRPC is not set
+
+#
+# Wireless
+#
+# CONFIG_CFG80211 is not set
+CONFIG_WIRELESS_EXT=y
+# CONFIG_MAC80211 is not set
+CONFIG_IEEE80211=m
+# CONFIG_IEEE80211_DEBUG is not set
+CONFIG_IEEE80211_CRYPT_WEP=m
+CONFIG_IEEE80211_CRYPT_CCMP=m
+CONFIG_IEEE80211_CRYPT_TKIP=m
+# CONFIG_RFKILL is not set
+# CONFIG_NET_9P is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_CONNECTOR is not set
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+# CONFIG_MTD_CONCAT is not set
+CONFIG_MTD_PARTITIONS=y
+# CONFIG_MTD_REDBOOT_PARTS is not set
+CONFIG_MTD_CMDLINE_PARTS=y
+# CONFIG_MTD_AFS_PARTS is not set
+# CONFIG_MTD_AR7_PARTS is not set
+
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+# CONFIG_MTD_OOPS is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+# CONFIG_MTD_CFI is not set
+# CONFIG_MTD_JEDECPROBE is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+# CONFIG_MTD_RAM is not set
+CONFIG_MTD_ROM=y
+# CONFIG_MTD_ABSENT is not set
+
+#
+# Mapping drivers for chip access
+#
+CONFIG_MTD_COMPLEX_MAPPINGS=y
+# CONFIG_MTD_PHYSMAP is not set
+CONFIG_MTD_SHARP_SL=y
+# CONFIG_MTD_PLATRAM is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLOCK2MTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_VERIFY_WRITE=y
+# CONFIG_MTD_NAND_ECC_SMC is not set
+# CONFIG_MTD_NAND_MUSEUM_IDS is not set
+# CONFIG_MTD_NAND_H1900 is not set
+CONFIG_MTD_NAND_IDS=y
+# CONFIG_MTD_NAND_DISKONCHIP is not set
+CONFIG_MTD_NAND_SHARPSL=y
+# CONFIG_MTD_NAND_NANDSIM is not set
+# CONFIG_MTD_NAND_PLATFORM is not set
+# CONFIG_MTD_ALAUDA is not set
+# CONFIG_MTD_ONENAND is not set
+
+#
+# UBI - Unsorted block images
+#
+# CONFIG_MTD_UBI is not set
+# CONFIG_PARPORT is not set
+CONFIG_BLK_DEV=y
+# CONFIG_BLK_DEV_COW_COMMON is not set
+CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_CRYPTOLOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_UB is not set
+# CONFIG_BLK_DEV_RAM is not set
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+CONFIG_MISC_DEVICES=y
+# CONFIG_EEPROM_93CX6 is not set
+# CONFIG_ENCLOSURE_SERVICES is not set
+CONFIG_HAVE_IDE=y
+CONFIG_IDE=y
+CONFIG_IDE_MAX_HWIFS=4
+CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_IDE_SATA is not set
+CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_IDEDISK_MULTI_MODE is not set
+CONFIG_BLK_DEV_IDECS=y
+# CONFIG_BLK_DEV_IDECD is not set
+# CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
+# CONFIG_BLK_DEV_IDESCSI is not set
+# CONFIG_IDE_TASK_IOCTL is not set
+CONFIG_IDE_PROC_FS=y
+
+#
+# IDE chipset support/bugfixes
+#
+# CONFIG_BLK_DEV_PLATFORM is not set
+# CONFIG_BLK_DEV_IDEDMA is not set
+# CONFIG_BLK_DEV_HD is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+CONFIG_SCSI=m
+CONFIG_SCSI_DMA=y
+# CONFIG_SCSI_TGT is not set
+# CONFIG_SCSI_NETLINK is not set
+CONFIG_SCSI_PROC_FS=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=m
+CONFIG_CHR_DEV_ST=m
+CONFIG_CHR_DEV_OSST=m
+CONFIG_BLK_DEV_SR=m
+# CONFIG_BLK_DEV_SR_VENDOR is not set
+CONFIG_CHR_DEV_SG=m
+# CONFIG_CHR_DEV_SCH is not set
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+CONFIG_SCSI_MULTI_LUN=y
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
+# CONFIG_SCSI_SCAN_ASYNC is not set
+CONFIG_SCSI_WAIT_SCAN=m
+
+#
+# SCSI Transports
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+# CONFIG_SCSI_ISCSI_ATTRS is not set
+# CONFIG_SCSI_SAS_LIBSAS is not set
+# CONFIG_SCSI_SRP_ATTRS is not set
+CONFIG_SCSI_LOWLEVEL=y
+# CONFIG_ISCSI_TCP is not set
+# CONFIG_SCSI_DEBUG is not set
+# CONFIG_SCSI_LOWLEVEL_PCMCIA is not set
+# CONFIG_ATA is not set
+CONFIG_MD=y
+# CONFIG_BLK_DEV_MD is not set
+CONFIG_BLK_DEV_DM=m
+# CONFIG_DM_DEBUG is not set
+CONFIG_DM_CRYPT=m
+CONFIG_DM_SNAPSHOT=m
+CONFIG_DM_MIRROR=m
+CONFIG_DM_ZERO=m
+CONFIG_DM_MULTIPATH=m
+CONFIG_DM_MULTIPATH_EMC=m
+# CONFIG_DM_MULTIPATH_RDAC is not set
+# CONFIG_DM_MULTIPATH_HP is not set
+# CONFIG_DM_DELAY is not set
+# CONFIG_DM_UEVENT is not set
+CONFIG_NETDEVICES=y
+# CONFIG_NETDEVICES_MULTIQUEUE is not set
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_MACVLAN is not set
+# CONFIG_EQUALIZER is not set
+CONFIG_TUN=m
+# CONFIG_VETH is not set
+# CONFIG_PHYLIB is not set
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=m
+# CONFIG_AX88796 is not set
+# CONFIG_SMC91X is not set
+# CONFIG_DM9000 is not set
+# CONFIG_SMC911X is not set
+# CONFIG_IBM_NEW_EMAC_ZMII is not set
+# CONFIG_IBM_NEW_EMAC_RGMII is not set
+# CONFIG_IBM_NEW_EMAC_TAH is not set
+# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
+# CONFIG_B44 is not set
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
+
+#
+# Wireless LAN
+#
+# CONFIG_WLAN_PRE80211 is not set
+CONFIG_WLAN_80211=y
+# CONFIG_PCMCIA_RAYCS is not set
+CONFIG_LIBERTAS=m
+CONFIG_LIBERTAS_USB=m
+CONFIG_LIBERTAS_CS=m
+# CONFIG_LIBERTAS_SDIO is not set
+# CONFIG_LIBERTAS_DEBUG is not set
+CONFIG_HERMES=m
+CONFIG_PCMCIA_HERMES=m
+CONFIG_PCMCIA_SPECTRUM=m
+# CONFIG_ATMEL is not set
+CONFIG_AIRO_CS=m
+# CONFIG_PCMCIA_WL3501 is not set
+# CONFIG_USB_ZD1201 is not set
+# CONFIG_USB_NET_RNDIS_WLAN is not set
+# CONFIG_IWLWIFI_LEDS is not set
+CONFIG_HOSTAP=m
+CONFIG_HOSTAP_FIRMWARE=y
+# CONFIG_HOSTAP_FIRMWARE_NVRAM is not set
+CONFIG_HOSTAP_CS=m
+
+#
+# USB Network Adapters
+#
+CONFIG_USB_CATC=m
+CONFIG_USB_KAWETH=m
+CONFIG_USB_PEGASUS=m
+CONFIG_USB_RTL8150=m
+CONFIG_USB_USBNET=m
+CONFIG_USB_NET_AX8817X=m
+CONFIG_USB_NET_CDCETHER=m
+CONFIG_USB_NET_DM9601=m
+# CONFIG_USB_NET_GL620A is not set
+CONFIG_USB_NET_NET1080=m
+# CONFIG_USB_NET_PLUSB is not set
+# CONFIG_USB_NET_MCS7830 is not set
+# CONFIG_USB_NET_RNDIS_HOST is not set
+# CONFIG_USB_NET_CDC_SUBSET is not set
+CONFIG_USB_NET_ZAURUS=m
+CONFIG_NET_PCMCIA=y
+# CONFIG_PCMCIA_3C589 is not set
+# CONFIG_PCMCIA_3C574 is not set
+# CONFIG_PCMCIA_FMVJ18X is not set
+CONFIG_PCMCIA_PCNET=m
+# CONFIG_PCMCIA_NMCLAN is not set
+# CONFIG_PCMCIA_SMC91C92 is not set
+# CONFIG_PCMCIA_XIRC2PS is not set
+# CONFIG_PCMCIA_AXNET is not set
+# CONFIG_WAN is not set
+CONFIG_PPP=m
+# CONFIG_PPP_MULTILINK is not set
+# CONFIG_PPP_FILTER is not set
+CONFIG_PPP_ASYNC=m
+# CONFIG_PPP_SYNC_TTY is not set
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_BSDCOMP=m
+# CONFIG_PPP_MPPE is not set
+# CONFIG_PPPOE is not set
+# CONFIG_PPPOL2TP is not set
+# CONFIG_SLIP is not set
+CONFIG_SLHC=m
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_ISDN is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=m
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=640
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=480
+# CONFIG_INPUT_JOYDEV is not set
+CONFIG_INPUT_EVDEV=y
+# CONFIG_INPUT_EVBUG is not set
+CONFIG_INPUT_APMPOWER=y
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+# CONFIG_KEYBOARD_ATKBD is not set
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+# CONFIG_KEYBOARD_XTKBD is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+# CONFIG_KEYBOARD_CORGI is not set
+CONFIG_KEYBOARD_SPITZ=y
+CONFIG_SHARPSL_RC=y
+# CONFIG_KEYBOARD_PXA27x is not set
+# CONFIG_KEYBOARD_GPIO is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_CORGI=y
+# CONFIG_TOUCHSCREEN_FUJITSU is not set
+# CONFIG_TOUCHSCREEN_GUNZE is not set
+# CONFIG_TOUCHSCREEN_ELO is not set
+# CONFIG_TOUCHSCREEN_MTOUCH is not set
+# CONFIG_TOUCHSCREEN_MK712 is not set
+# CONFIG_TOUCHSCREEN_PENMOUNT is not set
+# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set
+# CONFIG_TOUCHSCREEN_TOUCHWIN is not set
+# CONFIG_TOUCHSCREEN_UCB1400 is not set
+# CONFIG_TOUCHSCREEN_WM97XX is not set
+# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set
+CONFIG_INPUT_MISC=y
+# CONFIG_INPUT_ATI_REMOTE is not set
+# CONFIG_INPUT_ATI_REMOTE2 is not set
+# CONFIG_INPUT_KEYSPAN_REMOTE is not set
+# CONFIG_INPUT_POWERMATE is not set
+# CONFIG_INPUT_YEALINK is not set
+CONFIG_INPUT_UINPUT=m
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+# CONFIG_VT_HW_CONSOLE_BINDING is not set
+CONFIG_DEVKMEM=y
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+CONFIG_SERIAL_8250=m
+CONFIG_SERIAL_8250_CS=m
+CONFIG_SERIAL_8250_NR_UARTS=4
+CONFIG_SERIAL_8250_RUNTIME_UARTS=4
+# CONFIG_SERIAL_8250_EXTENDED is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_PXA=y
+CONFIG_SERIAL_PXA_CONSOLE=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_IPMI_HANDLER is not set
+CONFIG_HW_RANDOM=m
+# CONFIG_NVRAM is not set
+# CONFIG_R3964 is not set
+
+#
+# PCMCIA character devices
+#
+# CONFIG_SYNCLINK_CS is not set
+# CONFIG_CARDMAN_4000 is not set
+# CONFIG_CARDMAN_4040 is not set
+# CONFIG_IPWIRELESS is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+# CONFIG_I2C_CHARDEV is not set
+
+#
+# I2C Hardware Bus support
+#
+# CONFIG_I2C_GPIO is not set
+CONFIG_I2C_PXA=y
+# CONFIG_I2C_PXA_SLAVE is not set
+# CONFIG_I2C_OCORES is not set
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_SIMTEC is not set
+# CONFIG_I2C_TAOS_EVM is not set
+# CONFIG_I2C_STUB is not set
+# CONFIG_I2C_TINY_USB is not set
+# CONFIG_I2C_PCA_PLATFORM is not set
+
+#
+# Miscellaneous I2C Chip support
+#
+# CONFIG_DS1682 is not set
+# CONFIG_SENSORS_EEPROM is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_PCF8575 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_TPS65010 is not set
+# CONFIG_SENSORS_MAX6875 is not set
+# CONFIG_SENSORS_TSL2550 is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_I2C_DEBUG_CHIP is not set
+# CONFIG_SPI is not set
+CONFIG_HAVE_GPIO_LIB=y
+
+#
+# GPIO Support
+#
+
+#
+# I2C GPIO expanders:
+#
+# CONFIG_GPIO_PCA953X is not set
+# CONFIG_GPIO_PCF857X is not set
+
+#
+# SPI GPIO expanders:
+#
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+# CONFIG_WATCHDOG is not set
+
+#
+# Sonics Silicon Backplane
+#
+CONFIG_SSB_POSSIBLE=y
+# CONFIG_SSB is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_SM501 is not set
+# CONFIG_MFD_ASIC3 is not set
+# CONFIG_HTC_EGPIO is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_HTC_ASIC3 is not set
+# CONFIG_HTC_ASIC3_DS1WM is not set
+
+#
+# Multimedia devices
+#
+
+#
+# Multimedia core support
+#
+CONFIG_VIDEO_DEV=m
+CONFIG_VIDEO_V4L2_COMMON=m
+CONFIG_VIDEO_ALLOW_V4L1=y
+CONFIG_VIDEO_V4L1_COMPAT=y
+# CONFIG_DVB_CORE is not set
+CONFIG_VIDEO_MEDIA=m
+
+#
+# Multimedia drivers
+#
+# CONFIG_MEDIA_ATTACH is not set
+CONFIG_MEDIA_TUNER=m
+# CONFIG_MEDIA_TUNER_CUSTOMIZE is not set
+CONFIG_MEDIA_TUNER_SIMPLE=m
+CONFIG_MEDIA_TUNER_TDA8290=m
+CONFIG_MEDIA_TUNER_TDA9887=m
+CONFIG_MEDIA_TUNER_TEA5761=m
+CONFIG_MEDIA_TUNER_TEA5767=m
+CONFIG_MEDIA_TUNER_MT20XX=m
+CONFIG_MEDIA_TUNER_XC2028=m
+CONFIG_MEDIA_TUNER_XC5000=m
+CONFIG_VIDEO_V4L2=m
+CONFIG_VIDEO_V4L1=m
+CONFIG_VIDEO_CAPTURE_DRIVERS=y
+# CONFIG_VIDEO_ADV_DEBUG is not set
+CONFIG_VIDEO_HELPER_CHIPS_AUTO=y
+# CONFIG_VIDEO_VIVI is not set
+# CONFIG_VIDEO_CPIA is not set
+# CONFIG_VIDEO_CPIA2 is not set
+# CONFIG_VIDEO_SAA5246A is not set
+# CONFIG_VIDEO_SAA5249 is not set
+# CONFIG_TUNER_3036 is not set
+CONFIG_V4L_USB_DRIVERS=y
+# CONFIG_USB_VIDEO_CLASS is not set
+# CONFIG_VIDEO_PVRUSB2 is not set
+# CONFIG_VIDEO_EM28XX is not set
+# CONFIG_VIDEO_USBVISION is not set
+CONFIG_VIDEO_USBVIDEO=m
+CONFIG_USB_VICAM=m
+CONFIG_USB_IBMCAM=m
+CONFIG_USB_KONICAWC=m
+# CONFIG_USB_QUICKCAM_MESSENGER is not set
+# CONFIG_USB_ET61X251 is not set
+# CONFIG_VIDEO_OVCAMCHIP is not set
+# CONFIG_USB_W9968CF is not set
+CONFIG_USB_OV511=m
+CONFIG_USB_SE401=m
+CONFIG_USB_SN9C102=m
+CONFIG_USB_STV680=m
+# CONFIG_USB_ZC0301 is not set
+# CONFIG_USB_PWC is not set
+# CONFIG_USB_ZR364XX is not set
+# CONFIG_USB_STKWEBCAM is not set
+# CONFIG_SOC_CAMERA is not set
+# CONFIG_VIDEO_PXA27x is not set
+CONFIG_RADIO_ADAPTERS=y
+CONFIG_USB_DSBR=m
+# CONFIG_USB_SI470X is not set
+CONFIG_DAB=y
+CONFIG_USB_DABUSB=m
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+CONFIG_VIDEO_OUTPUT_CONTROL=m
+CONFIG_FB=y
+# CONFIG_FIRMWARE_EDID is not set
+# CONFIG_FB_DDC is not set
+CONFIG_FB_CFB_FILLRECT=y
+CONFIG_FB_CFB_COPYAREA=y
+CONFIG_FB_CFB_IMAGEBLIT=y
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+# CONFIG_FB_SYS_FILLRECT is not set
+# CONFIG_FB_SYS_COPYAREA is not set
+# CONFIG_FB_SYS_IMAGEBLIT is not set
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+# CONFIG_FB_SYS_FOPS is not set
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_BACKLIGHT is not set
+# CONFIG_FB_MODE_HELPERS is not set
+# CONFIG_FB_TILEBLITTING is not set
+
+#
+# Frame buffer hardware drivers
+#
+# CONFIG_FB_S1D13XXX is not set
+CONFIG_FB_PXA=y
+# CONFIG_FB_PXA_SMARTPANEL is not set
+# CONFIG_FB_PXA_PARAMETERS is not set
+# CONFIG_FB_MBX is not set
+# CONFIG_FB_W100 is not set
+# CONFIG_FB_AM200EPD is not set
+# CONFIG_FB_VIRTUAL is not set
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+# CONFIG_LCD_CLASS_DEVICE is not set
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+CONFIG_BACKLIGHT_CORGI=y
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
+CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y
+CONFIG_FONTS=y
+# CONFIG_FONT_8x8 is not set
+CONFIG_FONT_8x16=y
+# CONFIG_FONT_6x11 is not set
+# CONFIG_FONT_7x14 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+# CONFIG_FONT_ACORN_8x8 is not set
+# CONFIG_FONT_MINI_4x6 is not set
+# CONFIG_FONT_SUN8x16 is not set
+# CONFIG_FONT_SUN12x22 is not set
+# CONFIG_FONT_10x18 is not set
+# CONFIG_LOGO is not set
+
+#
+# Sound
+#
+CONFIG_SOUND=m
+
+#
+# Advanced Linux Sound Architecture
+#
+CONFIG_SND=m
+CONFIG_SND_TIMER=m
+CONFIG_SND_PCM=m
+CONFIG_SND_HWDEP=m
+CONFIG_SND_RAWMIDI=m
+CONFIG_SND_SEQUENCER=m
+# CONFIG_SND_SEQ_DUMMY is not set
+CONFIG_SND_OSSEMUL=y
+CONFIG_SND_MIXER_OSS=m
+CONFIG_SND_PCM_OSS=m
+CONFIG_SND_PCM_OSS_PLUGINS=y
+# CONFIG_SND_SEQUENCER_OSS is not set
+# CONFIG_SND_DYNAMIC_MINORS is not set
+CONFIG_SND_SUPPORT_OLD_API=y
+CONFIG_SND_VERBOSE_PROCFS=y
+# CONFIG_SND_VERBOSE_PRINTK is not set
+# CONFIG_SND_DEBUG is not set
+
+#
+# Generic devices
+#
+CONFIG_SND_AC97_CODEC=m
+# CONFIG_SND_DUMMY is not set
+# CONFIG_SND_VIRMIDI is not set
+# CONFIG_SND_MTPAV is not set
+# CONFIG_SND_SERIAL_U16550 is not set
+# CONFIG_SND_MPU401 is not set
+
+#
+# ALSA ARM devices
+#
+CONFIG_SND_PXA2XX_PCM=m
+CONFIG_SND_PXA2XX_AC97=m
+
+#
+# USB devices
+#
+CONFIG_SND_USB_AUDIO=m
+# CONFIG_SND_USB_CAIAQ is not set
+
+#
+# PCMCIA devices
+#
+# CONFIG_SND_VXPOCKET is not set
+# CONFIG_SND_PDAUDIOCF is not set
+
+#
+# System on Chip audio support
+#
+CONFIG_SND_SOC=m
+CONFIG_SND_PXA2XX_SOC=m
+CONFIG_SND_PXA2XX_SOC_I2S=m
+CONFIG_SND_PXA2XX_SOC_SPITZ=m
+
+#
+# ALSA SoC audio for Freescale SOCs
+#
+
+#
+# SoC Audio for the Texas Instruments OMAP
+#
+CONFIG_SND_SOC_WM8750=m
+
+#
+# Open Sound System
+#
+# CONFIG_SOUND_PRIME is not set
+CONFIG_AC97_BUS=m
+CONFIG_HID_SUPPORT=y
+CONFIG_HID=m
+# CONFIG_HID_DEBUG is not set
+# CONFIG_HIDRAW is not set
+
+#
+# USB Input Devices
+#
+CONFIG_USB_HID=m
+# CONFIG_USB_HIDINPUT_POWERBOOK is not set
+# CONFIG_HID_FF is not set
+# CONFIG_USB_HIDDEV is not set
+
+#
+# USB HID Boot Protocol drivers
+#
+CONFIG_USB_KBD=m
+CONFIG_USB_MOUSE=m
+CONFIG_USB_SUPPORT=y
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB_ARCH_HAS_OHCI=y
+# CONFIG_USB_ARCH_HAS_EHCI is not set
+CONFIG_USB=m
+# CONFIG_USB_DEBUG is not set
+# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set
+
+#
+# Miscellaneous USB options
+#
+CONFIG_USB_DEVICEFS=y
+CONFIG_USB_DEVICE_CLASS=y
+# CONFIG_USB_DYNAMIC_MINORS is not set
+# CONFIG_USB_SUSPEND is not set
+# CONFIG_USB_OTG is not set
+# CONFIG_USB_OTG_WHITELIST is not set
+# CONFIG_USB_OTG_BLACKLIST_HUB is not set
+
+#
+# USB Host Controller Drivers
+#
+# CONFIG_USB_C67X00_HCD is not set
+# CONFIG_USB_ISP116X_HCD is not set
+# CONFIG_USB_ISP1760_HCD is not set
+CONFIG_USB_OHCI_HCD=m
+# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set
+# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set
+CONFIG_USB_OHCI_LITTLE_ENDIAN=y
+CONFIG_USB_SL811_HCD=m
+CONFIG_USB_SL811_CS=m
+# CONFIG_USB_R8A66597_HCD is not set
+
+#
+# USB Device Class drivers
+#
+CONFIG_USB_ACM=m
+CONFIG_USB_PRINTER=m
+# CONFIG_USB_WDM is not set
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
+#
+
+#
+# may also be needed; see USB_STORAGE Help for more information
+#
+CONFIG_USB_STORAGE=m
+# CONFIG_USB_STORAGE_DEBUG is not set
+# CONFIG_USB_STORAGE_DATAFAB is not set
+# CONFIG_USB_STORAGE_FREECOM is not set
+# CONFIG_USB_STORAGE_ISD200 is not set
+# CONFIG_USB_STORAGE_DPCM is not set
+# CONFIG_USB_STORAGE_USBAT is not set
+# CONFIG_USB_STORAGE_SDDR09 is not set
+# CONFIG_USB_STORAGE_SDDR55 is not set
+# CONFIG_USB_STORAGE_JUMPSHOT is not set
+# CONFIG_USB_STORAGE_ALAUDA is not set
+# CONFIG_USB_STORAGE_ONETOUCH is not set
+# CONFIG_USB_STORAGE_KARMA is not set
+# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set
+# CONFIG_USB_LIBUSUAL is not set
+
+#
+# USB Imaging devices
+#
+CONFIG_USB_MDC800=m
+CONFIG_USB_MICROTEK=m
+CONFIG_USB_MON=y
+
+#
+# USB port drivers
+#
+CONFIG_USB_SERIAL=m
+CONFIG_USB_EZUSB=y
+CONFIG_USB_SERIAL_GENERIC=y
+# CONFIG_USB_SERIAL_AIRCABLE is not set
+# CONFIG_USB_SERIAL_AIRPRIME is not set
+# CONFIG_USB_SERIAL_ARK3116 is not set
+CONFIG_USB_SERIAL_BELKIN=m
+# CONFIG_USB_SERIAL_CH341 is not set
+# CONFIG_USB_SERIAL_WHITEHEAT is not set
+CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m
+CONFIG_USB_SERIAL_CP2101=m
+CONFIG_USB_SERIAL_CYPRESS_M8=m
+CONFIG_USB_SERIAL_EMPEG=m
+CONFIG_USB_SERIAL_FTDI_SIO=m
+# CONFIG_USB_SERIAL_FUNSOFT is not set
+CONFIG_USB_SERIAL_VISOR=m
+CONFIG_USB_SERIAL_IPAQ=m
+CONFIG_USB_SERIAL_IR=m
+CONFIG_USB_SERIAL_EDGEPORT=m
+CONFIG_USB_SERIAL_EDGEPORT_TI=m
+CONFIG_USB_SERIAL_GARMIN=m
+CONFIG_USB_SERIAL_IPW=m
+# CONFIG_USB_SERIAL_IUU is not set
+CONFIG_USB_SERIAL_KEYSPAN_PDA=m
+CONFIG_USB_SERIAL_KEYSPAN=m
+# CONFIG_USB_SERIAL_KEYSPAN_MPR is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19QW is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19QI is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA49WLC is not set
+CONFIG_USB_SERIAL_KLSI=m
+CONFIG_USB_SERIAL_KOBIL_SCT=m
+CONFIG_USB_SERIAL_MCT_U232=m
+# CONFIG_USB_SERIAL_MOS7720 is not set
+# CONFIG_USB_SERIAL_MOS7840 is not set
+# CONFIG_USB_SERIAL_MOTOROLA is not set
+# CONFIG_USB_SERIAL_NAVMAN is not set
+CONFIG_USB_SERIAL_PL2303=m
+# CONFIG_USB_SERIAL_OTI6858 is not set
+# CONFIG_USB_SERIAL_SPCP8X5 is not set
+# CONFIG_USB_SERIAL_HP4X is not set
+CONFIG_USB_SERIAL_SAFE=m
+# CONFIG_USB_SERIAL_SAFE_PADDED is not set
+# CONFIG_USB_SERIAL_SIERRAWIRELESS is not set
+CONFIG_USB_SERIAL_TI=m
+CONFIG_USB_SERIAL_CYBERJACK=m
+CONFIG_USB_SERIAL_XIRCOM=m
+# CONFIG_USB_SERIAL_OPTION is not set
+CONFIG_USB_SERIAL_OMNINET=m
+# CONFIG_USB_SERIAL_DEBUG is not set
+
+#
+# USB Miscellaneous drivers
+#
+CONFIG_USB_EMI62=m
+CONFIG_USB_EMI26=m
+# CONFIG_USB_ADUTUX is not set
+CONFIG_USB_AUERSWALD=m
+CONFIG_USB_RIO500=m
+CONFIG_USB_LEGOTOWER=m
+CONFIG_USB_LCD=m
+# CONFIG_USB_BERRY_CHARGE is not set
+CONFIG_USB_LED=m
+# CONFIG_USB_CYPRESS_CY7C63 is not set
+CONFIG_USB_CYTHERM=m
+# CONFIG_USB_PHIDGET is not set
+CONFIG_USB_IDMOUSE=m
+# CONFIG_USB_FTDI_ELAN is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+# CONFIG_USB_LD is not set
+# CONFIG_USB_TRANCEVIBRATOR is not set
+# CONFIG_USB_IOWARRIOR is not set
+# CONFIG_USB_TEST is not set
+# CONFIG_USB_ISIGHTFW is not set
+CONFIG_USB_GADGET=m
+# CONFIG_USB_GADGET_DEBUG_FILES is not set
+CONFIG_USB_GADGET_SELECTED=y
+# CONFIG_USB_GADGET_AMD5536UDC is not set
+# CONFIG_USB_GADGET_ATMEL_USBA is not set
+# CONFIG_USB_GADGET_FSL_USB2 is not set
+# CONFIG_USB_GADGET_NET2280 is not set
+# CONFIG_USB_GADGET_PXA2XX is not set
+# CONFIG_USB_GADGET_M66592 is not set
+CONFIG_USB_GADGET_PXA27X=y
+CONFIG_USB_PXA27X=m
+# CONFIG_USB_GADGET_GOKU is not set
+# CONFIG_USB_GADGET_LH7A40X is not set
+# CONFIG_USB_GADGET_OMAP is not set
+# CONFIG_USB_GADGET_S3C2410 is not set
+# CONFIG_USB_GADGET_AT91 is not set
+# CONFIG_USB_GADGET_DUMMY_HCD is not set
+# CONFIG_USB_GADGET_DUALSPEED is not set
+CONFIG_USB_ZERO=m
+CONFIG_USB_ETH=m
+CONFIG_USB_ETH_RNDIS=y
+CONFIG_USB_GADGETFS=m
+CONFIG_USB_FILE_STORAGE=m
+# CONFIG_USB_FILE_STORAGE_TEST is not set
+CONFIG_USB_G_SERIAL=m
+# CONFIG_USB_MIDI_GADGET is not set
+# CONFIG_USB_G_PRINTER is not set
+CONFIG_MMC=y
+# CONFIG_MMC_DEBUG is not set
+CONFIG_MMC_UNSAFE_RESUME=y
+
+#
+# MMC/SD Card Drivers
+#
+CONFIG_MMC_BLOCK=y
+CONFIG_MMC_BLOCK_BOUNCE=y
+# CONFIG_SDIO_UART is not set
+# CONFIG_MMC_TEST is not set
+
+#
+# MMC/SD Host Controller Drivers
+#
+CONFIG_MMC_PXA=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+
+#
+# LED drivers
+#
+CONFIG_LEDS_SPITZ=y
+# CONFIG_LEDS_GPIO is not set
+
+#
+# LED Triggers
+#
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=y
+CONFIG_LEDS_TRIGGER_IDE_DISK=y
+# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set
+# CONFIG_LEDS_TRIGGER_DEFAULT_ON is not set
+CONFIG_RTC_LIB=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_HCTOSYS=y
+CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
+# CONFIG_RTC_DEBUG is not set
+
+#
+# RTC interfaces
+#
+CONFIG_RTC_INTF_SYSFS=y
+CONFIG_RTC_INTF_PROC=y
+CONFIG_RTC_INTF_DEV=y
+# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
+# CONFIG_RTC_DRV_TEST is not set
+
+#
+# I2C RTC drivers
+#
+# CONFIG_RTC_DRV_DS1307 is not set
+# CONFIG_RTC_DRV_DS1374 is not set
+# CONFIG_RTC_DRV_DS1672 is not set
+# CONFIG_RTC_DRV_MAX6900 is not set
+# CONFIG_RTC_DRV_RS5C372 is not set
+# CONFIG_RTC_DRV_ISL1208 is not set
+# CONFIG_RTC_DRV_X1205 is not set
+# CONFIG_RTC_DRV_PCF8563 is not set
+# CONFIG_RTC_DRV_PCF8583 is not set
+# CONFIG_RTC_DRV_M41T80 is not set
+# CONFIG_RTC_DRV_S35390A is not set
+# CONFIG_RTC_DRV_FM3130 is not set
+
+#
+# SPI RTC drivers
+#
+
+#
+# Platform RTC drivers
+#
+# CONFIG_RTC_DRV_CMOS is not set
+# CONFIG_RTC_DRV_DS1511 is not set
+# CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_DS1742 is not set
+# CONFIG_RTC_DRV_STK17TA8 is not set
+# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_M48T59 is not set
+# CONFIG_RTC_DRV_V3020 is not set
+
+#
+# on-CPU RTC drivers
+#
+CONFIG_RTC_DRV_SA1100=y
+# CONFIG_UIO is not set
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+# CONFIG_EXT2_FS_XIP is not set
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_FS_XATTR is not set
+# CONFIG_EXT4DEV_FS is not set
+CONFIG_JBD=y
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+CONFIG_FS_POSIX_ACL=y
+# CONFIG_XFS_FS is not set
+# CONFIG_OCFS2_FS is not set
+CONFIG_DNOTIFY=y
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_QUOTA is not set
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+# CONFIG_MSDOS_FS is not set
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_DEBUG=0
+CONFIG_JFFS2_FS_WRITEBUFFER=y
+# CONFIG_JFFS2_FS_WBUF_VERIFY is not set
+CONFIG_JFFS2_SUMMARY=y
+# CONFIG_JFFS2_FS_XATTR is not set
+# CONFIG_JFFS2_SYSFS is not set
+CONFIG_JFFS2_COMPRESSION_OPTIONS=y
+CONFIG_JFFS2_ZLIB=y
+CONFIG_JFFS2_LZO=y
+CONFIG_JFFS2_RTIME=y
+CONFIG_JFFS2_RUBIN=y
+# CONFIG_JFFS2_CMODE_NONE is not set
+CONFIG_JFFS2_CMODE_PRIORITY=y
+# CONFIG_JFFS2_CMODE_SIZE is not set
+# CONFIG_JFFS2_CMODE_FAVOURLZO is not set
+CONFIG_CRAMFS=m
+# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+CONFIG_NFS_FS=m
+CONFIG_NFS_V3=y
+# CONFIG_NFS_V3_ACL is not set
+CONFIG_NFS_V4=y
+CONFIG_NFSD=m
+CONFIG_NFSD_V3=y
+# CONFIG_NFSD_V3_ACL is not set
+CONFIG_NFSD_V4=y
+CONFIG_LOCKD=m
+CONFIG_LOCKD_V4=y
+CONFIG_EXPORTFS=m
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=m
+CONFIG_SUNRPC_GSS=m
+# CONFIG_SUNRPC_BIND34 is not set
+CONFIG_RPCSEC_GSS_KRB5=m
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+CONFIG_SMB_FS=m
+CONFIG_SMB_NLS_DEFAULT=y
+CONFIG_SMB_NLS_REMOTE="cp437"
+CONFIG_CIFS=m
+# CONFIG_CIFS_STATS is not set
+# CONFIG_CIFS_WEAK_PW_HASH is not set
+# CONFIG_CIFS_XATTR is not set
+# CONFIG_CIFS_DEBUG2 is not set
+# CONFIG_CIFS_EXPERIMENTAL is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_MINIX_SUBPARTITION is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_UNIXWARE_DISKLABEL is not set
+# CONFIG_LDM_PARTITION is not set
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_ULTRIX_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_KARMA_PARTITION is not set
+# CONFIG_EFI_PARTITION is not set
+# CONFIG_SYSV68_PARTITION is not set
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="cp437"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_737=m
+CONFIG_NLS_CODEPAGE_775=m
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
+CONFIG_NLS_CODEPAGE_855=m
+CONFIG_NLS_CODEPAGE_857=m
+CONFIG_NLS_CODEPAGE_860=m
+CONFIG_NLS_CODEPAGE_861=m
+CONFIG_NLS_CODEPAGE_862=m
+CONFIG_NLS_CODEPAGE_863=m
+CONFIG_NLS_CODEPAGE_864=m
+CONFIG_NLS_CODEPAGE_865=m
+CONFIG_NLS_CODEPAGE_866=m
+CONFIG_NLS_CODEPAGE_869=m
+CONFIG_NLS_CODEPAGE_936=m
+CONFIG_NLS_CODEPAGE_950=m
+CONFIG_NLS_CODEPAGE_932=m
+CONFIG_NLS_CODEPAGE_949=m
+CONFIG_NLS_CODEPAGE_874=m
+CONFIG_NLS_ISO8859_8=m
+CONFIG_NLS_CODEPAGE_1250=m
+CONFIG_NLS_CODEPAGE_1251=m
+CONFIG_NLS_ASCII=m
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_ISO8859_2=m
+CONFIG_NLS_ISO8859_3=m
+CONFIG_NLS_ISO8859_4=m
+CONFIG_NLS_ISO8859_5=m
+CONFIG_NLS_ISO8859_6=m
+CONFIG_NLS_ISO8859_7=m
+CONFIG_NLS_ISO8859_9=m
+CONFIG_NLS_ISO8859_13=m
+CONFIG_NLS_ISO8859_14=m
+CONFIG_NLS_ISO8859_15=m
+CONFIG_NLS_KOI8_R=m
+CONFIG_NLS_KOI8_U=m
+CONFIG_NLS_UTF8=y
+# CONFIG_DLM is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_PRINTK_TIME is not set
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_FRAME_WARN=1024
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_DEBUG_FS is not set
+# CONFIG_HEADERS_CHECK is not set
+# CONFIG_DEBUG_KERNEL is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+CONFIG_FRAME_POINTER=y
+# CONFIG_SAMPLES is not set
+# CONFIG_DEBUG_USER is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+CONFIG_CRYPTO=y
+
+#
+# Crypto core or helper
+#
+CONFIG_CRYPTO_ALGAPI=m
+CONFIG_CRYPTO_AEAD=m
+CONFIG_CRYPTO_BLKCIPHER=m
+CONFIG_CRYPTO_HASH=m
+CONFIG_CRYPTO_MANAGER=m
+# CONFIG_CRYPTO_GF128MUL is not set
+CONFIG_CRYPTO_NULL=m
+# CONFIG_CRYPTO_CRYPTD is not set
+CONFIG_CRYPTO_AUTHENC=m
+CONFIG_CRYPTO_TEST=m
+
+#
+# Authenticated Encryption with Associated Data
+#
+# CONFIG_CRYPTO_CCM is not set
+# CONFIG_CRYPTO_GCM is not set
+# CONFIG_CRYPTO_SEQIV is not set
+
+#
+# Block modes
+#
+CONFIG_CRYPTO_CBC=m
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+CONFIG_CRYPTO_ECB=m
+# CONFIG_CRYPTO_LRW is not set
+CONFIG_CRYPTO_PCBC=m
+# CONFIG_CRYPTO_XTS is not set
+
+#
+# Hash modes
+#
+CONFIG_CRYPTO_HMAC=m
+# CONFIG_CRYPTO_XCBC is not set
+
+#
+# Digest
+#
+CONFIG_CRYPTO_CRC32C=m
+CONFIG_CRYPTO_MD4=m
+CONFIG_CRYPTO_MD5=m
+CONFIG_CRYPTO_MICHAEL_MIC=m
+CONFIG_CRYPTO_SHA1=m
+CONFIG_CRYPTO_SHA256=m
+CONFIG_CRYPTO_SHA512=m
+# CONFIG_CRYPTO_TGR192 is not set
+CONFIG_CRYPTO_WP512=m
+
+#
+# Ciphers
+#
+CONFIG_CRYPTO_AES=m
+CONFIG_CRYPTO_ANUBIS=m
+CONFIG_CRYPTO_ARC4=m
+CONFIG_CRYPTO_BLOWFISH=m
+CONFIG_CRYPTO_CAMELLIA=m
+CONFIG_CRYPTO_CAST5=m
+CONFIG_CRYPTO_CAST6=m
+CONFIG_CRYPTO_DES=m
+# CONFIG_CRYPTO_FCRYPT is not set
+CONFIG_CRYPTO_KHAZAD=m
+# CONFIG_CRYPTO_SALSA20 is not set
+# CONFIG_CRYPTO_SEED is not set
+CONFIG_CRYPTO_SERPENT=m
+CONFIG_CRYPTO_TEA=m
+CONFIG_CRYPTO_TWOFISH=m
+CONFIG_CRYPTO_TWOFISH_COMMON=m
+
+#
+# Compression
+#
+CONFIG_CRYPTO_DEFLATE=m
+CONFIG_CRYPTO_LZO=m
+# CONFIG_CRYPTO_HW is not set
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+# CONFIG_GENERIC_FIND_FIRST_BIT is not set
+# CONFIG_GENERIC_FIND_NEXT_BIT is not set
+CONFIG_CRC_CCITT=y
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
+CONFIG_LIBCRC32C=m
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_LZO_COMPRESS=y
+CONFIG_LZO_DECOMPRESS=y
+CONFIG_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
diff --git a/recipes/linux/linux-rp-2.6.26/defconfig-zylonite b/recipes/linux/linux-rp-2.6.26/defconfig-zylonite
new file mode 100644
index 0000000000..d34c79e062
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.26/defconfig-zylonite
@@ -0,0 +1,1740 @@
+#
+# Automatically generated make config: don't edit
+# Linux kernel version: 2.6.24-rc8
+# Sun Jan 20 18:42:35 2008
+#
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+CONFIG_GENERIC_GPIO=y
+CONFIG_GENERIC_TIME=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_MMU=y
+# CONFIG_NO_IOPORT is not set
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+# CONFIG_ARCH_HAS_ILOG2_U32 is not set
+# CONFIG_ARCH_HAS_ILOG2_U64 is not set
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_ZONE_DMA=y
+CONFIG_ARCH_MTD_XIP=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+
+#
+# General setup
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_LOCK_KERNEL=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+# CONFIG_POSIX_MQUEUE is not set
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_BSD_PROCESS_ACCT_V3=y
+# CONFIG_TASKSTATS is not set
+# CONFIG_USER_NS is not set
+# CONFIG_PID_NS is not set
+# CONFIG_AUDIT is not set
+# CONFIG_IKCONFIG is not set
+CONFIG_LOG_BUF_SHIFT=14
+# CONFIG_CGROUPS is not set
+CONFIG_FAIR_GROUP_SCHED=y
+CONFIG_FAIR_USER_SCHED=y
+# CONFIG_FAIR_CGROUP_SCHED is not set
+CONFIG_SYSFS_DEPRECATED=y
+# CONFIG_RELAY is not set
+# CONFIG_BLK_DEV_INITRD is not set
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+CONFIG_EMBEDDED=y
+CONFIG_UID16=y
+CONFIG_SYSCTL_SYSCALL=y
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+# CONFIG_KALLSYMS_EXTRA_PASS is not set
+CONFIG_HOTPLUG=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_ANON_INODES=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLAB=y
+# CONFIG_SLUB is not set
+# CONFIG_SLOB is not set
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+# CONFIG_TINY_SHMEM is not set
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+# CONFIG_MODVERSIONS is not set
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+CONFIG_KMOD=y
+CONFIG_BLOCK=y
+# CONFIG_LBD is not set
+# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_LSF is not set
+# CONFIG_BLK_DEV_BSG is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=m
+CONFIG_IOSCHED_CFQ=m
+CONFIG_DEFAULT_AS=y
+# CONFIG_DEFAULT_DEADLINE is not set
+# CONFIG_DEFAULT_CFQ is not set
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="anticipatory"
+
+#
+# System Type
+#
+# CONFIG_ARCH_AAEC2000 is not set
+# CONFIG_ARCH_INTEGRATOR is not set
+# CONFIG_ARCH_REALVIEW is not set
+# CONFIG_ARCH_VERSATILE is not set
+# CONFIG_ARCH_AT91 is not set
+# CONFIG_ARCH_CLPS7500 is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_CO285 is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE is not set
+# CONFIG_ARCH_NETX is not set
+# CONFIG_ARCH_H720X is not set
+# CONFIG_ARCH_IMX is not set
+# CONFIG_ARCH_IOP13XX is not set
+# CONFIG_ARCH_IOP32X is not set
+# CONFIG_ARCH_IOP33X is not set
+# CONFIG_ARCH_IXP23XX is not set
+# CONFIG_ARCH_IXP2000 is not set
+# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_L7200 is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_NS9XXX is not set
+# CONFIG_ARCH_MXC is not set
+# CONFIG_ARCH_PNX4008 is not set
+CONFIG_ARCH_PXA=y
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C2410 is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_LH7A40X is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP is not set
+
+#
+# Intel PXA2xx/PXA3xx Implementations
+#
+
+#
+# Supported PXA3xx Processor Variants
+#
+CONFIG_CPU_PXA300=y
+CONFIG_CPU_PXA310=y
+CONFIG_CPU_PXA320=y
+# CONFIG_ARCH_LUBBOCK is not set
+# CONFIG_MACH_LOGICPD_PXA270 is not set
+# CONFIG_MACH_MAINSTONE is not set
+# CONFIG_ARCH_PXA_IDP is not set
+# CONFIG_PXA_SHARPSL is not set
+# CONFIG_MACH_TRIZEPS4 is not set
+# CONFIG_MACH_HX2750 is not set
+# CONFIG_MACH_EM_X270 is not set
+CONFIG_MACH_ZYLONITE=y
+# CONFIG_MACH_ARMCORE is not set
+CONFIG_PXA3xx=y
+
+#
+# Boot options
+#
+
+#
+# Power management
+#
+
+#
+# Processor Type
+#
+CONFIG_CPU_32=y
+CONFIG_CPU_XSC3=y
+CONFIG_CPU_32v5=y
+CONFIG_CPU_ABRT_EV5T=y
+CONFIG_CPU_CACHE_VIVT=y
+CONFIG_CPU_TLB_V4WBI=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+CONFIG_IO_36=y
+
+#
+# Processor Features
+#
+CONFIG_ARM_THUMB=y
+# CONFIG_CPU_DCACHE_DISABLE is not set
+# CONFIG_CPU_BPREDICT_DISABLE is not set
+# CONFIG_OUTER_CACHE is not set
+CONFIG_IWMMXT=y
+
+#
+# Bus support
+#
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_ARCH_SUPPORTS_MSI is not set
+CONFIG_PCCARD=m
+# CONFIG_PCMCIA_DEBUG is not set
+CONFIG_PCMCIA=m
+CONFIG_PCMCIA_LOAD_CIS=y
+CONFIG_PCMCIA_IOCTL=y
+
+#
+# PC-card bridges
+#
+CONFIG_PCMCIA_PXA2XX=m
+
+#
+# Kernel Features
+#
+# CONFIG_TICK_ONESHOT is not set
+# CONFIG_NO_HZ is not set
+# CONFIG_HIGH_RES_TIMERS is not set
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_PREEMPT=y
+CONFIG_HZ=100
+CONFIG_AEABI=y
+CONFIG_OABI_COMPAT=y
+# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_FLATMEM_MANUAL=y
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+# CONFIG_SPARSEMEM_MANUAL is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+# CONFIG_SPARSEMEM_STATIC is not set
+# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set
+CONFIG_SPLIT_PTLOCK_CPUS=4096
+# CONFIG_RESOURCES_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=1
+CONFIG_BOUNCE=y
+CONFIG_VIRT_TO_BUS=y
+CONFIG_ALIGNMENT_TRAP=y
+
+#
+# Boot options
+#
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="console=ttyS0,38400 root=/dev/mtdblock2 rootfstype=jffs2 mem=64M dyntick=enable debug"
+# CONFIG_XIP_KERNEL is not set
+CONFIG_KEXEC=y
+CONFIG_ATAGS_PROC=y
+
+#
+# CPU Frequency scaling
+#
+# CONFIG_CPU_FREQ is not set
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+CONFIG_FPE_NWFPE=y
+# CONFIG_FPE_NWFPE_XP is not set
+# CONFIG_FPE_FASTFPE is not set
+
+#
+# Userspace binary formats
+#
+CONFIG_BINFMT_ELF=y
+CONFIG_BINFMT_AOUT=m
+CONFIG_BINFMT_MISC=m
+
+#
+# Power management options
+#
+CONFIG_PM=y
+# CONFIG_PM_LEGACY is not set
+# CONFIG_PM_DEBUG is not set
+CONFIG_PM_SLEEP=y
+CONFIG_SUSPEND_UP_POSSIBLE=y
+CONFIG_SUSPEND=y
+CONFIG_APM_EMULATION=y
+
+#
+# Networking
+#
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=m
+CONFIG_PACKET_MMAP=y
+CONFIG_UNIX=y
+CONFIG_XFRM=y
+CONFIG_XFRM_USER=m
+# CONFIG_XFRM_SUB_POLICY is not set
+# CONFIG_XFRM_MIGRATE is not set
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+# CONFIG_IP_MULTICAST is not set
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_FIB_HASH=y
+CONFIG_IP_PNP=y
+# CONFIG_IP_PNP_DHCP is not set
+# CONFIG_IP_PNP_BOOTP is not set
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_ARPD is not set
+CONFIG_SYN_COOKIES=y
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+CONFIG_INET_TUNNEL=m
+CONFIG_INET_XFRM_MODE_TRANSPORT=m
+CONFIG_INET_XFRM_MODE_TUNNEL=m
+CONFIG_INET_XFRM_MODE_BEET=m
+# CONFIG_INET_LRO is not set
+CONFIG_INET_DIAG=m
+CONFIG_INET_TCP_DIAG=m
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+# CONFIG_IP_VS is not set
+CONFIG_IPV6=m
+# CONFIG_IPV6_PRIVACY is not set
+# CONFIG_IPV6_ROUTER_PREF is not set
+# CONFIG_IPV6_OPTIMISTIC_DAD is not set
+CONFIG_INET6_AH=m
+CONFIG_INET6_ESP=m
+CONFIG_INET6_IPCOMP=m
+# CONFIG_IPV6_MIP6 is not set
+CONFIG_INET6_XFRM_TUNNEL=m
+CONFIG_INET6_TUNNEL=m
+CONFIG_INET6_XFRM_MODE_TRANSPORT=m
+CONFIG_INET6_XFRM_MODE_TUNNEL=m
+CONFIG_INET6_XFRM_MODE_BEET=m
+# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set
+CONFIG_IPV6_SIT=m
+CONFIG_IPV6_TUNNEL=m
+# CONFIG_IPV6_MULTIPLE_TABLES is not set
+# CONFIG_NETWORK_SECMARK is not set
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+
+#
+# Core Netfilter Configuration
+#
+# CONFIG_NETFILTER_NETLINK is not set
+# CONFIG_NF_CONNTRACK_ENABLED is not set
+# CONFIG_NF_CONNTRACK is not set
+CONFIG_NETFILTER_XTABLES=m
+# CONFIG_NETFILTER_XT_TARGET_CLASSIFY is not set
+# CONFIG_NETFILTER_XT_TARGET_DSCP is not set
+# CONFIG_NETFILTER_XT_TARGET_MARK is not set
+# CONFIG_NETFILTER_XT_TARGET_NFQUEUE is not set
+# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set
+# CONFIG_NETFILTER_XT_TARGET_TRACE is not set
+# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set
+# CONFIG_NETFILTER_XT_MATCH_COMMENT is not set
+# CONFIG_NETFILTER_XT_MATCH_DCCP is not set
+# CONFIG_NETFILTER_XT_MATCH_DSCP is not set
+# CONFIG_NETFILTER_XT_MATCH_ESP is not set
+# CONFIG_NETFILTER_XT_MATCH_LENGTH is not set
+# CONFIG_NETFILTER_XT_MATCH_LIMIT is not set
+# CONFIG_NETFILTER_XT_MATCH_MAC is not set
+# CONFIG_NETFILTER_XT_MATCH_MARK is not set
+# CONFIG_NETFILTER_XT_MATCH_POLICY is not set
+# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set
+# CONFIG_NETFILTER_XT_MATCH_PKTTYPE is not set
+# CONFIG_NETFILTER_XT_MATCH_QUOTA is not set
+# CONFIG_NETFILTER_XT_MATCH_REALM is not set
+# CONFIG_NETFILTER_XT_MATCH_SCTP is not set
+# CONFIG_NETFILTER_XT_MATCH_STATISTIC is not set
+# CONFIG_NETFILTER_XT_MATCH_STRING is not set
+# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set
+# CONFIG_NETFILTER_XT_MATCH_TIME is not set
+# CONFIG_NETFILTER_XT_MATCH_U32 is not set
+# CONFIG_NETFILTER_XT_MATCH_HASHLIMIT is not set
+
+#
+# IP: Netfilter Configuration
+#
+CONFIG_IP_NF_QUEUE=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_IPRANGE=m
+CONFIG_IP_NF_MATCH_TOS=m
+CONFIG_IP_NF_MATCH_RECENT=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_AH=m
+CONFIG_IP_NF_MATCH_TTL=m
+CONFIG_IP_NF_MATCH_OWNER=m
+CONFIG_IP_NF_MATCH_ADDRTYPE=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_LOG=m
+CONFIG_IP_NF_TARGET_ULOG=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_TOS=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_TTL=m
+CONFIG_IP_NF_RAW=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+
+#
+# IPv6: Netfilter Configuration (EXPERIMENTAL)
+#
+# CONFIG_IP6_NF_QUEUE is not set
+# CONFIG_IP6_NF_IPTABLES is not set
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_SCTP is not set
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_SCHED is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_HAMRADIO is not set
+CONFIG_IRDA=m
+
+#
+# IrDA protocols
+#
+CONFIG_IRLAN=m
+CONFIG_IRNET=m
+CONFIG_IRCOMM=m
+# CONFIG_IRDA_ULTRA is not set
+
+#
+# IrDA options
+#
+# CONFIG_IRDA_CACHE_LAST_LSAP is not set
+# CONFIG_IRDA_FAST_RR is not set
+# CONFIG_IRDA_DEBUG is not set
+
+#
+# Infrared-port device drivers
+#
+
+#
+# SIR device drivers
+#
+# CONFIG_IRTTY_SIR is not set
+
+#
+# Dongle support
+#
+# CONFIG_KINGSUN_DONGLE is not set
+# CONFIG_KSDAZZLE_DONGLE is not set
+# CONFIG_KS959_DONGLE is not set
+
+#
+# Old SIR device drivers
+#
+# CONFIG_IRPORT_SIR is not set
+
+#
+# Old Serial dongle support
+#
+
+#
+# FIR device drivers
+#
+# CONFIG_USB_IRDA is not set
+# CONFIG_SIGMATEL_FIR is not set
+CONFIG_PXA_FICP=m
+# CONFIG_MCS_FIR is not set
+CONFIG_BT=m
+CONFIG_BT_L2CAP=m
+CONFIG_BT_SCO=m
+CONFIG_BT_RFCOMM=m
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=m
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+CONFIG_BT_HIDP=m
+
+#
+# Bluetooth device drivers
+#
+CONFIG_BT_HCIUSB=m
+# CONFIG_BT_HCIUSB_SCO is not set
+# CONFIG_BT_HCIBTSDIO is not set
+CONFIG_BT_HCIUART=m
+CONFIG_BT_HCIUART_H4=y
+CONFIG_BT_HCIUART_BCSP=y
+# CONFIG_BT_HCIUART_LL is not set
+CONFIG_BT_HCIBCM203X=m
+CONFIG_BT_HCIBPA10X=m
+CONFIG_BT_HCIBFUSB=m
+CONFIG_BT_HCIDTL1=m
+CONFIG_BT_HCIBT3C=m
+CONFIG_BT_HCIBLUECARD=m
+CONFIG_BT_HCIBTUART=m
+CONFIG_BT_HCIVHCI=m
+# CONFIG_AF_RXRPC is not set
+
+#
+# Wireless
+#
+# CONFIG_CFG80211 is not set
+CONFIG_WIRELESS_EXT=y
+# CONFIG_MAC80211 is not set
+CONFIG_IEEE80211=m
+# CONFIG_IEEE80211_DEBUG is not set
+CONFIG_IEEE80211_CRYPT_WEP=m
+CONFIG_IEEE80211_CRYPT_CCMP=m
+CONFIG_IEEE80211_CRYPT_TKIP=m
+CONFIG_IEEE80211_SOFTMAC=m
+# CONFIG_IEEE80211_SOFTMAC_DEBUG is not set
+# CONFIG_RFKILL is not set
+# CONFIG_NET_9P is not set
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+# CONFIG_DEBUG_DRIVER is not set
+# CONFIG_DEBUG_DEVRES is not set
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_CONNECTOR is not set
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+# CONFIG_MTD_CONCAT is not set
+CONFIG_MTD_PARTITIONS=y
+# CONFIG_MTD_REDBOOT_PARTS is not set
+CONFIG_MTD_CMDLINE_PARTS=y
+# CONFIG_MTD_AFS_PARTS is not set
+
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+# CONFIG_MTD_OOPS is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+CONFIG_MTD_CFI=y
+# CONFIG_MTD_JEDECPROBE is not set
+CONFIG_MTD_GEN_PROBE=y
+CONFIG_MTD_CFI_ADV_OPTIONS=y
+CONFIG_MTD_CFI_NOSWAP=y
+# CONFIG_MTD_CFI_BE_BYTE_SWAP is not set
+# CONFIG_MTD_CFI_LE_BYTE_SWAP is not set
+CONFIG_MTD_CFI_GEOMETRY=y
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+# CONFIG_MTD_OTP is not set
+CONFIG_MTD_CFI_INTELEXT=y
+# CONFIG_MTD_CFI_AMDSTD is not set
+# CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
+# CONFIG_MTD_RAM is not set
+CONFIG_MTD_ROM=y
+# CONFIG_MTD_ABSENT is not set
+# CONFIG_MTD_XIP is not set
+
+#
+# Mapping drivers for chip access
+#
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+# CONFIG_MTD_PHYSMAP is not set
+# CONFIG_MTD_ARM_INTEGRATOR is not set
+# CONFIG_MTD_SHARP_SL is not set
+# CONFIG_MTD_PLATRAM is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLOCK2MTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+CONFIG_MTD_NAND=y
+# CONFIG_MTD_NAND_VERIFY_WRITE is not set
+# CONFIG_MTD_NAND_ECC_SMC is not set
+# CONFIG_MTD_NAND_MUSEUM_IDS is not set
+# CONFIG_MTD_NAND_H1900 is not set
+CONFIG_MTD_NAND_IDS=y
+# CONFIG_MTD_NAND_DISKONCHIP is not set
+# CONFIG_MTD_NAND_SHARPSL is not set
+# CONFIG_MTD_NAND_NANDSIM is not set
+# CONFIG_MTD_NAND_PLATFORM is not set
+# CONFIG_MTD_ALAUDA is not set
+# CONFIG_MTD_ONENAND is not set
+
+#
+# UBI - Unsorted block images
+#
+# CONFIG_MTD_UBI is not set
+# CONFIG_PARPORT is not set
+CONFIG_BLK_DEV=y
+# CONFIG_BLK_DEV_COW_COMMON is not set
+CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_CRYPTOLOOP is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_UB is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=4096
+CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+CONFIG_MISC_DEVICES=y
+# CONFIG_EEPROM_93CX6 is not set
+CONFIG_IDE=y
+CONFIG_IDE_MAX_HWIFS=4
+CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_IDE_SATA is not set
+CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_IDEDISK_MULTI_MODE is not set
+CONFIG_BLK_DEV_IDECS=m
+# CONFIG_BLK_DEV_IDECD is not set
+# CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
+# CONFIG_BLK_DEV_IDESCSI is not set
+# CONFIG_IDE_TASK_IOCTL is not set
+CONFIG_IDE_PROC_FS=y
+
+#
+# IDE chipset support/bugfixes
+#
+CONFIG_IDE_GENERIC=y
+# CONFIG_BLK_DEV_PLATFORM is not set
+# CONFIG_IDE_ARM is not set
+# CONFIG_BLK_DEV_IDEDMA is not set
+CONFIG_IDE_ARCH_OBSOLETE_INIT=y
+# CONFIG_BLK_DEV_HD is not set
+
+#
+# SCSI device support
+#
+# CONFIG_RAID_ATTRS is not set
+CONFIG_SCSI=m
+CONFIG_SCSI_DMA=y
+# CONFIG_SCSI_TGT is not set
+# CONFIG_SCSI_NETLINK is not set
+CONFIG_SCSI_PROC_FS=y
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=m
+CONFIG_CHR_DEV_ST=m
+CONFIG_CHR_DEV_OSST=m
+CONFIG_BLK_DEV_SR=m
+# CONFIG_BLK_DEV_SR_VENDOR is not set
+CONFIG_CHR_DEV_SG=m
+# CONFIG_CHR_DEV_SCH is not set
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+CONFIG_SCSI_MULTI_LUN=y
+# CONFIG_SCSI_CONSTANTS is not set
+# CONFIG_SCSI_LOGGING is not set
+# CONFIG_SCSI_SCAN_ASYNC is not set
+CONFIG_SCSI_WAIT_SCAN=m
+
+#
+# SCSI Transports
+#
+# CONFIG_SCSI_SPI_ATTRS is not set
+# CONFIG_SCSI_FC_ATTRS is not set
+# CONFIG_SCSI_ISCSI_ATTRS is not set
+# CONFIG_SCSI_SAS_LIBSAS is not set
+# CONFIG_SCSI_SRP_ATTRS is not set
+CONFIG_SCSI_LOWLEVEL=y
+# CONFIG_ISCSI_TCP is not set
+# CONFIG_SCSI_DEBUG is not set
+# CONFIG_SCSI_LOWLEVEL_PCMCIA is not set
+# CONFIG_ATA is not set
+CONFIG_MD=y
+# CONFIG_BLK_DEV_MD is not set
+CONFIG_BLK_DEV_DM=m
+# CONFIG_DM_DEBUG is not set
+CONFIG_DM_CRYPT=m
+CONFIG_DM_SNAPSHOT=m
+CONFIG_DM_MIRROR=m
+CONFIG_DM_ZERO=m
+CONFIG_DM_MULTIPATH=m
+CONFIG_DM_MULTIPATH_EMC=m
+# CONFIG_DM_MULTIPATH_RDAC is not set
+# CONFIG_DM_MULTIPATH_HP is not set
+# CONFIG_DM_DELAY is not set
+# CONFIG_DM_UEVENT is not set
+CONFIG_NETDEVICES=y
+# CONFIG_NETDEVICES_MULTIQUEUE is not set
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_MACVLAN is not set
+# CONFIG_EQUALIZER is not set
+CONFIG_TUN=m
+# CONFIG_VETH is not set
+# CONFIG_PHYLIB is not set
+CONFIG_NET_ETHERNET=y
+CONFIG_MII=y
+# CONFIG_AX88796 is not set
+CONFIG_SMC91X=y
+# CONFIG_DM9000 is not set
+# CONFIG_SMC911X is not set
+# CONFIG_IBM_NEW_EMAC_ZMII is not set
+# CONFIG_IBM_NEW_EMAC_RGMII is not set
+# CONFIG_IBM_NEW_EMAC_TAH is not set
+# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
+# CONFIG_B44 is not set
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
+
+#
+# Wireless LAN
+#
+# CONFIG_WLAN_PRE80211 is not set
+CONFIG_WLAN_80211=y
+# CONFIG_PCMCIA_RAYCS is not set
+# CONFIG_LIBERTAS is not set
+CONFIG_HERMES=m
+CONFIG_PCMCIA_HERMES=m
+CONFIG_PCMCIA_SPECTRUM=m
+# CONFIG_ATMEL is not set
+CONFIG_AIRO_CS=m
+# CONFIG_PCMCIA_WL3501 is not set
+# CONFIG_USB_ZD1201 is not set
+CONFIG_HOSTAP=m
+CONFIG_HOSTAP_FIRMWARE=y
+# CONFIG_HOSTAP_FIRMWARE_NVRAM is not set
+CONFIG_HOSTAP_CS=m
+# CONFIG_ZD1211RW is not set
+
+#
+# USB Network Adapters
+#
+CONFIG_USB_CATC=m
+CONFIG_USB_KAWETH=m
+CONFIG_USB_PEGASUS=m
+CONFIG_USB_RTL8150=m
+CONFIG_USB_USBNET=m
+CONFIG_USB_NET_AX8817X=m
+CONFIG_USB_NET_CDCETHER=m
+CONFIG_USB_NET_DM9601=m
+# CONFIG_USB_NET_GL620A is not set
+CONFIG_USB_NET_NET1080=m
+# CONFIG_USB_NET_PLUSB is not set
+# CONFIG_USB_NET_MCS7830 is not set
+# CONFIG_USB_NET_RNDIS_HOST is not set
+CONFIG_USB_NET_CDC_SUBSET=m
+# CONFIG_USB_ALI_M5632 is not set
+# CONFIG_USB_AN2720 is not set
+# CONFIG_USB_BELKIN is not set
+CONFIG_USB_ARMLINUX=y
+# CONFIG_USB_EPSON2888 is not set
+# CONFIG_USB_KC2190 is not set
+# CONFIG_USB_NET_ZAURUS is not set
+# CONFIG_NET_PCMCIA is not set
+# CONFIG_WAN is not set
+CONFIG_PPP=m
+# CONFIG_PPP_MULTILINK is not set
+# CONFIG_PPP_FILTER is not set
+CONFIG_PPP_ASYNC=m
+# CONFIG_PPP_SYNC_TTY is not set
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_BSDCOMP=m
+# CONFIG_PPP_MPPE is not set
+# CONFIG_PPPOE is not set
+# CONFIG_PPPOL2TP is not set
+# CONFIG_SLIP is not set
+CONFIG_SLHC=m
+# CONFIG_SHAPER is not set
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_ISDN is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=y
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+# CONFIG_INPUT_JOYDEV is not set
+CONFIG_INPUT_EVDEV=y
+# CONFIG_INPUT_EVBUG is not set
+CONFIG_INPUT_POWER=y
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+CONFIG_KEYBOARD_ATKBD=y
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+# CONFIG_KEYBOARD_XTKBD is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+# CONFIG_KEYBOARD_GPIO is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+CONFIG_INPUT_TOUCHSCREEN=y
+# CONFIG_TOUCHSCREEN_FUJITSU is not set
+# CONFIG_TOUCHSCREEN_GUNZE is not set
+# CONFIG_TOUCHSCREEN_ELO is not set
+# CONFIG_TOUCHSCREEN_MTOUCH is not set
+# CONFIG_TOUCHSCREEN_MK712 is not set
+# CONFIG_TOUCHSCREEN_PENMOUNT is not set
+# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set
+# CONFIG_TOUCHSCREEN_TOUCHWIN is not set
+# CONFIG_TOUCHSCREEN_UCB1400 is not set
+# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set
+CONFIG_INPUT_MISC=y
+# CONFIG_INPUT_ATI_REMOTE is not set
+# CONFIG_INPUT_ATI_REMOTE2 is not set
+# CONFIG_INPUT_KEYSPAN_REMOTE is not set
+# CONFIG_INPUT_POWERMATE is not set
+# CONFIG_INPUT_YEALINK is not set
+CONFIG_INPUT_UINPUT=m
+
+#
+# Hardware I/O ports
+#
+CONFIG_SERIO=y
+# CONFIG_SERIO_SERPORT is not set
+CONFIG_SERIO_LIBPS2=y
+# CONFIG_SERIO_RAW is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+# CONFIG_VT_HW_CONSOLE_BINDING is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+
+#
+# Serial drivers
+#
+CONFIG_SERIAL_8250=m
+CONFIG_SERIAL_8250_CS=m
+CONFIG_SERIAL_8250_NR_UARTS=4
+CONFIG_SERIAL_8250_RUNTIME_UARTS=4
+# CONFIG_SERIAL_8250_EXTENDED is not set
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_PXA=y
+CONFIG_SERIAL_PXA_CONSOLE=y
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_IPMI_HANDLER is not set
+CONFIG_HW_RANDOM=m
+# CONFIG_NVRAM is not set
+# CONFIG_R3964 is not set
+
+#
+# PCMCIA character devices
+#
+# CONFIG_SYNCLINK_CS is not set
+# CONFIG_CARDMAN_4000 is not set
+# CONFIG_CARDMAN_4040 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+# CONFIG_I2C_CHARDEV is not set
+
+#
+# I2C Algorithms
+#
+CONFIG_I2C_ALGOBIT=y
+# CONFIG_I2C_ALGOPCF is not set
+# CONFIG_I2C_ALGOPCA is not set
+
+#
+# I2C Hardware Bus support
+#
+# CONFIG_I2C_GPIO is not set
+CONFIG_I2C_PXA=y
+# CONFIG_I2C_PXA_SLAVE is not set
+# CONFIG_I2C_OCORES is not set
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_SIMTEC is not set
+# CONFIG_I2C_TAOS_EVM is not set
+# CONFIG_I2C_STUB is not set
+# CONFIG_I2C_TINY_USB is not set
+
+#
+# Miscellaneous I2C Chip support
+#
+# CONFIG_SENSORS_DS1337 is not set
+# CONFIG_SENSORS_DS1374 is not set
+# CONFIG_DS1682 is not set
+# CONFIG_SENSORS_EEPROM is not set
+# CONFIG_SENSORS_PCF8574 is not set
+# CONFIG_SENSORS_PCA9539 is not set
+# CONFIG_SENSORS_PCF8591 is not set
+# CONFIG_SENSORS_MAX6875 is not set
+# CONFIG_SENSORS_TSL2550 is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_I2C_DEBUG_CHIP is not set
+
+#
+# SPI support
+#
+# CONFIG_SPI is not set
+# CONFIG_SPI_MASTER is not set
+# CONFIG_W1 is not set
+# CONFIG_POWER_SUPPLY is not set
+# CONFIG_HWMON is not set
+# CONFIG_WATCHDOG is not set
+
+#
+# Sonics Silicon Backplane
+#
+CONFIG_SSB_POSSIBLE=y
+# CONFIG_SSB is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_SM501 is not set
+# CONFIG_HTC_ASIC3 is not set
+# CONFIG_HTC_ASIC3_DS1WM is not set
+
+#
+# Multimedia devices
+#
+CONFIG_VIDEO_DEV=m
+CONFIG_VIDEO_V4L1=y
+CONFIG_VIDEO_V4L1_COMPAT=y
+CONFIG_VIDEO_V4L2=y
+CONFIG_VIDEO_CAPTURE_DRIVERS=y
+# CONFIG_VIDEO_ADV_DEBUG is not set
+CONFIG_VIDEO_HELPER_CHIPS_AUTO=y
+# CONFIG_VIDEO_VIVI is not set
+# CONFIG_VIDEO_CPIA is not set
+# CONFIG_VIDEO_CPIA2 is not set
+# CONFIG_VIDEO_SAA5246A is not set
+# CONFIG_VIDEO_SAA5249 is not set
+# CONFIG_TUNER_3036 is not set
+CONFIG_V4L_USB_DRIVERS=y
+# CONFIG_VIDEO_PVRUSB2 is not set
+# CONFIG_VIDEO_EM28XX is not set
+# CONFIG_VIDEO_USBVISION is not set
+CONFIG_VIDEO_USBVIDEO=m
+CONFIG_USB_VICAM=m
+CONFIG_USB_IBMCAM=m
+CONFIG_USB_KONICAWC=m
+# CONFIG_USB_QUICKCAM_MESSENGER is not set
+# CONFIG_USB_ET61X251 is not set
+# CONFIG_VIDEO_OVCAMCHIP is not set
+# CONFIG_USB_W9968CF is not set
+CONFIG_USB_OV511=m
+CONFIG_USB_SE401=m
+CONFIG_USB_SN9C102=m
+CONFIG_USB_STV680=m
+# CONFIG_USB_ZC0301 is not set
+# CONFIG_USB_PWC is not set
+# CONFIG_USB_ZR364XX is not set
+CONFIG_RADIO_ADAPTERS=y
+CONFIG_USB_DSBR=m
+# CONFIG_DVB_CORE is not set
+CONFIG_DAB=y
+CONFIG_USB_DABUSB=m
+
+#
+# Graphics support
+#
+# CONFIG_VGASTATE is not set
+CONFIG_VIDEO_OUTPUT_CONTROL=m
+CONFIG_FB=y
+# CONFIG_FIRMWARE_EDID is not set
+# CONFIG_FB_DDC is not set
+CONFIG_FB_CFB_FILLRECT=y
+CONFIG_FB_CFB_COPYAREA=y
+CONFIG_FB_CFB_IMAGEBLIT=y
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+# CONFIG_FB_SYS_FILLRECT is not set
+# CONFIG_FB_SYS_COPYAREA is not set
+# CONFIG_FB_SYS_IMAGEBLIT is not set
+# CONFIG_FB_SYS_FOPS is not set
+# CONFIG_FB_DEFERRED_IO is not set
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_BACKLIGHT is not set
+# CONFIG_FB_MODE_HELPERS is not set
+# CONFIG_FB_TILEBLITTING is not set
+
+#
+# Frame buffer hardware drivers
+#
+# CONFIG_FB_S1D13XXX is not set
+CONFIG_FB_PXA=y
+# CONFIG_FB_PXA_LCD_QVGA is not set
+CONFIG_FB_PXA_LCD_VGA=y
+CONFIG_FB_PXA_OVERLAY=y
+# CONFIG_FB_PXA_PARAMETERS is not set
+# CONFIG_FB_MBX is not set
+# CONFIG_FB_VIRTUAL is not set
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_LCD_CLASS_DEVICE=m
+CONFIG_BACKLIGHT_CLASS_DEVICE=m
+CONFIG_BACKLIGHT_CORGI=m
+
+#
+# Display device support
+#
+# CONFIG_DISPLAY_SUPPORT is not set
+
+#
+# Console display driver support
+#
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
+# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
+CONFIG_FONTS=y
+# CONFIG_FONT_8x8 is not set
+CONFIG_FONT_8x16=y
+# CONFIG_FONT_6x11 is not set
+# CONFIG_FONT_7x14 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+# CONFIG_FONT_ACORN_8x8 is not set
+# CONFIG_FONT_MINI_4x6 is not set
+# CONFIG_FONT_SUN8x16 is not set
+# CONFIG_FONT_SUN12x22 is not set
+# CONFIG_FONT_10x18 is not set
+CONFIG_LOGO=y
+# CONFIG_LOGO_LINUX_MONO is not set
+# CONFIG_LOGO_LINUX_VGA16 is not set
+# CONFIG_LOGO_LINUX_CLUT224 is not set
+CONFIG_LOGO_OHAND_CLUT224=y
+
+#
+# Sound
+#
+CONFIG_SOUND=m
+
+#
+# Advanced Linux Sound Architecture
+#
+CONFIG_SND=m
+CONFIG_SND_TIMER=m
+CONFIG_SND_PCM=m
+CONFIG_SND_SEQUENCER=m
+# CONFIG_SND_SEQ_DUMMY is not set
+CONFIG_SND_OSSEMUL=y
+CONFIG_SND_MIXER_OSS=m
+CONFIG_SND_PCM_OSS=m
+CONFIG_SND_PCM_OSS_PLUGINS=y
+# CONFIG_SND_SEQUENCER_OSS is not set
+# CONFIG_SND_DYNAMIC_MINORS is not set
+CONFIG_SND_SUPPORT_OLD_API=y
+CONFIG_SND_VERBOSE_PROCFS=y
+# CONFIG_SND_VERBOSE_PRINTK is not set
+# CONFIG_SND_DEBUG is not set
+
+#
+# Generic devices
+#
+CONFIG_SND_AC97_CODEC=m
+# CONFIG_SND_DUMMY is not set
+# CONFIG_SND_VIRMIDI is not set
+# CONFIG_SND_MTPAV is not set
+# CONFIG_SND_SERIAL_U16550 is not set
+# CONFIG_SND_MPU401 is not set
+
+#
+# ALSA ARM devices
+#
+CONFIG_SND_PXA2XX_PCM=m
+CONFIG_SND_PXA2XX_AC97=m
+
+#
+# USB devices
+#
+# CONFIG_SND_USB_AUDIO is not set
+# CONFIG_SND_USB_CAIAQ is not set
+
+#
+# PCMCIA devices
+#
+# CONFIG_SND_VXPOCKET is not set
+# CONFIG_SND_PDAUDIOCF is not set
+
+#
+# System on Chip audio support
+#
+CONFIG_SND_SOC=m
+CONFIG_SND_PXA2XX_SOC=m
+
+#
+# SoC Audio support for SuperH
+#
+
+#
+# Open Sound System
+#
+# CONFIG_SOUND_PRIME is not set
+CONFIG_AC97_BUS=m
+CONFIG_HID_SUPPORT=y
+CONFIG_HID=m
+# CONFIG_HID_DEBUG is not set
+# CONFIG_HIDRAW is not set
+
+#
+# USB Input Devices
+#
+CONFIG_USB_HID=m
+# CONFIG_USB_HIDINPUT_POWERBOOK is not set
+# CONFIG_HID_FF is not set
+# CONFIG_USB_HIDDEV is not set
+
+#
+# USB HID Boot Protocol drivers
+#
+CONFIG_USB_KBD=m
+CONFIG_USB_MOUSE=m
+CONFIG_USB_SUPPORT=y
+CONFIG_USB_ARCH_HAS_HCD=y
+# CONFIG_USB_ARCH_HAS_OHCI is not set
+# CONFIG_USB_ARCH_HAS_EHCI is not set
+CONFIG_USB=m
+# CONFIG_USB_DEBUG is not set
+
+#
+# Miscellaneous USB options
+#
+CONFIG_USB_DEVICEFS=y
+CONFIG_USB_DEVICE_CLASS=y
+# CONFIG_USB_DYNAMIC_MINORS is not set
+# CONFIG_USB_SUSPEND is not set
+# CONFIG_USB_PERSIST is not set
+# CONFIG_USB_OTG is not set
+
+#
+# USB Host Controller Drivers
+#
+# CONFIG_USB_ISP116X_HCD is not set
+# CONFIG_USB_SL811_HCD is not set
+# CONFIG_USB_R8A66597_HCD is not set
+
+#
+# USB Device Class drivers
+#
+CONFIG_USB_ACM=m
+CONFIG_USB_PRINTER=m
+
+#
+# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'
+#
+
+#
+# may also be needed; see USB_STORAGE Help for more information
+#
+CONFIG_USB_STORAGE=m
+# CONFIG_USB_STORAGE_DEBUG is not set
+# CONFIG_USB_STORAGE_DATAFAB is not set
+# CONFIG_USB_STORAGE_FREECOM is not set
+# CONFIG_USB_STORAGE_ISD200 is not set
+# CONFIG_USB_STORAGE_DPCM is not set
+# CONFIG_USB_STORAGE_USBAT is not set
+# CONFIG_USB_STORAGE_SDDR09 is not set
+# CONFIG_USB_STORAGE_SDDR55 is not set
+# CONFIG_USB_STORAGE_JUMPSHOT is not set
+# CONFIG_USB_STORAGE_ALAUDA is not set
+# CONFIG_USB_STORAGE_KARMA is not set
+# CONFIG_USB_LIBUSUAL is not set
+
+#
+# USB Imaging devices
+#
+CONFIG_USB_MDC800=m
+CONFIG_USB_MICROTEK=m
+CONFIG_USB_MON=y
+
+#
+# USB port drivers
+#
+
+#
+# USB Serial Converter support
+#
+CONFIG_USB_SERIAL=m
+CONFIG_USB_SERIAL_GENERIC=y
+# CONFIG_USB_SERIAL_AIRCABLE is not set
+# CONFIG_USB_SERIAL_AIRPRIME is not set
+# CONFIG_USB_SERIAL_ARK3116 is not set
+CONFIG_USB_SERIAL_BELKIN=m
+# CONFIG_USB_SERIAL_CH341 is not set
+# CONFIG_USB_SERIAL_WHITEHEAT is not set
+CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m
+CONFIG_USB_SERIAL_CP2101=m
+CONFIG_USB_SERIAL_CYPRESS_M8=m
+CONFIG_USB_SERIAL_EMPEG=m
+CONFIG_USB_SERIAL_FTDI_SIO=m
+# CONFIG_USB_SERIAL_FUNSOFT is not set
+CONFIG_USB_SERIAL_VISOR=m
+CONFIG_USB_SERIAL_IPAQ=m
+CONFIG_USB_SERIAL_IR=m
+CONFIG_USB_SERIAL_EDGEPORT=m
+CONFIG_USB_SERIAL_EDGEPORT_TI=m
+CONFIG_USB_SERIAL_GARMIN=m
+CONFIG_USB_SERIAL_IPW=m
+CONFIG_USB_SERIAL_KEYSPAN_PDA=m
+CONFIG_USB_SERIAL_KEYSPAN=m
+# CONFIG_USB_SERIAL_KEYSPAN_MPR is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19QW is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA19QI is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set
+# CONFIG_USB_SERIAL_KEYSPAN_USA49WLC is not set
+CONFIG_USB_SERIAL_KLSI=m
+CONFIG_USB_SERIAL_KOBIL_SCT=m
+CONFIG_USB_SERIAL_MCT_U232=m
+# CONFIG_USB_SERIAL_MOS7720 is not set
+# CONFIG_USB_SERIAL_MOS7840 is not set
+# CONFIG_USB_SERIAL_NAVMAN is not set
+CONFIG_USB_SERIAL_PL2303=m
+# CONFIG_USB_SERIAL_OTI6858 is not set
+# CONFIG_USB_SERIAL_HP4X is not set
+CONFIG_USB_SERIAL_SAFE=m
+# CONFIG_USB_SERIAL_SAFE_PADDED is not set
+# CONFIG_USB_SERIAL_SIERRAWIRELESS is not set
+CONFIG_USB_SERIAL_TI=m
+CONFIG_USB_SERIAL_CYBERJACK=m
+CONFIG_USB_SERIAL_XIRCOM=m
+# CONFIG_USB_SERIAL_OPTION is not set
+CONFIG_USB_SERIAL_OMNINET=m
+# CONFIG_USB_SERIAL_DEBUG is not set
+CONFIG_USB_EZUSB=y
+
+#
+# USB Miscellaneous drivers
+#
+CONFIG_USB_EMI62=m
+CONFIG_USB_EMI26=m
+# CONFIG_USB_ADUTUX is not set
+CONFIG_USB_AUERSWALD=m
+CONFIG_USB_RIO500=m
+CONFIG_USB_LEGOTOWER=m
+CONFIG_USB_LCD=m
+# CONFIG_USB_BERRY_CHARGE is not set
+CONFIG_USB_LED=m
+# CONFIG_USB_CYPRESS_CY7C63 is not set
+CONFIG_USB_CYTHERM=m
+# CONFIG_USB_PHIDGET is not set
+CONFIG_USB_IDMOUSE=m
+# CONFIG_USB_FTDI_ELAN is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+# CONFIG_USB_LD is not set
+# CONFIG_USB_TRANCEVIBRATOR is not set
+# CONFIG_USB_IOWARRIOR is not set
+# CONFIG_USB_TEST is not set
+
+#
+# USB DSL modem support
+#
+
+#
+# USB Gadget Support
+#
+CONFIG_USB_GADGET=y
+# CONFIG_USB_GADGET_DEBUG is not set
+# CONFIG_USB_GADGET_DEBUG_FILES is not set
+CONFIG_USB_GADGET_SELECTED=y
+# CONFIG_USB_GADGET_AMD5536UDC is not set
+# CONFIG_USB_GADGET_ATMEL_USBA is not set
+# CONFIG_USB_GADGET_FSL_USB2 is not set
+# CONFIG_USB_GADGET_NET2280 is not set
+# CONFIG_USB_GADGET_PXA2XX is not set
+CONFIG_USB_GADGET_M66592=y
+CONFIG_USB_M66592=y
+# CONFIG_USB_GADGET_PXA27X is not set
+# CONFIG_USB_GADGET_GOKU is not set
+# CONFIG_USB_GADGET_LH7A40X is not set
+# CONFIG_USB_GADGET_OMAP is not set
+# CONFIG_USB_GADGET_S3C2410 is not set
+# CONFIG_USB_GADGET_AT91 is not set
+# CONFIG_USB_GADGET_DUMMY_HCD is not set
+CONFIG_USB_GADGET_DUALSPEED=y
+# CONFIG_USB_ZERO is not set
+# CONFIG_USB_ETH is not set
+# CONFIG_USB_GADGETFS is not set
+CONFIG_USB_FILE_STORAGE=m
+# CONFIG_USB_FILE_STORAGE_TEST is not set
+CONFIG_USB_G_SERIAL=m
+# CONFIG_USB_MIDI_GADGET is not set
+CONFIG_MMC=y
+# CONFIG_MMC_DEBUG is not set
+CONFIG_MMC_UNSAFE_RESUME=y
+
+#
+# MMC/SD Card Drivers
+#
+CONFIG_MMC_BLOCK=y
+CONFIG_MMC_BLOCK_BOUNCE=y
+# CONFIG_SDIO_UART is not set
+
+#
+# MMC/SD Host Controller Drivers
+#
+CONFIG_MMC_PXA=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+
+#
+# LED drivers
+#
+# CONFIG_LEDS_GPIO is not set
+
+#
+# LED Triggers
+#
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=y
+CONFIG_LEDS_TRIGGER_IDE_DISK=y
+# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set
+CONFIG_RTC_LIB=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_HCTOSYS=y
+CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
+# CONFIG_RTC_DEBUG is not set
+
+#
+# RTC interfaces
+#
+CONFIG_RTC_INTF_SYSFS=y
+CONFIG_RTC_INTF_PROC=y
+CONFIG_RTC_INTF_DEV=y
+# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
+# CONFIG_RTC_DRV_TEST is not set
+
+#
+# I2C RTC drivers
+#
+# CONFIG_RTC_DRV_DS1307 is not set
+# CONFIG_RTC_DRV_DS1374 is not set
+# CONFIG_RTC_DRV_DS1672 is not set
+# CONFIG_RTC_DRV_MAX6900 is not set
+# CONFIG_RTC_DRV_RS5C372 is not set
+# CONFIG_RTC_DRV_ISL1208 is not set
+# CONFIG_RTC_DRV_X1205 is not set
+# CONFIG_RTC_DRV_PCF8563 is not set
+# CONFIG_RTC_DRV_PCF8583 is not set
+# CONFIG_RTC_DRV_M41T80 is not set
+
+#
+# SPI RTC drivers
+#
+
+#
+# Platform RTC drivers
+#
+# CONFIG_RTC_DRV_CMOS is not set
+# CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_STK17TA8 is not set
+# CONFIG_RTC_DRV_DS1742 is not set
+# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_M48T59 is not set
+# CONFIG_RTC_DRV_V3020 is not set
+
+#
+# on-CPU RTC drivers
+#
+CONFIG_RTC_DRV_SA1100=y
+
+#
+# File systems
+#
+CONFIG_EXT2_FS=y
+# CONFIG_EXT2_FS_XATTR is not set
+# CONFIG_EXT2_FS_XIP is not set
+CONFIG_EXT3_FS=m
+# CONFIG_EXT3_FS_XATTR is not set
+# CONFIG_EXT4DEV_FS is not set
+CONFIG_JBD=m
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+CONFIG_FS_POSIX_ACL=y
+# CONFIG_XFS_FS is not set
+# CONFIG_GFS2_FS is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_ROMFS_FS is not set
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_QUOTA is not set
+CONFIG_DNOTIFY=y
+# CONFIG_AUTOFS_FS is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+# CONFIG_MSDOS_FS is not set
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_DEBUG=0
+CONFIG_JFFS2_FS_WRITEBUFFER=y
+# CONFIG_JFFS2_FS_WBUF_VERIFY is not set
+CONFIG_JFFS2_SUMMARY=y
+# CONFIG_JFFS2_FS_XATTR is not set
+# CONFIG_JFFS2_SYSFS is not set
+CONFIG_JFFS2_COMPRESSION_OPTIONS=y
+CONFIG_JFFS2_ZLIB=y
+CONFIG_JFFS2_LZO=y
+CONFIG_JFFS2_RTIME=y
+CONFIG_JFFS2_RUBIN=y
+# CONFIG_JFFS2_CMODE_NONE is not set
+CONFIG_JFFS2_CMODE_PRIORITY=y
+# CONFIG_JFFS2_CMODE_SIZE is not set
+# CONFIG_JFFS2_CMODE_FAVOURLZO is not set
+CONFIG_CRAMFS=m
+CONFIG_SQUASHFS=m
+# CONFIG_SQUASHFS_EMBEDDED is not set
+CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3
+# CONFIG_VXFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+CONFIG_NFS_FS=m
+CONFIG_NFS_V3=y
+# CONFIG_NFS_V3_ACL is not set
+CONFIG_NFS_V4=y
+# CONFIG_NFS_DIRECTIO is not set
+CONFIG_NFSD=m
+CONFIG_NFSD_V3=y
+# CONFIG_NFSD_V3_ACL is not set
+CONFIG_NFSD_V4=y
+CONFIG_NFSD_TCP=y
+CONFIG_LOCKD=m
+CONFIG_LOCKD_V4=y
+CONFIG_EXPORTFS=m
+CONFIG_NFS_COMMON=y
+CONFIG_SUNRPC=m
+CONFIG_SUNRPC_GSS=m
+# CONFIG_SUNRPC_BIND34 is not set
+CONFIG_RPCSEC_GSS_KRB5=m
+# CONFIG_RPCSEC_GSS_SPKM3 is not set
+CONFIG_SMB_FS=m
+CONFIG_SMB_NLS_DEFAULT=y
+CONFIG_SMB_NLS_REMOTE="cp437"
+CONFIG_CIFS=m
+# CONFIG_CIFS_STATS is not set
+# CONFIG_CIFS_WEAK_PW_HASH is not set
+# CONFIG_CIFS_XATTR is not set
+# CONFIG_CIFS_DEBUG2 is not set
+# CONFIG_CIFS_EXPERIMENTAL is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_MINIX_SUBPARTITION is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_UNIXWARE_DISKLABEL is not set
+# CONFIG_LDM_PARTITION is not set
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_ULTRIX_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_KARMA_PARTITION is not set
+# CONFIG_EFI_PARTITION is not set
+# CONFIG_SYSV68_PARTITION is not set
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="cp437"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_737=m
+CONFIG_NLS_CODEPAGE_775=m
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
+CONFIG_NLS_CODEPAGE_855=m
+CONFIG_NLS_CODEPAGE_857=m
+CONFIG_NLS_CODEPAGE_860=m
+CONFIG_NLS_CODEPAGE_861=m
+CONFIG_NLS_CODEPAGE_862=m
+CONFIG_NLS_CODEPAGE_863=m
+CONFIG_NLS_CODEPAGE_864=m
+CONFIG_NLS_CODEPAGE_865=m
+CONFIG_NLS_CODEPAGE_866=m
+CONFIG_NLS_CODEPAGE_869=m
+CONFIG_NLS_CODEPAGE_936=m
+CONFIG_NLS_CODEPAGE_950=m
+CONFIG_NLS_CODEPAGE_932=m
+CONFIG_NLS_CODEPAGE_949=m
+CONFIG_NLS_CODEPAGE_874=m
+CONFIG_NLS_ISO8859_8=m
+CONFIG_NLS_CODEPAGE_1250=m
+CONFIG_NLS_CODEPAGE_1251=m
+CONFIG_NLS_ASCII=m
+CONFIG_NLS_ISO8859_1=y
+CONFIG_NLS_ISO8859_2=m
+CONFIG_NLS_ISO8859_3=m
+CONFIG_NLS_ISO8859_4=m
+CONFIG_NLS_ISO8859_5=m
+CONFIG_NLS_ISO8859_6=m
+CONFIG_NLS_ISO8859_7=m
+CONFIG_NLS_ISO8859_9=m
+CONFIG_NLS_ISO8859_13=m
+CONFIG_NLS_ISO8859_14=m
+CONFIG_NLS_ISO8859_15=m
+CONFIG_NLS_KOI8_R=m
+CONFIG_NLS_KOI8_U=m
+CONFIG_NLS_UTF8=y
+# CONFIG_DLM is not set
+CONFIG_INSTRUMENTATION=y
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=m
+# CONFIG_MARKERS is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_PRINTK_TIME is not set
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_DEBUG_FS is not set
+# CONFIG_HEADERS_CHECK is not set
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_DEBUG_SHIRQ is not set
+# CONFIG_DETECT_SOFTLOCKUP is not set
+# CONFIG_SCHED_DEBUG is not set
+# CONFIG_SCHEDSTATS is not set
+CONFIG_TIMER_STATS=y
+# CONFIG_DEBUG_SLAB is not set
+# CONFIG_DEBUG_PREEMPT is not set
+# CONFIG_DEBUG_RT_MUTEXES is not set
+# CONFIG_RT_MUTEX_TESTER is not set
+# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_DEBUG_MUTEXES is not set
+# CONFIG_DEBUG_LOCK_ALLOC is not set
+# CONFIG_PROVE_LOCKING is not set
+# CONFIG_LOCK_STAT is not set
+# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
+# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
+# CONFIG_DEBUG_KOBJECT is not set
+CONFIG_DEBUG_BUGVERBOSE=y
+# CONFIG_DEBUG_INFO is not set
+# CONFIG_DEBUG_VM is not set
+# CONFIG_DEBUG_LIST is not set
+# CONFIG_DEBUG_SG is not set
+CONFIG_FRAME_POINTER=y
+# CONFIG_FORCED_INLINING is not set
+# CONFIG_BOOT_PRINTK_DELAY is not set
+# CONFIG_RCU_TORTURE_TEST is not set
+# CONFIG_FAULT_INJECTION is not set
+# CONFIG_SAMPLES is not set
+# CONFIG_DEBUG_USER is not set
+CONFIG_DEBUG_ERRORS=y
+# CONFIG_DEBUG_LL is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITY_FILE_CAPABILITIES is not set
+CONFIG_CRYPTO=y
+CONFIG_CRYPTO_ALGAPI=m
+CONFIG_CRYPTO_BLKCIPHER=m
+CONFIG_CRYPTO_HASH=m
+CONFIG_CRYPTO_MANAGER=m
+CONFIG_CRYPTO_HMAC=m
+# CONFIG_CRYPTO_XCBC is not set
+CONFIG_CRYPTO_NULL=m
+CONFIG_CRYPTO_MD4=m
+CONFIG_CRYPTO_MD5=m
+CONFIG_CRYPTO_SHA1=m
+CONFIG_CRYPTO_SHA256=m
+CONFIG_CRYPTO_SHA512=m
+CONFIG_CRYPTO_WP512=m
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_GF128MUL is not set
+CONFIG_CRYPTO_ECB=m
+CONFIG_CRYPTO_CBC=m
+CONFIG_CRYPTO_PCBC=m
+# CONFIG_CRYPTO_LRW is not set
+# CONFIG_CRYPTO_XTS is not set
+# CONFIG_CRYPTO_CRYPTD is not set
+CONFIG_CRYPTO_DES=m
+# CONFIG_CRYPTO_FCRYPT is not set
+CONFIG_CRYPTO_BLOWFISH=m
+CONFIG_CRYPTO_TWOFISH=m
+CONFIG_CRYPTO_TWOFISH_COMMON=m
+CONFIG_CRYPTO_SERPENT=m
+CONFIG_CRYPTO_AES=m
+CONFIG_CRYPTO_CAST5=m
+CONFIG_CRYPTO_CAST6=m
+CONFIG_CRYPTO_TEA=m
+CONFIG_CRYPTO_ARC4=m
+CONFIG_CRYPTO_KHAZAD=m
+CONFIG_CRYPTO_ANUBIS=m
+# CONFIG_CRYPTO_SEED is not set
+CONFIG_CRYPTO_DEFLATE=m
+CONFIG_CRYPTO_LZO=m
+CONFIG_CRYPTO_MICHAEL_MIC=m
+CONFIG_CRYPTO_CRC32C=m
+CONFIG_CRYPTO_CAMELLIA=m
+CONFIG_CRYPTO_TEST=m
+# CONFIG_CRYPTO_AUTHENC is not set
+# CONFIG_CRYPTO_HW is not set
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+CONFIG_CRC_CCITT=y
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC7 is not set
+CONFIG_LIBCRC32C=m
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_LZO_COMPRESS=y
+CONFIG_LZO_DECOMPRESS=y
+CONFIG_PLIST=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
diff --git a/recipes/linux/linux-rp-2.6.26/hostap-monitor-mode.patch b/recipes/linux/linux-rp-2.6.26/hostap-monitor-mode.patch
new file mode 100644
index 0000000000..641fd19e50
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.26/hostap-monitor-mode.patch
@@ -0,0 +1,209 @@
+This is a patch that I've been maintaining for a few years, and I'd
+really like to see it added to the mainstream zaurus kernel so I can
+finally stop distributing my own.
+
+This patch only effects the card while in monitor mode, and does not
+cause any known stability issues.
+
+http://patches.aircrack-ng.org/hostap-kernel-2.6.18.patch
+
+Rick Farina (Zero_Chaos)
+
+diff -ur linux-2.6.18-gentoo/drivers/net/wireless/hostap/hostap_80211_tx.c linux-2.6.18-gentoo-rawtx/drivers/net/wireless/hostap/hostap_80211_tx.c
+--- linux-2.6.18-gentoo/drivers/net/wireless/hostap/hostap_80211_tx.c 2006-09-21 01:26:27.000000000 -0400
++++ linux-2.6.18-gentoo-rawtx/drivers/net/wireless/hostap/hostap_80211_tx.c 2006-09-21 01:30:18.000000000 -0400
+@@ -69,6 +69,9 @@
+ iface = netdev_priv(dev);
+ local = iface->local;
+
++ if (local->iw_mode == IW_MODE_MONITOR)
++ goto xmit;
++
+ if (skb->len < ETH_HLEN) {
+ printk(KERN_DEBUG "%s: hostap_data_start_xmit: short skb "
+ "(len=%d)\n", dev->name, skb->len);
+@@ -234,6 +237,7 @@
+ memcpy(skb_put(skb, ETH_ALEN), &hdr.addr4, ETH_ALEN);
+ }
+
++xmit:
+ iface->stats.tx_packets++;
+ iface->stats.tx_bytes += skb->len;
+
+@@ -404,8 +408,6 @@
+ }
+
+ if (skb->len < 24) {
+- printk(KERN_DEBUG "%s: hostap_master_start_xmit: short skb "
+- "(len=%d)\n", dev->name, skb->len);
+ ret = 0;
+ iface->stats.tx_dropped++;
+ goto fail;
+Only in linux-2.6.18-gentoo-rawtx/drivers/net/wireless/hostap: hostap_cs.c.orig
+Only in linux-2.6.18-gentoo-rawtx/drivers/net/wireless/hostap: hostap_cs.c.rej
+diff -ur linux-2.6.18-gentoo/drivers/net/wireless/hostap/hostap_hw.c linux-2.6.18-gentoo-rawtx/drivers/net/wireless/hostap/hostap_hw.c
+--- linux-2.6.18-gentoo/drivers/net/wireless/hostap/hostap_hw.c 2006-09-21 01:26:27.000000000 -0400
++++ linux-2.6.18-gentoo-rawtx/drivers/net/wireless/hostap/hostap_hw.c 2006-09-21 01:30:18.000000000 -0400
+@@ -1005,6 +1005,35 @@
+ return fid;
+ }
+
++static int prism2_monitor_enable(struct net_device *dev)
++{
++ if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE, 5)) {
++ printk(KERN_DEBUG "Port type setting for monitor mode "
++ "failed\n");
++ return -EOPNOTSUPP;
++ }
++
++ if (hfa384x_cmd(dev, HFA384X_CMDCODE_TEST | (0x0a << 8),
++ 0, NULL, NULL)) {
++ printk(KERN_DEBUG "Could not enter testmode 0x0a\n");
++ return -EOPNOTSUPP;
++ }
++
++ if (hostap_set_word(dev, HFA384X_RID_CNFWEPFLAGS,
++ HFA384X_WEPFLAGS_PRIVACYINVOKED |
++ HFA384X_WEPFLAGS_HOSTENCRYPT |
++ HFA384X_WEPFLAGS_HOSTDECRYPT)) {
++ printk(KERN_DEBUG "WEP flags setting failed\n");
++ return -EOPNOTSUPP;
++ }
++
++ if (hostap_set_word(dev, HFA384X_RID_PROMISCUOUSMODE, 1)) {
++ printk(KERN_DEBUG "Could not set promiscuous mode\n");
++ return -EOPNOTSUPP;
++ }
++
++ return 0;
++}
+
+ static int prism2_reset_port(struct net_device *dev)
+ {
+@@ -1031,6 +1060,10 @@
+ "port\n", dev->name);
+ }
+
++ if (local->iw_mode == IW_MODE_MONITOR)
++ /* force mode 0x0a after port 0 reset */
++ return prism2_monitor_enable(dev);
++
+ /* It looks like at least some STA firmware versions reset
+ * fragmentation threshold back to 2346 after enable command. Restore
+ * the configured value, if it differs from this default. */
+@@ -1466,6 +1499,10 @@
+ return 1;
+ }
+
++ if (local->iw_mode == IW_MODE_MONITOR)
++ /* force mode 0x0a after port 0 reset */
++ prism2_monitor_enable(dev);
++
+ local->hw_ready = 1;
+ local->hw_reset_tries = 0;
+ local->hw_resetting = 0;
+@@ -3156,6 +3193,7 @@
+ local->func->hw_config = prism2_hw_config;
+ local->func->hw_reset = prism2_hw_reset;
+ local->func->hw_shutdown = prism2_hw_shutdown;
++ local->func->monitor_enable = prism2_monitor_enable;
+ local->func->reset_port = prism2_reset_port;
+ local->func->schedule_reset = prism2_schedule_reset;
+ #ifdef PRISM2_DOWNLOAD_SUPPORT
+Only in linux-2.6.18-gentoo-rawtx/drivers/net/wireless/hostap: hostap_hw.c.orig
+diff -ur linux-2.6.18-gentoo/drivers/net/wireless/hostap/hostap_ioctl.c linux-2.6.18-gentoo-rawtx/drivers/net/wireless/hostap/hostap_ioctl.c
+--- linux-2.6.18-gentoo/drivers/net/wireless/hostap/hostap_ioctl.c 2006-09-21 01:26:27.000000000 -0400
++++ linux-2.6.18-gentoo-rawtx/drivers/net/wireless/hostap/hostap_ioctl.c 2006-09-21 01:30:18.000000000 -0400
+@@ -1104,33 +1104,7 @@
+
+ printk(KERN_DEBUG "Enabling monitor mode\n");
+ hostap_monitor_set_type(local);
+-
+- if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE,
+- HFA384X_PORTTYPE_PSEUDO_IBSS)) {
+- printk(KERN_DEBUG "Port type setting for monitor mode "
+- "failed\n");
+- return -EOPNOTSUPP;
+- }
+-
+- /* Host decrypt is needed to get the IV and ICV fields;
+- * however, monitor mode seems to remove WEP flag from frame
+- * control field */
+- if (hostap_set_word(dev, HFA384X_RID_CNFWEPFLAGS,
+- HFA384X_WEPFLAGS_HOSTENCRYPT |
+- HFA384X_WEPFLAGS_HOSTDECRYPT)) {
+- printk(KERN_DEBUG "WEP flags setting failed\n");
+- return -EOPNOTSUPP;
+- }
+-
+- if (local->func->reset_port(dev) ||
+- local->func->cmd(dev, HFA384X_CMDCODE_TEST |
+- (HFA384X_TEST_MONITOR << 8),
+- 0, NULL, NULL)) {
+- printk(KERN_DEBUG "Setting monitor mode failed\n");
+- return -EOPNOTSUPP;
+- }
+-
+- return 0;
++ return local->func->reset_port(dev);
+ }
+
+
+@@ -1199,7 +1173,7 @@
+ local->iw_mode = *mode;
+
+ if (local->iw_mode == IW_MODE_MONITOR)
+- hostap_monitor_mode_enable(local);
++ return hostap_monitor_mode_enable(local);
+ else if (local->iw_mode == IW_MODE_MASTER && !local->host_encrypt &&
+ !local->fw_encrypt_ok) {
+ printk(KERN_DEBUG "%s: defaulting to host-based encryption as "
+diff -ur linux-2.6.18-gentoo/drivers/net/wireless/hostap/hostap_main.c linux-2.6.18-gentoo-rawtx/drivers/net/wireless/hostap/hostap_main.c
+--- linux-2.6.18-gentoo/drivers/net/wireless/hostap/hostap_main.c 2006-09-21 01:26:27.000000000 -0400
++++ linux-2.6.18-gentoo-rawtx/drivers/net/wireless/hostap/hostap_main.c 2006-09-21 01:30:18.000000000 -0400
+@@ -331,7 +331,7 @@
+ if (local->iw_mode == IW_MODE_REPEAT)
+ return HFA384X_PORTTYPE_WDS;
+ if (local->iw_mode == IW_MODE_MONITOR)
+- return HFA384X_PORTTYPE_PSEUDO_IBSS;
++ return 5; /*HFA384X_PORTTYPE_PSEUDO_IBSS;*/
+ return HFA384X_PORTTYPE_HOSTAP;
+ }
+
+Only in linux-2.6.18-gentoo-rawtx/drivers/net/wireless/hostap: hostap_main.c.orig
+diff -ur linux-2.6.18-gentoo/drivers/net/wireless/hostap/hostap_pci.c linux-2.6.18-gentoo-rawtx/drivers/net/wireless/hostap/hostap_pci.c
+--- linux-2.6.18-gentoo/drivers/net/wireless/hostap/hostap_pci.c 2006-09-21 01:26:27.000000000 -0400
++++ linux-2.6.18-gentoo-rawtx/drivers/net/wireless/hostap/hostap_pci.c 2006-09-21 01:30:18.000000000 -0400
+@@ -48,6 +48,8 @@
+ { 0x1260, 0x3873, PCI_ANY_ID, PCI_ANY_ID },
+ /* Samsung MagicLAN SWL-2210P */
+ { 0x167d, 0xa000, PCI_ANY_ID, PCI_ANY_ID },
++ /* NETGEAR MA311 */
++ { 0x1385, 0x3872, PCI_ANY_ID, PCI_ANY_ID },
+ { 0 }
+ };
+
+Only in linux-2.6.18-gentoo-rawtx/drivers/net/wireless/hostap: hostap_pci.c.orig
+diff -ur linux-2.6.18-gentoo/drivers/net/wireless/hostap/hostap_plx.c linux-2.6.18-gentoo-rawtx/drivers/net/wireless/hostap/hostap_plx.c
+--- linux-2.6.18-gentoo/drivers/net/wireless/hostap/hostap_plx.c 2006-09-21 01:26:27.000000000 -0400
++++ linux-2.6.18-gentoo-rawtx/drivers/net/wireless/hostap/hostap_plx.c 2006-09-21 01:30:18.000000000 -0400
+@@ -101,6 +101,7 @@
+ { 0xc250, 0x0002 } /* EMTAC A2424i */,
+ { 0xd601, 0x0002 } /* Z-Com XI300 */,
+ { 0xd601, 0x0005 } /* Zcomax XI-325H 200mW */,
++ { 0xd601, 0x0010 } /* Zcomax XI-325H 100mW */,
+ { 0, 0}
+ };
+
+Only in linux-2.6.18-gentoo-rawtx/drivers/net/wireless/hostap: hostap_plx.c.orig
+diff -ur linux-2.6.18-gentoo/drivers/net/wireless/hostap/hostap_wlan.h linux-2.6.18-gentoo-rawtx/drivers/net/wireless/hostap/hostap_wlan.h
+--- linux-2.6.18-gentoo/drivers/net/wireless/hostap/hostap_wlan.h 2006-09-21 01:26:27.000000000 -0400
++++ linux-2.6.18-gentoo-rawtx/drivers/net/wireless/hostap/hostap_wlan.h 2006-09-21 01:30:18.000000000 -0400
+@@ -575,6 +575,7 @@
+ int (*hw_config)(struct net_device *dev, int initial);
+ void (*hw_reset)(struct net_device *dev);
+ void (*hw_shutdown)(struct net_device *dev, int no_disable);
++ int (*monitor_enable)(struct net_device *dev);
+ int (*reset_port)(struct net_device *dev);
+ void (*schedule_reset)(local_info_t *local);
+ int (*download)(local_info_t *local,
diff --git a/recipes/linux/linux-rp-2.6.26/hrw-hostapcard.patch b/recipes/linux/linux-rp-2.6.26/hrw-hostapcard.patch
new file mode 100644
index 0000000000..67fc5b7c70
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.26/hrw-hostapcard.patch
@@ -0,0 +1,34 @@
+
+From: Marcin Juszkiewicz <openembedded@haerwu.biz>
+
+Card reported by Ångström user:
+http://bugs.openembedded.net/show_bug.cgi?id=3236
+
+Socket 1:
+ product info: "Wireless LAN", "11Mbps PC Card", "Version 01.02", ""
+ manfid: 0x0156, 0x0002
+ function: 6 (network)
+
+Signed-off-by: Marcin Juszkiewicz <openembedded@haerwu.biz>
+Acked-by: Pavel Roskin <proski@gnu.org>
+
+---
+ drivers/net/wireless/hostap/hostap_cs.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- linux-2.6.24.orig/drivers/net/wireless/hostap/hostap_cs.c
++++ linux-2.6.24/drivers/net/wireless/hostap/hostap_cs.c
+@@ -892,10 +892,13 @@ static struct pcmcia_device_id hostap_cs
+ 0xa21501a, 0x59868926, 0xc9049a39),
+ PCMCIA_DEVICE_PROD_ID1234(
+ "The Linksys Group, Inc.", "Wireless Network CF Card", "ISL37300P",
+ "RevA",
+ 0xa5f472c2, 0x9c05598d, 0xc9049a39, 0x57a66194),
++ PCMCIA_DEVICE_PROD_ID123(
++ "Wireless LAN" , "11Mbps PC Card", "Version 01.02",
++ 0x4b8870ff, 0x70e946d1, 0x4b74baa0),
+ PCMCIA_DEVICE_NULL
+ };
+ MODULE_DEVICE_TABLE(pcmcia, hostap_cs_ids);
+
+
diff --git a/recipes/linux/linux-rp-2.6.26/htcuni-acx.patch b/recipes/linux/linux-rp-2.6.26/htcuni-acx.patch
new file mode 100644
index 0000000000..1ccebddc8d
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.26/htcuni-acx.patch
@@ -0,0 +1,33527 @@
+---
+ drivers/net/wireless/Kconfig | 31
+ drivers/net/wireless/Makefile | 2
+ drivers/net/wireless/acx/Kconfig | 113
+ drivers/net/wireless/acx/Makefile | 21
+ drivers/net/wireless/acx/acx.h | 14
+ drivers/net/wireless/acx/acx_config.h | 50
+ drivers/net/wireless/acx/acx_func.h | 710 ++
+ drivers/net/wireless/acx/acx_hw.h | 18
+ drivers/net/wireless/acx/acx_struct.h | 2114 ++++++++
+ drivers/net/wireless/acx/common.c | 7388 ++++++++++++++++++++++++++++
+ drivers/net/wireless/acx/conv.c | 504 +
+ drivers/net/wireless/acx/cs.c | 5703 +++++++++++++++++++++
+ drivers/net/wireless/acx/htcsable_acx.c | 118
+ drivers/net/wireless/acx/htcuniversal_acx.c | 108
+ drivers/net/wireless/acx/hx4700_acx.c | 108
+ drivers/net/wireless/acx/ioctl.c | 2748 ++++++++++
+ drivers/net/wireless/acx/mem.c | 5363 ++++++++++++++++++++
+ drivers/net/wireless/acx/pci.c | 4234 ++++++++++++++++
+ drivers/net/wireless/acx/rx3000_acx.c | 110
+ drivers/net/wireless/acx/setrate.c | 213
+ drivers/net/wireless/acx/usb.c | 1922 +++++++
+ drivers/net/wireless/acx/wlan.c | 424 +
+ drivers/net/wireless/acx/wlan_compat.h | 260
+ drivers/net/wireless/acx/wlan_hdr.h | 497 +
+ drivers/net/wireless/acx/wlan_mgmt.h | 582 ++
+ 25 files changed, 33355 insertions(+)
+
+Index: linux-2.6.23/drivers/net/wireless/acx/acx_config.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23/drivers/net/wireless/acx/acx_config.h 2008-01-20 21:13:40.000000000 +0000
+@@ -0,0 +1,50 @@
++#define ACX_RELEASE "v0.3.36"
++
++/*
++ * Test out all the channels in reg domain 0x10
++ */
++#define ACX_ALLOW_ALLCHANNELS
++
++/* set to 0 if you don't want any debugging code to be compiled in */
++/* set to 1 if you want some debugging */
++/* set to 2 if you want extensive debug log */
++#define ACX_DEBUG 0
++
++/*
++ * Since we'll be changing channels a lot
++#define ACX_DEFAULT_MSG (L_ASSOC|L_INIT)
++*/
++#define ACX_DEFAULT_MSG (L_ASSOC|L_INIT)
++
++/* assume 32bit I/O width
++ * (16bit is also compatible with Compact Flash) */
++#define ACX_IO_WIDTH 32
++
++/* Set this to 1 if you want monitor mode to use
++ * phy header. Currently it is not useful anyway since we
++ * don't know what useful info (if any) is in phy header.
++ * If you want faster/smaller code, say 0 here */
++#define WANT_PHY_HDR 0
++
++/* whether to do Tx descriptor cleanup in softirq (i.e. not in IRQ
++ * handler) or not. Note that doing it later does slightly increase
++ * system load, so still do that stuff in the IRQ handler for now,
++ * even if that probably means worse latency */
++#define TX_CLEANUP_IN_SOFTIRQ 0
++
++/* if you want very experimental 802.11 power save mode features */
++#define POWER_SAVE_80211 0
++
++/* if you want very early packet fragmentation bits and pieces */
++#define ACX_FRAGMENTATION 0
++
++/* Locking: */
++/* very talkative */
++/* #define PARANOID_LOCKING 1 */
++/* normal (use when bug-free) */
++#define DO_LOCKING 1
++/* else locking is disabled! */
++
++/* 0 - normal mode */
++/* 1 - development/debug: probe for IEs on modprobe */
++#define CMD_DISCOVERY 0
+Index: linux-2.6.23/drivers/net/wireless/acx/acx_func.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23/drivers/net/wireless/acx/acx_func.h 2008-01-20 21:13:40.000000000 +0000
+@@ -0,0 +1,710 @@
++/***********************************************************************
++** Copyright (C) 2003 ACX100 Open Source Project
++**
++** The contents of this file are subject to the Mozilla Public
++** License Version 1.1 (the "License"); you may not use this file
++** except in compliance with the License. You may obtain a copy of
++** the License at http://www.mozilla.org/MPL/
++**
++** Software distributed under the License is distributed on an "AS
++** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
++** implied. See the License for the specific language governing
++** rights and limitations under the License.
++**
++** Alternatively, the contents of this file may be used under the
++** terms of the GNU Public License version 2 (the "GPL"), in which
++** case the provisions of the GPL are applicable instead of the
++** above. If you wish to allow the use of your version of this file
++** only under the terms of the GPL and not to allow others to use
++** your version of this file under the MPL, indicate your decision
++** by deleting the provisions above and replace them with the notice
++** and other provisions required by the GPL. If you do not delete
++** the provisions above, a recipient may use your version of this
++** file under either the MPL or the GPL.
++** ---------------------------------------------------------------------
++** Inquiries regarding the ACX100 Open Source Project can be
++** made directly to:
++**
++** acx100-users@lists.sf.net
++** http://acx100.sf.net
++** ---------------------------------------------------------------------
++*/
++
++
++/***********************************************************************
++** LOGGING
++**
++** - Avoid SHOUTING needlessly. Avoid excessive verbosity.
++** Gradually remove messages which are old debugging aids.
++**
++** - Use printk() for messages which are to be always logged.
++** Supply either 'acx:' or '<devname>:' prefix so that user
++** can figure out who's speaking among other kernel chatter.
++** acx: is for general issues (e.g. "acx: no firmware image!")
++** while <devname>: is related to a particular device
++** (think about multi-card setup). Double check that message
++** is not confusing to the average user.
++**
++** - use printk KERN_xxx level only if message is not a WARNING
++** but is INFO, ERR etc.
++**
++** - Use printk_ratelimited() for messages which may flood
++** (e.g. "rx DUP pkt!").
++**
++** - Use log() for messages which may be omitted (and they
++** _will_ be omitted in non-debug builds). Note that
++** message levels may be disabled at compile-time selectively,
++** thus select them wisely. Example: L_DEBUG is the lowest
++** (most likely to be compiled out) -> use for less important stuff.
++**
++** - Do not print important stuff with log(), or else people
++** will never build non-debug driver.
++**
++** Style:
++** hex: capital letters, zero filled (e.g. 0x02AC)
++** str: dont start from capitals, no trailing periods ("tx: queue is stopped")
++*/
++#if ACX_DEBUG > 1
++
++void log_fn_enter(const char *funcname);
++void log_fn_exit(const char *funcname);
++void log_fn_exit_v(const char *funcname, int v);
++
++#define FN_ENTER \
++ do { \
++ if (unlikely(acx_debug & L_FUNC)) { \
++ log_fn_enter(__func__); \
++ } \
++ } while (0)
++
++#define FN_EXIT1(v) \
++ do { \
++ if (unlikely(acx_debug & L_FUNC)) { \
++ log_fn_exit_v(__func__, v); \
++ } \
++ } while (0)
++#define FN_EXIT0 \
++ do { \
++ if (unlikely(acx_debug & L_FUNC)) { \
++ log_fn_exit(__func__); \
++ } \
++ } while (0)
++
++#else
++
++#define FN_ENTER
++#define FN_EXIT1(v)
++#define FN_EXIT0
++
++#endif /* ACX_DEBUG > 1 */
++
++
++#if ACX_DEBUG
++
++#define log(chan, args...) \
++ do { \
++ if (acx_debug & (chan)) \
++ printk(KERN_DEBUG args); \
++ } while (0)
++#define printk_ratelimited(args...) printk(args)
++
++#else /* Non-debug build: */
++
++#define log(chan, args...)
++/* Standard way of log flood prevention */
++#define printk_ratelimited(args...) \
++do { \
++ if (printk_ratelimit()) \
++ printk(args); \
++} while (0)
++
++#endif /* ACX_DEBUG */
++
++void acx_print_mac(const char *head, const u8 *mac, const char *tail);
++
++/* Optimized out to nothing in non-debug build */
++static inline void
++acxlog_mac(int level, const char *head, const u8 *mac, const char *tail)
++{
++ if (acx_debug & level) {
++ acx_print_mac(head, mac, tail);
++ }
++}
++
++
++/***********************************************************************
++** MAC address helpers
++*/
++static inline void
++MAC_COPY(u8 *mac, const u8 *src)
++{
++ *(u32*)mac = *(u32*)src;
++ ((u16*)mac)[2] = ((u16*)src)[2];
++ /* kernel's memcpy will do the same: memcpy(dst, src, ETH_ALEN); */
++}
++
++static inline void
++MAC_FILL(u8 *mac, u8 val)
++{
++ memset(mac, val, ETH_ALEN);
++}
++
++static inline void
++MAC_BCAST(u8 *mac)
++{
++ ((u16*)mac)[2] = *(u32*)mac = -1;
++}
++
++static inline void
++MAC_ZERO(u8 *mac)
++{
++ ((u16*)mac)[2] = *(u32*)mac = 0;
++}
++
++static inline int
++mac_is_equal(const u8 *a, const u8 *b)
++{
++ /* can't beat this */
++ return memcmp(a, b, ETH_ALEN) == 0;
++}
++
++static inline int
++mac_is_bcast(const u8 *mac)
++{
++ /* AND together 4 first bytes with sign-extended 2 last bytes
++ ** Only bcast address gives 0xffffffff. +1 gives 0 */
++ return ( *(s32*)mac & ((s16*)mac)[2] ) + 1 == 0;
++}
++
++static inline int
++mac_is_zero(const u8 *mac)
++{
++ return ( *(u32*)mac | ((u16*)mac)[2] ) == 0;
++}
++
++static inline int
++mac_is_directed(const u8 *mac)
++{
++ return (mac[0] & 1)==0;
++}
++
++static inline int
++mac_is_mcast(const u8 *mac)
++{
++ return (mac[0] & 1) && !mac_is_bcast(mac);
++}
++
++#define MACSTR "%02X:%02X:%02X:%02X:%02X:%02X"
++#define MAC(bytevector) \
++ ((unsigned char *)bytevector)[0], \
++ ((unsigned char *)bytevector)[1], \
++ ((unsigned char *)bytevector)[2], \
++ ((unsigned char *)bytevector)[3], \
++ ((unsigned char *)bytevector)[4], \
++ ((unsigned char *)bytevector)[5]
++
++
++/***********************************************************************
++** Random helpers
++*/
++#define TO_STRING(x) #x
++#define STRING(x) TO_STRING(x)
++
++#define CLEAR_BIT(val, mask) ((val) &= ~(mask))
++#define SET_BIT(val, mask) ((val) |= (mask))
++
++/* undefined if v==0 */
++static inline unsigned int
++lowest_bit(u16 v)
++{
++ unsigned int n = 0;
++ while (!(v & 0xf)) { v>>=4; n+=4; }
++ while (!(v & 1)) { v>>=1; n++; }
++ return n;
++}
++
++/* undefined if v==0 */
++static inline unsigned int
++highest_bit(u16 v)
++{
++ unsigned int n = 0;
++ while (v>0xf) { v>>=4; n+=4; }
++ while (v>1) { v>>=1; n++; }
++ return n;
++}
++
++/* undefined if v==0 */
++static inline int
++has_only_one_bit(u16 v)
++{
++ return ((v-1) ^ v) >= v;
++}
++
++
++static inline int
++is_hidden_essid(char *essid)
++{
++ return (('\0' == essid[0]) ||
++ ((' ' == essid[0]) && ('\0' == essid[1])));
++}
++
++/***********************************************************************
++** LOCKING
++** We have adev->sem and adev->lock.
++**
++** We employ following naming convention in order to get locking right:
++**
++** acx_e_xxxx - external entry points called from process context.
++** It is okay to sleep. adev->sem is to be taken on entry.
++** acx_i_xxxx - external entry points possibly called from atomic context.
++** Sleeping is not allowed (and thus down(sem) is not legal!)
++** acx_s_xxxx - potentially sleeping functions. Do not ever call under lock!
++** acx_l_xxxx - functions which expect lock to be already taken.
++** rest - non-sleeping functions which do not require locking
++** but may be run under lock
++**
++** A small number of local helpers do not have acx_[eisl]_ prefix.
++** They are always close to caller and are to be reviewed locally.
++**
++** Theory of operation:
++**
++** All process-context entry points (_e_ functions) take sem
++** immediately. IRQ handler and other 'atomic-context' entry points
++** (_i_ functions) take lock immediately on entry, but dont take sem
++** because that might sleep.
++**
++** Thus *all* code is either protected by sem or lock, or both.
++**
++** Code which must not run concurrently with IRQ takes lock.
++** Such code is marked with _l_.
++**
++** This results in the following rules of thumb useful in code review:
++**
++** + If a function calls _s_ fn, it must be an _s_ itself.
++** + You can call _l_ fn only (a) from another _l_ fn
++** or (b) from _s_, _e_ or _i_ fn by taking lock, calling _l_,
++** and dropping lock.
++** + All IRQ code runs under lock.
++** + Any _s_ fn is running under sem.
++** + Code under sem can race only with IRQ code.
++** + Code under sem+lock cannot race with anything.
++*/
++
++/* These functions *must* be inline or they will break horribly on SPARC, due
++ * to its weird semantics for save/restore flags */
++
++#if defined(PARANOID_LOCKING) /* Lock debugging */
++
++void acx_lock_debug(acx_device_t *adev, const char* where);
++void acx_unlock_debug(acx_device_t *adev, const char* where);
++void acx_down_debug(acx_device_t *adev, const char* where);
++void acx_up_debug(acx_device_t *adev, const char* where);
++void acx_lock_unhold(void);
++void acx_sem_unhold(void);
++
++static inline void
++acx_lock_helper(acx_device_t *adev, unsigned long *fp, const char* where)
++{
++ acx_lock_debug(adev, where);
++ spin_lock_irqsave(&adev->lock, *fp);
++}
++static inline void
++acx_unlock_helper(acx_device_t *adev, unsigned long *fp, const char* where)
++{
++ acx_unlock_debug(adev, where);
++ spin_unlock_irqrestore(&adev->lock, *fp);
++}
++static inline void
++acx_down_helper(acx_device_t *adev, const char* where)
++{
++ acx_down_debug(adev, where);
++}
++static inline void
++acx_up_helper(acx_device_t *adev, const char* where)
++{
++ acx_up_debug(adev, where);
++}
++#define acx_lock(adev, flags) acx_lock_helper(adev, &(flags), __FILE__ ":" STRING(__LINE__))
++#define acx_unlock(adev, flags) acx_unlock_helper(adev, &(flags), __FILE__ ":" STRING(__LINE__))
++#define acx_sem_lock(adev) acx_down_helper(adev, __FILE__ ":" STRING(__LINE__))
++#define acx_sem_unlock(adev) acx_up_helper(adev, __FILE__ ":" STRING(__LINE__))
++
++#elif defined(DO_LOCKING)
++
++#define acx_lock(adev, flags) spin_lock_irqsave(&adev->lock, flags)
++#define acx_unlock(adev, flags) spin_unlock_irqrestore(&adev->lock, flags)
++#define acx_sem_lock(adev) down(&adev->sem)
++#define acx_sem_unlock(adev) up(&adev->sem)
++#define acx_lock_unhold() ((void)0)
++#define acx_sem_unhold() ((void)0)
++
++#else /* no locking! :( */
++
++#define acx_lock(adev, flags) ((void)0)
++#define acx_unlock(adev, flags) ((void)0)
++#define acx_sem_lock(adev) ((void)0)
++#define acx_sem_unlock(adev) ((void)0)
++#define acx_lock_unhold() ((void)0)
++#define acx_sem_unhold() ((void)0)
++
++#endif
++
++
++/***********************************************************************
++*/
++
++/* Can race with rx path (which is not protected by sem):
++** rx -> process_[re]assocresp() -> set_status(ASSOCIATED) -> wake_queue()
++** Can race with tx_complete IRQ:
++** IRQ -> acxpci_l_clean_txdesc -> acx_wake_queue
++** Review carefully all callsites */
++static inline void
++acx_stop_queue(struct net_device *ndev, const char *msg)
++{
++ if (netif_queue_stopped(ndev))
++ return;
++
++ netif_stop_queue(ndev);
++ if (msg)
++ log(L_BUFT, "tx: stop queue %s\n", msg);
++}
++
++static inline int
++acx_queue_stopped(struct net_device *ndev)
++{
++ return netif_queue_stopped(ndev);
++}
++
++/*
++static inline void
++acx_start_queue(struct net_device *ndev, const char *msg)
++{
++ netif_start_queue(ndev);
++ if (msg)
++ log(L_BUFT, "tx: start queue %s\n", msg);
++}
++*/
++
++static inline void
++acx_wake_queue(struct net_device *ndev, const char *msg)
++{
++ netif_wake_queue(ndev);
++ if (msg)
++ log(L_BUFT, "tx: wake queue %s\n", msg);
++}
++
++static inline void
++acx_carrier_off(struct net_device *ndev, const char *msg)
++{
++ netif_carrier_off(ndev);
++ if (msg)
++ log(L_BUFT, "tx: carrier off %s\n", msg);
++}
++
++static inline void
++acx_carrier_on(struct net_device *ndev, const char *msg)
++{
++ netif_carrier_on(ndev);
++ if (msg)
++ log(L_BUFT, "tx: carrier on %s\n", msg);
++}
++
++/* This function does not need locking UNLESS you call it
++** as acx_set_status(ACX_STATUS_4_ASSOCIATED), bacause this can
++** wake queue. This can race with stop_queue elsewhere. */
++void acx_set_status(acx_device_t *adev, u16 status);
++
++
++/***********************************************************************
++** Communication with firmware
++*/
++#define CMD_TIMEOUT_MS(n) (n)
++#define ACX_CMD_TIMEOUT_DEFAULT CMD_TIMEOUT_MS(50)
++
++#if ACX_DEBUG
++
++/* We want to log cmd names */
++int acxpci_s_issue_cmd_timeo_debug(acx_device_t *adev, unsigned cmd, void *param, unsigned len, unsigned timeout, const char* cmdstr);
++int acxmem_s_issue_cmd_timeo_debug(acx_device_t *adev, unsigned cmd, void *param, unsigned len, unsigned timeout, const char* cmdstr);
++int acxusb_s_issue_cmd_timeo_debug(acx_device_t *adev, unsigned cmd, void *param, unsigned len, unsigned timeout, const char* cmdstr);
++static inline int
++acx_s_issue_cmd_timeo_debug(acx_device_t *adev, unsigned cmd, void *param, unsigned len, unsigned timeout, const char* cmdstr)
++{
++ if (IS_MEM(adev))
++ return acxmem_s_issue_cmd_timeo_debug(adev, cmd, param, len, timeout, cmdstr);
++ if (IS_PCI(adev))
++ return acxpci_s_issue_cmd_timeo_debug(adev, cmd, param, len, timeout, cmdstr);
++ return acxusb_s_issue_cmd_timeo_debug(adev, cmd, param, len, timeout, cmdstr);
++}
++#define acx_s_issue_cmd(adev,cmd,param,len) \
++ acx_s_issue_cmd_timeo_debug(adev,cmd,param,len,ACX_CMD_TIMEOUT_DEFAULT,#cmd)
++#define acx_s_issue_cmd_timeo(adev,cmd,param,len,timeo) \
++ acx_s_issue_cmd_timeo_debug(adev,cmd,param,len,timeo,#cmd)
++int acx_s_configure_debug(acx_device_t *adev, void *pdr, int type, const char* str);
++#define acx_s_configure(adev,pdr,type) \
++ acx_s_configure_debug(adev,pdr,type,#type)
++int acx_s_interrogate_debug(acx_device_t *adev, void *pdr, int type, const char* str);
++#define acx_s_interrogate(adev,pdr,type) \
++ acx_s_interrogate_debug(adev,pdr,type,#type)
++
++#else
++
++int acxpci_s_issue_cmd_timeo(acx_device_t *adev, unsigned cmd, void *param, unsigned len, unsigned timeout);
++int acxmem_s_issue_cmd_timeo(acx_device_t *adev, unsigned cmd, void *param, unsigned len, unsigned timeout);
++int acxusb_s_issue_cmd_timeo(acx_device_t *adev, unsigned cmd, void *param, unsigned len, unsigned timeout);
++static inline int
++acx_s_issue_cmd_timeo(acx_device_t *adev, unsigned cmd, void *param, unsigned len, unsigned timeout)
++{
++ if (IS_MEM(adev))
++ return acxmem_s_issue_cmd_timeo(adev, cmd, param, len, timeout);
++ if (IS_PCI(adev))
++ return acxpci_s_issue_cmd_timeo(adev, cmd, param, len, timeout);
++ return acxusb_s_issue_cmd_timeo(adev, cmd, param, len, timeout);
++}
++static inline int
++acx_s_issue_cmd(acx_device_t *adev, unsigned cmd, void *param, unsigned len)
++{
++ if (IS_MEM(adev))
++ return acxmem_s_issue_cmd_timeo(adev, cmd, param, len, ACX_CMD_TIMEOUT_DEFAULT);
++ if (IS_PCI(adev))
++ return acxpci_s_issue_cmd_timeo(adev, cmd, param, len, ACX_CMD_TIMEOUT_DEFAULT);
++ return acxusb_s_issue_cmd_timeo(adev, cmd, param, len, ACX_CMD_TIMEOUT_DEFAULT);
++}
++int acx_s_configure(acx_device_t *adev, void *pdr, int type);
++int acx_s_interrogate(acx_device_t *adev, void *pdr, int type);
++
++#endif
++
++void acx_s_cmd_start_scan(acx_device_t *adev);
++
++
++/***********************************************************************
++** Ioctls
++*/
++int
++acx111pci_ioctl_info(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra);
++int
++acx100pci_ioctl_set_phy_amp_bias(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra);
++int
++acx100mem_ioctl_set_phy_amp_bias(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra);
++
++
++/***********************************************************************
++** /proc
++*/
++#ifdef CONFIG_PROC_FS
++int acx_proc_register_entries(const struct net_device *ndev);
++int acx_proc_unregister_entries(const struct net_device *ndev);
++#else
++static inline int
++acx_proc_register_entries(const struct net_device *ndev) { return OK; }
++static inline int
++acx_proc_unregister_entries(const struct net_device *ndev) { return OK; }
++#endif
++
++
++/***********************************************************************
++*/
++firmware_image_t *acx_s_read_fw(struct device *dev, const char *file, u32 *size);
++int acxpci_s_upload_radio(acx_device_t *adev);
++int acxmem_s_upload_radio(acx_device_t *adev);
++
++
++/***********************************************************************
++** Unsorted yet :)
++*/
++int acxpci_s_read_phy_reg(acx_device_t *adev, u32 reg, u8 *charbuf);
++int acxmem_s_read_phy_reg(acx_device_t *adev, u32 reg, u8 *charbuf);
++int acxusb_s_read_phy_reg(acx_device_t *adev, u32 reg, u8 *charbuf);
++static inline int
++acx_s_read_phy_reg(acx_device_t *adev, u32 reg, u8 *charbuf)
++{
++ if (IS_MEM(adev))
++ return acxmem_s_read_phy_reg(adev, reg, charbuf);
++ if (IS_PCI(adev))
++ return acxpci_s_read_phy_reg(adev, reg, charbuf);
++ return acxusb_s_read_phy_reg(adev, reg, charbuf);
++}
++
++int acxpci_s_write_phy_reg(acx_device_t *adev, u32 reg, u8 value);
++int acxmem_s_write_phy_reg(acx_device_t *adev, u32 reg, u8 value);
++int acxusb_s_write_phy_reg(acx_device_t *adev, u32 reg, u8 value);
++static inline int
++acx_s_write_phy_reg(acx_device_t *adev, u32 reg, u8 value)
++{
++ if (IS_MEM(adev))
++ return acxmem_s_write_phy_reg(adev, reg, value);
++ if (IS_PCI(adev))
++ return acxpci_s_write_phy_reg(adev, reg, value);
++ return acxusb_s_write_phy_reg(adev, reg, value);
++}
++
++tx_t* acxpci_l_alloc_tx(acx_device_t *adev);
++tx_t* acxmem_l_alloc_tx(acx_device_t *adev);
++tx_t* acxusb_l_alloc_tx(acx_device_t *adev);
++static inline tx_t*
++acx_l_alloc_tx(acx_device_t *adev)
++{
++ if (IS_MEM(adev))
++ return acxmem_l_alloc_tx(adev);
++ if (IS_PCI(adev))
++ return acxpci_l_alloc_tx(adev);
++ return acxusb_l_alloc_tx(adev);
++}
++
++void acxusb_l_dealloc_tx(tx_t *tx_opaque);
++void acxmem_l_dealloc_tx(acx_device_t *adev, tx_t *tx_opaque);
++static inline void
++acx_l_dealloc_tx(acx_device_t *adev, tx_t *tx_opaque)
++{
++#ifdef ACX_MEM
++ acxmem_l_dealloc_tx (adev, tx_opaque);
++#else
++ if (IS_USB(adev))
++ acxusb_l_dealloc_tx(tx_opaque);
++#endif
++}
++
++void* acxpci_l_get_txbuf(acx_device_t *adev, tx_t *tx_opaque);
++void* acxmem_l_get_txbuf(acx_device_t *adev, tx_t *tx_opaque);
++void* acxusb_l_get_txbuf(acx_device_t *adev, tx_t *tx_opaque);
++static inline void*
++acx_l_get_txbuf(acx_device_t *adev, tx_t *tx_opaque)
++{
++#if defined (ACX_MEM)
++ return acxmem_l_get_txbuf(adev, tx_opaque);
++#else
++ if (IS_PCI(adev))
++ return acxpci_l_get_txbuf(adev, tx_opaque);
++ return acxusb_l_get_txbuf(adev, tx_opaque);
++#endif
++}
++
++void acxpci_l_tx_data(acx_device_t *adev, tx_t *tx_opaque, int len);
++void acxmem_l_tx_data(acx_device_t *adev, tx_t *tx_opaque, int len);
++void acxusb_l_tx_data(acx_device_t *adev, tx_t *tx_opaque, int len);
++static inline void
++acx_l_tx_data(acx_device_t *adev, tx_t *tx_opaque, int len)
++{
++#if defined (ACX_MEM)
++ acxmem_l_tx_data(adev, tx_opaque, len);
++#else
++ if (IS_PCI(adev))
++ acxpci_l_tx_data(adev, tx_opaque, len);
++ else
++ acxusb_l_tx_data(adev, tx_opaque, len);
++#endif
++}
++
++static inline wlan_hdr_t*
++acx_get_wlan_hdr(acx_device_t *adev, const rxbuffer_t *rxbuf)
++{
++ return (wlan_hdr_t*)((u8*)&rxbuf->hdr_a3 + adev->phy_header_len);
++}
++
++void acxpci_l_power_led(acx_device_t *adev, int enable);
++int acxpci_read_eeprom_byte(acx_device_t *adev, u32 addr, u8 *charbuf);
++unsigned int acxpci_l_clean_txdesc(acx_device_t *adev);
++void acxpci_l_clean_txdesc_emergency(acx_device_t *adev);
++int acxpci_s_create_hostdesc_queues(acx_device_t *adev);
++void acxpci_create_desc_queues(acx_device_t *adev, u32 tx_queue_start, u32 rx_queue_start);
++void acxpci_free_desc_queues(acx_device_t *adev);
++char* acxpci_s_proc_diag_output(char *p, acx_device_t *adev);
++int acxpci_proc_eeprom_output(char *p, acx_device_t *adev);
++void acxpci_set_interrupt_mask(acx_device_t *adev);
++int acx100pci_s_set_tx_level(acx_device_t *adev, u8 level_dbm);
++
++void acxmem_l_power_led(acx_device_t *adev, int enable);
++int acxmem_read_eeprom_byte(acx_device_t *adev, u32 addr, u8 *charbuf);
++unsigned int acxmem_l_clean_txdesc(acx_device_t *adev);
++void acxmem_l_clean_txdesc_emergency(acx_device_t *adev);
++int acxmem_s_create_hostdesc_queues(acx_device_t *adev);
++void acxmem_create_desc_queues(acx_device_t *adev, u32 tx_queue_start, u32 rx_queue_start);
++void acxmem_free_desc_queues(acx_device_t *adev);
++char* acxmem_s_proc_diag_output(char *p, acx_device_t *adev);
++int acxmem_proc_eeprom_output(char *p, acx_device_t *adev);
++void acxmem_set_interrupt_mask(acx_device_t *adev);
++int acx100mem_s_set_tx_level(acx_device_t *adev, u8 level_dbm);
++
++void acx_s_msleep(int ms);
++int acx_s_init_mac(acx_device_t *adev);
++void acx_set_reg_domain(acx_device_t *adev, unsigned char reg_dom_id);
++void acx_set_timer(acx_device_t *adev, int timeout_us);
++void acx_update_capabilities(acx_device_t *adev);
++void acx_s_start(acx_device_t *adev);
++
++void acx_s_update_card_settings(acx_device_t *adev);
++void acx_s_parse_configoption(acx_device_t *adev, const acx111_ie_configoption_t *pcfg);
++void acx_l_update_ratevector(acx_device_t *adev);
++
++void acx_init_task_scheduler(acx_device_t *adev);
++void acx_schedule_task(acx_device_t *adev, unsigned int set_flag);
++
++int acx_e_ioctl_old(struct net_device *ndev, struct ifreq *ifr, int cmd);
++
++client_t *acx_l_sta_list_get(acx_device_t *adev, const u8 *address);
++void acx_l_sta_list_del(acx_device_t *adev, client_t *clt);
++
++int acx_l_transmit_disassoc(acx_device_t *adev, client_t *clt);
++void acx_i_timer(unsigned long a);
++int acx_s_complete_scan(acx_device_t *adev);
++
++struct sk_buff *acx_rxbuf_to_ether(acx_device_t *adev, rxbuffer_t *rxbuf);
++int acx_ether_to_txbuf(acx_device_t *adev, void *txbuf, const struct sk_buff *skb);
++
++u8 acx_signal_determine_quality(u8 signal, u8 noise);
++
++void acx_l_process_rxbuf(acx_device_t *adev, rxbuffer_t *rxbuf);
++void acx_l_handle_txrate_auto(acx_device_t *adev, struct client *txc,
++ u16 intended_rate, u8 rate100, u16 rate111, u8 error,
++ int pkts_to_ignore);
++
++void acx_dump_bytes(const void *, int);
++void acx_log_bad_eid(wlan_hdr_t* hdr, int len, wlan_ie_t* ie_ptr);
++
++u8 acx_rate111to100(u16);
++
++void acx_s_set_defaults(acx_device_t *adev);
++
++#if !ACX_DEBUG
++static inline const char* acx_get_packet_type_string(u16 fc) { return ""; }
++#else
++const char* acx_get_packet_type_string(u16 fc);
++#endif
++const char* acx_cmd_status_str(unsigned int state);
++
++int acx_i_start_xmit(struct sk_buff *skb, struct net_device *ndev);
++
++void great_inquisitor(acx_device_t *adev);
++
++void acx_s_get_firmware_version(acx_device_t *adev);
++void acx_display_hardware_details(acx_device_t *adev);
++
++int acx_e_change_mtu(struct net_device *ndev, int mtu);
++struct net_device_stats* acx_e_get_stats(struct net_device *ndev);
++struct iw_statistics* acx_e_get_wireless_stats(struct net_device *ndev);
++
++#ifdef ACX_MEM
++int __init acxmem_e_init_module(void);
++void __exit acxmem_e_cleanup_module(void);
++void acxmem_e_release(struct device *dev);
++#else
++int __init acxpci_e_init_module(void);
++int __init acxusb_e_init_module(void);
++void __exit acxpci_e_cleanup_module(void);
++void __exit acxusb_e_cleanup_module(void);
++#endif
++int __init acx_cs_init(void);
++void __exit acx_cs_cleanup(void);
+Index: linux-2.6.23/drivers/net/wireless/acx/acx.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23/drivers/net/wireless/acx/acx.h 2008-01-20 21:13:40.000000000 +0000
+@@ -0,0 +1,14 @@
++#if defined(CONFIG_ACX_MEM) && !defined(ACX_MEM)
++#define ACX_MEM
++#endif
++
++#if defined(CONFIG_ACX_CS) && !defined(ACX_MEM)
++#define ACX_MEM
++#endif
++
++#include "acx_config.h"
++#include "wlan_compat.h"
++#include "wlan_hdr.h"
++#include "wlan_mgmt.h"
++#include "acx_struct.h"
++#include "acx_func.h"
+Index: linux-2.6.23/drivers/net/wireless/acx/acx_hw.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23/drivers/net/wireless/acx/acx_hw.h 2008-01-20 21:13:40.000000000 +0000
+@@ -0,0 +1,18 @@
++/*
++ * Interface for ACX slave memory driver
++ *
++ * Copyright (c) 2006 SDG Systems, LLC
++ *
++ * GPL
++ *
++ */
++
++#ifndef _ACX_HW_H
++#define _ACX_HW_H
++
++struct acx_hardware_data {
++ int (*start_hw)( void );
++ int (*stop_hw)( void );
++};
++
++#endif /* _ACX_HW_H */
+Index: linux-2.6.23/drivers/net/wireless/acx/acx_struct.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23/drivers/net/wireless/acx/acx_struct.h 2008-01-20 21:13:40.000000000 +0000
+@@ -0,0 +1,2114 @@
++/***********************************************************************
++** Copyright (C) 2003 ACX100 Open Source Project
++**
++** The contents of this file are subject to the Mozilla Public
++** License Version 1.1 (the "License"); you may not use this file
++** except in compliance with the License. You may obtain a copy of
++** the License at http://www.mozilla.org/MPL/
++**
++** Software distributed under the License is distributed on an "AS
++** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
++** implied. See the License for the specific language governing
++** rights and limitations under the License.
++**
++** Alternatively, the contents of this file may be used under the
++** terms of the GNU Public License version 2 (the "GPL"), in which
++** case the provisions of the GPL are applicable instead of the
++** above. If you wish to allow the use of your version of this file
++** only under the terms of the GPL and not to allow others to use
++** your version of this file under the MPL, indicate your decision
++** by deleting the provisions above and replace them with the notice
++** and other provisions required by the GPL. If you do not delete
++** the provisions above, a recipient may use your version of this
++** file under either the MPL or the GPL.
++** ---------------------------------------------------------------------
++** Inquiries regarding the ACX100 Open Source Project can be
++** made directly to:
++**
++** acx100-users@lists.sf.net
++** http://acx100.sf.net
++** ---------------------------------------------------------------------
++*/
++
++/***********************************************************************
++** Forward declarations of types
++*/
++typedef struct tx tx_t;
++typedef struct acx_device acx_device_t;
++typedef struct client client_t;
++typedef struct rxdesc rxdesc_t;
++typedef struct txdesc txdesc_t;
++typedef struct rxhostdesc rxhostdesc_t;
++typedef struct txhostdesc txhostdesc_t;
++
++
++/***********************************************************************
++** Debug / log functionality
++*/
++enum {
++ L_LOCK = (ACX_DEBUG>1)*0x0001, /* locking debug log */
++ L_INIT = (ACX_DEBUG>0)*0x0002, /* special card initialization logging */
++ L_IRQ = (ACX_DEBUG>0)*0x0004, /* interrupt stuff */
++ L_ASSOC = (ACX_DEBUG>0)*0x0008, /* assocation (network join) and station log */
++ L_FUNC = (ACX_DEBUG>1)*0x0020, /* logging of function enter / leave */
++ L_XFER = (ACX_DEBUG>1)*0x0080, /* logging of transfers and mgmt */
++ L_DATA = (ACX_DEBUG>1)*0x0100, /* logging of transfer data */
++ L_DEBUG = (ACX_DEBUG>1)*0x0200, /* log of debug info */
++ L_IOCTL = (ACX_DEBUG>0)*0x0400, /* log ioctl calls */
++ L_CTL = (ACX_DEBUG>1)*0x0800, /* log of low-level ctl commands */
++ L_BUFR = (ACX_DEBUG>1)*0x1000, /* debug rx buffer mgmt (ring buffer etc.) */
++ L_XFER_BEACON = (ACX_DEBUG>1)*0x2000, /* also log beacon packets */
++ L_BUFT = (ACX_DEBUG>1)*0x4000, /* debug tx buffer mgmt (ring buffer etc.) */
++ L_USBRXTX = (ACX_DEBUG>0)*0x8000, /* debug USB rx/tx operations */
++ L_BUF = L_BUFR + L_BUFT,
++ L_ANY = 0xffff
++};
++
++#if ACX_DEBUG
++extern unsigned int acx_debug;
++#else
++enum { acx_debug = 0 };
++#endif
++
++
++/***********************************************************************
++** Random helpers
++*/
++#define ACX_PACKED __attribute__ ((packed))
++
++#define VEC_SIZE(a) (sizeof(a)/sizeof(a[0]))
++
++/* Use worker_queues for 2.5/2.6 kernels and queue tasks for 2.4 kernels
++ (used for the 'bottom half' of the interrupt routine) */
++
++#include <linux/workqueue.h>
++#define USE_WORKER_TASKS
++#define WORK_STRUCT struct work_struct
++#define SCHEDULE_WORK schedule_work
++#define FLUSH_SCHEDULED_WORK flush_scheduled_work
++
++
++/***********************************************************************
++** Constants
++*/
++#define OK 0
++#define NOT_OK 1
++
++/* The supported chip models */
++#define CHIPTYPE_ACX100 1
++#define CHIPTYPE_ACX111 2
++
++#define IS_ACX100(adev) ((adev)->chip_type == CHIPTYPE_ACX100)
++#define IS_ACX111(adev) ((adev)->chip_type == CHIPTYPE_ACX111)
++
++/* Supported interfaces */
++#define DEVTYPE_PCI 0
++#define DEVTYPE_USB 1
++#define DEVTYPE_MEM 2
++
++#if !defined(CONFIG_ACX_PCI) && !defined(CONFIG_ACX_USB) && !defined(CONFIG_ACX_MEM) && !defined(CONFIG_ACX_CS)
++#error Driver must include PCI, USB, PCMCIA or memory mapped interface support. You selected none of them.
++#endif
++
++#if defined(CONFIG_ACX_PCI)
++ #if !defined(CONFIG_ACX_USB)
++ #define IS_PCI(adev) 1
++ #else
++ #define IS_PCI(adev) ((adev)->dev_type == DEVTYPE_PCI)
++ #endif
++#else
++ #define IS_PCI(adev) 0
++#endif
++
++#if defined(CONFIG_ACX_USB)
++ #if !defined(CONFIG_ACX_PCI)
++ #define IS_USB(adev) 1
++ #else
++ #define IS_USB(adev) ((adev)->dev_type == DEVTYPE_USB)
++ #endif
++#else
++ #define IS_USB(adev) 0
++#endif
++
++#if defined(CONFIG_ACX_MEM) || defined(CONFIG_ACX_CS)
++ #define IS_MEM(adev) 1
++#else
++ #define IS_MEM(adev) 0
++#endif
++
++/* Driver defaults */
++#define DEFAULT_DTIM_INTERVAL 10
++/* used to be 2048, but FreeBSD driver changed it to 4096 to work properly
++** in noisy wlans */
++#define DEFAULT_MSDU_LIFETIME 4096
++#define DEFAULT_RTS_THRESHOLD 2312 /* max. size: disable RTS mechanism */
++#define DEFAULT_BEACON_INTERVAL 100
++
++#define ACX100_BAP_DATALEN_MAX 4096
++#define ACX100_RID_GUESSING_MAXLEN 2048 /* I'm not really sure */
++#define ACX100_RIDDATA_MAXLEN ACX100_RID_GUESSING_MAXLEN
++
++/* Support Constants */
++/* Radio type names, found in Win98 driver's TIACXLN.INF */
++#define RADIO_MAXIM_0D 0x0d
++#define RADIO_RFMD_11 0x11
++#define RADIO_RALINK_15 0x15
++/* used in ACX111 cards (WG311v2, WL-121, ...): */
++#define RADIO_RADIA_16 0x16
++/* most likely *sometimes* used in ACX111 cards: */
++#define RADIO_UNKNOWN_17 0x17
++/* FwRad19.bin was found in a Safecom driver; must be an ACX111 radio: */
++#define RADIO_UNKNOWN_19 0x19
++#define RADIO_UNKNOWN_1B 0x1b /* radio in SafeCom SWLUT-54125 USB adapter; entirely unknown!! */
++
++/* Controller Commands */
++/* can be found in table cmdTable in firmware "Rev. 1.5.0" (FW150) */
++#define ACX1xx_CMD_RESET 0x00
++#define ACX1xx_CMD_INTERROGATE 0x01
++#define ACX1xx_CMD_CONFIGURE 0x02
++#define ACX1xx_CMD_ENABLE_RX 0x03
++#define ACX1xx_CMD_ENABLE_TX 0x04
++#define ACX1xx_CMD_DISABLE_RX 0x05
++#define ACX1xx_CMD_DISABLE_TX 0x06
++#define ACX1xx_CMD_FLUSH_QUEUE 0x07
++#define ACX1xx_CMD_SCAN 0x08
++#define ACX1xx_CMD_STOP_SCAN 0x09
++#define ACX1xx_CMD_CONFIG_TIM 0x0a
++#define ACX1xx_CMD_JOIN 0x0b
++#define ACX1xx_CMD_WEP_MGMT 0x0c
++#ifdef OLD_FIRMWARE_VERSIONS
++#define ACX100_CMD_HALT 0x0e /* mapped to unknownCMD in FW150 */
++#else
++#define ACX1xx_CMD_MEM_READ 0x0d
++#define ACX1xx_CMD_MEM_WRITE 0x0e
++#endif
++#define ACX1xx_CMD_SLEEP 0x0f
++#define ACX1xx_CMD_WAKE 0x10
++#define ACX1xx_CMD_UNKNOWN_11 0x11 /* mapped to unknownCMD in FW150 */
++#define ACX100_CMD_INIT_MEMORY 0x12
++#define ACX1FF_CMD_DISABLE_RADIO 0x12 /* new firmware? TNETW1450? */
++#define ACX1xx_CMD_CONFIG_BEACON 0x13
++#define ACX1xx_CMD_CONFIG_PROBE_RESPONSE 0x14
++#define ACX1xx_CMD_CONFIG_NULL_DATA 0x15
++#define ACX1xx_CMD_CONFIG_PROBE_REQUEST 0x16
++#define ACX1xx_CMD_FCC_TEST 0x17
++#define ACX1xx_CMD_RADIOINIT 0x18
++#define ACX111_CMD_RADIOCALIB 0x19
++#define ACX1FF_CMD_NOISE_HISTOGRAM 0x1c /* new firmware? TNETW1450? */
++#define ACX1FF_CMD_RX_RESET 0x1d /* new firmware? TNETW1450? */
++#define ACX1FF_CMD_LNA_CONTROL 0x20 /* new firmware? TNETW1450? */
++#define ACX1FF_CMD_CONTROL_DBG_TRACE 0x21 /* new firmware? TNETW1450? */
++
++/* 'After Interrupt' Commands */
++#define ACX_AFTER_IRQ_CMD_STOP_SCAN 0x01
++#define ACX_AFTER_IRQ_CMD_ASSOCIATE 0x02
++#define ACX_AFTER_IRQ_CMD_RADIO_RECALIB 0x04
++#define ACX_AFTER_IRQ_UPDATE_CARD_CFG 0x08
++#define ACX_AFTER_IRQ_TX_CLEANUP 0x10
++#define ACX_AFTER_IRQ_COMPLETE_SCAN 0x20
++#define ACX_AFTER_IRQ_RESTART_SCAN 0x40
++
++/***********************************************************************
++** Tx/Rx buffer sizes and watermarks
++**
++** This will alloc and use DMAable buffers of
++** WLAN_A4FR_MAXLEN_WEP_FCS * (RX_CNT + TX_CNT) bytes
++** RX/TX_CNT=32 -> ~150k DMA buffers
++** RX/TX_CNT=16 -> ~75k DMA buffers
++**
++** 2005-10-10: reduced memory usage by lowering both to 16
++*/
++#define RX_CNT 16
++#define TX_CNT 16
++
++/* we clean up txdescs when we have N free txdesc: */
++#define TX_CLEAN_BACKLOG (TX_CNT/4)
++#define TX_START_CLEAN (TX_CNT - TX_CLEAN_BACKLOG)
++#define TX_EMERG_CLEAN 2
++/* we stop queue if we have < N free txbufs: */
++#define TX_STOP_QUEUE 3
++/* we start queue if we have >= N free txbufs: */
++#define TX_START_QUEUE 5
++
++/***********************************************************************
++** Interrogate/Configure cmd constants
++**
++** NB: length includes JUST the data part of the IE
++** (does not include size of the (type,len) pair)
++**
++** TODO: seems that acx100, acx100usb, acx111 have some differences,
++** fix code with regard to this!
++*/
++
++#define DEF_IE(name, val, len) enum { ACX##name=val, ACX##name##_LEN=len }
++
++/* Information Elements: Network Parameters, Static Configuration Entities */
++/* these are handled by real_cfgtable in firmware "Rev 1.5.0" (FW150) */
++DEF_IE(1xx_IE_UNKNOWN_00 ,0x0000, -1); /* mapped to cfgInvalid in FW150 */
++DEF_IE(100_IE_ACX_TIMER ,0x0001, 0x10);
++DEF_IE(1xx_IE_POWER_MGMT ,0x0002, 0x06); /* TNETW1450: length 0x18!! */
++DEF_IE(1xx_IE_QUEUE_CONFIG ,0x0003, 0x1c);
++DEF_IE(100_IE_BLOCK_SIZE ,0x0004, 0x02);
++DEF_IE(1FF_IE_SLOT_TIME ,0x0004, 0x08); /* later firmware versions only? */
++DEF_IE(1xx_IE_MEMORY_CONFIG_OPTIONS ,0x0005, 0x14);
++DEF_IE(1FF_IE_QUEUE_HEAD ,0x0005, 0x14 /* FIXME: length? */);
++DEF_IE(1xx_IE_RATE_FALLBACK ,0x0006, 0x01); /* TNETW1450: length 2 */
++DEF_IE(100_IE_WEP_OPTIONS ,0x0007, 0x03);
++DEF_IE(111_IE_RADIO_BAND ,0x0007, -1);
++DEF_IE(1FF_IE_TIMING_CFG ,0x0007, -1); /* later firmware versions; TNETW1450 only? */
++DEF_IE(100_IE_SSID ,0x0008, 0x20); /* huh? */
++DEF_IE(1xx_IE_MEMORY_MAP ,0x0008, 0x28); /* huh? TNETW1450 has length 0x40!! */
++DEF_IE(1xx_IE_SCAN_STATUS ,0x0009, 0x04); /* mapped to cfgInvalid in FW150 */
++DEF_IE(1xx_IE_ASSOC_ID ,0x000a, 0x02);
++DEF_IE(1xx_IE_UNKNOWN_0B ,0x000b, -1); /* mapped to cfgInvalid in FW150 */
++DEF_IE(1FF_IE_TX_POWER_LEVEL_TABLE ,0x000b, 0x18); /* later firmware versions; TNETW1450 only? */
++DEF_IE(100_IE_UNKNOWN_0C ,0x000c, -1); /* very small implementation in FW150! */
++/* ACX100 has an equivalent struct in the cmd mailbox directly after reset.
++ * 0x14c seems extremely large, will trash stack on failure (memset!)
++ * in case of small input struct --> OOPS! */
++DEF_IE(111_IE_CONFIG_OPTIONS ,0x000c, 0x14c);
++DEF_IE(1xx_IE_FWREV ,0x000d, 0x18);
++DEF_IE(1xx_IE_FCS_ERROR_COUNT ,0x000e, 0x04);
++DEF_IE(1xx_IE_MEDIUM_USAGE ,0x000f, 0x08);
++DEF_IE(1xx_IE_RXCONFIG ,0x0010, 0x04);
++DEF_IE(100_IE_UNKNOWN_11 ,0x0011, -1); /* NONBINARY: large implementation in FW150! link quality readings or so? */
++DEF_IE(111_IE_QUEUE_THRESH ,0x0011, -1);
++DEF_IE(100_IE_UNKNOWN_12 ,0x0012, -1); /* NONBINARY: VERY large implementation in FW150!! */
++DEF_IE(111_IE_BSS_POWER_SAVE ,0x0012, /* -1 */ 2);
++DEF_IE(1xx_IE_FIRMWARE_STATISTICS ,0x0013, 0x9c); /* TNETW1450: length 0x134!! */
++DEF_IE(1FF_IE_RX_INTR_CONFIG ,0x0014, 0x14); /* later firmware versions, TNETW1450 only? */
++DEF_IE(1xx_IE_FEATURE_CONFIG ,0x0015, 0x08);
++DEF_IE(111_IE_KEY_CHOOSE ,0x0016, 0x04); /* for rekeying. really len=4?? */
++DEF_IE(1FF_IE_MISC_CONFIG_TABLE ,0x0017, 0x04); /* later firmware versions, TNETW1450 only? */
++DEF_IE(1FF_IE_WONE_CONFIG ,0x0018, -1); /* later firmware versions, TNETW1450 only? */
++DEF_IE(1FF_IE_TID_CONFIG ,0x001a, 0x2c); /* later firmware versions, TNETW1450 only? */
++DEF_IE(1FF_IE_CALIB_ASSESSMENT ,0x001e, 0x04); /* later firmware versions, TNETW1450 only? */
++DEF_IE(1FF_IE_BEACON_FILTER_OPTIONS ,0x001f, 0x02); /* later firmware versions, TNETW1450 only? */
++DEF_IE(1FF_IE_LOW_RSSI_THRESH_OPT ,0x0020, 0x04); /* later firmware versions, TNETW1450 only? */
++DEF_IE(1FF_IE_NOISE_HISTOGRAM_RESULTS ,0x0021, 0x30); /* later firmware versions, TNETW1450 only? */
++DEF_IE(1FF_IE_PACKET_DETECT_THRESH ,0x0023, 0x04); /* later firmware versions, TNETW1450 only? */
++DEF_IE(1FF_IE_TX_CONFIG_OPTIONS ,0x0024, 0x04); /* later firmware versions, TNETW1450 only? */
++DEF_IE(1FF_IE_CCA_THRESHOLD ,0x0025, 0x02); /* later firmware versions, TNETW1450 only? */
++DEF_IE(1FF_IE_EVENT_MASK ,0x0026, 0x08); /* later firmware versions, TNETW1450 only? */
++DEF_IE(1FF_IE_DTIM_PERIOD ,0x0027, 0x02); /* later firmware versions, TNETW1450 only? */
++DEF_IE(1FF_IE_ACI_CONFIG_SET ,0x0029, 0x06); /* later firmware versions; maybe TNETW1450 only? */
++DEF_IE(1FF_IE_EEPROM_VER ,0x0030, 0x04); /* later firmware versions; maybe TNETW1450 only? */
++DEF_IE(1xx_IE_DOT11_STATION_ID ,0x1001, 0x06);
++DEF_IE(100_IE_DOT11_UNKNOWN_1002 ,0x1002, -1); /* mapped to cfgInvalid in FW150 */
++DEF_IE(111_IE_DOT11_FRAG_THRESH ,0x1002, -1); /* mapped to cfgInvalid in FW150; TNETW1450 has length 2!! */
++DEF_IE(100_IE_DOT11_BEACON_PERIOD ,0x1003, 0x02); /* mapped to cfgInvalid in FW150 */
++DEF_IE(1xx_IE_DOT11_DTIM_PERIOD ,0x1004, -1); /* mapped to cfgInvalid in FW150 */
++DEF_IE(1FF_IE_DOT11_MAX_RX_LIFETIME ,0x1004, -1); /* later firmware versions; maybe TNETW1450 only? */
++DEF_IE(1xx_IE_DOT11_SHORT_RETRY_LIMIT ,0x1005, 0x01); /* TNETW1450: length 2 */
++DEF_IE(1xx_IE_DOT11_LONG_RETRY_LIMIT ,0x1006, 0x01); /* TNETW1450: length 2 */
++DEF_IE(100_IE_DOT11_WEP_DEFAULT_KEY_WRITE ,0x1007, 0x20); /* configure default keys; TNETW1450 has length 0x24!! */
++DEF_IE(1xx_IE_DOT11_MAX_XMIT_MSDU_LIFETIME ,0x1008, 0x04);
++DEF_IE(1xx_IE_DOT11_GROUP_ADDR ,0x1009, -1);
++DEF_IE(1xx_IE_DOT11_CURRENT_REG_DOMAIN ,0x100a, 0x02);
++/* It's harmless to have larger struct. Use USB case always. */
++DEF_IE(1xx_IE_DOT11_CURRENT_ANTENNA ,0x100b, 0x02); /* in fact len=1 for PCI */
++DEF_IE(1xx_IE_DOT11_UNKNOWN_100C ,0x100c, -1); /* mapped to cfgInvalid in FW150 */
++DEF_IE(1xx_IE_DOT11_TX_POWER_LEVEL ,0x100d, 0x01); /* TNETW1450 has length 2!! */
++DEF_IE(1xx_IE_DOT11_CURRENT_CCA_MODE ,0x100e, 0x02); /* in fact len=1 for PCI */
++/* USB doesn't return anything - len==0?! */
++DEF_IE(100_IE_DOT11_ED_THRESHOLD ,0x100f, 0x04);
++DEF_IE(1xx_IE_DOT11_WEP_DEFAULT_KEY_SET ,0x1010, 0x01); /* set default key ID; TNETW1450: length 2 */
++DEF_IE(100_IE_DOT11_UNKNOWN_1011 ,0x1011, -1); /* mapped to cfgInvalid in FW150 */
++DEF_IE(1FF_IE_DOT11_CURR_5GHZ_REGDOM ,0x1011, -1); /* later firmware versions; maybe TNETW1450 only? */
++DEF_IE(100_IE_DOT11_UNKNOWN_1012 ,0x1012, -1); /* mapped to cfgInvalid in FW150 */
++DEF_IE(100_IE_DOT11_UNKNOWN_1013 ,0x1013, -1); /* mapped to cfgInvalid in FW150 */
++
++#if 0
++/* Experimentally obtained on acx100, fw 1.9.8.b
++** -1 means that fw returned 'invalid IE'
++** 0200 FC00 nnnn... are test read contents: u16 type, u16 len, data
++** (AA are poison bytes marking bytes not written by fw)
++**
++** Looks like acx100 fw does not update len field (thus len=256-4=FC here)
++** A number of IEs seem to trash type,len fields
++** IEs marked 'huge' return gobs of data (no poison bytes remain)
++*/
++DEF_IE(100_IE_INVAL_00, 0x0000, -1);
++DEF_IE(100_IE_INVAL_01, 0x0001, -1); /* IE_ACX_TIMER, len=16 on older fw */
++DEF_IE(100_IE_POWER_MGMT, 0x0002, 4); /* 0200FC00 00040000 AAAAAAAA */
++DEF_IE(100_IE_QUEUE_CONFIG, 0x0003, 28); /* 0300FC00 48060000 9CAD0000 0101AAAA DCB00000 E4B00000 9CAA0000 00AAAAAA */
++DEF_IE(100_IE_BLOCK_SIZE, 0x0004, 2); /* 0400FC00 0001AAAA AAAAAAAA AAAAAAAA */
++/* write only: */
++DEF_IE(100_IE_MEMORY_CONFIG_OPTIONS, 0x0005, 20);
++DEF_IE(100_IE_RATE_FALLBACK, 0x0006, 1); /* 0600FC00 00AAAAAA AAAAAAAA AAAAAAAA */
++/* write only: */
++DEF_IE(100_IE_WEP_OPTIONS, 0x0007, 3);
++DEF_IE(100_IE_MEMORY_MAP, 0x0008, 40); /* huge: 0800FC00 30000000 6CA20000 70A20000... */
++/* gives INVAL on read: */
++DEF_IE(100_IE_SCAN_STATUS, 0x0009, -1);
++DEF_IE(100_IE_ASSOC_ID, 0x000a, 2); /* huge: 0A00FC00 00000000 01040800 00000000... */
++DEF_IE(100_IE_INVAL_0B, 0x000b, -1);
++/* 'command rejected': */
++DEF_IE(100_IE_CONFIG_OPTIONS, 0x000c, -3);
++DEF_IE(100_IE_FWREV, 0x000d, 24); /* 0D00FC00 52657620 312E392E 382E6200 AAAAAAAA AAAAAAAA 05050201 AAAAAAAA */
++DEF_IE(100_IE_FCS_ERROR_COUNT, 0x000e, 4);
++DEF_IE(100_IE_MEDIUM_USAGE, 0x000f, 8); /* E41F0000 2D780300 FCC91300 AAAAAAAA */
++DEF_IE(100_IE_RXCONFIG, 0x0010, 4); /* 1000FC00 00280000 AAAAAAAA AAAAAAAA */
++DEF_IE(100_IE_QUEUE_THRESH, 0x0011, 12); /* 1100FC00 AAAAAAAA 00000000 00000000 */
++DEF_IE(100_IE_BSS_POWER_SAVE, 0x0012, 1); /* 1200FC00 00AAAAAA AAAAAAAA AAAAAAAA */
++/* read only, variable len */
++DEF_IE(100_IE_FIRMWARE_STATISTICS, 0x0013, 256); /* 0000AC00 00000000 ... */
++DEF_IE(100_IE_INT_CONFIG, 0x0014, 20); /* 00000000 00000000 00000000 00000000 5D74D105 00000000 AAAAAAAA AAAAAAAA */
++DEF_IE(100_IE_FEATURE_CONFIG, 0x0015, 8); /* 1500FC00 16000000 AAAAAAAA AAAAAAAA */
++/* returns 'invalid MAC': */
++DEF_IE(100_IE_KEY_CHOOSE, 0x0016, -4);
++DEF_IE(100_IE_INVAL_17, 0x0017, -1);
++DEF_IE(100_IE_UNKNOWN_18, 0x0018, 0); /* null len?! 1800FC00 AAAAAAAA AAAAAAAA AAAAAAAA */
++DEF_IE(100_IE_UNKNOWN_19, 0x0019, 256); /* huge: 1900FC00 9C1F00EA FEFFFFEA FEFFFFEA... */
++DEF_IE(100_IE_INVAL_1A, 0x001A, -1);
++
++DEF_IE(100_IE_DOT11_INVAL_1000, 0x1000, -1);
++DEF_IE(100_IE_DOT11_STATION_ID, 0x1001, 6); /* huge: 0110FC00 58B10E2F 03000000 00000000... */
++DEF_IE(100_IE_DOT11_INVAL_1002, 0x1002, -1);
++DEF_IE(100_IE_DOT11_INVAL_1003, 0x1003, -1);
++DEF_IE(100_IE_DOT11_INVAL_1004, 0x1004, -1);
++DEF_IE(100_IE_DOT11_SHORT_RETRY_LIMIT, 0x1005, 1);
++DEF_IE(100_IE_DOT11_LONG_RETRY_LIMIT, 0x1006, 1);
++/* write only: */
++DEF_IE(100_IE_DOT11_WEP_DEFAULT_KEY_WRITE, 0x1007, 32);
++DEF_IE(100_IE_DOT11_MAX_XMIT_MSDU_LIFETIME, 0x1008, 4); /* huge: 0810FC00 00020000 F4010000 00000000... */
++/* undoc but returns something */
++DEF_IE(100_IE_DOT11_GROUP_ADDR, 0x1009, 12); /* huge: 0910FC00 00000000 00000000 00000000... */
++DEF_IE(100_IE_DOT11_CURRENT_REG_DOMAIN, 0x100a, 1); /* 0A10FC00 30AAAAAA AAAAAAAA AAAAAAAA */
++DEF_IE(100_IE_DOT11_CURRENT_ANTENNA, 0x100b, 1); /* 0B10FC00 8FAAAAAA AAAAAAAA AAAAAAAA */
++DEF_IE(100_IE_DOT11_INVAL_100C, 0x100c, -1);
++DEF_IE(100_IE_DOT11_TX_POWER_LEVEL, 0x100d, 2); /* 00000000 0100AAAA AAAAAAAA AAAAAAAA */
++DEF_IE(100_IE_DOT11_CURRENT_CCA_MODE, 0x100e, 1); /* 0E10FC00 0DAAAAAA AAAAAAAA AAAAAAAA */
++DEF_IE(100_IE_DOT11_ED_THRESHOLD, 0x100f, 4); /* 0F10FC00 70000000 AAAAAAAA AAAAAAAA */
++/* set default key ID */
++DEF_IE(100_IE_DOT11_WEP_DEFAULT_KEY_SET, 0x1010, 1); /* 1010FC00 00AAAAAA AAAAAAAA AAAAAAAA */
++DEF_IE(100_IE_DOT11_INVAL_1011, 0x1011, -1);
++DEF_IE(100_IE_DOT11_INVAL_1012, 0x1012, -1);
++DEF_IE(100_IE_DOT11_INVAL_1013, 0x1013, -1);
++DEF_IE(100_IE_DOT11_UNKNOWN_1014, 0x1014, 256); /* huge */
++DEF_IE(100_IE_DOT11_UNKNOWN_1015, 0x1015, 256); /* huge */
++DEF_IE(100_IE_DOT11_UNKNOWN_1016, 0x1016, 256); /* huge */
++DEF_IE(100_IE_DOT11_UNKNOWN_1017, 0x1017, 256); /* huge */
++DEF_IE(100_IE_DOT11_UNKNOWN_1018, 0x1018, 256); /* huge */
++DEF_IE(100_IE_DOT11_UNKNOWN_1019, 0x1019, 256); /* huge */
++#endif
++
++#if 0
++/* Experimentally obtained on PCI acx111 Xterasys XN-2522g, fw 1.2.1.34
++** -1 means that fw returned 'invalid IE'
++** 0400 0800 nnnn... are test read contents: u16 type, u16 len, data
++** (AA are poison bytes marking bytes not written by fw)
++**
++** Looks like acx111 fw reports real len!
++*/
++DEF_IE(111_IE_INVAL_00, 0x0000, -1);
++DEF_IE(111_IE_INVAL_01, 0x0001, -1);
++DEF_IE(111_IE_POWER_MGMT, 0x0002, 12);
++/* write only, variable len: 12 + rxqueue_cnt*8 + txqueue_cnt*4: */
++DEF_IE(111_IE_MEMORY_CONFIG, 0x0003, 24);
++DEF_IE(111_IE_BLOCK_SIZE, 0x0004, 8); /* 04000800 AA00AAAA AAAAAAAA */
++/* variable len: 8 + rxqueue_cnt*8 + txqueue_cnt*8: */
++DEF_IE(111_IE_QUEUE_HEAD, 0x0005, 24);
++DEF_IE(111_IE_RATE_FALLBACK, 0x0006, 1);
++/* acx100 name:WEP_OPTIONS */
++/* said to have len:1 (not true, actually returns 12 bytes): */
++DEF_IE(111_IE_RADIO_BAND, 0x0007, 12); /* 07000C00 AAAA1F00 FF03AAAA AAAAAAAA */
++DEF_IE(111_IE_MEMORY_MAP, 0x0008, 48);
++/* said to have len:4, but gives INVAL on read: */
++DEF_IE(111_IE_SCAN_STATUS, 0x0009, -1);
++DEF_IE(111_IE_ASSOC_ID, 0x000a, 2);
++/* write only, len is not known: */
++DEF_IE(111_IE_UNKNOWN_0B, 0x000b, 0);
++/* read only, variable len. I see 67 byte reads: */
++DEF_IE(111_IE_CONFIG_OPTIONS, 0x000c, 67); /* 0C004300 01160500 ... */
++DEF_IE(111_IE_FWREV, 0x000d, 24);
++DEF_IE(111_IE_FCS_ERROR_COUNT, 0x000e, 4);
++DEF_IE(111_IE_MEDIUM_USAGE, 0x000f, 8);
++DEF_IE(111_IE_RXCONFIG, 0x0010, 4);
++DEF_IE(111_IE_QUEUE_THRESH, 0x0011, 12);
++DEF_IE(111_IE_BSS_POWER_SAVE, 0x0012, 1);
++/* read only, variable len. I see 240 byte reads: */
++DEF_IE(111_IE_FIRMWARE_STATISTICS, 0x0013, 240); /* 1300F000 00000000 ... */
++/* said to have len=17. looks like fw pads it to 20: */
++DEF_IE(111_IE_INT_CONFIG, 0x0014, 20); /* 14001400 00000000 00000000 00000000 00000000 00000000 */
++DEF_IE(111_IE_FEATURE_CONFIG, 0x0015, 8);
++/* said to be name:KEY_INDICATOR, len:4, but gives INVAL on read: */
++DEF_IE(111_IE_KEY_CHOOSE, 0x0016, -1);
++/* said to have len:4, but in fact returns 8: */
++DEF_IE(111_IE_MAX_USB_XFR, 0x0017, 8); /* 17000800 00014000 00000000 */
++DEF_IE(111_IE_INVAL_18, 0x0018, -1);
++DEF_IE(111_IE_INVAL_19, 0x0019, -1);
++/* undoc but returns something: */
++/* huh, fw indicates len=20 but uses 4 more bytes in buffer??? */
++DEF_IE(111_IE_UNKNOWN_1A, 0x001A, 20); /* 1A001400 AA00AAAA 0000020F FF030000 00020000 00000007 04000000 */
++
++DEF_IE(111_IE_DOT11_INVAL_1000, 0x1000, -1);
++DEF_IE(111_IE_DOT11_STATION_ID, 0x1001, 6);
++DEF_IE(111_IE_DOT11_FRAG_THRESH, 0x1002, 2);
++/* acx100 only? gives INVAL on read: */
++DEF_IE(111_IE_DOT11_BEACON_PERIOD, 0x1003, -1);
++/* said to be MAX_RECV_MSDU_LIFETIME: */
++DEF_IE(111_IE_DOT11_DTIM_PERIOD, 0x1004, 4);
++DEF_IE(111_IE_DOT11_SHORT_RETRY_LIMIT, 0x1005, 1);
++DEF_IE(111_IE_DOT11_LONG_RETRY_LIMIT, 0x1006, 1);
++/* acx100 only? gives INVAL on read: */
++DEF_IE(111_IE_DOT11_WEP_DEFAULT_KEY_WRITE, 0x1007, -1);
++DEF_IE(111_IE_DOT11_MAX_XMIT_MSDU_LIFETIME, 0x1008, 4);
++/* undoc but returns something. maybe it's 2 multicast MACs to listen to? */
++DEF_IE(111_IE_DOT11_GROUP_ADDR, 0x1009, 12); /* 09100C00 00000000 00000000 00000000 */
++DEF_IE(111_IE_DOT11_CURRENT_REG_DOMAIN, 0x100a, 1);
++DEF_IE(111_IE_DOT11_CURRENT_ANTENNA, 0x100b, 2);
++DEF_IE(111_IE_DOT11_INVAL_100C, 0x100c, -1);
++DEF_IE(111_IE_DOT11_TX_POWER_LEVEL, 0x100d, 1);
++/* said to have len=1 but gives INVAL on read: */
++DEF_IE(111_IE_DOT11_CURRENT_CCA_MODE, 0x100e, -1);
++/* said to have len=4 but gives INVAL on read: */
++DEF_IE(111_IE_DOT11_ED_THRESHOLD, 0x100f, -1);
++/* set default key ID. write only: */
++DEF_IE(111_IE_DOT11_WEP_DEFAULT_KEY_SET, 0x1010, 1);
++/* undoc but returns something: */
++DEF_IE(111_IE_DOT11_UNKNOWN_1011, 0x1011, 1); /* 11100100 20 */
++DEF_IE(111_IE_DOT11_INVAL_1012, 0x1012, -1);
++DEF_IE(111_IE_DOT11_INVAL_1013, 0x1013, -1);
++#endif
++
++
++/***********************************************************************
++**Information Frames Structures
++*/
++
++/* Used in beacon frames and the like */
++#define DOT11RATEBYTE_1 (1*2)
++#define DOT11RATEBYTE_2 (2*2)
++#define DOT11RATEBYTE_5_5 (5*2+1)
++#define DOT11RATEBYTE_11 (11*2)
++#define DOT11RATEBYTE_22 (22*2)
++#define DOT11RATEBYTE_6_G (6*2)
++#define DOT11RATEBYTE_9_G (9*2)
++#define DOT11RATEBYTE_12_G (12*2)
++#define DOT11RATEBYTE_18_G (18*2)
++#define DOT11RATEBYTE_24_G (24*2)
++#define DOT11RATEBYTE_36_G (36*2)
++#define DOT11RATEBYTE_48_G (48*2)
++#define DOT11RATEBYTE_54_G (54*2)
++#define DOT11RATEBYTE_BASIC 0x80 /* flags rates included in basic rate set */
++
++
++/***********************************************************************
++** rxbuffer_t
++**
++** This is the format of rx data returned by acx
++*/
++
++/* I've hoped it's a 802.11 PHY header, but no...
++ * so far, I've seen on acx111:
++ * 0000 3a00 0000 0000 IBSS Beacons
++ * 0000 3c00 0000 0000 ESS Beacons
++ * 0000 2700 0000 0000 Probe requests
++ * --vda
++ */
++typedef struct phy_hdr {
++ u8 unknown[4];
++ u8 acx111_unknown[4];
++} ACX_PACKED phy_hdr_t;
++
++/* seems to be a bit similar to hfa384x_rx_frame.
++ * These fields are still not quite obvious, though.
++ * Some seem to have different meanings... */
++
++#define RXBUF_HDRSIZE 12
++#define RXBUF_BYTES_RCVD(adev, rxbuf) \
++ ((le16_to_cpu((rxbuf)->mac_cnt_rcvd) & 0xfff) - (adev)->phy_header_len)
++#define RXBUF_BYTES_USED(rxbuf) \
++ ((le16_to_cpu((rxbuf)->mac_cnt_rcvd) & 0xfff) + RXBUF_HDRSIZE)
++/* USBism */
++#define RXBUF_IS_TXSTAT(rxbuf) (le16_to_cpu((rxbuf)->mac_cnt_rcvd) & 0x8000)
++/*
++mac_cnt_rcvd:
++ 12 bits: length of frame from control field to first byte of FCS
++ 3 bits: reserved
++ 1 bit: 1 = it's a tx status info, not a rx packet (USB only)
++
++mac_cnt_mblks:
++ 6 bits: number of memory block used to store frame in adapter memory
++ 1 bit: Traffic Indicator bit in TIM of received Beacon was set
++
++mac_status: 1 byte (bitmap):
++ 7 Matching BSSID
++ 6 Matching SSID
++ 5 BDCST Address 1 field is a broadcast
++ 4 VBM received beacon frame has more than one set bit (?!)
++ 3 TIM Set bit representing this station is set in TIM of received beacon
++ 2 GROUP Address 1 is a multicast
++ 1 ADDR1 Address 1 matches our MAC
++ 0 FCSGD FSC is good
++
++phy_stat_baseband: 1 byte (bitmap):
++ 7 Preamble frame had a long preamble
++ 6 PLCP Error CRC16 error in PLCP header
++ 5 Unsup_Mod unsupported modulation
++ 4 Selected Antenna antenna 1 was used to receive this frame
++ 3 PBCC/CCK frame used: 1=PBCC, 0=CCK modulation
++ 2 OFDM frame used OFDM modulation
++ 1 TI Protection protection frame was detected
++ 0 Reserved
++
++phy_plcp_signal: 1 byte:
++ Receive PLCP Signal field from the Baseband Processor
++
++phy_level: 1 byte:
++ receive AGC gain level (can be used to measure receive signal strength)
++
++phy_snr: 1 byte:
++ estimated noise power of equalized receive signal
++ at input of FEC decoder (can be used to measure receive signal quality)
++
++time: 4 bytes:
++ timestamp sampled from either the Access Manager TSF counter
++ or free-running microsecond counter when the MAC receives
++ first byte of PLCP header.
++*/
++
++typedef struct rxbuffer {
++ u16 mac_cnt_rcvd; /* only 12 bits are len! (0xfff) */
++ u8 mac_cnt_mblks;
++ u8 mac_status;
++ u8 phy_stat_baseband; /* bit 0x80: used LNA (Low-Noise Amplifier) */
++ u8 phy_plcp_signal;
++ u8 phy_level; /* PHY stat */
++ u8 phy_snr; /* PHY stat */
++ u32 time; /* timestamp upon MAC rcv first byte */
++/* 4-byte (acx100) or 8-byte (acx111) phy header will be here
++** if RX_CFG1_INCLUDE_PHY_HDR is in effect:
++** phy_hdr_t phy */
++ wlan_hdr_a3_t hdr_a3;
++ /* maximally sized data part of wlan packet */
++ u8 data_a3[WLAN_A4FR_MAXLEN_WEP_FCS - WLAN_HDR_A3_LEN];
++ /* can add hdr/data_a4 if needed */
++} ACX_PACKED rxbuffer_t;
++
++
++/*--- Firmware statistics ----------------------------------------------------*/
++
++/* define a random 100 bytes more to catch firmware versions which
++ * provide a bigger struct */
++#define FW_STATS_FUTURE_EXTENSION 100
++
++typedef struct fw_stats_tx {
++ u32 tx_desc_of;
++} ACX_PACKED fw_stats_tx_t;
++
++typedef struct fw_stats_rx {
++ u32 rx_oom;
++ u32 rx_hdr_of;
++ u32 rx_hw_stuck; /* old: u32 rx_hdr_use_next */
++ u32 rx_dropped_frame;
++ u32 rx_frame_ptr_err;
++ u32 rx_xfr_hint_trig;
++ u32 rx_aci_events; /* later versions only */
++ u32 rx_aci_resets; /* later versions only */
++} ACX_PACKED fw_stats_rx_t;
++
++typedef struct fw_stats_dma {
++ u32 rx_dma_req;
++ u32 rx_dma_err;
++ u32 tx_dma_req;
++ u32 tx_dma_err;
++} ACX_PACKED fw_stats_dma_t;
++
++typedef struct fw_stats_irq {
++ u32 cmd_cplt;
++ u32 fiq;
++ u32 rx_hdrs;
++ u32 rx_cmplt;
++ u32 rx_mem_of;
++ u32 rx_rdys;
++ u32 irqs;
++ u32 tx_procs;
++ u32 decrypt_done;
++ u32 dma_0_done;
++ u32 dma_1_done;
++ u32 tx_exch_complet;
++ u32 commands;
++ u32 rx_procs;
++ u32 hw_pm_mode_changes;
++ u32 host_acks;
++ u32 pci_pm;
++ u32 acm_wakeups;
++} ACX_PACKED fw_stats_irq_t;
++
++typedef struct fw_stats_wep {
++ u32 wep_key_count;
++ u32 wep_default_key_count;
++ u32 dot11_def_key_mib;
++ u32 wep_key_not_found;
++ u32 wep_decrypt_fail;
++ u32 wep_pkt_decrypt;
++ u32 wep_decrypt_irqs;
++} ACX_PACKED fw_stats_wep_t;
++
++typedef struct fw_stats_pwr {
++ u32 tx_start_ctr;
++ u32 no_ps_tx_too_short;
++ u32 rx_start_ctr;
++ u32 no_ps_rx_too_short;
++ u32 lppd_started;
++ u32 no_lppd_too_noisy;
++ u32 no_lppd_too_short;
++ u32 no_lppd_matching_frame;
++} ACX_PACKED fw_stats_pwr_t;
++
++typedef struct fw_stats_mic {
++ u32 mic_rx_pkts;
++ u32 mic_calc_fail;
++} ACX_PACKED fw_stats_mic_t;
++
++typedef struct fw_stats_aes {
++ u32 aes_enc_fail;
++ u32 aes_dec_fail;
++ u32 aes_enc_pkts;
++ u32 aes_dec_pkts;
++ u32 aes_enc_irq;
++ u32 aes_dec_irq;
++} ACX_PACKED fw_stats_aes_t;
++
++typedef struct fw_stats_event {
++ u32 heartbeat;
++ u32 calibration;
++ u32 rx_mismatch;
++ u32 rx_mem_empty;
++ u32 rx_pool;
++ u32 oom_late;
++ u32 phy_tx_err;
++ u32 tx_stuck;
++} ACX_PACKED fw_stats_event_t;
++
++/* mainly for size calculation only */
++typedef struct fw_stats {
++ u16 type;
++ u16 len;
++ fw_stats_tx_t tx;
++ fw_stats_rx_t rx;
++ fw_stats_dma_t dma;
++ fw_stats_irq_t irq;
++ fw_stats_wep_t wep;
++ fw_stats_pwr_t pwr;
++ fw_stats_mic_t mic;
++ fw_stats_aes_t aes;
++ fw_stats_event_t evt;
++ u8 _padding[FW_STATS_FUTURE_EXTENSION];
++} fw_stats_t;
++
++/* Firmware version struct */
++
++typedef struct fw_ver {
++ u16 cmd;
++ u16 size;
++ char fw_id[20];
++ u32 hw_id;
++} ACX_PACKED fw_ver_t;
++
++#define FW_ID_SIZE 20
++
++typedef struct shared_queueindicator {
++ u32 indicator;
++ u16 host_lock;
++ u16 fw_lock;
++} ACX_PACKED queueindicator_t;
++
++/*--- WEP stuff --------------------------------------------------------------*/
++#define DOT11_MAX_DEFAULT_WEP_KEYS 4
++
++/* non-firmware struct, no packing necessary */
++typedef struct wep_key {
++ size_t size; /* most often used member first */
++ u8 index;
++ u8 key[29];
++ u16 strange_filler;
++} wep_key_t; /* size = 264 bytes (33*8) */
++/* FIXME: We don't have size 264! Or is there 2 bytes beyond the key
++ * (strange_filler)? */
++
++/* non-firmware struct, no packing necessary */
++typedef struct key_struct {
++ u8 addr[ETH_ALEN]; /* 0x00 */
++ u16 filler1; /* 0x06 */
++ u32 filler2; /* 0x08 */
++ u32 index; /* 0x0c */
++ u16 len; /* 0x10 */
++ u8 key[29]; /* 0x12; is this long enough??? */
++} key_struct_t; /* size = 276. FIXME: where is the remaining space?? */
++
++
++/*--- Client (peer) info -----------------------------------------------------*/
++/* adev->sta_list[] is used for:
++** accumulating and processing of scan results
++** keeping client info in AP mode
++** keeping AP info in STA mode (AP is the only one 'client')
++** keeping peer info in ad-hoc mode
++** non-firmware struct --> no packing necessary */
++enum {
++ CLIENT_EMPTY_SLOT_0 = 0,
++ CLIENT_EXIST_1 = 1,
++ CLIENT_AUTHENTICATED_2 = 2,
++ CLIENT_ASSOCIATED_3 = 3,
++ CLIENT_JOIN_CANDIDATE = 4
++};
++struct client {
++ /* most frequent access first */
++ u8 used; /* misnamed, more like 'status' */
++ struct client* next;
++ unsigned long mtime; /* last time we heard it, in jiffies */
++ size_t essid_len; /* length of ESSID (without '\0') */
++ u32 sir; /* Standard IR */
++ u32 snr; /* Signal to Noise Ratio */
++ u16 aid; /* association ID */
++ u16 seq; /* from client's auth req */
++ u16 auth_alg; /* from client's auth req */
++ u16 cap_info; /* from client's assoc req */
++ u16 rate_cap; /* what client supports (all rates) */
++ u16 rate_bas; /* what client supports (basic rates) */
++ u16 rate_cfg; /* what is allowed (by iwconfig etc) */
++ u16 rate_cur; /* currently used rate mask */
++ u8 rate_100; /* currently used rate byte (acx100 only) */
++ u8 address[ETH_ALEN];
++ u8 bssid[ETH_ALEN]; /* ad-hoc hosts can have bssid != mac */
++ u8 channel;
++ u8 auth_step;
++ u8 ignore_count;
++ u8 fallback_count;
++ u8 stepup_count;
++ char essid[IW_ESSID_MAX_SIZE + 1]; /* ESSID and trailing '\0' */
++/* FIXME: this one is too damn big */
++ char challenge_text[WLAN_CHALLENGE_LEN];
++};
++
++
++/***********************************************************************
++** Hardware structures
++*/
++
++/* An opaque typesafe helper type
++ *
++ * Some hardware fields are actually pointers,
++ * but they have to remain u32, since using ptr instead
++ * (8 bytes on 64bit systems!) would disrupt the fixed descriptor
++ * format the acx firmware expects in the non-user area.
++ * Since we cannot cram an 8 byte ptr into 4 bytes, we need to
++ * enforce that pointed to data remains in low memory
++ * (address value needs to fit in 4 bytes) on 64bit systems.
++ *
++ * This is easy to get wrong, thus we are using a small struct
++ * and special macros to access it. Macros will check for
++ * attempts to overflow an acx_ptr with value > 0xffffffff.
++ *
++ * Attempts to use acx_ptr without macros result in compile-time errors */
++
++typedef struct {
++ u32 v;
++} ACX_PACKED acx_ptr;
++
++#if ACX_DEBUG
++#define CHECK32(n) BUG_ON(sizeof(n)>4 && (long)(n)>0xffffff00)
++#else
++#define CHECK32(n) ((void)0)
++#endif
++
++/* acx_ptr <-> integer conversion */
++#define cpu2acx(n) ({ CHECK32(n); ((acx_ptr){ .v = cpu_to_le32(n) }); })
++#define acx2cpu(a) (le32_to_cpu(a.v))
++
++/* acx_ptr <-> pointer conversion */
++#define ptr2acx(p) ({ CHECK32(p); ((acx_ptr){ .v = cpu_to_le32((u32)(long)(p)) }); })
++#define acx2ptr(a) ((void*)le32_to_cpu(a.v))
++
++/* Values for rate field (acx100 only) */
++#define RATE100_1 10
++#define RATE100_2 20
++#define RATE100_5 55
++#define RATE100_11 110
++#define RATE100_22 220
++/* This bit denotes use of PBCC:
++** (PBCC encoding is usable with 11 and 22 Mbps speeds only) */
++#define RATE100_PBCC511 0x80
++
++/* Bit values for rate111 field */
++#define RATE111_1 0x0001 /* DBPSK */
++#define RATE111_2 0x0002 /* DQPSK */
++#define RATE111_5 0x0004 /* CCK or PBCC */
++#define RATE111_6 0x0008 /* CCK-OFDM or OFDM */
++#define RATE111_9 0x0010 /* CCK-OFDM or OFDM */
++#define RATE111_11 0x0020 /* CCK or PBCC */
++#define RATE111_12 0x0040 /* CCK-OFDM or OFDM */
++#define RATE111_18 0x0080 /* CCK-OFDM or OFDM */
++#define RATE111_22 0x0100 /* PBCC */
++#define RATE111_24 0x0200 /* CCK-OFDM or OFDM */
++#define RATE111_36 0x0400 /* CCK-OFDM or OFDM */
++#define RATE111_48 0x0800 /* CCK-OFDM or OFDM */
++#define RATE111_54 0x1000 /* CCK-OFDM or OFDM */
++#define RATE111_RESERVED 0x2000
++#define RATE111_PBCC511 0x4000 /* PBCC mod at 5.5 or 11Mbit (else CCK) */
++#define RATE111_SHORTPRE 0x8000 /* short preamble */
++/* Special 'try everything' value */
++#define RATE111_ALL 0x1fff
++/* These bits denote acx100 compatible settings */
++#define RATE111_ACX100_COMPAT 0x0127
++/* These bits denote 802.11b compatible settings */
++#define RATE111_80211B_COMPAT 0x0027
++
++/* Descriptor Ctl field bits
++ * init value is 0x8e, "idle" value is 0x82 (in idle tx descs)
++ */
++#define DESC_CTL_SHORT_PREAMBLE 0x01 /* preamble type: 0 = long; 1 = short */
++#define DESC_CTL_FIRSTFRAG 0x02 /* this is the 1st frag of the frame */
++#define DESC_CTL_AUTODMA 0x04
++#define DESC_CTL_RECLAIM 0x08 /* ready to reuse */
++#define DESC_CTL_HOSTDONE 0x20 /* host has finished processing */
++#define DESC_CTL_ACXDONE 0x40 /* acx has finished processing */
++/* host owns the desc [has to be released last, AFTER modifying all other desc fields!] */
++#define DESC_CTL_HOSTOWN 0x80
++#define DESC_CTL_ACXDONE_HOSTOWN (DESC_CTL_ACXDONE | DESC_CTL_HOSTOWN)
++
++/* Descriptor Status field
++ */
++#define DESC_STATUS_FULL (1 << 31)
++
++/* NB: some bits may be interesting for Monitor mode tx (aka Raw tx): */
++#define DESC_CTL2_SEQ 0x01 /* don't increase sequence field */
++#define DESC_CTL2_FCS 0x02 /* don't add the FCS */
++#define DESC_CTL2_MORE_FRAG 0x04
++#define DESC_CTL2_RETRY 0x08 /* don't increase retry field */
++#define DESC_CTL2_POWER 0x10 /* don't increase power mgmt. field */
++#define DESC_CTL2_RTS 0x20 /* do RTS/CTS magic before sending */
++#define DESC_CTL2_WEP 0x40 /* encrypt this frame */
++#define DESC_CTL2_DUR 0x80 /* don't increase duration field */
++
++/***********************************************************************
++** PCI structures
++*/
++/* IRQ Constants
++** (outside of "#ifdef PCI" because USB (mis)uses HOST_INT_SCAN_COMPLETE) */
++#define HOST_INT_RX_DATA 0x0001
++#define HOST_INT_TX_COMPLETE 0x0002
++#define HOST_INT_TX_XFER 0x0004
++#define HOST_INT_RX_COMPLETE 0x0008
++#define HOST_INT_DTIM 0x0010
++#define HOST_INT_BEACON 0x0020
++#define HOST_INT_TIMER 0x0040
++#define HOST_INT_KEY_NOT_FOUND 0x0080
++#define HOST_INT_IV_ICV_FAILURE 0x0100
++#define HOST_INT_CMD_COMPLETE 0x0200
++#define HOST_INT_INFO 0x0400
++#define HOST_INT_OVERFLOW 0x0800
++#define HOST_INT_PROCESS_ERROR 0x1000
++#define HOST_INT_SCAN_COMPLETE 0x2000
++#define HOST_INT_FCS_THRESHOLD 0x4000
++#define HOST_INT_UNKNOWN 0x8000
++
++/* Outside of "#ifdef PCI" because USB needs to know sizeof()
++** of txdesc and rxdesc: */
++struct txdesc {
++ acx_ptr pNextDesc; /* pointer to next txdesc */
++ acx_ptr HostMemPtr; /* 0x04 */
++ acx_ptr AcxMemPtr; /* 0x08 */
++ u32 tx_time; /* 0x0c */
++ u16 total_length; /* 0x10 */
++ u16 Reserved; /* 0x12 */
++
++/* The following 16 bytes do not change when acx100 owns the descriptor */
++/* BUG: fw clears last byte of this area which is supposedly reserved
++** for driver use. amd64 blew up. We dare not use it now */
++ u32 dummy[4];
++
++ u8 Ctl_8; /* 0x24, 8bit value */
++ u8 Ctl2_8; /* 0x25, 8bit value */
++ u8 error; /* 0x26 */
++ u8 ack_failures; /* 0x27 */
++
++ union {
++ /*
++ * Packing doesn't work correctly on ARM unless unions are on
++ * 4 byte boundaries.
++ */
++ struct {
++ u8 rts_failures; /* 0x28 */
++ u8 rts_ok; /* 0x29 */
++ u16 d1;
++ } ACX_PACKED rts;
++ struct {
++ u16 d1;
++ u8 rate; /* 0x2a */
++ u8 queue_ctrl; /* 0x2b */
++ } ACX_PACKED r1;
++ struct {
++ u16 d1;
++ u16 rate111; /* 0x2a */
++ } ACX_PACKED r2;
++ } ACX_PACKED u;
++ u32 queue_info; /* 0x2c (acx100, reserved on acx111) */
++} ACX_PACKED; /* size : 48 = 0x30 */
++/* NB: acx111 txdesc structure is 4 byte larger */
++/* All these 4 extra bytes are reserved. tx alloc code takes them into account */
++
++struct rxdesc {
++ acx_ptr pNextDesc; /* 0x00 */
++ acx_ptr HostMemPtr; /* 0x04 */
++ acx_ptr ACXMemPtr; /* 0x08 */
++ u32 rx_time; /* 0x0c */
++ u16 total_length; /* 0x10 */
++ u16 WEP_length; /* 0x12 */
++ u32 WEP_ofs; /* 0x14 */
++
++/* the following 16 bytes do not change when acx100 owns the descriptor */
++ u8 driverWorkspace[16]; /* 0x18 */
++
++ u8 Ctl_8;
++ u8 rate;
++ u8 error;
++ u8 SNR; /* Signal-to-Noise Ratio */
++ u8 RxLevel;
++ u8 queue_ctrl;
++ u16 unknown;
++ u32 unknown2;
++} ACX_PACKED; /* size 52 = 0x34 */
++
++#if defined(ACX_PCI) || defined(ACX_MEM)
++
++/* Register I/O offsets */
++#define ACX100_EEPROM_ID_OFFSET 0x380
++
++/* please add further ACX hardware register definitions only when
++ it turns out you need them in the driver, and please try to use
++ firmware functionality instead, since using direct I/O access instead
++ of letting the firmware do it might confuse the firmware's state
++ machine */
++
++/* ***** ABSOLUTELY ALWAYS KEEP OFFSETS IN SYNC WITH THE INITIALIZATION
++** OF THE I/O ARRAYS!!!! (grep for '^IO_ACX') ***** */
++enum {
++ IO_ACX_SOFT_RESET = 0,
++
++ IO_ACX_SLV_MEM_ADDR,
++ IO_ACX_SLV_MEM_DATA,
++ IO_ACX_SLV_MEM_CTL,
++ IO_ACX_SLV_END_CTL,
++
++ IO_ACX_FEMR, /* Function Event Mask */
++
++ IO_ACX_INT_TRIG,
++ IO_ACX_IRQ_MASK,
++ IO_ACX_IRQ_STATUS_NON_DES,
++ IO_ACX_IRQ_STATUS_CLEAR, /* CLEAR = clear on read */
++ IO_ACX_IRQ_ACK,
++ IO_ACX_HINT_TRIG,
++
++ IO_ACX_ENABLE,
++
++ IO_ACX_EEPROM_CTL,
++ IO_ACX_EEPROM_ADDR,
++ IO_ACX_EEPROM_DATA,
++ IO_ACX_EEPROM_CFG,
++
++ IO_ACX_PHY_ADDR,
++ IO_ACX_PHY_DATA,
++ IO_ACX_PHY_CTL,
++
++ IO_ACX_GPIO_OE,
++
++ IO_ACX_GPIO_OUT,
++
++ IO_ACX_CMD_MAILBOX_OFFS,
++ IO_ACX_INFO_MAILBOX_OFFS,
++ IO_ACX_EEPROM_INFORMATION,
++
++ IO_ACX_EE_START,
++ IO_ACX_SOR_CFG,
++ IO_ACX_ECPU_CTRL
++};
++/* ***** ABSOLUTELY ALWAYS KEEP OFFSETS IN SYNC WITH THE INITIALIZATION
++** OF THE I/O ARRAYS!!!! (grep for '^IO_ACX') ***** */
++
++/* Values for IO_ACX_INT_TRIG register: */
++/* inform hw that rxdesc in queue needs processing */
++#define INT_TRIG_RXPRC 0x08
++/* inform hw that txdesc in queue needs processing */
++#define INT_TRIG_TXPRC 0x04
++/* ack that we received info from info mailbox */
++#define INT_TRIG_INFOACK 0x02
++/* inform hw that we have filled command mailbox */
++#define INT_TRIG_CMD 0x01
++
++struct txhostdesc {
++ acx_ptr data_phy; /* 0x00 [u8 *] */
++ u16 data_offset; /* 0x04 */
++ u16 reserved; /* 0x06 */
++ u16 Ctl_16; /* 16bit value, endianness!! */
++ u16 length; /* 0x0a */
++ acx_ptr desc_phy_next; /* 0x0c [txhostdesc *] */
++ acx_ptr pNext; /* 0x10 [txhostdesc *] */
++ u32 Status; /* 0x14, unused on Tx */
++/* From here on you can use this area as you want (variable length, too!) */
++ u8 *data;
++} ACX_PACKED;
++
++struct rxhostdesc {
++ acx_ptr data_phy; /* 0x00 [rxbuffer_t *] */
++ u16 data_offset; /* 0x04 */
++ u16 reserved; /* 0x06 */
++ u16 Ctl_16; /* 0x08; 16bit value, endianness!! */
++ u16 length; /* 0x0a */
++ acx_ptr desc_phy_next; /* 0x0c [rxhostdesc_t *] */
++ acx_ptr pNext; /* 0x10 [rxhostdesc_t *] */
++ u32 Status; /* 0x14 */
++/* From here on you can use this area as you want (variable length, too!) */
++ rxbuffer_t *data;
++} ACX_PACKED;
++
++#endif /* ACX_PCI */
++
++/***********************************************************************
++** USB structures and constants
++*/
++#ifdef ACX_USB
++
++/* Used for usb_txbuffer.desc field */
++#define USB_TXBUF_TXDESC 0xA
++/* Size of header (everything up to data[]) */
++#define USB_TXBUF_HDRSIZE 14
++typedef struct usb_txbuffer {
++ u16 desc;
++ u16 mpdu_len;
++ u8 queue_index;
++ u8 rate;
++ u32 hostdata;
++ u8 ctrl1;
++ u8 ctrl2;
++ u16 data_len;
++ /* wlan packet content is placed here: */
++ u8 data[WLAN_A4FR_MAXLEN_WEP_FCS];
++} ACX_PACKED usb_txbuffer_t;
++
++/* USB returns either rx packets (see rxbuffer) or
++** these "tx status" structs: */
++typedef struct usb_txstatus {
++ u16 mac_cnt_rcvd; /* only 12 bits are len! (0xfff) */
++ u8 queue_index;
++ u8 mac_status; /* seen 0x20 on tx failure */
++ u32 hostdata;
++ u8 rate;
++ u8 ack_failures;
++ u8 rts_failures;
++ u8 rts_ok;
++} ACX_PACKED usb_txstatus_t;
++
++typedef struct usb_tx {
++ unsigned busy:1;
++ struct urb *urb;
++ acx_device_t *adev;
++ /* actual USB bulk output data block is here: */
++ usb_txbuffer_t bulkout;
++} usb_tx_t;
++
++struct usb_rx_plain {
++ unsigned busy:1;
++ struct urb *urb;
++ acx_device_t *adev;
++ rxbuffer_t bulkin;
++};
++
++typedef struct usb_rx {
++ unsigned busy:1;
++ struct urb *urb;
++ acx_device_t *adev;
++ rxbuffer_t bulkin;
++ /* Make entire structure 4k. Report if it breaks something. */
++ u8 padding[4*1024 - sizeof(struct usb_rx_plain)];
++} usb_rx_t;
++#endif /* ACX_USB */
++
++
++/* Config Option structs */
++
++typedef struct co_antennas {
++ u8 type;
++ u8 len;
++ u8 list[2];
++} ACX_PACKED co_antennas_t;
++
++typedef struct co_powerlevels {
++ u8 type;
++ u8 len;
++ u16 list[8];
++} ACX_PACKED co_powerlevels_t;
++
++typedef struct co_datarates {
++ u8 type;
++ u8 len;
++ u8 list[8];
++} ACX_PACKED co_datarates_t;
++
++typedef struct co_domains {
++ u8 type;
++ u8 len;
++ u8 list[6];
++} ACX_PACKED co_domains_t;
++
++typedef struct co_product_id {
++ u8 type;
++ u8 len;
++ u8 list[128];
++} ACX_PACKED co_product_id_t;
++
++typedef struct co_manuf_id {
++ u8 type;
++ u8 len;
++ u8 list[128];
++} ACX_PACKED co_manuf_t;
++
++typedef struct co_fixed {
++ char NVSv[8];
++/* u16 NVS_vendor_offs; ACX111-only */
++/* u16 unknown; ACX111-only */
++ u8 MAC[6]; /* ACX100-only */
++ u16 probe_delay; /* ACX100-only */
++ u32 eof_memory;
++ u8 dot11CCAModes;
++ u8 dot11Diversity;
++ u8 dot11ShortPreambleOption;
++ u8 dot11PBCCOption;
++ u8 dot11ChannelAgility;
++ u8 dot11PhyType; /* FIXME: does 802.11 call it "dot11PHYType"? */
++ u8 dot11TempType;
++ u8 table_count;
++} ACX_PACKED co_fixed_t;
++
++typedef struct acx111_ie_configoption {
++ u16 type;
++ u16 len;
++/* Do not access below members directly, they are in fact variable length */
++ co_fixed_t fixed;
++ co_antennas_t antennas;
++ co_powerlevels_t power_levels;
++ co_datarates_t data_rates;
++ co_domains_t domains;
++ co_product_id_t product_id;
++ co_manuf_t manufacturer;
++ u8 _padding[4];
++} ACX_PACKED acx111_ie_configoption_t;
++
++
++/***********************************************************************
++** Main acx per-device data structure
++*/
++#define ACX_STATE_FW_LOADED 0x01
++#define ACX_STATE_IFACE_UP 0x02
++
++/* MAC mode (BSS type) defines
++ * Note that they shouldn't be redefined, since they are also used
++ * during communication with firmware */
++#define ACX_MODE_0_ADHOC 0
++#define ACX_MODE_1_UNUSED 1
++#define ACX_MODE_2_STA 2
++#define ACX_MODE_3_AP 3
++/* These are our own inventions. Sending these to firmware
++** makes it stop emitting beacons, which is exactly what we want
++** for these modes */
++#define ACX_MODE_MONITOR 0xfe
++#define ACX_MODE_OFF 0xff
++/* 'Submode': identifies exact status of ADHOC/STA host */
++#define ACX_STATUS_0_STOPPED 0
++#define ACX_STATUS_1_SCANNING 1
++#define ACX_STATUS_2_WAIT_AUTH 2
++#define ACX_STATUS_3_AUTHENTICATED 3
++#define ACX_STATUS_4_ASSOCIATED 4
++
++/* FIXME: this should be named something like struct acx_priv (typedef'd to
++ * acx_priv_t) */
++
++/* non-firmware struct, no packing necessary */
++struct acx_device {
++ /* most frequent accesses first (dereferencing and cache line!) */
++
++ /*** Locking ***/
++ /* FIXME: try to convert semaphore to more efficient mutex according
++ to Ingo Molnar's docs (but not before driver is in mainline or
++ pre-mutex Linux 2.6.10 is very outdated). */
++ struct semaphore sem;
++ spinlock_t lock;
++#if defined(PARANOID_LOCKING) /* Lock debugging */
++ const char *last_sem;
++ const char *last_lock;
++ unsigned long sem_time;
++ unsigned long lock_time;
++#endif
++#ifdef ACX_MEM
++ spinlock_t txbuf_lock;
++#endif
++
++ /*** Linux network device ***/
++ struct net_device *ndev; /* pointer to linux netdevice */
++
++ /*** Device statistics ***/
++ struct net_device_stats stats; /* net device statistics */
++#ifdef WIRELESS_EXT
++ struct iw_statistics wstats; /* wireless statistics */
++#endif
++ /*** Power managment ***/
++ struct pm_dev *pm; /* PM crap */
++
++ /*** Management timer ***/
++ struct timer_list mgmt_timer;
++
++ /*** Hardware identification ***/
++ const char *chip_name;
++ u8 dev_type;
++ u8 chip_type;
++ u8 form_factor;
++ u8 radio_type;
++ u8 eeprom_version;
++
++ /*** Config retrieved from EEPROM ***/
++ char cfgopt_NVSv[8];
++ u16 cfgopt_NVS_vendor_offs;
++ u8 cfgopt_MAC[6];
++ u16 cfgopt_probe_delay;
++ u32 cfgopt_eof_memory;
++ u8 cfgopt_dot11CCAModes;
++ u8 cfgopt_dot11Diversity;
++ u8 cfgopt_dot11ShortPreambleOption;
++ u8 cfgopt_dot11PBCCOption;
++ u8 cfgopt_dot11ChannelAgility;
++ u8 cfgopt_dot11PhyType;
++ u8 cfgopt_dot11TempType;
++ co_antennas_t cfgopt_antennas;
++ co_powerlevels_t cfgopt_power_levels;
++ co_datarates_t cfgopt_data_rates;
++ co_domains_t cfgopt_domains;
++ co_product_id_t cfgopt_product_id;
++ co_manuf_t cfgopt_manufacturer;
++
++ /*** Firmware identification ***/
++ char firmware_version[FW_ID_SIZE+1];
++ u32 firmware_numver;
++ u32 firmware_id;
++ const u16 *ie_len;
++ const u16 *ie_len_dot11;
++
++ /*** Device state ***/
++ u16 dev_state_mask;
++ u8 led_power; /* power LED status */
++ u32 get_mask; /* mask of settings to fetch from the card */
++ u32 set_mask; /* mask of settings to write to the card */
++
++ /* Barely used in USB case */
++ u16 irq_status;
++
++ u8 after_interrupt_jobs; /* mini job list for doing actions after an interrupt occurred */
++ WORK_STRUCT after_interrupt_task; /* our task for after interrupt actions */
++
++ /*** scanning ***/
++ u16 scan_count; /* number of times to do channel scan */
++ u8 scan_mode; /* 0 == active, 1 == passive, 2 == background */
++ u8 scan_rate;
++ u16 scan_duration;
++ u16 scan_probe_delay;
++#if WIRELESS_EXT > 15
++ struct iw_spy_data spy_data; /* FIXME: needs to be implemented! */
++#endif
++
++ /*** Wireless network settings ***/
++ /* copy of the device address (ifconfig hw ether) that we actually use
++ ** for 802.11; copied over from the network device's MAC address
++ ** (ifconfig) when it makes sense only */
++ u8 dev_addr[MAX_ADDR_LEN];
++ u8 bssid[ETH_ALEN]; /* the BSSID after having joined */
++ u8 ap[ETH_ALEN]; /* The AP we want, FF:FF:FF:FF:FF:FF is any */
++ u16 aid; /* The Association ID sent from the AP / last used AID if we're an AP */
++ u16 mode; /* mode from iwconfig */
++ int monitor_type; /* ARPHRD_IEEE80211 or ARPHRD_IEEE80211_PRISM */
++ u16 status; /* 802.11 association status */
++ u8 essid_active; /* specific ESSID active, or select any? */
++ u8 essid_len; /* to avoid dozens of strlen() */
++ /* INCLUDES \0 termination for easy printf - but many places
++ ** simply want the string data memcpy'd plus a length indicator!
++ ** Keep that in mind... */
++ char essid[IW_ESSID_MAX_SIZE+1];
++ /* essid we are going to use for association, in case of "essid 'any'"
++ ** and in case of hidden ESSID (use configured ESSID then) */
++ char essid_for_assoc[IW_ESSID_MAX_SIZE+1];
++ char nick[IW_ESSID_MAX_SIZE+1]; /* see essid! */
++ u8 channel;
++ u8 reg_dom_id; /* reg domain setting */
++ u16 reg_dom_chanmask;
++ u16 auth_or_assoc_retries;
++ u16 scan_retries;
++ unsigned long scan_start; /* YES, jiffies is defined as "unsigned long" */
++
++ /* stations known to us (if we're an ap) */
++ client_t sta_list[32]; /* tab is larger than list, so that */
++ client_t *sta_hash_tab[64]; /* hash collisions are not likely */
++ client_t *ap_client; /* this one is our AP (STA mode only) */
++
++ int dup_count;
++ int nondup_count;
++ unsigned long dup_msg_expiry;
++ u16 last_seq_ctrl; /* duplicate packet detection */
++
++ /* 802.11 power save mode */
++ u8 ps_wakeup_cfg;
++ u8 ps_listen_interval;
++ u8 ps_options;
++ u8 ps_hangover_period;
++ u32 ps_enhanced_transition_time;
++ u32 ps_beacon_rx_time;
++
++ /*** PHY settings ***/
++ u8 fallback_threshold;
++ u8 stepup_threshold;
++ u16 rate_basic;
++ u16 rate_oper;
++ u16 rate_bcast;
++ u16 rate_bcast100;
++ u8 rate_auto; /* false if "iwconfig rate N" (WITHOUT 'auto'!) */
++ u8 preamble_mode; /* 0 == Long Preamble, 1 == Short, 2 == Auto */
++ u8 preamble_cur;
++
++ u8 tx_disabled;
++ u8 tx_level_dbm;
++ /* u8 tx_level_val; */
++ /* u8 tx_level_auto; whether to do automatic power adjustment */
++
++ unsigned long recalib_time_last_success;
++ unsigned long recalib_time_last_attempt;
++ int recalib_failure_count;
++ int recalib_msg_ratelimit;
++ int retry_errors_msg_ratelimit;
++
++ unsigned long brange_time_last_state_change; /* time the power LED was last changed */
++ u8 brange_last_state; /* last state of the LED */
++ u8 brange_max_quality; /* maximum quality that equates to full speed */
++
++ u8 sensitivity;
++ u8 antenna; /* antenna settings */
++ u8 ed_threshold; /* energy detect threshold */
++ u8 cca; /* clear channel assessment */
++
++ u16 rts_threshold;
++ u16 frag_threshold;
++ u32 short_retry;
++ u32 long_retry;
++ u16 msdu_lifetime;
++ u16 listen_interval; /* given in units of beacon interval */
++ u32 beacon_interval;
++
++ u16 capabilities;
++ u8 rate_supported_len;
++ u8 rate_supported[13];
++
++ /*** Encryption settings (WEP) ***/
++ u32 auth_alg; /* used in transmit_authen1 */
++ u8 wep_enabled;
++ u8 wep_restricted;
++ u8 wep_current_index;
++ wep_key_t wep_keys[DOT11_MAX_DEFAULT_WEP_KEYS]; /* the default WEP keys */
++ key_struct_t wep_key_struct[10];
++
++ /*** Unknown ***/
++ u8 dtim_interval;
++
++#ifdef ACX_MEM
++ u32 acx_txbuf_start;
++ int acx_txbuf_numblocks;
++ u32 acx_txbuf_free; /* addr of head of free list */
++ int acx_txbuf_blocks_free; /* how many are still open */
++ queueindicator_t *acx_queue_indicator;
++#endif
++
++ /*** Card Rx/Tx management ***/
++ u16 rx_config_1;
++ u16 rx_config_2;
++ u16 memblocksize;
++ unsigned int tx_free;
++ unsigned int tx_head; /* keep as close as possible to Tx stuff below (cache line) */
++ u16 phy_header_len;
++
++/*************************************************************************
++ *** PCI/USB/... must be last or else hw agnostic code breaks horribly ***
++ *************************************************************************/
++
++ /* hack to let common code compile. FIXME */
++ dma_addr_t rxhostdesc_startphy;
++
++ /*** PCI stuff ***/
++#if defined(ACX_PCI) || defined(ACX_MEM)
++ /* pointers to tx buffers, tx host descriptors (in host memory)
++ ** and tx descs in device memory */
++ unsigned int tx_tail;
++ u8 *txbuf_start;
++ txhostdesc_t *txhostdesc_start;
++ txdesc_t *txdesc_start; /* points to PCI-mapped memory */
++ dma_addr_t txbuf_startphy;
++ dma_addr_t txhostdesc_startphy;
++ /* sizes of above host memory areas */
++ unsigned int txbuf_area_size;
++ unsigned int txhostdesc_area_size;
++
++ unsigned int txdesc_size; /* size of txdesc; ACX111 = ACX100 + 4 */
++ client_t *txc[TX_CNT];
++ u16 txr[TX_CNT];
++
++ /* same for rx */
++ unsigned int rx_tail;
++ rxbuffer_t *rxbuf_start;
++ rxhostdesc_t *rxhostdesc_start;
++ rxdesc_t *rxdesc_start;
++ /* physical addresses of above host memory areas */
++ dma_addr_t rxbuf_startphy;
++ /* dma_addr_t rxhostdesc_startphy; */
++ unsigned int rxbuf_area_size;
++ unsigned int rxhostdesc_area_size;
++
++ u8 need_radio_fw;
++ u8 irqs_active; /* whether irq sending is activated */
++
++ const u16 *io; /* points to ACX100 or ACX111 PCI I/O register address set */
++
++#ifdef ACX_PCI
++ struct pci_dev *pdev;
++#endif
++#ifdef ACX_MEM
++ struct device *dev;
++#endif
++
++#ifdef ACX_PCI
++ unsigned long membase;
++#endif
++#ifdef ACX_MEM
++ volatile u32 *membase;
++#endif
++ unsigned long membase2;
++#ifdef ACX_PCI
++ void __iomem *iobase;
++#endif
++#ifdef ACX_MEM
++ volatile u32 *iobase;
++#endif
++ void __iomem *iobase2;
++ /* command interface */
++ u8 __iomem *cmd_area;
++ u8 __iomem *info_area;
++
++ u16 irq_mask; /* interrupt types to mask out (not wanted) with many IRQs activated */
++ u16 irq_mask_off; /* interrupt types to mask out (not wanted) with IRQs off */
++ unsigned int irq_loops_this_jiffy;
++ unsigned long irq_last_jiffies;
++#endif
++
++ /*** USB stuff ***/
++#ifdef ACX_USB
++ struct usb_device *usbdev;
++
++ rxbuffer_t rxtruncbuf;
++
++ usb_tx_t *usb_tx;
++ usb_rx_t *usb_rx;
++
++ int bulkinep; /* bulk-in endpoint */
++ int bulkoutep; /* bulk-out endpoint */
++ int rxtruncsize;
++#endif
++
++};
++
++static inline acx_device_t*
++ndev2adev(struct net_device *ndev)
++{
++ return netdev_priv(ndev);
++}
++
++
++/* For use with ACX1xx_IE_RXCONFIG */
++/* bit description
++ * 13 include additional header (length etc.) *required*
++ * struct is defined in 'struct rxbuffer'
++ * is this bit acx100 only? does acx111 always put the header,
++ * and bit setting is irrelevant? --vda
++ * 10 receive frames only with SSID used in last join cmd
++ * 9 discard broadcast
++ * 8 receive packets for multicast address 1
++ * 7 receive packets for multicast address 0
++ * 6 discard all multicast packets
++ * 5 discard frames from foreign BSSID
++ * 4 discard frames with foreign destination MAC address
++ * 3 promiscuous mode (receive ALL frames, disable filter)
++ * 2 include FCS
++ * 1 include phy header
++ * 0 ???
++ */
++#define RX_CFG1_INCLUDE_RXBUF_HDR 0x2000 /* ACX100 only */
++#define RX_CFG1_FILTER_SSID 0x0400
++#define RX_CFG1_FILTER_BCAST 0x0200
++#define RX_CFG1_RCV_MC_ADDR1 0x0100
++#define RX_CFG1_RCV_MC_ADDR0 0x0080
++#define RX_CFG1_FILTER_ALL_MULTI 0x0040
++#define RX_CFG1_FILTER_BSSID 0x0020
++#define RX_CFG1_FILTER_MAC 0x0010
++#define RX_CFG1_RCV_PROMISCUOUS 0x0008
++#define RX_CFG1_INCLUDE_FCS 0x0004
++#define RX_CFG1_INCLUDE_PHY_HDR (WANT_PHY_HDR ? 0x0002 : 0)
++/* bit description
++ * 11 receive association requests etc.
++ * 10 receive authentication frames
++ * 9 receive beacon frames
++ * 8 receive contention free packets
++ * 7 receive control frames
++ * 6 receive data frames
++ * 5 receive broken frames
++ * 4 receive management frames
++ * 3 receive probe requests
++ * 2 receive probe responses
++ * 1 receive RTS/CTS/ACK frames
++ * 0 receive other
++ */
++#define RX_CFG2_RCV_ASSOC_REQ 0x0800
++#define RX_CFG2_RCV_AUTH_FRAMES 0x0400
++#define RX_CFG2_RCV_BEACON_FRAMES 0x0200
++#define RX_CFG2_RCV_CONTENTION_FREE 0x0100
++#define RX_CFG2_RCV_CTRL_FRAMES 0x0080
++#define RX_CFG2_RCV_DATA_FRAMES 0x0040
++#define RX_CFG2_RCV_BROKEN_FRAMES 0x0020
++#define RX_CFG2_RCV_MGMT_FRAMES 0x0010
++#define RX_CFG2_RCV_PROBE_REQ 0x0008
++#define RX_CFG2_RCV_PROBE_RESP 0x0004
++#define RX_CFG2_RCV_ACK_FRAMES 0x0002
++#define RX_CFG2_RCV_OTHER 0x0001
++
++/* For use with ACX1xx_IE_FEATURE_CONFIG */
++#define FEATURE1_80MHZ_CLOCK 0x00000040L
++#define FEATURE1_4X 0x00000020L
++#define FEATURE1_LOW_RX 0x00000008L
++#define FEATURE1_EXTRA_LOW_RX 0x00000001L
++
++#define FEATURE2_SNIFFER 0x00000080L
++#define FEATURE2_NO_TXCRYPT 0x00000001L
++
++/*-- get and set mask values --*/
++#define GETSET_LED_POWER 0x00000001L
++#define GETSET_STATION_ID 0x00000002L
++#define SET_TEMPLATES 0x00000004L
++#define SET_STA_LIST 0x00000008L
++#define GETSET_TX 0x00000010L
++#define GETSET_RX 0x00000020L
++#define SET_RXCONFIG 0x00000040L
++#define GETSET_ANTENNA 0x00000080L
++#define GETSET_SENSITIVITY 0x00000100L
++#define GETSET_TXPOWER 0x00000200L
++#define GETSET_ED_THRESH 0x00000400L
++#define GETSET_CCA 0x00000800L
++#define GETSET_POWER_80211 0x00001000L
++#define GETSET_RETRY 0x00002000L
++#define GETSET_REG_DOMAIN 0x00004000L
++#define GETSET_CHANNEL 0x00008000L
++/* Used when ESSID changes etc and we need to scan for AP anew */
++#define GETSET_RESCAN 0x00010000L
++#define GETSET_MODE 0x00020000L
++#define GETSET_WEP 0x00040000L
++#define SET_WEP_OPTIONS 0x00080000L
++#define SET_MSDU_LIFETIME 0x00100000L
++#define SET_RATE_FALLBACK 0x00200000L
++
++/* keep in sync with the above */
++#define GETSET_ALL (0 \
++/* GETSET_LED_POWER */ | 0x00000001L \
++/* GETSET_STATION_ID */ | 0x00000002L \
++/* SET_TEMPLATES */ | 0x00000004L \
++/* SET_STA_LIST */ | 0x00000008L \
++/* GETSET_TX */ | 0x00000010L \
++/* GETSET_RX */ | 0x00000020L \
++/* SET_RXCONFIG */ | 0x00000040L \
++/* GETSET_ANTENNA */ | 0x00000080L \
++/* GETSET_SENSITIVITY */| 0x00000100L \
++/* GETSET_TXPOWER */ | 0x00000200L \
++/* GETSET_ED_THRESH */ | 0x00000400L \
++/* GETSET_CCA */ | 0x00000800L \
++/* GETSET_POWER_80211 */| 0x00001000L \
++/* GETSET_RETRY */ | 0x00002000L \
++/* GETSET_REG_DOMAIN */ | 0x00004000L \
++/* GETSET_CHANNEL */ | 0x00008000L \
++/* GETSET_RESCAN */ | 0x00010000L \
++/* GETSET_MODE */ | 0x00020000L \
++/* GETSET_WEP */ | 0x00040000L \
++/* SET_WEP_OPTIONS */ | 0x00080000L \
++/* SET_MSDU_LIFETIME */ | 0x00100000L \
++/* SET_RATE_FALLBACK */ | 0x00200000L \
++ )
++
++
++/***********************************************************************
++** Firmware loading
++*/
++#include <linux/firmware.h> /* request_firmware() */
++#include <linux/pci.h> /* struct pci_device */
++
++
++/***********************************************************************
++*/
++typedef struct acx100_ie_memblocksize {
++ u16 type;
++ u16 len;
++ u16 size;
++} ACX_PACKED acx100_ie_memblocksize_t;
++
++typedef struct acx100_ie_queueconfig {
++ u16 type;
++ u16 len;
++ u32 AreaSize;
++ u32 RxQueueStart;
++ u8 QueueOptions;
++ u8 NumTxQueues;
++ u8 NumRxDesc; /* for USB only */
++ u8 pad1;
++ u32 QueueEnd;
++ u32 HostQueueEnd; /* QueueEnd2 */
++ u32 TxQueueStart;
++ u8 TxQueuePri;
++ u8 NumTxDesc;
++ u16 pad2;
++} ACX_PACKED acx100_ie_queueconfig_t;
++
++typedef struct acx111_ie_queueconfig {
++ u16 type;
++ u16 len;
++ u32 tx_memory_block_address;
++ u32 rx_memory_block_address;
++ u32 rx1_queue_address;
++ u32 reserved1;
++ u32 tx1_queue_address;
++ u8 tx1_attributes;
++ u16 reserved2;
++ u8 reserved3;
++} ACX_PACKED acx111_ie_queueconfig_t;
++
++typedef struct acx100_ie_memconfigoption {
++ u16 type;
++ u16 len;
++ u32 DMA_config;
++ acx_ptr pRxHostDesc;
++ u32 rx_mem;
++ u32 tx_mem;
++ u16 RxBlockNum;
++ u16 TxBlockNum;
++} ACX_PACKED acx100_ie_memconfigoption_t;
++
++typedef struct acx111_ie_memoryconfig {
++ u16 type;
++ u16 len;
++ u16 no_of_stations;
++ u16 memory_block_size;
++ u8 tx_rx_memory_block_allocation;
++ u8 count_rx_queues;
++ u8 count_tx_queues;
++ u8 options;
++ u8 fragmentation;
++ u16 reserved1;
++ u8 reserved2;
++
++ /* start of rx1 block */
++ u8 rx_queue1_count_descs;
++ u8 rx_queue1_reserved1;
++ u8 rx_queue1_type; /* must be set to 7 */
++ u8 rx_queue1_prio; /* must be set to 0 */
++ acx_ptr rx_queue1_host_rx_start;
++ /* end of rx1 block */
++
++ /* start of tx1 block */
++ u8 tx_queue1_count_descs;
++ u8 tx_queue1_reserved1;
++ u8 tx_queue1_reserved2;
++ u8 tx_queue1_attributes;
++ /* end of tx1 block */
++} ACX_PACKED acx111_ie_memoryconfig_t;
++
++typedef struct acx_ie_memmap {
++ u16 type;
++ u16 len;
++ u32 CodeStart;
++ u32 CodeEnd;
++ u32 WEPCacheStart;
++ u32 WEPCacheEnd;
++ u32 PacketTemplateStart;
++ u32 PacketTemplateEnd;
++ u32 QueueStart;
++ u32 QueueEnd;
++ u32 PoolStart;
++ u32 PoolEnd;
++} ACX_PACKED acx_ie_memmap_t;
++
++typedef struct acx111_ie_feature_config {
++ u16 type;
++ u16 len;
++ u32 feature_options;
++ u32 data_flow_options;
++} ACX_PACKED acx111_ie_feature_config_t;
++
++typedef struct acx111_ie_tx_level {
++ u16 type;
++ u16 len;
++ u8 level;
++} ACX_PACKED acx111_ie_tx_level_t;
++
++#define PS_CFG_ENABLE 0x80
++#define PS_CFG_PENDING 0x40 /* status flag when entering PS */
++#define PS_CFG_WAKEUP_MODE_MASK 0x07
++#define PS_CFG_WAKEUP_BY_HOST 0x03
++#define PS_CFG_WAKEUP_EACH_ITVL 0x02
++#define PS_CFG_WAKEUP_ON_DTIM 0x01
++#define PS_CFG_WAKEUP_ALL_BEAC 0x00
++
++/* Enhanced PS mode: sleep until Rx Beacon w/ the STA's AID bit set
++** in the TIM; newer firmwares only(?) */
++#define PS_OPT_ENA_ENHANCED_PS 0x04
++#define PS_OPT_TX_PSPOLL 0x02 /* send PSPoll frame to fetch waiting frames from AP (on frame with matching AID) */
++#define PS_OPT_STILL_RCV_BCASTS 0x01
++
++typedef struct acx100_ie_powersave {
++ u16 type;
++ u16 len;
++ u8 wakeup_cfg;
++ u8 listen_interval; /* for EACH_ITVL: wake up every "beacon units" interval */
++ u8 options;
++ u8 hangover_period; /* remaining wake time after Tx MPDU w/ PS bit, in values of 1/1024 seconds */
++ u16 enhanced_ps_transition_time; /* rem. wake time for Enh. PS */
++} ACX_PACKED acx100_ie_powersave_t;
++
++typedef struct acx111_ie_powersave {
++ u16 type;
++ u16 len;
++ u8 wakeup_cfg;
++ u8 listen_interval; /* for EACH_ITVL: wake up every "beacon units" interval */
++ u8 options;
++ u8 hangover_period; /* remaining wake time after Tx MPDU w/ PS bit, in values of 1/1024 seconds */
++ u32 beacon_rx_time;
++ u32 enhanced_ps_transition_time; /* rem. wake time for Enh. PS */
++} ACX_PACKED acx111_ie_powersave_t;
++
++
++/***********************************************************************
++** Commands and template structures
++*/
++
++/*
++** SCAN command structure
++**
++** even though acx100 scan rates match RATE100 constants,
++** acx111 ones do not match! Therefore we do not use RATE100 #defines */
++#define ACX_SCAN_RATE_1 10
++#define ACX_SCAN_RATE_2 20
++#define ACX_SCAN_RATE_5 55
++#define ACX_SCAN_RATE_11 110
++#define ACX_SCAN_RATE_22 220
++#define ACX_SCAN_RATE_PBCC 0x80 /* OR with this if needed */
++#define ACX_SCAN_OPT_ACTIVE 0x00 /* a bit mask */
++#define ACX_SCAN_OPT_PASSIVE 0x01
++/* Background scan: we go into Power Save mode (by transmitting
++** NULL data frame to AP with the power mgmt bit set), do the scan,
++** and then exit Power Save mode. A plus is that AP buffers frames
++** for us while we do background scan. Thus we avoid frame losses.
++** Background scan can be active or passive, just like normal one */
++#define ACX_SCAN_OPT_BACKGROUND 0x02
++typedef struct acx100_scan {
++ u16 count; /* number of scans to do, 0xffff == continuous */
++ u16 start_chan;
++ u16 flags; /* channel list mask; 0x8000 == all channels? */
++ u8 max_rate; /* max. probe rate */
++ u8 options; /* bit mask, see defines above */
++ u16 chan_duration;
++ u16 max_probe_delay;
++} ACX_PACKED acx100_scan_t; /* length 0xc */
++
++#define ACX111_SCAN_RATE_6 0x0B
++#define ACX111_SCAN_RATE_9 0x0F
++#define ACX111_SCAN_RATE_12 0x0A
++#define ACX111_SCAN_RATE_18 0x0E
++#define ACX111_SCAN_RATE_24 0x09
++#define ACX111_SCAN_RATE_36 0x0D
++#define ACX111_SCAN_RATE_48 0x08
++#define ACX111_SCAN_RATE_54 0x0C
++#define ACX111_SCAN_OPT_5GHZ 0x04 /* else 2.4GHZ */
++#define ACX111_SCAN_MOD_SHORTPRE 0x01 /* you can combine SHORTPRE and PBCC */
++#define ACX111_SCAN_MOD_PBCC 0x80
++#define ACX111_SCAN_MOD_OFDM 0x40
++typedef struct acx111_scan {
++ u16 count; /* number of scans to do */
++ u8 channel_list_select; /* 0: scan all channels, 1: from chan_list only */
++ u16 reserved1;
++ u8 reserved2;
++ u8 rate; /* rate for probe requests (if active scan) */
++ u8 options; /* bit mask, see defines above */
++ u16 chan_duration; /* min time to wait for reply on one channel (in TU) */
++ /* (active scan only) (802.11 section 11.1.3.2.2) */
++ u16 max_probe_delay; /* max time to wait for reply on one channel (active scan) */
++ /* time to listen on a channel (passive scan) */
++ u8 modulation;
++ u8 channel_list[26]; /* bits 7:0 first byte: channels 8:1 */
++ /* bits 7:0 second byte: channels 16:9 */
++ /* 26 bytes is enough to cover 802.11a */
++} ACX_PACKED acx111_scan_t;
++
++
++/*
++** Radio calibration command structure
++*/
++typedef struct acx111_cmd_radiocalib {
++/* 0x80000000 == automatic calibration by firmware, according to interval;
++ * bits 0..3: select calibration methods to go through:
++ * calib based on DC, AfeDC, Tx mismatch, Tx equilization */
++ u32 methods;
++ u32 interval;
++} ACX_PACKED acx111_cmd_radiocalib_t;
++
++
++/*
++** Packet template structures
++**
++** Packet templates store contents of Beacon, Probe response, Probe request,
++** Null data frame, and TIM data frame. Firmware automatically transmits
++** contents of template at appropriate time:
++** - Beacon: when configured as AP or Ad-hoc
++** - Probe response: when configured as AP or Ad-hoc, whenever
++** a Probe request frame is received
++** - Probe request: when host issues SCAN command (active)
++** - Null data frame: when entering 802.11 power save mode
++** - TIM data: at the end of Beacon frames (if no TIM template
++** is configured, then transmits default TIM)
++** NB:
++** - size field must be set to size of actual template
++** (NOT sizeof(struct) - templates are variable in length),
++** size field is not itself counted.
++** - members flagged with an asterisk must be initialized with host,
++** rest must be zero filled.
++** - variable length fields shown only in comments */
++typedef struct acx_template_tim {
++ u16 size;
++ u8 tim_eid; /* 00 1 TIM IE ID * */
++ u8 len; /* 01 1 Length * */
++ u8 dtim_cnt; /* 02 1 DTIM Count */
++ u8 dtim_period; /* 03 1 DTIM Period */
++ u8 bitmap_ctrl; /* 04 1 Bitmap Control * (except bit0) */
++ /* 05 n Partial Virtual Bitmap * */
++ u8 variable[0x100 - 1-1-1-1-1];
++} ACX_PACKED acx_template_tim_t;
++
++typedef struct acx_template_probereq {
++ u16 size;
++ u16 fc; /* 00 2 fc * */
++ u16 dur; /* 02 2 Duration */
++ u8 da[6]; /* 04 6 Destination Address * */
++ u8 sa[6]; /* 0A 6 Source Address * */
++ u8 bssid[6]; /* 10 6 BSSID * */
++ u16 seq; /* 16 2 Sequence Control */
++ /* 18 n SSID * */
++ /* nn n Supported Rates * */
++ u8 variable[0x44 - 2-2-6-6-6-2];
++} ACX_PACKED acx_template_probereq_t;
++
++typedef struct acx_template_proberesp {
++ u16 size;
++ u16 fc; /* 00 2 fc * (bits [15:12] and [10:8] per 802.11 section 7.1.3.1) */
++ u16 dur; /* 02 2 Duration */
++ u8 da[6]; /* 04 6 Destination Address */
++ u8 sa[6]; /* 0A 6 Source Address */
++ u8 bssid[6]; /* 10 6 BSSID */
++ u16 seq; /* 16 2 Sequence Control */
++ u8 timestamp[8];/* 18 8 Timestamp */
++ u16 beacon_interval; /* 20 2 Beacon Interval * */
++ u16 cap; /* 22 2 Capability Information * */
++ /* 24 n SSID * */
++ /* nn n Supported Rates * */
++ /* nn 1 DS Parameter Set * */
++ u8 variable[0x54 - 2-2-6-6-6-2-8-2-2];
++} ACX_PACKED acx_template_proberesp_t;
++#define acx_template_beacon_t acx_template_proberesp_t
++#define acx_template_beacon acx_template_proberesp
++
++typedef struct acx_template_nullframe {
++ u16 size;
++ struct wlan_hdr_a3 hdr;
++} ACX_PACKED acx_template_nullframe_t;
++
++
++/*
++** JOIN command structure
++**
++** as opposed to acx100, acx111 dtim interval is AFTER rates_basic111.
++** NOTE: took me about an hour to get !@#$%^& packing right --> struct packing is eeeeevil... */
++typedef struct acx_joinbss {
++ u8 bssid[ETH_ALEN];
++ u16 beacon_interval;
++ union {
++ struct {
++ u8 dtim_interval;
++ u8 rates_basic;
++ u8 rates_supported;
++ /*
++ * ARM compiler doesn't pack correctly unless unions
++ * inside structures are multiples of 4 bytes. Ugh.
++ */
++ u8 genfrm_txrate; /* generated frame (bcn, proberesp, RTS, PSpoll) tx rate */
++ } ACX_PACKED acx100;
++ struct {
++ u16 rates_basic;
++ u8 dtim_interval;
++ u8 genfrm_txrate; /* generated frame (bcn, proberesp, RTS, PSpoll) tx rate */
++ } ACX_PACKED acx111;
++ /*
++ * ARM compiler doesn't pack correctly unles unions are aligned on
++ * 4 byte boundaries and are multiples of 4 bytes.
++ */
++ struct {
++ u8 d1;
++ u8 d2;
++ u8 d3;
++ u8 genfrm_txrate;
++ } ACX_PACKED txrate;
++ } ACX_PACKED u;
++ u8 genfrm_mod_pre; /* generated frame modulation/preamble:
++ ** bit7: PBCC, bit6: OFDM (else CCK/DQPSK/DBPSK)
++ ** bit5: short pre */
++ u8 macmode; /* BSS Type, must be one of ACX_MODE_xxx */
++ u8 channel;
++ u8 essid_len;
++ char essid[IW_ESSID_MAX_SIZE];
++} ACX_PACKED acx_joinbss_t;
++
++#define JOINBSS_RATES_1 0x01
++#define JOINBSS_RATES_2 0x02
++#define JOINBSS_RATES_5 0x04
++#define JOINBSS_RATES_11 0x08
++#define JOINBSS_RATES_22 0x10
++
++/* Looks like missing bits are used to indicate 11g rates!
++** (it follows from the fact that constants below match 1:1 to RATE111_nn)
++** This was actually seen! Look at that Assoc Request sent by acx111,
++** it _does_ contain 11g rates in basic set:
++01:30:20.070772 Beacon (xxx) [1.0* 2.0* 5.5* 11.0* 6.0* 9.0* 12.0* 18.0* 24.0* 36.0* 48.0* 54.0* Mbit] ESS CH: 1
++01:30:20.074425 Authentication (Open System)-1: Succesful
++01:30:20.076539 Authentication (Open System)-2:
++01:30:20.076620 Acknowledgment
++01:30:20.088546 Assoc Request (xxx) [1.0* 2.0* 5.5* 6.0* 9.0* 11.0* 12.0* 18.0* 24.0* 36.0* 48.0* 54.0* Mbit]
++01:30:20.122413 Assoc Response AID(1) :: Succesful
++01:30:20.122679 Acknowledgment
++01:30:20.173204 Beacon (xxx) [1.0* 2.0* 5.5* 11.0* 6.0* 9.0* 12.0* 18.0* 24.0* 36.0* 48.0* 54.0* Mbit] ESS CH: 1
++*/
++#define JOINBSS_RATES_BASIC111_1 0x0001
++#define JOINBSS_RATES_BASIC111_2 0x0002
++#define JOINBSS_RATES_BASIC111_5 0x0004
++#define JOINBSS_RATES_BASIC111_11 0x0020
++#define JOINBSS_RATES_BASIC111_22 0x0100
++
++
++/***********************************************************************
++*/
++typedef struct mem_read_write {
++ u16 addr;
++ u16 type; /* 0x0 int. RAM / 0xffff MAC reg. / 0x81 PHY RAM / 0x82 PHY reg.; or maybe it's actually 0x30 for MAC? Better verify it by writing and reading back and checking whether the value holds! */
++ u32 len;
++ u32 data;
++} ACX_PACKED mem_read_write_t;
++
++typedef struct firmware_image {
++ u32 chksum;
++ u32 size;
++ u8 data[1]; /* the byte array of the actual firmware... */
++} ACX_PACKED firmware_image_t;
++
++typedef struct acx_cmd_radioinit {
++ u32 offset;
++ u32 len;
++} ACX_PACKED acx_cmd_radioinit_t;
++
++typedef struct acx100_ie_wep_options {
++ u16 type;
++ u16 len;
++ u16 NumKeys; /* max # of keys */
++ u8 WEPOption; /* 0 == decrypt default key only, 1 == override decrypt */
++ u8 Pad; /* used only for acx111 */
++} ACX_PACKED acx100_ie_wep_options_t;
++
++typedef struct ie_dot11WEPDefaultKey {
++ u16 type;
++ u16 len;
++ u8 action;
++ u8 keySize;
++ u8 defaultKeyNum;
++ u8 key[29]; /* check this! was Key[19] */
++} ACX_PACKED ie_dot11WEPDefaultKey_t;
++
++typedef struct acx111WEPDefaultKey {
++ u8 MacAddr[ETH_ALEN];
++ u16 action; /* NOTE: this is a u16, NOT a u8!! */
++ u16 reserved;
++ u8 keySize;
++ u8 type;
++ u8 index;
++ u8 defaultKeyNum;
++ u8 counter[6];
++ u8 key[32]; /* up to 32 bytes (for TKIP!) */
++} ACX_PACKED acx111WEPDefaultKey_t;
++
++typedef struct ie_dot11WEPDefaultKeyID {
++ u16 type;
++ u16 len;
++ u8 KeyID;
++} ACX_PACKED ie_dot11WEPDefaultKeyID_t;
++
++typedef struct acx100_cmd_wep_mgmt {
++ u8 MacAddr[ETH_ALEN];
++ u16 Action;
++ u16 KeySize;
++ u8 Key[29]; /* 29*8 == 232bits == WEP256 */
++} ACX_PACKED acx100_cmd_wep_mgmt_t;
++
++typedef struct acx_ie_generic {
++ u16 type;
++ u16 len;
++ union {
++ /* Association ID IE: just a 16bit value: */
++ u16 aid;
++ /* generic member for quick implementation of commands */
++ u8 bytes[32];
++ } ACX_PACKED m;
++} ACX_PACKED acx_ie_generic_t;
++
++/***********************************************************************
++*/
++#define CHECK_SIZEOF(type,size) { \
++ extern void BUG_bad_size_for_##type(void); \
++ if (sizeof(type)!=(size)) BUG_bad_size_for_##type(); \
++}
++
++static inline void
++acx_struct_size_check(void)
++{
++ CHECK_SIZEOF(txdesc_t, 0x30);
++ CHECK_SIZEOF(acx100_ie_memconfigoption_t, 24);
++ CHECK_SIZEOF(acx100_ie_queueconfig_t, 0x20);
++ CHECK_SIZEOF(acx_joinbss_t, 0x30);
++ /* IEs need 4 bytes for (type,len) tuple */
++ CHECK_SIZEOF(acx111_ie_configoption_t, ACX111_IE_CONFIG_OPTIONS_LEN + 4);
++}
++
++
++/***********************************************************************
++** Global data
++*/
++extern const u8 acx_bitpos2ratebyte[];
++extern const u8 acx_bitpos2rate100[];
++
++extern const u8 acx_reg_domain_ids[];
++extern const char * const acx_reg_domain_strings[];
++enum {
++ acx_reg_domain_ids_len = 8
++};
++
++extern const struct iw_handler_def acx_ioctl_handler_def;
+Index: linux-2.6.23/drivers/net/wireless/acx/common.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23/drivers/net/wireless/acx/common.c 2008-01-20 21:13:40.000000000 +0000
+@@ -0,0 +1,7388 @@
++/***********************************************************************
++** Copyright (C) 2003 ACX100 Open Source Project
++**
++** The contents of this file are subject to the Mozilla Public
++** License Version 1.1 (the "License"); you may not use this file
++** except in compliance with the License. You may obtain a copy of
++** the License at http://www.mozilla.org/MPL/
++**
++** Software distributed under the License is distributed on an "AS
++** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
++** implied. See the License for the specific language governing
++** rights and limitations under the License.
++**
++** Alternatively, the contents of this file may be used under the
++** terms of the GNU Public License version 2 (the "GPL"), in which
++** case the provisions of the GPL are applicable instead of the
++** above. If you wish to allow the use of your version of this file
++** only under the terms of the GPL and not to allow others to use
++** your version of this file under the MPL, indicate your decision
++** by deleting the provisions above and replace them with the notice
++** and other provisions required by the GPL. If you do not delete
++** the provisions above, a recipient may use your version of this
++** file under either the MPL or the GPL.
++** ---------------------------------------------------------------------
++** Inquiries regarding the ACX100 Open Source Project can be
++** made directly to:
++**
++** acx100-users@lists.sf.net
++** http://acx100.sf.net
++** ---------------------------------------------------------------------
++*/
++
++#include <linux/version.h>
++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18)
++#include <linux/config.h>
++#endif
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/types.h>
++#include <linux/slab.h>
++#include <linux/delay.h>
++#include <linux/proc_fs.h>
++#include <linux/if_arp.h>
++#include <linux/rtnetlink.h>
++#include <linux/netdevice.h>
++#include <linux/etherdevice.h>
++#include <linux/wireless.h>
++#include <linux/pm.h>
++#include <linux/vmalloc.h>
++#include <net/iw_handler.h>
++
++#include "acx_hw.h"
++#include "acx.h"
++
++
++/***********************************************************************
++*/
++static client_t *acx_l_sta_list_alloc(acx_device_t *adev);
++static client_t *acx_l_sta_list_get_from_hash(acx_device_t *adev, const u8 *address);
++
++static int acx_l_process_data_frame_master(acx_device_t *adev, rxbuffer_t *rxbuf);
++static int acx_l_process_data_frame_client(acx_device_t *adev, rxbuffer_t *rxbuf);
++/* static int acx_l_process_NULL_frame(acx_device_t *adev, rxbuffer_t *rxbuf, int vala); */
++static int acx_l_process_mgmt_frame(acx_device_t *adev, rxbuffer_t *rxbuf);
++static void acx_l_process_disassoc_from_sta(acx_device_t *adev, const wlan_fr_disassoc_t *req);
++static void acx_l_process_disassoc_from_ap(acx_device_t *adev, const wlan_fr_disassoc_t *req);
++static void acx_l_process_deauth_from_sta(acx_device_t *adev, const wlan_fr_deauthen_t *req);
++static void acx_l_process_deauth_from_ap(acx_device_t *adev, const wlan_fr_deauthen_t *req);
++static int acx_l_process_probe_response(acx_device_t *adev, wlan_fr_proberesp_t *req, const rxbuffer_t *rxbuf);
++static int acx_l_process_assocresp(acx_device_t *adev, const wlan_fr_assocresp_t *req);
++static int acx_l_process_reassocresp(acx_device_t *adev, const wlan_fr_reassocresp_t *req);
++static int acx_l_process_authen(acx_device_t *adev, const wlan_fr_authen_t *req);
++static int acx_l_transmit_assocresp(acx_device_t *adev, const wlan_fr_assocreq_t *req);
++static int acx_l_transmit_reassocresp(acx_device_t *adev, const wlan_fr_reassocreq_t *req);
++static int acx_l_transmit_deauthen(acx_device_t *adev, const u8 *addr, u16 reason);
++static int acx_l_transmit_authen1(acx_device_t *adev);
++static int acx_l_transmit_authen2(acx_device_t *adev, const wlan_fr_authen_t *req, client_t *clt);
++static int acx_l_transmit_authen3(acx_device_t *adev, const wlan_fr_authen_t *req);
++static int acx_l_transmit_authen4(acx_device_t *adev, const wlan_fr_authen_t *req);
++static int acx_l_transmit_assoc_req(acx_device_t *adev);
++
++
++/***********************************************************************
++*/
++#if ACX_DEBUG
++unsigned int acx_debug /* will add __read_mostly later */ = ACX_DEFAULT_MSG;
++/* parameter is 'debug', corresponding var is acx_debug */
++module_param_named(debug, acx_debug, uint, 0);
++MODULE_PARM_DESC(debug, "Debug level mask (see L_xxx constants)");
++#endif
++
++#ifdef MODULE_LICENSE
++MODULE_LICENSE("Dual MPL/GPL");
++#endif
++/* USB had this: MODULE_AUTHOR("Martin Wawro <martin.wawro AT uni-dortmund.de>"); */
++MODULE_AUTHOR("ACX100 Open Source Driver development team");
++MODULE_DESCRIPTION("Driver for TI ACX1xx based wireless cards (CardBus/PCI/USB)");
++
++
++/***********************************************************************
++*/
++/* Probably a number of acx's intermediate buffers for USB transfers,
++** not to be confused with number of descriptors in tx/rx rings
++** (which are not directly accessible to host in USB devices) */
++#define USB_RX_CNT 10
++#define USB_TX_CNT 10
++
++
++/***********************************************************************
++*/
++
++/* minutes to wait until next radio recalibration: */
++#define RECALIB_PAUSE 5
++
++/* Please keep acx_reg_domain_ids_len in sync... */
++const u8 acx_reg_domain_ids[acx_reg_domain_ids_len] =
++ { 0x10, 0x20, 0x30, 0x31, 0x32, 0x40, 0x41, 0x51 };
++static const u16 reg_domain_channel_masks[acx_reg_domain_ids_len] =
++#ifdef ACX_ALLOW_ALLCHANNELS
++ { 0x3fff, 0x07ff, 0x1fff, 0x0600, 0x1e00, 0x2000, 0x3fff, 0x01fc };
++#else
++ { 0x07ff, 0x07ff, 0x1fff, 0x0600, 0x1e00, 0x2000, 0x3fff, 0x01fc };
++#endif
++const char * const
++acx_reg_domain_strings[] = {
++ /* 0 */ " 1-11 FCC (USA)",
++ /* 1 */ " 1-11 DOC/IC (Canada)",
++/* BTW: WLAN use in ETSI is regulated by ETSI standard EN 300 328-2 V1.1.2 */
++ /* 2 */ " 1-13 ETSI (Europe)",
++ /* 3 */ "10-11 Spain",
++ /* 4 */ "10-13 France",
++ /* 5 */ " 14 MKK (Japan)",
++ /* 6 */ " 1-14 MKK1",
++ /* 7 */ " 3-9 Israel (not all firmware versions)",
++ NULL /* needs to remain as last entry */
++};
++
++
++
++/***********************************************************************
++** Debugging support
++*/
++#ifdef PARANOID_LOCKING
++static unsigned max_lock_time;
++static unsigned max_sem_time;
++
++void
++acx_lock_unhold() { max_lock_time = 0; }
++void
++acx_sem_unhold() { max_sem_time = 0; }
++
++static inline const char*
++sanitize_str(const char *s)
++{
++ const char* t = strrchr(s, '/');
++ if (t) return t + 1;
++ return s;
++}
++
++void
++acx_lock_debug(acx_device_t *adev, const char* where)
++{
++ unsigned int count = 100*1000*1000;
++ where = sanitize_str(where);
++ while (--count) {
++ if (!spin_is_locked(&adev->lock)) break;
++ cpu_relax();
++ }
++ if (!count) {
++ printk(KERN_EMERG "LOCKUP: already taken at %s!\n", adev->last_lock);
++ BUG();
++ }
++ adev->last_lock = where;
++ rdtscl(adev->lock_time);
++}
++void
++acx_unlock_debug(acx_device_t *adev, const char* where)
++{
++#ifdef SMP
++ if (!spin_is_locked(&adev->lock)) {
++ where = sanitize_str(where);
++ printk(KERN_EMERG "STRAY UNLOCK at %s!\n", where);
++ BUG();
++ }
++#endif
++ if (acx_debug & L_LOCK) {
++ unsigned long diff;
++ rdtscl(diff);
++ diff -= adev->lock_time;
++ if (diff > max_lock_time) {
++ where = sanitize_str(where);
++ printk("max lock hold time %ld CPU ticks from %s "
++ "to %s\n", diff, adev->last_lock, where);
++ max_lock_time = diff;
++ }
++ }
++}
++void
++acx_down_debug(acx_device_t *adev, const char* where)
++{
++ int sem_count;
++ unsigned long timeout = jiffies + 5*HZ;
++
++ where = sanitize_str(where);
++
++ for (;;) {
++ sem_count = atomic_read(&adev->sem.count);
++ if (sem_count) break;
++ if (time_after(jiffies, timeout))
++ break;
++ msleep(5);
++ }
++ if (!sem_count) {
++ printk(KERN_EMERG "D STATE at %s! last sem at %s\n",
++ where, adev->last_sem);
++ dump_stack();
++ }
++ adev->last_sem = where;
++ adev->sem_time = jiffies;
++ down(&adev->sem);
++ if (acx_debug & L_LOCK) {
++ printk("%s: sem_down %d -> %d\n",
++ where, sem_count, atomic_read(&adev->sem.count));
++ }
++}
++void
++acx_up_debug(acx_device_t *adev, const char* where)
++{
++ int sem_count = atomic_read(&adev->sem.count);
++ if (sem_count) {
++ where = sanitize_str(where);
++ printk(KERN_EMERG "STRAY UP at %s! sem.count=%d\n", where, sem_count);
++ dump_stack();
++ }
++ if (acx_debug & L_LOCK) {
++ unsigned long diff = jiffies - adev->sem_time;
++ if (diff > max_sem_time) {
++ where = sanitize_str(where);
++ printk("max sem hold time %ld jiffies from %s "
++ "to %s\n", diff, adev->last_sem, where);
++ max_sem_time = diff;
++ }
++ }
++ up(&adev->sem);
++ if (acx_debug & L_LOCK) {
++ where = sanitize_str(where);
++ printk("%s: sem_up %d -> %d\n",
++ where, sem_count, atomic_read(&adev->sem.count));
++ }
++}
++#endif /* PARANOID_LOCKING */
++
++
++/***********************************************************************
++*/
++#if ACX_DEBUG > 1
++
++static int acx_debug_func_indent;
++#define DEBUG_TSC 0
++#define FUNC_INDENT_INCREMENT 2
++
++#if DEBUG_TSC
++#define TIMESTAMP(d) unsigned long d; rdtscl(d)
++#else
++#define TIMESTAMP(d) unsigned long d = jiffies
++#endif
++
++static const char
++spaces[] = " " " "; /* Nx10 spaces */
++
++void
++log_fn_enter(const char *funcname)
++{
++ int indent;
++ TIMESTAMP(d);
++
++ indent = acx_debug_func_indent;
++ if (indent >= sizeof(spaces))
++ indent = sizeof(spaces)-1;
++
++ printk("%08ld %s==> %s\n",
++ d % 100000000,
++ spaces + (sizeof(spaces)-1) - indent,
++ funcname
++ );
++
++ acx_debug_func_indent += FUNC_INDENT_INCREMENT;
++}
++void
++log_fn_exit(const char *funcname)
++{
++ int indent;
++ TIMESTAMP(d);
++
++ acx_debug_func_indent -= FUNC_INDENT_INCREMENT;
++
++ indent = acx_debug_func_indent;
++ if (indent >= sizeof(spaces))
++ indent = sizeof(spaces)-1;
++
++ printk("%08ld %s<== %s\n",
++ d % 100000000,
++ spaces + (sizeof(spaces)-1) - indent,
++ funcname
++ );
++}
++void
++log_fn_exit_v(const char *funcname, int v)
++{
++ int indent;
++ TIMESTAMP(d);
++
++ acx_debug_func_indent -= FUNC_INDENT_INCREMENT;
++
++ indent = acx_debug_func_indent;
++ if (indent >= sizeof(spaces))
++ indent = sizeof(spaces)-1;
++
++ printk("%08ld %s<== %s: %08X\n",
++ d % 100000000,
++ spaces + (sizeof(spaces)-1) - indent,
++ funcname,
++ v
++ );
++}
++#endif /* ACX_DEBUG > 1 */
++
++
++/***********************************************************************
++** Basically a msleep with logging
++*/
++void
++acx_s_msleep(int ms)
++{
++ FN_ENTER;
++ msleep(ms);
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** Not inlined: it's larger than it seems
++*/
++void
++acx_print_mac(const char *head, const u8 *mac, const char *tail)
++{
++ printk("%s"MACSTR"%s", head, MAC(mac), tail);
++}
++
++
++/***********************************************************************
++** acx_get_status_name
++*/
++static const char*
++acx_get_status_name(u16 status)
++{
++ static const char * const str[] = {
++ "STOPPED", "SCANNING", "WAIT_AUTH",
++ "AUTHENTICATED", "ASSOCIATED", "INVALID??"
++ };
++ if (status > VEC_SIZE(str)-1)
++ status = VEC_SIZE(str)-1;
++
++ return str[status];
++}
++
++
++/***********************************************************************
++** acx_get_packet_type_string
++*/
++#if ACX_DEBUG
++const char*
++acx_get_packet_type_string(u16 fc)
++{
++ static const char * const mgmt_arr[] = {
++ "MGMT/AssocReq", "MGMT/AssocResp", "MGMT/ReassocReq",
++ "MGMT/ReassocResp", "MGMT/ProbeReq", "MGMT/ProbeResp",
++ "MGMT/UNKNOWN", "MGMT/UNKNOWN", "MGMT/Beacon", "MGMT/ATIM",
++ "MGMT/Disassoc", "MGMT/Authen", "MGMT/Deauthen"
++ };
++ static const char * const ctl_arr[] = {
++ "CTL/PSPoll", "CTL/RTS", "CTL/CTS", "CTL/Ack", "CTL/CFEnd",
++ "CTL/CFEndCFAck"
++ };
++ static const char * const data_arr[] = {
++ "DATA/DataOnly", "DATA/Data CFAck", "DATA/Data CFPoll",
++ "DATA/Data CFAck/CFPoll", "DATA/Null", "DATA/CFAck",
++ "DATA/CFPoll", "DATA/CFAck/CFPoll"
++ };
++ const char *str;
++ u8 fstype = (WF_FC_FSTYPE & fc) >> 4;
++ u8 ctl;
++
++ switch (WF_FC_FTYPE & fc) {
++ case WF_FTYPE_MGMT:
++ if (fstype < VEC_SIZE(mgmt_arr))
++ str = mgmt_arr[fstype];
++ else
++ str = "MGMT/UNKNOWN";
++ break;
++ case WF_FTYPE_CTL:
++ ctl = fstype - 0x0a;
++ if (ctl < VEC_SIZE(ctl_arr))
++ str = ctl_arr[ctl];
++ else
++ str = "CTL/UNKNOWN";
++ break;
++ case WF_FTYPE_DATA:
++ if (fstype < VEC_SIZE(data_arr))
++ str = data_arr[fstype];
++ else
++ str = "DATA/UNKNOWN";
++ break;
++ default:
++ str = "UNKNOWN";
++ break;
++ }
++ return str;
++}
++#endif
++
++
++/***********************************************************************
++** acx_wlan_reason_str
++*/
++static inline const char*
++acx_wlan_reason_str(u16 reason)
++{
++ static const char* const reason_str[] = {
++ /* 0 */ "?",
++ /* 1 */ "unspecified",
++ /* 2 */ "prev auth is not valid",
++ /* 3 */ "leaving BBS",
++ /* 4 */ "due to inactivity",
++ /* 5 */ "AP is busy",
++ /* 6 */ "got class 2 frame from non-auth'ed STA",
++ /* 7 */ "got class 3 frame from non-assoc'ed STA",
++ /* 8 */ "STA has left BSS",
++ /* 9 */ "assoc without auth is not allowed",
++ /* 10 */ "bad power setting (802.11h)",
++ /* 11 */ "bad channel (802.11i)",
++ /* 12 */ "?",
++ /* 13 */ "invalid IE",
++ /* 14 */ "MIC failure",
++ /* 15 */ "four-way handshake timeout",
++ /* 16 */ "group key handshake timeout",
++ /* 17 */ "IE is different",
++ /* 18 */ "invalid group cipher",
++ /* 19 */ "invalid pairwise cipher",
++ /* 20 */ "invalid AKMP",
++ /* 21 */ "unsupported RSN version",
++ /* 22 */ "invalid RSN IE cap",
++ /* 23 */ "802.1x failed",
++ /* 24 */ "cipher suite rejected"
++ };
++ return reason < VEC_SIZE(reason_str) ? reason_str[reason] : "?";
++}
++
++
++/***********************************************************************
++** acx_cmd_status_str
++*/
++const char*
++acx_cmd_status_str(unsigned int state)
++{
++ static const char * const cmd_error_strings[] = {
++ "Idle",
++ "Success",
++ "Unknown Command",
++ "Invalid Information Element",
++ "Channel rejected",
++ "Channel invalid in current regulatory domain",
++ "MAC invalid",
++ "Command rejected (read-only information element)",
++ "Command rejected",
++ "Already asleep",
++ "TX in progress",
++ "Already awake",
++ "Write only",
++ "RX in progress",
++ "Invalid parameter",
++ "Scan in progress",
++ "Failed"
++ };
++ return state < VEC_SIZE(cmd_error_strings) ?
++ cmd_error_strings[state] : "?";
++}
++
++
++/***********************************************************************
++** get_status_string
++*/
++static inline const char*
++get_status_string(unsigned int status)
++{
++ /* A bit shortened, but hopefully still understandable */
++ static const char * const status_str[] = {
++ /* 0 */ "Successful",
++ /* 1 */ "Unspecified failure",
++ /* 2 */ "reserved",
++ /* 3 */ "reserved",
++ /* 4 */ "reserved",
++ /* 5 */ "reserved",
++ /* 6 */ "reserved",
++ /* 7 */ "reserved",
++ /* 8 */ "reserved",
++ /* 9 */ "reserved",
++ /*10 */ "Cannot support all requested capabilities in Capability Information field",
++ /*11 */ "Reassoc denied (reason outside of 802.11b scope)",
++ /*12 */ "Assoc denied (reason outside of 802.11b scope) -- maybe MAC filtering by peer?",
++ /*13 */ "Responding station doesnt support specified auth algorithm -- maybe WEP auth Open vs. Restricted?",
++ /*14 */ "Auth rejected: wrong transaction sequence number",
++ /*15 */ "Auth rejected: challenge failure",
++ /*16 */ "Auth rejected: timeout for next frame in sequence",
++ /*17 */ "Assoc denied: too many STAs on this AP",
++ /*18 */ "Assoc denied: requesting STA doesnt support all data rates in basic set",
++ /*19 */ "Assoc denied: requesting STA doesnt support Short Preamble",
++ /*20 */ "Assoc denied: requesting STA doesnt support PBCC Modulation",
++ /*21 */ "Assoc denied: requesting STA doesnt support Channel Agility"
++ /*22 */ "reserved",
++ /*23 */ "reserved",
++ /*24 */ "reserved",
++ /*25 */ "Assoc denied: requesting STA doesnt support Short Slot Time",
++ /*26 */ "Assoc denied: requesting STA doesnt support DSSS-OFDM"
++ };
++
++ return status_str[status < VEC_SIZE(status_str) ? status : 2];
++}
++
++
++/***********************************************************************
++*/
++void
++acx_log_bad_eid(wlan_hdr_t* hdr, int len, wlan_ie_t* ie_ptr)
++{
++ if (acx_debug & L_ASSOC) {
++ int offset = (u8*)ie_ptr - (u8*)hdr;
++ printk("acx: unknown EID %d in mgmt frame at offset %d. IE: ",
++ ie_ptr->eid, offset);
++ /* IE len can be bogus, IE can extend past packet end. Oh well... */
++ acx_dump_bytes(ie_ptr, ie_ptr->len + 2);
++ if (acx_debug & L_DATA) {
++ printk("frame (%s): ",
++ acx_get_packet_type_string(le16_to_cpu(hdr->fc)));
++ acx_dump_bytes(hdr, len);
++ }
++ }
++}
++
++
++/***********************************************************************
++*/
++#if ACX_DEBUG
++void
++acx_dump_bytes(const void *data, int num)
++{
++ const u8* ptr = (const u8*)data;
++
++ if (num <= 0) {
++ printk("\n");
++ return;
++ }
++
++ while (num >= 16) {
++ printk( "%02X %02X %02X %02X %02X %02X %02X %02X "
++ "%02X %02X %02X %02X %02X %02X %02X %02X\n",
++ ptr[0], ptr[1], ptr[2], ptr[3],
++ ptr[4], ptr[5], ptr[6], ptr[7],
++ ptr[8], ptr[9], ptr[10], ptr[11],
++ ptr[12], ptr[13], ptr[14], ptr[15]);
++ num -= 16;
++ ptr += 16;
++ }
++ if (num > 0) {
++ while (--num > 0)
++ printk("%02X ", *ptr++);
++ printk("%02X\n", *ptr);
++ }
++}
++#endif
++
++
++/***********************************************************************
++** acx_s_get_firmware_version
++*/
++void
++acx_s_get_firmware_version(acx_device_t *adev)
++{
++ fw_ver_t fw;
++ u8 hexarr[4] = { 0, 0, 0, 0 };
++ int hexidx = 0, val = 0;
++ const char *num;
++ char c;
++
++ FN_ENTER;
++
++ memset(fw.fw_id, 'E', FW_ID_SIZE);
++ acx_s_interrogate(adev, &fw, ACX1xx_IE_FWREV);
++ memcpy(adev->firmware_version, fw.fw_id, FW_ID_SIZE);
++ adev->firmware_version[FW_ID_SIZE] = '\0';
++
++ log(L_DEBUG, "fw_ver: fw_id='%s' hw_id=%08X\n",
++ adev->firmware_version, fw.hw_id);
++
++ if (strncmp(fw.fw_id, "Rev ", 4) != 0) {
++ printk("acx: strange firmware version string "
++ "'%s', please report\n", adev->firmware_version);
++ adev->firmware_numver = 0x01090407; /* assume 1.9.4.7 */
++ } else {
++ num = &fw.fw_id[4];
++ while (1) {
++ c = *num++;
++ if ((c == '.') || (c == '\0')) {
++ hexarr[hexidx++] = val;
++ if ((hexidx > 3) || (c == '\0')) /* end? */
++ break;
++ val = 0;
++ continue;
++ }
++ if ((c >= '0') && (c <= '9'))
++ c -= '0';
++ else
++ c = c - 'a' + (char)10;
++ val = val*16 + c;
++ }
++
++ adev->firmware_numver = (u32)(
++ (hexarr[0] << 24) | (hexarr[1] << 16)
++ | (hexarr[2] << 8) | hexarr[3]);
++ log(L_DEBUG, "firmware_numver 0x%08X\n", adev->firmware_numver);
++ }
++ if (IS_ACX111(adev)) {
++ if (adev->firmware_numver == 0x00010011) {
++ /* This one does not survive floodpinging */
++ printk("acx: firmware '%s' is known to be buggy, "
++ "please upgrade\n", adev->firmware_version);
++ }
++ }
++
++ adev->firmware_id = le32_to_cpu(fw.hw_id);
++
++ /* we're able to find out more detailed chip names now */
++ switch (adev->firmware_id & 0xffff0000) {
++ case 0x01010000:
++ case 0x01020000:
++ adev->chip_name = "TNETW1100A";
++ break;
++ case 0x01030000:
++ adev->chip_name = "TNETW1100B";
++ break;
++ case 0x03000000:
++ case 0x03010000:
++ adev->chip_name = "TNETW1130";
++ break;
++ case 0x04030000: /* 0x04030101 is TNETW1450 */
++ adev->chip_name = "TNETW1450";
++ break;
++ default:
++ printk("acx: unknown chip ID 0x%08X, "
++ "please report\n", adev->firmware_id);
++ break;
++ }
++
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acx_display_hardware_details
++**
++** Displays hw/fw version, radio type etc...
++*/
++void
++acx_display_hardware_details(acx_device_t *adev)
++{
++ const char *radio_str, *form_str;
++
++ FN_ENTER;
++
++ switch (adev->radio_type) {
++ case RADIO_MAXIM_0D:
++ radio_str = "Maxim";
++ break;
++ case RADIO_RFMD_11:
++ radio_str = "RFMD";
++ break;
++ case RADIO_RALINK_15:
++ radio_str = "Ralink";
++ break;
++ case RADIO_RADIA_16:
++ radio_str = "Radia";
++ break;
++ case RADIO_UNKNOWN_17:
++ /* TI seems to have a radio which is
++ * additionally 802.11a capable, too */
++ radio_str = "802.11a/b/g radio?! Please report";
++ break;
++ case RADIO_UNKNOWN_19:
++ radio_str = "A radio used by Safecom cards?! Please report";
++ break;
++ case RADIO_UNKNOWN_1B:
++ radio_str = "An unknown radio used by TNETW1450 USB adapters";
++ break;
++ default:
++ radio_str = "UNKNOWN, please report radio type name!";
++ break;
++ }
++
++ switch (adev->form_factor) {
++ case 0x00:
++ form_str = "unspecified";
++ break;
++ case 0x01:
++ form_str = "(mini-)PCI / CardBus";
++ break;
++ case 0x02:
++ form_str = "USB";
++ break;
++ case 0x03:
++ form_str = "Compact Flash";
++ break;
++ default:
++ form_str = "UNKNOWN, please report";
++ break;
++ }
++
++ printk("acx: === chipset %s, radio type 0x%02X (%s), "
++ "form factor 0x%02X (%s), EEPROM version 0x%02X: "
++ "uploaded firmware '%s' ===\n",
++ adev->chip_name, adev->radio_type, radio_str,
++ adev->form_factor, form_str, adev->eeprom_version,
++ adev->firmware_version);
++
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++*/
++int
++acx_e_change_mtu(struct net_device *ndev, int mtu)
++{
++ enum {
++ MIN_MTU = 256,
++ MAX_MTU = WLAN_DATA_MAXLEN - (ETH_HLEN)
++ };
++
++ if (mtu < MIN_MTU || mtu > MAX_MTU)
++ return -EINVAL;
++
++ ndev->mtu = mtu;
++ return 0;
++}
++
++
++/***********************************************************************
++** acx_e_get_stats, acx_e_get_wireless_stats
++*/
++struct net_device_stats*
++acx_e_get_stats(struct net_device *ndev)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ return &adev->stats;
++}
++
++struct iw_statistics*
++acx_e_get_wireless_stats(struct net_device *ndev)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ return &adev->wstats;
++}
++
++
++/***********************************************************************
++** maps acx111 tx descr rate field to acx100 one
++*/
++const u8
++acx_bitpos2rate100[] = {
++ RATE100_1 ,/* 0 */
++ RATE100_2 ,/* 1 */
++ RATE100_5 ,/* 2 */
++ RATE100_2 ,/* 3, should not happen */
++ RATE100_2 ,/* 4, should not happen */
++ RATE100_11 ,/* 5 */
++ RATE100_2 ,/* 6, should not happen */
++ RATE100_2 ,/* 7, should not happen */
++ RATE100_22 ,/* 8 */
++ RATE100_2 ,/* 9, should not happen */
++ RATE100_2 ,/* 10, should not happen */
++ RATE100_2 ,/* 11, should not happen */
++ RATE100_2 ,/* 12, should not happen */
++ RATE100_2 ,/* 13, should not happen */
++ RATE100_2 ,/* 14, should not happen */
++ RATE100_2 ,/* 15, should not happen */
++};
++
++u8
++acx_rate111to100(u16 r) {
++ return acx_bitpos2rate100[highest_bit(r)];
++}
++
++
++/***********************************************************************
++** Calculate level like the feb 2003 windows driver seems to do
++*/
++static u8
++acx_signal_to_winlevel(u8 rawlevel)
++{
++ /* u8 winlevel = (u8) (0.5 + 0.625 * rawlevel); */
++ u8 winlevel = ((4 + (rawlevel * 5)) / 8);
++
++ if (winlevel > 100)
++ winlevel = 100;
++ return winlevel;
++}
++
++u8
++acx_signal_determine_quality(u8 signal, u8 noise)
++{
++ int qual;
++
++ qual = (((signal - 30) * 100 / 70) + (100 - noise * 4)) / 2;
++
++ if (qual > 100)
++ return 100;
++ if (qual < 0)
++ return 0;
++ return qual;
++}
++
++
++/***********************************************************************
++** Interrogate/configure commands
++*/
++
++/* FIXME: the lengths given here probably aren't always correct.
++ * They should be gradually replaced by proper "sizeof(acx1XX_ie_XXXX)-4",
++ * unless the firmware actually expects a different length than the struct length */
++static const u16
++acx100_ie_len[] = {
++ 0,
++ ACX100_IE_ACX_TIMER_LEN,
++ sizeof(acx100_ie_powersave_t)-4, /* is that 6 or 8??? */
++ ACX1xx_IE_QUEUE_CONFIG_LEN,
++ ACX100_IE_BLOCK_SIZE_LEN,
++ ACX1xx_IE_MEMORY_CONFIG_OPTIONS_LEN,
++ ACX1xx_IE_RATE_FALLBACK_LEN,
++ ACX100_IE_WEP_OPTIONS_LEN,
++ ACX1xx_IE_MEMORY_MAP_LEN, /* ACX1xx_IE_SSID_LEN, */
++ 0,
++ ACX1xx_IE_ASSOC_ID_LEN,
++ 0,
++ ACX111_IE_CONFIG_OPTIONS_LEN,
++ ACX1xx_IE_FWREV_LEN,
++ ACX1xx_IE_FCS_ERROR_COUNT_LEN,
++ ACX1xx_IE_MEDIUM_USAGE_LEN,
++ ACX1xx_IE_RXCONFIG_LEN,
++ 0,
++ 0,
++ sizeof(fw_stats_t)-4,
++ 0,
++ ACX1xx_IE_FEATURE_CONFIG_LEN,
++ ACX111_IE_KEY_CHOOSE_LEN,
++ ACX1FF_IE_MISC_CONFIG_TABLE_LEN,
++ ACX1FF_IE_WONE_CONFIG_LEN,
++ 0,
++ ACX1FF_IE_TID_CONFIG_LEN,
++ 0,
++ 0,
++ 0,
++ ACX1FF_IE_CALIB_ASSESSMENT_LEN,
++ ACX1FF_IE_BEACON_FILTER_OPTIONS_LEN,
++ ACX1FF_IE_LOW_RSSI_THRESH_OPT_LEN,
++ ACX1FF_IE_NOISE_HISTOGRAM_RESULTS_LEN,
++ 0,
++ ACX1FF_IE_PACKET_DETECT_THRESH_LEN,
++ ACX1FF_IE_TX_CONFIG_OPTIONS_LEN,
++ ACX1FF_IE_CCA_THRESHOLD_LEN,
++ ACX1FF_IE_EVENT_MASK_LEN,
++ ACX1FF_IE_DTIM_PERIOD_LEN,
++ 0,
++ ACX1FF_IE_ACI_CONFIG_SET_LEN,
++ 0,
++ 0,
++ 0,
++ 0,
++ 0,
++ 0,
++ ACX1FF_IE_EEPROM_VER_LEN,
++};
++
++static const u16
++acx100_ie_len_dot11[] = {
++ 0,
++ ACX1xx_IE_DOT11_STATION_ID_LEN,
++ 0,
++ ACX100_IE_DOT11_BEACON_PERIOD_LEN,
++ ACX1xx_IE_DOT11_DTIM_PERIOD_LEN,
++ ACX1xx_IE_DOT11_SHORT_RETRY_LIMIT_LEN,
++ ACX1xx_IE_DOT11_LONG_RETRY_LIMIT_LEN,
++ ACX100_IE_DOT11_WEP_DEFAULT_KEY_WRITE_LEN,
++ ACX1xx_IE_DOT11_MAX_XMIT_MSDU_LIFETIME_LEN,
++ 0,
++ ACX1xx_IE_DOT11_CURRENT_REG_DOMAIN_LEN,
++ ACX1xx_IE_DOT11_CURRENT_ANTENNA_LEN,
++ 0,
++ ACX1xx_IE_DOT11_TX_POWER_LEVEL_LEN,
++ ACX1xx_IE_DOT11_CURRENT_CCA_MODE_LEN,
++ ACX100_IE_DOT11_ED_THRESHOLD_LEN,
++ ACX1xx_IE_DOT11_WEP_DEFAULT_KEY_SET_LEN,
++ 0,
++ 0,
++ 0,
++};
++
++static const u16
++acx111_ie_len[] = {
++ 0,
++ ACX100_IE_ACX_TIMER_LEN,
++ sizeof(acx111_ie_powersave_t)-4,
++ ACX1xx_IE_QUEUE_CONFIG_LEN,
++ ACX100_IE_BLOCK_SIZE_LEN,
++ ACX1xx_IE_MEMORY_CONFIG_OPTIONS_LEN,
++ ACX1xx_IE_RATE_FALLBACK_LEN,
++ ACX100_IE_WEP_OPTIONS_LEN,
++ ACX1xx_IE_MEMORY_MAP_LEN, /* ACX1xx_IE_SSID_LEN, */
++ 0,
++ ACX1xx_IE_ASSOC_ID_LEN,
++ 0,
++ ACX111_IE_CONFIG_OPTIONS_LEN,
++ ACX1xx_IE_FWREV_LEN,
++ ACX1xx_IE_FCS_ERROR_COUNT_LEN,
++ ACX1xx_IE_MEDIUM_USAGE_LEN,
++ ACX1xx_IE_RXCONFIG_LEN,
++ 0,
++ 0,
++ sizeof(fw_stats_t)-4,
++ 0,
++ ACX1xx_IE_FEATURE_CONFIG_LEN,
++ ACX111_IE_KEY_CHOOSE_LEN,
++ ACX1FF_IE_MISC_CONFIG_TABLE_LEN,
++ ACX1FF_IE_WONE_CONFIG_LEN,
++ 0,
++ ACX1FF_IE_TID_CONFIG_LEN,
++ 0,
++ 0,
++ 0,
++ ACX1FF_IE_CALIB_ASSESSMENT_LEN,
++ ACX1FF_IE_BEACON_FILTER_OPTIONS_LEN,
++ ACX1FF_IE_LOW_RSSI_THRESH_OPT_LEN,
++ ACX1FF_IE_NOISE_HISTOGRAM_RESULTS_LEN,
++ 0,
++ ACX1FF_IE_PACKET_DETECT_THRESH_LEN,
++ ACX1FF_IE_TX_CONFIG_OPTIONS_LEN,
++ ACX1FF_IE_CCA_THRESHOLD_LEN,
++ ACX1FF_IE_EVENT_MASK_LEN,
++ ACX1FF_IE_DTIM_PERIOD_LEN,
++ 0,
++ ACX1FF_IE_ACI_CONFIG_SET_LEN,
++ 0,
++ 0,
++ 0,
++ 0,
++ 0,
++ 0,
++ ACX1FF_IE_EEPROM_VER_LEN,
++};
++
++static const u16
++acx111_ie_len_dot11[] = {
++ 0,
++ ACX1xx_IE_DOT11_STATION_ID_LEN,
++ 0,
++ ACX100_IE_DOT11_BEACON_PERIOD_LEN,
++ ACX1xx_IE_DOT11_DTIM_PERIOD_LEN,
++ ACX1xx_IE_DOT11_SHORT_RETRY_LIMIT_LEN,
++ ACX1xx_IE_DOT11_LONG_RETRY_LIMIT_LEN,
++ ACX100_IE_DOT11_WEP_DEFAULT_KEY_WRITE_LEN,
++ ACX1xx_IE_DOT11_MAX_XMIT_MSDU_LIFETIME_LEN,
++ 0,
++ ACX1xx_IE_DOT11_CURRENT_REG_DOMAIN_LEN,
++ ACX1xx_IE_DOT11_CURRENT_ANTENNA_LEN,
++ 0,
++ ACX1xx_IE_DOT11_TX_POWER_LEVEL_LEN,
++ ACX1xx_IE_DOT11_CURRENT_CCA_MODE_LEN,
++ ACX100_IE_DOT11_ED_THRESHOLD_LEN,
++ ACX1xx_IE_DOT11_WEP_DEFAULT_KEY_SET_LEN,
++ 0,
++ 0,
++ 0,
++};
++
++
++#undef FUNC
++#define FUNC "configure"
++#if !ACX_DEBUG
++int
++acx_s_configure(acx_device_t *adev, void *pdr, int type)
++{
++#else
++int
++acx_s_configure_debug(acx_device_t *adev, void *pdr, int type, const char* typestr)
++{
++#endif
++ u16 len;
++ int res;
++
++ if (type < 0x1000)
++ len = adev->ie_len[type];
++ else
++ len = adev->ie_len_dot11[type - 0x1000];
++
++ log(L_CTL, FUNC"(type:%s,len:%u)\n", typestr, len);
++ if (unlikely(!len)) {
++ log(L_DEBUG, "zero-length type %s?!\n", typestr);
++ }
++
++ ((acx_ie_generic_t *)pdr)->type = cpu_to_le16(type);
++ ((acx_ie_generic_t *)pdr)->len = cpu_to_le16(len);
++ res = acx_s_issue_cmd(adev, ACX1xx_CMD_CONFIGURE, pdr, len + 4);
++ if (unlikely(OK != res)) {
++#if ACX_DEBUG
++ printk("%s: "FUNC"(type:%s) FAILED\n", adev->ndev->name, typestr);
++#else
++ printk("%s: "FUNC"(type:0x%X) FAILED\n", adev->ndev->name, type);
++#endif
++ /* dump_stack() is already done in issue_cmd() */
++ }
++ return res;
++}
++
++#undef FUNC
++#define FUNC "interrogate"
++#if !ACX_DEBUG
++int
++acx_s_interrogate(acx_device_t *adev, void *pdr, int type)
++{
++#else
++int
++acx_s_interrogate_debug(acx_device_t *adev, void *pdr, int type,
++ const char* typestr)
++{
++#endif
++ u16 len;
++ int res;
++
++ /* FIXME: no check whether this exceeds the array yet.
++ * We should probably remember the number of entries... */
++ if (type < 0x1000)
++ len = adev->ie_len[type];
++ else
++ len = adev->ie_len_dot11[type-0x1000];
++
++ log(L_CTL, FUNC"(type:%s,len:%u)\n", typestr, len);
++
++ ((acx_ie_generic_t *)pdr)->type = cpu_to_le16(type);
++ ((acx_ie_generic_t *)pdr)->len = cpu_to_le16(len);
++ res = acx_s_issue_cmd(adev, ACX1xx_CMD_INTERROGATE, pdr, len + 4);
++ if (unlikely(OK != res)) {
++#if ACX_DEBUG
++ printk("%s: "FUNC"(type:%s) FAILED\n", adev->ndev->name, typestr);
++#else
++ printk("%s: "FUNC"(type:0x%X) FAILED\n", adev->ndev->name, type);
++#endif
++ /* dump_stack() is already done in issue_cmd() */
++ }
++ return res;
++}
++
++#if CMD_DISCOVERY
++void
++great_inquisitor(acx_device_t *adev)
++{
++ static struct {
++ u16 type;
++ u16 len;
++ /* 0x200 was too large here: */
++ u8 data[0x100 - 4];
++ } ACX_PACKED ie;
++ u16 type;
++
++ FN_ENTER;
++
++ /* 0..0x20, 0x1000..0x1020 */
++ for (type = 0; type <= 0x1020; type++) {
++ if (type == 0x21)
++ type = 0x1000;
++ ie.type = cpu_to_le16(type);
++ ie.len = cpu_to_le16(sizeof(ie) - 4);
++ acx_s_issue_cmd(adev, ACX1xx_CMD_INTERROGATE, &ie, sizeof(ie));
++ }
++ FN_EXIT0;
++}
++#endif
++
++
++#ifdef CONFIG_PROC_FS
++/***********************************************************************
++** /proc files
++*/
++/***********************************************************************
++** acx_l_proc_output
++** Generate content for our /proc entry
++**
++** Arguments:
++** buf is a pointer to write output to
++** adev is the usual pointer to our private struct acx_device
++** Returns:
++** number of bytes actually written to buf
++** Side effects:
++** none
++*/
++static int
++acx_l_proc_output(char *buf, acx_device_t *adev)
++{
++ char *p = buf;
++ int i;
++
++ FN_ENTER;
++
++ p += sprintf(p,
++ "acx driver version:\t\t" ACX_RELEASE "\n"
++ "Wireless extension version:\t" STRING(WIRELESS_EXT) "\n"
++ "chip name:\t\t\t%s (0x%08X)\n"
++ "radio type:\t\t\t0x%02X\n"
++ "form factor:\t\t\t0x%02X\n"
++ "EEPROM version:\t\t\t0x%02X\n"
++ "firmware version:\t\t%s (0x%08X)\n",
++ adev->chip_name, adev->firmware_id,
++ adev->radio_type,
++ adev->form_factor,
++ adev->eeprom_version,
++ adev->firmware_version, adev->firmware_numver);
++
++ for (i = 0; i < VEC_SIZE(adev->sta_list); i++) {
++ struct client *bss = &adev->sta_list[i];
++ if (!bss->used) continue;
++ p += sprintf(p, "BSS %u BSSID "MACSTR" ESSID %s channel %u "
++ "Cap 0x%X SIR %u SNR %u\n",
++ i, MAC(bss->bssid), (char*)bss->essid, bss->channel,
++ bss->cap_info, bss->sir, bss->snr);
++ }
++ p += sprintf(p, "status:\t\t\t%u (%s)\n",
++ adev->status, acx_get_status_name(adev->status));
++
++ FN_EXIT1(p - buf);
++ return p - buf;
++}
++
++
++/***********************************************************************
++*/
++static int
++acx_s_proc_diag_output(char *buf, acx_device_t *adev)
++{
++ char *p = buf;
++ unsigned long flags;
++ unsigned int len = 0, partlen;
++ u32 temp1, temp2;
++ u8 *st, *st_end;
++#ifdef __BIG_ENDIAN
++ u8 *st2;
++#endif
++ fw_stats_t *fw_stats;
++ char *part_str = NULL;
++ fw_stats_tx_t *tx = NULL;
++ fw_stats_rx_t *rx = NULL;
++ fw_stats_dma_t *dma = NULL;
++ fw_stats_irq_t *irq = NULL;
++ fw_stats_wep_t *wep = NULL;
++ fw_stats_pwr_t *pwr = NULL;
++ fw_stats_mic_t *mic = NULL;
++ fw_stats_aes_t *aes = NULL;
++ fw_stats_event_t *evt = NULL;
++
++ FN_ENTER;
++
++ acx_lock(adev, flags);
++
++#if defined (ACX_MEM)
++ p = acxmem_s_proc_diag_output(p, adev);
++#else
++ if (IS_PCI(adev))
++ p = acxpci_s_proc_diag_output(p, adev);
++#endif
++
++ p += sprintf(p,
++ "\n"
++ "** network status **\n"
++ "dev_state_mask 0x%04X\n"
++ "status %u (%s), "
++ "mode %u, channel %u, "
++ "reg_dom_id 0x%02X, reg_dom_chanmask 0x%04X, ",
++ adev->dev_state_mask,
++ adev->status, acx_get_status_name(adev->status),
++ adev->mode, adev->channel,
++ adev->reg_dom_id, adev->reg_dom_chanmask
++ );
++ p += sprintf(p,
++ "ESSID \"%s\", essid_active %d, essid_len %d, "
++ "essid_for_assoc \"%s\", nick \"%s\"\n"
++ "WEP ena %d, restricted %d, idx %d\n",
++ adev->essid, adev->essid_active, (int)adev->essid_len,
++ adev->essid_for_assoc, adev->nick,
++ adev->wep_enabled, adev->wep_restricted,
++ adev->wep_current_index);
++ p += sprintf(p, "dev_addr "MACSTR"\n", MAC(adev->dev_addr));
++ p += sprintf(p, "bssid "MACSTR"\n", MAC(adev->bssid));
++ p += sprintf(p, "ap_filter "MACSTR"\n", MAC(adev->ap));
++
++ p += sprintf(p,
++ "\n"
++ "** PHY status **\n"
++ "tx_disabled %d, tx_level_dbm %d\n" /* "tx_level_val %d, tx_level_auto %d\n" */
++ "sensitivity %d, antenna 0x%02X, ed_threshold %d, cca %d, preamble_mode %d\n"
++ "rate_basic 0x%04X, rate_oper 0x%04X\n"
++ "rts_threshold %d, frag_threshold %d, short_retry %d, long_retry %d\n"
++ "msdu_lifetime %d, listen_interval %d, beacon_interval %d\n",
++ adev->tx_disabled, adev->tx_level_dbm, /* adev->tx_level_val, adev->tx_level_auto, */
++ adev->sensitivity, adev->antenna, adev->ed_threshold, adev->cca, adev->preamble_mode,
++ adev->rate_basic, adev->rate_oper,
++ adev->rts_threshold, adev->frag_threshold, adev->short_retry, adev->long_retry,
++ adev->msdu_lifetime, adev->listen_interval, adev->beacon_interval);
++
++ acx_unlock(adev, flags);
++
++ p += sprintf(p,
++ "\n"
++ "** Firmware **\n"
++ "NOTE: version dependent statistics layout, "
++ "please report if you suspect wrong parsing!\n"
++ "\n"
++ "version \"%s\"\n", adev->firmware_version);
++
++ /* TODO: may replace kmalloc/memset with kzalloc once
++ * Linux 2.6.14 is widespread */
++ fw_stats = kmalloc(sizeof(*fw_stats), GFP_KERNEL);
++ if (!fw_stats) {
++ FN_EXIT1(0);
++ return 0;
++ }
++ memset(fw_stats, 0, sizeof(*fw_stats));
++
++ st = (u8 *)fw_stats;
++
++ part_str = "statistics query command";
++
++ if (OK != acx_s_interrogate(adev, st, ACX1xx_IE_FIRMWARE_STATISTICS))
++ goto fw_stats_end;
++
++ st += sizeof(u16);
++ len = *(u16 *)st;
++
++ if (len > sizeof(*fw_stats)) {
++ p += sprintf(p,
++ "firmware version with bigger fw_stats struct detected\n"
++ "(%u vs. %u), please report\n", len, sizeof(fw_stats_t));
++ if (len > sizeof(*fw_stats)) {
++ p += sprintf(p, "struct size exceeded allocation!\n");
++ len = sizeof(*fw_stats);
++ }
++ }
++ st += sizeof(u16);
++ st_end = st - 2*sizeof(u16) + len;
++
++#ifdef __BIG_ENDIAN
++ /* let's make one bold assumption here:
++ * (hopefully!) *all* statistics fields are u32 only,
++ * thus if we need to make endianness corrections
++ * we can simply do them in one go, in advance */
++ st2 = (u8 *)fw_stats;
++ for (temp1 = 0; temp1 < len; temp1 += 4, st2 += 4)
++ *(u32 *)st2 = le32_to_cpu(*(u32 *)st2);
++#endif
++
++ part_str = "Rx/Tx";
++
++ /* directly at end of a struct part? --> no error! */
++ if (st == st_end)
++ goto fw_stats_end;
++
++ tx = (fw_stats_tx_t *)st;
++ st += sizeof(fw_stats_tx_t);
++ rx = (fw_stats_rx_t *)st;
++ st += sizeof(fw_stats_rx_t);
++ partlen = sizeof(fw_stats_tx_t) + sizeof(fw_stats_rx_t);
++
++ if (IS_ACX100(adev)) {
++ /* at least ACX100 PCI F/W 1.9.8.b
++ * and ACX100 USB F/W 1.0.7-USB
++ * don't have those two fields... */
++ st -= 2*sizeof(u32);
++
++ /* our parsing doesn't quite match this firmware yet,
++ * log failure */
++ if (st > st_end)
++ goto fw_stats_fail;
++ temp1 = temp2 = 999999999;
++ } else {
++ if (st > st_end)
++ goto fw_stats_fail;
++ temp1 = rx->rx_aci_events;
++ temp2 = rx->rx_aci_resets;
++ }
++
++ p += sprintf(p,
++ "%s:\n"
++ " tx_desc_overfl %u\n"
++ " rx_OutOfMem %u, rx_hdr_overfl %u, rx_hw_stuck %u\n"
++ " rx_dropped_frame %u, rx_frame_ptr_err %u, rx_xfr_hint_trig %u\n"
++ " rx_aci_events %u, rx_aci_resets %u\n",
++ part_str,
++ tx->tx_desc_of,
++ rx->rx_oom,
++ rx->rx_hdr_of,
++ rx->rx_hw_stuck,
++ rx->rx_dropped_frame,
++ rx->rx_frame_ptr_err,
++ rx->rx_xfr_hint_trig,
++ temp1,
++ temp2);
++
++ part_str = "DMA";
++
++ if (st == st_end)
++ goto fw_stats_end;
++
++ dma = (fw_stats_dma_t *)st;
++ partlen = sizeof(fw_stats_dma_t);
++ st += partlen;
++
++ if (st > st_end)
++ goto fw_stats_fail;
++
++ p += sprintf(p,
++ "%s:\n"
++ " rx_dma_req %u, rx_dma_err %u, tx_dma_req %u, tx_dma_err %u\n",
++ part_str,
++ dma->rx_dma_req,
++ dma->rx_dma_err,
++ dma->tx_dma_req,
++ dma->tx_dma_err);
++
++ part_str = "IRQ";
++
++ if (st == st_end)
++ goto fw_stats_end;
++
++ irq = (fw_stats_irq_t *)st;
++ partlen = sizeof(fw_stats_irq_t);
++ st += partlen;
++
++ if (st > st_end)
++ goto fw_stats_fail;
++
++ p += sprintf(p,
++ "%s:\n"
++ " cmd_cplt %u, fiq %u\n"
++ " rx_hdrs %u, rx_cmplt %u, rx_mem_overfl %u, rx_rdys %u\n"
++ " irqs %u, tx_procs %u, decrypt_done %u\n"
++ " dma_0_done %u, dma_1_done %u, tx_exch_complet %u\n"
++ " commands %u, rx_procs %u, hw_pm_mode_changes %u\n"
++ " host_acks %u, pci_pm %u, acm_wakeups %u\n",
++ part_str,
++ irq->cmd_cplt,
++ irq->fiq,
++ irq->rx_hdrs,
++ irq->rx_cmplt,
++ irq->rx_mem_of,
++ irq->rx_rdys,
++ irq->irqs,
++ irq->tx_procs,
++ irq->decrypt_done,
++ irq->dma_0_done,
++ irq->dma_1_done,
++ irq->tx_exch_complet,
++ irq->commands,
++ irq->rx_procs,
++ irq->hw_pm_mode_changes,
++ irq->host_acks,
++ irq->pci_pm,
++ irq->acm_wakeups);
++
++ part_str = "WEP";
++
++ if (st == st_end)
++ goto fw_stats_end;
++
++ wep = (fw_stats_wep_t *)st;
++ partlen = sizeof(fw_stats_wep_t);
++ st += partlen;
++
++ if (
++ (IS_PCI(adev) && IS_ACX100(adev))
++ || (IS_USB(adev) && IS_ACX100(adev))
++ || (IS_MEM(adev) && IS_ACX100(adev))
++ ) {
++ /* at least ACX100 PCI F/W 1.9.8.b,
++ * ACX100 USB F/W 1.0.7-USB
++ * and ACX100 Generic Slave F/W 1.10.7.K
++ * don't have those two fields...
++ */
++ st -= 2*sizeof(u32);
++ if (st > st_end)
++ goto fw_stats_fail;
++ temp1 = temp2 = 999999999;
++ } else {
++ if (st > st_end)
++ goto fw_stats_fail;
++ temp1 = wep->wep_pkt_decrypt;
++ temp2 = wep->wep_decrypt_irqs;
++ }
++
++ p += sprintf(p,
++ "%s:\n"
++ " wep_key_count %u, wep_default_key_count %u, dot11_def_key_mib %u\n"
++ " wep_key_not_found %u, wep_decrypt_fail %u\n"
++ " wep_pkt_decrypt %u, wep_decrypt_irqs %u\n",
++ part_str,
++ wep->wep_key_count,
++ wep->wep_default_key_count,
++ wep->dot11_def_key_mib,
++ wep->wep_key_not_found,
++ wep->wep_decrypt_fail,
++ temp1,
++ temp2);
++
++ part_str = "power";
++
++ if (st == st_end)
++ goto fw_stats_end;
++
++ pwr = (fw_stats_pwr_t *)st;
++ partlen = sizeof(fw_stats_pwr_t);
++ st += partlen;
++
++ if (st > st_end)
++ goto fw_stats_fail;
++
++ p += sprintf(p,
++ "%s:\n"
++ " tx_start_ctr %u, no_ps_tx_too_short %u\n"
++ " rx_start_ctr %u, no_ps_rx_too_short %u\n"
++ " lppd_started %u\n"
++ " no_lppd_too_noisy %u, no_lppd_too_short %u, no_lppd_matching_frame %u\n",
++ part_str,
++ pwr->tx_start_ctr,
++ pwr->no_ps_tx_too_short,
++ pwr->rx_start_ctr,
++ pwr->no_ps_rx_too_short,
++ pwr->lppd_started,
++ pwr->no_lppd_too_noisy,
++ pwr->no_lppd_too_short,
++ pwr->no_lppd_matching_frame);
++
++ part_str = "MIC";
++
++ if (st == st_end)
++ goto fw_stats_end;
++
++ mic = (fw_stats_mic_t *)st;
++ partlen = sizeof(fw_stats_mic_t);
++ st += partlen;
++
++ if (st > st_end)
++ goto fw_stats_fail;
++
++ p += sprintf(p,
++ "%s:\n"
++ " mic_rx_pkts %u, mic_calc_fail %u\n",
++ part_str,
++ mic->mic_rx_pkts,
++ mic->mic_calc_fail);
++
++ part_str = "AES";
++
++ if (st == st_end)
++ goto fw_stats_end;
++
++ aes = (fw_stats_aes_t *)st;
++ partlen = sizeof(fw_stats_aes_t);
++ st += partlen;
++
++ if (st > st_end)
++ goto fw_stats_fail;
++
++ p += sprintf(p,
++ "%s:\n"
++ " aes_enc_fail %u, aes_dec_fail %u\n"
++ " aes_enc_pkts %u, aes_dec_pkts %u\n"
++ " aes_enc_irq %u, aes_dec_irq %u\n",
++ part_str,
++ aes->aes_enc_fail,
++ aes->aes_dec_fail,
++ aes->aes_enc_pkts,
++ aes->aes_dec_pkts,
++ aes->aes_enc_irq,
++ aes->aes_dec_irq);
++
++ part_str = "event";
++
++ if (st == st_end)
++ goto fw_stats_end;
++
++ evt = (fw_stats_event_t *)st;
++ partlen = sizeof(fw_stats_event_t);
++ st += partlen;
++
++ if (st > st_end)
++ goto fw_stats_fail;
++
++ p += sprintf(p,
++ "%s:\n"
++ " heartbeat %u, calibration %u\n"
++ " rx_mismatch %u, rx_mem_empty %u, rx_pool %u\n"
++ " oom_late %u\n"
++ " phy_tx_err %u, tx_stuck %u\n",
++ part_str,
++ evt->heartbeat,
++ evt->calibration,
++ evt->rx_mismatch,
++ evt->rx_mem_empty,
++ evt->rx_pool,
++ evt->oom_late,
++ evt->phy_tx_err,
++ evt->tx_stuck);
++
++ if (st < st_end)
++ goto fw_stats_bigger;
++
++ goto fw_stats_end;
++
++fw_stats_fail:
++ st -= partlen;
++ p += sprintf(p,
++ "failed at %s part (size %u), offset %u (struct size %u), "
++ "please report\n", part_str, partlen,
++ (int)st - (int)fw_stats, len);
++
++fw_stats_bigger:
++ for (; st < st_end; st += 4)
++ p += sprintf(p,
++ "UNKN%3d: %u\n", (int)st - (int)fw_stats, *(u32 *)st);
++
++fw_stats_end:
++ kfree(fw_stats);
++
++ FN_EXIT1(p - buf);
++ return p - buf;
++}
++
++
++/***********************************************************************
++*/
++static int
++acx_s_proc_phy_output(char *buf, acx_device_t *adev)
++{
++ char *p = buf;
++ int i;
++
++ FN_ENTER;
++
++ /*
++ if (RADIO_RFMD_11 != adev->radio_type) {
++ printk("sorry, not yet adapted for radio types "
++ "other than RFMD, please verify "
++ "PHY size etc. first!\n");
++ goto end;
++ }
++ */
++
++ /* The PHY area is only 0x80 bytes long; further pages after that
++ * only have some page number registers with altered value,
++ * all other registers remain the same. */
++ for (i = 0; i < 0x80; i++) {
++ acx_s_read_phy_reg(adev, i, p++);
++ }
++
++ FN_EXIT1(p - buf);
++ return p - buf;
++}
++
++
++/***********************************************************************
++** acx_e_read_proc_XXXX
++** Handle our /proc entry
++**
++** Arguments:
++** standard kernel read_proc interface
++** Returns:
++** number of bytes written to buf
++** Side effects:
++** none
++*/
++static int
++acx_e_read_proc(char *buf, char **start, off_t offset, int count,
++ int *eof, void *data)
++{
++ acx_device_t *adev = (acx_device_t*)data;
++ unsigned long flags;
++ int length;
++
++ FN_ENTER;
++
++ acx_sem_lock(adev);
++ acx_lock(adev, flags);
++ /* fill buf */
++ length = acx_l_proc_output(buf, adev);
++ acx_unlock(adev, flags);
++ acx_sem_unlock(adev);
++
++ /* housekeeping */
++ if (length <= offset + count)
++ *eof = 1;
++ *start = buf + offset;
++ length -= offset;
++ if (length > count)
++ length = count;
++ if (length < 0)
++ length = 0;
++ FN_EXIT1(length);
++ return length;
++}
++
++static char _buf[32768];
++static int
++acx_e_read_proc_diag(char *buf, char **start, off_t offset, int count,
++ int *eof, void *data)
++{
++ acx_device_t *adev = (acx_device_t*)data;
++ int length;
++
++ FN_ENTER;
++
++ acx_sem_lock(adev);
++ /* fill buf */
++ length = acx_s_proc_diag_output(_buf, adev);
++ acx_sem_unlock(adev);
++
++ memcpy(buf, _buf + offset, count);
++
++ /* housekeeping */
++ if (length <= offset + count)
++ *eof = 1;
++ *start = count;
++ length -= offset;
++ if (length > count)
++ length = count;
++ if (length < 0)
++ length = 0;
++ FN_EXIT1(length);
++ return length;
++}
++
++static int
++acx_e_read_proc_eeprom(char *buf, char **start, off_t offset, int count,
++ int *eof, void *data)
++{
++ acx_device_t *adev = (acx_device_t*)data;
++ int length;
++
++ FN_ENTER;
++
++ /* fill buf */
++ length = 0;
++#if defined (ACX_MEM)
++ acx_sem_lock(adev);
++ length = acxmem_proc_eeprom_output(buf, adev);
++ acx_sem_unlock(adev);
++#else
++ if (IS_PCI(adev)) {
++ acx_sem_lock(adev);
++ length = acxpci_proc_eeprom_output(buf, adev);
++ acx_sem_unlock(adev);
++ }
++#endif
++
++ /* housekeeping */
++ if (length <= offset + count)
++ *eof = 1;
++ *start = buf + offset;
++ length -= offset;
++ if (length > count)
++ length = count;
++ if (length < 0)
++ length = 0;
++ FN_EXIT1(length);
++ return length;
++}
++
++static int
++acx_e_read_proc_phy(char *buf, char **start, off_t offset, int count,
++ int *eof, void *data)
++{
++ acx_device_t *adev = (acx_device_t*)data;
++ int length;
++
++ FN_ENTER;
++
++ acx_sem_lock(adev);
++ /* fill buf */
++ length = acx_s_proc_phy_output(buf, adev);
++ acx_sem_unlock(adev);
++
++ /* housekeeping */
++ if (length <= offset + count)
++ *eof = 1;
++ *start = buf + offset;
++ length -= offset;
++ if (length > count)
++ length = count;
++ if (length < 0)
++ length = 0;
++ FN_EXIT1(length);
++ return length;
++}
++
++
++/***********************************************************************
++** /proc files registration
++*/
++static const char * const
++proc_files[] = { "", "_diag", "_eeprom", "_phy" };
++
++static read_proc_t * const
++proc_funcs[] = {
++ acx_e_read_proc,
++ acx_e_read_proc_diag,
++ acx_e_read_proc_eeprom,
++ acx_e_read_proc_phy
++};
++
++static int
++manage_proc_entries(const struct net_device *ndev, int remove)
++{
++ acx_device_t *adev = ndev2adev((struct net_device *)ndev);
++ char procbuf[80];
++ int i;
++
++ for (i = 0; i < VEC_SIZE(proc_files); i++) {
++ snprintf(procbuf, sizeof(procbuf),
++ "driver/acx_%s%s", ndev->name, proc_files[i]);
++ log(L_INIT, "%sing /proc entry %s\n",
++ remove ? "remov" : "creat", procbuf);
++ if (!remove) {
++ if (!create_proc_read_entry(procbuf, 0, 0, proc_funcs[i], adev)) {
++ printk("acx: cannot register /proc entry %s\n", procbuf);
++ return NOT_OK;
++ }
++ } else {
++ remove_proc_entry(procbuf, NULL);
++ }
++ }
++ return OK;
++}
++
++int
++acx_proc_register_entries(const struct net_device *ndev)
++{
++ return manage_proc_entries(ndev, 0);
++}
++
++int
++acx_proc_unregister_entries(const struct net_device *ndev)
++{
++ return manage_proc_entries(ndev, 1);
++}
++#endif /* CONFIG_PROC_FS */
++
++
++/***********************************************************************
++** acx_cmd_join_bssid
++**
++** Common code for both acx100 and acx111.
++*/
++/* NB: does NOT match RATE100_nn but matches ACX[111]_SCAN_RATE_n */
++static const u8
++bitpos2genframe_txrate[] = {
++ 10, /* 0. 1 Mbit/s */
++ 20, /* 1. 2 Mbit/s */
++ 55, /* 2. 5.5 Mbit/s */
++ 0x0B, /* 3. 6 Mbit/s */
++ 0x0F, /* 4. 9 Mbit/s */
++ 110, /* 5. 11 Mbit/s */
++ 0x0A, /* 6. 12 Mbit/s */
++ 0x0E, /* 7. 18 Mbit/s */
++ 220, /* 8. 22 Mbit/s */
++ 0x09, /* 9. 24 Mbit/s */
++ 0x0D, /* 10. 36 Mbit/s */
++ 0x08, /* 11. 48 Mbit/s */
++ 0x0C, /* 12. 54 Mbit/s */
++ 10, /* 13. 1 Mbit/s, should never happen */
++ 10, /* 14. 1 Mbit/s, should never happen */
++ 10, /* 15. 1 Mbit/s, should never happen */
++};
++
++/* Looks scary, eh?
++** Actually, each one compiled into one AND and one SHIFT,
++** 31 bytes in x86 asm (more if uints are replaced by u16/u8) */
++static inline unsigned int
++rate111to5bits(unsigned int rate)
++{
++ return (rate & 0x7)
++ | ( (rate & RATE111_11) / (RATE111_11/JOINBSS_RATES_11) )
++ | ( (rate & RATE111_22) / (RATE111_22/JOINBSS_RATES_22) )
++ ;
++}
++
++static void
++acx_s_cmd_join_bssid(acx_device_t *adev, const u8 *bssid)
++{
++ acx_joinbss_t tmp;
++ int dtim_interval;
++ int i;
++
++ if (mac_is_zero(bssid))
++ return;
++
++ FN_ENTER;
++
++ dtim_interval = (ACX_MODE_0_ADHOC == adev->mode) ?
++ 1 : adev->dtim_interval;
++
++ memset(&tmp, 0, sizeof(tmp));
++
++ for (i = 0; i < ETH_ALEN; i++) {
++ tmp.bssid[i] = bssid[ETH_ALEN-1 - i];
++ }
++
++ tmp.beacon_interval = cpu_to_le16(adev->beacon_interval);
++
++ /* Basic rate set. Control frame responses (such as ACK or CTS frames)
++ ** are sent with one of these rates */
++ if (IS_ACX111(adev)) {
++ /* It was experimentally determined that rates_basic
++ ** can take 11g rates as well, not only rates
++ ** defined with JOINBSS_RATES_BASIC111_nnn.
++ ** Just use RATE111_nnn constants... */
++ tmp.u.acx111.dtim_interval = dtim_interval;
++ tmp.u.acx111.rates_basic = cpu_to_le16(adev->rate_basic);
++ log(L_ASSOC, "rates_basic:%04X, rates_supported:%04X\n",
++ adev->rate_basic, adev->rate_oper);
++ } else {
++ tmp.u.acx100.dtim_interval = dtim_interval;
++ tmp.u.acx100.rates_basic = rate111to5bits(adev->rate_basic);
++ tmp.u.acx100.rates_supported = rate111to5bits(adev->rate_oper);
++ log(L_ASSOC, "rates_basic:%04X->%02X, "
++ "rates_supported:%04X->%02X\n",
++ adev->rate_basic, tmp.u.acx100.rates_basic,
++ adev->rate_oper, tmp.u.acx100.rates_supported);
++ }
++
++ /* Setting up how Beacon, Probe Response, RTS, and PS-Poll frames
++ ** will be sent (rate/modulation/preamble) */
++ tmp.u.txrate.genfrm_txrate = bitpos2genframe_txrate[lowest_bit(adev->rate_basic)];
++ tmp.genfrm_mod_pre = 0; /* FIXME: was = adev->capab_short (which was always 0); */
++ /* we can use short pre *if* all peers can understand it */
++ /* FIXME #2: we need to correctly set PBCC/OFDM bits here too */
++
++ /* we switch fw to STA mode in MONITOR mode, it seems to be
++ ** the only mode where fw does not emit beacons by itself
++ ** but allows us to send anything (we really want to retain
++ ** ability to tx arbitrary frames in MONITOR mode)
++ */
++ tmp.macmode = (adev->mode != ACX_MODE_MONITOR ? adev->mode : ACX_MODE_2_STA);
++ tmp.channel = adev->channel;
++ tmp.essid_len = adev->essid_len;
++ /* NOTE: the code memcpy'd essid_len + 1 before, which is WRONG! */
++ memcpy(tmp.essid, adev->essid, tmp.essid_len);
++ acx_s_issue_cmd(adev, ACX1xx_CMD_JOIN, &tmp, tmp.essid_len + 0x11);
++
++ log(L_ASSOC|L_DEBUG, "BSS_Type = %u\n", tmp.macmode);
++ acxlog_mac(L_ASSOC|L_DEBUG, "JoinBSSID MAC:", adev->bssid, "\n");
++
++ acx_update_capabilities(adev);
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acx_s_cmd_start_scan
++**
++** Issue scan command to the hardware
++**
++** unified function for both ACX111 and ACX100
++*/
++static void
++acx_s_scan_chan(acx_device_t *adev)
++{
++ union {
++ acx111_scan_t acx111;
++ acx100_scan_t acx100;
++ } s;
++
++ FN_ENTER;
++
++ memset(&s, 0, sizeof(s));
++
++ /* first common positions... */
++
++ s.acx111.count = cpu_to_le16(adev->scan_count);
++ s.acx111.rate = adev->scan_rate;
++ s.acx111.options = adev->scan_mode;
++ s.acx111.chan_duration = cpu_to_le16(adev->scan_duration);
++ s.acx111.max_probe_delay = cpu_to_le16(adev->scan_probe_delay);
++
++ /* ...then differences */
++
++ if (IS_ACX111(adev)) {
++ s.acx111.channel_list_select = 0; /* scan every allowed channel */
++ /*s.acx111.channel_list_select = 1;*/ /* scan given channels */
++ /*s.acx111.modulation = 0x40;*/ /* long preamble? OFDM? -> only for active scan */
++ s.acx111.modulation = 0;
++ /*s.acx111.channel_list[0] = 6;
++ s.acx111.channel_list[1] = 4;*/
++ } else {
++ s.acx100.start_chan = cpu_to_le16(1);
++ s.acx100.flags = cpu_to_le16(0x8000);
++ }
++
++ acx_s_issue_cmd(adev, ACX1xx_CMD_SCAN, &s, sizeof(s));
++ FN_EXIT0;
++}
++
++
++void
++acx_s_cmd_start_scan(acx_device_t *adev)
++{
++ /* time_before check is 'just in case' thing */
++ if (!(adev->irq_status & HOST_INT_SCAN_COMPLETE)
++ && time_before(jiffies, adev->scan_start + 10*HZ)
++ ) {
++ log(L_INIT, "start_scan: seems like previous scan "
++ "is still running. Not starting anew. Please report\n");
++ return;
++ }
++
++ log(L_INIT, "starting radio scan\n");
++ /* remember that fw is commanded to do scan */
++ adev->scan_start = jiffies;
++ CLEAR_BIT(adev->irq_status, HOST_INT_SCAN_COMPLETE);
++ /* issue it */
++ acx_s_scan_chan(adev);
++}
++
++
++/***********************************************************************
++** acx111 feature config
++*/
++static int
++acx111_s_get_feature_config(acx_device_t *adev,
++ u32 *feature_options, u32 *data_flow_options)
++{
++ struct acx111_ie_feature_config feat;
++
++ if (!IS_ACX111(adev)) {
++ return NOT_OK;
++ }
++
++ memset(&feat, 0, sizeof(feat));
++
++ if (OK != acx_s_interrogate(adev, &feat, ACX1xx_IE_FEATURE_CONFIG)) {
++ return NOT_OK;
++ }
++ log(L_DEBUG,
++ "got Feature option:0x%X, DataFlow option: 0x%X\n",
++ feat.feature_options,
++ feat.data_flow_options);
++
++ if (feature_options)
++ *feature_options = le32_to_cpu(feat.feature_options);
++ if (data_flow_options)
++ *data_flow_options = le32_to_cpu(feat.data_flow_options);
++
++ return OK;
++}
++
++static int
++acx111_s_set_feature_config(acx_device_t *adev,
++ u32 feature_options, u32 data_flow_options,
++ unsigned int mode /* 0 == remove, 1 == add, 2 == set */)
++{
++ struct acx111_ie_feature_config feat;
++
++ if (!IS_ACX111(adev)) {
++ return NOT_OK;
++ }
++
++ if ((mode < 0) || (mode > 2))
++ return NOT_OK;
++
++ if (mode != 2)
++ /* need to modify old data */
++ acx111_s_get_feature_config(adev, &feat.feature_options, &feat.data_flow_options);
++ else {
++ /* need to set a completely new value */
++ feat.feature_options = 0;
++ feat.data_flow_options = 0;
++ }
++
++ if (mode == 0) { /* remove */
++ CLEAR_BIT(feat.feature_options, cpu_to_le32(feature_options));
++ CLEAR_BIT(feat.data_flow_options, cpu_to_le32(data_flow_options));
++ } else { /* add or set */
++ SET_BIT(feat.feature_options, cpu_to_le32(feature_options));
++ SET_BIT(feat.data_flow_options, cpu_to_le32(data_flow_options));
++ }
++
++ log(L_DEBUG,
++ "old: feature 0x%08X dataflow 0x%08X. mode: %u\n"
++ "new: feature 0x%08X dataflow 0x%08X\n",
++ feature_options, data_flow_options, mode,
++ le32_to_cpu(feat.feature_options),
++ le32_to_cpu(feat.data_flow_options));
++
++ if (OK != acx_s_configure(adev, &feat, ACX1xx_IE_FEATURE_CONFIG)) {
++ return NOT_OK;
++ }
++
++ return OK;
++}
++
++static inline int
++acx111_s_feature_off(acx_device_t *adev, u32 f, u32 d)
++{
++ return acx111_s_set_feature_config(adev, f, d, 0);
++}
++static inline int
++acx111_s_feature_on(acx_device_t *adev, u32 f, u32 d)
++{
++ return acx111_s_set_feature_config(adev, f, d, 1);
++}
++static inline int
++acx111_s_feature_set(acx_device_t *adev, u32 f, u32 d)
++{
++ return acx111_s_set_feature_config(adev, f, d, 2);
++}
++
++
++/***********************************************************************
++** acx100_s_init_memory_pools
++*/
++static int
++acx100_s_init_memory_pools(acx_device_t *adev, const acx_ie_memmap_t *mmt)
++{
++ acx100_ie_memblocksize_t MemoryBlockSize;
++ acx100_ie_memconfigoption_t MemoryConfigOption;
++ int TotalMemoryBlocks;
++ int RxBlockNum;
++ int TotalRxBlockSize;
++ int TxBlockNum;
++ int TotalTxBlockSize;
++
++ FN_ENTER;
++
++ /* Let's see if we can follow this:
++ first we select our memory block size (which I think is
++ completely arbitrary) */
++ MemoryBlockSize.size = cpu_to_le16(adev->memblocksize);
++
++ /* Then we alert the card to our decision of block size */
++ if (OK != acx_s_configure(adev, &MemoryBlockSize, ACX100_IE_BLOCK_SIZE)) {
++ goto bad;
++ }
++
++ /* We figure out how many total blocks we can create, using
++ the block size we chose, and the beginning and ending
++ memory pointers, i.e.: end-start/size */
++ TotalMemoryBlocks = (le32_to_cpu(mmt->PoolEnd) - le32_to_cpu(mmt->PoolStart)) / adev->memblocksize;
++
++ log(L_DEBUG, "TotalMemoryBlocks=%u (%u bytes)\n",
++ TotalMemoryBlocks, TotalMemoryBlocks*adev->memblocksize);
++
++ /* MemoryConfigOption.DMA_config bitmask:
++ access to ACX memory is to be done:
++ 0x00080000 using PCI conf space?!
++ 0x00040000 using IO instructions?
++ 0x00000000 using memory access instructions
++ 0x00020000 using local memory block linked list (else what?)
++ 0x00010000 using host indirect descriptors (else host must access ACX memory?)
++ */
++#if defined (ACX_MEM)
++ /*
++ * ACX ignores DMA_config for generic slave mode.
++ */
++ MemoryConfigOption.DMA_config = 0;
++ /* Declare start of the Rx host pool */
++ MemoryConfigOption.pRxHostDesc = cpu2acx(0);
++ log(L_DEBUG, "pRxHostDesc 0x%08X, rxhostdesc_startphy 0x%lX\n",
++ acx2cpu(MemoryConfigOption.pRxHostDesc),
++ (long)adev->rxhostdesc_startphy);
++#else
++ if (IS_PCI(adev)) {
++ MemoryConfigOption.DMA_config = cpu_to_le32(0x30000);
++ /* Declare start of the Rx host pool */
++ MemoryConfigOption.pRxHostDesc = cpu2acx(adev->rxhostdesc_startphy);
++ log(L_DEBUG, "pRxHostDesc 0x%08X, rxhostdesc_startphy 0x%lX\n",
++ acx2cpu(MemoryConfigOption.pRxHostDesc),
++ (long)adev->rxhostdesc_startphy);
++ } else {
++ MemoryConfigOption.DMA_config = cpu_to_le32(0x20000);
++ }
++#endif
++
++ /* 50% of the allotment of memory blocks go to tx descriptors */
++ TxBlockNum = TotalMemoryBlocks / 2;
++ MemoryConfigOption.TxBlockNum = cpu_to_le16(TxBlockNum);
++
++ /* and 50% go to the rx descriptors */
++ RxBlockNum = TotalMemoryBlocks - TxBlockNum;
++ MemoryConfigOption.RxBlockNum = cpu_to_le16(RxBlockNum);
++
++ /* size of the tx and rx descriptor queues */
++ TotalTxBlockSize = TxBlockNum * adev->memblocksize;
++ TotalRxBlockSize = RxBlockNum * adev->memblocksize;
++ log(L_DEBUG, "TxBlockNum %u RxBlockNum %u TotalTxBlockSize %u "
++ "TotalTxBlockSize %u\n", TxBlockNum, RxBlockNum,
++ TotalTxBlockSize, TotalRxBlockSize);
++
++
++ /* align the tx descriptor queue to an alignment of 0x20 (32 bytes) */
++ MemoryConfigOption.rx_mem =
++ cpu_to_le32((le32_to_cpu(mmt->PoolStart) + 0x1f) & ~0x1f);
++
++ /* align the rx descriptor queue to units of 0x20
++ * and offset it by the tx descriptor queue */
++ MemoryConfigOption.tx_mem =
++ cpu_to_le32((le32_to_cpu(mmt->PoolStart) + TotalRxBlockSize + 0x1f) & ~0x1f);
++ log(L_DEBUG, "rx_mem %08X rx_mem %08X\n",
++ MemoryConfigOption.tx_mem, MemoryConfigOption.rx_mem);
++
++ /* alert the device to our decision */
++ if (OK != acx_s_configure(adev, &MemoryConfigOption, ACX1xx_IE_MEMORY_CONFIG_OPTIONS)) {
++ goto bad;
++ }
++
++ /* and tell the device to kick it into gear */
++ if (OK != acx_s_issue_cmd(adev, ACX100_CMD_INIT_MEMORY, NULL, 0)) {
++ goto bad;
++ }
++#ifdef ACX_MEM
++ /*
++ * slave memory interface has to manage the transmit pools for the ACX,
++ * so it needs to know what we chose here.
++ */
++ adev->acx_txbuf_start = MemoryConfigOption.tx_mem;
++ adev->acx_txbuf_numblocks = MemoryConfigOption.TxBlockNum;
++#endif
++
++ FN_EXIT1(OK);
++ return OK;
++bad:
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++}
++
++
++/***********************************************************************
++** acx100_s_create_dma_regions
++**
++** Note that this fn messes up heavily with hardware, but we cannot
++** lock it (we need to sleep). Not a problem since IRQs can't happen
++*/
++static int
++acx100_s_create_dma_regions(acx_device_t *adev)
++{
++ acx100_ie_queueconfig_t queueconf;
++ acx_ie_memmap_t memmap;
++ int res = NOT_OK;
++ u32 tx_queue_start, rx_queue_start;
++
++ FN_ENTER;
++
++ /* read out the acx100 physical start address for the queues */
++ if (OK != acx_s_interrogate(adev, &memmap, ACX1xx_IE_MEMORY_MAP)) {
++ goto fail;
++ }
++
++ tx_queue_start = le32_to_cpu(memmap.QueueStart);
++ rx_queue_start = tx_queue_start + TX_CNT * sizeof(txdesc_t);
++
++ log(L_DEBUG, "initializing Queue Indicator\n");
++
++ memset(&queueconf, 0, sizeof(queueconf));
++
++ /* Not needed for PCI or slave memory, so we can avoid setting them altogether */
++ if (IS_USB(adev)) {
++ queueconf.NumTxDesc = USB_TX_CNT;
++ queueconf.NumRxDesc = USB_RX_CNT;
++ }
++
++ /* calculate size of queues */
++ queueconf.AreaSize = cpu_to_le32(
++ TX_CNT * sizeof(txdesc_t) +
++ RX_CNT * sizeof(rxdesc_t) + 8
++ );
++ queueconf.NumTxQueues = 1; /* number of tx queues */
++ /* sets the beginning of the tx descriptor queue */
++ queueconf.TxQueueStart = memmap.QueueStart;
++ /* done by memset: queueconf.TxQueuePri = 0; */
++ queueconf.RxQueueStart = cpu_to_le32(rx_queue_start);
++ queueconf.QueueOptions = 1; /* auto reset descriptor */
++ /* sets the end of the rx descriptor queue */
++ queueconf.QueueEnd = cpu_to_le32(
++ rx_queue_start + RX_CNT * sizeof(rxdesc_t)
++ );
++ /* sets the beginning of the next queue */
++ queueconf.HostQueueEnd = cpu_to_le32(le32_to_cpu(queueconf.QueueEnd) + 8);
++ if (OK != acx_s_configure(adev, &queueconf, ACX1xx_IE_QUEUE_CONFIG)) {
++ goto fail;
++ }
++
++#if defined (ACX_MEM)
++ /* sets the beginning of the rx descriptor queue, after the tx descrs */
++ adev->acx_queue_indicator =
++ (queueindicator_t *) le32_to_cpu (queueconf.QueueEnd);
++ if (OK != acxmem_s_create_hostdesc_queues(adev))
++ goto fail;
++
++ acxmem_create_desc_queues(adev, tx_queue_start, rx_queue_start);
++#else
++ if (IS_PCI(adev)) {
++ /* sets the beginning of the rx descriptor queue, after the tx descrs */
++ if (OK != acxpci_s_create_hostdesc_queues(adev))
++ goto fail;
++ acxpci_create_desc_queues(adev, tx_queue_start, rx_queue_start);
++ }
++#endif
++
++ if (OK != acx_s_interrogate(adev, &memmap, ACX1xx_IE_MEMORY_MAP)) {
++ goto fail;
++ }
++
++ /*
++ * Have to make sure we skip past the Queue Indicator (QueueEnd) and Host Queue Indicator
++ * maps, each of which are 8 bytes and follow immediately after the transmit and
++ * receive queues.
++ */
++ memmap.PoolStart = cpu_to_le32(
++ (le32_to_cpu(memmap.QueueEnd) + 4 + 0x1f) & ~0x1f
++ );
++
++ if (OK != acx_s_configure(adev, &memmap, ACX1xx_IE_MEMORY_MAP)) {
++ goto fail;
++ }
++
++ if (OK != acx100_s_init_memory_pools(adev, &memmap)) {
++ goto fail;
++ }
++
++ res = OK;
++ goto end;
++
++fail:
++ acx_s_msleep(1000); /* ? */
++#if defined (ACX_MEM)
++ acxmem_free_desc_queues(adev);
++#else
++ if (IS_PCI(adev))
++ acxpci_free_desc_queues(adev);
++#endif
++end:
++ FN_EXIT1(res);
++ return res;
++}
++
++
++/***********************************************************************
++** acx111_s_create_dma_regions
++**
++** Note that this fn messes heavily with hardware, but we cannot
++** lock it (we need to sleep). Not a problem since IRQs can't happen
++*/
++#define ACX111_PERCENT(percent) ((percent)/5)
++
++static int
++acx111_s_create_dma_regions(acx_device_t *adev)
++{
++ struct acx111_ie_memoryconfig memconf;
++ struct acx111_ie_queueconfig queueconf;
++ u32 tx_queue_start, rx_queue_start;
++
++ FN_ENTER;
++
++ /* Calculate memory positions and queue sizes */
++
++ /* Set up our host descriptor pool + data pool */
++#if defined (ACX_MEM)
++ if (OK != acxmem_s_create_hostdesc_queues(adev))
++ goto fail;
++#else
++ if (IS_PCI(adev)) {
++ if (OK != acxpci_s_create_hostdesc_queues(adev))
++ goto fail;
++ }
++#endif
++
++ memset(&memconf, 0, sizeof(memconf));
++ /* the number of STAs (STA contexts) to support
++ ** NB: was set to 1 and everything seemed to work nevertheless... */
++ memconf.no_of_stations = cpu_to_le16(VEC_SIZE(adev->sta_list));
++ /* specify the memory block size. Default is 256 */
++ memconf.memory_block_size = cpu_to_le16(adev->memblocksize);
++ /* let's use 50%/50% for tx/rx (specify percentage, units of 5%) */
++ memconf.tx_rx_memory_block_allocation = ACX111_PERCENT(50);
++ /* set the count of our queues
++ ** NB: struct acx111_ie_memoryconfig shall be modified
++ ** if we ever will switch to more than one rx and/or tx queue */
++ memconf.count_rx_queues = 1;
++ memconf.count_tx_queues = 1;
++ /* 0 == Busmaster Indirect Memory Organization, which is what we want
++ * (using linked host descs with their allocated mem).
++ * 2 == Generic Bus Slave */
++ /* done by memset: memconf.options = 0; */
++ /* let's use 25% for fragmentations and 75% for frame transfers
++ * (specified in units of 5%) */
++ memconf.fragmentation = ACX111_PERCENT(75);
++ /* Rx descriptor queue config */
++ memconf.rx_queue1_count_descs = RX_CNT;
++ memconf.rx_queue1_type = 7; /* must be set to 7 */
++ /* done by memset: memconf.rx_queue1_prio = 0; low prio */
++#if defined (ACX_MEM)
++ memconf.rx_queue1_host_rx_start = cpu2acx(adev->rxhostdesc_startphy);
++#else
++ if (IS_PCI(adev)) {
++ memconf.rx_queue1_host_rx_start = cpu2acx(adev->rxhostdesc_startphy);
++ }
++#endif
++ /* Tx descriptor queue config */
++ memconf.tx_queue1_count_descs = TX_CNT;
++ /* done by memset: memconf.tx_queue1_attributes = 0; lowest priority */
++
++ /* NB1: this looks wrong: (memconf,ACX1xx_IE_QUEUE_CONFIG),
++ ** (queueconf,ACX1xx_IE_MEMORY_CONFIG_OPTIONS) look swapped, eh?
++ ** But it is actually correct wrt IE numbers.
++ ** NB2: sizeof(memconf) == 28 == 0x1c but configure(ACX1xx_IE_QUEUE_CONFIG)
++ ** writes 0x20 bytes (because same IE for acx100 uses struct acx100_ie_queueconfig
++ ** which is 4 bytes larger. what a mess. TODO: clean it up) */
++ if (OK != acx_s_configure(adev, &memconf, ACX1xx_IE_QUEUE_CONFIG)) {
++ goto fail;
++ }
++
++ acx_s_interrogate(adev, &queueconf, ACX1xx_IE_MEMORY_CONFIG_OPTIONS);
++
++ tx_queue_start = le32_to_cpu(queueconf.tx1_queue_address);
++ rx_queue_start = le32_to_cpu(queueconf.rx1_queue_address);
++
++ log(L_INIT, "dump queue head (from card):\n"
++ "len: %u\n"
++ "tx_memory_block_address: %X\n"
++ "rx_memory_block_address: %X\n"
++ "tx1_queue address: %X\n"
++ "rx1_queue address: %X\n",
++ le16_to_cpu(queueconf.len),
++ le32_to_cpu(queueconf.tx_memory_block_address),
++ le32_to_cpu(queueconf.rx_memory_block_address),
++ tx_queue_start,
++ rx_queue_start);
++
++#if defined (ACX_MEM)
++ acxmem_create_desc_queues(adev, tx_queue_start, rx_queue_start);
++#else
++ if (IS_PCI(adev))
++ acxpci_create_desc_queues(adev, tx_queue_start, rx_queue_start);
++#endif
++
++ FN_EXIT1(OK);
++ return OK;
++fail:
++#if defined (ACX_MEM)
++ acxmem_free_desc_queues(adev);
++#else
++ if (IS_PCI(adev))
++ acxpci_free_desc_queues(adev);
++#endif
++
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++}
++
++
++/***********************************************************************
++*/
++static void
++acx_s_initialize_rx_config(acx_device_t *adev)
++{
++ struct {
++ u16 id;
++ u16 len;
++ u16 rx_cfg1;
++ u16 rx_cfg2;
++ } ACX_PACKED cfg;
++
++ switch (adev->mode) {
++ case ACX_MODE_OFF:
++ adev->rx_config_1 = (u16) (0
++ /* | RX_CFG1_INCLUDE_RXBUF_HDR */
++ /* | RX_CFG1_FILTER_SSID */
++ /* | RX_CFG1_FILTER_BCAST */
++ /* | RX_CFG1_RCV_MC_ADDR1 */
++ /* | RX_CFG1_RCV_MC_ADDR0 */
++ /* | RX_CFG1_FILTER_ALL_MULTI */
++ /* | RX_CFG1_FILTER_BSSID */
++ /* | RX_CFG1_FILTER_MAC */
++ /* | RX_CFG1_RCV_PROMISCUOUS */
++ /* | RX_CFG1_INCLUDE_FCS */
++ /* | RX_CFG1_INCLUDE_PHY_HDR */
++ );
++ adev->rx_config_2 = (u16) (0
++ /*| RX_CFG2_RCV_ASSOC_REQ */
++ /*| RX_CFG2_RCV_AUTH_FRAMES */
++ /*| RX_CFG2_RCV_BEACON_FRAMES */
++ /*| RX_CFG2_RCV_CONTENTION_FREE */
++ /*| RX_CFG2_RCV_CTRL_FRAMES */
++ /*| RX_CFG2_RCV_DATA_FRAMES */
++ /*| RX_CFG2_RCV_BROKEN_FRAMES */
++ /*| RX_CFG2_RCV_MGMT_FRAMES */
++ /*| RX_CFG2_RCV_PROBE_REQ */
++ /*| RX_CFG2_RCV_PROBE_RESP */
++ /*| RX_CFG2_RCV_ACK_FRAMES */
++ /*| RX_CFG2_RCV_OTHER */
++ );
++ break;
++ case ACX_MODE_MONITOR:
++ adev->rx_config_1 = (u16) (0
++ /* | RX_CFG1_INCLUDE_RXBUF_HDR */
++ /* | RX_CFG1_FILTER_SSID */
++ /* | RX_CFG1_FILTER_BCAST */
++ /* | RX_CFG1_RCV_MC_ADDR1 */
++ /* | RX_CFG1_RCV_MC_ADDR0 */
++ /* | RX_CFG1_FILTER_ALL_MULTI */
++ /* | RX_CFG1_FILTER_BSSID */
++ /* | RX_CFG1_FILTER_MAC */
++ | RX_CFG1_RCV_PROMISCUOUS
++ /* | RX_CFG1_INCLUDE_FCS */
++ /* | RX_CFG1_INCLUDE_PHY_HDR */
++ );
++ adev->rx_config_2 = (u16) (0
++ | RX_CFG2_RCV_ASSOC_REQ
++ | RX_CFG2_RCV_AUTH_FRAMES
++ | RX_CFG2_RCV_BEACON_FRAMES
++ | RX_CFG2_RCV_CONTENTION_FREE
++ | RX_CFG2_RCV_CTRL_FRAMES
++ | RX_CFG2_RCV_DATA_FRAMES
++ | RX_CFG2_RCV_BROKEN_FRAMES
++ | RX_CFG2_RCV_MGMT_FRAMES
++ | RX_CFG2_RCV_PROBE_REQ
++ | RX_CFG2_RCV_PROBE_RESP
++ | RX_CFG2_RCV_ACK_FRAMES
++ | RX_CFG2_RCV_OTHER
++ );
++ break;
++ default:
++ adev->rx_config_1 = (u16) (0
++ /* | RX_CFG1_INCLUDE_RXBUF_HDR */
++ /* | RX_CFG1_FILTER_SSID */
++ /* | RX_CFG1_FILTER_BCAST */
++ /* | RX_CFG1_RCV_MC_ADDR1 */
++ /* | RX_CFG1_RCV_MC_ADDR0 */
++ /* | RX_CFG1_FILTER_ALL_MULTI */
++ /* | RX_CFG1_FILTER_BSSID */
++ | RX_CFG1_FILTER_MAC
++ /* | RX_CFG1_RCV_PROMISCUOUS */
++ /* | RX_CFG1_INCLUDE_FCS */
++ /* | RX_CFG1_INCLUDE_PHY_HDR */
++ );
++ adev->rx_config_2 = (u16) (0
++ | RX_CFG2_RCV_ASSOC_REQ
++ | RX_CFG2_RCV_AUTH_FRAMES
++ | RX_CFG2_RCV_BEACON_FRAMES
++ | RX_CFG2_RCV_CONTENTION_FREE
++ | RX_CFG2_RCV_CTRL_FRAMES
++ | RX_CFG2_RCV_DATA_FRAMES
++ /*| RX_CFG2_RCV_BROKEN_FRAMES */
++ | RX_CFG2_RCV_MGMT_FRAMES
++ | RX_CFG2_RCV_PROBE_REQ
++ | RX_CFG2_RCV_PROBE_RESP
++ /*| RX_CFG2_RCV_ACK_FRAMES */
++ | RX_CFG2_RCV_OTHER
++ );
++ break;
++ }
++ adev->rx_config_1 |= RX_CFG1_INCLUDE_RXBUF_HDR;
++
++ if ((adev->rx_config_1 & RX_CFG1_INCLUDE_PHY_HDR)
++ || (adev->firmware_numver >= 0x02000000))
++ adev->phy_header_len = IS_ACX111(adev) ? 8 : 4;
++ else
++ adev->phy_header_len = 0;
++
++ log(L_INIT, "setting RXconfig to %04X:%04X\n",
++ adev->rx_config_1, adev->rx_config_2);
++ cfg.rx_cfg1 = cpu_to_le16(adev->rx_config_1);
++ cfg.rx_cfg2 = cpu_to_le16(adev->rx_config_2);
++ acx_s_configure(adev, &cfg, ACX1xx_IE_RXCONFIG);
++}
++
++
++/***********************************************************************
++** acx_s_set_defaults
++*/
++void
++acx_s_set_defaults(acx_device_t *adev)
++{
++ unsigned long flags;
++
++ FN_ENTER;
++
++ /* do it before getting settings, prevent bogus channel 0 warning */
++ adev->channel = 1;
++
++ /* query some settings from the card.
++ * NOTE: for some settings, e.g. CCA and ED (ACX100!), an initial
++ * query is REQUIRED, otherwise the card won't work correctly! */
++ adev->get_mask = GETSET_ANTENNA|GETSET_SENSITIVITY|GETSET_STATION_ID|GETSET_REG_DOMAIN;
++ /* Only ACX100 supports ED and CCA */
++ if (IS_ACX100(adev))
++ adev->get_mask |= GETSET_CCA|GETSET_ED_THRESH;
++
++ acx_s_update_card_settings(adev);
++
++ acx_lock(adev, flags);
++
++ /* set our global interrupt mask */
++#if defined (ACX_MEM)
++ acxmem_set_interrupt_mask(adev);
++#else
++ if (IS_PCI(adev))
++ acxpci_set_interrupt_mask(adev);
++#endif
++
++ adev->led_power = 1; /* LED is active on startup */
++ adev->brange_max_quality = 60; /* LED blink max quality is 60 */
++ adev->brange_time_last_state_change = jiffies;
++
++ /* copy the MAC address we just got from the card
++ * into our MAC address used during current 802.11 session */
++ MAC_COPY(adev->dev_addr, adev->ndev->dev_addr);
++ MAC_BCAST(adev->ap);
++
++ adev->essid_len =
++ snprintf(adev->essid, sizeof(adev->essid), "STA%02X%02X%02X",
++ adev->dev_addr[3], adev->dev_addr[4], adev->dev_addr[5]);
++ adev->essid_active = 1;
++
++ /* we have a nick field to waste, so why not abuse it
++ * to announce the driver version? ;-) */
++ strncpy(adev->nick, "acx " ACX_RELEASE, IW_ESSID_MAX_SIZE);
++
++#if defined (ACX_MEM)
++ adev->reg_dom_id = adev->cfgopt_domains.list[0];
++#else
++ if (IS_PCI(adev)) { /* FIXME: this should be made to apply to USB, too! */
++ /* first regulatory domain entry in EEPROM == default reg. domain */
++ adev->reg_dom_id = adev->cfgopt_domains.list[0];
++ }
++#endif
++
++ /* 0xffff would be better, but then we won't get a "scan complete"
++ * interrupt, so our current infrastructure will fail: */
++ adev->scan_count = 1;
++ adev->scan_mode = ACX_SCAN_OPT_ACTIVE;
++ adev->scan_duration = 100;
++ adev->scan_probe_delay = 200;
++ /* reported to break scanning: adev->scan_probe_delay = adev->cfgopt_probe_delay; */
++ adev->scan_rate = ACX_SCAN_RATE_1;
++
++ adev->mode = ACX_MODE_2_STA;
++ adev->auth_alg = WLAN_AUTH_ALG_OPENSYSTEM;
++ adev->listen_interval = 100;
++ adev->beacon_interval = DEFAULT_BEACON_INTERVAL;
++ adev->dtim_interval = DEFAULT_DTIM_INTERVAL;
++
++ adev->msdu_lifetime = DEFAULT_MSDU_LIFETIME;
++
++ adev->rts_threshold = DEFAULT_RTS_THRESHOLD;
++ adev->frag_threshold = 2346;
++
++ /* use standard default values for retry limits */
++ adev->short_retry = 7; /* max. retries for (short) non-RTS packets */
++ adev->long_retry = 4; /* max. retries for long (RTS) packets */
++
++ adev->preamble_mode = 2; /* auto */
++ adev->fallback_threshold = 3;
++ adev->stepup_threshold = 10;
++ adev->rate_bcast = RATE111_1;
++ adev->rate_bcast100 = RATE100_1;
++ adev->rate_basic = RATE111_1 | RATE111_2;
++ adev->rate_auto = 1;
++ if (IS_ACX111(adev)) {
++ adev->rate_oper = RATE111_ALL;
++ } else {
++ adev->rate_oper = RATE111_ACX100_COMPAT;
++ }
++
++ /* Supported Rates element - the rates here are given in units of
++ * 500 kbit/s, plus 0x80 added. See 802.11-1999.pdf item 7.3.2.2 */
++ acx_l_update_ratevector(adev);
++
++ /* set some more defaults */
++ if (IS_ACX111(adev)) {
++ /* 30mW (15dBm) is default, at least in my acx111 card: */
++ adev->tx_level_dbm = 15;
++ } else {
++ /* don't use max. level, since it might be dangerous
++ * (e.g. WRT54G people experience
++ * excessive Tx power damage!) */
++ adev->tx_level_dbm = 18;
++ /*
++ * Lower power for the iPaq hx4700
++ */
++ if (IS_MEM(adev)) {
++ adev->tx_level_dbm = 14;
++ }
++ }
++ /* adev->tx_level_auto = 1; */
++ if (IS_ACX111(adev)) {
++ /* start with sensitivity level 1 out of 3: */
++ adev->sensitivity = 1;
++ }
++
++/* #define ENABLE_POWER_SAVE */
++#ifdef ENABLE_POWER_SAVE
++ adev->ps_wakeup_cfg = PS_CFG_ENABLE | PS_CFG_WAKEUP_ALL_BEAC;
++ adev->ps_listen_interval = 1;
++ adev->ps_options = PS_OPT_ENA_ENHANCED_PS | PS_OPT_TX_PSPOLL | PS_OPT_STILL_RCV_BCASTS;
++ adev->ps_hangover_period = 30;
++ adev->ps_enhanced_transition_time = 0;
++#else
++ adev->ps_wakeup_cfg = 0;
++ adev->ps_listen_interval = 0;
++ adev->ps_options = 0;
++ adev->ps_hangover_period = 0;
++ adev->ps_enhanced_transition_time = 0;
++#endif
++
++ /* These settings will be set in fw on ifup */
++ adev->set_mask = 0
++ | GETSET_RETRY
++ | SET_MSDU_LIFETIME
++ /* configure card to do rate fallback when in auto rate mode */
++ | SET_RATE_FALLBACK
++ | SET_RXCONFIG
++ | GETSET_TXPOWER
++ /* better re-init the antenna value we got above */
++ | GETSET_ANTENNA
++#if POWER_SAVE_80211
++ | GETSET_POWER_80211
++#endif
++ ;
++
++ acx_unlock(adev, flags);
++ acx_lock_unhold(); /* hold time 844814 CPU ticks @2GHz */
++
++ acx_s_initialize_rx_config(adev);
++
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** FIXME: this should be solved in a general way for all radio types
++** by decoding the radio firmware module,
++** since it probably has some standard structure describing how to
++** set the power level of the radio module which it controls.
++** Or maybe not, since the radio module probably has a function interface
++** instead which then manages Tx level programming :-\
++*/
++static int
++acx111_s_set_tx_level(acx_device_t *adev, u8 level_dbm)
++{
++ struct acx111_ie_tx_level tx_level;
++
++ /* my acx111 card has two power levels in its configoptions (== EEPROM):
++ * 1 (30mW) [15dBm]
++ * 2 (10mW) [10dBm]
++ * For now, just assume all other acx111 cards have the same.
++ * FIXME: Ideally we would query it here, but we first need a
++ * standard way to query individual configoptions easily.
++ * Well, now we have proper cfgopt txpower variables, but this still
++ * hasn't been done yet, since it also requires dBm <-> mW conversion here... */
++ if (level_dbm <= 12) {
++ tx_level.level = 2; /* 10 dBm */
++ adev->tx_level_dbm = 10;
++ } else {
++ tx_level.level = 1; /* 15 dBm */
++ adev->tx_level_dbm = 15;
++ }
++ if (level_dbm != adev->tx_level_dbm)
++ log(L_INIT, "acx111 firmware has specific "
++ "power levels only: adjusted %d dBm to %d dBm!\n",
++ level_dbm, adev->tx_level_dbm);
++
++ return acx_s_configure(adev, &tx_level, ACX1xx_IE_DOT11_TX_POWER_LEVEL);
++}
++
++static int
++acx_s_set_tx_level(acx_device_t *adev, u8 level_dbm)
++{
++ if (IS_ACX111(adev)) {
++ return acx111_s_set_tx_level(adev, level_dbm);
++ }
++#if defined (ACX_MEM)
++ return acx100mem_s_set_tx_level(adev, level_dbm);
++#else
++ if (IS_PCI(adev)) {
++ return acx100pci_s_set_tx_level(adev, level_dbm);
++ }
++#endif
++ return OK;
++}
++
++
++/***********************************************************************
++*/
++#ifdef UNUSED
++/* Returns the current tx level (ACX111) */
++static u8
++acx111_s_get_tx_level(acx_device_t *adev)
++{
++ struct acx111_ie_tx_level tx_level;
++
++ tx_level.level = 0;
++ acx_s_interrogate(adev, &tx_level, ACX1xx_IE_DOT11_TX_POWER_LEVEL);
++ return tx_level.level;
++}
++#endif
++
++
++/***********************************************************************
++** acx_l_rxmonitor
++** Called from IRQ context only
++*/
++static void
++acx_l_rxmonitor(acx_device_t *adev, const rxbuffer_t *rxbuf)
++{
++ wlansniffrm_t *msg;
++ struct sk_buff *skb;
++ void *datap;
++ unsigned int skb_len;
++ int payload_offset;
++
++ FN_ENTER;
++
++ /* we are in big luck: the acx100 doesn't modify any of the fields */
++ /* in the 802.11 frame. just pass this packet into the PF_PACKET */
++ /* subsystem. yeah. */
++ payload_offset = ((u8*)acx_get_wlan_hdr(adev, rxbuf) - (u8*)rxbuf);
++ skb_len = RXBUF_BYTES_USED(rxbuf) - payload_offset;
++
++ /* sanity check */
++ if (unlikely(skb_len > WLAN_A4FR_MAXLEN_WEP)) {
++ printk("%s: monitor mode panic: oversized frame!\n",
++ adev->ndev->name);
++ goto end;
++ }
++
++ if (adev->ndev->type == ARPHRD_IEEE80211_PRISM)
++ skb_len += sizeof(*msg);
++
++ /* allocate skb */
++ skb = dev_alloc_skb(skb_len);
++ if (unlikely(!skb)) {
++ printk("%s: no memory for skb (%u bytes)\n",
++ adev->ndev->name, skb_len);
++ goto end;
++ }
++
++ skb_put(skb, skb_len);
++
++ if (adev->ndev->type == ARPHRD_IEEE80211) {
++ /* when in raw 802.11 mode, just copy frame as-is */
++ datap = skb->data;
++ } else if (adev->ndev->type == ARPHRD_IEEE80211_PRISM) {
++ /* emulate prism header */
++ msg = (wlansniffrm_t*)skb->data;
++ datap = msg + 1;
++
++ msg->msgcode = WLANSNIFFFRM;
++ msg->msglen = sizeof(*msg);
++ strncpy(msg->devname, adev->ndev->name, sizeof(msg->devname)-1);
++ msg->devname[sizeof(msg->devname)-1] = '\0';
++
++ msg->hosttime.did = WLANSNIFFFRM_hosttime;
++ msg->hosttime.status = WLANITEM_STATUS_data_ok;
++ msg->hosttime.len = 4;
++ msg->hosttime.data = jiffies;
++
++ msg->mactime.did = WLANSNIFFFRM_mactime;
++ msg->mactime.status = WLANITEM_STATUS_data_ok;
++ msg->mactime.len = 4;
++ msg->mactime.data = rxbuf->time;
++
++ msg->channel.did = WLANSNIFFFRM_channel;
++ msg->channel.status = WLANITEM_STATUS_data_ok;
++ msg->channel.len = 4;
++ msg->channel.data = adev->channel;
++
++ msg->rssi.did = WLANSNIFFFRM_rssi;
++ msg->rssi.status = WLANITEM_STATUS_no_value;
++ msg->rssi.len = 4;
++ msg->rssi.data = 0;
++
++ msg->sq.did = WLANSNIFFFRM_sq;
++ msg->sq.status = WLANITEM_STATUS_no_value;
++ msg->sq.len = 4;
++ msg->sq.data = 0;
++
++ msg->signal.did = WLANSNIFFFRM_signal;
++ msg->signal.status = WLANITEM_STATUS_data_ok;
++ msg->signal.len = 4;
++ msg->signal.data = rxbuf->phy_snr;
++
++ msg->noise.did = WLANSNIFFFRM_noise;
++ msg->noise.status = WLANITEM_STATUS_data_ok;
++ msg->noise.len = 4;
++ msg->noise.data = rxbuf->phy_level;
++
++ msg->rate.did = WLANSNIFFFRM_rate;
++ msg->rate.status = WLANITEM_STATUS_data_ok;
++ msg->rate.len = 4;
++ msg->rate.data = rxbuf->phy_plcp_signal / 5;
++
++ msg->istx.did = WLANSNIFFFRM_istx;
++ msg->istx.status = WLANITEM_STATUS_data_ok;
++ msg->istx.len = 4;
++ msg->istx.data = 0; /* tx=0: it's not a tx packet */
++
++ skb_len -= sizeof(*msg);
++
++ msg->frmlen.did = WLANSNIFFFRM_signal;
++ msg->frmlen.status = WLANITEM_STATUS_data_ok;
++ msg->frmlen.len = 4;
++ msg->frmlen.data = skb_len;
++ } else {
++ printk("acx: unsupported netdev type %d!\n", adev->ndev->type);
++ dev_kfree_skb(skb);
++ return;
++ }
++
++ /* sanity check (keep it here) */
++ if (unlikely((int)skb_len < 0)) {
++ printk("acx: skb_len=%d. Driver bug, please report\n", (int)skb_len);
++ dev_kfree_skb(skb);
++ return;
++ }
++ memcpy(datap, ((unsigned char*)rxbuf)+payload_offset, skb_len);
++
++ skb->dev = adev->ndev;
++ skb->dev->last_rx = jiffies;
++
++ skb_reset_mac_header(skb);
++ skb->ip_summed = CHECKSUM_NONE;
++ skb->pkt_type = PACKET_OTHERHOST;
++ skb->protocol = htons(ETH_P_80211_RAW);
++ netif_rx(skb);
++
++ adev->stats.rx_packets++;
++ adev->stats.rx_bytes += skb->len;
++
++end:
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acx_l_rx_ieee802_11_frame
++**
++** Called from IRQ context only
++*/
++
++/* All these contortions are for saner dup logging
++**
++** We want: (a) to know about excessive dups
++** (b) to not spam kernel log about occasional dups
++**
++** 1/64 threshold was chosen by running "ping -A"
++** It gave "rx: 59 DUPs in 2878 packets" only with 4 parallel
++** "ping -A" streams running. */
++/* 2005-10-11: bumped up to 1/8
++** subtract a $smallint from dup_count in order to
++** avoid "2 DUPs in 19 packets" messages */
++static inline int
++acx_l_handle_dup(acx_device_t *adev, u16 seq)
++{
++ if (adev->dup_count) {
++ adev->nondup_count++;
++ if (time_after(jiffies, adev->dup_msg_expiry)) {
++ /* Log only if more than 1 dup in 64 packets */
++ if (adev->nondup_count/8 < adev->dup_count-5) {
++ printk(KERN_INFO "%s: rx: %d DUPs in "
++ "%d packets received in 10 secs\n",
++ adev->ndev->name,
++ adev->dup_count,
++ adev->nondup_count);
++ }
++ adev->dup_count = 0;
++ adev->nondup_count = 0;
++ }
++ }
++ if (unlikely(seq == adev->last_seq_ctrl)) {
++ if (!adev->dup_count++)
++ adev->dup_msg_expiry = jiffies + 10*HZ;
++ adev->stats.rx_errors++;
++ return 1; /* a dup */
++ }
++ adev->last_seq_ctrl = seq;
++ return 0;
++}
++
++static int
++acx_l_rx_ieee802_11_frame(acx_device_t *adev, rxbuffer_t *rxbuf)
++{
++ unsigned int ftype, fstype;
++ const wlan_hdr_t *hdr;
++ int result = NOT_OK;
++
++ FN_ENTER;
++
++ hdr = acx_get_wlan_hdr(adev, rxbuf);
++
++ /* see IEEE 802.11-1999.pdf chapter 7 "MAC frame formats" */
++ if (unlikely((hdr->fc & WF_FC_PVERi) != 0)) {
++ printk_ratelimited(KERN_INFO "rx: unsupported 802.11 protocol\n");
++ goto end;
++ }
++
++ ftype = hdr->fc & WF_FC_FTYPEi;
++ fstype = hdr->fc & WF_FC_FSTYPEi;
++
++ switch (ftype) {
++ /* check data frames first, for speed */
++ case WF_FTYPE_DATAi:
++ switch (fstype) {
++ case WF_FSTYPE_DATAONLYi:
++ if (acx_l_handle_dup(adev, hdr->seq))
++ break; /* a dup, simply discard it */
++
++ /* TODO:
++ if (WF_FC_FROMTODSi == (hdr->fc & WF_FC_FROMTODSi)) {
++ result = acx_l_process_data_frame_wds(adev, rxbuf);
++ break;
++ }
++ */
++
++ switch (adev->mode) {
++ case ACX_MODE_3_AP:
++ result = acx_l_process_data_frame_master(adev, rxbuf);
++ break;
++ case ACX_MODE_0_ADHOC:
++ case ACX_MODE_2_STA:
++ result = acx_l_process_data_frame_client(adev, rxbuf);
++ break;
++ }
++ case WF_FSTYPE_DATA_CFACKi:
++ case WF_FSTYPE_DATA_CFPOLLi:
++ case WF_FSTYPE_DATA_CFACK_CFPOLLi:
++ case WF_FSTYPE_CFPOLLi:
++ case WF_FSTYPE_CFACK_CFPOLLi:
++ /* see above.
++ acx_process_class_frame(adev, rxbuf, 3); */
++ break;
++ case WF_FSTYPE_NULLi:
++ /* acx_l_process_NULL_frame(adev, rxbuf, 3); */
++ break;
++ /* FIXME: same here, see above */
++ case WF_FSTYPE_CFACKi:
++ default:
++ break;
++ }
++ break;
++ case WF_FTYPE_MGMTi:
++ result = acx_l_process_mgmt_frame(adev, rxbuf);
++ break;
++ case WF_FTYPE_CTLi:
++ if (fstype == WF_FSTYPE_PSPOLLi)
++ result = OK;
++ /* this call is irrelevant, since
++ * acx_process_class_frame is a stub, so return
++ * immediately instead.
++ * return acx_process_class_frame(adev, rxbuf, 3); */
++ break;
++ default:
++ break;
++ }
++end:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acx_l_process_rxbuf
++**
++** NB: used by USB code also
++*/
++void
++acx_l_process_rxbuf(acx_device_t *adev, rxbuffer_t *rxbuf)
++{
++ struct wlan_hdr *hdr;
++ unsigned int qual;
++ int buf_len;
++ u16 fc;
++
++ hdr = acx_get_wlan_hdr(adev, rxbuf);
++ fc = le16_to_cpu(hdr->fc);
++ /* length of frame from control field to first byte of FCS */
++ buf_len = RXBUF_BYTES_RCVD(adev, rxbuf);
++
++ if ( ((WF_FC_FSTYPE & fc) != WF_FSTYPE_BEACON)
++ || (acx_debug & L_XFER_BEACON)
++ ) {
++ log(L_XFER|L_DATA, "rx: %s "
++ "time:%u len:%u signal:%u SNR:%u macstat:%02X "
++ "phystat:%02X phyrate:%u status:%u\n",
++ acx_get_packet_type_string(fc),
++ le32_to_cpu(rxbuf->time),
++ buf_len,
++ acx_signal_to_winlevel(rxbuf->phy_level),
++ acx_signal_to_winlevel(rxbuf->phy_snr),
++ rxbuf->mac_status,
++ rxbuf->phy_stat_baseband,
++ rxbuf->phy_plcp_signal,
++ adev->status);
++ }
++
++ if (unlikely(acx_debug & L_DATA)) {
++ printk("rx: 802.11 buf[%u]: ", buf_len);
++ acx_dump_bytes(hdr, buf_len);
++ }
++
++ /* FIXME: should check for Rx errors (rxbuf->mac_status?
++ * discard broken packets - but NOT for monitor!)
++ * and update Rx packet statistics here */
++
++ if (unlikely(adev->mode == ACX_MODE_MONITOR)) {
++ acx_l_rxmonitor(adev, rxbuf);
++ } else if (likely(buf_len >= WLAN_HDR_A3_LEN)) {
++ acx_l_rx_ieee802_11_frame(adev, rxbuf);
++ } else {
++ log(L_DEBUG|L_XFER|L_DATA,
++ "rx: NOT receiving packet (%s): "
++ "size too small (%u)\n",
++ acx_get_packet_type_string(fc),
++ buf_len);
++ }
++
++ /* Now check Rx quality level, AFTER processing packet.
++ * I tried to figure out how to map these levels to dBm
++ * values, but for the life of me I really didn't
++ * manage to get it. Either these values are not meant to
++ * be expressed in dBm, or it's some pretty complicated
++ * calculation. */
++
++#ifdef FROM_SCAN_SOURCE_ONLY
++ /* only consider packets originating from the MAC
++ * address of the device that's managing our BSSID.
++ * Disable it for now, since it removes information (levels
++ * from different peers) and slows the Rx path. */
++ if (adev->ap_client
++ && mac_is_equal(hdr->a2, adev->ap_client->address)) {
++#endif
++ adev->wstats.qual.level = acx_signal_to_winlevel(rxbuf->phy_level);
++ adev->wstats.qual.noise = acx_signal_to_winlevel(rxbuf->phy_snr);
++#ifndef OLD_QUALITY
++ qual = acx_signal_determine_quality(adev->wstats.qual.level,
++ adev->wstats.qual.noise);
++#else
++ qual = (adev->wstats.qual.noise <= 100) ?
++ 100 - adev->wstats.qual.noise : 0;
++#endif
++ adev->wstats.qual.qual = qual;
++ adev->wstats.qual.updated = 7; /* all 3 indicators updated */
++#ifdef FROM_SCAN_SOURCE_ONLY
++ }
++#endif
++}
++
++
++/***********************************************************************
++** acx_l_handle_txrate_auto
++**
++** Theory of operation:
++** client->rate_cap is a bitmask of rates client is capable of.
++** client->rate_cfg is a bitmask of allowed (configured) rates.
++** It is set as a result of iwconfig rate N [auto]
++** or iwpriv set_rates "N,N,N N,N,N" commands.
++** It can be fixed (e.g. 0x0080 == 18Mbit only),
++** auto (0x00ff == 18Mbit or any lower value),
++** and code handles any bitmask (0x1081 == try 54Mbit,18Mbit,1Mbit _only_).
++**
++** client->rate_cur is a value for rate111 field in tx descriptor.
++** It is always set to txrate_cfg sans zero or more most significant
++** bits. This routine handles selection of new rate_cur value depending on
++** outcome of last tx event.
++**
++** client->rate_100 is a precalculated rate value for acx100
++** (we can do without it, but will need to calculate it on each tx).
++**
++** You cannot configure mixed usage of 5.5 and/or 11Mbit rate
++** with PBCC and CCK modulation. Either both at CCK or both at PBCC.
++** In theory you can implement it, but so far it is considered not worth doing.
++**
++** 22Mbit, of course, is PBCC always. */
++
++/* maps acx100 tx descr rate field to acx111 one */
++static u16
++rate100to111(u8 r)
++{
++ switch (r) {
++ case RATE100_1: return RATE111_1;
++ case RATE100_2: return RATE111_2;
++ case RATE100_5:
++ case (RATE100_5 | RATE100_PBCC511): return RATE111_5;
++ case RATE100_11:
++ case (RATE100_11 | RATE100_PBCC511): return RATE111_11;
++ case RATE100_22: return RATE111_22;
++ default:
++ printk("acx: unexpected acx100 txrate: %u! "
++ "Please report\n", r);
++ return RATE111_1;
++ }
++}
++
++
++void
++acx_l_handle_txrate_auto(acx_device_t *adev, struct client *txc,
++ u16 cur, u8 rate100, u16 rate111,
++ u8 error, int pkts_to_ignore)
++{
++ u16 sent_rate;
++ int slower_rate_was_used;
++
++ /* vda: hmm. current code will do this:
++ ** 1. send packets at 11 Mbit, stepup++
++ ** 2. will try to send at 22Mbit. hardware will see no ACK,
++ ** retries at 11Mbit, success. code notes that used rate
++ ** is lower. stepup = 0, fallback++
++ ** 3. repeat step 2 fallback_count times. Fall back to
++ ** 11Mbit. go to step 1.
++ ** If stepup_count is large (say, 16) and fallback_count
++ ** is small (3), this wouldn't be too bad wrt throughput */
++
++ if (unlikely(!cur)) {
++ printk("acx: BUG! ratemask is empty\n");
++ return; /* or else we may lock up the box */
++ }
++
++ /* do some preparations, i.e. calculate the one rate that was
++ * used to send this packet */
++ if (IS_ACX111(adev)) {
++ sent_rate = 1 << highest_bit(rate111 & RATE111_ALL);
++ } else {
++ sent_rate = rate100to111(rate100);
++ }
++ /* sent_rate has only one bit set now, corresponding to tx rate
++ * which was used by hardware to tx this particular packet */
++
++ /* now do the actual auto rate management */
++ log(L_XFER, "tx: %sclient=%p/"MACSTR" used=%04X cur=%04X cfg=%04X "
++ "__=%u/%u ^^=%u/%u\n",
++ (txc->ignore_count > 0) ? "[IGN] " : "",
++ txc, MAC(txc->address), sent_rate, cur, txc->rate_cfg,
++ txc->fallback_count, adev->fallback_threshold,
++ txc->stepup_count, adev->stepup_threshold
++ );
++
++ /* we need to ignore old packets already in the tx queue since
++ * they use older rate bytes configured before our last rate change,
++ * otherwise our mechanism will get confused by interpreting old data.
++ * Do it after logging above */
++ if (txc->ignore_count) {
++ txc->ignore_count--;
++ return;
++ }
++
++ /* true only if the only nonzero bit in sent_rate is
++ ** less significant than highest nonzero bit in cur */
++ slower_rate_was_used = ( cur > ((sent_rate<<1)-1) );
++
++ if (slower_rate_was_used || error) {
++ txc->stepup_count = 0;
++ if (++txc->fallback_count <= adev->fallback_threshold)
++ return;
++ txc->fallback_count = 0;
++
++ /* clear highest 1 bit in cur */
++ sent_rate = RATE111_54;
++ while (!(cur & sent_rate)) sent_rate >>= 1;
++ CLEAR_BIT(cur, sent_rate);
++ if (!cur) /* we can't disable all rates! */
++ cur = sent_rate;
++ log(L_XFER, "tx: falling back to ratemask %04X\n", cur);
++
++ } else { /* there was neither lower rate nor error */
++ txc->fallback_count = 0;
++ if (++txc->stepup_count <= adev->stepup_threshold)
++ return;
++ txc->stepup_count = 0;
++
++ /* Sanitize. Sort of not needed, but I dont trust hw that much...
++ ** what if it can report bogus tx rates sometimes? */
++ while (!(cur & sent_rate)) sent_rate >>= 1;
++
++ /* try to find a higher sent_rate that isn't yet in our
++ * current set, but is an allowed cfg */
++ while (1) {
++ sent_rate <<= 1;
++ if (sent_rate > txc->rate_cfg)
++ /* no higher rates allowed by config */
++ return;
++ if (!(cur & sent_rate) && (txc->rate_cfg & sent_rate))
++ /* found */
++ break;
++ /* not found, try higher one */
++ }
++ SET_BIT(cur, sent_rate);
++ log(L_XFER, "tx: stepping up to ratemask %04X\n", cur);
++ }
++
++ txc->rate_cur = cur;
++ txc->ignore_count = pkts_to_ignore;
++ /* calculate acx100 style rate byte if needed */
++ if (IS_ACX100(adev)) {
++ txc->rate_100 = acx_bitpos2rate100[highest_bit(cur)];
++ }
++}
++
++
++/***********************************************************************
++** acx_i_start_xmit
++**
++** Called by network core. Can be called outside of process context.
++*/
++int
++acx_i_start_xmit(struct sk_buff *skb, struct net_device *ndev)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ tx_t *tx;
++ void *txbuf;
++ unsigned long flags;
++ int txresult = NOT_OK;
++ int len;
++
++ FN_ENTER;
++
++ if (unlikely(!skb)) {
++ /* indicate success */
++ txresult = OK;
++ goto end_no_unlock;
++ }
++ if (unlikely(!adev)) {
++ goto end_no_unlock;
++ }
++
++ acx_lock(adev, flags);
++
++ if (unlikely(!(adev->dev_state_mask & ACX_STATE_IFACE_UP))) {
++ goto end;
++ }
++ if (unlikely(adev->mode == ACX_MODE_OFF)) {
++ goto end;
++ }
++ if (unlikely(acx_queue_stopped(ndev))) {
++ log(L_DEBUG, "%s: called when queue stopped\n", __func__);
++ goto end;
++ }
++ if (unlikely(ACX_STATUS_4_ASSOCIATED != adev->status)) {
++ log(L_XFER, "trying to xmit, but not associated yet: "
++ "aborting...\n");
++ /* silently drop the packet, since we're not connected yet */
++ txresult = OK;
++ /* ...but indicate an error nevertheless */
++ adev->stats.tx_errors++;
++ goto end;
++ }
++
++ tx = acx_l_alloc_tx(adev);
++ if (unlikely(!tx)) {
++#ifndef ACX_MEM
++ /*
++ * generic slave interface has to make do with the tiny amount, around
++ * 7k, of transmit buffer space on the ACX itself. It is likely this will
++ * frequently be full.
++ */
++ printk_ratelimited("%s: start_xmit: txdesc ring is full, "
++ "dropping tx\n", ndev->name);
++#endif
++ txresult = NOT_OK;
++ goto end;
++ }
++
++ txbuf = acx_l_get_txbuf(adev, tx);
++ if (unlikely(!txbuf)) {
++ /* Card was removed */
++ txresult = NOT_OK;
++ acx_l_dealloc_tx(adev, tx);
++ goto end;
++ }
++ len = acx_ether_to_txbuf(adev, txbuf, skb);
++ if (unlikely(len < 0)) {
++ /* Error in packet conversion */
++ txresult = NOT_OK;
++ acx_l_dealloc_tx(adev, tx);
++ goto end;
++ }
++ acx_l_tx_data(adev, tx, len);
++ ndev->trans_start = jiffies;
++
++ txresult = OK;
++ adev->stats.tx_packets++;
++ adev->stats.tx_bytes += skb->len;
++
++end:
++ acx_unlock(adev, flags);
++
++end_no_unlock:
++ if ((txresult == OK) && skb)
++ dev_kfree_skb_any(skb);
++
++ FN_EXIT1(txresult);
++ return txresult;
++}
++
++
++/***********************************************************************
++** acx_l_update_ratevector
++**
++** Updates adev->rate_supported[_len] according to rate_{basic,oper}
++*/
++const u8
++acx_bitpos2ratebyte[] = {
++ DOT11RATEBYTE_1,
++ DOT11RATEBYTE_2,
++ DOT11RATEBYTE_5_5,
++ DOT11RATEBYTE_6_G,
++ DOT11RATEBYTE_9_G,
++ DOT11RATEBYTE_11,
++ DOT11RATEBYTE_12_G,
++ DOT11RATEBYTE_18_G,
++ DOT11RATEBYTE_22,
++ DOT11RATEBYTE_24_G,
++ DOT11RATEBYTE_36_G,
++ DOT11RATEBYTE_48_G,
++ DOT11RATEBYTE_54_G,
++};
++
++void
++acx_l_update_ratevector(acx_device_t *adev)
++{
++ u16 bcfg = adev->rate_basic;
++ u16 ocfg = adev->rate_oper;
++ u8 *supp = adev->rate_supported;
++ const u8 *dot11 = acx_bitpos2ratebyte;
++
++ FN_ENTER;
++
++ while (ocfg) {
++ if (ocfg & 1) {
++ *supp = *dot11;
++ if (bcfg & 1) {
++ *supp |= 0x80;
++ }
++ supp++;
++ }
++ dot11++;
++ ocfg >>= 1;
++ bcfg >>= 1;
++ }
++ adev->rate_supported_len = supp - adev->rate_supported;
++ if (acx_debug & L_ASSOC) {
++ printk("new ratevector: ");
++ acx_dump_bytes(adev->rate_supported, adev->rate_supported_len);
++ }
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acx_l_sta_list_init
++*/
++static void
++acx_l_sta_list_init(acx_device_t *adev)
++{
++ FN_ENTER;
++ memset(adev->sta_hash_tab, 0, sizeof(adev->sta_hash_tab));
++ memset(adev->sta_list, 0, sizeof(adev->sta_list));
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acx_l_sta_list_get_from_hash
++*/
++static inline client_t*
++acx_l_sta_list_get_from_hash(acx_device_t *adev, const u8 *address)
++{
++ return adev->sta_hash_tab[address[5] % VEC_SIZE(adev->sta_hash_tab)];
++}
++
++
++/***********************************************************************
++** acx_l_sta_list_get
++*/
++client_t*
++acx_l_sta_list_get(acx_device_t *adev, const u8 *address)
++{
++ client_t *client;
++ FN_ENTER;
++ client = acx_l_sta_list_get_from_hash(adev, address);
++ while (client) {
++ if (mac_is_equal(address, client->address)) {
++ client->mtime = jiffies;
++ break;
++ }
++ client = client->next;
++ }
++ FN_EXIT0;
++ return client;
++}
++
++
++/***********************************************************************
++** acx_l_sta_list_del
++*/
++void
++acx_l_sta_list_del(acx_device_t *adev, client_t *victim)
++{
++ client_t *client, *next;
++
++ client = acx_l_sta_list_get_from_hash(adev, victim->address);
++ next = client;
++ /* tricky. next = client on first iteration only,
++ ** on all other iters next = client->next */
++ while (next) {
++ if (next == victim) {
++ client->next = victim->next;
++ /* Overkill */
++ memset(victim, 0, sizeof(*victim));
++ break;
++ }
++ client = next;
++ next = client->next;
++ }
++}
++
++
++/***********************************************************************
++** acx_l_sta_list_alloc
++**
++** Never fails - will evict oldest client if needed
++*/
++static client_t*
++acx_l_sta_list_alloc(acx_device_t *adev)
++{
++ int i;
++ unsigned long age, oldest_age;
++ client_t *client, *oldest;
++
++ FN_ENTER;
++
++ oldest = &adev->sta_list[0];
++ oldest_age = 0;
++ for (i = 0; i < VEC_SIZE(adev->sta_list); i++) {
++ client = &adev->sta_list[i];
++
++ if (!client->used) {
++ goto found;
++ } else {
++ age = jiffies - client->mtime;
++ if (oldest_age < age) {
++ oldest_age = age;
++ oldest = client;
++ }
++ }
++ }
++ acx_l_sta_list_del(adev, oldest);
++ client = oldest;
++found:
++ memset(client, 0, sizeof(*client));
++ FN_EXIT0;
++ return client;
++}
++
++
++/***********************************************************************
++** acx_l_sta_list_add
++**
++** Never fails - will evict oldest client if needed
++*/
++/* In case we will reimplement it differently... */
++#define STA_LIST_ADD_CAN_FAIL 0
++
++static client_t*
++acx_l_sta_list_add(acx_device_t *adev, const u8 *address)
++{
++ client_t *client;
++ int index;
++
++ FN_ENTER;
++
++ client = acx_l_sta_list_alloc(adev);
++
++ client->mtime = jiffies;
++ MAC_COPY(client->address, address);
++ client->used = CLIENT_EXIST_1;
++ client->auth_alg = WLAN_AUTH_ALG_SHAREDKEY;
++ client->auth_step = 1;
++ /* give some tentative peer rate values
++ ** (needed because peer may do auth without probing us first,
++ ** thus we'll have no idea of peer's ratevector yet).
++ ** Will be overwritten by scanning or assoc code */
++ client->rate_cap = adev->rate_basic;
++ client->rate_cfg = adev->rate_basic;
++ client->rate_cur = 1 << lowest_bit(adev->rate_basic);
++
++ index = address[5] % VEC_SIZE(adev->sta_hash_tab);
++ client->next = adev->sta_hash_tab[index];
++ adev->sta_hash_tab[index] = client;
++
++ acxlog_mac(L_ASSOC, "sta_list_add: sta=", address, "\n");
++
++ FN_EXIT0;
++ return client;
++}
++
++
++/***********************************************************************
++** acx_l_sta_list_get_or_add
++**
++** Never fails - will evict oldest client if needed
++*/
++static client_t*
++acx_l_sta_list_get_or_add(acx_device_t *adev, const u8 *address)
++{
++ client_t *client = acx_l_sta_list_get(adev, address);
++ if (!client)
++ client = acx_l_sta_list_add(adev, address);
++ return client;
++}
++
++
++/***********************************************************************
++** acx_set_status
++**
++** This function is called in many atomic regions, must not sleep
++**
++** This function does not need locking UNLESS you call it
++** as acx_set_status(ACX_STATUS_4_ASSOCIATED), bacause this can
++** wake queue. This can race with stop_queue elsewhere.
++** See acx_stop_queue comment. */
++void
++acx_set_status(acx_device_t *adev, u16 new_status)
++{
++#define QUEUE_OPEN_AFTER_ASSOC 1 /* this really seems to be needed now */
++ u16 old_status = adev->status;
++
++ FN_ENTER;
++
++ log(L_ASSOC, "%s(%d):%s\n",
++ __func__, new_status, acx_get_status_name(new_status));
++
++ /* wireless_send_event never sleeps */
++ if (ACX_STATUS_4_ASSOCIATED == new_status) {
++ union iwreq_data wrqu;
++
++ wrqu.data.length = 0;
++ wrqu.data.flags = 0;
++ wireless_send_event(adev->ndev, SIOCGIWSCAN, &wrqu, NULL);
++
++ wrqu.data.length = 0;
++ wrqu.data.flags = 0;
++ MAC_COPY(wrqu.ap_addr.sa_data, adev->bssid);
++ wrqu.ap_addr.sa_family = ARPHRD_ETHER;
++ wireless_send_event(adev->ndev, SIOCGIWAP, &wrqu, NULL);
++ } else {
++ union iwreq_data wrqu;
++
++ /* send event with empty BSSID to indicate we're not associated */
++ MAC_ZERO(wrqu.ap_addr.sa_data);
++ wrqu.ap_addr.sa_family = ARPHRD_ETHER;
++ wireless_send_event(adev->ndev, SIOCGIWAP, &wrqu, NULL);
++ }
++
++ adev->status = new_status;
++
++ switch (new_status) {
++ case ACX_STATUS_1_SCANNING:
++ adev->scan_retries = 0;
++ /* 1.0 s initial scan time */
++ acx_set_timer(adev, 1000000);
++ break;
++ case ACX_STATUS_2_WAIT_AUTH:
++ case ACX_STATUS_3_AUTHENTICATED:
++ adev->auth_or_assoc_retries = 0;
++ acx_set_timer(adev, 1500000); /* 1.5 s */
++ break;
++ }
++
++#if QUEUE_OPEN_AFTER_ASSOC
++ if (new_status == ACX_STATUS_4_ASSOCIATED) {
++ if (old_status < ACX_STATUS_4_ASSOCIATED) {
++ /* ah, we're newly associated now,
++ * so let's indicate carrier */
++ acx_carrier_on(adev->ndev, "after association");
++ acx_wake_queue(adev->ndev, "after association");
++ }
++ } else {
++ /* not associated any more, so let's kill carrier */
++ if (old_status >= ACX_STATUS_4_ASSOCIATED) {
++ acx_carrier_off(adev->ndev, "after losing association");
++ acx_stop_queue(adev->ndev, "after losing association");
++ }
++ }
++#endif
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acx_i_timer
++**
++** Fires up periodically. Used to kick scan/auth/assoc if something goes wrong
++*/
++void
++acx_i_timer(unsigned long address)
++{
++ unsigned long flags;
++ acx_device_t *adev = (acx_device_t*)address;
++
++ FN_ENTER;
++
++ acx_lock(adev, flags);
++
++ log(L_DEBUG|L_ASSOC, "%s: adev->status=%d (%s)\n",
++ __func__, adev->status, acx_get_status_name(adev->status));
++
++ switch (adev->status) {
++ case ACX_STATUS_1_SCANNING:
++ /* was set to 0 by set_status() */
++ if (++adev->scan_retries < 7) {
++ acx_set_timer(adev, 1000000);
++ /* used to interrogate for scan status.
++ ** We rely on SCAN_COMPLETE IRQ instead */
++ log(L_ASSOC, "continuing scan (%d sec)\n",
++ adev->scan_retries);
++ } else {
++ log(L_ASSOC, "stopping scan\n");
++ /* send stop_scan cmd when we leave the interrupt context,
++ * and make a decision what to do next (COMPLETE_SCAN) */
++ acx_schedule_task(adev,
++ ACX_AFTER_IRQ_CMD_STOP_SCAN + ACX_AFTER_IRQ_COMPLETE_SCAN);
++ }
++ break;
++ case ACX_STATUS_2_WAIT_AUTH:
++ /* was set to 0 by set_status() */
++ if (++adev->auth_or_assoc_retries < 10) {
++ log(L_ASSOC, "resend authen1 request (attempt %d)\n",
++ adev->auth_or_assoc_retries + 1);
++ acx_l_transmit_authen1(adev);
++ } else {
++ /* time exceeded: fall back to scanning mode */
++ log(L_ASSOC,
++ "authen1 request reply timeout, giving up\n");
++ /* we are a STA, need to find AP anyhow */
++ acx_set_status(adev, ACX_STATUS_1_SCANNING);
++ acx_schedule_task(adev, ACX_AFTER_IRQ_RESTART_SCAN);
++ }
++ /* used to be 1500000, but some other driver uses 2.5s */
++ acx_set_timer(adev, 2500000);
++ break;
++ case ACX_STATUS_3_AUTHENTICATED:
++ /* was set to 0 by set_status() */
++ if (++adev->auth_or_assoc_retries < 10) {
++ log(L_ASSOC, "resend assoc request (attempt %d)\n",
++ adev->auth_or_assoc_retries + 1);
++ acx_l_transmit_assoc_req(adev);
++ } else {
++ /* time exceeded: give up */
++ log(L_ASSOC,
++ "association request reply timeout, giving up\n");
++ /* we are a STA, need to find AP anyhow */
++ acx_set_status(adev, ACX_STATUS_1_SCANNING);
++ acx_schedule_task(adev, ACX_AFTER_IRQ_RESTART_SCAN);
++ }
++ acx_set_timer(adev, 2500000); /* see above */
++ break;
++ case ACX_STATUS_4_ASSOCIATED:
++ default:
++ break;
++ }
++
++ acx_unlock(adev, flags);
++
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acx_set_timer
++**
++** Sets the 802.11 state management timer's timeout.
++*/
++void
++acx_set_timer(acx_device_t *adev, int timeout_us)
++{
++ FN_ENTER;
++
++ log(L_DEBUG|L_IRQ, "%s(%u ms)\n", __func__, timeout_us/1000);
++ if (!(adev->dev_state_mask & ACX_STATE_IFACE_UP)) {
++ printk("attempt to set the timer "
++ "when the card interface is not up!\n");
++ goto end;
++ }
++
++ /* first check if the timer was already initialized, THEN modify it */
++ if (adev->mgmt_timer.function) {
++ mod_timer(&adev->mgmt_timer,
++ jiffies + (timeout_us * HZ / 1000000));
++ }
++end:
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acx_l_transmit_assocresp
++**
++** We are an AP here
++*/
++static const u8
++dot11ratebyte[] = {
++ DOT11RATEBYTE_1,
++ DOT11RATEBYTE_2,
++ DOT11RATEBYTE_5_5,
++ DOT11RATEBYTE_6_G,
++ DOT11RATEBYTE_9_G,
++ DOT11RATEBYTE_11,
++ DOT11RATEBYTE_12_G,
++ DOT11RATEBYTE_18_G,
++ DOT11RATEBYTE_22,
++ DOT11RATEBYTE_24_G,
++ DOT11RATEBYTE_36_G,
++ DOT11RATEBYTE_48_G,
++ DOT11RATEBYTE_54_G,
++};
++
++static inline int
++find_pos(const u8 *p, int size, u8 v)
++{
++ int i;
++ for (i = 0; i < size; i++)
++ if (p[i] == v)
++ return i;
++ /* printk a message about strange byte? */
++ return 0;
++}
++
++static void
++add_bits_to_ratemasks(u8* ratevec, int len, u16* brate, u16* orate)
++{
++ while (len--) {
++ int n = 1 << find_pos(dot11ratebyte,
++ sizeof(dot11ratebyte), *ratevec & 0x7f);
++ if (*ratevec & 0x80)
++ *brate |= n;
++ *orate |= n;
++ ratevec++;
++ }
++}
++
++static int
++acx_l_transmit_assocresp(acx_device_t *adev, const wlan_fr_assocreq_t *req)
++{
++ struct tx *tx;
++ struct wlan_hdr_mgmt *head;
++ struct assocresp_frame_body *body;
++ u8 *p;
++ const u8 *da;
++ /* const u8 *sa; */
++ const u8 *bssid;
++ client_t *clt;
++
++ FN_ENTER;
++
++ /* sa = req->hdr->a1; */
++ da = req->hdr->a2;
++ bssid = req->hdr->a3;
++
++ clt = acx_l_sta_list_get(adev, da);
++ if (!clt)
++ goto ok;
++
++ /* Assoc without auth is a big no-no */
++ /* Let's be liberal: if already assoc'ed STA sends assoc req again,
++ ** we won't be rude */
++ if (clt->used != CLIENT_AUTHENTICATED_2
++ && clt->used != CLIENT_ASSOCIATED_3) {
++ acx_l_transmit_deauthen(adev, da, WLAN_MGMT_REASON_CLASS2_NONAUTH);
++ goto bad;
++ }
++
++ clt->used = CLIENT_ASSOCIATED_3;
++
++ if (clt->aid == 0)
++ clt->aid = ++adev->aid;
++ clt->cap_info = ieee2host16(*(req->cap_info));
++
++ /* We cheat here a bit. We don't really care which rates are flagged
++ ** as basic by the client, so we stuff them in single ratemask */
++ clt->rate_cap = 0;
++ if (req->supp_rates)
++ add_bits_to_ratemasks(req->supp_rates->rates,
++ req->supp_rates->len, &clt->rate_cap, &clt->rate_cap);
++ if (req->ext_rates)
++ add_bits_to_ratemasks(req->ext_rates->rates,
++ req->ext_rates->len, &clt->rate_cap, &clt->rate_cap);
++ /* We can check that client supports all basic rates,
++ ** and deny assoc if not. But let's be liberal, right? ;) */
++ clt->rate_cfg = clt->rate_cap & adev->rate_oper;
++ if (!clt->rate_cfg) clt->rate_cfg = 1 << lowest_bit(adev->rate_oper);
++ clt->rate_cur = 1 << lowest_bit(clt->rate_cfg);
++ if (IS_ACX100(adev))
++ clt->rate_100 = acx_bitpos2rate100[lowest_bit(clt->rate_cfg)];
++ clt->fallback_count = clt->stepup_count = 0;
++ clt->ignore_count = 16;
++
++ tx = acx_l_alloc_tx(adev);
++ if (!tx)
++ goto bad;
++ head = acx_l_get_txbuf(adev, tx);
++ if (!head) {
++ acx_l_dealloc_tx(adev, tx);
++ goto bad;
++ }
++ body = (void*)(head + 1);
++
++ head->fc = WF_FSTYPE_ASSOCRESPi;
++ head->dur = req->hdr->dur;
++ MAC_COPY(head->da, da);
++ MAC_COPY(head->sa, adev->dev_addr);
++ MAC_COPY(head->bssid, bssid);
++ head->seq = req->hdr->seq;
++
++ body->cap_info = host2ieee16(adev->capabilities);
++ body->status = host2ieee16(0);
++ body->aid = host2ieee16(clt->aid);
++ p = wlan_fill_ie_rates((u8*)&body->rates, adev->rate_supported_len,
++ adev->rate_supported);
++ p = wlan_fill_ie_rates_ext(p, adev->rate_supported_len,
++ adev->rate_supported);
++
++ acx_l_tx_data(adev, tx, p - (u8*)head);
++ok:
++ FN_EXIT1(OK);
++ return OK;
++bad:
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++}
++
++
++/***********************************************************************
++* acx_l_transmit_reassocresp
++
++You may be wondering, just like me, what the hell ReAuth is.
++In practice it was seen sent by STA when STA feels like losing connection.
++
++[802.11]
++
++5.4.2.3 Reassociation
++
++Association is sufficient for no-transition message delivery between
++IEEE 802.11 stations. Additional functionality is needed to support
++BSS-transition mobility. The additional required functionality
++is provided by the reassociation service. Reassociation is a DSS.
++The reassociation service is invoked to 'move' a current association
++from one AP to another. This keeps the DS informed of the current
++mapping between AP and STA as the station moves from BSS to BSS within
++an ESS. Reassociation also enables changing association attributes
++of an established association while the STA remains associated with
++the same AP. Reassociation is always initiated by the mobile STA.
++
++5.4.3.1 Authentication
++...
++A STA may be authenticated with many other STAs at any given instant.
++
++5.4.3.1.1 Preauthentication
++
++Because the authentication process could be time-consuming (depending
++on the authentication protocol in use), the authentication service can
++be invoked independently of the association service. Preauthentication
++is typically done by a STA while it is already associated with an AP
++(with which it previously authenticated). IEEE 802.11 does not require
++that STAs preauthenticate with APs. However, authentication is required
++before an association can be established. If the authentication is left
++until reassociation time, this may impact the speed with which a STA can
++reassociate between APs, limiting BSS-transition mobility performance.
++The use of preauthentication takes the authentication service overhead
++out of the time-critical reassociation process.
++
++5.7.3 Reassociation
++
++For a STA to reassociate, the reassociation service causes the following
++message to occur:
++
++ Reassociation request
++
++* Message type: Management
++* Message subtype: Reassociation request
++* Information items:
++ - IEEE address of the STA
++ - IEEE address of the AP with which the STA will reassociate
++ - IEEE address of the AP with which the STA is currently associated
++ - ESSID
++* Direction of message: From STA to 'new' AP
++
++The address of the current AP is included for efficiency. The inclusion
++of the current AP address facilitates MAC reassociation to be independent
++of the DS implementation.
++
++ Reassociation response
++* Message type: Management
++* Message subtype: Reassociation response
++* Information items:
++ - Result of the requested reassociation. (success/failure)
++ - If the reassociation is successful, the response shall include the AID.
++* Direction of message: From AP to STA
++
++7.2.3.6 Reassociation Request frame format
++
++The frame body of a management frame of subtype Reassociation Request
++contains the information shown in Table 9.
++
++Table 9 Reassociation Request frame body
++Order Information
++1 Capability information
++2 Listen interval
++3 Current AP address
++4 SSID
++5 Supported rates
++
++7.2.3.7 Reassociation Response frame format
++
++The frame body of a management frame of subtype Reassociation Response
++contains the information shown in Table 10.
++
++Table 10 Reassociation Response frame body
++Order Information
++1 Capability information
++2 Status code
++3 Association ID (AID)
++4 Supported rates
++
++*/
++static int
++acx_l_transmit_reassocresp(acx_device_t *adev, const wlan_fr_reassocreq_t *req)
++{
++ struct tx *tx;
++ struct wlan_hdr_mgmt *head;
++ struct reassocresp_frame_body *body;
++ u8 *p;
++ const u8 *da;
++ /* const u8 *sa; */
++ const u8 *bssid;
++ client_t *clt;
++
++ FN_ENTER;
++
++ /* sa = req->hdr->a1; */
++ da = req->hdr->a2;
++ bssid = req->hdr->a3;
++
++ /* Must be already authenticated, so it must be in the list */
++ clt = acx_l_sta_list_get(adev, da);
++ if (!clt)
++ goto ok;
++
++ /* Assoc without auth is a big no-no */
++ /* Already assoc'ed STAs sending ReAssoc req are ok per 802.11 */
++ if (clt->used != CLIENT_AUTHENTICATED_2
++ && clt->used != CLIENT_ASSOCIATED_3) {
++ acx_l_transmit_deauthen(adev, da, WLAN_MGMT_REASON_CLASS2_NONAUTH);
++ goto bad;
++ }
++
++ clt->used = CLIENT_ASSOCIATED_3;
++ if (clt->aid == 0) {
++ clt->aid = ++adev->aid;
++ }
++ if (req->cap_info)
++ clt->cap_info = ieee2host16(*(req->cap_info));
++
++ /* We cheat here a bit. We don't really care which rates are flagged
++ ** as basic by the client, so we stuff them in single ratemask */
++ clt->rate_cap = 0;
++ if (req->supp_rates)
++ add_bits_to_ratemasks(req->supp_rates->rates,
++ req->supp_rates->len, &clt->rate_cap, &clt->rate_cap);
++ if (req->ext_rates)
++ add_bits_to_ratemasks(req->ext_rates->rates,
++ req->ext_rates->len, &clt->rate_cap, &clt->rate_cap);
++ /* We can check that client supports all basic rates,
++ ** and deny assoc if not. But let's be liberal, right? ;) */
++ clt->rate_cfg = clt->rate_cap & adev->rate_oper;
++ if (!clt->rate_cfg) clt->rate_cfg = 1 << lowest_bit(adev->rate_oper);
++ clt->rate_cur = 1 << lowest_bit(clt->rate_cfg);
++ if (IS_ACX100(adev))
++ clt->rate_100 = acx_bitpos2rate100[lowest_bit(clt->rate_cfg)];
++
++ clt->fallback_count = clt->stepup_count = 0;
++ clt->ignore_count = 16;
++
++ tx = acx_l_alloc_tx(adev);
++ if (!tx)
++ goto ok;
++ head = acx_l_get_txbuf(adev, tx);
++ if (!head) {
++ acx_l_dealloc_tx(adev, tx);
++ goto ok;
++ }
++ body = (void*)(head + 1);
++
++ head->fc = WF_FSTYPE_REASSOCRESPi;
++ head->dur = req->hdr->dur;
++ MAC_COPY(head->da, da);
++ MAC_COPY(head->sa, adev->dev_addr);
++ MAC_COPY(head->bssid, bssid);
++ head->seq = req->hdr->seq;
++
++ /* IEs: 1. caps */
++ body->cap_info = host2ieee16(adev->capabilities);
++ /* 2. status code */
++ body->status = host2ieee16(0);
++ /* 3. AID */
++ body->aid = host2ieee16(clt->aid);
++ /* 4. supp rates */
++ p = wlan_fill_ie_rates((u8*)&body->rates, adev->rate_supported_len,
++ adev->rate_supported);
++ /* 5. ext supp rates */
++ p = wlan_fill_ie_rates_ext(p, adev->rate_supported_len,
++ adev->rate_supported);
++
++ acx_l_tx_data(adev, tx, p - (u8*)head);
++ok:
++ FN_EXIT1(OK);
++ return OK;
++bad:
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++}
++
++
++/***********************************************************************
++** acx_l_process_disassoc_from_sta
++*/
++static void
++acx_l_process_disassoc_from_sta(acx_device_t *adev, const wlan_fr_disassoc_t *req)
++{
++ const u8 *ta;
++ client_t *clt;
++
++ FN_ENTER;
++
++ ta = req->hdr->a2;
++ clt = acx_l_sta_list_get(adev, ta);
++ if (!clt)
++ goto end;
++
++ if (clt->used != CLIENT_ASSOCIATED_3
++ && clt->used != CLIENT_AUTHENTICATED_2) {
++ /* it's disassociating, but it's
++ ** not even authenticated! Let it know that */
++ acxlog_mac(L_ASSOC|L_XFER, "peer ", ta, "has sent disassoc "
++ "req but it is not even auth'ed! sending deauth\n");
++ acx_l_transmit_deauthen(adev, ta,
++ WLAN_MGMT_REASON_CLASS2_NONAUTH);
++ clt->used = CLIENT_EXIST_1;
++ } else {
++ /* mark it as auth'ed only */
++ clt->used = CLIENT_AUTHENTICATED_2;
++ }
++end:
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acx_l_process_deauthen_from_sta
++*/
++static void
++acx_l_process_deauth_from_sta(acx_device_t *adev, const wlan_fr_deauthen_t *req)
++{
++ const wlan_hdr_t *hdr;
++ client_t *client;
++
++ FN_ENTER;
++
++ hdr = req->hdr;
++
++ if (acx_debug & L_ASSOC) {
++ acx_print_mac("got deauth from sta:", hdr->a2, " ");
++ acx_print_mac("a1:", hdr->a1, " ");
++ acx_print_mac("a3:", hdr->a3, " ");
++ acx_print_mac("adev->addr:", adev->dev_addr, " ");
++ acx_print_mac("adev->bssid:", adev->bssid, "\n");
++ }
++
++ if (!mac_is_equal(adev->dev_addr, hdr->a1)) {
++ goto end;
++ }
++
++ client = acx_l_sta_list_get(adev, hdr->a2);
++ if (!client) {
++ goto end;
++ }
++ client->used = CLIENT_EXIST_1;
++end:
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acx_l_process_disassoc_from_ap
++*/
++static void
++acx_l_process_disassoc_from_ap(acx_device_t *adev, const wlan_fr_disassoc_t *req)
++{
++ FN_ENTER;
++
++ if (!adev->ap_client) {
++ /* Hrm, we aren't assoc'ed yet anyhow... */
++ goto end;
++ }
++
++ printk("%s: got disassoc frame with reason %d (%s)\n",
++ adev->ndev->name, *req->reason,
++ acx_wlan_reason_str(*req->reason));
++
++ if (mac_is_equal(adev->dev_addr, req->hdr->a1)) {
++ acx_l_transmit_deauthen(adev, adev->bssid,
++ WLAN_MGMT_REASON_DEAUTH_LEAVING);
++ SET_BIT(adev->set_mask, GETSET_RESCAN);
++ acx_schedule_task(adev, ACX_AFTER_IRQ_UPDATE_CARD_CFG);
++ }
++end:
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acx_l_process_deauth_from_ap
++*/
++static void
++acx_l_process_deauth_from_ap(acx_device_t *adev, const wlan_fr_deauthen_t *req)
++{
++ FN_ENTER;
++
++ if (!adev->ap_client) {
++ /* Hrm, we aren't assoc'ed yet anyhow... */
++ goto end;
++ }
++
++ printk("%s: got deauth frame with reason %d (%s)\n",
++ adev->ndev->name, *req->reason,
++ acx_wlan_reason_str(*req->reason));
++
++ /* Chk: is ta verified to be from our AP? */
++ if (mac_is_equal(adev->dev_addr, req->hdr->a1)) {
++ log(L_DEBUG, "AP sent us deauth packet\n");
++ SET_BIT(adev->set_mask, GETSET_RESCAN);
++ acx_schedule_task(adev, ACX_AFTER_IRQ_UPDATE_CARD_CFG);
++ }
++end:
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acx_l_rx
++**
++** The end of the Rx path. Pulls data from a rxhostdesc into a socket
++** buffer and feeds it to the network stack via netif_rx().
++*/
++static void
++acx_l_rx(acx_device_t *adev, rxbuffer_t *rxbuf)
++{
++ FN_ENTER;
++ if (likely(adev->dev_state_mask & ACX_STATE_IFACE_UP)) {
++ struct sk_buff *skb;
++ skb = acx_rxbuf_to_ether(adev, rxbuf);
++ if (likely(skb)) {
++ netif_rx(skb);
++ adev->ndev->last_rx = jiffies;
++ adev->stats.rx_packets++;
++ adev->stats.rx_bytes += skb->len;
++ }
++ }
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acx_l_process_data_frame_master
++*/
++static int
++acx_l_process_data_frame_master(acx_device_t *adev, rxbuffer_t *rxbuf)
++{
++ struct wlan_hdr *hdr;
++ struct tx *tx;
++ void *txbuf;
++ int len;
++ int result = NOT_OK;
++
++ FN_ENTER;
++
++ hdr = acx_get_wlan_hdr(adev, rxbuf);
++
++ switch (WF_FC_FROMTODSi & hdr->fc) {
++ case 0:
++ case WF_FC_FROMDSi:
++ log(L_DEBUG, "ap->sta or adhoc->adhoc data frame ignored\n");
++ goto done;
++ case WF_FC_TODSi:
++ break;
++ default: /* WF_FC_FROMTODSi */
++ log(L_DEBUG, "wds data frame ignored (TODO)\n");
++ goto done;
++ }
++
++ /* check if it is our BSSID, if not, leave */
++ if (!mac_is_equal(adev->bssid, hdr->a1)) {
++ goto done;
++ }
++
++ if (mac_is_equal(adev->dev_addr, hdr->a3)) {
++ /* this one is for us */
++ acx_l_rx(adev, rxbuf);
++ } else {
++ if (mac_is_bcast(hdr->a3)) {
++ /* this one is bcast, rx it too */
++ acx_l_rx(adev, rxbuf);
++ }
++ tx = acx_l_alloc_tx(adev);
++ if (!tx) {
++ goto fail;
++ }
++ /* repackage, tx, and hope it someday reaches its destination */
++ /* order is important, we do it in-place */
++ MAC_COPY(hdr->a1, hdr->a3);
++ MAC_COPY(hdr->a3, hdr->a2);
++ MAC_COPY(hdr->a2, adev->bssid);
++ /* To_DS = 0, From_DS = 1 */
++ hdr->fc = WF_FC_FROMDSi + WF_FTYPE_DATAi;
++
++ txbuf = acx_l_get_txbuf(adev, tx);
++ if (txbuf) {
++ len = RXBUF_BYTES_RCVD(adev, rxbuf);
++ memcpy(txbuf, hdr, len);
++ acx_l_tx_data(adev, tx, len);
++ } else {
++ acx_l_dealloc_tx(adev, tx);
++ }
++ }
++done:
++ result = OK;
++fail:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acx_l_process_data_frame_client
++*/
++static int
++acx_l_process_data_frame_client(acx_device_t *adev, rxbuffer_t *rxbuf)
++{
++ const u8 *da, *bssid;
++ const wlan_hdr_t *hdr;
++ struct net_device *ndev = adev->ndev;
++ int result = NOT_OK;
++
++ FN_ENTER;
++
++ if (ACX_STATUS_4_ASSOCIATED != adev->status)
++ goto drop;
++
++ hdr = acx_get_wlan_hdr(adev, rxbuf);
++
++ switch (WF_FC_FROMTODSi & hdr->fc) {
++ case 0:
++ if (adev->mode != ACX_MODE_0_ADHOC) {
++ log(L_DEBUG, "adhoc->adhoc data frame ignored\n");
++ goto drop;
++ }
++ bssid = hdr->a3;
++ break;
++ case WF_FC_FROMDSi:
++ if (adev->mode != ACX_MODE_2_STA) {
++ log(L_DEBUG, "ap->sta data frame ignored\n");
++ goto drop;
++ }
++ bssid = hdr->a2;
++ break;
++ case WF_FC_TODSi:
++ log(L_DEBUG, "sta->ap data frame ignored\n");
++ goto drop;
++ default: /* WF_FC_FROMTODSi: wds->wds */
++ log(L_DEBUG, "wds data frame ignored (todo)\n");
++ goto drop;
++ }
++
++ da = hdr->a1;
++
++ if (unlikely(acx_debug & L_DEBUG)) {
++ acx_print_mac("rx: da=", da, "");
++ acx_print_mac(" bssid=", bssid, "");
++ acx_print_mac(" adev->bssid=", adev->bssid, "");
++ acx_print_mac(" adev->addr=", adev->dev_addr, "\n");
++ }
++
++ /* promiscuous mode --> receive all packets */
++ if (unlikely(ndev->flags & IFF_PROMISC))
++ goto process;
++
++ /* FIRST, check if it is our BSSID */
++ if (!mac_is_equal(adev->bssid, bssid)) {
++ /* is not our BSSID, so bail out */
++ goto drop;
++ }
++
++ /* then, check if it is our address */
++ if (mac_is_equal(adev->dev_addr, da)) {
++ goto process;
++ }
++
++ /* then, check if it is broadcast */
++ if (mac_is_bcast(da)) {
++ goto process;
++ }
++
++ if (mac_is_mcast(da)) {
++ /* unconditionally receive all multicasts */
++ if (ndev->flags & IFF_ALLMULTI)
++ goto process;
++
++ /* FIXME: need to check against the list of
++ * multicast addresses that are configured
++ * for the interface (ifconfig) */
++ log(L_XFER, "FIXME: multicast packet, need to check "
++ "against a list of multicast addresses "
++ "(to be created!); accepting packet for now\n");
++ /* for now, just accept it here */
++ goto process;
++ }
++
++ log(L_DEBUG, "rx: foreign packet, dropping\n");
++ goto drop;
++process:
++ /* receive packet */
++ acx_l_rx(adev, rxbuf);
++
++ result = OK;
++drop:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acx_l_process_mgmt_frame
++**
++** Theory of operation: mgmt packet gets parsed (to make it easy
++** to access variable-sized IEs), results stored in 'parsed'.
++** Then we react to the packet.
++*/
++typedef union parsed_mgmt_req {
++ wlan_fr_mgmt_t mgmt;
++ wlan_fr_assocreq_t assocreq;
++ wlan_fr_reassocreq_t reassocreq;
++ wlan_fr_assocresp_t assocresp;
++ wlan_fr_reassocresp_t reassocresp;
++ wlan_fr_beacon_t beacon;
++ wlan_fr_disassoc_t disassoc;
++ wlan_fr_authen_t authen;
++ wlan_fr_deauthen_t deauthen;
++ wlan_fr_proberesp_t proberesp;
++} parsed_mgmt_req_t;
++
++void BUG_excessive_stack_usage(void);
++
++static int
++acx_l_process_mgmt_frame(acx_device_t *adev, rxbuffer_t *rxbuf)
++{
++ parsed_mgmt_req_t parsed; /* takes ~100 bytes of stack */
++ wlan_hdr_t *hdr;
++ int adhoc, sta_scan, sta, ap;
++ int len;
++
++ if (sizeof(parsed) > 256)
++ BUG_excessive_stack_usage();
++
++ FN_ENTER;
++
++ hdr = acx_get_wlan_hdr(adev, rxbuf);
++
++ /* Management frames never have these set */
++ if (WF_FC_FROMTODSi & hdr->fc) {
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++ }
++
++ len = RXBUF_BYTES_RCVD(adev, rxbuf);
++ if (WF_FC_ISWEPi & hdr->fc)
++ len -= 0x10;
++
++ adhoc = (adev->mode == ACX_MODE_0_ADHOC);
++ sta_scan = ((adev->mode == ACX_MODE_2_STA)
++ && (adev->status != ACX_STATUS_4_ASSOCIATED));
++ sta = ((adev->mode == ACX_MODE_2_STA)
++ && (adev->status == ACX_STATUS_4_ASSOCIATED));
++ ap = (adev->mode == ACX_MODE_3_AP);
++
++ switch (WF_FC_FSTYPEi & hdr->fc) {
++ /* beacons first, for speed */
++ case WF_FSTYPE_BEACONi:
++ memset(&parsed.beacon, 0, sizeof(parsed.beacon));
++ parsed.beacon.hdr = hdr;
++ parsed.beacon.len = len;
++ if (acx_debug & L_DATA) {
++ printk("beacon len:%d fc:%04X dur:%04X seq:%04X",
++ len, hdr->fc, hdr->dur, hdr->seq);
++ acx_print_mac(" a1:", hdr->a1, "");
++ acx_print_mac(" a2:", hdr->a2, "");
++ acx_print_mac(" a3:", hdr->a3, "\n");
++ }
++ wlan_mgmt_decode_beacon(&parsed.beacon);
++ /* beacon and probe response are very similar, so... */
++ acx_l_process_probe_response(adev, &parsed.beacon, rxbuf);
++ break;
++ case WF_FSTYPE_ASSOCREQi:
++ if (!ap)
++ break;
++ memset(&parsed.assocreq, 0, sizeof(parsed.assocreq));
++ parsed.assocreq.hdr = hdr;
++ parsed.assocreq.len = len;
++ wlan_mgmt_decode_assocreq(&parsed.assocreq);
++ if (mac_is_equal(hdr->a1, adev->bssid)
++ && mac_is_equal(hdr->a3, adev->bssid)) {
++ acx_l_transmit_assocresp(adev, &parsed.assocreq);
++ }
++ break;
++ case WF_FSTYPE_REASSOCREQi:
++ if (!ap)
++ break;
++ memset(&parsed.assocreq, 0, sizeof(parsed.assocreq));
++ parsed.assocreq.hdr = hdr;
++ parsed.assocreq.len = len;
++ wlan_mgmt_decode_assocreq(&parsed.assocreq);
++ /* reassocreq and assocreq are equivalent */
++ acx_l_transmit_reassocresp(adev, &parsed.reassocreq);
++ break;
++ case WF_FSTYPE_ASSOCRESPi:
++ if (!sta_scan)
++ break;
++ memset(&parsed.assocresp, 0, sizeof(parsed.assocresp));
++ parsed.assocresp.hdr = hdr;
++ parsed.assocresp.len = len;
++ wlan_mgmt_decode_assocresp(&parsed.assocresp);
++ acx_l_process_assocresp(adev, &parsed.assocresp);
++ break;
++ case WF_FSTYPE_REASSOCRESPi:
++ if (!sta_scan)
++ break;
++ memset(&parsed.assocresp, 0, sizeof(parsed.assocresp));
++ parsed.assocresp.hdr = hdr;
++ parsed.assocresp.len = len;
++ wlan_mgmt_decode_assocresp(&parsed.assocresp);
++ acx_l_process_reassocresp(adev, &parsed.reassocresp);
++ break;
++ case WF_FSTYPE_PROBEREQi:
++ if (ap || adhoc) {
++ /* FIXME: since we're supposed to be an AP,
++ ** we need to return a Probe Response packet.
++ ** Currently firmware is doing it for us,
++ ** but firmware is buggy! See comment elsewhere --vda */
++ }
++ break;
++ case WF_FSTYPE_PROBERESPi:
++ memset(&parsed.proberesp, 0, sizeof(parsed.proberesp));
++ parsed.proberesp.hdr = hdr;
++ parsed.proberesp.len = len;
++ wlan_mgmt_decode_proberesp(&parsed.proberesp);
++ acx_l_process_probe_response(adev, &parsed.proberesp, rxbuf);
++ break;
++ case 6:
++ case 7:
++ /* exit */
++ break;
++ case WF_FSTYPE_ATIMi:
++ /* exit */
++ break;
++ case WF_FSTYPE_DISASSOCi:
++ if (!sta && !ap)
++ break;
++ memset(&parsed.disassoc, 0, sizeof(parsed.disassoc));
++ parsed.disassoc.hdr = hdr;
++ parsed.disassoc.len = len;
++ wlan_mgmt_decode_disassoc(&parsed.disassoc);
++ if (sta)
++ acx_l_process_disassoc_from_ap(adev, &parsed.disassoc);
++ else
++ acx_l_process_disassoc_from_sta(adev, &parsed.disassoc);
++ break;
++ case WF_FSTYPE_AUTHENi:
++ if (!sta_scan && !ap)
++ break;
++ memset(&parsed.authen, 0, sizeof(parsed.authen));
++ parsed.authen.hdr = hdr;
++ parsed.authen.len = len;
++ wlan_mgmt_decode_authen(&parsed.authen);
++ acx_l_process_authen(adev, &parsed.authen);
++ break;
++ case WF_FSTYPE_DEAUTHENi:
++ if (!sta && !ap)
++ break;
++ memset(&parsed.deauthen, 0, sizeof(parsed.deauthen));
++ parsed.deauthen.hdr = hdr;
++ parsed.deauthen.len = len;
++ wlan_mgmt_decode_deauthen(&parsed.deauthen);
++ if (sta)
++ acx_l_process_deauth_from_ap(adev, &parsed.deauthen);
++ else
++ acx_l_process_deauth_from_sta(adev, &parsed.deauthen);
++ break;
++ }
++
++ FN_EXIT1(OK);
++ return OK;
++}
++
++
++#ifdef UNUSED
++/***********************************************************************
++** acx_process_class_frame
++**
++** Called from IRQ context only
++*/
++static int
++acx_process_class_frame(acx_device_t *adev, rxbuffer_t *rxbuf, int vala)
++{
++ return OK;
++}
++#endif
++
++
++/***********************************************************************
++** acx_l_process_NULL_frame
++*/
++#ifdef BOGUS_ITS_NOT_A_NULL_FRAME_HANDLER_AT_ALL
++static int
++acx_l_process_NULL_frame(acx_device_t *adev, rxbuffer_t *rxbuf, int vala)
++{
++ const signed char *esi;
++ const u8 *ebx;
++ const wlan_hdr_t *hdr;
++ const client_t *client;
++ int result = NOT_OK;
++
++ hdr = acx_get_wlan_hdr(adev, rxbuf);
++
++ switch (WF_FC_FROMTODSi & hdr->fc) {
++ case 0:
++ esi = hdr->a1;
++ ebx = hdr->a2;
++ break;
++ case WF_FC_FROMDSi:
++ esi = hdr->a1;
++ ebx = hdr->a3;
++ break;
++ case WF_FC_TODSi:
++ esi = hdr->a1;
++ ebx = hdr->a2;
++ break;
++ default: /* WF_FC_FROMTODSi */
++ esi = hdr->a1; /* added by me! --vda */
++ ebx = hdr->a2;
++ }
++
++ if (esi[0x0] < 0) {
++ result = OK;
++ goto done;
++ }
++
++ client = acx_l_sta_list_get(adev, ebx);
++ if (client)
++ result = NOT_OK;
++ else {
++#ifdef IS_IT_BROKEN
++ log(L_DEBUG|L_XFER, "<transmit_deauth 7>\n");
++ acx_l_transmit_deauthen(adev, ebx,
++ WLAN_MGMT_REASON_CLASS2_NONAUTH);
++#else
++ log(L_DEBUG, "received NULL frame from unknown client! "
++ "We really shouldn't send deauthen here, right?\n");
++#endif
++ result = OK;
++ }
++done:
++ return result;
++}
++#endif
++
++
++/***********************************************************************
++** acx_l_process_probe_response
++*/
++static int
++acx_l_process_probe_response(acx_device_t *adev, wlan_fr_proberesp_t *req,
++ const rxbuffer_t *rxbuf)
++{
++ struct client *bss;
++ wlan_hdr_t *hdr;
++
++ FN_ENTER;
++
++ hdr = req->hdr;
++
++ if (mac_is_equal(hdr->a3, adev->dev_addr)) {
++ log(L_ASSOC, "huh, scan found our own MAC!?\n");
++ goto ok; /* just skip this one silently */
++ }
++
++ bss = acx_l_sta_list_get_or_add(adev, hdr->a2);
++
++ /* NB: be careful modifying bss data! It may be one
++ ** of the already known clients (like our AP if we are a STA)
++ ** Thus do not blindly modify e.g. current ratemask! */
++
++ if (STA_LIST_ADD_CAN_FAIL && !bss) {
++ /* uh oh, we found more sites/stations than we can handle with
++ * our current setup: pull the emergency brake and stop scanning! */
++ acx_schedule_task(adev, ACX_AFTER_IRQ_CMD_STOP_SCAN);
++ /* TODO: a nice comment what below call achieves --vda */
++ acx_set_status(adev, ACX_STATUS_2_WAIT_AUTH);
++ goto ok;
++ }
++ /* NB: get_or_add already filled bss->address = hdr->a2 */
++ MAC_COPY(bss->bssid, hdr->a3);
++
++ /* copy the ESSID element */
++ if (req->ssid && req->ssid->len <= IW_ESSID_MAX_SIZE) {
++ bss->essid_len = req->ssid->len;
++ memcpy(bss->essid, req->ssid->ssid, req->ssid->len);
++ bss->essid[req->ssid->len] = '\0';
++ } else {
++ /* Either no ESSID IE or oversized one */
++ printk("%s: received packet has bogus ESSID\n",
++ adev->ndev->name);
++ }
++
++ if (req->ds_parms)
++ bss->channel = req->ds_parms->curr_ch;
++ if (req->cap_info)
++ bss->cap_info = ieee2host16(*req->cap_info);
++
++ bss->sir = acx_signal_to_winlevel(rxbuf->phy_level);
++ bss->snr = acx_signal_to_winlevel(rxbuf->phy_snr);
++
++ bss->rate_cap = 0; /* operational mask */
++ bss->rate_bas = 0; /* basic mask */
++ if (req->supp_rates)
++ add_bits_to_ratemasks(req->supp_rates->rates,
++ req->supp_rates->len, &bss->rate_bas, &bss->rate_cap);
++ if (req->ext_rates)
++ add_bits_to_ratemasks(req->ext_rates->rates,
++ req->ext_rates->len, &bss->rate_bas, &bss->rate_cap);
++ /* Fix up any possible bogosity - code elsewhere
++ * is not expecting empty masks */
++ if (!bss->rate_cap)
++ bss->rate_cap = adev->rate_basic;
++ if (!bss->rate_bas)
++ bss->rate_bas = 1 << lowest_bit(bss->rate_cap);
++ if (!bss->rate_cur)
++ bss->rate_cur = 1 << lowest_bit(bss->rate_bas);
++
++ /* People moan about this being too noisy at L_ASSOC */
++ log(L_DEBUG,
++ "found %s: ESSID=\"%s\" ch=%d "
++ "BSSID="MACSTR" caps=0x%04X SIR=%d SNR=%d\n",
++ (bss->cap_info & WF_MGMT_CAP_IBSS) ? "Ad-Hoc peer" : "AP",
++ bss->essid, bss->channel, MAC(bss->bssid), bss->cap_info,
++ bss->sir, bss->snr);
++ok:
++ FN_EXIT0;
++ return OK;
++}
++
++
++/***********************************************************************
++** acx_l_process_assocresp
++*/
++static int
++acx_l_process_assocresp(acx_device_t *adev, const wlan_fr_assocresp_t *req)
++{
++ const wlan_hdr_t *hdr;
++ int res = OK;
++
++ FN_ENTER;
++
++ hdr = req->hdr;
++
++ if ((ACX_MODE_2_STA == adev->mode)
++ && mac_is_equal(adev->dev_addr, hdr->a1)) {
++ u16 st = ieee2host16(*(req->status));
++ if (WLAN_MGMT_STATUS_SUCCESS == st) {
++ adev->aid = ieee2host16(*(req->aid));
++ /* tell the card we are associated when
++ ** we are out of interrupt context */
++ acx_schedule_task(adev, ACX_AFTER_IRQ_CMD_ASSOCIATE);
++ } else {
++
++ /* TODO: we shall delete peer from sta_list, and try
++ ** other candidates... */
++
++ printk("%s: association FAILED: peer sent "
++ "Status Code %d (%s)\n",
++ adev->ndev->name, st, get_status_string(st));
++ res = NOT_OK;
++ }
++ }
++
++ FN_EXIT1(res);
++ return res;
++}
++
++
++/***********************************************************************
++** acx_l_process_reassocresp
++*/
++static int
++acx_l_process_reassocresp(acx_device_t *adev, const wlan_fr_reassocresp_t *req)
++{
++ const wlan_hdr_t *hdr;
++ int result = NOT_OK;
++ u16 st;
++
++ FN_ENTER;
++
++ hdr = req->hdr;
++
++ if (!mac_is_equal(adev->dev_addr, hdr->a1)) {
++ goto end;
++ }
++ st = ieee2host16(*(req->status));
++ if (st == WLAN_MGMT_STATUS_SUCCESS) {
++ acx_set_status(adev, ACX_STATUS_4_ASSOCIATED);
++ result = OK;
++ } else {
++ printk("%s: reassociation FAILED: peer sent "
++ "response code %d (%s)\n",
++ adev->ndev->name, st, get_status_string(st));
++ }
++end:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acx_l_process_authen
++**
++** Called only in STA_SCAN or AP mode
++*/
++static int
++acx_l_process_authen(acx_device_t *adev, const wlan_fr_authen_t *req)
++{
++ const wlan_hdr_t *hdr;
++ client_t *clt;
++ wlan_ie_challenge_t *chal;
++ u16 alg, seq, status;
++ int ap, result;
++
++ FN_ENTER;
++
++ hdr = req->hdr;
++
++ if (acx_debug & L_ASSOC) {
++ acx_print_mac("AUTHEN adev->addr=", adev->dev_addr, " ");
++ acx_print_mac("a1=", hdr->a1, " ");
++ acx_print_mac("a2=", hdr->a2, " ");
++ acx_print_mac("a3=", hdr->a3, " ");
++ acx_print_mac("adev->bssid=", adev->bssid, "\n");
++ }
++
++ if (!mac_is_equal(adev->dev_addr, hdr->a1)
++ || !mac_is_equal(adev->bssid, hdr->a3)) {
++ result = OK;
++ goto end;
++ }
++
++ alg = ieee2host16(*(req->auth_alg));
++ seq = ieee2host16(*(req->auth_seq));
++ status = ieee2host16(*(req->status));
++
++ log(L_ASSOC, "auth algorithm %d, auth sequence %d, status %d\n", alg, seq, status);
++
++ ap = (adev->mode == ACX_MODE_3_AP);
++
++ if (adev->auth_alg <= 1) {
++ if (adev->auth_alg != alg) {
++ log(L_ASSOC, "auth algorithm mismatch: "
++ "our:%d peer:%d\n", adev->auth_alg, alg);
++ result = NOT_OK;
++ goto end;
++ }
++ }
++ if (ap) {
++ clt = acx_l_sta_list_get_or_add(adev, hdr->a2);
++ if (STA_LIST_ADD_CAN_FAIL && !clt) {
++ log(L_ASSOC, "could not allocate room for client\n");
++ result = NOT_OK;
++ goto end;
++ }
++ } else {
++ clt = adev->ap_client;
++ if (!mac_is_equal(clt->address, hdr->a2)) {
++ printk("%s: malformed auth frame from AP?!\n",
++ adev->ndev->name);
++ result = NOT_OK;
++ goto end;
++ }
++ }
++
++ /* now check which step in the authentication sequence we are
++ * currently in, and act accordingly */
++ switch (seq) {
++ case 1:
++ if (!ap)
++ break;
++ acx_l_transmit_authen2(adev, req, clt);
++ break;
++ case 2:
++ if (ap)
++ break;
++ if (status == WLAN_MGMT_STATUS_SUCCESS) {
++ if (alg == WLAN_AUTH_ALG_OPENSYSTEM) {
++ acx_set_status(adev, ACX_STATUS_3_AUTHENTICATED);
++ acx_l_transmit_assoc_req(adev);
++ } else
++ if (alg == WLAN_AUTH_ALG_SHAREDKEY) {
++ acx_l_transmit_authen3(adev, req);
++ }
++ } else {
++ printk("%s: auth FAILED: peer sent "
++ "response code %d (%s), "
++ "still waiting for authentication\n",
++ adev->ndev->name,
++ status, get_status_string(status));
++ acx_set_status(adev, ACX_STATUS_2_WAIT_AUTH);
++ }
++ break;
++ case 3:
++ if (!ap)
++ break;
++ if ((clt->auth_alg != WLAN_AUTH_ALG_SHAREDKEY)
++ || (alg != WLAN_AUTH_ALG_SHAREDKEY)
++ || (clt->auth_step != 2))
++ break;
++ chal = req->challenge;
++ if (!chal
++ || memcmp(chal->challenge, clt->challenge_text, WLAN_CHALLENGE_LEN)
++ || (chal->eid != WLAN_EID_CHALLENGE)
++ || (chal->len != WLAN_CHALLENGE_LEN)
++ )
++ break;
++ acx_l_transmit_authen4(adev, req);
++ MAC_COPY(clt->address, hdr->a2);
++ clt->used = CLIENT_AUTHENTICATED_2;
++ clt->auth_step = 4;
++ clt->seq = ieee2host16(hdr->seq);
++ break;
++ case 4:
++ if (ap)
++ break;
++ /* ok, we're through: we're authenticated. Woohoo!! */
++ acx_set_status(adev, ACX_STATUS_3_AUTHENTICATED);
++ log(L_ASSOC, "Authenticated!\n");
++ /* now that we're authenticated, request association */
++ acx_l_transmit_assoc_req(adev);
++ break;
++ }
++ result = OK;
++end:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acx_gen_challenge
++*/
++static inline void
++acx_gen_challenge(wlan_ie_challenge_t* d)
++{
++ FN_ENTER;
++ d->eid = WLAN_EID_CHALLENGE;
++ d->len = WLAN_CHALLENGE_LEN;
++ get_random_bytes(d->challenge, WLAN_CHALLENGE_LEN);
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acx_l_transmit_deauthen
++*/
++static int
++acx_l_transmit_deauthen(acx_device_t *adev, const u8 *addr, u16 reason)
++{
++ struct tx *tx;
++ struct wlan_hdr_mgmt *head;
++ struct deauthen_frame_body *body;
++
++ FN_ENTER;
++
++ tx = acx_l_alloc_tx(adev);
++ if (!tx)
++ goto bad;
++ head = acx_l_get_txbuf(adev, tx);
++ if (!head) {
++ acx_l_dealloc_tx(adev, tx);
++ goto bad;
++ }
++ body = (void*)(head + 1);
++
++ head->fc = (WF_FTYPE_MGMTi | WF_FSTYPE_DEAUTHENi);
++ head->dur = 0;
++ MAC_COPY(head->da, addr);
++ MAC_COPY(head->sa, adev->dev_addr);
++ MAC_COPY(head->bssid, adev->bssid);
++ head->seq = 0;
++
++ log(L_DEBUG|L_ASSOC|L_XFER,
++ "sending deauthen to "MACSTR" for %d\n",
++ MAC(addr), reason);
++
++ body->reason = host2ieee16(reason);
++
++ /* body is fixed size here, but beware of cutting-and-pasting this -
++ ** do not use sizeof(*body) for variable sized mgmt packets! */
++ acx_l_tx_data(adev, tx, WLAN_HDR_A3_LEN + sizeof(*body));
++
++ FN_EXIT1(OK);
++ return OK;
++bad:
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++}
++
++
++/***********************************************************************
++** acx_l_transmit_authen1
++*/
++static int
++acx_l_transmit_authen1(acx_device_t *adev)
++{
++ struct tx *tx;
++ struct wlan_hdr_mgmt *head;
++ struct auth_frame_body *body;
++
++ FN_ENTER;
++
++ log(L_ASSOC, "sending authentication1 request (auth algo %d), "
++ "awaiting response\n", adev->auth_alg);
++
++ tx = acx_l_alloc_tx(adev);
++ if (!tx)
++ goto bad;
++ head = acx_l_get_txbuf(adev, tx);
++ if (!head) {
++ acx_l_dealloc_tx(adev, tx);
++ goto bad;
++ }
++ body = (void*)(head + 1);
++
++ head->fc = WF_FSTYPE_AUTHENi;
++ /* duration should be 0 instead of 0x8000 to have
++ * the firmware calculate the value, right? */
++ head->dur = 0;
++ MAC_COPY(head->da, adev->bssid);
++ MAC_COPY(head->sa, adev->dev_addr);
++ MAC_COPY(head->bssid, adev->bssid);
++ head->seq = 0;
++
++ body->auth_alg = host2ieee16(adev->auth_alg);
++ body->auth_seq = host2ieee16(1);
++ body->status = host2ieee16(0);
++
++ acx_l_tx_data(adev, tx, WLAN_HDR_A3_LEN + 2 + 2 + 2);
++
++ FN_EXIT1(OK);
++ return OK;
++bad:
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++}
++
++
++/***********************************************************************
++** acx_l_transmit_authen2
++*/
++static int
++acx_l_transmit_authen2(acx_device_t *adev, const wlan_fr_authen_t *req,
++ client_t *clt)
++{
++ struct tx *tx;
++ struct wlan_hdr_mgmt *head;
++ struct auth_frame_body *body;
++ unsigned int packet_len;
++
++ FN_ENTER;
++
++ if (!clt)
++ goto ok;
++
++ MAC_COPY(clt->address, req->hdr->a2);
++#ifdef UNUSED
++ clt->ps = ((WF_FC_PWRMGTi & req->hdr->fc) != 0);
++#endif
++ clt->auth_alg = ieee2host16(*(req->auth_alg));
++ clt->auth_step = 2;
++ clt->seq = ieee2host16(req->hdr->seq);
++
++ tx = acx_l_alloc_tx(adev);
++ if (!tx)
++ goto bad;
++ head = acx_l_get_txbuf(adev, tx);
++ if (!head) {
++ acx_l_dealloc_tx(adev, tx);
++ goto bad;
++ }
++ body = (void*)(head + 1);
++
++ head->fc = WF_FSTYPE_AUTHENi;
++ head->dur = 0 /* req->hdr->dur */;
++ MAC_COPY(head->da, req->hdr->a2);
++ MAC_COPY(head->sa, adev->dev_addr);
++ MAC_COPY(head->bssid, req->hdr->a3);
++ head->seq = 0 /* req->hdr->seq */;
++
++ /* already in IEEE format, no endianness conversion */
++ body->auth_alg = *(req->auth_alg);
++ body->auth_seq = host2ieee16(2);
++ body->status = host2ieee16(0);
++
++ packet_len = WLAN_HDR_A3_LEN + 2 + 2 + 2;
++ if (ieee2host16(*(req->auth_alg)) == WLAN_AUTH_ALG_OPENSYSTEM) {
++ clt->used = CLIENT_AUTHENTICATED_2;
++ } else { /* shared key */
++ acx_gen_challenge(&body->challenge);
++ memcpy(&clt->challenge_text, body->challenge.challenge, WLAN_CHALLENGE_LEN);
++ packet_len += 2 + 2 + 2 + 1+1+WLAN_CHALLENGE_LEN;
++ }
++
++ acxlog_mac(L_ASSOC|L_XFER,
++ "transmit_auth2: BSSID=", head->bssid, "\n");
++
++ acx_l_tx_data(adev, tx, packet_len);
++ok:
++ FN_EXIT1(OK);
++ return OK;
++bad:
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++}
++
++
++/***********************************************************************
++** acx_l_transmit_authen3
++*/
++static int
++acx_l_transmit_authen3(acx_device_t *adev, const wlan_fr_authen_t *req)
++{
++ struct tx *tx;
++ struct wlan_hdr_mgmt *head;
++ struct auth_frame_body *body;
++ unsigned int packet_len;
++
++ FN_ENTER;
++
++ tx = acx_l_alloc_tx(adev);
++ if (!tx)
++ goto ok;
++ head = acx_l_get_txbuf(adev, tx);
++ if (!head) {
++ acx_l_dealloc_tx(adev, tx);
++ goto ok;
++ }
++ body = (void*)(head + 1);
++
++ /* add WF_FC_ISWEPi: auth step 3 needs to be encrypted */
++ head->fc = WF_FC_ISWEPi + WF_FSTYPE_AUTHENi;
++ /* FIXME: is this needed?? authen4 does it...
++ * I think it's even wrong since we shouldn't re-use old
++ * values but instead let the firmware calculate proper ones
++ head->dur = req->hdr->dur;
++ head->seq = req->hdr->seq;
++ */
++ MAC_COPY(head->da, adev->bssid);
++ MAC_COPY(head->sa, adev->dev_addr);
++ MAC_COPY(head->bssid, adev->bssid);
++
++ /* already in IEEE format, no endianness conversion */
++ body->auth_alg = *(req->auth_alg);
++ body->auth_seq = host2ieee16(3);
++ body->status = host2ieee16(0);
++ memcpy(&body->challenge, req->challenge, req->challenge->len + 2);
++ packet_len = WLAN_HDR_A3_LEN + 8 + req->challenge->len;
++
++ log(L_ASSOC|L_XFER, "transmit_authen3!\n");
++
++ acx_l_tx_data(adev, tx, packet_len);
++ok:
++ FN_EXIT1(OK);
++ return OK;
++}
++
++
++/***********************************************************************
++** acx_l_transmit_authen4
++*/
++static int
++acx_l_transmit_authen4(acx_device_t *adev, const wlan_fr_authen_t *req)
++{
++ struct tx *tx;
++ struct wlan_hdr_mgmt *head;
++ struct auth_frame_body *body;
++
++ FN_ENTER;
++
++ tx = acx_l_alloc_tx(adev);
++ if (!tx)
++ goto ok;
++ head = acx_l_get_txbuf(adev, tx);
++ if (!head) {
++ acx_l_dealloc_tx(adev, tx);
++ goto ok;
++ }
++ body = (void*)(head + 1);
++
++ head->fc = WF_FSTYPE_AUTHENi; /* 0xb0 */
++ head->dur = 0 /* req->hdr->dur */;
++ MAC_COPY(head->da, req->hdr->a2);
++ MAC_COPY(head->sa, adev->dev_addr);
++ MAC_COPY(head->bssid, req->hdr->a3);
++ head->seq = 0 /* req->hdr->seq */;
++
++ /* already in IEEE format, no endianness conversion */
++ body->auth_alg = *(req->auth_alg);
++ body->auth_seq = host2ieee16(4);
++ body->status = host2ieee16(0);
++
++ acx_l_tx_data(adev, tx, WLAN_HDR_A3_LEN + 2 + 2 + 2);
++ok:
++ FN_EXIT1(OK);
++ return OK;
++}
++
++
++/***********************************************************************
++** acx_l_transmit_assoc_req
++**
++** adev->ap_client is a current candidate AP here
++*/
++static int
++acx_l_transmit_assoc_req(acx_device_t *adev)
++{
++ struct tx *tx;
++ struct wlan_hdr_mgmt *head;
++ u8 *body, *p, *prate;
++ unsigned int packet_len;
++ u16 cap;
++
++ FN_ENTER;
++
++ log(L_ASSOC, "sending association request, "
++ "awaiting response. NOT ASSOCIATED YET\n");
++ tx = acx_l_alloc_tx(adev);
++ if (!tx)
++ goto bad;
++ head = acx_l_get_txbuf(adev, tx);
++ if (!head) {
++ acx_l_dealloc_tx(adev, tx);
++ goto bad;
++ }
++ body = (void*)(head + 1);
++
++ head->fc = WF_FSTYPE_ASSOCREQi;
++ head->dur = host2ieee16(0x8000);
++ MAC_COPY(head->da, adev->bssid);
++ MAC_COPY(head->sa, adev->dev_addr);
++ MAC_COPY(head->bssid, adev->bssid);
++ head->seq = 0;
++
++ p = body;
++ /* now start filling the AssocReq frame body */
++
++ /* since this assoc request will most likely only get
++ * sent in the STA to AP case (and not when Ad-Hoc IBSS),
++ * the cap combination indicated here will thus be
++ * WF_MGMT_CAP_ESSi *always* (no IBSS ever)
++ * The specs are more than non-obvious on all that:
++ *
++ * 802.11 7.3.1.4 Capability Information field
++ ** APs set the ESS subfield to 1 and the IBSS subfield to 0 within
++ ** Beacon or Probe Response management frames. STAs within an IBSS
++ ** set the ESS subfield to 0 and the IBSS subfield to 1 in transmitted
++ ** Beacon or Probe Response management frames
++ **
++ ** APs set the Privacy subfield to 1 within transmitted Beacon,
++ ** Probe Response, Association Response, and Reassociation Response
++ ** if WEP is required for all data type frames within the BSS.
++ ** STAs within an IBSS set the Privacy subfield to 1 in Beacon
++ ** or Probe Response management frames if WEP is required
++ ** for all data type frames within the IBSS */
++
++ /* note that returning 0 will be refused by several APs...
++ * (so this indicates that you're probably supposed to
++ * "confirm" the ESS mode) */
++ cap = WF_MGMT_CAP_ESSi;
++
++ /* this one used to be a check on wep_restricted,
++ * but more likely it's wep_enabled instead */
++ if (adev->wep_enabled)
++ SET_BIT(cap, WF_MGMT_CAP_PRIVACYi);
++
++ /* Probably we can just set these always, because our hw is
++ ** capable of shortpre and PBCC --vda */
++ /* only ask for short preamble if the peer station supports it */
++ if (adev->ap_client->cap_info & WF_MGMT_CAP_SHORT)
++ SET_BIT(cap, WF_MGMT_CAP_SHORTi);
++ /* only ask for PBCC support if the peer station supports it */
++ if (adev->ap_client->cap_info & WF_MGMT_CAP_PBCC)
++ SET_BIT(cap, WF_MGMT_CAP_PBCCi);
++
++ /* IEs: 1. caps */
++ *(u16*)p = cap; p += 2;
++ /* 2. listen interval */
++ *(u16*)p = host2ieee16(adev->listen_interval); p += 2;
++ /* 3. ESSID */
++ p = wlan_fill_ie_ssid(p,
++ strlen(adev->essid_for_assoc), adev->essid_for_assoc);
++ /* 4. supp rates */
++ prate = p;
++ p = wlan_fill_ie_rates(p,
++ adev->rate_supported_len, adev->rate_supported);
++ /* 5. ext supp rates */
++ p = wlan_fill_ie_rates_ext(p,
++ adev->rate_supported_len, adev->rate_supported);
++
++ if (acx_debug & L_DEBUG) {
++ printk("association: rates element\n");
++ acx_dump_bytes(prate, p - prate);
++ }
++
++ /* calculate lengths */
++ packet_len = WLAN_HDR_A3_LEN + (p - body);
++
++ log(L_ASSOC, "association: requesting caps 0x%04X, ESSID \"%s\"\n",
++ cap, adev->essid_for_assoc);
++
++ acx_l_tx_data(adev, tx, packet_len);
++ FN_EXIT1(OK);
++ return OK;
++bad:
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++}
++
++
++/***********************************************************************
++** acx_l_transmit_disassoc
++**
++** FIXME: looks like incomplete implementation of a helper:
++** acx_l_transmit_disassoc(adev, clt) - kick this client (we're an AP)
++** acx_l_transmit_disassoc(adev, NULL) - leave BSSID (we're a STA)
++*/
++#ifdef BROKEN
++int
++acx_l_transmit_disassoc(acx_device_t *adev, client_t *clt)
++{
++ struct tx *tx;
++ struct wlan_hdr_mgmt *head;
++ struct disassoc_frame_body *body;
++
++ FN_ENTER;
++/* if (clt != NULL) { */
++ tx = acx_l_alloc_tx(adev);
++ if (!tx)
++ goto bad;
++ head = acx_l_get_txbuf(adev, tx);
++ if (!head) {
++ acx_l_dealloc_tx(adev, tx);
++ goto bad;
++ }
++ body = (void*)(head + 1);
++
++/* clt->used = CLIENT_AUTHENTICATED_2; - not (yet?) associated */
++
++ head->fc = WF_FSTYPE_DISASSOCi;
++ head->dur = 0;
++ /* huh? It muchly depends on whether we're STA or AP...
++ ** sta->ap: da=bssid, sa=own, bssid=bssid
++ ** ap->sta: da=sta, sa=bssid, bssid=bssid. FIXME! */
++ MAC_COPY(head->da, adev->bssid);
++ MAC_COPY(head->sa, adev->dev_addr);
++ MAC_COPY(head->bssid, adev->dev_addr);
++ head->seq = 0;
++
++ /* "Class 3 frame received from nonassociated station." */
++ body->reason = host2ieee16(7);
++
++ /* fixed size struct, ok to sizeof */
++ acx_l_tx_data(adev, tx, WLAN_HDR_A3_LEN + sizeof(*body));
++/* } */
++ FN_EXIT1(OK);
++ return OK;
++bad:
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++}
++#endif
++
++
++/***********************************************************************
++** acx_s_complete_scan
++**
++** Called either from after_interrupt_task() if:
++** 1) there was Scan_Complete IRQ, or
++** 2) scanning expired in timer()
++** We need to decide which ESS or IBSS to join.
++** Iterates thru adev->sta_list:
++** if adev->ap is not bcast, will join only specified
++** ESS or IBSS with this bssid
++** checks peers' caps for ESS/IBSS bit
++** checks peers' SSID, allows exact match or hidden SSID
++** If station to join is chosen:
++** points adev->ap_client to the chosen struct client
++** sets adev->essid_for_assoc for future assoc attempt
++** Auth/assoc is not yet performed
++** Returns OK if there is no need to restart scan
++*/
++int
++acx_s_complete_scan(acx_device_t *adev)
++{
++ struct client *bss;
++ unsigned long flags;
++ u16 needed_cap;
++ int i;
++ int idx_found = -1;
++ int result = OK;
++
++ FN_ENTER;
++
++ switch (adev->mode) {
++ case ACX_MODE_0_ADHOC:
++ needed_cap = WF_MGMT_CAP_IBSS; /* 2, we require Ad-Hoc */
++ break;
++ case ACX_MODE_2_STA:
++ needed_cap = WF_MGMT_CAP_ESS; /* 1, we require Managed */
++ break;
++ default:
++ printk("acx: driver bug: mode=%d in complete_scan()\n", adev->mode);
++ dump_stack();
++ goto end;
++ }
++
++ acx_lock(adev, flags);
++
++ /* TODO: sta_iterator hiding implementation would be nice here... */
++
++ for (i = 0; i < VEC_SIZE(adev->sta_list); i++) {
++ bss = &adev->sta_list[i];
++ if (!bss->used) continue;
++
++
++ log(L_ASSOC, "scan table: SSID=\"%s\" CH=%d SIR=%d SNR=%d\n",
++ bss->essid, bss->channel, bss->sir, bss->snr);
++
++ if (!mac_is_bcast(adev->ap))
++ if (!mac_is_equal(bss->bssid, adev->ap))
++ continue; /* keep looking */
++
++ /* broken peer with no mode flags set? */
++ if (unlikely(!(bss->cap_info & (WF_MGMT_CAP_ESS | WF_MGMT_CAP_IBSS)))) {
++ printk("%s: strange peer "MACSTR" found with "
++ "neither ESS (AP) nor IBSS (Ad-Hoc) "
++ "capability - skipped\n",
++ adev->ndev->name, MAC(bss->address));
++ continue;
++ }
++ log(L_ASSOC, "peer_cap 0x%04X, needed_cap 0x%04X\n",
++ bss->cap_info, needed_cap);
++
++ /* does peer station support what we need? */
++ if ((bss->cap_info & needed_cap) != needed_cap)
++ continue; /* keep looking */
++
++ /* strange peer with NO basic rates?! */
++ if (unlikely(!bss->rate_bas)) {
++ printk("%s: strange peer "MACSTR" with empty rate set "
++ "- skipped\n",
++ adev->ndev->name, MAC(bss->address));
++ continue;
++ }
++
++ /* do we support all basic rates of this peer? */
++ if ((bss->rate_bas & adev->rate_oper) != bss->rate_bas) {
++/* we probably need to have all rates as operational rates,
++ even in case of an 11M-only configuration */
++#ifdef THIS_IS_TROUBLESOME
++ printk("%s: peer "MACSTR": incompatible basic rates "
++ "(AP requests 0x%04X, we have 0x%04X) "
++ "- skipped\n",
++ adev->ndev->name, MAC(bss->address),
++ bss->rate_bas, adev->rate_oper);
++ continue;
++#else
++ printk("%s: peer "MACSTR": incompatible basic rates "
++ "(AP requests 0x%04X, we have 0x%04X). "
++ "Considering anyway...\n",
++ adev->ndev->name, MAC(bss->address),
++ bss->rate_bas, adev->rate_oper);
++#endif
++ }
++
++ if ( !(adev->reg_dom_chanmask & (1<<(bss->channel-1))) ) {
++ printk("%s: warning: peer "MACSTR" is on channel %d "
++ "outside of channel range of current "
++ "regulatory domain - couldn't join "
++ "even if other settings match. "
++ "You might want to adapt your config\n",
++ adev->ndev->name, MAC(bss->address),
++ bss->channel);
++ continue; /* keep looking */
++ }
++
++ if (!adev->essid_active || !strcmp(bss->essid, adev->essid)) {
++ log(L_ASSOC,
++ "found station with matching ESSID! ('%s' "
++ "station, '%s' config)\n",
++ bss->essid,
++ (adev->essid_active) ? adev->essid : "[any]");
++ /* TODO: continue looking for peer with better SNR */
++ bss->used = CLIENT_JOIN_CANDIDATE;
++ idx_found = i;
++
++ /* stop searching if this station is
++ * on the current channel, otherwise
++ * keep looking for an even better match */
++ if (bss->channel == adev->channel)
++ break;
++ } else
++ if (is_hidden_essid(bss->essid)) {
++ /* hmm, station with empty or single-space SSID:
++ * using hidden SSID broadcast?
++ */
++ /* This behaviour is broken: which AP from zillion
++ ** of APs with hidden SSID you'd try?
++ ** We should use Probe requests to get Probe responses
++ ** and check for real SSID (are those never hidden?) */
++ bss->used = CLIENT_JOIN_CANDIDATE;
++ if (idx_found == -1)
++ idx_found = i;
++ log(L_ASSOC, "found station with empty or "
++ "single-space (hidden) SSID, considering "
++ "for assoc attempt\n");
++ /* ...and keep looking for better matches */
++ } else {
++ log(L_ASSOC, "ESSID doesn't match! ('%s' "
++ "station, '%s' config)\n",
++ bss->essid,
++ (adev->essid_active) ? adev->essid : "[any]");
++ }
++ }
++
++ /* TODO: iterate thru join candidates instead */
++ /* TODO: rescan if not associated within some timeout */
++ if (idx_found != -1) {
++ char *essid_src;
++ size_t essid_len;
++
++ bss = &adev->sta_list[idx_found];
++ adev->ap_client = bss;
++
++ if (is_hidden_essid(bss->essid)) {
++ /* if the ESSID of the station we found is empty
++ * (no broadcast), then use user-configured ESSID
++ * instead */
++ essid_src = adev->essid;
++ essid_len = adev->essid_len;
++ } else {
++ essid_src = bss->essid;
++ essid_len = strlen(bss->essid);
++ }
++
++ acx_update_capabilities(adev);
++
++ memcpy(adev->essid_for_assoc, essid_src, essid_len);
++ adev->essid_for_assoc[essid_len] = '\0';
++ adev->channel = bss->channel;
++ MAC_COPY(adev->bssid, bss->bssid);
++
++ bss->rate_cfg = (bss->rate_cap & adev->rate_oper);
++ bss->rate_cur = 1 << lowest_bit(bss->rate_cfg);
++ bss->rate_100 = acx_rate111to100(bss->rate_cur);
++
++ acxlog_mac(L_ASSOC,
++ "matching station found: ", adev->bssid, ", joining\n");
++
++ /* TODO: do we need to switch to the peer's channel first? */
++
++ if (ACX_MODE_0_ADHOC == adev->mode) {
++ acx_set_status(adev, ACX_STATUS_4_ASSOCIATED);
++ } else {
++ acx_l_transmit_authen1(adev);
++ acx_set_status(adev, ACX_STATUS_2_WAIT_AUTH);
++ }
++ } else { /* idx_found == -1 */
++ /* uh oh, no station found in range */
++ if (ACX_MODE_0_ADHOC == adev->mode) {
++ printk("%s: no matching station found in range, "
++ "generating our own IBSS instead\n",
++ adev->ndev->name);
++ /* we do it the HostAP way: */
++ MAC_COPY(adev->bssid, adev->dev_addr);
++ adev->bssid[0] |= 0x02; /* 'local assigned addr' bit */
++ /* add IBSS bit to our caps... */
++ acx_update_capabilities(adev);
++ acx_set_status(adev, ACX_STATUS_4_ASSOCIATED);
++ /* In order to cmd_join be called below */
++ idx_found = 0;
++ } else {
++ /* we shall scan again, AP can be
++ ** just temporarily powered off */
++ log(L_ASSOC,
++ "no matching station found in range yet\n");
++ acx_set_status(adev, ACX_STATUS_1_SCANNING);
++ result = NOT_OK;
++ }
++ }
++
++ acx_unlock(adev, flags);
++
++ if (idx_found != -1) {
++ if (ACX_MODE_0_ADHOC == adev->mode) {
++ /* need to update channel in beacon template */
++ SET_BIT(adev->set_mask, SET_TEMPLATES);
++ if (ACX_STATE_IFACE_UP & adev->dev_state_mask)
++ acx_s_update_card_settings(adev);
++ }
++ /* Inform firmware on our decision to start or join BSS */
++ acx_s_cmd_join_bssid(adev, adev->bssid);
++ }
++
++end:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acx_s_read_fw
++**
++** Loads a firmware image
++**
++** Returns:
++** 0 unable to load file
++** pointer to firmware success
++*/
++firmware_image_t*
++acx_s_read_fw(struct device *dev, const char *file, u32 *size)
++{
++ firmware_image_t *res;
++ const struct firmware *fw_entry;
++
++ res = NULL;
++ log(L_INIT, "requesting firmware image '%s'\n", file);
++ if (!request_firmware(&fw_entry, file, dev)) {
++ *size = 8;
++ if (fw_entry->size >= 8)
++ *size = 8 + le32_to_cpu(*(u32 *)(fw_entry->data + 4));
++ if (fw_entry->size != *size) {
++ printk("acx: firmware size does not match "
++ "firmware header: %d != %d, "
++ "aborting fw upload\n",
++ (int) fw_entry->size, (int) *size);
++ goto release_ret;
++ }
++ res = vmalloc(*size);
++ if (!res) {
++ printk("acx: no memory for firmware "
++ "(%u bytes)\n", *size);
++ goto release_ret;
++ }
++ memcpy(res, fw_entry->data, fw_entry->size);
++release_ret:
++ release_firmware(fw_entry);
++ return res;
++ }
++ printk("acx: firmware image '%s' was not provided. "
++ "Check your hotplug scripts\n", file);
++
++ /* checksum will be verified in write_fw, so don't bother here */
++ return res;
++}
++
++
++/***********************************************************************
++** acx_s_set_wepkey
++*/
++static void
++acx100_s_set_wepkey(acx_device_t *adev)
++{
++ ie_dot11WEPDefaultKey_t dk;
++ int i;
++
++ for (i = 0; i < DOT11_MAX_DEFAULT_WEP_KEYS; i++) {
++ if (adev->wep_keys[i].size != 0) {
++ log(L_INIT, "setting WEP key: %d with "
++ "total size: %d\n", i, (int) adev->wep_keys[i].size);
++ dk.action = 1;
++ dk.keySize = adev->wep_keys[i].size;
++ dk.defaultKeyNum = i;
++ memcpy(dk.key, adev->wep_keys[i].key, dk.keySize);
++ acx_s_configure(adev, &dk, ACX100_IE_DOT11_WEP_DEFAULT_KEY_WRITE);
++ }
++ }
++}
++
++static void
++acx111_s_set_wepkey(acx_device_t *adev)
++{
++ acx111WEPDefaultKey_t dk;
++ int i;
++
++ for (i = 0; i < DOT11_MAX_DEFAULT_WEP_KEYS; i++) {
++ if (adev->wep_keys[i].size != 0) {
++ log(L_INIT, "setting WEP key: %d with "
++ "total size: %d\n", i, (int) adev->wep_keys[i].size);
++ memset(&dk, 0, sizeof(dk));
++ dk.action = cpu_to_le16(1); /* "add key"; yes, that's a 16bit value */
++ dk.keySize = adev->wep_keys[i].size;
++
++ /* are these two lines necessary? */
++ dk.type = 0; /* default WEP key */
++ dk.index = 0; /* ignored when setting default key */
++
++ dk.defaultKeyNum = i;
++ memcpy(dk.key, adev->wep_keys[i].key, dk.keySize);
++ acx_s_issue_cmd(adev, ACX1xx_CMD_WEP_MGMT, &dk, sizeof(dk));
++ }
++ }
++}
++
++static void
++acx_s_set_wepkey(acx_device_t *adev)
++{
++ if (IS_ACX111(adev))
++ acx111_s_set_wepkey(adev);
++ else
++ acx100_s_set_wepkey(adev);
++}
++
++
++/***********************************************************************
++** acx100_s_init_wep
++**
++** FIXME: this should probably be moved into the new card settings
++** management, but since we're also modifying the memory map layout here
++** due to the WEP key space we want, we should take care...
++*/
++static int
++acx100_s_init_wep(acx_device_t *adev)
++{
++ acx100_ie_wep_options_t options;
++ ie_dot11WEPDefaultKeyID_t dk;
++ acx_ie_memmap_t pt;
++ int res = NOT_OK;
++
++ FN_ENTER;
++
++ if (OK != acx_s_interrogate(adev, &pt, ACX1xx_IE_MEMORY_MAP)) {
++ goto fail;
++ }
++
++ log(L_DEBUG, "CodeEnd:%X\n", pt.CodeEnd);
++
++ pt.WEPCacheStart = cpu_to_le32(le32_to_cpu(pt.CodeEnd) + 0x4);
++ pt.WEPCacheEnd = cpu_to_le32(le32_to_cpu(pt.CodeEnd) + 0x4);
++
++ if (OK != acx_s_configure(adev, &pt, ACX1xx_IE_MEMORY_MAP)) {
++ goto fail;
++ }
++
++ /* let's choose maximum setting: 4 default keys, plus 10 other keys: */
++ options.NumKeys = cpu_to_le16(DOT11_MAX_DEFAULT_WEP_KEYS + 10);
++ options.WEPOption = 0x00;
++
++ log(L_ASSOC, "%s: writing WEP options\n", __func__);
++ acx_s_configure(adev, &options, ACX100_IE_WEP_OPTIONS);
++
++ acx100_s_set_wepkey(adev);
++
++ if (adev->wep_keys[adev->wep_current_index].size != 0) {
++ log(L_ASSOC, "setting active default WEP key number: %d\n",
++ adev->wep_current_index);
++ dk.KeyID = adev->wep_current_index;
++ acx_s_configure(adev, &dk, ACX1xx_IE_DOT11_WEP_DEFAULT_KEY_SET); /* 0x1010 */
++ }
++ /* FIXME!!! wep_key_struct is filled nowhere! But adev
++ * is initialized to 0, and we don't REALLY need those keys either */
++/* for (i = 0; i < 10; i++) {
++ if (adev->wep_key_struct[i].len != 0) {
++ MAC_COPY(wep_mgmt.MacAddr, adev->wep_key_struct[i].addr);
++ wep_mgmt.KeySize = cpu_to_le16(adev->wep_key_struct[i].len);
++ memcpy(&wep_mgmt.Key, adev->wep_key_struct[i].key, le16_to_cpu(wep_mgmt.KeySize));
++ wep_mgmt.Action = cpu_to_le16(1);
++ log(L_ASSOC, "writing WEP key %d (len %d)\n", i, le16_to_cpu(wep_mgmt.KeySize));
++ if (OK == acx_s_issue_cmd(adev, ACX1xx_CMD_WEP_MGMT, &wep_mgmt, sizeof(wep_mgmt))) {
++ adev->wep_key_struct[i].index = i;
++ }
++ }
++ }
++*/
++
++ /* now retrieve the updated WEPCacheEnd pointer... */
++ if (OK != acx_s_interrogate(adev, &pt, ACX1xx_IE_MEMORY_MAP)) {
++ printk("%s: ACX1xx_IE_MEMORY_MAP read #2 FAILED\n",
++ adev->ndev->name);
++ goto fail;
++ }
++ /* ...and tell it to start allocating templates at that location */
++ /* (no endianness conversion needed) */
++ pt.PacketTemplateStart = pt.WEPCacheEnd;
++
++ if (OK != acx_s_configure(adev, &pt, ACX1xx_IE_MEMORY_MAP)) {
++ printk("%s: ACX1xx_IE_MEMORY_MAP write #2 FAILED\n",
++ adev->ndev->name);
++ goto fail;
++ }
++ res = OK;
++
++fail:
++ FN_EXIT1(res);
++ return res;
++}
++
++
++static int
++acx_s_init_max_template_generic(acx_device_t *adev, unsigned int len, unsigned int cmd)
++{
++ int res;
++ union {
++ acx_template_nullframe_t null;
++ acx_template_beacon_t b;
++ acx_template_tim_t tim;
++ acx_template_probereq_t preq;
++ acx_template_proberesp_t presp;
++ } templ;
++
++ memset(&templ, 0, len);
++ templ.null.size = cpu_to_le16(len - 2);
++ res = acx_s_issue_cmd(adev, cmd, &templ, len);
++ return res;
++}
++
++static inline int
++acx_s_init_max_null_data_template(acx_device_t *adev)
++{
++ return acx_s_init_max_template_generic(
++ adev, sizeof(acx_template_nullframe_t), ACX1xx_CMD_CONFIG_NULL_DATA
++ );
++}
++
++static inline int
++acx_s_init_max_beacon_template(acx_device_t *adev)
++{
++ return acx_s_init_max_template_generic(
++ adev, sizeof(acx_template_beacon_t), ACX1xx_CMD_CONFIG_BEACON
++ );
++}
++
++static inline int
++acx_s_init_max_tim_template(acx_device_t *adev)
++{
++ return acx_s_init_max_template_generic(
++ adev, sizeof(acx_template_tim_t), ACX1xx_CMD_CONFIG_TIM
++ );
++}
++
++static inline int
++acx_s_init_max_probe_response_template(acx_device_t *adev)
++{
++ return acx_s_init_max_template_generic(
++ adev, sizeof(acx_template_proberesp_t), ACX1xx_CMD_CONFIG_PROBE_RESPONSE
++ );
++}
++
++static inline int
++acx_s_init_max_probe_request_template(acx_device_t *adev)
++{
++ return acx_s_init_max_template_generic(
++ adev, sizeof(acx_template_probereq_t), ACX1xx_CMD_CONFIG_PROBE_REQUEST
++ );
++}
++
++/***********************************************************************
++** acx_s_set_tim_template
++**
++** FIXME: In full blown driver we will regularly update partial virtual bitmap
++** by calling this function
++** (it can be done by irq handler on each DTIM irq or by timer...)
++
++[802.11 7.3.2.6] TIM information element:
++- 1 EID
++- 1 Length
++1 1 DTIM Count
++ indicates how many beacons (including this) appear before next DTIM
++ (0=this one is a DTIM)
++2 1 DTIM Period
++ number of beacons between successive DTIMs
++ (0=reserved, 1=all TIMs are DTIMs, 2=every other, etc)
++3 1 Bitmap Control
++ bit0: Traffic Indicator bit associated with Assoc ID 0 (Bcast AID?)
++ set to 1 in TIM elements with a value of 0 in the DTIM Count field
++ when one or more broadcast or multicast frames are buffered at the AP.
++ bit1-7: Bitmap Offset (logically Bitmap_Offset = Bitmap_Control & 0xFE).
++4 n Partial Virtual Bitmap
++ Visible part of traffic-indication bitmap.
++ Full bitmap consists of 2008 bits (251 octets) such that bit number N
++ (0<=N<=2007) in the bitmap corresponds to bit number (N mod 8)
++ in octet number N/8 where the low-order bit of each octet is bit0,
++ and the high order bit is bit7.
++ Each set bit in virtual bitmap corresponds to traffic buffered by AP
++ for a specific station (with corresponding AID?).
++ Partial Virtual Bitmap shows a part of bitmap which has non-zero.
++ Bitmap Offset is a number of skipped zero octets (see above).
++ 'Missing' octets at the tail are also assumed to be zero.
++ Example: Length=6, Bitmap_Offset=2, Partial_Virtual_Bitmap=55 55 55
++ This means that traffic-indication bitmap is:
++ 00000000 00000000 01010101 01010101 01010101 00000000 00000000...
++ (is bit0 in the map is always 0 and real value is in Bitmap Control bit0?)
++*/
++static int
++acx_s_set_tim_template(acx_device_t *adev)
++{
++/* For now, configure smallish test bitmap, all zero ("no pending data") */
++ enum { bitmap_size = 5 };
++
++ acx_template_tim_t t;
++ int result;
++
++ FN_ENTER;
++
++ memset(&t, 0, sizeof(t));
++ t.size = 5 + bitmap_size; /* eid+len+count+period+bmap_ctrl + bmap */
++ t.tim_eid = WLAN_EID_TIM;
++ t.len = 3 + bitmap_size; /* count+period+bmap_ctrl + bmap */
++ result = acx_s_issue_cmd(adev, ACX1xx_CMD_CONFIG_TIM, &t, sizeof(t));
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acx_fill_beacon_or_proberesp_template
++**
++** For frame format info, please see 802.11-1999.pdf item 7.2.3.9 and below!!
++**
++** NB: we use the fact that
++** struct acx_template_proberesp and struct acx_template_beacon are the same
++** (well, almost...)
++**
++** [802.11] Beacon's body consist of these IEs:
++** 1 Timestamp
++** 2 Beacon interval
++** 3 Capability information
++** 4 SSID
++** 5 Supported rates (up to 8 rates)
++** 6 FH Parameter Set (frequency-hopping PHYs only)
++** 7 DS Parameter Set (direct sequence PHYs only)
++** 8 CF Parameter Set (only if PCF is supported)
++** 9 IBSS Parameter Set (ad-hoc only)
++**
++** Beacon only:
++** 10 TIM (AP only) (see 802.11 7.3.2.6)
++** 11 Country Information (802.11d)
++** 12 FH Parameters (802.11d)
++** 13 FH Pattern Table (802.11d)
++** ... (?!! did not yet find relevant PDF file... --vda)
++** 19 ERP Information (extended rate PHYs)
++** 20 Extended Supported Rates (if more than 8 rates)
++**
++** Proberesp only:
++** 10 Country information (802.11d)
++** 11 FH Parameters (802.11d)
++** 12 FH Pattern Table (802.11d)
++** 13-n Requested information elements (802.11d)
++** ????
++** 18 ERP Information (extended rate PHYs)
++** 19 Extended Supported Rates (if more than 8 rates)
++*/
++static int
++acx_fill_beacon_or_proberesp_template(acx_device_t *adev,
++ struct acx_template_beacon *templ,
++ u16 fc /* in host order! */)
++{
++ int len;
++ u8 *p;
++
++ FN_ENTER;
++
++ memset(templ, 0, sizeof(*templ));
++ MAC_BCAST(templ->da);
++ MAC_COPY(templ->sa, adev->dev_addr);
++ MAC_COPY(templ->bssid, adev->bssid);
++
++ templ->beacon_interval = cpu_to_le16(adev->beacon_interval);
++ acx_update_capabilities(adev);
++ templ->cap = cpu_to_le16(adev->capabilities);
++
++ p = templ->variable;
++ p = wlan_fill_ie_ssid(p, adev->essid_len, adev->essid);
++ p = wlan_fill_ie_rates(p, adev->rate_supported_len, adev->rate_supported);
++ p = wlan_fill_ie_ds_parms(p, adev->channel);
++ /* NB: should go AFTER tim, but acx seem to keep tim last always */
++ p = wlan_fill_ie_rates_ext(p, adev->rate_supported_len, adev->rate_supported);
++
++ switch (adev->mode) {
++ case ACX_MODE_0_ADHOC:
++ /* ATIM window */
++ p = wlan_fill_ie_ibss_parms(p, 0); break;
++ case ACX_MODE_3_AP:
++ /* TIM IE is set up as separate template */
++ break;
++ }
++
++ len = p - (u8*)templ;
++ templ->fc = cpu_to_le16(WF_FTYPE_MGMT | fc);
++ /* - 2: do not count 'u16 size' field */
++ templ->size = cpu_to_le16(len - 2);
++
++ FN_EXIT1(len);
++ return len;
++}
++
++
++#if POWER_SAVE_80211
++/***********************************************************************
++** acx_s_set_null_data_template
++*/
++static int
++acx_s_set_null_data_template(acx_device_t *adev)
++{
++ struct acx_template_nullframe b;
++ int result;
++
++ FN_ENTER;
++
++ /* memset(&b, 0, sizeof(b)); not needed, setting all members */
++
++ b.size = cpu_to_le16(sizeof(b) - 2);
++ b.hdr.fc = WF_FTYPE_MGMTi | WF_FSTYPE_NULLi;
++ b.hdr.dur = 0;
++ MAC_BCAST(b.hdr.a1);
++ MAC_COPY(b.hdr.a2, adev->dev_addr);
++ MAC_COPY(b.hdr.a3, adev->bssid);
++ b.hdr.seq = 0;
++
++ result = acx_s_issue_cmd(adev, ACX1xx_CMD_CONFIG_NULL_DATA, &b, sizeof(b));
++
++ FN_EXIT1(result);
++ return result;
++}
++#endif
++
++
++/***********************************************************************
++** acx_s_set_beacon_template
++*/
++static int
++acx_s_set_beacon_template(acx_device_t *adev)
++{
++ struct acx_template_beacon bcn;
++ int len, result;
++
++ FN_ENTER;
++
++ len = acx_fill_beacon_or_proberesp_template(adev, &bcn, WF_FSTYPE_BEACON);
++ result = acx_s_issue_cmd(adev, ACX1xx_CMD_CONFIG_BEACON, &bcn, len);
++
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acx_s_set_probe_response_template
++*/
++static int
++acx_s_set_probe_response_template(acx_device_t *adev)
++{
++ struct acx_template_proberesp pr;
++ int len, result;
++
++ FN_ENTER;
++
++ len = acx_fill_beacon_or_proberesp_template(adev, &pr, WF_FSTYPE_PROBERESP);
++ result = acx_s_issue_cmd(adev, ACX1xx_CMD_CONFIG_PROBE_RESPONSE, &pr, len);
++
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acx_s_init_packet_templates()
++**
++** NOTE: order is very important here, to have a correct memory layout!
++** init templates: max Probe Request (station mode), max NULL data,
++** max Beacon, max TIM, max Probe Response.
++*/
++static int
++acx_s_init_packet_templates(acx_device_t *adev)
++{
++ acx_ie_memmap_t mm; /* ACX100 only */
++ int result = NOT_OK;
++
++ FN_ENTER;
++
++ log(L_DEBUG|L_INIT, "initializing max packet templates\n");
++
++ if (OK != acx_s_init_max_probe_request_template(adev))
++ goto failed;
++
++ if (OK != acx_s_init_max_null_data_template(adev))
++ goto failed;
++
++ if (OK != acx_s_init_max_beacon_template(adev))
++ goto failed;
++
++ if (OK != acx_s_init_max_tim_template(adev))
++ goto failed;
++
++ if (OK != acx_s_init_max_probe_response_template(adev))
++ goto failed;
++
++ if (IS_ACX111(adev)) {
++ /* ACX111 doesn't need the memory map magic below,
++ * and the other templates will be set later (acx_start) */
++ result = OK;
++ goto success;
++ }
++
++ /* ACX100 will have its TIM template set,
++ * and we also need to update the memory map */
++
++ if (OK != acx_s_set_tim_template(adev))
++ goto failed_acx100;
++
++ log(L_DEBUG, "sizeof(memmap)=%d bytes\n", (int)sizeof(mm));
++
++ if (OK != acx_s_interrogate(adev, &mm, ACX1xx_IE_MEMORY_MAP))
++ goto failed_acx100;
++
++ mm.QueueStart = cpu_to_le32(le32_to_cpu(mm.PacketTemplateEnd) + 4);
++ if (OK != acx_s_configure(adev, &mm, ACX1xx_IE_MEMORY_MAP))
++ goto failed_acx100;
++
++ result = OK;
++ goto success;
++
++failed_acx100:
++ log(L_DEBUG|L_INIT,
++ /* "cb=0x%X\n" */
++ "ACXMemoryMap:\n"
++ ".CodeStart=0x%X\n"
++ ".CodeEnd=0x%X\n"
++ ".WEPCacheStart=0x%X\n"
++ ".WEPCacheEnd=0x%X\n"
++ ".PacketTemplateStart=0x%X\n"
++ ".PacketTemplateEnd=0x%X\n",
++ /* len, */
++ le32_to_cpu(mm.CodeStart),
++ le32_to_cpu(mm.CodeEnd),
++ le32_to_cpu(mm.WEPCacheStart),
++ le32_to_cpu(mm.WEPCacheEnd),
++ le32_to_cpu(mm.PacketTemplateStart),
++ le32_to_cpu(mm.PacketTemplateEnd));
++
++failed:
++ printk("%s: %s() FAILED\n", adev->ndev->name, __func__);
++
++success:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++*/
++static int
++acx_s_set_probe_request_template(acx_device_t *adev)
++{
++ struct acx_template_probereq probereq;
++ char *p;
++ int res;
++ int frame_len;
++
++ FN_ENTER;
++
++ memset(&probereq, 0, sizeof(probereq));
++
++ probereq.fc = WF_FTYPE_MGMTi | WF_FSTYPE_PROBEREQi;
++ MAC_BCAST(probereq.da);
++ MAC_COPY(probereq.sa, adev->dev_addr);
++ MAC_BCAST(probereq.bssid);
++
++ p = probereq.variable;
++ p = wlan_fill_ie_ssid(p, adev->essid_len, adev->essid);
++ p = wlan_fill_ie_rates(p, adev->rate_supported_len, adev->rate_supported);
++ p = wlan_fill_ie_rates_ext(p, adev->rate_supported_len, adev->rate_supported);
++ frame_len = p - (char*)&probereq;
++ probereq.size = cpu_to_le16(frame_len - 2);
++
++ res = acx_s_issue_cmd(adev, ACX1xx_CMD_CONFIG_PROBE_REQUEST, &probereq, frame_len);
++ FN_EXIT0;
++ return res;
++}
++
++
++/***********************************************************************
++** acx_s_init_mac
++*/
++int
++acx_s_init_mac(acx_device_t *adev)
++{
++ int result = NOT_OK;
++
++ FN_ENTER;
++
++ if (IS_ACX111(adev)) {
++ adev->ie_len = acx111_ie_len;
++ adev->ie_len_dot11 = acx111_ie_len_dot11;
++ } else {
++ adev->ie_len = acx100_ie_len;
++ adev->ie_len_dot11 = acx100_ie_len_dot11;
++ }
++
++#if defined (ACX_MEM)
++ adev->memblocksize = 256; /* 256 is default */
++ /* try to load radio for both ACX100 and ACX111, since both
++ * chips have at least some firmware versions making use of an
++ * external radio module */
++ acxmem_s_upload_radio(adev);
++#else
++ if (IS_PCI(adev)) {
++ adev->memblocksize = 256; /* 256 is default */
++ /* try to load radio for both ACX100 and ACX111, since both
++ * chips have at least some firmware versions making use of an
++ * external radio module */
++ acxpci_s_upload_radio(adev);
++ } else {
++ adev->memblocksize = 128;
++ }
++#endif
++
++ if (IS_ACX111(adev)) {
++ /* for ACX111, the order is different from ACX100
++ 1. init packet templates
++ 2. create station context and create dma regions
++ 3. init wep default keys
++ */
++ if (OK != acx_s_init_packet_templates(adev))
++ goto fail;
++ if (OK != acx111_s_create_dma_regions(adev)) {
++ printk("%s: acx111_create_dma_regions FAILED\n",
++ adev->ndev->name);
++ goto fail;
++ }
++ } else {
++ if (OK != acx100_s_init_wep(adev))
++ goto fail;
++ if (OK != acx_s_init_packet_templates(adev))
++ goto fail;
++ if (OK != acx100_s_create_dma_regions(adev)) {
++ printk("%s: acx100_create_dma_regions FAILED\n",
++ adev->ndev->name);
++ goto fail;
++ }
++ }
++
++ MAC_COPY(adev->ndev->dev_addr, adev->dev_addr);
++ result = OK;
++
++fail:
++ if (result)
++ printk("acx: init_mac() FAILED\n");
++ FN_EXIT1(result);
++ return result;
++}
++
++
++void
++acx_s_set_sane_reg_domain(acx_device_t *adev, int do_set)
++{
++ unsigned mask;
++
++ unsigned int i;
++
++ for (i = 0; i < sizeof(acx_reg_domain_ids); i++)
++ if (acx_reg_domain_ids[i] == adev->reg_dom_id)
++ break;
++
++ if (sizeof(acx_reg_domain_ids) == i) {
++ log(L_INIT, "Invalid or unsupported regulatory domain"
++ " 0x%02X specified, falling back to FCC (USA)!"
++ " Please report if this sounds fishy!\n",
++ adev->reg_dom_id);
++ i = 0;
++ adev->reg_dom_id = acx_reg_domain_ids[i];
++
++ /* since there was a mismatch, we need to force updating */
++ do_set = 1;
++ }
++
++ if (do_set) {
++ acx_ie_generic_t dom;
++ dom.m.bytes[0] = adev->reg_dom_id;
++ acx_s_configure(adev, &dom, ACX1xx_IE_DOT11_CURRENT_REG_DOMAIN);
++ }
++
++ adev->reg_dom_chanmask = reg_domain_channel_masks[i];
++
++ mask = (1 << (adev->channel - 1));
++ if (!(adev->reg_dom_chanmask & mask)) {
++ /* hmm, need to adjust our channel to reside within domain */
++ mask = 1;
++ for (i = 1; i <= 14; i++) {
++ if (adev->reg_dom_chanmask & mask) {
++ printk("%s: adjusting selected channel from %d "
++ "to %d due to new regulatory domain\n",
++ adev->ndev->name, adev->channel, i);
++ adev->channel = i;
++ break;
++ }
++ mask <<= 1;
++ }
++ }
++}
++
++
++#if POWER_SAVE_80211
++static void
++acx_s_update_80211_powersave_mode(acx_device_t *adev)
++{
++ /* merge both structs in a union to be able to have common code */
++ union {
++ acx111_ie_powersave_t acx111;
++ acx100_ie_powersave_t acx100;
++ } pm;
++
++ /* change 802.11 power save mode settings */
++ log(L_INIT, "updating 802.11 power save mode settings: "
++ "wakeup_cfg 0x%02X, listen interval %u, "
++ "options 0x%02X, hangover period %u, "
++ "enhanced_ps_transition_time %u\n",
++ adev->ps_wakeup_cfg, adev->ps_listen_interval,
++ adev->ps_options, adev->ps_hangover_period,
++ adev->ps_enhanced_transition_time);
++ acx_s_interrogate(adev, &pm, ACX1xx_IE_POWER_MGMT);
++ log(L_INIT, "Previous PS mode settings: wakeup_cfg 0x%02X, "
++ "listen interval %u, options 0x%02X, "
++ "hangover period %u, "
++ "enhanced_ps_transition_time %u, beacon_rx_time %u\n",
++ pm.acx111.wakeup_cfg,
++ pm.acx111.listen_interval,
++ pm.acx111.options,
++ pm.acx111.hangover_period,
++ IS_ACX111(adev) ?
++ pm.acx111.enhanced_ps_transition_time
++ : pm.acx100.enhanced_ps_transition_time,
++ IS_ACX111(adev) ?
++ pm.acx111.beacon_rx_time
++ : (u32)-1
++ );
++ pm.acx111.wakeup_cfg = adev->ps_wakeup_cfg;
++ pm.acx111.listen_interval = adev->ps_listen_interval;
++ pm.acx111.options = adev->ps_options;
++ pm.acx111.hangover_period = adev->ps_hangover_period;
++ if (IS_ACX111(adev)) {
++ pm.acx111.beacon_rx_time = cpu_to_le32(adev->ps_beacon_rx_time);
++ pm.acx111.enhanced_ps_transition_time = cpu_to_le32(adev->ps_enhanced_transition_time);
++ } else {
++ pm.acx100.enhanced_ps_transition_time = cpu_to_le16(adev->ps_enhanced_transition_time);
++ }
++ acx_s_configure(adev, &pm, ACX1xx_IE_POWER_MGMT);
++ acx_s_interrogate(adev, &pm, ACX1xx_IE_POWER_MGMT);
++ log(L_INIT, "wakeup_cfg: 0x%02X\n", pm.acx111.wakeup_cfg);
++ acx_s_msleep(40);
++ acx_s_interrogate(adev, &pm, ACX1xx_IE_POWER_MGMT);
++ log(L_INIT, "wakeup_cfg: 0x%02X\n", pm.acx111.wakeup_cfg);
++ log(L_INIT, "power save mode change %s\n",
++ (pm.acx111.wakeup_cfg & PS_CFG_PENDING) ? "FAILED" : "was successful");
++ /* FIXME: maybe verify via PS_CFG_PENDING bit here
++ * that power save mode change was successful. */
++ /* FIXME: we shouldn't trigger a scan immediately after
++ * fiddling with power save mode (since the firmware is sending
++ * a NULL frame then). */
++}
++#endif
++
++
++/***********************************************************************
++** acx_s_update_card_settings
++**
++** Applies accumulated changes in various adev->xxxx members
++** Called by ioctl commit handler, acx_start, acx_set_defaults,
++** acx_s_after_interrupt_task (if IRQ_CMD_UPDATE_CARD_CFG),
++*/
++static void
++acx111_s_sens_radio_16_17(acx_device_t *adev)
++{
++ u32 feature1, feature2;
++
++ if ((adev->sensitivity < 1) || (adev->sensitivity > 3)) {
++ printk("%s: invalid sensitivity setting (1..3), "
++ "setting to 1\n", adev->ndev->name);
++ adev->sensitivity = 1;
++ }
++ acx111_s_get_feature_config(adev, &feature1, &feature2);
++ CLEAR_BIT(feature1, FEATURE1_LOW_RX|FEATURE1_EXTRA_LOW_RX);
++ if (adev->sensitivity > 1)
++ SET_BIT(feature1, FEATURE1_LOW_RX);
++ if (adev->sensitivity > 2)
++ SET_BIT(feature1, FEATURE1_EXTRA_LOW_RX);
++ acx111_s_feature_set(adev, feature1, feature2);
++}
++
++
++void
++acx_s_update_card_settings(acx_device_t *adev)
++{
++ unsigned long flags;
++ unsigned int start_scan = 0;
++ int i;
++
++ FN_ENTER;
++
++ log(L_INIT, "get_mask 0x%08X, set_mask 0x%08X\n",
++ adev->get_mask, adev->set_mask);
++
++ /* Track dependencies betweed various settings */
++
++ if (adev->set_mask & (GETSET_MODE|GETSET_RESCAN|GETSET_WEP)) {
++ log(L_INIT, "important setting has been changed. "
++ "Need to update packet templates, too\n");
++ SET_BIT(adev->set_mask, SET_TEMPLATES);
++ }
++ if (adev->set_mask & GETSET_CHANNEL) {
++ /* This will actually tune RX/TX to the channel */
++ SET_BIT(adev->set_mask, GETSET_RX|GETSET_TX);
++ switch (adev->mode) {
++ case ACX_MODE_0_ADHOC:
++ case ACX_MODE_3_AP:
++ /* Beacons contain channel# - update them */
++ SET_BIT(adev->set_mask, SET_TEMPLATES);
++ }
++ switch (adev->mode) {
++ case ACX_MODE_0_ADHOC:
++ case ACX_MODE_2_STA:
++ start_scan = 1;
++ }
++ }
++
++ /* Apply settings */
++
++#ifdef WHY_SHOULD_WE_BOTHER /* imagine we were just powered off */
++ /* send a disassoc request in case it's required */
++ if (adev->set_mask & (GETSET_MODE|GETSET_RESCAN|GETSET_CHANNEL|GETSET_WEP)) {
++ if (ACX_MODE_2_STA == adev->mode) {
++ if (ACX_STATUS_4_ASSOCIATED == adev->status) {
++ log(L_ASSOC, "we were ASSOCIATED - "
++ "sending disassoc request\n");
++ acx_lock(adev, flags);
++ acx_l_transmit_disassoc(adev, NULL);
++ /* FIXME: deauth? */
++ acx_unlock(adev, flags);
++ }
++ /* need to reset some other stuff as well */
++ log(L_DEBUG, "resetting bssid\n");
++ MAC_ZERO(adev->bssid);
++ SET_BIT(adev->set_mask, SET_TEMPLATES|SET_STA_LIST);
++ start_scan = 1;
++ }
++ }
++#endif
++
++ if (adev->get_mask & GETSET_STATION_ID) {
++ u8 stationID[4 + ACX1xx_IE_DOT11_STATION_ID_LEN];
++ const u8 *paddr;
++
++ acx_s_interrogate(adev, &stationID, ACX1xx_IE_DOT11_STATION_ID);
++ paddr = &stationID[4];
++ for (i = 0; i < ETH_ALEN; i++) {
++ /* we copy the MAC address (reversed in
++ * the card) to the netdevice's MAC
++ * address, and on ifup it will be
++ * copied into iwadev->dev_addr */
++ adev->ndev->dev_addr[ETH_ALEN - 1 - i] = paddr[i];
++ }
++ CLEAR_BIT(adev->get_mask, GETSET_STATION_ID);
++ }
++
++ if (adev->get_mask & GETSET_SENSITIVITY) {
++ if ((RADIO_RFMD_11 == adev->radio_type)
++ || (RADIO_MAXIM_0D == adev->radio_type)
++ || (RADIO_RALINK_15 == adev->radio_type)) {
++ acx_s_read_phy_reg(adev, 0x30, &adev->sensitivity);
++ } else {
++ log(L_INIT, "don't know how to get sensitivity "
++ "for radio type 0x%02X\n", adev->radio_type);
++ adev->sensitivity = 0;
++ }
++ log(L_INIT, "got sensitivity value %u\n", adev->sensitivity);
++
++ CLEAR_BIT(adev->get_mask, GETSET_SENSITIVITY);
++ }
++
++ if (adev->get_mask & GETSET_ANTENNA) {
++ u8 antenna[4 + ACX1xx_IE_DOT11_CURRENT_ANTENNA_LEN];
++
++ memset(antenna, 0, sizeof(antenna));
++ acx_s_interrogate(adev, antenna, ACX1xx_IE_DOT11_CURRENT_ANTENNA);
++ adev->antenna = antenna[4];
++ log(L_INIT, "got antenna value 0x%02X\n", adev->antenna);
++ CLEAR_BIT(adev->get_mask, GETSET_ANTENNA);
++ }
++
++ if (adev->get_mask & GETSET_ED_THRESH) {
++ if (IS_ACX100(adev)) {
++ u8 ed_threshold[4 + ACX100_IE_DOT11_ED_THRESHOLD_LEN];
++
++ memset(ed_threshold, 0, sizeof(ed_threshold));
++ acx_s_interrogate(adev, ed_threshold, ACX100_IE_DOT11_ED_THRESHOLD);
++ adev->ed_threshold = ed_threshold[4];
++ } else {
++ log(L_INIT, "acx111 doesn't support ED\n");
++ adev->ed_threshold = 0;
++ }
++ log(L_INIT, "got Energy Detect (ED) threshold %u\n", adev->ed_threshold);
++ CLEAR_BIT(adev->get_mask, GETSET_ED_THRESH);
++ }
++
++ if (adev->get_mask & GETSET_CCA) {
++ if (IS_ACX100(adev)) {
++ u8 cca[4 + ACX1xx_IE_DOT11_CURRENT_CCA_MODE_LEN];
++
++ memset(cca, 0, sizeof(adev->cca));
++ acx_s_interrogate(adev, cca, ACX1xx_IE_DOT11_CURRENT_CCA_MODE);
++ adev->cca = cca[4];
++ } else {
++ log(L_INIT, "acx111 doesn't support CCA\n");
++ adev->cca = 0;
++ }
++ log(L_INIT, "got Channel Clear Assessment (CCA) value %u\n", adev->cca);
++ CLEAR_BIT(adev->get_mask, GETSET_CCA);
++ }
++
++ if (adev->get_mask & GETSET_REG_DOMAIN) {
++ acx_ie_generic_t dom;
++
++ acx_s_interrogate(adev, &dom, ACX1xx_IE_DOT11_CURRENT_REG_DOMAIN);
++ adev->reg_dom_id = dom.m.bytes[0];
++ acx_s_set_sane_reg_domain(adev, 0);
++ log(L_INIT, "got regulatory domain 0x%02X\n", adev->reg_dom_id);
++ CLEAR_BIT(adev->get_mask, GETSET_REG_DOMAIN);
++ }
++
++ if (adev->set_mask & GETSET_STATION_ID) {
++ u8 stationID[4 + ACX1xx_IE_DOT11_STATION_ID_LEN];
++ u8 *paddr;
++
++ paddr = &stationID[4];
++ memcpy(adev->dev_addr, adev->ndev->dev_addr, ETH_ALEN);
++ for (i = 0; i < ETH_ALEN; i++) {
++ /* copy the MAC address we obtained when we noticed
++ * that the ethernet iface's MAC changed
++ * to the card (reversed in
++ * the card!) */
++ paddr[i] = adev->dev_addr[ETH_ALEN - 1 - i];
++ }
++ acx_s_configure(adev, &stationID, ACX1xx_IE_DOT11_STATION_ID);
++ CLEAR_BIT(adev->set_mask, GETSET_STATION_ID);
++ }
++
++ if (adev->set_mask & SET_TEMPLATES) {
++ log(L_INIT, "updating packet templates\n");
++ switch (adev->mode) {
++ case ACX_MODE_2_STA:
++ acx_s_set_probe_request_template(adev);
++#if POWER_SAVE_80211
++ acx_s_set_null_data_template(adev);
++#endif
++ break;
++ case ACX_MODE_0_ADHOC:
++ acx_s_set_probe_request_template(adev);
++#if POWER_SAVE_80211
++ /* maybe power save functionality is somehow possible
++ * for Ad-Hoc mode, too... FIXME: verify it somehow? firmware debug fields? */
++ acx_s_set_null_data_template(adev);
++#endif
++ /* fall through */
++ case ACX_MODE_3_AP:
++ acx_s_set_beacon_template(adev);
++ acx_s_set_tim_template(adev);
++ /* BTW acx111 firmware would not send probe responses
++ ** if probe request does not have all basic rates flagged
++ ** by 0x80! Thus firmware does not conform to 802.11,
++ ** it should ignore 0x80 bit in ratevector from STA.
++ ** We can 'fix' it by not using this template and
++ ** sending probe responses by hand. TODO --vda */
++ acx_s_set_probe_response_template(adev);
++ }
++ /* Needed if generated frames are to be emitted at different tx rate now */
++ log(L_IRQ, "redoing cmd_join_bssid() after template cfg\n");
++ acx_s_cmd_join_bssid(adev, adev->bssid);
++ CLEAR_BIT(adev->set_mask, SET_TEMPLATES);
++ }
++ if (adev->set_mask & SET_STA_LIST) {
++ acx_lock(adev, flags);
++ acx_l_sta_list_init(adev);
++ CLEAR_BIT(adev->set_mask, SET_STA_LIST);
++ acx_unlock(adev, flags);
++ }
++ if (adev->set_mask & SET_RATE_FALLBACK) {
++ u8 rate[4 + ACX1xx_IE_RATE_FALLBACK_LEN];
++
++ /* configure to not do fallbacks when not in auto rate mode */
++ rate[4] = (adev->rate_auto) ? /* adev->txrate_fallback_retries */ 1 : 0;
++ log(L_INIT, "updating Tx fallback to %u retries\n", rate[4]);
++ acx_s_configure(adev, &rate, ACX1xx_IE_RATE_FALLBACK);
++ CLEAR_BIT(adev->set_mask, SET_RATE_FALLBACK);
++ }
++ if (adev->set_mask & GETSET_TXPOWER) {
++ log(L_INIT, "updating transmit power: %u dBm\n",
++ adev->tx_level_dbm);
++ acx_s_set_tx_level(adev, adev->tx_level_dbm);
++ CLEAR_BIT(adev->set_mask, GETSET_TXPOWER);
++ }
++
++ if (adev->set_mask & GETSET_SENSITIVITY) {
++ log(L_INIT, "updating sensitivity value: %u\n",
++ adev->sensitivity);
++ switch (adev->radio_type) {
++ case RADIO_RFMD_11:
++ case RADIO_MAXIM_0D:
++ case RADIO_RALINK_15:
++ acx_s_write_phy_reg(adev, 0x30, adev->sensitivity);
++ break;
++ case RADIO_RADIA_16:
++ case RADIO_UNKNOWN_17:
++ acx111_s_sens_radio_16_17(adev);
++ break;
++ default:
++ log(L_INIT, "don't know how to modify sensitivity "
++ "for radio type 0x%02X\n", adev->radio_type);
++ }
++ CLEAR_BIT(adev->set_mask, GETSET_SENSITIVITY);
++ }
++
++ if (adev->set_mask & GETSET_ANTENNA) {
++ /* antenna */
++ u8 antenna[4 + ACX1xx_IE_DOT11_CURRENT_ANTENNA_LEN];
++
++ memset(antenna, 0, sizeof(antenna));
++ antenna[4] = adev->antenna;
++ log(L_INIT, "updating antenna value: 0x%02X\n",
++ adev->antenna);
++ acx_s_configure(adev, &antenna, ACX1xx_IE_DOT11_CURRENT_ANTENNA);
++ CLEAR_BIT(adev->set_mask, GETSET_ANTENNA);
++ }
++
++ if (adev->set_mask & GETSET_ED_THRESH) {
++ /* ed_threshold */
++ log(L_INIT, "updating Energy Detect (ED) threshold: %u\n",
++ adev->ed_threshold);
++ if (IS_ACX100(adev)) {
++ u8 ed_threshold[4 + ACX100_IE_DOT11_ED_THRESHOLD_LEN];
++
++ memset(ed_threshold, 0, sizeof(ed_threshold));
++ ed_threshold[4] = adev->ed_threshold;
++ acx_s_configure(adev, &ed_threshold, ACX100_IE_DOT11_ED_THRESHOLD);
++ }
++ else
++ log(L_INIT, "acx111 doesn't support ED!\n");
++ CLEAR_BIT(adev->set_mask, GETSET_ED_THRESH);
++ }
++
++ if (adev->set_mask & GETSET_CCA) {
++ /* CCA value */
++ log(L_INIT, "updating Channel Clear Assessment "
++ "(CCA) value: 0x%02X\n", adev->cca);
++ if (IS_ACX100(adev)) {
++ u8 cca[4 + ACX1xx_IE_DOT11_CURRENT_CCA_MODE_LEN];
++
++ memset(cca, 0, sizeof(cca));
++ cca[4] = adev->cca;
++ acx_s_configure(adev, &cca, ACX1xx_IE_DOT11_CURRENT_CCA_MODE);
++ }
++ else
++ log(L_INIT, "acx111 doesn't support CCA!\n");
++ CLEAR_BIT(adev->set_mask, GETSET_CCA);
++ }
++
++ if (adev->set_mask & GETSET_LED_POWER) {
++ /* Enable Tx */
++ log(L_INIT, "updating power LED status: %u\n", adev->led_power);
++
++ acx_lock(adev, flags);
++#if defined (ACX_MEM)
++ acxmem_l_power_led(adev, adev->led_power);
++#else
++ if (IS_PCI(adev))
++ acxpci_l_power_led(adev, adev->led_power);
++#endif
++ CLEAR_BIT(adev->set_mask, GETSET_LED_POWER);
++ acx_unlock(adev, flags);
++ }
++
++ if (adev->set_mask & GETSET_POWER_80211) {
++#if POWER_SAVE_80211
++ acx_s_update_80211_powersave_mode(adev);
++#endif
++ CLEAR_BIT(adev->set_mask, GETSET_POWER_80211);
++ }
++
++ if (adev->set_mask & GETSET_CHANNEL) {
++ /* channel */
++ log(L_INIT, "updating channel to: %u\n", adev->channel);
++ CLEAR_BIT(adev->set_mask, GETSET_CHANNEL);
++ }
++
++ if (adev->set_mask & GETSET_TX) {
++ /* set Tx */
++ log(L_INIT, "updating: %s Tx\n",
++ adev->tx_disabled ? "disable" : "enable");
++ if (adev->tx_disabled)
++ acx_s_issue_cmd(adev, ACX1xx_CMD_DISABLE_TX, NULL, 0);
++ else
++ acx_s_issue_cmd(adev, ACX1xx_CMD_ENABLE_TX, &adev->channel, 1);
++ CLEAR_BIT(adev->set_mask, GETSET_TX);
++ }
++
++ if (adev->set_mask & GETSET_RX) {
++ /* Enable Rx */
++ log(L_INIT, "updating: enable Rx on channel: %u\n",
++ adev->channel);
++ acx_s_issue_cmd(adev, ACX1xx_CMD_ENABLE_RX, &adev->channel, 1);
++ CLEAR_BIT(adev->set_mask, GETSET_RX);
++ }
++
++ if (adev->set_mask & GETSET_RETRY) {
++ u8 short_retry[4 + ACX1xx_IE_DOT11_SHORT_RETRY_LIMIT_LEN];
++ u8 long_retry[4 + ACX1xx_IE_DOT11_LONG_RETRY_LIMIT_LEN];
++
++ log(L_INIT, "updating short retry limit: %u, long retry limit: %u\n",
++ adev->short_retry, adev->long_retry);
++ short_retry[0x4] = adev->short_retry;
++ long_retry[0x4] = adev->long_retry;
++ acx_s_configure(adev, &short_retry, ACX1xx_IE_DOT11_SHORT_RETRY_LIMIT);
++ acx_s_configure(adev, &long_retry, ACX1xx_IE_DOT11_LONG_RETRY_LIMIT);
++ CLEAR_BIT(adev->set_mask, GETSET_RETRY);
++ }
++
++ if (adev->set_mask & SET_MSDU_LIFETIME) {
++ u8 xmt_msdu_lifetime[4 + ACX1xx_IE_DOT11_MAX_XMIT_MSDU_LIFETIME_LEN];
++
++ log(L_INIT, "updating tx MSDU lifetime: %u\n",
++ adev->msdu_lifetime);
++ *(u32 *)&xmt_msdu_lifetime[4] = cpu_to_le32((u32)adev->msdu_lifetime);
++ acx_s_configure(adev, &xmt_msdu_lifetime, ACX1xx_IE_DOT11_MAX_XMIT_MSDU_LIFETIME);
++ CLEAR_BIT(adev->set_mask, SET_MSDU_LIFETIME);
++ }
++
++ if (adev->set_mask & GETSET_REG_DOMAIN) {
++ log(L_INIT, "updating regulatory domain: 0x%02X\n",
++ adev->reg_dom_id);
++ acx_s_set_sane_reg_domain(adev, 1);
++ CLEAR_BIT(adev->set_mask, GETSET_REG_DOMAIN);
++ }
++
++ if (adev->set_mask & GETSET_MODE) {
++ adev->ndev->type = (adev->mode == ACX_MODE_MONITOR) ?
++ adev->monitor_type : ARPHRD_ETHER;
++
++ switch (adev->mode) {
++ case ACX_MODE_3_AP:
++
++ acx_lock(adev, flags);
++ acx_l_sta_list_init(adev);
++ adev->aid = 0;
++ adev->ap_client = NULL;
++ MAC_COPY(adev->bssid, adev->dev_addr);
++ /* this basically says "we're connected" */
++ acx_set_status(adev, ACX_STATUS_4_ASSOCIATED);
++ acx_unlock(adev, flags);
++
++ acx111_s_feature_off(adev, 0, FEATURE2_NO_TXCRYPT|FEATURE2_SNIFFER);
++ /* start sending beacons */
++ acx_s_cmd_join_bssid(adev, adev->bssid);
++ break;
++ case ACX_MODE_MONITOR:
++ acx111_s_feature_on(adev, 0, FEATURE2_NO_TXCRYPT|FEATURE2_SNIFFER);
++ /* this stops beacons */
++ acx_s_cmd_join_bssid(adev, adev->bssid);
++ /* this basically says "we're connected" */
++ acx_set_status(adev, ACX_STATUS_4_ASSOCIATED);
++ SET_BIT(adev->set_mask, SET_RXCONFIG|SET_WEP_OPTIONS);
++ break;
++ case ACX_MODE_0_ADHOC:
++ case ACX_MODE_2_STA:
++ acx111_s_feature_off(adev, 0, FEATURE2_NO_TXCRYPT|FEATURE2_SNIFFER);
++
++ acx_lock(adev, flags);
++ adev->aid = 0;
++ adev->ap_client = NULL;
++ acx_unlock(adev, flags);
++
++ /* we want to start looking for peer or AP */
++ start_scan = 1;
++ break;
++ case ACX_MODE_OFF:
++ /* TODO: disable RX/TX, stop any scanning activity etc: */
++ /* adev->tx_disabled = 1; */
++ /* SET_BIT(adev->set_mask, GETSET_RX|GETSET_TX); */
++
++ /* This stops beacons (invalid macmode...) */
++ acx_s_cmd_join_bssid(adev, adev->bssid);
++ acx_set_status(adev, ACX_STATUS_0_STOPPED);
++ break;
++ }
++ CLEAR_BIT(adev->set_mask, GETSET_MODE);
++ }
++
++ if (adev->set_mask & SET_RXCONFIG) {
++ acx_s_initialize_rx_config(adev);
++ CLEAR_BIT(adev->set_mask, SET_RXCONFIG);
++ }
++
++ if (adev->set_mask & GETSET_RESCAN) {
++ switch (adev->mode) {
++ case ACX_MODE_0_ADHOC:
++ case ACX_MODE_2_STA:
++ start_scan = 1;
++ break;
++ }
++ CLEAR_BIT(adev->set_mask, GETSET_RESCAN);
++ }
++
++ if (adev->set_mask & GETSET_WEP) {
++ /* encode */
++
++ ie_dot11WEPDefaultKeyID_t dkey;
++#ifdef DEBUG_WEP
++ struct {
++ u16 type;
++ u16 len;
++ u8 val;
++ } ACX_PACKED keyindic;
++#endif
++ log(L_INIT, "updating WEP key settings\n");
++
++ acx_s_set_wepkey(adev);
++
++ dkey.KeyID = adev->wep_current_index;
++ log(L_INIT, "setting WEP key %u as default\n", dkey.KeyID);
++ acx_s_configure(adev, &dkey, ACX1xx_IE_DOT11_WEP_DEFAULT_KEY_SET);
++#ifdef DEBUG_WEP
++ keyindic.val = 3;
++ acx_s_configure(adev, &keyindic, ACX111_IE_KEY_CHOOSE);
++#endif
++ start_scan = 1;
++ CLEAR_BIT(adev->set_mask, GETSET_WEP);
++ }
++
++ if (adev->set_mask & SET_WEP_OPTIONS) {
++ acx100_ie_wep_options_t options;
++ if (IS_ACX111(adev)) {
++ log(L_DEBUG, "setting WEP Options for acx111 is not supported\n");
++ } else {
++ log(L_INIT, "setting WEP Options\n");
++ acx100_s_init_wep(adev);
++#if 0
++ /* let's choose maximum setting: 4 default keys,
++ * plus 10 other keys: */
++ options.NumKeys = cpu_to_le16(DOT11_MAX_DEFAULT_WEP_KEYS + 10);
++ /* don't decrypt default key only,
++ * don't override decryption: */
++ options.WEPOption = 0;
++ if (adev->mode == ACX_MODE_MONITOR) {
++ /* don't decrypt default key only,
++ * override decryption mechanism: */
++ options.WEPOption = 2;
++ }
++
++ acx_s_configure(adev, &options, ACX100_IE_WEP_OPTIONS);
++#endif
++ }
++ CLEAR_BIT(adev->set_mask, SET_WEP_OPTIONS);
++ }
++
++ /* Rescan was requested */
++ if (start_scan) {
++ switch (adev->mode) {
++ case ACX_MODE_0_ADHOC:
++ case ACX_MODE_2_STA:
++ /* We can avoid clearing list if join code
++ ** will be a bit more clever about not picking
++ ** 'bad' AP over and over again */
++ acx_lock(adev, flags);
++ adev->ap_client = NULL;
++ acx_l_sta_list_init(adev);
++ acx_set_status(adev, ACX_STATUS_1_SCANNING);
++ acx_unlock(adev, flags);
++
++ acx_s_cmd_start_scan(adev);
++ }
++ }
++
++ /* debug, rate, and nick don't need any handling */
++ /* what about sniffing mode?? */
++
++ log(L_INIT, "get_mask 0x%08X, set_mask 0x%08X - after update\n",
++ adev->get_mask, adev->set_mask);
++
++/* end: */
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acx_e_after_interrupt_task
++*/
++static int
++acx_s_recalib_radio(acx_device_t *adev)
++{
++ if (IS_ACX111(adev)) {
++ acx111_cmd_radiocalib_t cal;
++
++ printk("%s: recalibrating radio\n", adev->ndev->name);
++ /* automatic recalibration, choose all methods: */
++ cal.methods = cpu_to_le32(0x8000000f);
++ /* automatic recalibration every 60 seconds (value in TUs)
++ * I wonder what the firmware default here is? */
++ cal.interval = cpu_to_le32(58594);
++ return acx_s_issue_cmd_timeo(adev, ACX111_CMD_RADIOCALIB,
++ &cal, sizeof(cal), CMD_TIMEOUT_MS(100));
++ } else {
++ /* On ACX100, we need to recalibrate the radio
++ * by issuing a GETSET_TX|GETSET_RX */
++ if (/* (OK == acx_s_issue_cmd(adev, ACX1xx_CMD_DISABLE_TX, NULL, 0)) &&
++ (OK == acx_s_issue_cmd(adev, ACX1xx_CMD_DISABLE_RX, NULL, 0)) && */
++ (OK == acx_s_issue_cmd(adev, ACX1xx_CMD_ENABLE_TX, &adev->channel, 1)) &&
++ (OK == acx_s_issue_cmd(adev, ACX1xx_CMD_ENABLE_RX, &adev->channel, 1)) )
++ return OK;
++ return NOT_OK;
++ }
++}
++
++static void
++acx_s_after_interrupt_recalib(acx_device_t *adev)
++{
++ int res;
++
++ /* this helps with ACX100 at least;
++ * hopefully ACX111 also does a
++ * recalibration here */
++
++ /* clear flag beforehand, since we want to make sure
++ * it's cleared; then only set it again on specific circumstances */
++ CLEAR_BIT(adev->after_interrupt_jobs, ACX_AFTER_IRQ_CMD_RADIO_RECALIB);
++
++ /* better wait a bit between recalibrations to
++ * prevent overheating due to torturing the card
++ * into working too long despite high temperature
++ * (just a safety measure) */
++ if (adev->recalib_time_last_success
++ && time_before(jiffies, adev->recalib_time_last_success
++ + RECALIB_PAUSE * 60 * HZ)) {
++ if (adev->recalib_msg_ratelimit <= 4) {
++ printk("%s: less than " STRING(RECALIB_PAUSE)
++ " minutes since last radio recalibration, "
++ "not recalibrating (maybe card is too hot?)\n",
++ adev->ndev->name);
++ adev->recalib_msg_ratelimit++;
++ if (adev->recalib_msg_ratelimit == 5)
++ printk("disabling above message until next recalib\n");
++ }
++ return;
++ }
++
++ adev->recalib_msg_ratelimit = 0;
++
++ /* note that commands sometimes fail (card busy),
++ * so only clear flag if we were fully successful */
++ res = acx_s_recalib_radio(adev);
++ if (res == OK) {
++ printk("%s: successfully recalibrated radio\n",
++ adev->ndev->name);
++ adev->recalib_time_last_success = jiffies;
++ adev->recalib_failure_count = 0;
++ } else {
++ /* failed: resubmit, but only limited
++ * amount of times within some time range
++ * to prevent endless loop */
++
++ adev->recalib_time_last_success = 0; /* we failed */
++
++ /* if some time passed between last
++ * attempts, then reset failure retry counter
++ * to be able to do next recalib attempt */
++ if (time_after(jiffies, adev->recalib_time_last_attempt + 5*HZ))
++ adev->recalib_failure_count = 0;
++
++ if (adev->recalib_failure_count < 5) {
++ /* increment inside only, for speedup of outside path */
++ adev->recalib_failure_count++;
++ adev->recalib_time_last_attempt = jiffies;
++ acx_schedule_task(adev, ACX_AFTER_IRQ_CMD_RADIO_RECALIB);
++ }
++ }
++}
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
++static void
++acx_e_after_interrupt_task(struct work_struct *work)
++{
++ acx_device_t *adev = container_of(work, acx_device_t, after_interrupt_task);
++#else
++ static void
++ acx_e_after_interrupt_task(void *data)
++ {
++ struct net_device *ndev = (struct net_device*)data;
++ acx_device_t *adev = ndev2adev(ndev);
++#endif
++ FN_ENTER;
++
++ acx_sem_lock(adev);
++
++ if (!adev->after_interrupt_jobs)
++ goto end; /* no jobs to do */
++
++#if TX_CLEANUP_IN_SOFTIRQ
++ /* can happen only on PCI */
++ if (adev->after_interrupt_jobs & ACX_AFTER_IRQ_TX_CLEANUP) {
++ acx_lock(adev, flags);
++ acxpci_l_clean_txdesc(adev);
++ CLEAR_BIT(adev->after_interrupt_jobs, ACX_AFTER_IRQ_TX_CLEANUP);
++ acx_unlock(adev, flags);
++ }
++#endif
++ /* we see lotsa tx errors */
++ if (adev->after_interrupt_jobs & ACX_AFTER_IRQ_CMD_RADIO_RECALIB) {
++ acx_s_after_interrupt_recalib(adev);
++ }
++
++ /* a poor interrupt code wanted to do update_card_settings() */
++ if (adev->after_interrupt_jobs & ACX_AFTER_IRQ_UPDATE_CARD_CFG) {
++ if (ACX_STATE_IFACE_UP & adev->dev_state_mask)
++ acx_s_update_card_settings(adev);
++ CLEAR_BIT(adev->after_interrupt_jobs, ACX_AFTER_IRQ_UPDATE_CARD_CFG);
++ }
++
++ /* 1) we detected that no Scan_Complete IRQ came from fw, or
++ ** 2) we found too many STAs */
++ if (adev->after_interrupt_jobs & ACX_AFTER_IRQ_CMD_STOP_SCAN) {
++ log(L_IRQ, "sending a stop scan cmd...\n");
++ acx_s_issue_cmd(adev, ACX1xx_CMD_STOP_SCAN, NULL, 0);
++ /* HACK: set the IRQ bit, since we won't get a
++ * scan complete IRQ any more on ACX111 (works on ACX100!),
++ * since _we_, not a fw, have stopped the scan */
++ SET_BIT(adev->irq_status, HOST_INT_SCAN_COMPLETE);
++ CLEAR_BIT(adev->after_interrupt_jobs, ACX_AFTER_IRQ_CMD_STOP_SCAN);
++ }
++
++ /* either fw sent Scan_Complete or we detected that
++ ** no Scan_Complete IRQ came from fw. Finish scanning,
++ ** pick join partner if any */
++ if (adev->after_interrupt_jobs & ACX_AFTER_IRQ_COMPLETE_SCAN) {
++ if (adev->status == ACX_STATUS_1_SCANNING) {
++ if (OK != acx_s_complete_scan(adev)) {
++ SET_BIT(adev->after_interrupt_jobs,
++ ACX_AFTER_IRQ_RESTART_SCAN);
++ }
++ } else {
++ /* + scan kills current join status - restore it
++ ** (do we need it for STA?) */
++ /* + does it happen only with active scans?
++ ** active and passive scans? ALL scans including
++ ** background one? */
++ /* + was not verified that everything is restored
++ ** (but at least we start to emit beacons again) */
++ switch (adev->mode) {
++ case ACX_MODE_0_ADHOC:
++ case ACX_MODE_3_AP:
++ log(L_IRQ, "redoing cmd_join_bssid() after scan\n");
++ acx_s_cmd_join_bssid(adev, adev->bssid);
++ }
++ }
++ CLEAR_BIT(adev->after_interrupt_jobs, ACX_AFTER_IRQ_COMPLETE_SCAN);
++ }
++
++ /* STA auth or assoc timed out, start over again */
++ if (adev->after_interrupt_jobs & ACX_AFTER_IRQ_RESTART_SCAN) {
++ log(L_IRQ, "sending a start_scan cmd...\n");
++ acx_s_cmd_start_scan(adev);
++ CLEAR_BIT(adev->after_interrupt_jobs, ACX_AFTER_IRQ_RESTART_SCAN);
++ }
++
++ /* whee, we got positive assoc response! 8) */
++ if (adev->after_interrupt_jobs & ACX_AFTER_IRQ_CMD_ASSOCIATE) {
++ acx_ie_generic_t pdr;
++ /* tiny race window exists, checking that we still a STA */
++ switch (adev->mode) {
++ case ACX_MODE_2_STA:
++ pdr.m.aid = cpu_to_le16(adev->aid);
++ acx_s_configure(adev, &pdr, ACX1xx_IE_ASSOC_ID);
++ acx_set_status(adev, ACX_STATUS_4_ASSOCIATED);
++ log(L_ASSOC|L_DEBUG, "ASSOCIATED!\n");
++ CLEAR_BIT(adev->after_interrupt_jobs, ACX_AFTER_IRQ_CMD_ASSOCIATE);
++ }
++ }
++end:
++ acx_sem_unlock(adev);
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acx_schedule_task
++**
++** Schedule the call of the after_interrupt method after leaving
++** the interrupt context.
++*/
++void
++acx_schedule_task(acx_device_t *adev, unsigned int set_flag)
++{
++ SET_BIT(adev->after_interrupt_jobs, set_flag);
++ SCHEDULE_WORK(&adev->after_interrupt_task);
++}
++
++
++/***********************************************************************
++*/
++void
++acx_init_task_scheduler(acx_device_t *adev)
++{
++ /* configure task scheduler */
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
++ INIT_WORK(&adev->after_interrupt_task, acx_e_after_interrupt_task);
++#else
++ INIT_WORK(&adev->after_interrupt_task, acx_e_after_interrupt_task,
++ adev->ndev);
++#endif
++}
++
++
++/***********************************************************************
++** acx_s_start
++*/
++void
++acx_s_start(acx_device_t *adev)
++{
++ FN_ENTER;
++
++ /*
++ * Ok, now we do everything that can possibly be done with ioctl
++ * calls to make sure that when it was called before the card
++ * was up we get the changes asked for
++ */
++
++ SET_BIT(adev->set_mask, SET_TEMPLATES|SET_STA_LIST|GETSET_WEP
++ |GETSET_TXPOWER|GETSET_ANTENNA|GETSET_ED_THRESH|GETSET_CCA
++ |GETSET_REG_DOMAIN|GETSET_MODE|GETSET_CHANNEL
++ |GETSET_TX|GETSET_RX|GETSET_STATION_ID);
++
++ log(L_INIT, "updating initial settings on iface activation\n");
++ acx_s_update_card_settings(adev);
++
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acx_update_capabilities
++*/
++void
++acx_update_capabilities(acx_device_t *adev)
++{
++ u16 cap = 0;
++
++ switch (adev->mode) {
++ case ACX_MODE_3_AP:
++ SET_BIT(cap, WF_MGMT_CAP_ESS); break;
++ case ACX_MODE_0_ADHOC:
++ SET_BIT(cap, WF_MGMT_CAP_IBSS); break;
++ /* other types of stations do not emit beacons */
++ }
++
++ if (adev->wep_restricted) {
++ SET_BIT(cap, WF_MGMT_CAP_PRIVACY);
++ }
++ if (adev->cfgopt_dot11ShortPreambleOption) {
++ SET_BIT(cap, WF_MGMT_CAP_SHORT);
++ }
++ if (adev->cfgopt_dot11PBCCOption) {
++ SET_BIT(cap, WF_MGMT_CAP_PBCC);
++ }
++ if (adev->cfgopt_dot11ChannelAgility) {
++ SET_BIT(cap, WF_MGMT_CAP_AGILITY);
++ }
++ log(L_DEBUG, "caps updated from 0x%04X to 0x%04X\n",
++ adev->capabilities, cap);
++ adev->capabilities = cap;
++}
++
++/***********************************************************************
++** Common function to parse ALL configoption struct formats
++** (ACX100 and ACX111; FIXME: how to make it work with ACX100 USB!?!?).
++** FIXME: logging should be removed here and added to a /proc file instead
++*/
++void
++acx_s_parse_configoption(acx_device_t *adev, const acx111_ie_configoption_t *pcfg)
++{
++ const u8 *pEle;
++ int i;
++ int is_acx111 = IS_ACX111(adev);
++
++ if (acx_debug & L_DEBUG) {
++ printk("configoption struct content:\n");
++ acx_dump_bytes(pcfg, sizeof(*pcfg));
++ }
++
++ if (( is_acx111 && (adev->eeprom_version == 5))
++ || (!is_acx111 && (adev->eeprom_version == 4))
++ || (!is_acx111 && (adev->eeprom_version == 5))) {
++ /* these versions are known to be supported */
++ } else {
++ printk("unknown chip and EEPROM version combination (%s, v%d), "
++ "don't know how to parse config options yet. "
++ "Please report\n", is_acx111 ? "ACX111" : "ACX100",
++ adev->eeprom_version);
++ return;
++ }
++
++ /* first custom-parse the first part which has chip-specific layout */
++
++ pEle = (const u8 *) pcfg;
++
++ pEle += 4; /* skip (type,len) header */
++
++ memcpy(adev->cfgopt_NVSv, pEle, sizeof(adev->cfgopt_NVSv));
++ pEle += sizeof(adev->cfgopt_NVSv);
++
++ if (is_acx111) {
++ adev->cfgopt_NVS_vendor_offs = le16_to_cpu(*(u16 *)pEle);
++ pEle += sizeof(adev->cfgopt_NVS_vendor_offs);
++
++ adev->cfgopt_probe_delay = 200; /* good default value? */
++ pEle += 2; /* FIXME: unknown, value 0x0001 */
++ } else {
++ memcpy(adev->cfgopt_MAC, pEle, sizeof(adev->cfgopt_MAC));
++ pEle += sizeof(adev->cfgopt_MAC);
++
++ adev->cfgopt_probe_delay = le16_to_cpu(*(u16 *)pEle);
++ pEle += sizeof(adev->cfgopt_probe_delay);
++ if ((adev->cfgopt_probe_delay < 100) || (adev->cfgopt_probe_delay > 500)) {
++ printk("strange probe_delay value %d, "
++ "tweaking to 200\n", adev->cfgopt_probe_delay);
++ adev->cfgopt_probe_delay = 200;
++ }
++ }
++
++ adev->cfgopt_eof_memory = le32_to_cpu(*(u32 *)pEle);
++ pEle += sizeof(adev->cfgopt_eof_memory);
++
++ printk("NVS_vendor_offs:%04X probe_delay:%d eof_memory:%d\n",
++ adev->cfgopt_NVS_vendor_offs,
++ adev->cfgopt_probe_delay,
++ adev->cfgopt_eof_memory);
++
++ adev->cfgopt_dot11CCAModes = *pEle++;
++ adev->cfgopt_dot11Diversity = *pEle++;
++ adev->cfgopt_dot11ShortPreambleOption = *pEle++;
++ adev->cfgopt_dot11PBCCOption = *pEle++;
++ adev->cfgopt_dot11ChannelAgility = *pEle++;
++ adev->cfgopt_dot11PhyType = *pEle++;
++ adev->cfgopt_dot11TempType = *pEle++;
++ printk("CCAModes:%02X Diversity:%02X ShortPreOpt:%02X "
++ "PBCC:%02X ChanAgil:%02X PHY:%02X Temp:%02X\n",
++ adev->cfgopt_dot11CCAModes,
++ adev->cfgopt_dot11Diversity,
++ adev->cfgopt_dot11ShortPreambleOption,
++ adev->cfgopt_dot11PBCCOption,
++ adev->cfgopt_dot11ChannelAgility,
++ adev->cfgopt_dot11PhyType,
++ adev->cfgopt_dot11TempType);
++
++ /* then use common parsing for next part which has common layout */
++
++ pEle++; /* skip table_count (6) */
++
++ if (IS_MEM(adev) && IS_ACX100(adev))
++ {
++ /*
++ * For iPaq hx4700 Generic Slave F/W 1.10.7.K. I'm not sure if these
++ * 4 extra bytes are before the dot11 things above or after, so I'm just
++ * going to guess after. If someone sees these aren't reasonable numbers,
++ * please fix this.
++ * The area from which the dot11 values above are read contains:
++ * 04 01 01 01 00 05 01 06 00 02 01 02
++ * the 8 dot11 reads above take care of 8 of them, but which 8...
++ */
++ pEle += 4;
++ }
++
++ adev->cfgopt_antennas.type = pEle[0];
++ adev->cfgopt_antennas.len = pEle[1];
++ printk("AntennaID:%02X Len:%02X Data:",
++ adev->cfgopt_antennas.type, adev->cfgopt_antennas.len);
++ for (i = 0; i < pEle[1]; i++) {
++ adev->cfgopt_antennas.list[i] = pEle[i+2];
++ printk("%02X ", pEle[i+2]);
++ }
++ printk("\n");
++
++ pEle += pEle[1] + 2;
++ adev->cfgopt_power_levels.type = pEle[0];
++ adev->cfgopt_power_levels.len = pEle[1];
++ printk("PowerLevelID:%02X Len:%02X Data:",
++ adev->cfgopt_power_levels.type, adev->cfgopt_power_levels.len);
++ for (i = 0; i < pEle[1]; i++) {
++ adev->cfgopt_power_levels.list[i] = le16_to_cpu(*(u16 *)&pEle[i*2+2]);
++ printk("%04X ", adev->cfgopt_power_levels.list[i]);
++ }
++ printk("\n");
++
++ pEle += pEle[1]*2 + 2;
++ adev->cfgopt_data_rates.type = pEle[0];
++ adev->cfgopt_data_rates.len = pEle[1];
++ printk("DataRatesID:%02X Len:%02X Data:",
++ adev->cfgopt_data_rates.type, adev->cfgopt_data_rates.len);
++ for (i = 0; i < pEle[1]; i++) {
++ adev->cfgopt_data_rates.list[i] = pEle[i+2];
++ printk("%02X ", pEle[i+2]);
++ }
++ printk("\n");
++
++ pEle += pEle[1] + 2;
++ adev->cfgopt_domains.type = pEle[0];
++ adev->cfgopt_domains.len = pEle[1];
++ if (IS_MEM(adev) && IS_ACX100(adev))
++ {
++ /*
++ * For iPaq hx4700 Generic Slave F/W 1.10.7.K.
++ * There's an extra byte between this structure and the next
++ * that is not accounted for with this structure's length. It's
++ * most likely a bug in the firmware, but we can fix it here
++ * by bumping the length of this field by 1.
++ */
++ adev->cfgopt_domains.len++;
++ }
++ printk("DomainID:%02X Len:%02X Data:",
++ adev->cfgopt_domains.type, adev->cfgopt_domains.len);
++ for (i = 0; i < adev->cfgopt_domains.len; i++) {
++ adev->cfgopt_domains.list[i] = pEle[i+2];
++ printk("%02X ", pEle[i+2]);
++ }
++ printk("\n");
++
++ pEle += adev->cfgopt_domains.len + 2;
++
++ adev->cfgopt_product_id.type = pEle[0];
++ adev->cfgopt_product_id.len = pEle[1];
++ for (i = 0; i < pEle[1]; i++) {
++ adev->cfgopt_product_id.list[i] = pEle[i+2];
++ }
++ printk("ProductID:%02X Len:%02X Data:%.*s\n",
++ adev->cfgopt_product_id.type, adev->cfgopt_product_id.len,
++ adev->cfgopt_product_id.len, (char *)adev->cfgopt_product_id.list);
++
++ pEle += pEle[1] + 2;
++ adev->cfgopt_manufacturer.type = pEle[0];
++ adev->cfgopt_manufacturer.len = pEle[1];
++ for (i = 0; i < pEle[1]; i++) {
++ adev->cfgopt_manufacturer.list[i] = pEle[i+2];
++ }
++ printk("ManufacturerID:%02X Len:%02X Data:%.*s\n",
++ adev->cfgopt_manufacturer.type, adev->cfgopt_manufacturer.len,
++ adev->cfgopt_manufacturer.len, (char *)adev->cfgopt_manufacturer.list);
++/*
++ printk("EEPROM part:\n");
++ for (i=0; i<58; i++) {
++ printk("%02X =======> 0x%02X\n",
++ i, (u8 *)adev->cfgopt_NVSv[i-2]);
++ }
++*/
++}
++
++
++/***********************************************************************
++*/
++static int __init
++acx_e_init_module(void)
++{
++ int r1,r2,r3,r4;
++
++ acx_struct_size_check();
++
++ printk("acx: this driver is still EXPERIMENTAL\n"
++ "acx: reading README file and/or Craig's HOWTO is "
++ "recommended, visit http://acx100.sf.net in case "
++ "of further questions/discussion\n");
++
++#if defined(CONFIG_ACX_PCI)
++ r1 = acxpci_e_init_module();
++#else
++ r1 = -EINVAL;
++#endif
++#if defined(CONFIG_ACX_MEM)
++ r2 = acxmem_e_init_module();
++#else
++ r2 = -EINVAL;
++#endif
++#if defined(CONFIG_ACX_USB)
++ r3 = acxusb_e_init_module();
++#else
++ r3 = -EINVAL;
++#endif
++#if defined(CONFIG_ACX_CS)
++ r4 = acx_cs_init();
++#else
++ r4 = -EINVAL;
++#endif
++ if (r2 && r1 && r3 && r4) { /* all failed! */
++ if (r3 || r1)
++ return r3 ? r3 : r1;
++ else
++ return r2;
++ }
++ /* return success if at least one succeeded */
++ return 0;
++
++}
++
++static void __exit
++acx_e_cleanup_module(void)
++{
++#if defined(CONFIG_ACX_PCI)
++ acxpci_e_cleanup_module();
++#endif
++#if defined(CONFIG_ACX_MEM)
++ acxmem_e_cleanup_module();
++#endif
++#if defined(CONFIG_ACX_USB)
++ acxusb_e_cleanup_module();
++#endif
++#if defined(CONFIG_ACX_CS)
++ acx_cs_cleanup();
++#endif
++}
++
++module_init(acx_e_init_module)
++module_exit(acx_e_cleanup_module)
+Index: linux-2.6.23/drivers/net/wireless/acx/conv.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23/drivers/net/wireless/acx/conv.c 2008-01-20 21:13:40.000000000 +0000
+@@ -0,0 +1,504 @@
++/***********************************************************************
++** Copyright (C) 2003 ACX100 Open Source Project
++**
++** The contents of this file are subject to the Mozilla Public
++** License Version 1.1 (the "License"); you may not use this file
++** except in compliance with the License. You may obtain a copy of
++** the License at http://www.mozilla.org/MPL/
++**
++** Software distributed under the License is distributed on an "AS
++** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
++** implied. See the License for the specific language governing
++** rights and limitations under the License.
++**
++** Alternatively, the contents of this file may be used under the
++** terms of the GNU Public License version 2 (the "GPL"), in which
++** case the provisions of the GPL are applicable instead of the
++** above. If you wish to allow the use of your version of this file
++** only under the terms of the GPL and not to allow others to use
++** your version of this file under the MPL, indicate your decision
++** by deleting the provisions above and replace them with the notice
++** and other provisions required by the GPL. If you do not delete
++** the provisions above, a recipient may use your version of this
++** file under either the MPL or the GPL.
++** ---------------------------------------------------------------------
++** Inquiries regarding the ACX100 Open Source Project can be
++** made directly to:
++**
++** acx100-users@lists.sf.net
++** http://acx100.sf.net
++** ---------------------------------------------------------------------
++*/
++
++#include <linux/version.h>
++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18)
++#include <linux/config.h>
++#endif
++#include <linux/skbuff.h>
++#include <linux/if_arp.h>
++#include <linux/etherdevice.h>
++#include <linux/wireless.h>
++#include <net/iw_handler.h>
++
++#include "acx.h"
++
++
++/***********************************************************************
++** proto_is_stt
++**
++** Searches the 802.1h Selective Translation Table for a given
++** protocol.
++**
++** prottype - protocol number (in host order) to search for.
++**
++** Returns:
++** 1 - if the table is empty or a match is found.
++** 0 - if the table is non-empty and a match is not found.
++**
++** Based largely on p80211conv.c of the linux-wlan-ng project
++*/
++static inline int
++proto_is_stt(unsigned int proto)
++{
++ /* Always return found for now. This is the behavior used by the */
++ /* Zoom Win95 driver when 802.1h mode is selected */
++ /* TODO: If necessary, add an actual search we'll probably
++ need this to match the CMAC's way of doing things.
++ Need to do some testing to confirm.
++ */
++
++ if (proto == 0x80f3) /* APPLETALK */
++ return 1;
++
++ return 0;
++/* return ((prottype == ETH_P_AARP) || (prottype == ETH_P_IPX)); */
++}
++
++/* Helpers */
++
++static inline void
++store_llc_snap(struct wlan_llc *llc)
++{
++ llc->dsap = 0xaa; /* SNAP, see IEEE 802 */
++ llc->ssap = 0xaa;
++ llc->ctl = 0x03;
++}
++static inline int
++llc_is_snap(const struct wlan_llc *llc)
++{
++ return (llc->dsap == 0xaa)
++ && (llc->ssap == 0xaa)
++ && (llc->ctl == 0x03);
++}
++static inline void
++store_oui_rfc1042(struct wlan_snap *snap)
++{
++ snap->oui[0] = 0;
++ snap->oui[1] = 0;
++ snap->oui[2] = 0;
++}
++static inline int
++oui_is_rfc1042(const struct wlan_snap *snap)
++{
++ return (snap->oui[0] == 0)
++ && (snap->oui[1] == 0)
++ && (snap->oui[2] == 0);
++}
++static inline void
++store_oui_8021h(struct wlan_snap *snap)
++{
++ snap->oui[0] = 0;
++ snap->oui[1] = 0;
++ snap->oui[2] = 0xf8;
++}
++static inline int
++oui_is_8021h(const struct wlan_snap *snap)
++{
++ return (snap->oui[0] == 0)
++ && (snap->oui[1] == 0)
++ && (snap->oui[2] == 0xf8);
++}
++
++
++/***********************************************************************
++** acx_ether_to_txbuf
++**
++** Uses the contents of the ether frame to build the elements of
++** the 802.11 frame.
++**
++** We don't actually set up the frame header here. That's the
++** MAC's job. We're only handling conversion of DIXII or 802.3+LLC
++** frames to something that works with 802.11.
++**
++** Based largely on p80211conv.c of the linux-wlan-ng project
++*/
++int
++acx_ether_to_txbuf(acx_device_t *adev, void *txbuf, const struct sk_buff *skb)
++{
++ struct wlan_hdr_a3 *w_hdr;
++ struct wlan_ethhdr *e_hdr;
++ struct wlan_llc *e_llc;
++ struct wlan_snap *e_snap;
++ const u8 *a1, *a3;
++ int header_len, payload_len = -1;
++ /* protocol type or data length, depending on whether
++ * DIX or 802.3 ethernet format */
++ u16 proto;
++ u16 fc;
++
++ FN_ENTER;
++
++ if (unlikely(!skb->len)) {
++ log(L_DEBUG, "zero-length skb!\n");
++ goto end;
++ }
++
++ w_hdr = (struct wlan_hdr_a3*)txbuf;
++
++ switch (adev->mode) {
++ case ACX_MODE_MONITOR:
++ /* NB: one day we might want to play with DESC_CTL2_FCS
++ ** Will need to stop doing "- WLAN_FCS_LEN" here then */
++ if (unlikely(skb->len >= WLAN_A4FR_MAXLEN_WEP_FCS - WLAN_FCS_LEN)) {
++ printk("%s: can't tx oversized frame (%d bytes)\n",
++ adev->ndev->name, skb->len);
++ goto end;
++ }
++ memcpy(w_hdr, skb->data, skb->len);
++ payload_len = skb->len;
++ goto end;
++ }
++
++ /* step 1: classify ether frame, DIX or 802.3? */
++ e_hdr = (wlan_ethhdr_t *)skb->data;
++ proto = ntohs(e_hdr->type);
++ if (proto <= 1500) {
++ log(L_DEBUG, "tx: 802.3 len: %d\n", skb->len);
++ /* codes <= 1500 reserved for 802.3 lengths */
++ /* it's 802.3, pass ether payload unchanged, */
++ /* trim off ethernet header and copy payload to txdesc */
++ header_len = WLAN_HDR_A3_LEN;
++ } else {
++ /* it's DIXII, time for some conversion */
++ /* Create 802.11 packet. Header also contains llc and snap. */
++
++ log(L_DEBUG, "tx: DIXII len: %d\n", skb->len);
++
++ /* size of header is 802.11 header + llc + snap */
++ header_len = WLAN_HDR_A3_LEN + sizeof(wlan_llc_t) + sizeof(wlan_snap_t);
++ /* llc is located behind the 802.11 header */
++ e_llc = (wlan_llc_t*)(w_hdr + 1);
++ /* snap is located behind the llc */
++ e_snap = (wlan_snap_t*)(e_llc + 1);
++
++ /* setup the LLC header */
++ store_llc_snap(e_llc);
++
++ /* setup the SNAP header */
++ e_snap->type = htons(proto);
++ if (proto_is_stt(proto)) {
++ store_oui_8021h(e_snap);
++ } else {
++ store_oui_rfc1042(e_snap);
++ }
++ }
++ /* trim off ethernet header and copy payload to txbuf */
++ payload_len = skb->len - sizeof(wlan_ethhdr_t);
++ /* TODO: can we just let acx DMA payload from skb instead? */
++ memcpy((u8*)txbuf + header_len, skb->data + sizeof(wlan_ethhdr_t), payload_len);
++ payload_len += header_len;
++
++ /* Set up the 802.11 header */
++ switch (adev->mode) {
++ case ACX_MODE_0_ADHOC:
++ fc = (WF_FTYPE_DATAi | WF_FSTYPE_DATAONLYi);
++ a1 = e_hdr->daddr;
++ a3 = adev->bssid;
++ break;
++ case ACX_MODE_2_STA:
++ fc = (WF_FTYPE_DATAi | WF_FSTYPE_DATAONLYi | WF_FC_TODSi);
++ a1 = adev->bssid;
++ a3 = e_hdr->daddr;
++ break;
++ case ACX_MODE_3_AP:
++ fc = (WF_FTYPE_DATAi | WF_FSTYPE_DATAONLYi | WF_FC_FROMDSi);
++ a1 = e_hdr->daddr;
++ a3 = e_hdr->saddr;
++ break;
++ default:
++ printk("%s: error - converting eth to wlan in unknown mode\n",
++ adev->ndev->name);
++ payload_len = -1;
++ goto end;
++ }
++ if (adev->wep_enabled)
++ SET_BIT(fc, WF_FC_ISWEPi);
++
++ w_hdr->fc = fc;
++ w_hdr->dur = 0;
++ MAC_COPY(w_hdr->a1, a1);
++ MAC_COPY(w_hdr->a2, adev->dev_addr);
++ MAC_COPY(w_hdr->a3, a3);
++ w_hdr->seq = 0;
++
++#ifdef DEBUG_CONVERT
++ if (acx_debug & L_DATA) {
++ printk("original eth frame [%d]: ", skb->len);
++ acx_dump_bytes(skb->data, skb->len);
++ printk("802.11 frame [%d]: ", payload_len);
++ acx_dump_bytes(w_hdr, payload_len);
++ }
++#endif
++
++end:
++ FN_EXIT1(payload_len);
++ return payload_len;
++}
++
++
++/***********************************************************************
++** acx_rxbuf_to_ether
++**
++** Uses the contents of a received 802.11 frame to build an ether
++** frame.
++**
++** This function extracts the src and dest address from the 802.11
++** frame to use in the construction of the eth frame.
++**
++** Based largely on p80211conv.c of the linux-wlan-ng project
++*/
++struct sk_buff*
++acx_rxbuf_to_ether(acx_device_t *adev, rxbuffer_t *rxbuf)
++{
++ struct wlan_hdr *w_hdr;
++ struct wlan_ethhdr *e_hdr;
++ struct wlan_llc *e_llc;
++ struct wlan_snap *e_snap;
++ struct sk_buff *skb;
++ const u8 *daddr;
++ const u8 *saddr;
++ const u8 *e_payload;
++ int buflen, payload_length;
++ unsigned int payload_offset, mtu;
++ u16 fc;
++
++ FN_ENTER;
++
++ /* This looks complex because it must handle possible
++ ** phy header in rxbuff */
++ w_hdr = acx_get_wlan_hdr(adev, rxbuf);
++ payload_offset = WLAN_HDR_A3_LEN; /* it is relative to w_hdr */
++ payload_length = RXBUF_BYTES_USED(rxbuf) /* entire rxbuff... */
++ - ((u8*)w_hdr - (u8*)rxbuf) /* minus space before 802.11 frame */
++ - WLAN_HDR_A3_LEN; /* minus 802.11 header */
++
++ /* setup some vars for convenience */
++ fc = w_hdr->fc;
++ switch (WF_FC_FROMTODSi & fc) {
++ case 0:
++ daddr = w_hdr->a1;
++ saddr = w_hdr->a2;
++ break;
++ case WF_FC_FROMDSi:
++ daddr = w_hdr->a1;
++ saddr = w_hdr->a3;
++ break;
++ case WF_FC_TODSi:
++ daddr = w_hdr->a3;
++ saddr = w_hdr->a2;
++ break;
++ default: /* WF_FC_FROMTODSi */
++ payload_offset += (WLAN_HDR_A4_LEN - WLAN_HDR_A3_LEN);
++ payload_length -= (WLAN_HDR_A4_LEN - WLAN_HDR_A3_LEN);
++ daddr = w_hdr->a3;
++ saddr = w_hdr->a4;
++ }
++
++ if ((WF_FC_ISWEPi & fc) && IS_ACX100(adev)) {
++ /* chop off the IV+ICV WEP header and footer */
++ log(L_DATA|L_DEBUG, "rx: WEP packet, "
++ "chopping off IV and ICV\n");
++ payload_offset += WLAN_WEP_IV_LEN;
++ payload_length -= WLAN_WEP_IV_LEN + WLAN_WEP_ICV_LEN;
++ }
++
++ if (unlikely(payload_length < 0)) {
++ printk("%s: rx frame too short, ignored\n", adev->ndev->name);
++ goto ret_null;
++ }
++
++ e_hdr = (wlan_ethhdr_t*) ((u8*) w_hdr + payload_offset);
++ e_llc = (wlan_llc_t*) e_hdr;
++ e_snap = (wlan_snap_t*) (e_llc + 1);
++ mtu = adev->ndev->mtu;
++ e_payload = (u8*) (e_snap + 1);
++
++ log(L_DATA, "rx: payload_offset %d, payload_length %d\n",
++ payload_offset, payload_length);
++ log(L_XFER|L_DATA,
++ "rx: frame info: llc=%02X%02X%02X "
++ "snap.oui=%02X%02X%02X snap.type=%04X\n",
++ e_llc->dsap, e_llc->ssap, e_llc->ctl,
++ e_snap->oui[0], e_snap->oui[1], e_snap->oui[2],
++ ntohs(e_snap->type));
++
++ /* Test for the various encodings */
++ if ((payload_length >= sizeof(wlan_ethhdr_t))
++ && ((e_llc->dsap != 0xaa) || (e_llc->ssap != 0xaa))
++ && ( (mac_is_equal(daddr, e_hdr->daddr))
++ || (mac_is_equal(saddr, e_hdr->saddr))
++ )
++ ) {
++ /* 802.3 Encapsulated: */
++ /* wlan frame body contains complete eth frame (header+body) */
++ log(L_DEBUG|L_DATA, "rx: 802.3 ENCAP len=%d\n", payload_length);
++
++ if (unlikely(payload_length > (mtu + ETH_HLEN))) {
++ printk("%s: rx: ENCAP frame too large (%d > %d)\n",
++ adev->ndev->name,
++ payload_length, mtu + ETH_HLEN);
++ goto ret_null;
++ }
++
++ /* allocate space and setup host buffer */
++ buflen = payload_length;
++ /* Attempt to align IP header (14 bytes eth header + 2 = 16) */
++ skb = dev_alloc_skb(buflen + 2);
++ if (unlikely(!skb))
++ goto no_skb;
++ skb_reserve(skb, 2);
++ skb_put(skb, buflen); /* make room */
++
++ /* now copy the data from the 80211 frame */
++ memcpy(skb->data, e_hdr, payload_length);
++
++ } else if ( (payload_length >= sizeof(wlan_llc_t)+sizeof(wlan_snap_t))
++ && llc_is_snap(e_llc) ) {
++ /* wlan frame body contains: AA AA 03 ... (it's a SNAP) */
++
++ if ( !oui_is_rfc1042(e_snap)
++ || (proto_is_stt(ieee2host16(e_snap->type)) /* && (ethconv == WLAN_ETHCONV_8021h) */)) {
++ log(L_DEBUG|L_DATA, "rx: SNAP+RFC1042 len=%d\n", payload_length);
++ /* wlan frame body contains: AA AA 03 !(00 00 00) ... -or- */
++ /* wlan frame body contains: AA AA 03 00 00 00 0x80f3 ... */
++ /* build eth hdr, type = len, copy AA AA 03... as eth body */
++ /* it's a SNAP + RFC1042 frame && protocol is in STT */
++
++ if (unlikely(payload_length > mtu)) {
++ printk("%s: rx: SNAP frame too large (%d > %d)\n",
++ adev->ndev->name,
++ payload_length, mtu);
++ goto ret_null;
++ }
++
++ /* allocate space and setup host buffer */
++ buflen = payload_length + ETH_HLEN;
++ skb = dev_alloc_skb(buflen + 2);
++ if (unlikely(!skb))
++ goto no_skb;
++ skb_reserve(skb, 2);
++ skb_put(skb, buflen); /* make room */
++
++ /* create 802.3 header */
++ e_hdr = (wlan_ethhdr_t*) skb->data;
++ MAC_COPY(e_hdr->daddr, daddr);
++ MAC_COPY(e_hdr->saddr, saddr);
++ e_hdr->type = htons(payload_length);
++
++ /* Now copy the data from the 80211 frame.
++ Make room in front for the eth header, and keep the
++ llc and snap from the 802.11 payload */
++ memcpy(skb->data + ETH_HLEN,
++ e_llc, payload_length);
++
++ } else {
++ /* wlan frame body contains: AA AA 03 00 00 00 [type] [tail] */
++ /* build eth hdr, type=[type], copy [tail] as eth body */
++ log(L_DEBUG|L_DATA, "rx: 802.1h/RFC1042 len=%d\n",
++ payload_length);
++ /* it's an 802.1h frame (an RFC1042 && protocol is not in STT) */
++ /* build a DIXII + RFC894 */
++
++ payload_length -= sizeof(wlan_llc_t) + sizeof(wlan_snap_t);
++ if (unlikely(payload_length > mtu)) {
++ printk("%s: rx: DIXII frame too large (%d > %d)\n",
++ adev->ndev->name,
++ payload_length, mtu);
++ goto ret_null;
++ }
++
++ /* allocate space and setup host buffer */
++ buflen = payload_length + ETH_HLEN;
++ skb = dev_alloc_skb(buflen + 2);
++ if (unlikely(!skb))
++ goto no_skb;
++ skb_reserve(skb, 2);
++ skb_put(skb, buflen); /* make room */
++
++ /* create 802.3 header */
++ e_hdr = (wlan_ethhdr_t *) skb->data;
++ MAC_COPY(e_hdr->daddr, daddr);
++ MAC_COPY(e_hdr->saddr, saddr);
++ e_hdr->type = e_snap->type;
++
++ /* Now copy the data from the 80211 frame.
++ Make room in front for the eth header, and cut off the
++ llc and snap from the 802.11 payload */
++ memcpy(skb->data + ETH_HLEN,
++ e_payload, payload_length);
++ }
++
++ } else {
++ log(L_DEBUG|L_DATA, "rx: NON-ENCAP len=%d\n", payload_length);
++ /* build eth hdr, type=len, copy wlan body as eth body */
++ /* any NON-ENCAP */
++ /* it's a generic 80211+LLC or IPX 'Raw 802.3' */
++ /* build an 802.3 frame */
++
++ if (unlikely(payload_length > mtu)) {
++ printk("%s: rx: OTHER frame too large (%d > %d)\n",
++ adev->ndev->name, payload_length, mtu);
++ goto ret_null;
++ }
++
++ /* allocate space and setup host buffer */
++ buflen = payload_length + ETH_HLEN;
++ skb = dev_alloc_skb(buflen + 2);
++ if (unlikely(!skb))
++ goto no_skb;
++ skb_reserve(skb, 2);
++ skb_put(skb, buflen); /* make room */
++
++ /* set up the 802.3 header */
++ e_hdr = (wlan_ethhdr_t *) skb->data;
++ MAC_COPY(e_hdr->daddr, daddr);
++ MAC_COPY(e_hdr->saddr, saddr);
++ e_hdr->type = htons(payload_length);
++
++ /* now copy the data from the 80211 frame */
++ memcpy(skb->data + ETH_HLEN, e_llc, payload_length);
++ }
++
++ skb->dev = adev->ndev;
++ skb->protocol = eth_type_trans(skb, adev->ndev);
++
++#ifdef DEBUG_CONVERT
++ if (acx_debug & L_DATA) {
++ int len = RXBUF_BYTES_RCVD(adev, rxbuf);
++ printk("p802.11 frame [%d]: ", len);
++ acx_dump_bytes(w_hdr, len);
++ printk("eth frame [%d]: ", skb->len);
++ acx_dump_bytes(skb->data, skb->len);
++ }
++#endif
++
++ FN_EXIT0;
++ return skb;
++
++no_skb:
++ printk("%s: rx: no memory for skb (%d bytes)\n",
++ adev->ndev->name, buflen + 2);
++ret_null:
++ FN_EXIT1((int)NULL);
++ return NULL;
++}
+Index: linux-2.6.23/drivers/net/wireless/acx/cs.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23/drivers/net/wireless/acx/cs.c 2008-01-20 21:13:40.000000000 +0000
+@@ -0,0 +1,5703 @@
++/***********************************************************************
++** Copyright (C) 2003 ACX100 Open Source Project
++**
++** The contents of this file are subject to the Mozilla Public
++** License Version 1.1 (the "License"); you may not use this file
++** except in compliance with the License. You may obtain a copy of
++** the License at http://www.mozilla.org/MPL/
++**
++** Software distributed under the License is distributed on an "AS
++** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
++** implied. See the License for the specific language governing
++** rights and limitations under the License.
++**
++** Alternatively, the contents of this file may be used under the
++** terms of the GNU Public License version 2 (the "GPL"), in which
++** case the provisions of the GPL are applicable instead of the
++** above. If you wish to allow the use of your version of this file
++** only under the terms of the GPL and not to allow others to use
++** your version of this file under the MPL, indicate your decision
++** by deleting the provisions above and replace them with the notice
++** and other provisions required by the GPL. If you do not delete
++** the provisions above, a recipient may use your version of this
++** file under either the MPL or the GPL.
++** ---------------------------------------------------------------------
++** Inquiries regarding the ACX100 Open Source Project can be
++** made directly to:
++**
++** acx100-users@lists.sf.net
++** http://acx100.sf.net
++** ---------------------------------------------------------------------
++**
++** Slave memory interface support:
++**
++** Todd Blumer - SDG Systems
++** Bill Reese - HP
++** Eric McCorkle - Shadowsun
++**
++** CF support, (c) Fabrice Crohas, Paul Sokolovsky
++*/
++#define ACX_MEM 1
++
++/*
++ * non-zero makes it dump the ACX memory to the console then
++ * panic when you cat /proc/driver/acx_wlan0_diag
++ */
++#define DUMP_MEM_DEFINED 1
++
++#define DUMP_MEM_DURING_DIAG 0
++#define DUMP_IF_SLOW 0
++
++#define PATCH_AROUND_BAD_SPOTS 1
++#define HX4700_FIRMWARE_CHECKSUM 0x0036862e
++#define HX4700_ALTERNATE_FIRMWARE_CHECKSUM 0x00368a75
++
++#include <linux/version.h>
++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18)
++#include <linux/config.h>
++#endif
++
++/* Linux 2.6.18+ uses <linux/utsrelease.h> */
++#ifndef UTS_RELEASE
++#include <linux/utsrelease.h>
++#endif
++
++#include <linux/compiler.h> /* required for Lx 2.6.8 ?? */
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/sched.h>
++#include <linux/types.h>
++#include <linux/skbuff.h>
++#include <linux/slab.h>
++#include <linux/if_arp.h>
++#include <linux/irq.h>
++#include <linux/rtnetlink.h>
++#include <linux/wireless.h>
++#include <net/iw_handler.h>
++#include <linux/netdevice.h>
++#include <linux/ioport.h>
++#include <linux/pci.h>
++#include <linux/platform_device.h>
++#include <linux/pm.h>
++#include <linux/vmalloc.h>
++#include <linux/delay.h>
++#include <linux/workqueue.h>
++#include <linux/inetdevice.h>
++
++#define PCMCIA_DEBUG 1
++
++/*
++ All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If
++ you do not define PCMCIA_DEBUG at all, all the debug code will be
++ left out. If you compile with PCMCIA_DEBUG=0, the debug code will
++ be present but disabled -- but it can then be enabled for specific
++ modules at load time with a 'pc_debug=#' option to insmod.
++
++*/
++#include <pcmcia/cs_types.h>
++#include <pcmcia/cs.h>
++#include <pcmcia/cistpl.h>
++#include <pcmcia/cisreg.h>
++#include <pcmcia/ds.h>
++#include "acx.h"
++#include "acx_hw.h"
++
++#ifdef PCMCIA_DEBUG
++static int pc_debug = PCMCIA_DEBUG;
++module_param(pc_debug, int, 0);
++static char *version = "$Revision: 1.10 $";
++#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args);
++#else
++#define DEBUG(n, args...)
++#endif
++
++
++static win_req_t memwin;
++
++typedef struct local_info_t {
++ dev_node_t node;
++ struct net_device *ndev;
++} local_info_t;
++
++static struct net_device *resume_ndev;
++
++
++/***********************************************************************
++*/
++
++#define CARD_EEPROM_ID_SIZE 6
++
++#include <asm/io.h>
++
++#define REG_ACX_VENDOR_ID 0x900
++/*
++ * This is the vendor id on the HX4700, anyway
++ */
++#define ACX_VENDOR_ID 0x8400104c
++
++typedef enum {
++ ACX_SOFT_RESET = 0,
++
++ ACX_SLV_REG_ADDR,
++ ACX_SLV_REG_DATA,
++ ACX_SLV_REG_ADATA,
++
++ ACX_SLV_MEM_CP,
++ ACX_SLV_MEM_ADDR,
++ ACX_SLV_MEM_DATA,
++ ACX_SLV_MEM_CTL,
++} acxreg_t;
++
++/***********************************************************************
++*/
++static void acxmem_i_tx_timeout(struct net_device *ndev);
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
++static irqreturn_t acxmem_i_interrupt(int irq, void *dev_id);
++#else
++static irqreturn_t acxmem_i_interrupt(int irq, void *dev_id, struct pt_regs *regs);
++#endif
++static void acxmem_i_set_multicast_list(struct net_device *ndev);
++
++static int acxmem_e_open(struct net_device *ndev);
++static int acxmem_e_close(struct net_device *ndev);
++static void acxmem_s_up(struct net_device *ndev);
++static void acxmem_s_down(struct net_device *ndev);
++
++static void dump_acxmem (acx_device_t *adev, u32 start, int length);
++static int acxmem_complete_hw_reset (acx_device_t *adev);
++static void acxmem_s_delete_dma_regions(acx_device_t *adev);
++
++static int
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11)
++acxmem_e_suspend( struct net_device *ndev, pm_message_t state);
++#else
++acxmem_e_suspend( struct net_device *ndev, u32 state);
++#endif
++static void
++fw_resumer(struct work_struct *notused);
++//fw_resumer( void *data );
++
++static int acx_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
++{
++ struct net_device *ndev = ptr;
++ acx_device_t *adev = ndev2adev(ndev);
++
++ /*
++ * Upper level ioctl() handlers send a NETDEV_CHANGEADDR if the MAC address changes.
++ */
++
++ if (NETDEV_CHANGEADDR == event) {
++ /*
++ * the upper layers put the new MAC address in ndev->dev_addr; we just copy
++ * it over and update the ACX with it.
++ */
++ MAC_COPY(adev->dev_addr, adev->ndev->dev_addr);
++ adev->set_mask |= GETSET_STATION_ID;
++ acx_s_update_card_settings (adev);
++ }
++
++ return 0;
++}
++
++static struct notifier_block acx_netdev_notifier = {
++ .notifier_call = acx_netdev_event,
++};
++
++/***********************************************************************
++** Register access
++*/
++
++/* Pick one */
++/* #define INLINE_IO static */
++#define INLINE_IO static inline
++
++INLINE_IO u32
++read_id_register (acx_device_t *adev)
++{
++ writel (0x24, &adev->iobase[ACX_SLV_REG_ADDR]);
++ return readl (&adev->iobase[ACX_SLV_REG_DATA]);
++}
++
++INLINE_IO u32
++read_reg32(acx_device_t *adev, unsigned int offset)
++{
++ u32 val;
++ u32 addr;
++
++ if (offset > IO_ACX_ECPU_CTRL)
++ addr = offset;
++ else
++ addr = adev->io[offset];
++
++ if (addr < 0x20) {
++ return readl(((u8*)adev->iobase) + addr);
++ }
++
++ writel( addr, &adev->iobase[ACX_SLV_REG_ADDR] );
++ val = readl( &adev->iobase[ACX_SLV_REG_DATA] );
++
++ return val;
++}
++
++INLINE_IO u16
++read_reg16(acx_device_t *adev, unsigned int offset)
++{
++ u16 lo;
++ u32 addr;
++
++ if (offset > IO_ACX_ECPU_CTRL)
++ addr = offset;
++ else
++ addr = adev->io[offset];
++
++ if (addr < 0x20) {
++ return readw(((u8 *) adev->iobase) + addr);
++ }
++
++ writel( addr, &adev->iobase[ACX_SLV_REG_ADDR] );
++ lo = readw( (u16 *)&adev->iobase[ACX_SLV_REG_DATA] );
++
++ return lo;
++}
++
++INLINE_IO u8
++read_reg8(acx_device_t *adev, unsigned int offset)
++{
++ u8 lo;
++ u32 addr;
++
++ if (offset > IO_ACX_ECPU_CTRL)
++ addr = offset;
++ else
++ addr = adev->io[offset];
++
++ if (addr < 0x20)
++ return readb(((u8 *)adev->iobase) + addr);
++
++ writel( addr, &adev->iobase[ACX_SLV_REG_ADDR] );
++ lo = readw( (u8 *)&adev->iobase[ACX_SLV_REG_DATA] );
++
++ return (u8)lo;
++}
++
++INLINE_IO void
++write_reg32(acx_device_t *adev, unsigned int offset, u32 val)
++{
++ u32 addr;
++
++ if (offset > IO_ACX_ECPU_CTRL)
++ addr = offset;
++ else
++ addr = adev->io[offset];
++
++ if (addr < 0x20) {
++ writel(val, ((u8*)adev->iobase) + addr);
++ return;
++ }
++
++ writel( addr, &adev->iobase[ACX_SLV_REG_ADDR] );
++ writel( val, &adev->iobase[ACX_SLV_REG_DATA] );
++}
++
++INLINE_IO void
++write_reg16(acx_device_t *adev, unsigned int offset, u16 val)
++{
++ u32 addr;
++
++ if (offset > IO_ACX_ECPU_CTRL)
++ addr = offset;
++ else
++ addr = adev->io[offset];
++
++ if (addr < 0x20) {
++ writew(val, ((u8 *)adev->iobase) + addr);
++ return;
++ }
++ writel( addr, &adev->iobase[ACX_SLV_REG_ADDR] );
++ writew( val, (u16 *) &adev->iobase[ACX_SLV_REG_DATA] );
++}
++
++INLINE_IO void
++write_reg8(acx_device_t *adev, unsigned int offset, u8 val)
++{
++ u32 addr;
++
++ if (offset > IO_ACX_ECPU_CTRL)
++ addr = offset;
++ else
++ addr = adev->io[offset];
++
++ if (addr < 0x20) {
++ writeb(val, ((u8 *) adev->iobase) + addr);
++ return;
++ }
++ writel( addr, &adev->iobase[ACX_SLV_REG_ADDR] );
++ writeb( val, (u8 *)&adev->iobase[ACX_SLV_REG_DATA] );
++}
++
++/* Handle PCI posting properly:
++ * Make sure that writes reach the adapter in case they require to be executed
++ * *before* the next write, by reading a random (and safely accessible) register.
++ * This call has to be made if there is no read following (which would flush the data
++ * to the adapter), yet the written data has to reach the adapter immediately. */
++INLINE_IO void
++write_flush(acx_device_t *adev)
++{
++ /* readb(adev->iobase + adev->io[IO_ACX_INFO_MAILBOX_OFFS]); */
++ /* faster version (accesses the first register, IO_ACX_SOFT_RESET,
++ * which should also be safe): */
++ (void) readl(adev->iobase);
++}
++
++INLINE_IO void
++set_regbits (acx_device_t *adev, unsigned int offset, u32 bits) {
++ u32 tmp;
++
++ tmp = read_reg32 (adev, offset);
++ tmp = tmp | bits;
++ write_reg32 (adev, offset, tmp);
++ write_flush (adev);
++}
++
++INLINE_IO void
++clear_regbits (acx_device_t *adev, unsigned int offset, u32 bits) {
++ u32 tmp;
++
++ tmp = read_reg32 (adev, offset);
++ tmp = tmp & ~bits;
++ write_reg32 (adev, offset, tmp);
++ write_flush (adev);
++}
++
++/*
++ * Copy from PXA memory to the ACX memory. This assumes both the PXA and ACX
++ * addresses are 32 bit aligned. Count is in bytes.
++ */
++INLINE_IO void
++write_slavemem32 (acx_device_t *adev, u32 slave_address, u32 val)
++{
++ write_reg32 (adev, IO_ACX_SLV_MEM_CTL, 0x0);
++ write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, slave_address);
++ udelay (10);
++ write_reg32 (adev, IO_ACX_SLV_MEM_DATA, val);
++}
++
++INLINE_IO u32
++read_slavemem32 (acx_device_t *adev, u32 slave_address)
++{
++ u32 val;
++
++ write_reg32 (adev, IO_ACX_SLV_MEM_CTL, 0x0);
++ write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, slave_address);
++ udelay (10);
++ val = read_reg32 (adev, IO_ACX_SLV_MEM_DATA);
++
++ return val;
++}
++
++INLINE_IO void
++write_slavemem8 (acx_device_t *adev, u32 slave_address, u8 val)
++{
++ u32 data;
++ u32 base;
++ int offset;
++
++ /*
++ * Get the word containing the target address and the byte offset in that word.
++ */
++ base = slave_address & ~3;
++ offset = (slave_address & 3) * 8;
++
++ data = read_slavemem32 (adev, base);
++ data &= ~(0xff << offset);
++ data |= val << offset;
++ write_slavemem32 (adev, base, data);
++}
++
++INLINE_IO u8
++read_slavemem8 (acx_device_t *adev, u32 slave_address)
++{
++ u8 val;
++ u32 base;
++ u32 data;
++ int offset;
++
++ base = slave_address & ~3;
++ offset = (slave_address & 3) * 8;
++
++ data = read_slavemem32 (adev, base);
++
++ val = (data >> offset) & 0xff;
++
++ return val;
++}
++
++/*
++ * doesn't split across word boundaries
++ */
++INLINE_IO void
++write_slavemem16 (acx_device_t *adev, u32 slave_address, u16 val)
++{
++ u32 data;
++ u32 base;
++ int offset;
++
++ /*
++ * Get the word containing the target address and the byte offset in that word.
++ */
++ base = slave_address & ~3;
++ offset = (slave_address & 3) * 8;
++
++ data = read_slavemem32 (adev, base);
++ data &= ~(0xffff << offset);
++ data |= val << offset;
++ write_slavemem32 (adev, base, data);
++}
++
++/*
++ * doesn't split across word boundaries
++ */
++INLINE_IO u16
++read_slavemem16 (acx_device_t *adev, u32 slave_address)
++{
++ u16 val;
++ u32 base;
++ u32 data;
++ int offset;
++
++ base = slave_address & ~3;
++ offset = (slave_address & 3) * 8;
++
++ data = read_slavemem32 (adev, base);
++
++ val = (data >> offset) & 0xffff;
++
++ return val;
++}
++
++/*
++ * Copy from slave memory
++ *
++ * TODO - rewrite using address autoincrement, handle partial words
++ */
++void
++copy_from_slavemem (acx_device_t *adev, u8 *destination, u32 source, int count) {
++ u32 tmp = 0;
++ u8 *ptmp = (u8 *) &tmp;
++
++ /*
++ * Right now I'm making the assumption that the destination is aligned, but
++ * I'd better check.
++ */
++ if ((u32) destination & 3) {
++ printk ("acx copy_from_slavemem: warning! destination not word-aligned!\n");
++ }
++
++ while (count >= 4) {
++ write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, source);
++ udelay (10);
++ *((u32 *) destination) = read_reg32 (adev, IO_ACX_SLV_MEM_DATA);
++ count -= 4;
++ source += 4;
++ destination += 4;
++ }
++
++ /*
++ * If the word reads above didn't satisfy the count, read one more word
++ * and transfer a byte at a time until the request is satisfied.
++ */
++ if (count) {
++ write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, source);
++ udelay (10);
++ tmp = read_reg32 (adev, IO_ACX_SLV_MEM_DATA);
++ while (count--) {
++ *destination++ = *ptmp++;
++ }
++ }
++}
++
++/*
++ * Copy to slave memory
++ *
++ * TODO - rewrite using autoincrement, handle partial words
++ */
++void
++copy_to_slavemem (acx_device_t *adev, u32 destination, u8 *source, int count)
++{
++ u32 tmp = 0;
++ u8* ptmp = (u8 *) &tmp;
++ static u8 src[512]; /* make static to avoid huge stack objects */
++
++ /*
++ * For now, make sure the source is word-aligned by copying it to a word-aligned
++ * buffer. Someday rewrite to avoid the extra copy.
++ */
++ if (count > sizeof (src)) {
++ printk ("acx copy_to_slavemem: Warning! buffer overflow!\n");
++ count = sizeof (src);
++ }
++ memcpy (src, source, count);
++ source = src;
++
++ while (count >= 4) {
++ write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, destination);
++ udelay (10);
++ write_reg32 (adev, IO_ACX_SLV_MEM_DATA, *((u32 *) source));
++ count -= 4;
++ source += 4;
++ destination += 4;
++ }
++
++ /*
++ * If there are leftovers read the next word from the acx and merge in
++ * what they want to write.
++ */
++ if (count) {
++ write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, destination);
++ udelay (10);
++ tmp = read_reg32 (adev, IO_ACX_SLV_MEM_DATA);
++ while (count--) {
++ *ptmp++ = *source++;
++ }
++ /*
++ * reset address in case we're currently in auto-increment mode
++ */
++ write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, destination);
++ udelay (10);
++ write_reg32 (adev, IO_ACX_SLV_MEM_DATA, tmp);
++ udelay (10);
++ }
++
++}
++
++/*
++ * Block copy to slave buffers using memory block chain mode. Copies to the ACX
++ * transmit buffer structure with minimal intervention on our part.
++ * Interrupts should be disabled when calling this.
++ */
++void
++chaincopy_to_slavemem (acx_device_t *adev, u32 destination, u8 *source, int count)
++{
++ u32 val;
++ u32 *data = (u32 *) source;
++ static u8 aligned_source[WLAN_A4FR_MAXLEN_WEP_FCS];
++
++ /*
++ * Warn if the pointers don't look right. Destination must fit in [23:5] with
++ * zero elsewhere and source should be 32 bit aligned.
++ * This should never happen since we're in control of both, but I want to know about
++ * it if it does.
++ */
++ if ((destination & 0x00ffffe0) != destination) {
++ printk ("acx chaincopy: destination block 0x%04x not aligned!\n", destination);
++ }
++ if (count > sizeof aligned_source) {
++ printk( KERN_ERR "chaincopy_to_slavemem overflow!\n" );
++ count = sizeof aligned_source;
++ }
++ if ((u32) source & 3) {
++ memcpy (aligned_source, source, count);
++ data = (u32 *) aligned_source;
++ }
++
++ /*
++ * SLV_MEM_CTL[17:16] = memory block chain mode with auto-increment
++ * SLV_MEM_CTL[5:2] = offset to data portion = 1 word
++ */
++ val = 2 << 16 | 1 << 2;
++ writel (val, &adev->iobase[ACX_SLV_MEM_CTL]);
++
++ /*
++ * SLV_MEM_CP[23:5] = start of 1st block
++ * SLV_MEM_CP[3:2] = offset to memblkptr = 0
++ */
++ val = destination & 0x00ffffe0;
++ writel (val, &adev->iobase[ACX_SLV_MEM_CP]);
++
++ /*
++ * SLV_MEM_ADDR[23:2] = SLV_MEM_CTL[5:2] + SLV_MEM_CP[23:5]
++ */
++ val = (destination & 0x00ffffe0) + (1<<2);
++ writel (val, &adev->iobase[ACX_SLV_MEM_ADDR]);
++
++ /*
++ * Write the data to the slave data register, rounding up to the end
++ * of the word containing the last byte (hence the > 0)
++ */
++ while (count > 0) {
++ writel (*data++, &adev->iobase[ACX_SLV_MEM_DATA]);
++ count -= 4;
++ }
++}
++
++
++/*
++ * Block copy from slave buffers using memory block chain mode. Copies from the ACX
++ * receive buffer structures with minimal intervention on our part.
++ * Interrupts should be disabled when calling this.
++ */
++void
++chaincopy_from_slavemem (acx_device_t *adev, u8 *destination, u32 source, int count)
++{
++ u32 val;
++ u32 *data = (u32 *) destination;
++ static u8 aligned_destination[WLAN_A4FR_MAXLEN_WEP_FCS];
++ int saved_count = count;
++
++ /*
++ * Warn if the pointers don't look right. Destination must fit in [23:5] with
++ * zero elsewhere and source should be 32 bit aligned.
++ * Turns out the network stack sends unaligned things, so fix them before
++ * copying to the ACX.
++ */
++ if ((source & 0x00ffffe0) != source) {
++ printk ("acx chaincopy: source block 0x%04x not aligned!\n", source);
++ dump_acxmem (adev, 0, 0x10000);
++ }
++ if ((u32) destination & 3) {
++ //printk ("acx chaincopy: data destination not word aligned!\n");
++ data = (u32 *) aligned_destination;
++ if (count > sizeof aligned_destination) {
++ printk( KERN_ERR "chaincopy_from_slavemem overflow!\n" );
++ count = sizeof aligned_destination;
++ }
++ }
++
++ /*
++ * SLV_MEM_CTL[17:16] = memory block chain mode with auto-increment
++ * SLV_MEM_CTL[5:2] = offset to data portion = 1 word
++ */
++ val = (2 << 16) | (1 << 2);
++ writel (val, &adev->iobase[ACX_SLV_MEM_CTL]);
++
++ /*
++ * SLV_MEM_CP[23:5] = start of 1st block
++ * SLV_MEM_CP[3:2] = offset to memblkptr = 0
++ */
++ val = source & 0x00ffffe0;
++ writel (val, &adev->iobase[ACX_SLV_MEM_CP]);
++
++ /*
++ * SLV_MEM_ADDR[23:2] = SLV_MEM_CTL[5:2] + SLV_MEM_CP[23:5]
++ */
++ val = (source & 0x00ffffe0) + (1<<2);
++ writel (val, &adev->iobase[ACX_SLV_MEM_ADDR]);
++
++ /*
++ * Read the data from the slave data register, rounding up to the end
++ * of the word containing the last byte (hence the > 0)
++ */
++ while (count > 0) {
++ *data++ = readl (&adev->iobase[ACX_SLV_MEM_DATA]);
++ count -= 4;
++ }
++
++ /*
++ * If the destination wasn't aligned, we would have saved it in
++ * the aligned buffer, so copy it where it should go.
++ */
++ if ((u32) destination & 3) {
++ memcpy (destination, aligned_destination, saved_count);
++ }
++}
++
++char
++printable (char c)
++{
++ return ((c >= 20) && (c < 127)) ? c : '.';
++}
++
++#if DUMP_MEM_DEFINED > 0
++static void
++dump_acxmem (acx_device_t *adev, u32 start, int length)
++{
++ int i;
++ u8 buf[16];
++
++ while (length > 0) {
++ printk ("%04x ", start);
++ copy_from_slavemem (adev, buf, start, 16);
++ for (i = 0; (i < 16) && (i < length); i++) {
++ printk ("%02x ", buf[i]);
++ }
++ for (i = 0; (i < 16) && (i < length); i++) {
++ printk ("%c", printable (buf[i]));
++ }
++ printk ("\n");
++ start += 16;
++ length -= 16;
++ }
++}
++#endif
++
++static void
++enable_acx_irq(acx_device_t *adev);
++static void
++disable_acx_irq(acx_device_t *adev);
++
++/*
++ * Return an acx pointer to the next transmit data block.
++ */
++u32
++allocate_acx_txbuf_space (acx_device_t *adev, int count) {
++ u32 block, next, last_block;
++ int blocks_needed;
++ unsigned long flags;
++
++ spin_lock_irqsave(&adev->txbuf_lock, flags);
++ /*
++ * Take 4 off the memory block size to account for the reserved word at the start of
++ * the block.
++ */
++ blocks_needed = count / (adev->memblocksize - 4);
++ if (count % (adev->memblocksize - 4))
++ blocks_needed++;
++
++ if (blocks_needed <= adev->acx_txbuf_blocks_free) {
++ /*
++ * Take blocks at the head of the free list.
++ */
++ last_block = block = adev->acx_txbuf_free;
++
++ /*
++ * Follow block pointers through the requested number of blocks both to
++ * find the new head of the free list and to set the flags for the blocks
++ * appropriately.
++ */
++ while (blocks_needed--) {
++ /*
++ * Keep track of the last block of the allocation
++ */
++ last_block = adev->acx_txbuf_free;
++
++ /*
++ * Make sure the end control flag is not set.
++ */
++ next = read_slavemem32 (adev, adev->acx_txbuf_free) & 0x7ffff;
++ write_slavemem32 (adev, adev->acx_txbuf_free, next);
++
++ /*
++ * Update the new head of the free list
++ */
++ adev->acx_txbuf_free = next << 5;
++ adev->acx_txbuf_blocks_free--;
++
++ }
++
++ /*
++ * Flag the last block both by clearing out the next pointer
++ * and marking the control field.
++ */
++ write_slavemem32 (adev, last_block, 0x02000000);
++
++ /*
++ * If we're out of buffers make sure the free list pointer is NULL
++ */
++ if (!adev->acx_txbuf_blocks_free) {
++ adev->acx_txbuf_free = 0;
++ }
++ }
++ else {
++ block = 0;
++ }
++ spin_unlock_irqrestore (&adev->txbuf_lock, flags);
++ return block;
++}
++
++/*
++ * Return buffer space back to the pool by following the next pointers until we find
++ * the block marked as the end. Point the last block to the head of the free list,
++ * then update the head of the free list to point to the newly freed memory.
++ * This routine gets called in interrupt context, so it shouldn't block to protect
++ * the integrity of the linked list. The ISR already holds the lock.
++ */
++void
++reclaim_acx_txbuf_space (acx_device_t *adev, u32 blockptr) {
++ u32 cur, last, next;
++ unsigned long flags;
++
++ spin_lock_irqsave (&adev->txbuf_lock, flags);
++ if ((blockptr >= adev->acx_txbuf_start) &&
++ (blockptr <= adev->acx_txbuf_start +
++ (adev->acx_txbuf_numblocks - 1) * adev->memblocksize)) {
++ cur = blockptr;
++ do {
++ last = cur;
++ next = read_slavemem32 (adev, cur);
++
++ /*
++ * Advance to the next block in this allocation
++ */
++ cur = (next & 0x7ffff) << 5;
++
++ /*
++ * This block now counts as free.
++ */
++ adev->acx_txbuf_blocks_free++;
++ } while (!(next & 0x02000000));
++
++ /*
++ * last now points to the last block of that allocation. Update the pointer
++ * in that block to point to the free list and reset the free list to the
++ * first block of the free call. If there were no free blocks, make sure
++ * the new end of the list marks itself as truly the end.
++ */
++ if (adev->acx_txbuf_free) {
++ write_slavemem32 (adev, last, adev->acx_txbuf_free >> 5);
++ }
++ else {
++ write_slavemem32 (adev, last, 0x02000000);
++ }
++ adev->acx_txbuf_free = blockptr;
++ }
++ spin_unlock_irqrestore(&adev->txbuf_lock, flags);
++}
++
++/*
++ * Initialize the pieces managing the transmit buffer pool on the ACX. The transmit
++ * buffer is a circular queue with one 32 bit word reserved at the beginning of each
++ * block. The upper 13 bits are a control field, of which only 0x02000000 has any
++ * meaning. The lower 19 bits are the address of the next block divided by 32.
++ */
++void
++init_acx_txbuf (acx_device_t *adev) {
++
++ /*
++ * acx100_s_init_memory_pools set up txbuf_start and txbuf_numblocks for us.
++ * All we need to do is reset the rest of the bookeeping.
++ */
++
++ adev->acx_txbuf_free = adev->acx_txbuf_start;
++ adev->acx_txbuf_blocks_free = adev->acx_txbuf_numblocks;
++
++ /*
++ * Initialization leaves the last transmit pool block without a pointer back to
++ * the head of the list, but marked as the end of the list. That's how we want
++ * to see it, too, so leave it alone. This is only ever called after a firmware
++ * reset, so the ACX memory is in the state we want.
++ */
++
++}
++
++INLINE_IO int
++adev_present(acx_device_t *adev)
++{
++ /* fast version (accesses the first register, IO_ACX_SOFT_RESET,
++ * which should be safe): */
++ return readl(adev->iobase) != 0xffffffff;
++}
++
++/***********************************************************************
++*/
++static inline txdesc_t*
++get_txdesc(acx_device_t *adev, int index)
++{
++ return (txdesc_t*) (((u8*)adev->txdesc_start) + index * adev->txdesc_size);
++}
++
++static inline txdesc_t*
++advance_txdesc(acx_device_t *adev, txdesc_t* txdesc, int inc)
++{
++ return (txdesc_t*) (((u8*)txdesc) + inc * adev->txdesc_size);
++}
++
++static txhostdesc_t*
++get_txhostdesc(acx_device_t *adev, txdesc_t* txdesc)
++{
++ int index = (u8*)txdesc - (u8*)adev->txdesc_start;
++ if (unlikely(ACX_DEBUG && (index % adev->txdesc_size))) {
++ printk("bad txdesc ptr %p\n", txdesc);
++ return NULL;
++ }
++ index /= adev->txdesc_size;
++ if (unlikely(ACX_DEBUG && (index >= TX_CNT))) {
++ printk("bad txdesc ptr %p\n", txdesc);
++ return NULL;
++ }
++ return &adev->txhostdesc_start[index*2];
++}
++
++static inline client_t*
++get_txc(acx_device_t *adev, txdesc_t* txdesc)
++{
++ int index = (u8*)txdesc - (u8*)adev->txdesc_start;
++ if (unlikely(ACX_DEBUG && (index % adev->txdesc_size))) {
++ printk("bad txdesc ptr %p\n", txdesc);
++ return NULL;
++ }
++ index /= adev->txdesc_size;
++ if (unlikely(ACX_DEBUG && (index >= TX_CNT))) {
++ printk("bad txdesc ptr %p\n", txdesc);
++ return NULL;
++ }
++ return adev->txc[index];
++}
++
++static inline u16
++get_txr(acx_device_t *adev, txdesc_t* txdesc)
++{
++ int index = (u8*)txdesc - (u8*)adev->txdesc_start;
++ index /= adev->txdesc_size;
++ return adev->txr[index];
++}
++
++static inline void
++put_txcr(acx_device_t *adev, txdesc_t* txdesc, client_t* c, u16 r111)
++{
++ int index = (u8*)txdesc - (u8*)adev->txdesc_start;
++ if (unlikely(ACX_DEBUG && (index % adev->txdesc_size))) {
++ printk("bad txdesc ptr %p\n", txdesc);
++ return;
++ }
++ index /= adev->txdesc_size;
++ if (unlikely(ACX_DEBUG && (index >= TX_CNT))) {
++ printk("bad txdesc ptr %p\n", txdesc);
++ return;
++ }
++ adev->txc[index] = c;
++ adev->txr[index] = r111;
++}
++
++
++/***********************************************************************
++** EEPROM and PHY read/write helpers
++*/
++/***********************************************************************
++** acxmem_read_eeprom_byte
++**
++** Function called to read an octet in the EEPROM.
++**
++** This function is used by acxmem_e_probe to check if the
++** connected card is a legal one or not.
++**
++** Arguments:
++** adev ptr to acx_device structure
++** addr address to read in the EEPROM
++** charbuf ptr to a char. This is where the read octet
++** will be stored
++*/
++int
++acxmem_read_eeprom_byte(acx_device_t *adev, u32 addr, u8 *charbuf)
++{
++ int result;
++ int count;
++
++ write_reg32(adev, IO_ACX_EEPROM_CFG, 0);
++ write_reg32(adev, IO_ACX_EEPROM_ADDR, addr);
++ write_flush(adev);
++ write_reg32(adev, IO_ACX_EEPROM_CTL, 2);
++
++ count = 0xffff;
++ while (read_reg16(adev, IO_ACX_EEPROM_CTL)) {
++ /* scheduling away instead of CPU burning loop
++ * doesn't seem to work here at all:
++ * awful delay, sometimes also failure.
++ * Doesn't matter anyway (only small delay). */
++ if (unlikely(!--count)) {
++ printk("%s: timeout waiting for EEPROM read\n",
++ adev->ndev->name);
++ result = NOT_OK;
++ goto fail;
++ }
++ cpu_relax();
++ }
++
++ *charbuf = read_reg8(adev, IO_ACX_EEPROM_DATA);
++ log(L_DEBUG, "EEPROM at 0x%04X = 0x%02X\n", addr, *charbuf);
++ result = OK;
++
++fail:
++ return result;
++}
++
++
++/***********************************************************************
++** We don't lock hw accesses here since we never r/w eeprom in IRQ
++** Note: this function sleeps only because of GFP_KERNEL alloc
++*/
++#ifdef UNUSED
++int
++acxmem_s_write_eeprom(acx_device_t *adev, u32 addr, u32 len, const u8 *charbuf)
++{
++ u8 *data_verify = NULL;
++ unsigned long flags;
++ int count, i;
++ int result = NOT_OK;
++ u16 gpio_orig;
++
++ printk("acx: WARNING! I would write to EEPROM now. "
++ "Since I really DON'T want to unless you know "
++ "what you're doing (THIS CODE WILL PROBABLY "
++ "NOT WORK YET!), I will abort that now. And "
++ "definitely make sure to make a "
++ "/proc/driver/acx_wlan0_eeprom backup copy first!!! "
++ "(the EEPROM content includes the PCI config header!! "
++ "If you kill important stuff, then you WILL "
++ "get in trouble and people DID get in trouble already)\n");
++ return OK;
++
++ FN_ENTER;
++
++ data_verify = kmalloc(len, GFP_KERNEL);
++ if (!data_verify) {
++ goto end;
++ }
++
++ /* first we need to enable the OE (EEPROM Output Enable) GPIO line
++ * to be able to write to the EEPROM.
++ * NOTE: an EEPROM writing success has been reported,
++ * but you probably have to modify GPIO_OUT, too,
++ * and you probably need to activate a different GPIO
++ * line instead! */
++ gpio_orig = read_reg16(adev, IO_ACX_GPIO_OE);
++ write_reg16(adev, IO_ACX_GPIO_OE, gpio_orig & ~1);
++ write_flush(adev);
++
++ /* ok, now start writing the data out */
++ for (i = 0; i < len; i++) {
++ write_reg32(adev, IO_ACX_EEPROM_CFG, 0);
++ write_reg32(adev, IO_ACX_EEPROM_ADDR, addr + i);
++ write_reg32(adev, IO_ACX_EEPROM_DATA, *(charbuf + i));
++ write_flush(adev);
++ write_reg32(adev, IO_ACX_EEPROM_CTL, 1);
++
++ count = 0xffff;
++ while (read_reg16(adev, IO_ACX_EEPROM_CTL)) {
++ if (unlikely(!--count)) {
++ printk("WARNING, DANGER!!! "
++ "Timeout waiting for EEPROM write\n");
++ goto end;
++ }
++ cpu_relax();
++ }
++ }
++
++ /* disable EEPROM writing */
++ write_reg16(adev, IO_ACX_GPIO_OE, gpio_orig);
++ write_flush(adev);
++
++ /* now start a verification run */
++ for (i = 0; i < len; i++) {
++ write_reg32(adev, IO_ACX_EEPROM_CFG, 0);
++ write_reg32(adev, IO_ACX_EEPROM_ADDR, addr + i);
++ write_flush(adev);
++ write_reg32(adev, IO_ACX_EEPROM_CTL, 2);
++
++ count = 0xffff;
++ while (read_reg16(adev, IO_ACX_EEPROM_CTL)) {
++ if (unlikely(!--count)) {
++ printk("timeout waiting for EEPROM read\n");
++ goto end;
++ }
++ cpu_relax();
++ }
++
++ data_verify[i] = read_reg16(adev, IO_ACX_EEPROM_DATA);
++ }
++
++ if (0 == memcmp(charbuf, data_verify, len))
++ result = OK; /* read data matches, success */
++
++end:
++ kfree(data_verify);
++ FN_EXIT1(result);
++ return result;
++}
++#endif /* UNUSED */
++
++
++/***********************************************************************
++** acxmem_s_read_phy_reg
++**
++** Messing with rx/tx disabling and enabling here
++** (write_reg32(adev, IO_ACX_ENABLE, 0b000000xx)) kills traffic
++*/
++int
++acxmem_s_read_phy_reg(acx_device_t *adev, u32 reg, u8 *charbuf)
++{
++ int result = NOT_OK;
++ int count;
++
++ FN_ENTER;
++
++ write_reg32(adev, IO_ACX_PHY_ADDR, reg);
++ write_flush(adev);
++ write_reg32(adev, IO_ACX_PHY_CTL, 2);
++
++ count = 0xffff;
++ while (read_reg32(adev, IO_ACX_PHY_CTL)) {
++ /* scheduling away instead of CPU burning loop
++ * doesn't seem to work here at all:
++ * awful delay, sometimes also failure.
++ * Doesn't matter anyway (only small delay). */
++ if (unlikely(!--count)) {
++ printk("%s: timeout waiting for phy read\n",
++ adev->ndev->name);
++ *charbuf = 0;
++ goto fail;
++ }
++ cpu_relax();
++ }
++
++ log(L_DEBUG, "count was %u\n", count);
++ *charbuf = read_reg8(adev, IO_ACX_PHY_DATA);
++
++ log(L_DEBUG, "radio PHY at 0x%04X = 0x%02X\n", *charbuf, reg);
++ result = OK;
++ goto fail; /* silence compiler warning */
++fail:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++*/
++int
++acxmem_s_write_phy_reg(acx_device_t *adev, u32 reg, u8 value)
++{
++ int count;
++ FN_ENTER;
++
++ /* mprusko said that 32bit accesses result in distorted sensitivity
++ * on his card. Unconfirmed, looks like it's not true (most likely since we
++ * now properly flush writes). */
++ write_reg32(adev, IO_ACX_PHY_DATA, value);
++ write_reg32(adev, IO_ACX_PHY_ADDR, reg);
++ write_flush(adev);
++ write_reg32(adev, IO_ACX_PHY_CTL, 1);
++ write_flush(adev);
++
++ count = 0xffff;
++ while (read_reg32(adev, IO_ACX_PHY_CTL)) {
++ /* scheduling away instead of CPU burning loop
++ * doesn't seem to work here at all:
++ * awful delay, sometimes also failure.
++ * Doesn't matter anyway (only small delay). */
++ if (unlikely(!--count)) {
++ printk("%s: timeout waiting for phy read\n",
++ adev->ndev->name);
++ goto fail;
++ }
++ cpu_relax();
++ }
++
++ log(L_DEBUG, "radio PHY write 0x%02X at 0x%04X\n", value, reg);
++ fail:
++ FN_EXIT1(OK);
++ return OK;
++}
++
++
++#define NO_AUTO_INCREMENT 1
++
++/***********************************************************************
++** acxmem_s_write_fw
++**
++** Write the firmware image into the card.
++**
++** Arguments:
++** adev wlan device structure
++** fw_image firmware image.
++**
++** Returns:
++** 1 firmware image corrupted
++** 0 success
++*/
++static int
++acxmem_s_write_fw(acx_device_t *adev, const firmware_image_t *fw_image, u32 offset)
++{
++ int len, size, checkMismatch = -1;
++ u32 sum, v32, tmp, id;
++ /* we skip the first four bytes which contain the control sum */
++ const u8 *p = (u8*)fw_image + 4;
++
++ /* start the image checksum by adding the image size value */
++ sum = p[0]+p[1]+p[2]+p[3];
++ p += 4;
++
++#ifdef NOPE
++#if NO_AUTO_INCREMENT
++ write_reg32(adev, IO_ACX_SLV_MEM_CTL, 0); /* use basic mode */
++#else
++ write_reg32(adev, IO_ACX_SLV_MEM_CTL, 1); /* use autoincrement mode */
++ write_reg32(adev, IO_ACX_SLV_MEM_ADDR, offset); /* configure start address */
++ write_flush(adev);
++#endif
++#endif
++ len = 0;
++ size = le32_to_cpu(fw_image->size) & (~3);
++
++ while (likely(len < size)) {
++ v32 = be32_to_cpu(*(u32*)p);
++ sum += p[0]+p[1]+p[2]+p[3];
++ p += 4;
++ len += 4;
++
++#ifdef NOPE
++#if NO_AUTO_INCREMENT
++ write_reg32(adev, IO_ACX_SLV_MEM_ADDR, offset + len - 4);
++ write_flush(adev);
++#endif
++ write_reg32(adev, IO_ACX_SLV_MEM_DATA, v32);
++ write_flush(adev);
++#endif
++ write_slavemem32 (adev, offset + len - 4, v32);
++
++ id = read_id_register (adev);
++
++ /*
++ * check the data written
++ */
++ tmp = read_slavemem32 (adev, offset + len - 4);
++ if (checkMismatch && (tmp != v32)) {
++ printk ("first data mismatch at 0x%08x good 0x%08x bad 0x%08x id 0x%08x\n",
++ offset + len - 4, v32, tmp, id);
++ checkMismatch = 0;
++ }
++ }
++ log(L_DEBUG, "firmware written, size:%d sum1:%x sum2:%x\n",
++ size, sum, le32_to_cpu(fw_image->chksum));
++
++ /* compare our checksum with the stored image checksum */
++ return (sum != le32_to_cpu(fw_image->chksum));
++}
++
++
++/***********************************************************************
++** acxmem_s_validate_fw
++**
++** Compare the firmware image given with
++** the firmware image written into the card.
++**
++** Arguments:
++** adev wlan device structure
++** fw_image firmware image.
++**
++** Returns:
++** NOT_OK firmware image corrupted or not correctly written
++** OK success
++*/
++static int
++acxmem_s_validate_fw(acx_device_t *adev, const firmware_image_t *fw_image,
++ u32 offset)
++{
++ u32 sum, v32, w32;
++ int len, size;
++ int result = OK;
++ /* we skip the first four bytes which contain the control sum */
++ const u8 *p = (u8*)fw_image + 4;
++
++ /* start the image checksum by adding the image size value */
++ sum = p[0]+p[1]+p[2]+p[3];
++ p += 4;
++
++ write_reg32(adev, IO_ACX_SLV_END_CTL, 0);
++
++#if NO_AUTO_INCREMENT
++ write_reg32(adev, IO_ACX_SLV_MEM_CTL, 0); /* use basic mode */
++#else
++ write_reg32(adev, IO_ACX_SLV_MEM_CTL, 1); /* use autoincrement mode */
++ write_reg32(adev, IO_ACX_SLV_MEM_ADDR, offset); /* configure start address */
++#endif
++
++ len = 0;
++ size = le32_to_cpu(fw_image->size) & (~3);
++
++ while (likely(len < size)) {
++ v32 = be32_to_cpu(*(u32*)p);
++ p += 4;
++ len += 4;
++
++#ifdef NOPE
++#if NO_AUTO_INCREMENT
++ write_reg32(adev, IO_ACX_SLV_MEM_ADDR, offset + len - 4);
++#endif
++ udelay(10);
++ w32 = read_reg32(adev, IO_ACX_SLV_MEM_DATA);
++#endif
++ w32 = read_slavemem32 (adev, offset + len - 4);
++
++ if (unlikely(w32 != v32)) {
++ printk("acx: FATAL: firmware upload: "
++ "data parts at offset %d don't match\n(0x%08X vs. 0x%08X)!\n"
++ "I/O timing issues or defective memory, with DWL-xx0+? "
++ "ACX_IO_WIDTH=16 may help. Please report\n",
++ len, v32, w32);
++ result = NOT_OK;
++ break;
++ }
++
++ sum += (u8)w32 + (u8)(w32>>8) + (u8)(w32>>16) + (u8)(w32>>24);
++ }
++
++ /* sum control verification */
++ if (result != NOT_OK) {
++ if (sum != le32_to_cpu(fw_image->chksum)) {
++ printk("acx: FATAL: firmware upload: "
++ "checksums don't match!\n");
++ result = NOT_OK;
++ }
++ }
++
++ return result;
++}
++
++
++/***********************************************************************
++** acxmem_s_upload_fw
++**
++** Called from acx_reset_dev
++*/
++static int
++acxmem_s_upload_fw(acx_device_t *adev)
++{
++ firmware_image_t *fw_image = NULL;
++ int res = NOT_OK;
++ int try;
++ u32 file_size;
++ char *filename = "WLANGEN.BIN";
++#ifdef PATCH_AROUND_BAD_SPOTS
++ u32 offset;
++ int i;
++ /*
++ * arm-linux-objdump -d patch.bin, or
++ * od -Ax -t x4 patch.bin after finding the bounds
++ * of the .text section with arm-linux-objdump -s patch.bin
++ */
++ u32 patch[] = {
++ 0xe584c030, 0xe59fc008,
++ 0xe92d1000, 0xe59fc004, 0xe8bd8000, 0x0000080c,
++ 0x0000aa68, 0x605a2200, 0x2c0a689c, 0x2414d80a,
++ 0x2f00689f, 0x1c27d007, 0x06241e7c, 0x2f000e24,
++ 0xe000d1f6, 0x602e6018, 0x23036468, 0x480203db,
++ 0x60ca6003, 0xbdf0750a, 0xffff0808
++ };
++#endif
++
++ FN_ENTER;
++ /* No combined image; tell common we need the radio firmware, too */
++ adev->need_radio_fw = 1;
++
++ fw_image = acx_s_read_fw(adev->dev, filename, &file_size);
++ if (!fw_image) {
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++ }
++
++ for (try = 1; try <= 5; try++) {
++ res = acxmem_s_write_fw(adev, fw_image, 0);
++ log(L_DEBUG|L_INIT, "acx_write_fw (main): %d\n", res);
++ if (OK == res) {
++ res = acxmem_s_validate_fw(adev, fw_image, 0);
++ log(L_DEBUG|L_INIT, "acx_validate_fw "
++ "(main): %d\n", res);
++ }
++
++ if (OK == res) {
++ SET_BIT(adev->dev_state_mask, ACX_STATE_FW_LOADED);
++ break;
++ }
++ printk("acx: firmware upload attempt #%d FAILED, "
++ "retrying...\n", try);
++ acx_s_msleep(1000); /* better wait for a while... */
++ }
++
++#ifdef PATCH_AROUND_BAD_SPOTS
++ /*
++ * Only want to do this if the firmware is exactly what we expect for an
++ * iPaq 4700; otherwise, bad things would ensue.
++ */
++ if ((HX4700_FIRMWARE_CHECKSUM == fw_image->chksum) ||
++ (HX4700_ALTERNATE_FIRMWARE_CHECKSUM == fw_image->chksum)) {
++ /*
++ * Put the patch after the main firmware image. 0x950c contains
++ * the ACX's idea of the end of the firmware. Use that location to
++ * load ours (which depends on that location being 0xab58) then
++ * update that location to point to after ours.
++ */
++
++ offset = read_slavemem32 (adev, 0x950c);
++
++ log (L_DEBUG, "acx: patching in at 0x%04x\n", offset);
++
++ for (i = 0; i < sizeof(patch) / sizeof(patch[0]); i++) {
++ write_slavemem32 (adev, offset, patch[i]);
++ offset += sizeof(u32);
++ }
++
++ /*
++ * Patch the instruction at 0x0804 to branch to our ARM patch at 0xab58
++ */
++ write_slavemem32 (adev, 0x0804, 0xea000000 + (0xab58-0x0804-8)/4);
++
++ /*
++ * Patch the instructions at 0x1f40 to branch to our Thumb patch at 0xab74
++ *
++ * 4a00 ldr r2, [pc, #0]
++ * 4710 bx r2
++ * .data 0xab74+1
++ */
++ write_slavemem32 (adev, 0x1f40, 0x47104a00);
++ write_slavemem32 (adev, 0x1f44, 0x0000ab74+1);
++
++ /*
++ * Bump the end of the firmware up to beyond our patch.
++ */
++ write_slavemem32 (adev, 0x950c, offset);
++
++ }
++#endif
++
++ vfree(fw_image);
++
++ FN_EXIT1(res);
++ return res;
++}
++
++
++/***********************************************************************
++** acxmem_s_upload_radio
++**
++** Uploads the appropriate radio module firmware into the card.
++*/
++int
++acxmem_s_upload_radio(acx_device_t *adev)
++{
++ acx_ie_memmap_t mm;
++ firmware_image_t *radio_image;
++ acx_cmd_radioinit_t radioinit;
++ int res = NOT_OK;
++ int try;
++ u32 offset;
++ u32 size;
++ char filename[sizeof("RADIONN.BIN")];
++
++ if (!adev->need_radio_fw) return OK;
++
++ FN_ENTER;
++
++ acx_s_interrogate(adev, &mm, ACX1xx_IE_MEMORY_MAP);
++ offset = le32_to_cpu(mm.CodeEnd);
++
++ snprintf(filename, sizeof(filename), "RADIO%02x.BIN",
++ adev->radio_type);
++ radio_image = acx_s_read_fw(adev->dev, filename, &size);
++ if (!radio_image) {
++ printk("acx: can't load radio module '%s'\n", filename);
++ goto fail;
++ }
++
++ acx_s_issue_cmd(adev, ACX1xx_CMD_SLEEP, NULL, 0);
++
++ for (try = 1; try <= 5; try++) {
++ res = acxmem_s_write_fw(adev, radio_image, offset);
++ log(L_DEBUG|L_INIT, "acx_write_fw (radio): %d\n", res);
++ if (OK == res) {
++ res = acxmem_s_validate_fw(adev, radio_image, offset);
++ log(L_DEBUG|L_INIT, "acx_validate_fw (radio): %d\n", res);
++ }
++
++ if (OK == res)
++ break;
++ printk("acx: radio firmware upload attempt #%d FAILED, "
++ "retrying...\n", try);
++ acx_s_msleep(1000); /* better wait for a while... */
++ }
++
++ acx_s_issue_cmd(adev, ACX1xx_CMD_WAKE, NULL, 0);
++ radioinit.offset = cpu_to_le32(offset);
++
++ /* no endian conversion needed, remains in card CPU area: */
++ radioinit.len = radio_image->size;
++
++ vfree(radio_image);
++
++ if (OK != res)
++ goto fail;
++
++ /* will take a moment so let's have a big timeout */
++ acx_s_issue_cmd_timeo(adev, ACX1xx_CMD_RADIOINIT,
++ &radioinit, sizeof(radioinit), CMD_TIMEOUT_MS(1000));
++
++ res = acx_s_interrogate(adev, &mm, ACX1xx_IE_MEMORY_MAP);
++
++fail:
++ FN_EXIT1(res);
++ return res;
++}
++
++/***********************************************************************
++** acxmem_l_reset_mac
++**
++** MAC will be reset
++** Call context: reset_dev
++*/
++static void
++acxmem_l_reset_mac(acx_device_t *adev)
++{
++ int count;
++ FN_ENTER;
++
++ /* halt eCPU */
++ set_regbits (adev, IO_ACX_ECPU_CTRL, 0x1);
++
++ /* now do soft reset of eCPU, set bit */
++ set_regbits (adev, IO_ACX_SOFT_RESET, 0x1);
++ log(L_DEBUG, "%s: enable soft reset...\n", __func__);
++
++ /* Windows driver sleeps here for a while with this sequence */
++ for (count = 0; count < 200; count++) {
++ udelay (50);
++ }
++
++ /* now clear bit again: deassert eCPU reset */
++ log(L_DEBUG, "%s: disable soft reset and go to init mode...\n", __func__);
++ clear_regbits (adev, IO_ACX_SOFT_RESET, 0x1);
++
++ /* now start a burst read from initial EEPROM */
++ set_regbits (adev, IO_ACX_EE_START, 0x1);
++
++ /*
++ * Windows driver sleeps here for a while with this sequence
++ */
++ for (count = 0; count < 200; count++) {
++ udelay (50);
++ }
++
++ /* Windows driver writes 0x10000 to register 0x808 here */
++
++ write_reg32 (adev, 0x808, 0x10000);
++
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acxmem_s_verify_init
++*/
++static int
++acxmem_s_verify_init(acx_device_t *adev)
++{
++ int result = NOT_OK;
++ unsigned long timeout;
++
++ FN_ENTER;
++
++ timeout = jiffies + 2*HZ;
++ for (;;) {
++ u32 irqstat = read_reg32(adev, IO_ACX_IRQ_STATUS_NON_DES);
++ if ((irqstat != 0xFFFFFFFF) && (irqstat & HOST_INT_FCS_THRESHOLD)) {
++ result = OK;
++ write_reg32(adev, IO_ACX_IRQ_ACK, HOST_INT_FCS_THRESHOLD);
++ break;
++ }
++ if (time_after(jiffies, timeout))
++ break;
++ /* Init may take up to ~0.5 sec total */
++ acx_s_msleep(50);
++ }
++
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** A few low-level helpers
++**
++** Note: these functions are not protected by lock
++** and thus are never allowed to be called from IRQ.
++** Also they must not race with fw upload which uses same hw regs
++*/
++
++/***********************************************************************
++** acxmem_write_cmd_type_status
++*/
++
++static inline void
++acxmem_write_cmd_type_status(acx_device_t *adev, u16 type, u16 status)
++{
++ write_slavemem32 (adev, (u32) adev->cmd_area, type | (status << 16));
++ write_flush(adev);
++}
++
++
++/***********************************************************************
++** acxmem_read_cmd_type_status
++*/
++static u32
++acxmem_read_cmd_type_status(acx_device_t *adev)
++{
++ u32 cmd_type, cmd_status;
++
++ cmd_type = read_slavemem32 (adev, (u32) adev->cmd_area);
++
++ cmd_status = (cmd_type >> 16);
++ cmd_type = (u16)cmd_type;
++
++ log(L_CTL, "cmd_type:%04X cmd_status:%04X [%s]\n",
++ cmd_type, cmd_status,
++ acx_cmd_status_str(cmd_status));
++
++ return cmd_status;
++}
++
++
++/***********************************************************************
++** acxmem_s_reset_dev
++**
++** Arguments:
++** netdevice that contains the adev variable
++** Returns:
++** NOT_OK on fail
++** OK on success
++** Side effects:
++** device is hard reset
++** Call context:
++** acxmem_e_probe
++** Comment:
++** This resets the device using low level hardware calls
++** as well as uploads and verifies the firmware to the card
++*/
++
++static inline void
++init_mboxes(acx_device_t *adev)
++{
++ u32 cmd_offs, info_offs;
++
++ cmd_offs = read_reg32(adev, IO_ACX_CMD_MAILBOX_OFFS);
++ info_offs = read_reg32(adev, IO_ACX_INFO_MAILBOX_OFFS);
++ adev->cmd_area = (u8*) cmd_offs;
++ adev->info_area = (u8*) info_offs;
++ /*
++ log(L_DEBUG, "iobase2=%p\n"
++ */
++ log( L_DEBUG, "cmd_mbox_offset=%X cmd_area=%p\n"
++ "info_mbox_offset=%X info_area=%p\n",
++ cmd_offs, adev->cmd_area,
++ info_offs, adev->info_area);
++}
++
++
++static inline void
++read_eeprom_area(acx_device_t *adev)
++{
++#if ACX_DEBUG > 1
++ int offs;
++ u8 tmp;
++
++ for (offs = 0x8c; offs < 0xb9; offs++)
++ acxmem_read_eeprom_byte(adev, offs, &tmp);
++#endif
++}
++
++static int
++acxmem_s_reset_dev(acx_device_t *adev)
++{
++ const char* msg = "";
++ unsigned long flags;
++ int result = NOT_OK;
++ u16 hardware_info;
++ u16 ecpu_ctrl;
++ int count;
++ u32 tmp;
++
++ FN_ENTER;
++ /*
++ write_reg32 (adev, IO_ACX_SLV_MEM_CP, 0);
++ */
++ /* reset the device to make sure the eCPU is stopped
++ * to upload the firmware correctly */
++
++ acx_lock(adev, flags);
++
++ /* Windows driver does some funny things here */
++ /*
++ * clear bit 0x200 in register 0x2A0
++ */
++ clear_regbits (adev, 0x2A0, 0x200);
++
++ /*
++ * Set bit 0x200 in ACX_GPIO_OUT
++ */
++ set_regbits (adev, IO_ACX_GPIO_OUT, 0x200);
++
++ /*
++ * read register 0x900 until its value is 0x8400104C, sleeping
++ * in between reads if it's not immediate
++ */
++ tmp = read_reg32 (adev, REG_ACX_VENDOR_ID);
++ count = 500;
++ while (count-- && (tmp != ACX_VENDOR_ID)) {
++ mdelay (10);
++ tmp = read_reg32 (adev, REG_ACX_VENDOR_ID);
++ }
++
++ /* end what Windows driver does */
++
++ acxmem_l_reset_mac(adev);
++
++ ecpu_ctrl = read_reg32(adev, IO_ACX_ECPU_CTRL) & 1;
++ if (!ecpu_ctrl) {
++ msg = "eCPU is already running. ";
++ goto end_unlock;
++ }
++
++#ifdef WE_DONT_NEED_THAT_DO_WE
++ if (read_reg16(adev, IO_ACX_SOR_CFG) & 2) {
++ /* eCPU most likely means "embedded CPU" */
++ msg = "eCPU did not start after boot from flash. ";
++ goto end_unlock;
++ }
++
++ /* check sense on reset flags */
++ if (read_reg16(adev, IO_ACX_SOR_CFG) & 0x10) {
++ printk("%s: eCPU did not start after boot (SOR), "
++ "is this fatal?\n", adev->ndev->name);
++ }
++#endif
++ /* scan, if any, is stopped now, setting corresponding IRQ bit */
++ adev->irq_status |= HOST_INT_SCAN_COMPLETE;
++
++ acx_unlock(adev, flags);
++
++ /* need to know radio type before fw load */
++ /* Need to wait for arrival of this information in a loop,
++ * most probably since eCPU runs some init code from EEPROM
++ * (started burst read in reset_mac()) which also
++ * sets the radio type ID */
++
++ count = 0xffff;
++ do {
++ hardware_info = read_reg16(adev, IO_ACX_EEPROM_INFORMATION);
++ if (!--count) {
++ msg = "eCPU didn't indicate radio type";
++ goto end_fail;
++ }
++ cpu_relax();
++ } while (!(hardware_info & 0xff00)); /* radio type still zero? */
++ printk("ACX radio type 0x%02x\n", (hardware_info >> 8) & 0xff);
++ /* printk("DEBUG: count %d\n", count); */
++ adev->form_factor = hardware_info & 0xff;
++ adev->radio_type = hardware_info >> 8;
++
++ /* load the firmware */
++ if (OK != acxmem_s_upload_fw(adev))
++ goto end_fail;
++
++ /* acx_s_msleep(10); this one really shouldn't be required */
++
++ /* now start eCPU by clearing bit */
++ clear_regbits (adev, IO_ACX_ECPU_CTRL, 0x1);
++ log(L_DEBUG, "booted eCPU up and waiting for completion...\n");
++
++ /* Windows driver clears bit 0x200 in register 0x2A0 here */
++ clear_regbits (adev, 0x2A0, 0x200);
++
++ /* Windows driver sets bit 0x200 in ACX_GPIO_OUT here */
++ set_regbits (adev, IO_ACX_GPIO_OUT, 0x200);
++ /* wait for eCPU bootup */
++ if (OK != acxmem_s_verify_init(adev)) {
++ msg = "timeout waiting for eCPU. ";
++ goto end_fail;
++ }
++ log(L_DEBUG, "eCPU has woken up, card is ready to be configured\n");
++ init_mboxes(adev);
++ acxmem_write_cmd_type_status(adev, ACX1xx_CMD_RESET, 0);
++
++ /* test that EEPROM is readable */
++ read_eeprom_area(adev);
++
++ result = OK;
++ goto end;
++
++/* Finish error message. Indicate which function failed */
++end_unlock:
++ acx_unlock(adev, flags);
++end_fail:
++ printk("acx: %sreset_dev() FAILED\n", msg);
++end:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acxmem_s_issue_cmd_timeo
++**
++** Sends command to fw, extract result
++**
++** NB: we do _not_ take lock inside, so be sure to not touch anything
++** which may interfere with IRQ handler operation
++**
++** TODO: busy wait is a bit silly, so:
++** 1) stop doing many iters - go to sleep after first
++** 2) go to waitqueue based approach: wait, not poll!
++*/
++#undef FUNC
++#define FUNC "issue_cmd"
++
++#if !ACX_DEBUG
++int
++acxmem_s_issue_cmd_timeo(
++ acx_device_t *adev,
++ unsigned int cmd,
++ void *buffer,
++ unsigned buflen,
++ unsigned cmd_timeout)
++{
++#else
++int
++acxmem_s_issue_cmd_timeo_debug(
++ acx_device_t *adev,
++ unsigned cmd,
++ void *buffer,
++ unsigned buflen,
++ unsigned cmd_timeout,
++ const char* cmdstr)
++{
++ unsigned long start = jiffies;
++#endif
++ const char *devname;
++ unsigned counter;
++ u16 irqtype;
++ int i, j;
++ u8 *p;
++ u16 cmd_status;
++ unsigned long timeout;
++
++ FN_ENTER;
++
++ devname = adev->ndev->name;
++ if (!devname || !devname[0] || devname[4]=='%')
++ devname = "acx";
++
++ log(L_CTL, FUNC"(cmd:%s,buflen:%u,timeout:%ums,type:0x%04X)\n",
++ cmdstr, buflen, cmd_timeout,
++ buffer ? le16_to_cpu(((acx_ie_generic_t *)buffer)->type) : -1);
++
++ if (!(adev->dev_state_mask & ACX_STATE_FW_LOADED)) {
++ printk("%s: "FUNC"(): firmware is not loaded yet, "
++ "cannot execute commands!\n", devname);
++ goto bad;
++ }
++
++ if ((acx_debug & L_DEBUG) && (cmd != ACX1xx_CMD_INTERROGATE)) {
++ printk("input buffer (len=%u):\n", buflen);
++ acx_dump_bytes(buffer, buflen);
++ }
++
++ /* wait for firmware to become idle for our command submission */
++ timeout = HZ/5;
++ counter = (timeout * 1000 / HZ) - 1; /* in ms */
++ timeout += jiffies;
++ do {
++ cmd_status = acxmem_read_cmd_type_status(adev);
++ /* Test for IDLE state */
++ if (!cmd_status)
++ break;
++ if (counter % 8 == 0) {
++ if (time_after(jiffies, timeout)) {
++ counter = 0;
++ break;
++ }
++ /* we waited 8 iterations, no luck. Sleep 8 ms */
++ acx_s_msleep(8);
++ }
++ } while (likely(--counter));
++
++ if (!counter) {
++ /* the card doesn't get idle, we're in trouble */
++ printk("%s: "FUNC"(): cmd_status is not IDLE: 0x%04X!=0\n",
++ devname, cmd_status);
++#if DUMP_IF_SLOW > 0
++ dump_acxmem (adev, 0, 0x10000);
++ panic ("not idle");
++#endif
++ goto bad;
++ } else if (counter < 190) { /* if waited >10ms... */
++ log(L_CTL|L_DEBUG, FUNC"(): waited for IDLE %dms. "
++ "Please report\n", 199 - counter);
++ }
++
++ /* now write the parameters of the command if needed */
++ if (buffer && buflen) {
++ /* if it's an INTERROGATE command, just pass the length
++ * of parameters to read, as data */
++#if CMD_DISCOVERY
++ if (cmd == ACX1xx_CMD_INTERROGATE)
++ memset_io(adev->cmd_area + 4, 0xAA, buflen);
++#endif
++ /*
++ * slave memory version
++ */
++ copy_to_slavemem (adev, (u32) (adev->cmd_area + 4), buffer,
++ (cmd == ACX1xx_CMD_INTERROGATE) ? 4 : buflen);
++ }
++ /* now write the actual command type */
++ acxmem_write_cmd_type_status(adev, cmd, 0);
++
++ /* clear CMD_COMPLETE bit. can be set only by IRQ handler: */
++ adev->irq_status &= ~HOST_INT_CMD_COMPLETE;
++
++ /* execute command */
++ write_reg16(adev, IO_ACX_INT_TRIG, INT_TRIG_CMD);
++ write_flush(adev);
++
++ /* wait for firmware to process command */
++
++ /* Ensure nonzero and not too large timeout.
++ ** Also converts e.g. 100->99, 200->199
++ ** which is nice but not essential */
++ cmd_timeout = (cmd_timeout-1) | 1;
++ if (unlikely(cmd_timeout > 1199))
++ cmd_timeout = 1199;
++
++ /* we schedule away sometimes (timeout can be large) */
++ counter = cmd_timeout;
++ timeout = jiffies + cmd_timeout * HZ / 1000;
++ do {
++ if (!adev->irqs_active) { /* IRQ disabled: poll */
++ irqtype = read_reg16(adev, IO_ACX_IRQ_STATUS_NON_DES);
++ if (irqtype & HOST_INT_CMD_COMPLETE) {
++ write_reg16(adev, IO_ACX_IRQ_ACK,
++ HOST_INT_CMD_COMPLETE);
++ break;
++ }
++ } else { /* Wait when IRQ will set the bit */
++ irqtype = adev->irq_status;
++ if (irqtype & HOST_INT_CMD_COMPLETE)
++ break;
++ }
++
++ if (counter % 8 == 0) {
++ if (time_after(jiffies, timeout)) {
++ counter = 0;
++ break;
++ }
++ /* we waited 8 iterations, no luck. Sleep 8 ms */
++ acx_s_msleep(8);
++ }
++ } while (likely(--counter));
++
++ /* save state for debugging */
++ cmd_status = acxmem_read_cmd_type_status(adev);
++
++ /* put the card in IDLE state */
++ acxmem_write_cmd_type_status(adev, ACX1xx_CMD_RESET, 0);
++
++ if (!counter) { /* timed out! */
++ printk("%s: "FUNC"(): timed out %s for CMD_COMPLETE. "
++ "irq bits:0x%04X irq_status:0x%04X timeout:%dms "
++ "cmd_status:%d (%s)\n",
++ devname, (adev->irqs_active) ? "waiting" : "polling",
++ irqtype, adev->irq_status, cmd_timeout,
++ cmd_status, acx_cmd_status_str(cmd_status));
++ printk("%s: "FUNC"(): device irq status 0x%04x\n",
++ devname, read_reg16(adev, IO_ACX_IRQ_STATUS_NON_DES));
++ printk("%s: "FUNC"(): IO_ACX_IRQ_MASK 0x%04x IO_ACX_FEMR 0x%04x\n",
++ devname,
++ read_reg16 (adev, IO_ACX_IRQ_MASK),
++ read_reg16 (adev, IO_ACX_FEMR));
++ if (read_reg16 (adev, IO_ACX_IRQ_MASK) == 0xffff) {
++ printk ("acxmem: firmware probably hosed - reloading\n");
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11)
++ {
++ pm_message_t state;
++ /* acxmem_e_suspend (resume_pdev, state); */
++ acxmem_e_suspend (adev->ndev , state);
++ }
++#else
++ acxmem_e_suspend (adev, 0);
++#endif
++ {
++ resume_ndev = adev->ndev;
++ fw_resumer (NULL);
++ }
++ }
++
++ goto bad;
++ } else if (cmd_timeout - counter > 30) { /* if waited >30ms... */
++ log(L_CTL|L_DEBUG, FUNC"(): %s for CMD_COMPLETE %dms. "
++ "count:%d. Please report\n",
++ (adev->irqs_active) ? "waited" : "polled",
++ cmd_timeout - counter, counter);
++ }
++
++ if (1 != cmd_status) { /* it is not a 'Success' */
++ printk("%s: "FUNC"(): cmd_status is not SUCCESS: %d (%s). "
++ "Took %dms of %d\n",
++ devname, cmd_status, acx_cmd_status_str(cmd_status),
++ cmd_timeout - counter, cmd_timeout);
++ /* zero out result buffer
++ * WARNING: this will trash stack in case of illegally large input
++ * length! */
++ if (buflen > 388) {
++ /*
++ * 388 is maximum command length
++ */
++ printk ("invalid length 0x%08x\n", buflen);
++ buflen = 388;
++ }
++ p = (u8 *) buffer;
++ for (i = 0; i < buflen; i+= 16) {
++ printk ("%04x:", i);
++ for (j = 0; (j < 16) && (i+j < buflen); j++) {
++ printk (" %02x", *p++);
++ }
++ printk ("\n");
++ }
++ if (buffer && buflen)
++ memset(buffer, 0, buflen);
++ goto bad;
++ }
++
++ /* read in result parameters if needed */
++ if (buffer && buflen && (cmd == ACX1xx_CMD_INTERROGATE)) {
++ copy_from_slavemem (adev, buffer, (u32) (adev->cmd_area + 4), buflen);
++ if (acx_debug & L_DEBUG) {
++ printk("output buffer (len=%u): ", buflen);
++ acx_dump_bytes(buffer, buflen);
++ }
++ }
++
++/* ok: */
++ log(L_CTL, FUNC"(%s): took %ld jiffies to complete\n",
++ cmdstr, jiffies - start);
++ FN_EXIT1(OK);
++ return OK;
++
++bad:
++ /* Give enough info so that callers can avoid
++ ** printing their own diagnostic messages */
++#if ACX_DEBUG
++ printk("%s: "FUNC"(cmd:%s) FAILED\n", devname, cmdstr);
++#else
++ printk("%s: "FUNC"(cmd:0x%04X) FAILED\n", devname, cmd);
++#endif
++ dump_stack();
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++}
++
++
++/***********************************************************************
++*/
++#if defined(NONESSENTIAL_FEATURES)
++typedef struct device_id {
++ unsigned char id[6];
++ char *descr;
++ char *type;
++} device_id_t;
++
++static const device_id_t
++device_ids[] =
++{
++ {
++ {'G', 'l', 'o', 'b', 'a', 'l'},
++ NULL,
++ NULL,
++ },
++ {
++ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
++ "uninitialized",
++ "SpeedStream SS1021 or Gigafast WF721-AEX"
++ },
++ {
++ {0x80, 0x81, 0x82, 0x83, 0x84, 0x85},
++ "non-standard",
++ "DrayTek Vigor 520"
++ },
++ {
++ {'?', '?', '?', '?', '?', '?'},
++ "non-standard",
++ "Level One WPC-0200"
++ },
++ {
++ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
++ "empty",
++ "DWL-650+ variant"
++ }
++};
++
++static void
++acx_show_card_eeprom_id(acx_device_t *adev)
++{
++ unsigned char buffer[CARD_EEPROM_ID_SIZE];
++ int i;
++
++ memset(&buffer, 0, CARD_EEPROM_ID_SIZE);
++ /* use direct EEPROM access */
++ for (i = 0; i < CARD_EEPROM_ID_SIZE; i++) {
++ if (OK != acxmem_read_eeprom_byte(adev,
++ ACX100_EEPROM_ID_OFFSET + i,
++ &buffer[i])) {
++ printk("acx: reading EEPROM FAILED\n");
++ break;
++ }
++ }
++
++ for (i = 0; i < VEC_SIZE(device_ids); i++) {
++ if (!memcmp(&buffer, device_ids[i].id, CARD_EEPROM_ID_SIZE)) {
++ if (device_ids[i].descr) {
++ printk("acx: EEPROM card ID string check "
++ "found %s card ID: is this %s?\n",
++ device_ids[i].descr, device_ids[i].type);
++ }
++ break;
++ }
++ }
++ if (i == VEC_SIZE(device_ids)) {
++ printk("acx: EEPROM card ID string check found "
++ "unknown card: expected 'Global', got '%.*s\'. "
++ "Please report\n", CARD_EEPROM_ID_SIZE, buffer);
++ }
++}
++#endif /* NONESSENTIAL_FEATURES */
++
++/***********************************************************************
++** acxmem_free_desc_queues
++**
++** Releases the queues that have been allocated, the
++** others have been initialised to NULL so this
++** function can be used if only part of the queues were allocated.
++*/
++
++void
++acxmem_free_desc_queues(acx_device_t *adev)
++{
++#define ACX_FREE_QUEUE(size, ptr, phyaddr) \
++ if (ptr) { \
++ kfree(ptr); \
++ ptr = NULL; \
++ size = 0; \
++ }
++
++ FN_ENTER;
++
++ ACX_FREE_QUEUE(adev->txhostdesc_area_size, adev->txhostdesc_start, adev->txhostdesc_startphy);
++ ACX_FREE_QUEUE(adev->txbuf_area_size, adev->txbuf_start, adev->txbuf_startphy);
++
++ adev->txdesc_start = NULL;
++
++ ACX_FREE_QUEUE(adev->rxhostdesc_area_size, adev->rxhostdesc_start, adev->rxhostdesc_startphy);
++ ACX_FREE_QUEUE(adev->rxbuf_area_size, adev->rxbuf_start, adev->rxbuf_startphy);
++
++ adev->rxdesc_start = NULL;
++
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acxmem_s_delete_dma_regions
++*/
++static void
++acxmem_s_delete_dma_regions(acx_device_t *adev)
++{
++ unsigned long flags;
++
++ FN_ENTER;
++ /* disable radio Tx/Rx. Shouldn't we use the firmware commands
++ * here instead? Or are we that much down the road that it's no
++ * longer possible here? */
++ /*
++ * slave memory interface really doesn't like this.
++ */
++ /*
++ write_reg16(adev, IO_ACX_ENABLE, 0);
++ */
++
++ acx_s_msleep(100);
++
++ acx_lock(adev, flags);
++ acxmem_free_desc_queues(adev);
++ acx_unlock(adev, flags);
++
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acxmem_e_probe
++**
++** Probe routine called when a PCI device w/ matching ID is found.
++** Here's the sequence:
++** - Allocate the PCI resources.
++** - Read the PCMCIA attribute memory to make sure we have a WLAN card
++** - Reset the MAC
++** - Initialize the dev and wlan data
++** - Initialize the MAC
++**
++** pdev - ptr to pci device structure containing info about pci configuration
++** id - ptr to the device id entry that matched this device
++*/
++static const u16
++IO_ACX100[] =
++{
++ 0x0000, /* IO_ACX_SOFT_RESET */
++
++ 0x0014, /* IO_ACX_SLV_MEM_ADDR */
++ 0x0018, /* IO_ACX_SLV_MEM_DATA */
++ 0x001c, /* IO_ACX_SLV_MEM_CTL */
++ 0x0020, /* IO_ACX_SLV_END_CTL */
++
++ 0x0034, /* IO_ACX_FEMR */
++
++ 0x007c, /* IO_ACX_INT_TRIG */
++ 0x0098, /* IO_ACX_IRQ_MASK */
++ 0x00a4, /* IO_ACX_IRQ_STATUS_NON_DES */
++ 0x00a8, /* IO_ACX_IRQ_STATUS_CLEAR */
++ 0x00ac, /* IO_ACX_IRQ_ACK */
++ 0x00b0, /* IO_ACX_HINT_TRIG */
++
++ 0x0104, /* IO_ACX_ENABLE */
++
++ 0x0250, /* IO_ACX_EEPROM_CTL */
++ 0x0254, /* IO_ACX_EEPROM_ADDR */
++ 0x0258, /* IO_ACX_EEPROM_DATA */
++ 0x025c, /* IO_ACX_EEPROM_CFG */
++
++ 0x0268, /* IO_ACX_PHY_ADDR */
++ 0x026c, /* IO_ACX_PHY_DATA */
++ 0x0270, /* IO_ACX_PHY_CTL */
++
++ 0x0290, /* IO_ACX_GPIO_OE */
++
++ 0x0298, /* IO_ACX_GPIO_OUT */
++
++ 0x02a4, /* IO_ACX_CMD_MAILBOX_OFFS */
++ 0x02a8, /* IO_ACX_INFO_MAILBOX_OFFS */
++ 0x02ac, /* IO_ACX_EEPROM_INFORMATION */
++
++ 0x02d0, /* IO_ACX_EE_START */
++ 0x02d4, /* IO_ACX_SOR_CFG */
++ 0x02d8 /* IO_ACX_ECPU_CTRL */
++};
++
++static const u16
++IO_ACX111[] =
++{
++ 0x0000, /* IO_ACX_SOFT_RESET */
++
++ 0x0014, /* IO_ACX_SLV_MEM_ADDR */
++ 0x0018, /* IO_ACX_SLV_MEM_DATA */
++ 0x001c, /* IO_ACX_SLV_MEM_CTL */
++ 0x0020, /* IO_ACX_SLV_MEM_CP */
++
++ 0x0034, /* IO_ACX_FEMR */
++
++ 0x00b4, /* IO_ACX_INT_TRIG */
++ 0x00d4, /* IO_ACX_IRQ_MASK */
++ /* we do mean NON_DES (0xf0), not NON_DES_MASK which is at 0xe0: */
++ 0x00f0, /* IO_ACX_IRQ_STATUS_NON_DES */
++ 0x00e4, /* IO_ACX_IRQ_STATUS_CLEAR */
++ 0x00e8, /* IO_ACX_IRQ_ACK */
++ 0x00ec, /* IO_ACX_HINT_TRIG */
++
++ 0x01d0, /* IO_ACX_ENABLE */
++
++ 0x0338, /* IO_ACX_EEPROM_CTL */
++ 0x033c, /* IO_ACX_EEPROM_ADDR */
++ 0x0340, /* IO_ACX_EEPROM_DATA */
++ 0x0344, /* IO_ACX_EEPROM_CFG */
++
++ 0x0350, /* IO_ACX_PHY_ADDR */
++ 0x0354, /* IO_ACX_PHY_DATA */
++ 0x0358, /* IO_ACX_PHY_CTL */
++
++ 0x0374, /* IO_ACX_GPIO_OE */
++
++ 0x037c, /* IO_ACX_GPIO_OUT */
++
++ 0x0388, /* IO_ACX_CMD_MAILBOX_OFFS */
++ 0x038c, /* IO_ACX_INFO_MAILBOX_OFFS */
++ 0x0390, /* IO_ACX_EEPROM_INFORMATION */
++
++ 0x0100, /* IO_ACX_EE_START */
++ 0x0104, /* IO_ACX_SOR_CFG */
++ 0x0108, /* IO_ACX_ECPU_CTRL */
++};
++
++static void
++dummy_netdev_init(struct net_device *ndev) {}
++
++/*
++ * Most of the acx specific pieces of hardware reset.
++ */
++static int
++acxmem_complete_hw_reset (acx_device_t *adev)
++{
++ acx111_ie_configoption_t co;
++
++ /* NB: read_reg() reads may return bogus data before reset_dev(),
++ * since the firmware which directly controls large parts of the I/O
++ * registers isn't initialized yet.
++ * acx100 seems to be more affected than acx111 */
++ if (OK != acxmem_s_reset_dev (adev))
++ return -1;
++
++ if (IS_ACX100(adev)) {
++ /* ACX100: configopt struct in cmd mailbox - directly after reset */
++ copy_from_slavemem (adev, (u8*) &co, (u32) adev->cmd_area, sizeof (co));
++ }
++
++ if (OK != acx_s_init_mac(adev))
++ return -3;
++
++ if (IS_ACX111(adev)) {
++ /* ACX111: configopt struct needs to be queried after full init */
++ acx_s_interrogate(adev, &co, ACX111_IE_CONFIG_OPTIONS);
++ }
++
++ /*
++ * Set up transmit buffer administration
++ */
++ init_acx_txbuf (adev);
++
++ /*
++ * Windows driver writes 0x01000000 to register 0x288, RADIO_CTL, if the form factor
++ * is 3. It also write protects the EEPROM by writing 1<<9 to GPIO_OUT
++ */
++ if (adev->form_factor == 3) {
++ set_regbits (adev, 0x288, 0x01000000);
++ set_regbits (adev, 0x298, 1<<9);
++ }
++
++/* TODO: merge them into one function, they are called just once and are the same for pci & usb */
++ if (OK != acxmem_read_eeprom_byte(adev, 0x05, &adev->eeprom_version))
++ return -2;
++
++ acx_s_parse_configoption(adev, &co);
++ acx_s_get_firmware_version(adev); /* needs to be after acx_s_init_mac() */
++ acx_display_hardware_details(adev);
++
++ return 0;
++}
++
++static int acx_init_netdev(struct net_device *ndev, struct device *dev, int base_addr, int addr_size, int irq)
++{
++ const char *chip_name;
++ int result = -EIO;
++ int err;
++ u8 chip_type;
++ acx_device_t *adev = NULL;
++
++ FN_ENTER;
++
++ /* FIXME: prism54 calls pci_set_mwi() here,
++ * should we do/support the same? */
++
++ /* chiptype is u8 but id->driver_data is ulong
++ ** Works for now (possible values are 1 and 2) */
++ chip_type = CHIPTYPE_ACX100;
++ /* acx100 and acx111 have different PCI memory regions */
++ if (chip_type == CHIPTYPE_ACX100) {
++ chip_name = "ACX100";
++ } else if (chip_type == CHIPTYPE_ACX111) {
++ chip_name = "ACX111";
++ } else {
++ printk("acx: unknown chip type 0x%04X\n", chip_type);
++ goto fail_unknown_chiptype;
++ }
++
++ printk("acx: found %s-based wireless network card\n", chip_name);
++ log(L_ANY, "initial debug setting is 0x%04X\n", acx_debug);
++
++
++ dev_set_drvdata(dev, ndev);
++
++ ether_setup(ndev);
++
++ ndev->irq = irq;
++
++ ndev->base_addr = base_addr;
++printk (KERN_INFO "memwinbase=%lx memwinsize=%u\n",memwin.Base,memwin.Size);
++ if (addr_size == 0 || ndev->irq == 0)
++ goto fail_hw_params;
++ ndev->open = &acxmem_e_open;
++ ndev->stop = &acxmem_e_close;
++ //pdev->dev.release = &acxmem_e_release;
++ ndev->hard_start_xmit = &acx_i_start_xmit;
++ ndev->get_stats = &acx_e_get_stats;
++#if IW_HANDLER_VERSION <= 5
++ ndev->get_wireless_stats = &acx_e_get_wireless_stats;
++#endif
++ ndev->wireless_handlers = (struct iw_handler_def *)&acx_ioctl_handler_def;
++ ndev->set_multicast_list = &acxmem_i_set_multicast_list;
++ ndev->tx_timeout = &acxmem_i_tx_timeout;
++ ndev->change_mtu = &acx_e_change_mtu;
++ ndev->watchdog_timeo = 4 * HZ;
++
++ adev = ndev2adev(ndev);
++ spin_lock_init(&adev->lock); /* initial state: unlocked */
++ spin_lock_init(&adev->txbuf_lock);
++ /* We do not start with downed sem: we want PARANOID_LOCKING to work */
++ sema_init(&adev->sem, 1); /* initial state: 1 (upped) */
++ /* since nobody can see new netdev yet, we can as well
++ ** just _presume_ that we're under sem (instead of actually taking it): */
++ /* acx_sem_lock(adev); */
++ adev->dev = dev;
++ adev->ndev = ndev;
++ adev->dev_type = DEVTYPE_MEM;
++ adev->chip_type = chip_type;
++ adev->chip_name = chip_name;
++ adev->io = (CHIPTYPE_ACX100 == chip_type) ? IO_ACX100 : IO_ACX111;
++ adev->membase = (volatile u32 *) ndev->base_addr;
++ adev->iobase = (volatile u32 *) ioremap_nocache (ndev->base_addr, addr_size);
++ /* to find crashes due to weird driver access
++ * to unconfigured interface (ifup) */
++ adev->mgmt_timer.function = (void (*)(unsigned long))0x0000dead;
++
++#if defined(NONESSENTIAL_FEATURES)
++ acx_show_card_eeprom_id(adev);
++#endif /* NONESSENTIAL_FEATURES */
++
++#ifdef SET_MODULE_OWNER
++ SET_MODULE_OWNER(ndev);
++#endif
++ // need to fix that @@
++ SET_NETDEV_DEV(ndev, dev);
++
++ log(L_IRQ|L_INIT, "using IRQ %d\n", ndev->irq);
++
++ /* ok, pci setup is finished, now start initializing the card */
++
++ if (OK != acxmem_complete_hw_reset (adev))
++ goto fail_reset;
++
++ /*
++ * Set up default things for most of the card settings.
++ */
++ acx_s_set_defaults(adev);
++
++ /* Register the card, AFTER everything else has been set up,
++ * since otherwise an ioctl could step on our feet due to
++ * firmware operations happening in parallel or uninitialized data */
++ err = register_netdev(ndev);
++ if (OK != err) {
++ printk("acx: register_netdev() FAILED: %d\n", err);
++ goto fail_register_netdev;
++ }
++
++ acx_proc_register_entries(ndev);
++
++ /* Now we have our device, so make sure the kernel doesn't try
++ * to send packets even though we're not associated to a network yet */
++ acx_stop_queue(ndev, "on probe");
++ acx_carrier_off(ndev, "on probe");
++
++ /*
++ * Set up a default monitor type so that poor combinations of initialization
++ * sequences in monitor mode don't end up destroying the hardware type.
++ */
++ adev->monitor_type = ARPHRD_ETHER;
++
++ /*
++ * Register to receive inetaddr notifier changes. This will allow us to
++ * catch if the user changes the MAC address of the interface.
++ */
++ register_netdevice_notifier(&acx_netdev_notifier);
++
++ /* after register_netdev() userspace may start working with dev
++ * (in particular, on other CPUs), we only need to up the sem */
++ /* acx_sem_unlock(adev); */
++
++ printk("acx "ACX_RELEASE": net device %s, driver compiled "
++ "against wireless extensions %d and Linux %s\n",
++ ndev->name, WIRELESS_EXT, UTS_RELEASE);
++
++#if CMD_DISCOVERY
++ great_inquisitor(adev);
++#endif
++
++ result = OK;
++ goto done;
++
++ /* error paths: undo everything in reverse order... */
++
++fail_register_netdev:
++
++ acxmem_s_delete_dma_regions(adev);
++
++fail_reset:
++fail_hw_params:
++ free_netdev(ndev);
++fail_unknown_chiptype:
++
++
++done:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acxmem_e_remove
++**
++** Shut device down (if not hot unplugged)
++** and deallocate PCI resources for the acx chip.
++**
++** pdev - ptr to PCI device structure containing info about pci configuration
++*/
++static int __devexit
++acxmem_e_remove(struct pcmcia_device *link)
++{
++ struct net_device *ndev;
++ acx_device_t *adev;
++ unsigned long flags;
++
++ FN_ENTER;
++
++ ndev = ((local_info_t*)link->priv)->ndev;
++ if (!ndev) {
++ log(L_DEBUG, "%s: card is unused. Skipping any release code\n",
++ __func__);
++ goto end;
++ }
++
++ adev = ndev2adev(ndev);
++
++ /* If device wasn't hot unplugged... */
++ if (adev_present(adev)) {
++
++ acx_sem_lock(adev);
++
++ /* disable both Tx and Rx to shut radio down properly */
++ acx_s_issue_cmd(adev, ACX1xx_CMD_DISABLE_TX, NULL, 0);
++ acx_s_issue_cmd(adev, ACX1xx_CMD_DISABLE_RX, NULL, 0);
++
++#ifdef REDUNDANT
++ /* put the eCPU to sleep to save power
++ * Halting is not possible currently,
++ * since not supported by all firmware versions */
++ acx_s_issue_cmd(adev, ACX100_CMD_SLEEP, NULL, 0);
++#endif
++ acx_lock(adev, flags);
++
++ /* disable power LED to save power :-) */
++ log(L_INIT, "switching off power LED to save power\n");
++ acxmem_l_power_led(adev, 0);
++
++ /* stop our eCPU */
++ if (IS_ACX111(adev)) {
++ /* FIXME: does this actually keep halting the eCPU?
++ * I don't think so...
++ */
++ acxmem_l_reset_mac(adev);
++ } else {
++ u16 temp;
++
++ /* halt eCPU */
++ temp = read_reg16(adev, IO_ACX_ECPU_CTRL) | 0x1;
++ write_reg16(adev, IO_ACX_ECPU_CTRL, temp);
++ write_flush(adev);
++ }
++
++ acx_unlock(adev, flags);
++
++ acx_sem_unlock(adev);
++ }
++
++
++ /*
++ * Unregister the notifier chain
++ */
++ unregister_netdevice_notifier(&acx_netdev_notifier);
++
++ /* unregister the device to not let the kernel
++ * (e.g. ioctls) access a half-deconfigured device
++ * NB: this will cause acxmem_e_close() to be called,
++ * thus we shouldn't call it under sem! */
++ log(L_INIT, "removing device %s\n", ndev->name);
++ unregister_netdev(ndev);
++
++ /* unregister_netdev ensures that no references to us left.
++ * For paranoid reasons we continue to follow the rules */
++ acx_sem_lock(adev);
++
++ if (adev->dev_state_mask & ACX_STATE_IFACE_UP) {
++ acxmem_s_down(ndev);
++ CLEAR_BIT(adev->dev_state_mask, ACX_STATE_IFACE_UP);
++ }
++
++ acx_proc_unregister_entries(ndev);
++
++ acxmem_s_delete_dma_regions(adev);
++
++ /* finally, clean up PCI bus state */
++ if (adev->iobase) iounmap((void *)adev->iobase);
++
++ acx_sem_unlock(adev);
++
++ /* Free netdev (quite late,
++ * since otherwise we might get caught off-guard
++ * by a netdev timeout handler execution
++ * expecting to see a working dev...) */
++ free_netdev(ndev);
++
++ printk ("e_remove done\n");
++end:
++ FN_EXIT0;
++
++ return 0;
++}
++
++
++/***********************************************************************
++** TODO: PM code needs to be fixed / debugged / tested.
++*/
++#ifdef CONFIG_PM
++static int
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11)
++acxmem_e_suspend( struct net_device *ndev, pm_message_t state)
++#else
++acxmem_e_suspend( struct net_device *ndev, u32 state)
++#endif
++{
++ FN_ENTER;
++ acx_device_t *adev;
++ printk("acx: suspend handler is experimental!\n");
++ printk("sus: dev %p\n", ndev);
++
++ if (!netif_running(ndev))
++ goto end;
++ // @@ need to get it from link or something like that
++ adev = ndev2adev(ndev);
++ printk("sus: adev %p\n", adev);
++
++ acx_sem_lock(adev);
++
++ netif_device_detach(adev->ndev); /* this one cannot sleep */
++ acxmem_s_down(adev->ndev);
++ /* down() does not set it to 0xffff, but here we really want that */
++ write_reg16(adev, IO_ACX_IRQ_MASK, 0xffff);
++ write_reg16(adev, IO_ACX_FEMR, 0x0);
++ acxmem_s_delete_dma_regions(adev);
++
++ /*
++ * Turn the ACX chip off.
++ */
++
++ acx_sem_unlock(adev);
++end:
++ FN_EXIT0;
++ return OK;
++}
++
++
++static void
++fw_resumer(struct work_struct *notused)
++{
++ acx_device_t *adev;
++ struct net_device *ndev = resume_ndev;
++
++ printk("acx: resume handler is experimental!\n");
++ printk("rsm: got dev %p\n", ndev);
++
++ if (!netif_running(ndev))
++ return;
++
++ adev = ndev2adev(ndev);
++ printk("rsm: got adev %p\n", adev);
++
++ acx_sem_lock(adev);
++
++ /*
++ * Turn on the ACX.
++ */
++
++ acxmem_complete_hw_reset (adev);
++
++ /*
++ * done by acx_s_set_defaults for initial startup
++ */
++ acxmem_set_interrupt_mask(adev);
++
++ printk ("rsm: bringing up interface\n");
++ SET_BIT (adev->set_mask, GETSET_ALL);
++ acxmem_s_up(ndev);
++ printk("rsm: acx up done\n");
++
++ /* now even reload all card parameters as they were before suspend,
++ * and possibly be back in the network again already :-)
++ */
++ /* - most settings updated in acxmem_s_up()
++ if (ACX_STATE_IFACE_UP & adev->dev_state_mask) {
++ adev->set_mask = GETSET_ALL;
++ acx_s_update_card_settings(adev);
++ printk("rsm: settings updated\n");
++ }
++ */
++ netif_device_attach(ndev);
++ printk("rsm: device attached\n");
++
++ acx_sem_unlock(adev);
++}
++
++DECLARE_WORK( fw_resume_work, fw_resumer );
++
++static int
++acxmem_e_resume(struct pcmcia_device *link)
++{
++ FN_ENTER;
++
++ //resume_pdev = pdev;
++ schedule_work( &fw_resume_work );
++
++ FN_EXIT0;
++ return OK;
++}
++#endif /* CONFIG_PM */
++
++
++/***********************************************************************
++** acxmem_s_up
++**
++** This function is called by acxmem_e_open (when ifconfig sets the device as up)
++**
++** Side effects:
++** - Enables on-card interrupt requests
++** - calls acx_s_start
++*/
++
++static void
++enable_acx_irq(acx_device_t *adev)
++{
++ FN_ENTER;
++ write_reg16(adev, IO_ACX_IRQ_MASK, adev->irq_mask);
++ write_reg16(adev, IO_ACX_FEMR, 0x8000);
++ adev->irqs_active = 1;
++ FN_EXIT0;
++}
++
++static void
++acxmem_s_up(struct net_device *ndev)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ unsigned long flags;
++
++ FN_ENTER;
++
++ acx_lock(adev, flags);
++ enable_acx_irq(adev);
++ acx_unlock(adev, flags);
++
++ /* acx fw < 1.9.3.e has a hardware timer, and older drivers
++ ** used to use it. But we don't do that anymore, our OS
++ ** has reliable software timers */
++ init_timer(&adev->mgmt_timer);
++ adev->mgmt_timer.function = acx_i_timer;
++ adev->mgmt_timer.data = (unsigned long)adev;
++
++ /* Need to set ACX_STATE_IFACE_UP first, or else
++ ** timer won't be started by acx_set_status() */
++ SET_BIT(adev->dev_state_mask, ACX_STATE_IFACE_UP);
++ switch (adev->mode) {
++ case ACX_MODE_0_ADHOC:
++ case ACX_MODE_2_STA:
++ /* actual scan cmd will happen in start() */
++ acx_set_status(adev, ACX_STATUS_1_SCANNING); break;
++ case ACX_MODE_3_AP:
++ case ACX_MODE_MONITOR:
++ acx_set_status(adev, ACX_STATUS_4_ASSOCIATED); break;
++ }
++
++ acx_s_start(adev);
++
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acxmem_s_down
++**
++** This disables the netdevice
++**
++** Side effects:
++** - disables on-card interrupt request
++*/
++
++static void
++disable_acx_irq(acx_device_t *adev)
++{
++ FN_ENTER;
++
++ /* I guess mask is not 0xffff because acx100 won't signal
++ ** cmd completion then (needed for ifup).
++ ** Someone with acx100 please confirm */
++ write_reg16(adev, IO_ACX_IRQ_MASK, adev->irq_mask_off);
++ write_reg16(adev, IO_ACX_FEMR, 0x0);
++ adev->irqs_active = 0;
++ FN_EXIT0;
++}
++
++static void
++acxmem_s_down(struct net_device *ndev)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ unsigned long flags;
++
++ FN_ENTER;
++
++ /* Disable IRQs first, so that IRQs cannot race with us */
++ /* then wait until interrupts have finished executing on other CPUs */
++ acx_lock(adev, flags);
++ disable_acx_irq(adev);
++ synchronize_irq(adev->pdev->irq);
++ acx_unlock(adev, flags);
++
++ /* we really don't want to have an asynchronous tasklet disturb us
++ ** after something vital for its job has been shut down, so
++ ** end all remaining work now.
++ **
++ ** NB: carrier_off (done by set_status below) would lead to
++ ** not yet fully understood deadlock in FLUSH_SCHEDULED_WORK().
++ ** That's why we do FLUSH first.
++ **
++ ** NB2: we have a bad locking bug here: FLUSH_SCHEDULED_WORK()
++ ** waits for acx_e_after_interrupt_task to complete if it is running
++ ** on another CPU, but acx_e_after_interrupt_task
++ ** will sleep on sem forever, because it is taken by us!
++ ** Work around that by temporary sem unlock.
++ ** This will fail miserably if we'll be hit by concurrent
++ ** iwconfig or something in between. TODO! */
++ acx_sem_unlock(adev);
++ FLUSH_SCHEDULED_WORK();
++ acx_sem_lock(adev);
++
++ /* This is possible:
++ ** FLUSH_SCHEDULED_WORK -> acx_e_after_interrupt_task ->
++ ** -> set_status(ASSOCIATED) -> wake_queue()
++ ** That's why we stop queue _after_ FLUSH_SCHEDULED_WORK
++ ** lock/unlock is just paranoia, maybe not needed */
++ acx_lock(adev, flags);
++ acx_stop_queue(ndev, "on ifdown");
++ acx_set_status(adev, ACX_STATUS_0_STOPPED);
++ acx_unlock(adev, flags);
++
++ /* kernel/timer.c says it's illegal to del_timer_sync()
++ ** a timer which restarts itself. We guarantee this cannot
++ ** ever happen because acx_i_timer() never does this if
++ ** status is ACX_STATUS_0_STOPPED */
++ del_timer_sync(&adev->mgmt_timer);
++
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acxmem_e_open
++**
++** Called as a result of SIOCSIFFLAGS ioctl changing the flags bit IFF_UP
++** from clear to set. In other words: ifconfig up.
++**
++** Returns:
++** 0 success
++** >0 f/w reported error
++** <0 driver reported error
++*/
++static int
++acxmem_e_open(struct net_device *ndev)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ int result = OK;
++
++ FN_ENTER;
++
++ acx_sem_lock(adev);
++
++ acx_init_task_scheduler(adev);
++
++/* TODO: pci_set_power_state(pdev, PCI_D0); ? */
++
++#if 0
++ /* request shared IRQ handler */
++ if (request_irq(ndev->irq, acxmem_i_interrupt, SA_INTERRUPT, ndev->name, ndev)) {
++ printk("%s: request_irq FAILED\n", ndev->name);
++ result = -EAGAIN;
++ goto done;
++ }
++ set_irq_type (ndev->irq, IRQT_FALLING);
++ log(L_DEBUG|L_IRQ, "request_irq %d successful\n", ndev->irq);
++#endif
++
++ /* ifup device */
++ acxmem_s_up(ndev);
++
++ /* We don't currently have to do anything else.
++ * The setup of the MAC should be subsequently completed via
++ * the mlme commands.
++ * Higher layers know we're ready from dev->start==1 and
++ * dev->tbusy==0. Our rx path knows to pass up received/
++ * frames because of dev->flags&IFF_UP is true.
++ */
++done:
++ acx_sem_unlock(adev);
++
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acxmem_e_close
++**
++** Called as a result of SIOCSIIFFLAGS ioctl changing the flags bit IFF_UP
++** from set to clear. I.e. called by "ifconfig DEV down"
++**
++** Returns:
++** 0 success
++** >0 f/w reported error
++** <0 driver reported error
++*/
++static int
++acxmem_e_close(struct net_device *ndev)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++
++ FN_ENTER;
++
++ acx_sem_lock(adev);
++
++ /* ifdown device */
++ CLEAR_BIT(adev->dev_state_mask, ACX_STATE_IFACE_UP);
++ if (netif_device_present(ndev)) {
++ acxmem_s_down(ndev);
++ }
++
++ /* disable all IRQs, release shared IRQ handler */
++ write_reg16(adev, IO_ACX_IRQ_MASK, 0xffff);
++ write_reg16(adev, IO_ACX_FEMR, 0x0);
++ free_irq(ndev->irq, ndev);
++
++/* TODO: pci_set_power_state(pdev, PCI_D3hot); ? */
++
++ /* We currently don't have to do anything else.
++ * Higher layers know we're not ready from dev->start==0 and
++ * dev->tbusy==1. Our rx path knows to not pass up received
++ * frames because of dev->flags&IFF_UP is false.
++ */
++ acx_sem_unlock(adev);
++
++ log(L_INIT, "closed device\n");
++ FN_EXIT0;
++ return OK;
++}
++
++
++/***********************************************************************
++** acxmem_i_tx_timeout
++**
++** Called from network core. Must not sleep!
++*/
++static void
++acxmem_i_tx_timeout(struct net_device *ndev)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ unsigned long flags;
++ unsigned int tx_num_cleaned;
++
++ FN_ENTER;
++
++ acx_lock(adev, flags);
++
++ /* clean processed tx descs, they may have been completely full */
++ tx_num_cleaned = acxmem_l_clean_txdesc(adev);
++
++ /* nothing cleaned, yet (almost) no free buffers available?
++ * --> clean all tx descs, no matter which status!!
++ * Note that I strongly suspect that doing emergency cleaning
++ * may confuse the firmware. This is a last ditch effort to get
++ * ANYTHING to work again...
++ *
++ * TODO: it's best to simply reset & reinit hw from scratch...
++ */
++ if ((adev->tx_free <= TX_EMERG_CLEAN) && (tx_num_cleaned == 0)) {
++ printk("%s: FAILED to free any of the many full tx buffers. "
++ "Switching to emergency freeing. "
++ "Please report!\n", ndev->name);
++ acxmem_l_clean_txdesc_emergency(adev);
++ }
++
++ if (acx_queue_stopped(ndev) && (ACX_STATUS_4_ASSOCIATED == adev->status))
++ acx_wake_queue(ndev, "after tx timeout");
++
++ /* stall may have happened due to radio drift, so recalib radio */
++ acx_schedule_task(adev, ACX_AFTER_IRQ_CMD_RADIO_RECALIB);
++
++ /* do unimportant work last */
++ printk("%s: tx timeout!\n", ndev->name);
++ adev->stats.tx_errors++;
++
++ acx_unlock(adev, flags);
++
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acxmem_i_set_multicast_list
++** FIXME: most likely needs refinement
++*/
++static void
++acxmem_i_set_multicast_list(struct net_device *ndev)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ unsigned long flags;
++
++ FN_ENTER;
++
++ acx_lock(adev, flags);
++
++ /* firmwares don't have allmulti capability,
++ * so just use promiscuous mode instead in this case. */
++ if (ndev->flags & (IFF_PROMISC|IFF_ALLMULTI)) {
++ SET_BIT(adev->rx_config_1, RX_CFG1_RCV_PROMISCUOUS);
++ CLEAR_BIT(adev->rx_config_1, RX_CFG1_FILTER_ALL_MULTI);
++ SET_BIT(adev->set_mask, SET_RXCONFIG);
++ /* let kernel know in case *we* needed to set promiscuous */
++ ndev->flags |= (IFF_PROMISC|IFF_ALLMULTI);
++ } else {
++ CLEAR_BIT(adev->rx_config_1, RX_CFG1_RCV_PROMISCUOUS);
++ SET_BIT(adev->rx_config_1, RX_CFG1_FILTER_ALL_MULTI);
++ SET_BIT(adev->set_mask, SET_RXCONFIG);
++ ndev->flags &= ~(IFF_PROMISC|IFF_ALLMULTI);
++ }
++
++ /* cannot update card settings directly here, atomic context */
++ acx_schedule_task(adev, ACX_AFTER_IRQ_UPDATE_CARD_CFG);
++
++ acx_unlock(adev, flags);
++
++ FN_EXIT0;
++}
++
++
++/***************************************************************
++** acxmem_l_process_rxdesc
++**
++** Called directly and only from the IRQ handler
++*/
++
++#if !ACX_DEBUG
++static inline void log_rxbuffer(const acx_device_t *adev) {}
++#else
++static void
++log_rxbuffer(const acx_device_t *adev)
++{
++ register const struct rxhostdesc *rxhostdesc;
++ int i;
++ /* no FN_ENTER here, we don't want that */
++
++ rxhostdesc = adev->rxhostdesc_start;
++ if (unlikely(!rxhostdesc)) return;
++ for (i = 0; i < RX_CNT; i++) {
++ if ((rxhostdesc->Ctl_16 & cpu_to_le16(DESC_CTL_HOSTOWN))
++ && (rxhostdesc->Status & cpu_to_le32(DESC_STATUS_FULL)))
++ printk("rx: buf %d full\n", i);
++ rxhostdesc++;
++ }
++}
++#endif
++
++static void
++acxmem_l_process_rxdesc(acx_device_t *adev)
++{
++ register rxhostdesc_t *hostdesc;
++ register rxdesc_t *rxdesc;
++ unsigned count, tail;
++ u32 addr;
++ u8 Ctl_8;
++
++ FN_ENTER;
++
++ if (unlikely(acx_debug & L_BUFR))
++ log_rxbuffer(adev);
++
++ /* First, have a loop to determine the first descriptor that's
++ * full, just in case there's a mismatch between our current
++ * rx_tail and the full descriptor we're supposed to handle. */
++ tail = adev->rx_tail;
++ count = RX_CNT;
++ while (1) {
++ hostdesc = &adev->rxhostdesc_start[tail];
++ rxdesc = &adev->rxdesc_start[tail];
++ /* advance tail regardless of outcome of the below test */
++ tail = (tail + 1) % RX_CNT;
++
++ /*
++ * Unlike the PCI interface, where the ACX can write directly to
++ * the host descriptors, on the slave memory interface we have to
++ * pull these. All we really need to do is check the Ctl_8 field
++ * in the rx descriptor on the ACX, which should be 0x11000000 if
++ * we should process it.
++ */
++ Ctl_8 = hostdesc->Ctl_16 = read_slavemem8 (adev, (u32) &(rxdesc->Ctl_8));
++ if ((Ctl_8 & DESC_CTL_HOSTOWN) &&
++ (Ctl_8 & DESC_CTL_ACXDONE))
++ break; /* found it! */
++
++ if (unlikely(!--count)) /* hmm, no luck: all descs empty, bail out */
++ goto end;
++ }
++
++ /* now process descriptors, starting with the first we figured out */
++ while (1) {
++ log(L_BUFR, "rx: tail=%u Ctl_8=%02X\n", tail, Ctl_8);
++ /*
++ * If the ACX has CTL_RECLAIM set on this descriptor there
++ * is no buffer associated; it just wants us to tell it to
++ * reclaim the memory.
++ */
++ if (!(Ctl_8 & DESC_CTL_RECLAIM)) {
++
++ /*
++ * slave interface - pull data now
++ */
++ hostdesc->length = read_slavemem16 (adev, (u32) &(rxdesc->total_length));
++
++ /*
++ * hostdesc->data is an rxbuffer_t, which includes header information,
++ * but the length in the data packet doesn't. The header information
++ * takes up an additional 12 bytes, so add that to the length we copy.
++ */
++ addr = read_slavemem32 (adev, (u32) &(rxdesc->ACXMemPtr));
++ if (addr) {
++ /*
++ * How can &(rxdesc->ACXMemPtr) above ever be zero? Looks like we
++ * get that now and then - try to trap it for debug.
++ */
++ if (addr & 0xffff0000) {
++ printk("rxdesc 0x%08x\n", (u32) rxdesc);
++ dump_acxmem (adev, 0, 0x10000);
++ panic ("Bad access!");
++ }
++ chaincopy_from_slavemem (adev, (u8 *) hostdesc->data, addr,
++ hostdesc->length +
++ (u32) &((rxbuffer_t *)0)->hdr_a3);
++ acx_l_process_rxbuf(adev, hostdesc->data);
++ }
++ }
++ else {
++ printk ("rx reclaim only!\n");
++ }
++
++ hostdesc->Status = 0;
++
++ /*
++ * Let the ACX know we're done.
++ */
++ CLEAR_BIT (Ctl_8, DESC_CTL_HOSTOWN);
++ SET_BIT (Ctl_8, DESC_CTL_HOSTDONE);
++ SET_BIT (Ctl_8, DESC_CTL_RECLAIM);
++ write_slavemem8 (adev, (u32) &rxdesc->Ctl_8, Ctl_8);
++
++ /*
++ * Now tell the ACX we've finished with the receive buffer so
++ * it can finish the reclaim.
++ */
++ write_reg16 (adev, IO_ACX_INT_TRIG, INT_TRIG_RXPRC);
++
++ /* ok, descriptor is handled, now check the next descriptor */
++ hostdesc = &adev->rxhostdesc_start[tail];
++ rxdesc = &adev->rxdesc_start[tail];
++
++ Ctl_8 = hostdesc->Ctl_16 = read_slavemem8 (adev, (u32) &(rxdesc->Ctl_8));
++
++ /* if next descriptor is empty, then bail out */
++ if (!(Ctl_8 & DESC_CTL_HOSTOWN) || !(Ctl_8 & DESC_CTL_ACXDONE))
++ break;
++
++ tail = (tail + 1) % RX_CNT;
++ }
++end:
++ adev->rx_tail = tail;
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acxmem_i_interrupt
++**
++** IRQ handler (atomic context, must not sleep, blah, blah)
++*/
++
++/* scan is complete. all frames now on the receive queue are valid */
++#define INFO_SCAN_COMPLETE 0x0001
++#define INFO_WEP_KEY_NOT_FOUND 0x0002
++/* hw has been reset as the result of a watchdog timer timeout */
++#define INFO_WATCH_DOG_RESET 0x0003
++/* failed to send out NULL frame from PS mode notification to AP */
++/* recommended action: try entering 802.11 PS mode again */
++#define INFO_PS_FAIL 0x0004
++/* encryption/decryption process on a packet failed */
++#define INFO_IV_ICV_FAILURE 0x0005
++
++/* Info mailbox format:
++2 bytes: type
++2 bytes: status
++more bytes may follow
++ rumors say about status:
++ 0x0000 info available (set by hw)
++ 0x0001 information received (must be set by host)
++ 0x1000 info available, mailbox overflowed (messages lost) (set by hw)
++ but in practice we've seen:
++ 0x9000 when we did not set status to 0x0001 on prev message
++ 0x1001 when we did set it
++ 0x0000 was never seen
++ conclusion: this is really a bitfield:
++ 0x1000 is 'info available' bit
++ 'mailbox overflowed' bit is 0x8000, not 0x1000
++ value of 0x0000 probably means that there are no messages at all
++ P.S. I dunno how in hell hw is supposed to notice that messages are lost -
++ it does NOT clear bit 0x0001, and this bit will probably stay forever set
++ after we set it once. Let's hope this will be fixed in firmware someday
++*/
++
++static void
++handle_info_irq(acx_device_t *adev)
++{
++#if ACX_DEBUG
++ static const char * const info_type_msg[] = {
++ "(unknown)",
++ "scan complete",
++ "WEP key not found",
++ "internal watchdog reset was done",
++ "failed to send powersave (NULL frame) notification to AP",
++ "encrypt/decrypt on a packet has failed",
++ "TKIP tx keys disabled",
++ "TKIP rx keys disabled",
++ "TKIP rx: key ID not found",
++ "???",
++ "???",
++ "???",
++ "???",
++ "???",
++ "???",
++ "???",
++ "TKIP IV value exceeds thresh"
++ };
++#endif
++ u32 info_type, info_status;
++
++ info_type = read_slavemem32 (adev, (u32) adev->info_area);
++
++ info_status = (info_type >> 16);
++ info_type = (u16)info_type;
++
++ /* inform fw that we have read this info message */
++ write_slavemem32(adev, (u32) adev->info_area, info_type | 0x00010000);
++ write_reg16(adev, IO_ACX_INT_TRIG, INT_TRIG_INFOACK);
++ write_flush(adev);
++
++ log(L_CTL, "info_type:%04X info_status:%04X\n",
++ info_type, info_status);
++
++ log(L_IRQ, "got Info IRQ: status %04X type %04X: %s\n",
++ info_status, info_type,
++ info_type_msg[(info_type >= VEC_SIZE(info_type_msg)) ?
++ 0 : info_type]
++ );
++}
++
++
++static void
++log_unusual_irq(u16 irqtype) {
++ /*
++ if (!printk_ratelimit())
++ return;
++ */
++
++ printk("acx: got");
++ if (irqtype & HOST_INT_TX_XFER) {
++ printk(" Tx_Xfer");
++ }
++ if (irqtype & HOST_INT_RX_COMPLETE) {
++ printk(" Rx_Complete");
++ }
++ if (irqtype & HOST_INT_DTIM) {
++ printk(" DTIM");
++ }
++ if (irqtype & HOST_INT_BEACON) {
++ printk(" Beacon");
++ }
++ if (irqtype & HOST_INT_TIMER) {
++ log(L_IRQ, " Timer");
++ }
++ if (irqtype & HOST_INT_KEY_NOT_FOUND) {
++ printk(" Key_Not_Found");
++ }
++ if (irqtype & HOST_INT_IV_ICV_FAILURE) {
++ printk(" IV_ICV_Failure (crypto)");
++ }
++ /* HOST_INT_CMD_COMPLETE */
++ /* HOST_INT_INFO */
++ if (irqtype & HOST_INT_OVERFLOW) {
++ printk(" Overflow");
++ }
++ if (irqtype & HOST_INT_PROCESS_ERROR) {
++ printk(" Process_Error");
++ }
++ /* HOST_INT_SCAN_COMPLETE */
++ if (irqtype & HOST_INT_FCS_THRESHOLD) {
++ printk(" FCS_Threshold");
++ }
++ if (irqtype & HOST_INT_UNKNOWN) {
++ printk(" Unknown");
++ }
++ printk(" IRQ(s)\n");
++}
++
++
++static void
++update_link_quality_led(acx_device_t *adev)
++{
++ int qual;
++
++ qual = acx_signal_determine_quality(adev->wstats.qual.level, adev->wstats.qual.noise);
++ if (qual > adev->brange_max_quality)
++ qual = adev->brange_max_quality;
++
++ if (time_after(jiffies, adev->brange_time_last_state_change +
++ (HZ/2 - HZ/2 * (unsigned long)qual / adev->brange_max_quality ) )) {
++ acxmem_l_power_led(adev, (adev->brange_last_state == 0));
++ adev->brange_last_state ^= 1; /* toggle */
++ adev->brange_time_last_state_change = jiffies;
++ }
++}
++
++
++#define MAX_IRQLOOPS_PER_JIFFY (20000/HZ) /* a la orinoco.c */
++
++static irqreturn_t
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
++acxmem_i_interrupt(int irq, void *dev_id)
++#else
++acxmwm_i_interrupt(int irq, void *dev_id, struct pt_regs *regs)
++#endif
++{
++ acx_device_t *adev;
++ unsigned long flags;
++ unsigned int irqcount = MAX_IRQLOOPS_PER_JIFFY;
++ register u16 irqtype;
++ u16 unmasked;
++
++ adev = ndev2adev((struct net_device*)dev_id);
++
++ /* LOCKING: can just spin_lock() since IRQs are disabled anyway.
++ * I am paranoid */
++ acx_lock(adev, flags);
++
++ unmasked = read_reg16(adev, IO_ACX_IRQ_STATUS_CLEAR);
++ if (unlikely(0xffff == unmasked)) {
++ /* 0xffff value hints at missing hardware,
++ * so don't do anything.
++ * Not very clean, but other drivers do the same... */
++ log(L_IRQ, "IRQ type:FFFF - device removed? IRQ_NONE\n");
++ goto none;
++ }
++
++ /* We will check only "interesting" IRQ types */
++ irqtype = unmasked & ~adev->irq_mask;
++ if (!irqtype) {
++ /* We are on a shared IRQ line and it wasn't our IRQ */
++ log(L_IRQ, "IRQ type:%04X, mask:%04X - all are masked, IRQ_NONE\n",
++ unmasked, adev->irq_mask);
++ goto none;
++ }
++
++ /* Done here because IRQ_NONEs taking three lines of log
++ ** drive me crazy */
++ FN_ENTER;
++
++#define IRQ_ITERATE 1
++#if IRQ_ITERATE
++if (jiffies != adev->irq_last_jiffies) {
++ adev->irq_loops_this_jiffy = 0;
++ adev->irq_last_jiffies = jiffies;
++}
++
++/* safety condition; we'll normally abort loop below
++ * in case no IRQ type occurred */
++while (likely(--irqcount)) {
++#endif
++ /* ACK all IRQs ASAP */
++ write_reg16(adev, IO_ACX_IRQ_ACK, 0xffff);
++
++ log(L_IRQ, "IRQ type:%04X, mask:%04X, type & ~mask:%04X\n",
++ unmasked, adev->irq_mask, irqtype);
++
++ /* Handle most important IRQ types first */
++ if (irqtype & HOST_INT_RX_DATA) {
++ log(L_IRQ, "got Rx_Data IRQ\n");
++ acxmem_l_process_rxdesc(adev);
++ }
++ if (irqtype & HOST_INT_TX_COMPLETE) {
++ log(L_IRQ, "got Tx_Complete IRQ\n");
++ /* don't clean up on each Tx complete, wait a bit
++ * unless we're going towards full, in which case
++ * we do it immediately, too (otherwise we might lockup
++ * with a full Tx buffer if we go into
++ * acxmem_l_clean_txdesc() at a time when we won't wakeup
++ * the net queue in there for some reason...) */
++ if (adev->tx_free <= TX_START_CLEAN) {
++#if TX_CLEANUP_IN_SOFTIRQ
++ acx_schedule_task(adev, ACX_AFTER_IRQ_TX_CLEANUP);
++#else
++ acxmem_l_clean_txdesc(adev);
++#endif
++ }
++ }
++
++ /* Less frequent ones */
++ if (irqtype & (0
++ | HOST_INT_CMD_COMPLETE
++ | HOST_INT_INFO
++ | HOST_INT_SCAN_COMPLETE
++ )) {
++ if (irqtype & HOST_INT_CMD_COMPLETE) {
++ log(L_IRQ, "got Command_Complete IRQ\n");
++ /* save the state for the running issue_cmd() */
++ SET_BIT(adev->irq_status, HOST_INT_CMD_COMPLETE);
++ }
++ if (irqtype & HOST_INT_INFO) {
++ handle_info_irq(adev);
++ }
++ if (irqtype & HOST_INT_SCAN_COMPLETE) {
++ log(L_IRQ, "got Scan_Complete IRQ\n");
++ /* need to do that in process context */
++ acx_schedule_task(adev, ACX_AFTER_IRQ_COMPLETE_SCAN);
++ /* remember that fw is not scanning anymore */
++ SET_BIT(adev->irq_status, HOST_INT_SCAN_COMPLETE);
++ }
++ }
++
++ /* These we just log, but either they happen rarely
++ * or we keep them masked out */
++ if (irqtype & (0
++ /* | HOST_INT_RX_DATA */
++ /* | HOST_INT_TX_COMPLETE */
++ | HOST_INT_TX_XFER
++ | HOST_INT_RX_COMPLETE
++ | HOST_INT_DTIM
++ | HOST_INT_BEACON
++ | HOST_INT_TIMER
++ | HOST_INT_KEY_NOT_FOUND
++ | HOST_INT_IV_ICV_FAILURE
++ /* | HOST_INT_CMD_COMPLETE */
++ /* | HOST_INT_INFO */
++ | HOST_INT_OVERFLOW
++ | HOST_INT_PROCESS_ERROR
++ /* | HOST_INT_SCAN_COMPLETE */
++ | HOST_INT_FCS_THRESHOLD
++ | HOST_INT_UNKNOWN
++ )) {
++ log_unusual_irq(irqtype);
++ }
++
++#if IRQ_ITERATE
++ unmasked = read_reg16(adev, IO_ACX_IRQ_STATUS_CLEAR);
++ irqtype = unmasked & ~adev->irq_mask;
++ /* Bail out if no new IRQ bits or if all are masked out */
++ if (!irqtype)
++ break;
++
++ if (unlikely(++adev->irq_loops_this_jiffy > MAX_IRQLOOPS_PER_JIFFY)) {
++ printk(KERN_ERR "acx: too many interrupts per jiffy!\n");
++ /* Looks like card floods us with IRQs! Try to stop that */
++ write_reg16(adev, IO_ACX_IRQ_MASK, 0xffff);
++ /* This will short-circuit all future attempts to handle IRQ.
++ * We cant do much more... */
++ adev->irq_mask = 0;
++ break;
++ }
++}
++#endif
++ /* Routine to perform blink with range */
++ if (unlikely(adev->led_power == 2))
++ update_link_quality_led(adev);
++
++/* handled: */
++ /* write_flush(adev); - not needed, last op was read anyway */
++ acx_unlock(adev, flags);
++ FN_EXIT0;
++ return IRQ_HANDLED;
++
++none:
++ acx_unlock(adev, flags);
++ return IRQ_NONE;
++}
++
++
++/***********************************************************************
++** acxmem_l_power_led
++*/
++void
++acxmem_l_power_led(acx_device_t *adev, int enable)
++{
++ u16 gpio_pled = IS_ACX111(adev) ? 0x0040 : 0x0800;
++
++ /* A hack. Not moving message rate limiting to adev->xxx
++ * (it's only a debug message after all) */
++ static int rate_limit = 0;
++
++ if (rate_limit++ < 3)
++ log(L_IOCTL, "Please report in case toggling the power "
++ "LED doesn't work for your card!\n");
++ if (enable)
++ write_reg16(adev, IO_ACX_GPIO_OUT,
++ read_reg16(adev, IO_ACX_GPIO_OUT) & ~gpio_pled);
++ else
++ write_reg16(adev, IO_ACX_GPIO_OUT,
++ read_reg16(adev, IO_ACX_GPIO_OUT) | gpio_pled);
++}
++
++
++/***********************************************************************
++** Ioctls
++*/
++
++/***********************************************************************
++*/
++int
++acx111pci_ioctl_info(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++#if ACX_DEBUG > 1
++ acx_device_t *adev = ndev2adev(ndev);
++ rxdesc_t *rxdesc;
++ txdesc_t *txdesc;
++ rxhostdesc_t *rxhostdesc;
++ txhostdesc_t *txhostdesc;
++ struct acx111_ie_memoryconfig memconf;
++ struct acx111_ie_queueconfig queueconf;
++ unsigned long flags;
++ int i;
++ char memmap[0x34];
++ char rxconfig[0x8];
++ char fcserror[0x8];
++ char ratefallback[0x5];
++
++ if ( !(acx_debug & (L_IOCTL|L_DEBUG)) )
++ return OK;
++ /* using printk() since we checked debug flag already */
++
++ acx_sem_lock(adev);
++
++ if (!IS_ACX111(adev)) {
++ printk("acx111-specific function called "
++ "with non-acx111 chip, aborting\n");
++ goto end_ok;
++ }
++
++ /* get Acx111 Memory Configuration */
++ memset(&memconf, 0, sizeof(memconf));
++ /* BTW, fails with 12 (Write only) error code.
++ ** Retained for easy testing of issue_cmd error handling :) */
++ printk ("Interrogating queue config\n");
++ acx_s_interrogate(adev, &memconf, ACX1xx_IE_QUEUE_CONFIG);
++ printk ("done with queue config\n");
++
++ /* get Acx111 Queue Configuration */
++ memset(&queueconf, 0, sizeof(queueconf));
++ printk ("Interrogating mem config options\n");
++ acx_s_interrogate(adev, &queueconf, ACX1xx_IE_MEMORY_CONFIG_OPTIONS);
++ printk ("done with mem config options\n");
++
++ /* get Acx111 Memory Map */
++ memset(memmap, 0, sizeof(memmap));
++ printk ("Interrogating mem map\n");
++ acx_s_interrogate(adev, &memmap, ACX1xx_IE_MEMORY_MAP);
++ printk ("done with mem map\n");
++
++ /* get Acx111 Rx Config */
++ memset(rxconfig, 0, sizeof(rxconfig));
++ printk ("Interrogating rxconfig\n");
++ acx_s_interrogate(adev, &rxconfig, ACX1xx_IE_RXCONFIG);
++ printk ("done with queue rxconfig\n");
++
++ /* get Acx111 fcs error count */
++ memset(fcserror, 0, sizeof(fcserror));
++ printk ("Interrogating fcs err count\n");
++ acx_s_interrogate(adev, &fcserror, ACX1xx_IE_FCS_ERROR_COUNT);
++ printk ("done with err count\n");
++
++ /* get Acx111 rate fallback */
++ memset(ratefallback, 0, sizeof(ratefallback));
++ printk ("Interrogating rate fallback\n");
++ acx_s_interrogate(adev, &ratefallback, ACX1xx_IE_RATE_FALLBACK);
++ printk ("done with rate fallback\n");
++
++ /* force occurrence of a beacon interrupt */
++ /* TODO: comment why is this necessary */
++ write_reg16(adev, IO_ACX_HINT_TRIG, HOST_INT_BEACON);
++
++ /* dump Acx111 Mem Configuration */
++ printk("dump mem config:\n"
++ "data read: %d, struct size: %d\n"
++ "Number of stations: %1X\n"
++ "Memory block size: %1X\n"
++ "tx/rx memory block allocation: %1X\n"
++ "count rx: %X / tx: %X queues\n"
++ "options %1X\n"
++ "fragmentation %1X\n"
++ "Rx Queue 1 Count Descriptors: %X\n"
++ "Rx Queue 1 Host Memory Start: %X\n"
++ "Tx Queue 1 Count Descriptors: %X\n"
++ "Tx Queue 1 Attributes: %X\n",
++ memconf.len, (int) sizeof(memconf),
++ memconf.no_of_stations,
++ memconf.memory_block_size,
++ memconf.tx_rx_memory_block_allocation,
++ memconf.count_rx_queues, memconf.count_tx_queues,
++ memconf.options,
++ memconf.fragmentation,
++ memconf.rx_queue1_count_descs,
++ acx2cpu(memconf.rx_queue1_host_rx_start),
++ memconf.tx_queue1_count_descs,
++ memconf.tx_queue1_attributes);
++
++ /* dump Acx111 Queue Configuration */
++ printk("dump queue head:\n"
++ "data read: %d, struct size: %d\n"
++ "tx_memory_block_address (from card): %X\n"
++ "rx_memory_block_address (from card): %X\n"
++ "rx1_queue address (from card): %X\n"
++ "tx1_queue address (from card): %X\n"
++ "tx1_queue attributes (from card): %X\n",
++ queueconf.len, (int) sizeof(queueconf),
++ queueconf.tx_memory_block_address,
++ queueconf.rx_memory_block_address,
++ queueconf.rx1_queue_address,
++ queueconf.tx1_queue_address,
++ queueconf.tx1_attributes);
++
++ /* dump Acx111 Mem Map */
++ printk("dump mem map:\n"
++ "data read: %d, struct size: %d\n"
++ "Code start: %X\n"
++ "Code end: %X\n"
++ "WEP default key start: %X\n"
++ "WEP default key end: %X\n"
++ "STA table start: %X\n"
++ "STA table end: %X\n"
++ "Packet template start: %X\n"
++ "Packet template end: %X\n"
++ "Queue memory start: %X\n"
++ "Queue memory end: %X\n"
++ "Packet memory pool start: %X\n"
++ "Packet memory pool end: %X\n"
++ "iobase: %p\n"
++ "iobase2: %p\n",
++ *((u16 *)&memmap[0x02]), (int) sizeof(memmap),
++ *((u32 *)&memmap[0x04]),
++ *((u32 *)&memmap[0x08]),
++ *((u32 *)&memmap[0x0C]),
++ *((u32 *)&memmap[0x10]),
++ *((u32 *)&memmap[0x14]),
++ *((u32 *)&memmap[0x18]),
++ *((u32 *)&memmap[0x1C]),
++ *((u32 *)&memmap[0x20]),
++ *((u32 *)&memmap[0x24]),
++ *((u32 *)&memmap[0x28]),
++ *((u32 *)&memmap[0x2C]),
++ *((u32 *)&memmap[0x30]),
++ adev->iobase,
++ adev->iobase2);
++
++ /* dump Acx111 Rx Config */
++ printk("dump rx config:\n"
++ "data read: %d, struct size: %d\n"
++ "rx config: %X\n"
++ "rx filter config: %X\n",
++ *((u16 *)&rxconfig[0x02]), (int) sizeof(rxconfig),
++ *((u16 *)&rxconfig[0x04]),
++ *((u16 *)&rxconfig[0x06]));
++
++ /* dump Acx111 fcs error */
++ printk("dump fcserror:\n"
++ "data read: %d, struct size: %d\n"
++ "fcserrors: %X\n",
++ *((u16 *)&fcserror[0x02]), (int) sizeof(fcserror),
++ *((u32 *)&fcserror[0x04]));
++
++ /* dump Acx111 rate fallback */
++ printk("dump rate fallback:\n"
++ "data read: %d, struct size: %d\n"
++ "ratefallback: %X\n",
++ *((u16 *)&ratefallback[0x02]), (int) sizeof(ratefallback),
++ *((u8 *)&ratefallback[0x04]));
++
++ /* protect against IRQ */
++ acx_lock(adev, flags);
++
++ /* dump acx111 internal rx descriptor ring buffer */
++ rxdesc = adev->rxdesc_start;
++
++ /* loop over complete receive pool */
++ if (rxdesc) for (i = 0; i < RX_CNT; i++) {
++ printk("\ndump internal rxdesc %d:\n"
++ "mem pos %p\n"
++ "next 0x%X\n"
++ "acx mem pointer (dynamic) 0x%X\n"
++ "CTL (dynamic) 0x%X\n"
++ "Rate (dynamic) 0x%X\n"
++ "RxStatus (dynamic) 0x%X\n"
++ "Mod/Pre (dynamic) 0x%X\n",
++ i,
++ rxdesc,
++ acx2cpu(rxdesc->pNextDesc),
++ acx2cpu(rxdesc->ACXMemPtr),
++ rxdesc->Ctl_8,
++ rxdesc->rate,
++ rxdesc->error,
++ rxdesc->SNR);
++ rxdesc++;
++ }
++
++ /* dump host rx descriptor ring buffer */
++
++ rxhostdesc = adev->rxhostdesc_start;
++
++ /* loop over complete receive pool */
++ if (rxhostdesc) for (i = 0; i < RX_CNT; i++) {
++ printk("\ndump host rxdesc %d:\n"
++ "mem pos %p\n"
++ "buffer mem pos 0x%X\n"
++ "buffer mem offset 0x%X\n"
++ "CTL 0x%X\n"
++ "Length 0x%X\n"
++ "next 0x%X\n"
++ "Status 0x%X\n",
++ i,
++ rxhostdesc,
++ acx2cpu(rxhostdesc->data_phy),
++ rxhostdesc->data_offset,
++ le16_to_cpu(rxhostdesc->Ctl_16),
++ le16_to_cpu(rxhostdesc->length),
++ acx2cpu(rxhostdesc->desc_phy_next),
++ rxhostdesc->Status);
++ rxhostdesc++;
++ }
++
++ /* dump acx111 internal tx descriptor ring buffer */
++ txdesc = adev->txdesc_start;
++
++ /* loop over complete transmit pool */
++ if (txdesc) for (i = 0; i < TX_CNT; i++) {
++ printk("\ndump internal txdesc %d:\n"
++ "size 0x%X\n"
++ "mem pos %p\n"
++ "next 0x%X\n"
++ "acx mem pointer (dynamic) 0x%X\n"
++ "host mem pointer (dynamic) 0x%X\n"
++ "length (dynamic) 0x%X\n"
++ "CTL (dynamic) 0x%X\n"
++ "CTL2 (dynamic) 0x%X\n"
++ "Status (dynamic) 0x%X\n"
++ "Rate (dynamic) 0x%X\n",
++ i,
++ (int) sizeof(struct txdesc),
++ txdesc,
++ acx2cpu(txdesc->pNextDesc),
++ acx2cpu(txdesc->AcxMemPtr),
++ acx2cpu(txdesc->HostMemPtr),
++ le16_to_cpu(txdesc->total_length),
++ txdesc->Ctl_8,
++ txdesc->Ctl2_8, txdesc->error,
++ txdesc->u.r1.rate);
++ txdesc = advance_txdesc(adev, txdesc, 1);
++ }
++
++ /* dump host tx descriptor ring buffer */
++
++ txhostdesc = adev->txhostdesc_start;
++
++ /* loop over complete host send pool */
++ if (txhostdesc) for (i = 0; i < TX_CNT * 2; i++) {
++ printk("\ndump host txdesc %d:\n"
++ "mem pos %p\n"
++ "buffer mem pos 0x%X\n"
++ "buffer mem offset 0x%X\n"
++ "CTL 0x%X\n"
++ "Length 0x%X\n"
++ "next 0x%X\n"
++ "Status 0x%X\n",
++ i,
++ txhostdesc,
++ acx2cpu(txhostdesc->data_phy),
++ txhostdesc->data_offset,
++ le16_to_cpu(txhostdesc->Ctl_16),
++ le16_to_cpu(txhostdesc->length),
++ acx2cpu(txhostdesc->desc_phy_next),
++ le32_to_cpu(txhostdesc->Status));
++ txhostdesc++;
++ }
++
++ /* write_reg16(adev, 0xb4, 0x4); */
++
++ acx_unlock(adev, flags);
++end_ok:
++
++ acx_sem_unlock(adev);
++#endif /* ACX_DEBUG */
++ return OK;
++}
++
++
++/***********************************************************************
++*/
++int
++acx100mem_ioctl_set_phy_amp_bias(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ unsigned long flags;
++ u16 gpio_old;
++
++ if (!IS_ACX100(adev)) {
++ /* WARNING!!!
++ * Removing this check *might* damage
++ * hardware, since we're tweaking GPIOs here after all!!!
++ * You've been warned...
++ * WARNING!!! */
++ printk("acx: sorry, setting bias level for non-acx100 "
++ "is not supported yet\n");
++ return OK;
++ }
++
++ if (*extra > 7) {
++ printk("acx: invalid bias parameter, range is 0-7\n");
++ return -EINVAL;
++ }
++
++ acx_sem_lock(adev);
++
++ /* Need to lock accesses to [IO_ACX_GPIO_OUT]:
++ * IRQ handler uses it to update LED */
++ acx_lock(adev, flags);
++ gpio_old = read_reg16(adev, IO_ACX_GPIO_OUT);
++ write_reg16(adev, IO_ACX_GPIO_OUT, (gpio_old & 0xf8ff) | ((u16)*extra << 8));
++ acx_unlock(adev, flags);
++
++ log(L_DEBUG, "gpio_old: 0x%04X\n", gpio_old);
++ printk("%s: PHY power amplifier bias: old:%d, new:%d\n",
++ ndev->name,
++ (gpio_old & 0x0700) >> 8, (unsigned char)*extra);
++
++ acx_sem_unlock(adev);
++
++ return OK;
++}
++
++/***************************************************************
++** acxmem_l_alloc_tx
++** Actually returns a txdesc_t* ptr
++**
++** FIXME: in case of fragments, should allocate multiple descrs
++** after figuring out how many we need and whether we still have
++** sufficiently many.
++*/
++tx_t*
++acxmem_l_alloc_tx(acx_device_t *adev)
++{
++ struct txdesc *txdesc;
++ unsigned head;
++ u8 ctl8;
++ static int txattempts = 0;
++
++ FN_ENTER;
++
++ if (unlikely(!adev->tx_free)) {
++ printk("acx: BUG: no free txdesc left\n");
++ /*
++ * Probably the ACX ignored a transmit attempt and now there's a packet
++ * sitting in the queue we think should be transmitting but the ACX doesn't
++ * know about.
++ * On the first pass, send the ACX a TxProc interrupt to try moving
++ * things along, and if that doesn't work (ie, we get called again) completely
++ * flush the transmit queue.
++ */
++ if (txattempts < 10) {
++ txattempts++;
++ printk ("acx: trying to wake up ACX\n");
++ write_reg16(adev, IO_ACX_INT_TRIG, INT_TRIG_TXPRC);
++ write_flush(adev); }
++ else {
++ txattempts = 0;
++ printk ("acx: flushing transmit queue.\n");
++ acxmem_l_clean_txdesc_emergency (adev);
++ }
++ txdesc = NULL;
++ goto end;
++ }
++
++ /*
++ * Make a quick check to see if there is transmit buffer space on
++ * the ACX. This can't guarantee there is enough space for the packet
++ * since we don't yet know how big it is, but it will prevent at least some
++ * annoyances.
++ */
++ if (!adev->acx_txbuf_blocks_free) {
++ txdesc = NULL;
++ goto end;
++ }
++
++ head = adev->tx_head;
++ /*
++ * txdesc points to ACX memory
++ */
++ txdesc = get_txdesc(adev, head);
++ ctl8 = read_slavemem8 (adev, (u32) &(txdesc->Ctl_8));
++
++ /*
++ * If we don't own the buffer (HOSTOWN) it is certainly not free; however,
++ * we may have previously thought we had enough memory to send
++ * a packet, allocated the buffer then gave up when we found not enough
++ * transmit buffer space on the ACX. In that case, HOSTOWN and
++ * ACXDONE will both be set.
++ */
++ if (unlikely(DESC_CTL_HOSTOWN != (ctl8 & DESC_CTL_HOSTOWN))) {
++ /* whoops, descr at current index is not free, so probably
++ * ring buffer already full */
++ printk("acx: BUG: tx_head:%d Ctl8:0x%02X - failed to find "
++ "free txdesc\n", head, ctl8);
++ txdesc = NULL;
++ goto end;
++ }
++
++ /* Needed in case txdesc won't be eventually submitted for tx */
++ write_slavemem8 (adev, (u32) &(txdesc->Ctl_8), DESC_CTL_ACXDONE_HOSTOWN);
++
++ adev->tx_free--;
++ log(L_BUFT, "tx: got desc %u, %u remain\n",
++ head, adev->tx_free);
++ /* Keep a few free descs between head and tail of tx ring.
++ ** It is not absolutely needed, just feels safer */
++ if (adev->tx_free < TX_STOP_QUEUE) {
++ log(L_BUF, "stop queue (%u tx desc left)\n",
++ adev->tx_free);
++ acx_stop_queue(adev->ndev, NULL);
++ }
++
++ /* returning current descriptor, so advance to next free one */
++ adev->tx_head = (head + 1) % TX_CNT;
++end:
++ FN_EXIT0;
++
++ return (tx_t*)txdesc;
++}
++
++
++/***************************************************************
++** acxmem_l_dealloc_tx
++** Clears out a previously allocatedvoid acxmem_l_dealloc_tx(tx_t *tx_opaque);
++ transmit descriptor. The ACX
++** can get confused if we skip transmit descriptors in the queue,
++** so when we don't need a descriptor return it to its original
++** state and move the queue head pointer back.
++**
++*/
++void
++acxmem_l_dealloc_tx(acx_device_t *adev, tx_t *tx_opaque)
++{
++ /*
++ * txdesc is the address of the descriptor on the ACX.
++ */
++ txdesc_t *txdesc = (txdesc_t*)tx_opaque;
++ txdesc_t tmptxdesc;
++ int index;
++
++ memset (&tmptxdesc, 0, sizeof(tmptxdesc));
++ tmptxdesc.Ctl_8 = DESC_CTL_HOSTOWN | DESC_CTL_FIRSTFRAG;
++ tmptxdesc.u.r1.rate = 0x0a;
++
++ /*
++ * Clear out all of the transmit descriptor except for the next pointer
++ */
++ copy_to_slavemem (adev, (u32) &(txdesc->HostMemPtr),
++ (u8 *) &(tmptxdesc.HostMemPtr),
++ sizeof (tmptxdesc) - sizeof(tmptxdesc.pNextDesc));
++
++ /*
++ * This is only called immediately after we've allocated, so we should
++ * be able to set the head back to this descriptor.
++ */
++ index = ((u8*) txdesc - (u8*)adev->txdesc_start) / adev->txdesc_size;
++ printk ("acx_dealloc: moving head from %d to %d\n", adev->tx_head, index);
++ adev->tx_head = index;
++}
++
++
++/***********************************************************************
++*/
++void*
++acxmem_l_get_txbuf(acx_device_t *adev, tx_t* tx_opaque)
++{
++ return get_txhostdesc(adev, (txdesc_t*)tx_opaque)->data;
++}
++
++
++/***********************************************************************
++** acxmem_l_tx_data
++**
++** Can be called from IRQ (rx -> (AP bridging or mgmt response) -> tx).
++** Can be called from acx_i_start_xmit (data frames from net core).
++**
++** FIXME: in case of fragments, should loop over the number of
++** pre-allocated tx descrs, properly setting up transfer data and
++** CTL_xxx flags according to fragment number.
++*/
++void
++acxmem_update_queue_indicator (acx_device_t *adev, int txqueue)
++{
++#ifdef USING_MORE_THAN_ONE_TRANSMIT_QUEUE
++ u32 indicator;
++ unsigned long flags;
++ int count;
++
++ /*
++ * Can't handle an interrupt while we're fiddling with the ACX's lock,
++ * according to TI. The ACX is supposed to hold fw_lock for at most
++ * 500ns.
++ */
++ local_irq_save (flags);
++
++ /*
++ * Wait for ACX to release the lock (at most 500ns).
++ */
++ count = 0;
++ while (read_slavemem16 (adev, (u32) &(adev->acx_queue_indicator->fw_lock))
++ && (count++ < 50)) {
++ ndelay (10);
++ }
++ if (count < 50) {
++
++ /*
++ * Take out the host lock - anything non-zero will work, so don't worry about
++ * be/le
++ */
++ write_slavemem16 (adev, (u32) &(adev->acx_queue_indicator->host_lock), 1);
++
++ /*
++ * Avoid a race condition
++ */
++ count = 0;
++ while (read_slavemem16 (adev, (u32) &(adev->acx_queue_indicator->fw_lock))
++ && (count++ < 50)) {
++ ndelay (10);
++ }
++
++ if (count < 50) {
++ /*
++ * Mark the queue active
++ */
++ indicator = read_slavemem32 (adev, (u32) &(adev->acx_queue_indicator->indicator));
++ indicator |= cpu_to_le32 (1 << txqueue);
++ write_slavemem32 (adev, (u32) &(adev->acx_queue_indicator->indicator), indicator);
++ }
++
++ /*
++ * Release the host lock
++ */
++ write_slavemem16 (adev, (u32) &(adev->acx_queue_indicator->host_lock), 0);
++
++ }
++
++ /*
++ * Restore interrupts
++ */
++ local_irq_restore (flags);
++#endif
++}
++
++void
++acxmem_l_tx_data(acx_device_t *adev, tx_t* tx_opaque, int len)
++{
++ /*
++ * txdesc is the address on the ACX
++ */
++ txdesc_t *txdesc = (txdesc_t*)tx_opaque;
++ txhostdesc_t *hostdesc1, *hostdesc2;
++ client_t *clt;
++ u16 rate_cur;
++ u8 Ctl_8, Ctl2_8;
++ u32 addr;
++
++ FN_ENTER;
++ /* fw doesn't tx such packets anyhow */
++ if (unlikely(len < WLAN_HDR_A3_LEN))
++ goto end;
++
++ hostdesc1 = get_txhostdesc(adev, txdesc);
++ /* modify flag status in separate variable to be able to write it back
++ * in one big swoop later (also in order to have less device memory
++ * accesses) */
++ Ctl_8 = read_slavemem8 (adev, (u32) &(txdesc->Ctl_8));
++ Ctl2_8 = 0; /* really need to init it to 0, not txdesc->Ctl2_8, it seems */
++
++ hostdesc2 = hostdesc1 + 1;
++
++ /* DON'T simply set Ctl field to 0 here globally,
++ * it needs to maintain a consistent flag status (those are state flags!!),
++ * otherwise it may lead to severe disruption. Only set or reset particular
++ * flags at the exact moment this is needed... */
++
++ /* let chip do RTS/CTS handshaking before sending
++ * in case packet size exceeds threshold */
++ if (len > adev->rts_threshold)
++ SET_BIT(Ctl2_8, DESC_CTL2_RTS);
++ else
++ CLEAR_BIT(Ctl2_8, DESC_CTL2_RTS);
++
++ switch (adev->mode) {
++ case ACX_MODE_0_ADHOC:
++ case ACX_MODE_3_AP:
++ clt = acx_l_sta_list_get(adev, ((wlan_hdr_t*)hostdesc1->data)->a1);
++ break;
++ case ACX_MODE_2_STA:
++ clt = adev->ap_client;
++ break;
++#if 0
++/* testing was done on acx111: */
++ case ACX_MODE_MONITOR:
++ SET_BIT(Ctl2_8, 0
++/* sends CTS to self before packet */
++ + DESC_CTL2_SEQ /* don't increase sequence field */
++/* not working (looks like good fcs is still added) */
++ + DESC_CTL2_FCS /* don't add the FCS */
++/* not tested */
++ + DESC_CTL2_MORE_FRAG
++/* not tested */
++ + DESC_CTL2_RETRY /* don't increase retry field */
++/* not tested */
++ + DESC_CTL2_POWER /* don't increase power mgmt. field */
++/* no effect */
++ + DESC_CTL2_WEP /* encrypt this frame */
++/* not tested */
++ + DESC_CTL2_DUR /* don't increase duration field */
++ );
++ /* fallthrough */
++#endif
++ default: /* ACX_MODE_OFF, ACX_MODE_MONITOR */
++ clt = NULL;
++ break;
++ }
++
++ rate_cur = clt ? clt->rate_cur : adev->rate_bcast;
++ if (unlikely(!rate_cur)) {
++ printk("acx: driver bug! bad ratemask\n");
++ goto end;
++ }
++
++ /* used in tx cleanup routine for auto rate and accounting: */
++ put_txcr(adev, txdesc, clt, rate_cur);
++
++ write_slavemem16 (adev, (u32) &(txdesc->total_length), cpu_to_le16(len));
++ hostdesc2->length = cpu_to_le16(len - WLAN_HDR_A3_LEN);
++ if (IS_ACX111(adev)) {
++ /* note that if !txdesc->do_auto, txrate->cur
++ ** has only one nonzero bit */
++ txdesc->u.r2.rate111 = cpu_to_le16(
++ rate_cur
++ /* WARNING: I was never able to make it work with prism54 AP.
++ ** It was falling down to 1Mbit where shortpre is not applicable,
++ ** and not working at all at "5,11 basic rates only" setting.
++ ** I even didn't see tx packets in radio packet capture.
++ ** Disabled for now --vda */
++ /*| ((clt->shortpre && clt->cur!=RATE111_1) ? RATE111_SHORTPRE : 0) */
++ );
++#ifdef TODO_FIGURE_OUT_WHEN_TO_SET_THIS
++ /* should add this to rate111 above as necessary */
++ | (clt->pbcc511 ? RATE111_PBCC511 : 0)
++#endif
++ hostdesc1->length = cpu_to_le16(len);
++ } else { /* ACX100 */
++ u8 rate_100 = clt ? clt->rate_100 : adev->rate_bcast100;
++ write_slavemem8 (adev, (u32) &(txdesc->u.r1.rate), rate_100);
++#ifdef TODO_FIGURE_OUT_WHEN_TO_SET_THIS
++ if (clt->pbcc511) {
++ if (n == RATE100_5 || n == RATE100_11)
++ n |= RATE100_PBCC511;
++ }
++
++ if (clt->shortpre && (clt->cur != RATE111_1))
++ SET_BIT(Ctl_8, DESC_CTL_SHORT_PREAMBLE); /* set Short Preamble */
++#endif
++ /* set autodma and reclaim and 1st mpdu */
++ SET_BIT(Ctl_8, DESC_CTL_FIRSTFRAG);
++
++#if ACX_FRAGMENTATION
++ /* SET_BIT(Ctl2_8, DESC_CTL2_MORE_FRAG); cannot set it unconditionally, needs to be set for all non-last fragments */
++#endif
++ hostdesc1->length = cpu_to_le16(WLAN_HDR_A3_LEN);
++
++ /*
++ * Since we're not using autodma copy the packet data to the acx now.
++ * Even host descriptors point to the packet header, and the odd indexed
++ * descriptor following points to the packet data.
++ *
++ * The first step is to find free memory in the ACX transmit buffers.
++ * They don't necessarily map one to one with the transmit queue entries,
++ * so search through them starting just after the last one used.
++ */
++ addr = allocate_acx_txbuf_space (adev, len);
++ if (addr) {
++ chaincopy_to_slavemem (adev, addr, hostdesc1->data, len);
++ }
++ else {
++ /*
++ * Bummer. We thought we might have enough room in the transmit
++ * buffers to send this packet, but it turns out we don't. alloc_tx
++ * has already marked this transmit descriptor as HOSTOWN and ACXDONE,
++ * which means the ACX will hang when it gets to this descriptor unless
++ * we do something about it. Having a bubble in the transmit queue just
++ * doesn't seem to work, so we have to reset this transmit queue entry's
++ * state to its original value and back up our head pointer to point
++ * back to this entry.
++ */
++ hostdesc1->length = 0;
++ hostdesc2->length = 0;
++ write_slavemem16 (adev, (u32) &(txdesc->total_length), 0);
++ write_slavemem8 (adev, (u32) &(txdesc->Ctl_8), DESC_CTL_HOSTOWN | DESC_CTL_FIRSTFRAG);
++ adev->tx_head = ((u8*) txdesc - (u8*) adev->txdesc_start) / adev->txdesc_size;
++ goto end;
++ }
++ /*
++ * Tell the ACX where the packet is.
++ */
++ write_slavemem32 (adev, (u32) &(txdesc->AcxMemPtr), addr);
++
++ }
++ /* don't need to clean ack/rts statistics here, already
++ * done on descr cleanup */
++
++ /* clears HOSTOWN and ACXDONE bits, thus telling that the descriptors
++ * are now owned by the acx100; do this as LAST operation */
++ CLEAR_BIT(Ctl_8, DESC_CTL_ACXDONE_HOSTOWN);
++ /* flush writes before we release hostdesc to the adapter here */
++ //wmb();
++
++ /* write back modified flags */
++ /*
++ * At this point Ctl_8 should just be FIRSTFRAG
++ */
++ write_slavemem8 (adev, (u32) &(txdesc->Ctl2_8),Ctl2_8);
++ write_slavemem8 (adev, (u32) &(txdesc->Ctl_8), Ctl_8);
++ /* unused: txdesc->tx_time = cpu_to_le32(jiffies); */
++
++ /*
++ * Update the queue indicator to say there's data on the first queue.
++ */
++ acxmem_update_queue_indicator (adev, 0);
++
++ /* flush writes before we tell the adapter that it's its turn now */
++ mmiowb();
++ write_reg16(adev, IO_ACX_INT_TRIG, INT_TRIG_TXPRC);
++ write_flush(adev);
++
++ /* log the packet content AFTER sending it,
++ * in order to not delay sending any further than absolutely needed
++ * Do separate logs for acx100/111 to have human-readable rates */
++ if (unlikely(acx_debug & (L_XFER|L_DATA))) {
++ u16 fc = ((wlan_hdr_t*)hostdesc1->data)->fc;
++ if (IS_ACX111(adev))
++ printk("tx: pkt (%s): len %d "
++ "rate %04X%s status %u\n",
++ acx_get_packet_type_string(le16_to_cpu(fc)), len,
++ le16_to_cpu(txdesc->u.r2.rate111),
++ (le16_to_cpu(txdesc->u.r2.rate111) & RATE111_SHORTPRE) ? "(SPr)" : "",
++ adev->status);
++ else
++ printk("tx: pkt (%s): len %d rate %03u%s status %u\n",
++ acx_get_packet_type_string(fc), len,
++ read_slavemem8 (adev, (u32) &(txdesc->u.r1.rate)),
++ (Ctl_8 & DESC_CTL_SHORT_PREAMBLE) ? "(SPr)" : "",
++ adev->status);
++
++ if (acx_debug & L_DATA) {
++ printk("tx: 802.11 [%d]: ", len);
++ acx_dump_bytes(hostdesc1->data, len);
++ }
++ }
++end:
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acxmem_l_clean_txdesc
++**
++** This function resets the txdescs' status when the ACX100
++** signals the TX done IRQ (txdescs have been processed), starting with
++** the pool index of the descriptor which we would use next,
++** in order to make sure that we can be as fast as possible
++** in filling new txdescs.
++** Everytime we get called we know where the next packet to be cleaned is.
++*/
++
++#if !ACX_DEBUG
++static inline void log_txbuffer(const acx_device_t *adev) {}
++#else
++static void
++log_txbuffer(acx_device_t *adev)
++{
++ txdesc_t *txdesc;
++ int i;
++ u8 Ctl_8;
++
++ /* no FN_ENTER here, we don't want that */
++ /* no locks here, since it's entirely non-critical code */
++ txdesc = adev->txdesc_start;
++ if (unlikely(!txdesc)) return;
++ printk("tx: desc->Ctl8's:");
++ for (i = 0; i < TX_CNT; i++) {
++ Ctl_8 = read_slavemem8 (adev, (u32) &(txdesc->Ctl_8));
++ printk(" %02X", Ctl_8);
++ txdesc = advance_txdesc(adev, txdesc, 1);
++ }
++ printk("\n");
++}
++#endif
++
++
++static void
++handle_tx_error(acx_device_t *adev, u8 error, unsigned int finger)
++{
++ const char *err = "unknown error";
++
++ /* hmm, should we handle this as a mask
++ * of *several* bits?
++ * For now I think only caring about
++ * individual bits is ok... */
++ switch (error) {
++ case 0x01:
++ err = "no Tx due to error in other fragment";
++ adev->wstats.discard.fragment++;
++ break;
++ case 0x02:
++ err = "Tx aborted";
++ adev->stats.tx_aborted_errors++;
++ break;
++ case 0x04:
++ err = "Tx desc wrong parameters";
++ adev->wstats.discard.misc++;
++ break;
++ case 0x08:
++ err = "WEP key not found";
++ adev->wstats.discard.misc++;
++ break;
++ case 0x10:
++ err = "MSDU lifetime timeout? - try changing "
++ "'iwconfig retry lifetime XXX'";
++ adev->wstats.discard.misc++;
++ break;
++ case 0x20:
++ err = "excessive Tx retries due to either distance "
++ "too high or unable to Tx or Tx frame error - "
++ "try changing 'iwconfig txpower XXX' or "
++ "'sens'itivity or 'retry'";
++ adev->wstats.discard.retries++;
++ /* Tx error 0x20 also seems to occur on
++ * overheating, so I'm not sure whether we
++ * actually want to do aggressive radio recalibration,
++ * since people maybe won't notice then that their hardware
++ * is slowly getting cooked...
++ * Or is it still a safe long distance from utter
++ * radio non-functionality despite many radio recalibs
++ * to final destructive overheating of the hardware?
++ * In this case we really should do recalib here...
++ * I guess the only way to find out is to do a
++ * potentially fatal self-experiment :-\
++ * Or maybe only recalib in case we're using Tx
++ * rate auto (on errors switching to lower speed
++ * --> less heat?) or 802.11 power save mode?
++ *
++ * ok, just do it. */
++ if (++adev->retry_errors_msg_ratelimit % 4 == 0) {
++ if (adev->retry_errors_msg_ratelimit <= 20) {
++ printk("%s: several excessive Tx "
++ "retry errors occurred, attempting "
++ "to recalibrate radio. Radio "
++ "drift might be caused by increasing "
++ "card temperature, please check the card "
++ "before it's too late!\n",
++ adev->ndev->name);
++ if (adev->retry_errors_msg_ratelimit == 20)
++ printk("disabling above message\n");
++ }
++
++ acx_schedule_task(adev, ACX_AFTER_IRQ_CMD_RADIO_RECALIB);
++ }
++ break;
++ case 0x40:
++ err = "Tx buffer overflow";
++ adev->stats.tx_fifo_errors++;
++ break;
++ case 0x80:
++ err = "DMA error";
++ adev->wstats.discard.misc++;
++ break;
++ }
++ adev->stats.tx_errors++;
++ if (adev->stats.tx_errors <= 20)
++ printk("%s: tx error 0x%02X, buf %02u! (%s)\n",
++ adev->ndev->name, error, finger, err);
++ else
++ printk("%s: tx error 0x%02X, buf %02u!\n",
++ adev->ndev->name, error, finger);
++}
++
++
++unsigned int
++acxmem_l_clean_txdesc(acx_device_t *adev)
++{
++ txdesc_t *txdesc;
++ unsigned finger;
++ int num_cleaned;
++ u16 r111;
++ u8 error, ack_failures, rts_failures, rts_ok, r100, Ctl_8;
++ u32 acxmem;
++ txdesc_t tmptxdesc;
++
++ FN_ENTER;
++
++ /*
++ * Set up a template descriptor for re-initialization. The only
++ * things that get set are Ctl_8 and the rate, and the rate defaults
++ * to 1Mbps.
++ */
++ memset (&tmptxdesc, 0, sizeof (tmptxdesc));
++ tmptxdesc.Ctl_8 = DESC_CTL_HOSTOWN | DESC_CTL_FIRSTFRAG;
++ tmptxdesc.u.r1.rate = 0x0a;
++
++ if (unlikely(acx_debug & L_DEBUG))
++ log_txbuffer(adev);
++
++ log(L_BUFT, "tx: cleaning up bufs from %u\n", adev->tx_tail);
++
++ /* We know first descr which is not free yet. We advance it as far
++ ** as we see correct bits set in following descs (if next desc
++ ** is NOT free, we shouldn't advance at all). We know that in
++ ** front of tx_tail may be "holes" with isolated free descs.
++ ** We will catch up when all intermediate descs will be freed also */
++
++ finger = adev->tx_tail;
++ num_cleaned = 0;
++ while (likely(finger != adev->tx_head)) {
++ txdesc = get_txdesc(adev, finger);
++
++ /* If we allocated txdesc on tx path but then decided
++ ** to NOT use it, then it will be left as a free "bubble"
++ ** in the "allocated for tx" part of the ring.
++ ** We may meet it on the next ring pass here. */
++
++ /* stop if not marked as "tx finished" and "host owned" */
++ Ctl_8 = read_slavemem8 (adev, (u32) &(txdesc->Ctl_8));
++ if ((Ctl_8 & DESC_CTL_ACXDONE_HOSTOWN)
++ != DESC_CTL_ACXDONE_HOSTOWN) {
++ if (unlikely(!num_cleaned)) { /* maybe remove completely */
++ log(L_BUFT, "clean_txdesc: tail isn't free. "
++ "tail:%d head:%d\n",
++ adev->tx_tail, adev->tx_head);
++ }
++ break;
++ }
++
++ /* remember desc values... */
++ error = read_slavemem8 (adev, (u32) &(txdesc->error));
++ ack_failures = read_slavemem8 (adev, (u32) &(txdesc->ack_failures));
++ rts_failures = read_slavemem8 (adev, (u32) &(txdesc->u.rts.rts_failures));
++ rts_ok = read_slavemem8 (adev, (u32) &(txdesc->u.rts.rts_ok));
++ r100 = read_slavemem8 (adev, (u32) &(txdesc->u.r1.rate));
++ r111 = le16_to_cpu(read_slavemem16 (adev, (u32) &(txdesc->u.r2.rate111)));
++
++ /* need to check for certain error conditions before we
++ * clean the descriptor: we still need valid descr data here */
++ if (unlikely(0x30 & error)) {
++ /* only send IWEVTXDROP in case of retry or lifetime exceeded;
++ * all other errors mean we screwed up locally */
++ union iwreq_data wrqu;
++ wlan_hdr_t *hdr;
++ txhostdesc_t *hostdesc;
++
++ hostdesc = get_txhostdesc(adev, txdesc);
++ hdr = (wlan_hdr_t *)hostdesc->data;
++ MAC_COPY(wrqu.addr.sa_data, hdr->a1);
++ wireless_send_event(adev->ndev, IWEVTXDROP, &wrqu, NULL);
++ }
++
++ /*
++ * Free up the transmit data buffers
++ */
++ acxmem = read_slavemem32 (adev, (u32) &(txdesc->AcxMemPtr));
++ if (acxmem) {
++ reclaim_acx_txbuf_space (adev, acxmem);
++ }
++
++ /* ...and free the desc by clearing all the fields
++ except the next pointer */
++ copy_to_slavemem (adev,
++ (u32) &(txdesc->HostMemPtr),
++ (u8 *) &(tmptxdesc.HostMemPtr),
++ sizeof (tmptxdesc) - sizeof(tmptxdesc.pNextDesc)
++ );
++
++ adev->tx_free++;
++ num_cleaned++;
++
++ if ((adev->tx_free >= TX_START_QUEUE)
++ && (adev->status == ACX_STATUS_4_ASSOCIATED)
++ && (acx_queue_stopped(adev->ndev))
++ ) {
++ log(L_BUF, "tx: wake queue (avail. Tx desc %u)\n",
++ adev->tx_free);
++ acx_wake_queue(adev->ndev, NULL);
++ }
++
++ /* do error checking, rate handling and logging
++ * AFTER having done the work, it's faster */
++
++ /* do rate handling */
++ if (adev->rate_auto) {
++ struct client *clt = get_txc(adev, txdesc);
++ if (clt) {
++ u16 cur = get_txr(adev, txdesc);
++ if (clt->rate_cur == cur) {
++ acx_l_handle_txrate_auto(adev, clt,
++ cur, /* intended rate */
++ r100, r111, /* actually used rate */
++ (error & 0x30), /* was there an error? */
++ TX_CNT + TX_CLEAN_BACKLOG - adev->tx_free);
++ }
++ }
++ }
++
++ if (unlikely(error))
++ handle_tx_error(adev, error, finger);
++
++ if (IS_ACX111(adev))
++ log(L_BUFT, "tx: cleaned %u: !ACK=%u !RTS=%u RTS=%u r111=%04X\n",
++ finger, ack_failures, rts_failures, rts_ok, r111);
++ else
++ log(L_BUFT, "tx: cleaned %u: !ACK=%u !RTS=%u RTS=%u rate=%u\n",
++ finger, ack_failures, rts_failures, rts_ok, r100);
++
++ /* update pointer for descr to be cleaned next */
++ finger = (finger + 1) % TX_CNT;
++ }
++
++ /* remember last position */
++ adev->tx_tail = finger;
++/* end: */
++ FN_EXIT1(num_cleaned);
++ return num_cleaned;
++}
++
++/* clean *all* Tx descriptors, and regardless of their previous state.
++ * Used for brute-force reset handling. */
++void
++acxmem_l_clean_txdesc_emergency(acx_device_t *adev)
++{
++ txdesc_t *txdesc;
++ int i;
++ u32 acxmem;
++
++ FN_ENTER;
++
++ for (i = 0; i < TX_CNT; i++) {
++ txdesc = get_txdesc(adev, i);
++
++ /* free it */
++ write_slavemem8 (adev, (u32) &(txdesc->ack_failures), 0);
++ write_slavemem8 (adev, (u32) &(txdesc->u.rts.rts_failures), 0);
++ write_slavemem8 (adev, (u32) &(txdesc->u.rts.rts_ok), 0);
++ write_slavemem8 (adev, (u32) &(txdesc->error), 0);
++ write_slavemem8 (adev, (u32) &(txdesc->Ctl_8), DESC_CTL_HOSTOWN);
++
++ /*
++ * Clean up the memory allocated on the ACX for this transmit descriptor.
++ */
++ acxmem = read_slavemem32 (adev, (u32) &(txdesc->AcxMemPtr));
++ if (acxmem) {
++ reclaim_acx_txbuf_space (adev, acxmem);
++ }
++
++ write_slavemem32 (adev, (u32) &(txdesc->AcxMemPtr), 0);
++ }
++
++ adev->tx_free = TX_CNT;
++
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acxmem_s_create_tx_host_desc_queue
++*/
++
++static void*
++allocate(acx_device_t *adev, size_t size, dma_addr_t *phy, const char *msg)
++{
++ void *ptr;
++ ptr = kmalloc (size, GFP_KERNEL);
++ /*
++ * The ACX can't use the physical address, so we'll have to fake it
++ * later and it might be handy to have the virtual address.
++ */
++ *phy = (dma_addr_t) NULL;
++
++ if (ptr) {
++ log(L_DEBUG, "%s sz=%d adr=0x%p phy=0x%08llx\n",
++ msg, (int)size, ptr, (unsigned long long)*phy);
++ memset(ptr, 0, size);
++ return ptr;
++ }
++ printk(KERN_ERR "acx: %s allocation FAILED (%d bytes)\n",
++ msg, (int)size);
++ return NULL;
++}
++
++
++/*
++ * In the generic slave memory access mode, most of the stuff in
++ * the txhostdesc_t is unused. It's only here because the rest of
++ * the ACX driver expects it to be since the PCI version uses indirect
++ * host memory organization with DMA. Since we're not using DMA the
++ * only use we have for the host descriptors is to store the packets
++ * on the way out.
++ */
++static int
++acxmem_s_create_tx_host_desc_queue(acx_device_t *adev)
++{
++ txhostdesc_t *hostdesc;
++ u8 *txbuf;
++ int i;
++
++ FN_ENTER;
++
++ /* allocate TX buffer */
++ adev->txbuf_area_size = TX_CNT * WLAN_A4FR_MAXLEN_WEP_FCS;
++
++ adev->txbuf_start = allocate(adev, adev->txbuf_area_size,
++ &adev->txbuf_startphy, "txbuf_start");
++ if (!adev->txbuf_start)
++ goto fail;
++
++ /* allocate the TX host descriptor queue pool */
++ adev->txhostdesc_area_size = TX_CNT * 2*sizeof(*hostdesc);
++
++ adev->txhostdesc_start = allocate(adev, adev->txhostdesc_area_size,
++ &adev->txhostdesc_startphy, "txhostdesc_start");
++ if (!adev->txhostdesc_start)
++ goto fail;
++
++ /* check for proper alignment of TX host descriptor pool */
++ if ((long) adev->txhostdesc_start & 3) {
++ printk("acx: driver bug: dma alloc returns unaligned address\n");
++ goto fail;
++ }
++
++ hostdesc = adev->txhostdesc_start;
++ txbuf = adev->txbuf_start;
++
++#if 0
++/* Each tx buffer is accessed by hardware via
++** txdesc -> txhostdesc(s) -> txbuffer(s).
++** We use only one txhostdesc per txdesc, but it looks like
++** acx111 is buggy: it accesses second txhostdesc
++** (via hostdesc.desc_phy_next field) even if
++** txdesc->length == hostdesc->length and thus
++** entire packet was placed into first txhostdesc.
++** Due to this bug acx111 hangs unless second txhostdesc
++** has le16_to_cpu(hostdesc.length) = 3 (or larger)
++** Storing NULL into hostdesc.desc_phy_next
++** doesn't seem to help.
++**
++** Update: although it worked on Xterasys XN-2522g
++** with len=3 trick, WG311v2 is even more bogus, doesn't work.
++** Keeping this code (#ifdef'ed out) for documentational purposes.
++*/
++ for (i = 0; i < TX_CNT*2; i++) {
++ hostdesc_phy += sizeof(*hostdesc);
++ if (!(i & 1)) {
++ hostdesc->data_phy = cpu2acx(txbuf_phy);
++ /* hostdesc->data_offset = ... */
++ /* hostdesc->reserved = ... */
++ hostdesc->Ctl_16 = cpu_to_le16(DESC_CTL_HOSTOWN);
++ /* hostdesc->length = ... */
++ hostdesc->desc_phy_next = cpu2acx(hostdesc_phy);
++ hostdesc->pNext = ptr2acx(NULL);
++ /* hostdesc->Status = ... */
++ /* below: non-hardware fields */
++ hostdesc->data = txbuf;
++
++ txbuf += WLAN_A4FR_MAXLEN_WEP_FCS;
++ txbuf_phy += WLAN_A4FR_MAXLEN_WEP_FCS;
++ } else {
++ /* hostdesc->data_phy = ... */
++ /* hostdesc->data_offset = ... */
++ /* hostdesc->reserved = ... */
++ /* hostdesc->Ctl_16 = ... */
++ hostdesc->length = cpu_to_le16(3); /* bug workaround */
++ /* hostdesc->desc_phy_next = ... */
++ /* hostdesc->pNext = ... */
++ /* hostdesc->Status = ... */
++ /* below: non-hardware fields */
++ /* hostdesc->data = ... */
++ }
++ hostdesc++;
++ }
++#endif
++/* We initialize two hostdescs so that they point to adjacent
++** memory areas. Thus txbuf is really just a contiguous memory area */
++ for (i = 0; i < TX_CNT*2; i++) {
++ /* ->data is a non-hardware field: */
++ hostdesc->data = txbuf;
++
++ if (!(i & 1)) {
++ txbuf += WLAN_HDR_A3_LEN;
++ } else {
++ txbuf += WLAN_A4FR_MAXLEN_WEP_FCS - WLAN_HDR_A3_LEN;
++ }
++ hostdesc++;
++ }
++ hostdesc--;
++
++ FN_EXIT1(OK);
++ return OK;
++fail:
++ printk("acx: create_tx_host_desc_queue FAILED\n");
++ /* dealloc will be done by free function on error case */
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++}
++
++
++/***************************************************************
++** acxmem_s_create_rx_host_desc_queue
++*/
++/* the whole size of a data buffer (header plus data body)
++ * plus 32 bytes safety offset at the end */
++#define RX_BUFFER_SIZE (sizeof(rxbuffer_t) + 32)
++
++static int
++acxmem_s_create_rx_host_desc_queue(acx_device_t *adev)
++{
++ rxhostdesc_t *hostdesc;
++ rxbuffer_t *rxbuf;
++ int i;
++
++ FN_ENTER;
++
++ /* allocate the RX host descriptor queue pool */
++ adev->rxhostdesc_area_size = RX_CNT * sizeof(*hostdesc);
++
++ adev->rxhostdesc_start = allocate(adev, adev->rxhostdesc_area_size,
++ &adev->rxhostdesc_startphy, "rxhostdesc_start");
++ if (!adev->rxhostdesc_start)
++ goto fail;
++
++ /* check for proper alignment of RX host descriptor pool */
++ if ((long) adev->rxhostdesc_start & 3) {
++ printk("acx: driver bug: dma alloc returns unaligned address\n");
++ goto fail;
++ }
++
++ /* allocate Rx buffer pool which will be used by the acx
++ * to store the whole content of the received frames in it */
++ adev->rxbuf_area_size = RX_CNT * RX_BUFFER_SIZE;
++
++ adev->rxbuf_start = allocate(adev, adev->rxbuf_area_size,
++ &adev->rxbuf_startphy, "rxbuf_start");
++ if (!adev->rxbuf_start)
++ goto fail;
++
++ rxbuf = adev->rxbuf_start;
++ hostdesc = adev->rxhostdesc_start;
++
++ /* don't make any popular C programming pointer arithmetic mistakes
++ * here, otherwise I'll kill you...
++ * (and don't dare asking me why I'm warning you about that...) */
++ for (i = 0; i < RX_CNT; i++) {
++ hostdesc->data = rxbuf;
++ hostdesc->length = cpu_to_le16(RX_BUFFER_SIZE);
++ rxbuf++;
++ hostdesc++;
++ }
++ hostdesc--;
++ FN_EXIT1(OK);
++ return OK;
++fail:
++ printk("acx: create_rx_host_desc_queue FAILED\n");
++ /* dealloc will be done by free function on error case */
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++}
++
++
++/***************************************************************
++** acxmem_s_create_hostdesc_queues
++*/
++int
++acxmem_s_create_hostdesc_queues(acx_device_t *adev)
++{
++ int result;
++ result = acxmem_s_create_tx_host_desc_queue(adev);
++ if (OK != result) return result;
++ result = acxmem_s_create_rx_host_desc_queue(adev);
++ return result;
++}
++
++
++/***************************************************************
++** acxmem_create_tx_desc_queue
++*/
++static void
++acxmem_create_tx_desc_queue(acx_device_t *adev, u32 tx_queue_start)
++{
++ txdesc_t *txdesc;
++ u32 clr;
++ int i;
++
++ FN_ENTER;
++
++ if (IS_ACX100(adev))
++ adev->txdesc_size = sizeof(*txdesc);
++ else
++ /* the acx111 txdesc is 4 bytes larger */
++ adev->txdesc_size = sizeof(*txdesc) + 4;
++
++ /*
++ * This refers to an ACX address, not one of ours
++ */
++ adev->txdesc_start = (txdesc_t *) tx_queue_start;
++
++ log(L_DEBUG, "adev->txdesc_start=%p\n",
++ adev->txdesc_start);
++
++ adev->tx_free = TX_CNT;
++ /* done by memset: adev->tx_head = 0; */
++ /* done by memset: adev->tx_tail = 0; */
++ txdesc = adev->txdesc_start;
++
++ if (IS_ACX111(adev)) {
++ /* ACX111 has a preinitialized Tx buffer! */
++ /* loop over whole send pool */
++ /* FIXME: do we have to do the hostmemptr stuff here?? */
++ for (i = 0; i < TX_CNT; i++) {
++ txdesc->Ctl_8 = DESC_CTL_HOSTOWN;
++ /* reserve two (hdr desc and payload desc) */
++ txdesc = advance_txdesc(adev, txdesc, 1);
++ }
++ } else {
++ /* ACX100 Tx buffer needs to be initialized by us */
++ /* clear whole send pool. sizeof is safe here (we are acx100) */
++
++ /*
++ * adev->txdesc_start refers to device memory, so we can't write
++ * directly to it.
++ */
++ clr = (u32) adev->txdesc_start;
++ while (clr < (u32) adev->txdesc_start + (TX_CNT * sizeof(*txdesc))) {
++ write_slavemem32 (adev, clr, 0);
++ clr += 4;
++ }
++
++ /* loop over whole send pool */
++ for (i = 0; i < TX_CNT; i++) {
++ log(L_DEBUG, "configure card tx descriptor: 0x%p, "
++ "size: 0x%X\n", txdesc, adev->txdesc_size);
++
++ /* initialise ctl */
++ /*
++ * No auto DMA here
++ */
++ write_slavemem8 (adev, (u32) &(txdesc->Ctl_8),
++ (u8) (DESC_CTL_HOSTOWN | DESC_CTL_FIRSTFRAG));
++ /* done by memset(0): txdesc->Ctl2_8 = 0; */
++
++ /* point to next txdesc */
++ write_slavemem32 (adev, (u32) &(txdesc->pNextDesc),
++ (u32) cpu_to_le32 ((u8 *) txdesc + adev->txdesc_size));
++
++ /* go to the next one */
++ /* ++ is safe here (we are acx100) */
++ txdesc++;
++ }
++ /* go back to the last one */
++ txdesc--;
++ /* and point to the first making it a ring buffer */
++ write_slavemem32 (adev, (u32) &(txdesc->pNextDesc),
++ (u32) cpu_to_le32 (tx_queue_start));
++ }
++ FN_EXIT0;
++}
++
++
++/***************************************************************
++** acxmem_create_rx_desc_queue
++*/
++static void
++acxmem_create_rx_desc_queue(acx_device_t *adev, u32 rx_queue_start)
++{
++ rxdesc_t *rxdesc;
++ u32 mem_offs;
++ int i;
++
++ FN_ENTER;
++
++ /* done by memset: adev->rx_tail = 0; */
++
++ /* ACX111 doesn't need any further config: preconfigures itself.
++ * Simply print ring buffer for debugging */
++ if (IS_ACX111(adev)) {
++ /* rxdesc_start already set here */
++
++ adev->rxdesc_start = (rxdesc_t *) rx_queue_start;
++
++ rxdesc = adev->rxdesc_start;
++ for (i = 0; i < RX_CNT; i++) {
++ log(L_DEBUG, "rx descriptor %d @ 0x%p\n", i, rxdesc);
++ rxdesc = adev->rxdesc_start = (rxdesc_t *)
++ acx2cpu(rxdesc->pNextDesc);
++ }
++ } else {
++ /* we didn't pre-calculate rxdesc_start in case of ACX100 */
++ /* rxdesc_start should be right AFTER Tx pool */
++ adev->rxdesc_start = (rxdesc_t *)
++ ((u8 *) adev->txdesc_start + (TX_CNT * sizeof(txdesc_t)));
++ /* NB: sizeof(txdesc_t) above is valid because we know
++ ** we are in if (acx100) block. Beware of cut-n-pasting elsewhere!
++ ** acx111's txdesc is larger! */
++
++ mem_offs = (u32) adev->rxdesc_start;
++ while (mem_offs < (u32) adev->rxdesc_start + (RX_CNT * sizeof (*rxdesc))) {
++ write_slavemem32 (adev, mem_offs, 0);
++ mem_offs += 4;
++ }
++
++ /* loop over whole receive pool */
++ rxdesc = adev->rxdesc_start;
++ for (i = 0; i < RX_CNT; i++) {
++ log(L_DEBUG, "rx descriptor @ 0x%p\n", rxdesc);
++ /* point to next rxdesc */
++ write_slavemem32 (adev, (u32) &(rxdesc->pNextDesc),
++ (u32) cpu_to_le32 ((u8 *) rxdesc + sizeof(*rxdesc)));
++ /* go to the next one */
++ rxdesc++;
++ }
++ /* go to the last one */
++ rxdesc--;
++
++ /* and point to the first making it a ring buffer */
++ write_slavemem32 (adev, (u32) &(rxdesc->pNextDesc),
++ (u32) cpu_to_le32 (rx_queue_start));
++ }
++ FN_EXIT0;
++}
++
++
++/***************************************************************
++** acxmem_create_desc_queues
++*/
++void
++acxmem_create_desc_queues(acx_device_t *adev, u32 tx_queue_start, u32 rx_queue_start)
++{
++ u32 *p;
++ int i;
++
++ acxmem_create_tx_desc_queue(adev, tx_queue_start);
++ acxmem_create_rx_desc_queue(adev, rx_queue_start);
++ p = (u32 *) adev->acx_queue_indicator;
++ for (i = 0; i < 4; i++) {
++ write_slavemem32 (adev, (u32) p, 0);
++ p++;
++ }
++}
++
++
++/***************************************************************
++** acxmem_s_proc_diag_output
++*/
++char*
++acxmem_s_proc_diag_output(char *p, acx_device_t *adev)
++{
++ const char *rtl, *thd, *ttl;
++ txdesc_t *txdesc;
++ u8 Ctl_8;
++ rxdesc_t *rxdesc;
++ int i;
++ u32 tmp;
++ txdesc_t txd;
++ u8 buf[0x200];
++ int j, k;
++
++ FN_ENTER;
++
++#if DUMP_MEM_DURING_DIAG > 0
++ dump_acxmem (adev, 0, 0x10000);
++ panic ("dump finished");
++#endif
++
++ p += sprintf(p, "** Rx buf **\n");
++ rxdesc = adev->rxdesc_start;
++ if (rxdesc) for (i = 0; i < RX_CNT; i++) {
++ rtl = (i == adev->rx_tail) ? " [tail]" : "";
++ Ctl_8 = read_slavemem8 (adev, (u32) &(rxdesc->Ctl_8));
++ if (Ctl_8 & DESC_CTL_HOSTOWN)
++ p += sprintf(p, "%02u (%02x) FULL%s\n", i, Ctl_8, rtl);
++ else
++ p += sprintf(p, "%02u (%02x) empty%s\n", i, Ctl_8, rtl);
++ rxdesc++;
++ }
++ p += sprintf(p, "** Tx buf (free %d, Linux netqueue %s) **\n", adev->tx_free,
++ acx_queue_stopped(adev->ndev) ? "STOPPED" : "running");
++
++ p += sprintf(p, "** Tx buf %d blocks total, %d available, free list head %04x\n",
++ adev->acx_txbuf_numblocks, adev->acx_txbuf_blocks_free, adev->acx_txbuf_free);
++ txdesc = adev->txdesc_start;
++ if (txdesc) {
++ for (i = 0; i < TX_CNT; i++) {
++ thd = (i == adev->tx_head) ? " [head]" : "";
++ ttl = (i == adev->tx_tail) ? " [tail]" : "";
++ copy_from_slavemem (adev, (u8 *) &txd, (u32) txdesc, sizeof (txd));
++ Ctl_8 = read_slavemem8 (adev, (u32) &(txdesc->Ctl_8));
++ if (Ctl_8 & DESC_CTL_ACXDONE)
++ p += sprintf(p, "%02u ready to free (%02X)%s%s", i, Ctl_8, thd, ttl);
++ else if (Ctl_8 & DESC_CTL_HOSTOWN)
++ p += sprintf(p, "%02u available (%02X)%s%s", i, Ctl_8, thd, ttl);
++ else
++ p += sprintf(p, "%02u busy (%02X)%s%s", i, Ctl_8, thd, ttl);
++ tmp = read_slavemem32 (adev, (u32) &(txdesc->AcxMemPtr));
++ if (tmp) {
++ p += sprintf (p, " %04x", tmp);
++ while ((tmp = read_slavemem32 (adev, (u32) tmp)) != 0x02000000) {
++ tmp <<= 5;
++ p += sprintf (p, " %04x", tmp);
++ }
++ }
++ p += sprintf (p, "\n");
++ p += sprintf (p, " %04x: %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %02x %02x %02x %02x\n"
++ "%02x %02x %02x %02x %04x\n",
++ (u32) txdesc,
++ txd.pNextDesc.v, txd.HostMemPtr.v, txd.AcxMemPtr.v, txd.tx_time,
++ txd.total_length, txd.Reserved,
++ txd.dummy[0], txd.dummy[1], txd.dummy[2], txd.dummy[3],
++ txd.Ctl_8, txd.Ctl2_8, txd.error, txd.ack_failures,
++ txd.u.rts.rts_failures, txd.u.rts.rts_ok, txd.u.r1.rate, txd.u.r1.queue_ctrl,
++ txd.queue_info
++ );
++ if (txd.AcxMemPtr.v) {
++ copy_from_slavemem (adev, buf, txd.AcxMemPtr.v, sizeof (buf));
++ for (j = 0; (j < txd.total_length) && (j<(sizeof(buf)-4)); j+=16) {
++ p += sprintf (p, " ");
++ for (k = 0; (k < 16) && (j+k < txd.total_length); k++) {
++ p += sprintf (p, " %02x", buf[j+k+4]);
++ }
++ p += sprintf (p, "\n");
++ }
++ }
++ txdesc = advance_txdesc(adev, txdesc, 1);
++ }
++ }
++
++ p += sprintf(p,
++ "\n"
++ "** Generic slave data **\n"
++ "irq_mask 0x%04x irq_status 0x%04x irq on acx 0x%04x\n"
++ "txbuf_start 0x%p, txbuf_area_size %u\n"
++ "txdesc_size %u, txdesc_start 0x%p\n"
++ "txhostdesc_start 0x%p, txhostdesc_area_size %u\n"
++ "txbuf start 0x%04x, txbuf size %d\n"
++ "rxdesc_start 0x%p\n"
++ "rxhostdesc_start 0x%p, rxhostdesc_area_size %u\n"
++ "rxbuf_start 0x%p, rxbuf_area_size %u\n",
++ adev->irq_mask, adev->irq_status, read_reg32(adev, IO_ACX_IRQ_STATUS_NON_DES),
++ adev->txbuf_start, adev->txbuf_area_size,
++ adev->txdesc_size, adev->txdesc_start,
++ adev->txhostdesc_start, adev->txhostdesc_area_size,
++ adev->acx_txbuf_start, adev->acx_txbuf_numblocks * adev->memblocksize,
++ adev->rxdesc_start,
++ adev->rxhostdesc_start, adev->rxhostdesc_area_size,
++ adev->rxbuf_start, adev->rxbuf_area_size);
++ FN_EXIT0;
++ return p;
++}
++
++
++/***********************************************************************
++*/
++int
++acxmem_proc_eeprom_output(char *buf, acx_device_t *adev)
++{
++ char *p = buf;
++ int i;
++
++ FN_ENTER;
++
++ for (i = 0; i < 0x400; i++) {
++ acxmem_read_eeprom_byte(adev, i, p++);
++ }
++
++ FN_EXIT1(p - buf);
++ return p - buf;
++}
++
++
++/***********************************************************************
++*/
++void
++acxmem_set_interrupt_mask(acx_device_t *adev)
++{
++ if (IS_ACX111(adev)) {
++ adev->irq_mask = (u16) ~(0
++ | HOST_INT_RX_DATA
++ | HOST_INT_TX_COMPLETE
++ /* | HOST_INT_TX_XFER */
++ /* | HOST_INT_RX_COMPLETE */
++ /* | HOST_INT_DTIM */
++ /* | HOST_INT_BEACON */
++ /* | HOST_INT_TIMER */
++ /* | HOST_INT_KEY_NOT_FOUND */
++ | HOST_INT_IV_ICV_FAILURE
++ | HOST_INT_CMD_COMPLETE
++ | HOST_INT_INFO
++ | HOST_INT_OVERFLOW
++ /* | HOST_INT_PROCESS_ERROR */
++ | HOST_INT_SCAN_COMPLETE
++ | HOST_INT_FCS_THRESHOLD
++ | HOST_INT_UNKNOWN
++ );
++ /* Or else acx100 won't signal cmd completion, right? */
++ adev->irq_mask_off = (u16)~( HOST_INT_CMD_COMPLETE ); /* 0xfdff */
++ } else {
++ adev->irq_mask = (u16) ~(0
++ | HOST_INT_RX_DATA
++ | HOST_INT_TX_COMPLETE
++ /* | HOST_INT_TX_XFER */
++ /* | HOST_INT_RX_COMPLETE */
++ /* | HOST_INT_DTIM */
++ /* | HOST_INT_BEACON */
++ /* | HOST_INT_TIMER */
++ /* | HOST_INT_KEY_NOT_FOUND */
++ /* | HOST_INT_IV_ICV_FAILURE */
++ | HOST_INT_CMD_COMPLETE
++ | HOST_INT_INFO
++ /* | HOST_INT_OVERFLOW */
++ /* | HOST_INT_PROCESS_ERROR */
++ | HOST_INT_SCAN_COMPLETE
++ /* | HOST_INT_FCS_THRESHOLD */
++ /* | HOST_INT_BEACON_MISSED */
++ );
++ adev->irq_mask_off = (u16)~( HOST_INT_UNKNOWN ); /* 0x7fff */
++ }
++}
++
++
++/***********************************************************************
++*/
++int
++acx100mem_s_set_tx_level(acx_device_t *adev, u8 level_dbm)
++{
++ struct acx111_ie_tx_level tx_level;
++
++ /* since it can be assumed that at least the Maxim radio has a
++ * maximum power output of 20dBm and since it also can be
++ * assumed that these values drive the DAC responsible for
++ * setting the linear Tx level, I'd guess that these values
++ * should be the corresponding linear values for a dBm value,
++ * in other words: calculate the values from that formula:
++ * Y [dBm] = 10 * log (X [mW])
++ * then scale the 0..63 value range onto the 1..100mW range (0..20 dBm)
++ * and you're done...
++ * Hopefully that's ok, but you never know if we're actually
++ * right... (especially since Windows XP doesn't seem to show
++ * actual Tx dBm values :-P) */
++
++ /* NOTE: on Maxim, value 30 IS 30mW, and value 10 IS 10mW - so the
++ * values are EXACTLY mW!!! Not sure about RFMD and others,
++ * though... */
++ static const u8 dbm2val_maxim[21] = {
++ 63, 63, 63, 62,
++ 61, 61, 60, 60,
++ 59, 58, 57, 55,
++ 53, 50, 47, 43,
++ 38, 31, 23, 13,
++ 0
++ };
++ static const u8 dbm2val_rfmd[21] = {
++ 0, 0, 0, 1,
++ 2, 2, 3, 3,
++ 4, 5, 6, 8,
++ 10, 13, 16, 20,
++ 25, 32, 41, 50,
++ 63
++ };
++ const u8 *table;
++
++ switch (adev->radio_type) {
++ case RADIO_MAXIM_0D:
++ table = &dbm2val_maxim[0];
++ break;
++ case RADIO_RFMD_11:
++ case RADIO_RALINK_15:
++ table = &dbm2val_rfmd[0];
++ break;
++ default:
++ printk("%s: unknown/unsupported radio type, "
++ "cannot modify tx power level yet!\n",
++ adev->ndev->name);
++ return NOT_OK;
++ }
++ /*
++ * The hx4700 EEPROM, at least, only supports 1 power setting. The configure
++ * routine matches the PA bias with the gain, so just use its default value.
++ * The values are: 0x2b for the gain and 0x03 for the PA bias. The firmware
++ * writes the gain level to the Tx gain control DAC and the PA bias to the Maxim
++ * radio's PA bias register. The firmware limits itself to 0 - 64 when writing to the
++ * gain control DAC.
++ *
++ * Physically between the ACX and the radio, higher Tx gain control DAC values result
++ * in less power output; 0 volts to the Maxim radio results in the highest output power
++ * level, which I'm assuming matches up with 0 in the Tx Gain DAC register.
++ *
++ * Although there is only the 1 power setting, one of the radio firmware functions adjusts
++ * the transmit power level up and down. That function is called by the ACX FIQ handler
++ * under certain conditions.
++ */
++ tx_level.level = 1;
++ //return acx_s_configure(adev, &tx_level, ACX1xx_IE_DOT11_TX_POWER_LEVEL);
++
++ printk("%s: changing radio power level to %u dBm (%u)\n",
++ adev->ndev->name, level_dbm, table[level_dbm]);
++ acxmem_s_write_phy_reg(adev, 0x11, table[level_dbm]);
++
++ return 0;
++}
++
++void acxmem_e_release(struct device *dev) {
++}
++
++/***********************************************************************
++** acx_cs part
++**
++** called by pcmcia card service
++*/
++
++/*
++ The event() function is this driver's Card Services event handler.
++ It will be called by Card Services when an appropriate card status
++ event is received. The config() and release() entry points are
++ used to configure or release a socket, in response to card
++ insertion and ejection events. They are invoked from the acx_cs
++ event handler.
++*/
++
++static int acx_cs_config(struct pcmcia_device *link);
++static void acx_cs_release(struct pcmcia_device *link);
++
++/*
++ The attach() and detach() entry points are used to create and destroy
++ "instances" of the driver, where each instance represents everything
++ needed to manage one actual PCMCIA card.
++*/
++
++static void acx_cs_detach(struct pcmcia_device *p_dev);
++
++/*
++ You'll also need to prototype all the functions that will actually
++ be used to talk to your device. See 'pcmem_cs' for a good example
++ of a fully self-sufficient driver; the other drivers rely more or
++ less on other parts of the kernel.
++*/
++
++/*
++ A linked list of "instances" of the acxnet device. Each actual
++ PCMCIA card corresponds to one device instance, and is described
++ by one struct pcmcia_device structure (defined in ds.h).
++
++ You may not want to use a linked list for this -- for example, the
++ memory card driver uses an array of struct pcmcia_device pointers, where minor
++ device numbers are used to derive the corresponding array index.
++*/
++
++/*
++ A driver needs to provide a dev_node_t structure for each device
++ on a card. In some cases, there is only one device per card (for
++ example, ethernet cards, modems). In other cases, there may be
++ many actual or logical devices (SCSI adapters, memory cards with
++ multiple partitions). The dev_node_t structures need to be kept
++ in a linked list starting at the 'dev' field of a struct pcmcia_device
++ structure. We allocate them in the card's private data structure,
++ because they generally shouldn't be allocated dynamically.
++
++ In this case, we also provide a flag to indicate if a device is
++ "stopped" due to a power management event, or card ejection. The
++ device IO routines can use a flag like this to throttle IO to a
++ card that is not ready to accept it.
++*/
++
++
++/*======================================================================
++
++ acx_attach() creates an "instance" of the driver, allocating
++ local data structures for one device. The device is registered
++ with Card Services.
++
++ The dev_link structure is initialized, but we don't actually
++ configure the card at this point -- we wait until we receive a
++ card insertion event.
++
++ ======================================================================*/
++
++static int acx_cs_probe(struct pcmcia_device *link)
++{
++ local_info_t *local;
++ struct net_device *ndev;
++
++ DEBUG(0, "acx_attach()\n");
++
++ ndev = alloc_netdev(sizeof(acx_device_t), "wlan%d", dummy_netdev_init);
++ if (!ndev) {
++ printk("acx: no memory for netdevice struct\n");
++ return -ENOMEM;
++ }
++
++ /* Interrupt setup */
++ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT;
++ link->irq.IRQInfo1 = IRQ_LEVEL_ID;
++ link->irq.Handler = acxmem_i_interrupt;
++ link->irq.Instance = ndev;
++
++ /*
++ General socket configuration defaults can go here. In this
++ client, we assume very little, and rely on the CIS for almost
++ everything. In most clients, many details (i.e., number, sizes,
++ and attributes of IO windows) are fixed by the nature of the
++ device, and can be hard-wired here.
++ */
++ link->conf.Attributes = CONF_ENABLE_IRQ;
++ link->conf.IntType = INT_MEMORY_AND_IO;
++ link->conf.Present = PRESENT_OPTION | PRESENT_COPY;
++
++ /* Allocate space for private device-specific data */
++ local = kzalloc(sizeof(local_info_t), GFP_KERNEL);
++ if (!local) {
++ printk(KERN_ERR "acx_cs: no memory for new device\n");
++ return -ENOMEM;
++ }
++ local->ndev = ndev;
++
++ link->priv = local;
++
++ return acx_cs_config(link);
++} /* acx_attach */
++
++/*======================================================================
++
++ This deletes a driver "instance". The device is de-registered
++ with Card Services. If it has been released, all local data
++ structures are freed. Otherwise, the structures will be freed
++ when the device is released.
++
++ ======================================================================*/
++
++static void acx_cs_detach(struct pcmcia_device *link)
++{
++ DEBUG(0, "acx_detach(0x%p)\n", link);
++
++
++ if ( ((local_info_t*)link->priv)->ndev ) {
++ acxmem_e_close( ((local_info_t*)link->priv)->ndev );
++ }
++
++ acx_cs_release(link);
++
++ ((local_info_t*)link->priv)->ndev = NULL;
++
++ kfree(link->priv);
++} /* acx_detach */
++
++/*======================================================================
++
++ acx_config() is scheduled to run after a CARD_INSERTION event
++ is received, to configure the PCMCIA socket, and to make the
++ device available to the system.
++
++ ======================================================================*/
++
++#define CS_CHECK(fn, ret) \
++do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
++
++static int acx_cs_config(struct pcmcia_device *link)
++{
++ tuple_t tuple;
++ cisparse_t parse;
++ local_info_t *local = link->priv;
++ int last_fn, last_ret;
++ u_char buf[64];
++ win_req_t req;
++ memreq_t map;
++// int i;
++// acx_device_t *adev;
++
++// adev = (acx_device_t *)link->priv;
++
++ DEBUG(0, "acx_cs_config(0x%p)\n", link);
++
++ /*
++ In this loop, we scan the CIS for configuration table entries,
++ each of which describes a valid card configuration, including
++ voltage, IO window, memory window, and interrupt settings.
++
++ We make no assumptions about the card to be configured: we use
++ just the information available in the CIS. In an ideal world,
++ this would work for any PCMCIA card, but it requires a complete
++ and accurate CIS. In practice, a driver usually "knows" most of
++ these things without consulting the CIS, and most client drivers
++ will only use the CIS to fill in implementation-defined details.
++ */
++ tuple.Attributes = 0;
++ tuple.TupleData = (cisdata_t *)buf;
++ tuple.TupleDataMax = sizeof(buf);
++ tuple.TupleOffset = 0;
++ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY;
++
++ /* don't trust the CIS on this; Linksys got it wrong */
++ //link->conf.Present = 0x63;
++
++ CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple));
++ while (1) {
++ cistpl_cftable_entry_t dflt = { 0 };
++ cistpl_cftable_entry_t *cfg = &(parse.cftable_entry);
++ if (pcmcia_get_tuple_data(link, &tuple) != 0 ||
++ pcmcia_parse_tuple(link, &tuple, &parse) != 0)
++ goto next_entry;
++
++ if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg;
++ if (cfg->index == 0) goto next_entry;
++ link->conf.ConfigIndex = cfg->index;
++
++ /* Does this card need audio output? */
++ if (cfg->flags & CISTPL_CFTABLE_AUDIO) {
++ link->conf.Attributes |= CONF_ENABLE_SPKR;
++ link->conf.Status = CCSR_AUDIO_ENA;
++ }
++
++ /* Use power settings for Vcc and Vpp if present */
++ /* Note that the CIS values need to be rescaled */
++ if (cfg->vpp1.present & (1<<CISTPL_POWER_VNOM))
++ link->conf.Vpp =
++ cfg->vpp1.param[CISTPL_POWER_VNOM]/10000;
++ else if (dflt.vpp1.present & (1<<CISTPL_POWER_VNOM))
++ link->conf.Vpp =
++ dflt.vpp1.param[CISTPL_POWER_VNOM]/10000;
++
++ /* Do we need to allocate an interrupt? */
++ if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1)
++ link->conf.Attributes |= CONF_ENABLE_IRQ;
++ if ((cfg->mem.nwin > 0) || (dflt.mem.nwin > 0)) {
++ cistpl_mem_t *mem =
++ (cfg->mem.nwin) ? &cfg->mem : &dflt.mem;
++// req.Attributes = WIN_DATA_WIDTH_16|WIN_MEMORY_TYPE_AM|WIN_ENABLE|WIN_USE_WAIT;
++ req.Attributes = WIN_DATA_WIDTH_16|WIN_MEMORY_TYPE_CM|WIN_ENABLE|WIN_USE_WAIT;
++ req.Base = mem->win[0].host_addr;
++ req.Size = mem->win[0].len;
++ req.Size=0x1000;
++ req.AccessSpeed = 0;
++ if (pcmcia_request_window(&link, &req, &link->win) != 0)
++ goto next_entry;
++ map.Page = 0; map.CardOffset = mem->win[0].card_addr;
++ if (pcmcia_map_mem_page(link->win, &map) != 0)
++ goto next_entry;
++ else
++ printk(KERN_INFO "MEMORY WINDOW FOUND!!!\n");
++ }
++ /* If we got this far, we're cool! */
++ break;
++
++ next_entry:
++ CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple));
++ }
++
++ if (link->conf.Attributes & CONF_ENABLE_IRQ) {
++ printk(KERN_INFO "requesting Irq...\n");
++ CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq));
++ }
++
++ /*
++ This actually configures the PCMCIA socket -- setting up
++ the I/O windows and the interrupt mapping, and putting the
++ card and host interface into "Memory and IO" mode.
++ */
++ CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf));
++ DEBUG(0,"RequestConfiguration OK\n");
++
++
++ memwin.Base=req.Base;
++ memwin.Size=req.Size;
++
++ acx_init_netdev(local->ndev, &link->dev, memwin.Base, memwin.Size, link->irq.AssignedIRQ);
++
++#if 1
++ /*
++ At this point, the dev_node_t structure(s) need to be
++ initialized and arranged in a linked list at link->dev_node.
++ */
++ strcpy(local->node.dev_name, local->ndev->name );
++ local->node.major = local->node.minor = 0;
++ link->dev_node = &local->node;
++
++ /* Finally, report what we've done */
++ printk(KERN_INFO "%s: index 0x%02x: ",
++ local->ndev->name, link->conf.ConfigIndex);
++#endif
++ if (link->conf.Attributes & CONF_ENABLE_IRQ)
++ printk("irq %d", link->irq.AssignedIRQ);
++ if (link->io.NumPorts1)
++ printk(", io 0x%04x-0x%04x", link->io.BasePort1,
++ link->io.BasePort1+link->io.NumPorts1-1);
++ if (link->io.NumPorts2)
++ printk(" & 0x%04x-0x%04x", link->io.BasePort2,
++ link->io.BasePort2+link->io.NumPorts2-1);
++ if (link->win)
++ printk(", mem 0x%06lx-0x%06lx\n", req.Base,
++ req.Base+req.Size-1);
++ return 0;
++
++ cs_failed:
++ cs_error(link, last_fn, last_ret);
++ acx_cs_release(link);
++ return -ENODEV;
++} /* acx_config */
++
++/*======================================================================
++
++ After a card is removed, acx_release() will unregister the
++ device, and release the PCMCIA configuration. If the device is
++ still open, this will be postponed until it is closed.
++
++ ======================================================================*/
++
++static void acx_cs_release(struct pcmcia_device *link)
++{
++ DEBUG(0, "acx_release(0x%p)\n", link);
++ acxmem_e_remove(link);
++ pcmcia_disable_device(link);
++}
++
++static int acx_cs_suspend(struct pcmcia_device *link)
++{
++ local_info_t *local = link->priv;
++
++ pm_message_t state;
++ acxmem_e_suspend ( local->ndev, state);
++ /* Already done in suspend
++ * netif_device_detach(local->ndev); */
++
++ return 0;
++}
++
++static int acx_cs_resume(struct pcmcia_device *link)
++{
++ local_info_t *local = link->priv;
++
++ FN_ENTER;
++ resume_ndev = local->ndev;
++
++ schedule_work( &fw_resume_work );
++
++ /* Already done in suspend
++ if (link->open) {
++ // do we need reset for ACX, if so what function nane is ?
++ //reset_acx_card(local->eth_dev);
++ netif_device_attach(local->ndev);
++ } */
++
++ FN_EXIT0;
++ return 0;
++}
++
++static struct pcmcia_device_id acx_ids[] = {
++ PCMCIA_DEVICE_MANF_CARD(0x0097, 0x8402),
++ PCMCIA_DEVICE_MANF_CARD(0x0250, 0xb001),
++ PCMCIA_DEVICE_NULL,
++};
++MODULE_DEVICE_TABLE(pcmcia, acx_ids);
++
++static struct pcmcia_driver acx_driver = {
++ .owner = THIS_MODULE,
++ .drv = {
++ .name = "acx_cs",
++ },
++ .probe = acx_cs_probe,
++ .remove = acx_cs_detach,
++ .id_table = acx_ids,
++ .suspend = acx_cs_suspend,
++ .resume = acx_cs_resume,
++};
++
++int acx_cs_init(void)
++{
++ /* return success if at least one succeeded */
++ DEBUG(0, "acxcs_init()\n");
++ return pcmcia_register_driver(&acx_driver);
++}
++
++void acx_cs_cleanup(void)
++{
++ pcmcia_unregister_driver(&acx_driver);
++}
++
++/*
++ This program is free software; you can redistribute it and/or
++ modify it under the terms of the GNU General Public License
++ as published by the Free Software Foundation; either version 2
++ of the License, or (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ In addition:
++
++ Redistribution and use in source and binary forms, with or without
++ modification, are permitted provided that the following conditions
++ are met:
++
++ 1. Redistributions of source code must retain the above copyright
++ notice, this list of conditions and the following disclaimer.
++ 2. Redistributions in binary form must reproduce the above copyright
++ notice, this list of conditions and the following disclaimer in the
++ documentation and/or other materials provided with the distribution.
++ 3. The name of the author may not be used to endorse or promote
++ products derived from this software without specific prior written
++ permission.
++
++ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
++ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
++ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
++ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
++ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
++ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
++ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
++ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
++ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
++ IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
++ POSSIBILITY OF SUCH DAMAGE.
++*/
++
++MODULE_DESCRIPTION( "ACX Cardbus Driver" );
++MODULE_LICENSE( "GPL" );
++
+Index: linux-2.6.23/drivers/net/wireless/acx/htcsable_acx.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23/drivers/net/wireless/acx/htcsable_acx.c 2008-01-20 21:13:40.000000000 +0000
+@@ -0,0 +1,118 @@
++/*
++ * WLAN (TI TNETW1100B) support in the HTC Sable
++ *
++ * Copyright (c) 2006 SDG Systems, LLC
++ *
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License. See the file COPYING in the main directory of this archive for
++ * more details.
++ *
++ * 28-March-2006 Todd Blumer <todd@sdgsystems.com>
++ */
++
++
++#include <linux/kernel.h>
++#include <linux/platform_device.h>
++#include <linux/delay.h>
++
++#include <asm/hardware.h>
++
++#include <asm/arch/pxa-regs.h>
++#include <linux/mfd/asic3_base.h>
++#include <asm/arch/htcsable-gpio.h>
++#include <asm/arch/htcsable-asic.h>
++#include <asm/io.h>
++
++#include "acx_hw.h"
++
++#define WLAN_BASE PXA_CS2_PHYS
++
++/*
++off: b15 c8 d3
++on: d3 c8 b5 b5-
++*/
++
++#define GPIO_NR_HTCSABLE_ACX111 111
++
++static int
++htcsable_wlan_stop( void );
++
++static int
++htcsable_wlan_start( void )
++{
++ printk( "htcsable_wlan_start\n" );
++
++ /*asic3_set_gpio_out_c(&htcsable_asic3.dev, 1<<GPIOC_ACX_RESET, 0);*/
++ asic3_set_gpio_out_c(&htcsable_asic3.dev, 1<<GPIOC_ACX_PWR_3, 1<<GPIOC_ACX_PWR_3); /* related to acx */
++ SET_HTCSABLE_GPIO(ACX111, 1);
++ asic3_set_gpio_out_b(&htcsable_asic3.dev, 1<<GPIOB_ACX_PWR_1, 1<<GPIOB_ACX_PWR_1);
++ asic3_set_gpio_out_d(&htcsable_asic3.dev, 1<<GPIOD_ACX_PWR_2, 1<<GPIOD_ACX_PWR_2);
++ mdelay(260);
++ asic3_set_gpio_out_c(&htcsable_asic3.dev, 1<<GPIOC_ACX_RESET, 1<<GPIOC_ACX_RESET);
++
++ return 0;
++}
++
++static int
++htcsable_wlan_stop( void )
++{
++ printk( "htcsable_wlan_stop\n" );
++ asic3_set_gpio_out_b(&htcsable_asic3.dev, 1<<GPIOB_ACX_PWR_1, 0);
++ asic3_set_gpio_out_c(&htcsable_asic3.dev, 1<<GPIOC_ACX_RESET, 0);
++ asic3_set_gpio_out_d(&htcsable_asic3.dev, 1<<GPIOD_ACX_PWR_2, 0);
++ SET_HTCSABLE_GPIO(ACX111, 0); /* not necessary to power down this one? */
++ asic3_set_gpio_out_c(&htcsable_asic3.dev, 1<<GPIOC_ACX_PWR_3, 0); /* not necessary to power down this one? */
++
++ return 0;
++}
++
++static struct resource acx_resources[] = {
++ [0] = {
++ .start = WLAN_BASE,
++ .end = WLAN_BASE + 0x20,
++ .flags = IORESOURCE_MEM,
++ },
++ [1] = {
++// .start = asic3_irq_base(&htcsable_asic3.dev) + ASIC3_GPIOC_IRQ_BASE+GPIOC_WIFI_IRQ_N,
++// .end = asic3_irq_base(&htcsable_asic3.dev) + ASIC3_GPIOC_IRQ_BASE+GPIOC_WIFI_IRQ_N,
++ .flags = IORESOURCE_IRQ,
++ },
++};
++
++static struct acx_hardware_data acx_data = {
++ .start_hw = htcsable_wlan_start,
++ .stop_hw = htcsable_wlan_stop,
++};
++
++static struct platform_device acx_device = {
++ .name = "acx-mem",
++ .dev = {
++ .platform_data = &acx_data,
++ },
++ .num_resources = ARRAY_SIZE( acx_resources ),
++ .resource = acx_resources,
++};
++
++static int __init
++htcsable_wlan_init( void )
++{
++ printk( "htcsable_wlan_init: acx-mem platform_device_register\n" );
++ acx_device.resource[1].start = asic3_irq_base(&htcsable_asic3.dev) + ASIC3_GPIOB_IRQ_BASE+GPIOB_ACX_IRQ_N;
++ acx_device.resource[1].end = asic3_irq_base(&htcsable_asic3.dev) + ASIC3_GPIOB_IRQ_BASE+GPIOB_ACX_IRQ_N;
++ return platform_device_register( &acx_device );
++}
++
++
++static void __exit
++htcsable_wlan_exit( void )
++{
++ platform_device_unregister( &acx_device );
++}
++
++module_init( htcsable_wlan_init );
++module_exit( htcsable_wlan_exit );
++
++MODULE_AUTHOR( "Todd Blumer <todd@sdgsystems.com>" );
++MODULE_DESCRIPTION( "WLAN driver for HTC Sable" );
++MODULE_LICENSE( "GPL" );
++
+Index: linux-2.6.23/drivers/net/wireless/acx/htcuniversal_acx.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23/drivers/net/wireless/acx/htcuniversal_acx.c 2008-01-20 21:13:40.000000000 +0000
+@@ -0,0 +1,108 @@
++/*
++ * WLAN (TI TNETW1100B) support in the HTC Universal
++ *
++ * Copyright (c) 2006 SDG Systems, LLC
++ *
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License. See the file COPYING in the main directory of this archive for
++ * more details.
++ *
++ * 28-March-2006 Todd Blumer <todd@sdgsystems.com>
++ */
++
++
++#include <linux/kernel.h>
++#include <linux/platform_device.h>
++#include <linux/delay.h>
++
++#include <asm/hardware.h>
++
++#include <asm/arch/pxa-regs.h>
++#include <linux/soc/asic3_base.h>
++#include <asm/arch/htcuniversal-gpio.h>
++#include <asm/arch/htcuniversal-asic.h>
++#include <asm/io.h>
++
++#include "acx_hw.h"
++
++#define WLAN_BASE PXA_CS2_PHYS
++
++
++static int
++htcuniversal_wlan_start( void )
++{
++ htcuniversal_egpio_enable(1<<EGPIO6_WIFI_ON);
++ asic3_set_gpio_out_c(&htcuniversal_asic3.dev, 1<<GPIOC_WIFI_PWR1_ON, 1<<GPIOC_WIFI_PWR1_ON);
++ asic3_set_gpio_out_d(&htcuniversal_asic3.dev, 1<<GPIOD_WIFI_PWR3_ON, 1<<GPIOD_WIFI_PWR3_ON);
++ asic3_set_gpio_out_d(&htcuniversal_asic3.dev, 1<<GPIOD_WIFI_PWR2_ON, 1<<GPIOD_WIFI_PWR2_ON);
++ mdelay(100);
++
++ asic3_set_gpio_out_c(&htcuniversal_asic3.dev, 1<<GPIOC_WIFI_RESET, 0);
++ mdelay(100);
++ asic3_set_gpio_out_c(&htcuniversal_asic3.dev, 1<<GPIOC_WIFI_RESET, 1<<GPIOC_WIFI_RESET);
++ mdelay(100);
++ return 0;
++}
++
++static int
++htcuniversal_wlan_stop( void )
++{
++ asic3_set_gpio_out_c(&htcuniversal_asic3.dev, 1<<GPIOC_WIFI_RESET, 0);
++
++ htcuniversal_egpio_disable(1<<EGPIO6_WIFI_ON);
++ asic3_set_gpio_out_c(&htcuniversal_asic3.dev, 1<<GPIOC_WIFI_PWR1_ON, 0);
++ asic3_set_gpio_out_d(&htcuniversal_asic3.dev, 1<<GPIOD_WIFI_PWR2_ON, 0);
++ asic3_set_gpio_out_d(&htcuniversal_asic3.dev, 1<<GPIOD_WIFI_PWR3_ON, 0);
++ return 0;
++}
++
++static struct resource acx_resources[] = {
++ [0] = {
++ .start = WLAN_BASE,
++ .end = WLAN_BASE + 0x20,
++ .flags = IORESOURCE_MEM,
++ },
++ [1] = {
++// .start = asic3_irq_base(&htcuniversal_asic3.dev) + ASIC3_GPIOC_IRQ_BASE+GPIOC_WIFI_IRQ_N,
++// .end = asic3_irq_base(&htcuniversal_asic3.dev) + ASIC3_GPIOC_IRQ_BASE+GPIOC_WIFI_IRQ_N,
++ .flags = IORESOURCE_IRQ,
++ },
++};
++
++static struct acx_hardware_data acx_data = {
++ .start_hw = htcuniversal_wlan_start,
++ .stop_hw = htcuniversal_wlan_stop,
++};
++
++static struct platform_device acx_device = {
++ .name = "acx-mem",
++ .dev = {
++ .platform_data = &acx_data,
++ },
++ .num_resources = ARRAY_SIZE( acx_resources ),
++ .resource = acx_resources,
++};
++
++static int __init
++htcuniversal_wlan_init( void )
++{
++ printk( "htcuniversal_wlan_init: acx-mem platform_device_register\n" );
++ acx_device.resource[1].start = asic3_irq_base(&htcuniversal_asic3.dev) + ASIC3_GPIOC_IRQ_BASE+GPIOC_WIFI_IRQ_N;
++ acx_device.resource[1].end = asic3_irq_base(&htcuniversal_asic3.dev) + ASIC3_GPIOC_IRQ_BASE+GPIOC_WIFI_IRQ_N;
++ return platform_device_register( &acx_device );
++}
++
++
++static void __exit
++htcuniversal_wlan_exit( void )
++{
++ platform_device_unregister( &acx_device );
++}
++
++module_init( htcuniversal_wlan_init );
++module_exit( htcuniversal_wlan_exit );
++
++MODULE_AUTHOR( "Todd Blumer <todd@sdgsystems.com>" );
++MODULE_DESCRIPTION( "WLAN driver for HTC Universal" );
++MODULE_LICENSE( "GPL" );
++
+Index: linux-2.6.23/drivers/net/wireless/acx/hx4700_acx.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23/drivers/net/wireless/acx/hx4700_acx.c 2008-01-20 21:13:40.000000000 +0000
+@@ -0,0 +1,108 @@
++/*
++ * WLAN (TI TNETW1100B) support in the hx470x.
++ *
++ * Copyright (c) 2006 SDG Systems, LLC
++ *
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License. See the file COPYING in the main directory of this archive for
++ * more details.
++ *
++ * 28-March-2006 Todd Blumer <todd@sdgsystems.com>
++ */
++
++
++#include <linux/kernel.h>
++#include <linux/platform_device.h>
++#include <linux/delay.h>
++#include <linux/leds.h>
++
++#include <asm/hardware.h>
++
++#include <asm/arch/pxa-regs.h>
++#include <asm/arch/hx4700-gpio.h>
++#include <asm/arch/hx4700-core.h>
++#include <asm/io.h>
++
++#include "acx_hw.h"
++
++#define WLAN_OFFSET 0x1000000
++#define WLAN_BASE (PXA_CS5_PHYS+WLAN_OFFSET)
++
++
++static int
++hx4700_wlan_start( void )
++{
++ SET_HX4700_GPIO( WLAN_RESET_N, 0 );
++ mdelay(5);
++ hx4700_egpio_enable( EGPIO0_VCC_3V3_EN );
++ mdelay(100);
++ hx4700_egpio_enable( EGPIO7_VCC_3V3_WL_EN );
++ mdelay(150);
++ hx4700_egpio_enable( EGPIO1_WL_VREG_EN | EGPIO2_VCC_2V1_WL_EN |
++ EGPIO6_WL1V8_EN );
++ mdelay(10);
++ SET_HX4700_GPIO( WLAN_RESET_N, 1 );
++ mdelay(50);
++ led_trigger_event_shared(hx4700_radio_trig, LED_FULL);
++ return 0;
++}
++
++static int
++hx4700_wlan_stop( void )
++{
++ hx4700_egpio_disable( EGPIO0_VCC_3V3_EN | EGPIO1_WL_VREG_EN |
++ EGPIO7_VCC_3V3_WL_EN | EGPIO2_VCC_2V1_WL_EN |
++ EGPIO6_WL1V8_EN );
++ SET_HX4700_GPIO( WLAN_RESET_N, 0 );
++ led_trigger_event_shared(hx4700_radio_trig, LED_OFF);
++ return 0;
++}
++
++static struct resource acx_resources[] = {
++ [0] = {
++ .start = WLAN_BASE,
++ .end = WLAN_BASE + 0x20,
++ .flags = IORESOURCE_MEM,
++ },
++ [1] = {
++ .start = HX4700_IRQ(WLAN_IRQ_N),
++ .end = HX4700_IRQ(WLAN_IRQ_N),
++ .flags = IORESOURCE_IRQ,
++ },
++};
++
++static struct acx_hardware_data acx_data = {
++ .start_hw = hx4700_wlan_start,
++ .stop_hw = hx4700_wlan_stop,
++};
++
++static struct platform_device acx_device = {
++ .name = "acx-mem",
++ .dev = {
++ .platform_data = &acx_data,
++ },
++ .num_resources = ARRAY_SIZE( acx_resources ),
++ .resource = acx_resources,
++};
++
++static int __init
++hx4700_wlan_init( void )
++{
++ printk( "hx4700_wlan_init: acx-mem platform_device_register\n" );
++ return platform_device_register( &acx_device );
++}
++
++
++static void __exit
++hx4700_wlan_exit( void )
++{
++ platform_device_unregister( &acx_device );
++}
++
++module_init( hx4700_wlan_init );
++module_exit( hx4700_wlan_exit );
++
++MODULE_AUTHOR( "Todd Blumer <todd@sdgsystems.com>" );
++MODULE_DESCRIPTION( "WLAN driver for iPAQ hx4700" );
++MODULE_LICENSE( "GPL" );
++
+Index: linux-2.6.23/drivers/net/wireless/acx/ioctl.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23/drivers/net/wireless/acx/ioctl.c 2008-01-20 21:13:40.000000000 +0000
+@@ -0,0 +1,2748 @@
++/***********************************************************************
++** Copyright (C) 2003 ACX100 Open Source Project
++**
++** The contents of this file are subject to the Mozilla Public
++** License Version 1.1 (the "License"); you may not use this file
++** except in compliance with the License. You may obtain a copy of
++** the License at http://www.mozilla.org/MPL/
++**
++** Software distributed under the License is distributed on an "AS
++** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
++** implied. See the License for the specific language governing
++** rights and limitations under the License.
++**
++** Alternatively, the contents of this file may be used under the
++** terms of the GNU Public License version 2 (the "GPL"), in which
++** case the provisions of the GPL are applicable instead of the
++** above. If you wish to allow the use of your version of this file
++** only under the terms of the GPL and not to allow others to use
++** your version of this file under the MPL, indicate your decision
++** by deleting the provisions above and replace them with the notice
++** and other provisions required by the GPL. If you do not delete
++** the provisions above, a recipient may use your version of this
++** file under either the MPL or the GPL.
++** ---------------------------------------------------------------------
++** Inquiries regarding the ACX100 Open Source Project can be
++** made directly to:
++**
++** acx100-users@lists.sf.net
++** http://acx100.sf.net
++** ---------------------------------------------------------------------
++*/
++
++#include <linux/version.h>
++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18)
++#include <linux/config.h>
++#endif
++#include <linux/kernel.h>
++#include <linux/types.h>
++#include <asm/io.h>
++/* #include <asm/uaccess.h> */ /* required for 2.4.x kernels; verify_write() */
++#include <linux/if_arp.h>
++#include <linux/wireless.h>
++#include <net/iw_handler.h>
++
++#include "acx.h"
++
++
++/***********************************************************************
++*/
++
++/* channel frequencies
++ * TODO: Currently, every other 802.11 driver keeps its own copy of this. In
++ * the long run this should be integrated into ieee802_11.h or wireless.h or
++ * whatever IEEE802.11x framework evolves */
++static const u16 acx_channel_freq[] = {
++ 2412, 2417, 2422, 2427, 2432, 2437, 2442,
++ 2447, 2452, 2457, 2462, 2467, 2472, 2484,
++};
++
++
++/***********************************************************************
++** acx_ioctl_commit
++*/
++static int
++acx_ioctl_commit(struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++
++ FN_ENTER;
++
++ acx_sem_lock(adev);
++ if (ACX_STATE_IFACE_UP & adev->dev_state_mask)
++ acx_s_update_card_settings(adev);
++ acx_sem_unlock(adev);
++
++ FN_EXIT0;
++ return OK;
++}
++
++
++/***********************************************************************
++*/
++static int
++acx_ioctl_get_name(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ static const char * const names[] = { "IEEE 802.11b+/g+", "IEEE 802.11b+" };
++
++ strcpy(wrqu->name, names[IS_ACX111(adev) ? 0 : 1]);
++
++ return OK;
++}
++
++
++/***********************************************************************
++** acx_ioctl_set_freq
++*/
++static int
++acx_ioctl_set_freq(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ int channel = -1;
++ unsigned int mult = 1;
++ int result;
++
++ FN_ENTER;
++
++ if (wrqu->freq.e == 0 && wrqu->freq.m <= 1000) {
++ /* Setting by channel number */
++ channel = wrqu->freq.m;
++ } else {
++ /* If setting by frequency, convert to a channel */
++ int i;
++
++ for (i = 0; i < (6 - wrqu->freq.e); i++)
++ mult *= 10;
++
++ for (i = 1; i <= 14; i++)
++ if (wrqu->freq.m == acx_channel_freq[i - 1] * mult)
++ channel = i;
++ }
++
++ if (channel > 14) {
++ result = -EINVAL;
++ goto end;
++ }
++
++ acx_sem_lock(adev);
++
++ adev->channel = channel;
++ /* hmm, the following code part is strange, but this is how
++ * it was being done before... */
++ log(L_IOCTL, "Changing to channel %d\n", channel);
++ SET_BIT(adev->set_mask, GETSET_CHANNEL);
++
++ result = -EINPROGRESS; /* need to call commit handler */
++
++ acx_sem_unlock(adev);
++end:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++*/
++static inline int
++acx_ioctl_get_freq(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ wrqu->freq.e = 0;
++ wrqu->freq.m = adev->channel;
++ return OK;
++}
++
++
++/***********************************************************************
++** acx_ioctl_set_mode
++*/
++static int
++acx_ioctl_set_mode(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ int result;
++
++ FN_ENTER;
++
++ acx_sem_lock(adev);
++
++ switch (wrqu->mode) {
++ case IW_MODE_AUTO:
++ adev->mode = ACX_MODE_OFF;
++ break;
++ case IW_MODE_MONITOR:
++ adev->mode = ACX_MODE_MONITOR;
++ break;
++ case IW_MODE_ADHOC:
++ adev->mode = ACX_MODE_0_ADHOC;
++ break;
++ case IW_MODE_INFRA:
++ adev->mode = ACX_MODE_2_STA;
++ break;
++ case IW_MODE_MASTER:
++ printk("acx: master mode (HostAP) is very, very "
++ "experimental! It might work partially, but "
++ "better get prepared for nasty surprises "
++ "at any time\n");
++ adev->mode = ACX_MODE_3_AP;
++ break;
++ case IW_MODE_REPEAT:
++ case IW_MODE_SECOND:
++ default:
++ result = -EOPNOTSUPP;
++ goto end_unlock;
++ }
++
++ log(L_ASSOC, "new adev->mode=%d\n", adev->mode);
++ SET_BIT(adev->set_mask, GETSET_MODE);
++ result = -EINPROGRESS;
++
++end_unlock:
++ acx_sem_unlock(adev);
++
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++*/
++static int
++acx_ioctl_get_mode(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ int result = 0;
++
++ switch (adev->mode) {
++ case ACX_MODE_OFF:
++ wrqu->mode = IW_MODE_AUTO; break;
++ case ACX_MODE_MONITOR:
++ wrqu->mode = IW_MODE_MONITOR; break;
++ case ACX_MODE_0_ADHOC:
++ wrqu->mode = IW_MODE_ADHOC; break;
++ case ACX_MODE_2_STA:
++ wrqu->mode = IW_MODE_INFRA; break;
++ case ACX_MODE_3_AP:
++ wrqu->mode = IW_MODE_MASTER; break;
++ default:
++ result = -EOPNOTSUPP;
++ }
++ return result;
++}
++
++
++/***********************************************************************
++*/
++static int
++acx_ioctl_set_sens(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ struct iw_param *vwrq = &wrqu->sens;
++ acx_device_t *adev = ndev2adev(ndev);
++
++ acx_sem_lock(adev);
++
++ adev->sensitivity = (1 == vwrq->disabled) ? 0 : vwrq->value;
++ SET_BIT(adev->set_mask, GETSET_SENSITIVITY);
++
++ acx_sem_unlock(adev);
++
++ return -EINPROGRESS;
++}
++
++
++/***********************************************************************
++*/
++static int
++acx_ioctl_get_sens(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ struct iw_param *vwrq = &wrqu->sens;
++ acx_device_t *adev = ndev2adev(ndev);
++
++ if (IS_USB(adev))
++ /* setting the PHY reg via fw cmd doesn't work yet */
++ return -EOPNOTSUPP;
++
++ /* acx_sem_lock(adev); */
++
++ vwrq->value = adev->sensitivity;
++ vwrq->disabled = (vwrq->value == 0);
++ vwrq->fixed = 1;
++
++ /* acx_sem_unlock(adev); */
++
++ return OK;
++}
++
++
++/***********************************************************************
++** acx_ioctl_set_ap
++**
++** Sets the MAC address of the AP to associate with
++*/
++static int
++acx_ioctl_set_ap(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ struct sockaddr *awrq = &wrqu->ap_addr;
++ acx_device_t *adev = ndev2adev(ndev);
++ int result = 0;
++ const u8 *ap;
++
++ FN_ENTER;
++ if (NULL == awrq) {
++ result = -EFAULT;
++ goto end;
++ }
++ if (ARPHRD_ETHER != awrq->sa_family) {
++ result = -EINVAL;
++ goto end;
++ }
++
++ ap = awrq->sa_data;
++ acxlog_mac(L_IOCTL, "set AP=", ap, "\n");
++
++ MAC_COPY(adev->ap, ap);
++
++ /* We want to start rescan in managed or ad-hoc mode,
++ ** otherwise just set adev->ap.
++ ** "iwconfig <if> ap <mac> mode managed": we must be able
++ ** to set ap _first_ and _then_ set mode */
++ switch (adev->mode) {
++ case ACX_MODE_0_ADHOC:
++ case ACX_MODE_2_STA:
++ /* FIXME: if there is a convention on what zero AP means,
++ ** please add a comment about that. I don't know of any --vda */
++ if (mac_is_zero(ap)) {
++ /* "off" == 00:00:00:00:00:00 */
++ MAC_BCAST(adev->ap);
++ log(L_IOCTL, "Not reassociating\n");
++ } else {
++ log(L_IOCTL, "Forcing reassociation\n");
++ SET_BIT(adev->set_mask, GETSET_RESCAN);
++ }
++ break;
++ }
++ result = -EINPROGRESS;
++end:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++*/
++static int
++acx_ioctl_get_ap(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ struct sockaddr *awrq = &wrqu->ap_addr;
++ acx_device_t *adev = ndev2adev(ndev);
++
++ if (ACX_STATUS_4_ASSOCIATED == adev->status) {
++ /* as seen in Aironet driver, airo.c */
++ MAC_COPY(awrq->sa_data, adev->bssid);
++ } else {
++ MAC_ZERO(awrq->sa_data);
++ }
++ awrq->sa_family = ARPHRD_ETHER;
++ return OK;
++}
++
++
++/***********************************************************************
++** acx_ioctl_get_aplist
++**
++** Deprecated in favor of iwscan.
++** We simply return the list of currently available stations in range,
++** don't do a new scan.
++*/
++static int
++acx_ioctl_get_aplist(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ struct iw_point *dwrq = &wrqu->data;
++ acx_device_t *adev = ndev2adev(ndev);
++ struct sockaddr *address = (struct sockaddr *) extra;
++ struct iw_quality qual[IW_MAX_AP];
++ int i, cur;
++ int result = OK;
++
++ FN_ENTER;
++
++ /* we have AP list only in STA mode */
++ if (ACX_MODE_2_STA != adev->mode) {
++ result = -EOPNOTSUPP;
++ goto end;
++ }
++
++ cur = 0;
++ for (i = 0; i < VEC_SIZE(adev->sta_list); i++) {
++ struct client *bss = &adev->sta_list[i];
++ if (!bss->used) continue;
++ MAC_COPY(address[cur].sa_data, bss->bssid);
++ address[cur].sa_family = ARPHRD_ETHER;
++ qual[cur].level = bss->sir;
++ qual[cur].noise = bss->snr;
++#ifndef OLD_QUALITY
++ qual[cur].qual = acx_signal_determine_quality(qual[cur].level,
++ qual[cur].noise);
++#else
++ qual[cur].qual = (qual[cur].noise <= 100) ?
++ 100 - qual[cur].noise : 0;
++#endif
++ /* no scan: level/noise/qual not updated: */
++ qual[cur].updated = 0;
++ cur++;
++ }
++ if (cur) {
++ dwrq->flags = 1;
++ memcpy(extra + sizeof(struct sockaddr)*cur, &qual,
++ sizeof(struct iw_quality)*cur);
++ }
++ dwrq->length = cur;
++end:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++*/
++static int
++acx_ioctl_set_scan(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ int result;
++
++ FN_ENTER;
++
++ acx_sem_lock(adev);
++
++ /* don't start scan if device is not up yet */
++ if (!(adev->dev_state_mask & ACX_STATE_IFACE_UP)) {
++ result = -EAGAIN;
++ goto end_unlock;
++ }
++
++ /* This is NOT a rescan for new AP!
++ ** Do not use SET_BIT(GETSET_RESCAN); */
++ acx_s_cmd_start_scan(adev);
++ result = OK;
++
++end_unlock:
++ acx_sem_unlock(adev);
++/* end: */
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acx_s_scan_add_station
++*/
++/* helper. not sure whether it's really a _s_leeping fn */
++static char*
++acx_s_scan_add_station(
++ acx_device_t *adev,
++ char *ptr,
++ char *end_buf,
++ struct client *bss)
++{
++ struct iw_event iwe;
++ char *ptr_rate;
++
++ FN_ENTER;
++
++ /* MAC address has to be added first */
++ iwe.cmd = SIOCGIWAP;
++ iwe.u.ap_addr.sa_family = ARPHRD_ETHER;
++ MAC_COPY(iwe.u.ap_addr.sa_data, bss->bssid);
++ acxlog_mac(L_IOCTL, "scan, station address: ", bss->bssid, "\n");
++ ptr = iwe_stream_add_event(ptr, end_buf, &iwe, IW_EV_ADDR_LEN);
++
++ /* Add ESSID */
++ iwe.cmd = SIOCGIWESSID;
++ iwe.u.data.length = bss->essid_len;
++ iwe.u.data.flags = 1;
++ log(L_IOCTL, "scan, essid: %s\n", bss->essid);
++ ptr = iwe_stream_add_point(ptr, end_buf, &iwe, bss->essid);
++
++ /* Add mode */
++ iwe.cmd = SIOCGIWMODE;
++ if (bss->cap_info & (WF_MGMT_CAP_ESS | WF_MGMT_CAP_IBSS)) {
++ if (bss->cap_info & WF_MGMT_CAP_ESS)
++ iwe.u.mode = IW_MODE_MASTER;
++ else
++ iwe.u.mode = IW_MODE_ADHOC;
++ log(L_IOCTL, "scan, mode: %d\n", iwe.u.mode);
++ ptr = iwe_stream_add_event(ptr, end_buf, &iwe, IW_EV_UINT_LEN);
++ }
++
++ /* Add frequency */
++ iwe.cmd = SIOCGIWFREQ;
++ iwe.u.freq.m = acx_channel_freq[bss->channel - 1] * 100000;
++ iwe.u.freq.e = 1;
++ log(L_IOCTL, "scan, frequency: %d\n", iwe.u.freq.m);
++ ptr = iwe_stream_add_event(ptr, end_buf, &iwe, IW_EV_FREQ_LEN);
++
++ /* Add link quality */
++ iwe.cmd = IWEVQUAL;
++ /* FIXME: these values should be expressed in dBm, but we don't know
++ * how to calibrate it yet */
++ iwe.u.qual.level = bss->sir;
++ iwe.u.qual.noise = bss->snr;
++#ifndef OLD_QUALITY
++ iwe.u.qual.qual = acx_signal_determine_quality(iwe.u.qual.level,
++ iwe.u.qual.noise);
++#else
++ iwe.u.qual.qual = (iwe.u.qual.noise <= 100) ?
++ 100 - iwe.u.qual.noise : 0;
++#endif
++ iwe.u.qual.updated = 7;
++ log(L_IOCTL, "scan, link quality: %d/%d/%d\n",
++ iwe.u.qual.level, iwe.u.qual.noise, iwe.u.qual.qual);
++ ptr = iwe_stream_add_event(ptr, end_buf, &iwe, IW_EV_QUAL_LEN);
++
++ /* Add encryption */
++ iwe.cmd = SIOCGIWENCODE;
++ if (bss->cap_info & WF_MGMT_CAP_PRIVACY)
++ iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY;
++ else
++ iwe.u.data.flags = IW_ENCODE_DISABLED;
++ iwe.u.data.length = 0;
++ log(L_IOCTL, "scan, encryption flags: %X\n", iwe.u.data.flags);
++ ptr = iwe_stream_add_point(ptr, end_buf, &iwe, bss->essid);
++
++ /* add rates */
++ iwe.cmd = SIOCGIWRATE;
++ iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0;
++ ptr_rate = ptr + IW_EV_LCP_LEN;
++
++ {
++ u16 rate = bss->rate_cap;
++ const u8* p = acx_bitpos2ratebyte;
++ while (rate) {
++ if (rate & 1) {
++ iwe.u.bitrate.value = *p * 500000; /* units of 500kb/s */
++ log(L_IOCTL, "scan, rate: %d\n", iwe.u.bitrate.value);
++ ptr_rate = iwe_stream_add_value(ptr, ptr_rate, end_buf,
++ &iwe, IW_EV_PARAM_LEN);
++ }
++ rate >>= 1;
++ p++;
++ }}
++
++ if ((ptr_rate - ptr) > (ptrdiff_t)IW_EV_LCP_LEN)
++ ptr = ptr_rate;
++
++ /* drop remaining station data items for now */
++
++ FN_EXIT0;
++ return ptr;
++}
++
++
++/***********************************************************************
++ * acx_ioctl_get_scan
++ */
++static int
++acx_ioctl_get_scan(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ struct iw_point *dwrq = &wrqu->data;
++ acx_device_t *adev = ndev2adev(ndev);
++ char *ptr = extra;
++ int i;
++ int result = OK;
++
++ FN_ENTER;
++
++ acx_sem_lock(adev);
++
++ /* no scan available if device is not up yet */
++ if (!(adev->dev_state_mask & ACX_STATE_IFACE_UP)) {
++ log(L_IOCTL, "iface not up yet\n");
++ result = -EAGAIN;
++ goto end_unlock;
++ }
++
++#ifdef ENODATA_TO_BE_USED_AFTER_SCAN_ERROR_ONLY
++ if (adev->bss_table_count == 0) {
++ /* no stations found */
++ result = -ENODATA;
++ goto end_unlock;
++ }
++#endif
++
++ for (i = 0; i < VEC_SIZE(adev->sta_list); i++) {
++ struct client *bss = &adev->sta_list[i];
++ if (!bss->used) continue;
++ ptr = acx_s_scan_add_station(adev, ptr,
++ extra + IW_SCAN_MAX_DATA, bss);
++ }
++ dwrq->length = ptr - extra;
++ dwrq->flags = 0;
++
++end_unlock:
++ acx_sem_unlock(adev);
++/* end: */
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acx_ioctl_set_essid
++*/
++static int
++acx_ioctl_set_essid(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ struct iw_point *dwrq = &wrqu->essid;
++ acx_device_t *adev = ndev2adev(ndev);
++ int len = dwrq->length;
++ int result;
++
++ FN_ENTER;
++
++ if (len < 0) {
++ result = -EINVAL;
++ goto end;
++ }
++
++ log(L_IOCTL, "set ESSID '%*s', length %d, flags 0x%04X\n",
++ len, extra, len, dwrq->flags);
++
++#if WIRELESS_EXT >= 21
++ /* WE 21 gives real ESSID strlen, not +1 (trailing zero):
++ * see LKML "[patch] drivers/net/wireless: correct reported ssid lengths" */
++ len += 1;
++#endif
++
++ acx_sem_lock(adev);
++
++ /* ESSID disabled? */
++ if (0 == dwrq->flags) {
++ adev->essid_active = 0;
++
++ } else {
++ if (len > IW_ESSID_MAX_SIZE) {
++ result = -E2BIG;
++ goto end_unlock;
++ }
++
++ if (len >= sizeof(adev->essid))
++ len = sizeof(adev->essid) - 1;
++ memcpy(adev->essid, extra, len);
++ adev->essid[len] = '\0';
++ /* Paranoia: just in case there is a '\0'... */
++ adev->essid_len = strlen(adev->essid);
++ adev->essid_active = 1;
++ }
++
++ SET_BIT(adev->set_mask, GETSET_RESCAN);
++
++ result = -EINPROGRESS;
++
++end_unlock:
++ acx_sem_unlock(adev);
++end:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++*/
++static int
++acx_ioctl_get_essid(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ struct iw_point *dwrq = &wrqu->essid;
++ acx_device_t *adev = ndev2adev(ndev);
++
++ dwrq->flags = adev->essid_active;
++ if (adev->essid_active) {
++ memcpy(extra, adev->essid, adev->essid_len);
++ extra[adev->essid_len] = '\0';
++ dwrq->length = adev->essid_len + 1;
++ dwrq->flags = 1;
++ }
++ return OK;
++}
++
++
++/***********************************************************************
++** acx_l_update_client_rates
++*/
++static void
++acx_l_update_client_rates(acx_device_t *adev, u16 rate)
++{
++ int i;
++ for (i = 0; i < VEC_SIZE(adev->sta_list); i++) {
++ client_t *clt = &adev->sta_list[i];
++ if (!clt->used) continue;
++ clt->rate_cfg = (clt->rate_cap & rate);
++ if (!clt->rate_cfg) {
++ /* no compatible rates left: kick client */
++ acxlog_mac(L_ASSOC, "client ",clt->address," kicked: "
++ "rates are not compatible anymore\n");
++ acx_l_sta_list_del(adev, clt);
++ continue;
++ }
++ clt->rate_cur &= clt->rate_cfg;
++ if (!clt->rate_cur) {
++ /* current rate become invalid, choose a valid one */
++ clt->rate_cur = 1 << lowest_bit(clt->rate_cfg);
++ }
++ if (IS_ACX100(adev))
++ clt->rate_100 = acx_bitpos2rate100[highest_bit(clt->rate_cur)];
++ clt->fallback_count = clt->stepup_count = 0;
++ clt->ignore_count = 16;
++ }
++ switch (adev->mode) {
++ case ACX_MODE_2_STA:
++ if (adev->ap_client && !adev->ap_client->used) {
++ /* Owwww... we kicked our AP!! :) */
++ SET_BIT(adev->set_mask, GETSET_RESCAN);
++ }
++ }
++}
++
++
++/***********************************************************************
++*/
++/* maps bits from acx111 rate to rate in Mbits */
++static const unsigned int
++acx111_rate_tbl[] = {
++ 1000000, /* 0 */
++ 2000000, /* 1 */
++ 5500000, /* 2 */
++ 6000000, /* 3 */
++ 9000000, /* 4 */
++ 11000000, /* 5 */
++ 12000000, /* 6 */
++ 18000000, /* 7 */
++ 22000000, /* 8 */
++ 24000000, /* 9 */
++ 36000000, /* 10 */
++ 48000000, /* 11 */
++ 54000000, /* 12 */
++ 500000, /* 13, should not happen */
++ 500000, /* 14, should not happen */
++ 500000, /* 15, should not happen */
++};
++
++/***********************************************************************
++ * acx_ioctl_set_rate
++ */
++static int
++acx_ioctl_set_rate(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ struct iw_param *vwrq = &wrqu->param;
++ acx_device_t *adev = ndev2adev(ndev);
++ u16 txrate_cfg = 1;
++ unsigned long flags;
++ int autorate;
++ int result = -EINVAL;
++
++ FN_ENTER;
++ log(L_IOCTL, "rate %d fixed 0x%X disabled 0x%X flags 0x%X\n",
++ vwrq->value, vwrq->fixed, vwrq->disabled, vwrq->flags);
++
++ if ((0 == vwrq->fixed) || (1 == vwrq->fixed)) {
++ int i = VEC_SIZE(acx111_rate_tbl)-1;
++ if (vwrq->value == -1)
++ /* "iwconfig rate auto" --> choose highest */
++ vwrq->value = IS_ACX100(adev) ? 22000000 : 54000000;
++ while (i >= 0) {
++ if (vwrq->value == acx111_rate_tbl[i]) {
++ txrate_cfg <<= i;
++ i = 0;
++ break;
++ }
++ i--;
++ }
++ if (i == -1) { /* no matching rate */
++ result = -EINVAL;
++ goto end;
++ }
++ } else { /* rate N, N<1000 (driver specific): we don't use this */
++ result = -EOPNOTSUPP;
++ goto end;
++ }
++ /* now: only one bit is set in txrate_cfg, corresponding to
++ ** indicated rate */
++
++ autorate = (vwrq->fixed == 0) && (RATE111_1 != txrate_cfg);
++ if (autorate) {
++ /* convert 00100000 -> 00111111 */
++ txrate_cfg = (txrate_cfg<<1)-1;
++ }
++
++ if (IS_ACX100(adev)) {
++ txrate_cfg &= RATE111_ACX100_COMPAT;
++ if (!txrate_cfg) {
++ result = -ENOTSUPP; /* rate is not supported by acx100 */
++ goto end;
++ }
++ }
++
++ acx_sem_lock(adev);
++ acx_lock(adev, flags);
++
++ adev->rate_auto = autorate;
++ adev->rate_oper = txrate_cfg;
++ adev->rate_basic = txrate_cfg;
++ /* only do that in auto mode, non-auto will be able to use
++ * one specific Tx rate only anyway */
++ if (autorate) {
++ /* only use 802.11b base rates, for standard 802.11b H/W
++ * compatibility */
++ adev->rate_basic &= RATE111_80211B_COMPAT;
++ }
++ adev->rate_bcast = 1 << lowest_bit(txrate_cfg);
++ if (IS_ACX100(adev))
++ adev->rate_bcast100 = acx_rate111to100(adev->rate_bcast);
++ acx_l_update_ratevector(adev);
++ acx_l_update_client_rates(adev, txrate_cfg);
++
++ /* Do/don't do tx rate fallback; beacon contents and rate */
++ SET_BIT(adev->set_mask, SET_RATE_FALLBACK|SET_TEMPLATES);
++ result = -EINPROGRESS;
++
++ acx_unlock(adev, flags);
++ acx_sem_unlock(adev);
++end:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acx_ioctl_get_rate
++*/
++static int
++acx_ioctl_get_rate(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ struct iw_param *vwrq = &wrqu->param;
++ acx_device_t *adev = ndev2adev(ndev);
++ unsigned long flags;
++ u16 rate;
++
++ acx_lock(adev, flags);
++ rate = adev->rate_oper;
++ if (adev->ap_client)
++ rate = adev->ap_client->rate_cur;
++ vwrq->value = acx111_rate_tbl[highest_bit(rate)];
++ vwrq->fixed = !adev->rate_auto;
++ vwrq->disabled = 0;
++ acx_unlock(adev, flags);
++
++ return OK;
++}
++
++static int
++acx_ioctl_set_rts(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ struct iw_param *vwrq = &wrqu->rts;
++ acx_device_t *adev = ndev2adev(ndev);
++ int val = vwrq->value;
++
++ if (vwrq->disabled)
++ val = 2312;
++ if ((val < 0) || (val > 2312))
++ return -EINVAL;
++
++ adev->rts_threshold = val;
++ return OK;
++}
++
++static inline int
++acx_ioctl_get_rts(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ struct iw_param *vwrq = &wrqu->rts;
++ acx_device_t *adev = ndev2adev(ndev);
++
++ vwrq->value = adev->rts_threshold;
++ vwrq->disabled = (vwrq->value >= 2312);
++ vwrq->fixed = 1;
++ return OK;
++}
++
++
++#if ACX_FRAGMENTATION
++static int
++acx_ioctl_set_frag(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ int val = vwrq->value;
++
++ if (vwrq->disabled)
++ val = 32767;
++ else
++ if ((val < 256) || (val > 2347))
++ return -EINVAL;
++
++ adev->frag_threshold = val;
++ return OK;
++}
++
++static inline int
++acx_ioctl_get_frag(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ struct iw_param *vwrq = &wrqu->frag;
++ acx_device_t *adev = ndev2adev(ndev);
++
++ vwrq->value = adev->frag_threshold;
++ vwrq->disabled = (vwrq->value >= 2347);
++ vwrq->fixed = 1;
++ return OK;
++}
++#endif
++
++
++/***********************************************************************
++** acx_ioctl_set_encode
++*/
++static int
++acx_ioctl_set_encode(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ struct iw_point *dwrq = &wrqu->encoding;
++ acx_device_t *adev = ndev2adev(ndev);
++ int index;
++ int result;
++
++ FN_ENTER;
++
++ log(L_IOCTL, "set encoding flags=0x%04X, size=%d, key: %s\n",
++ dwrq->flags, dwrq->length, extra ? "set" : "No key");
++
++ acx_sem_lock(adev);
++
++ index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
++
++ if (dwrq->length > 0) {
++ /* if index is 0 or invalid, use default key */
++ if ((index < 0) || (index > 3))
++ index = (int)adev->wep_current_index;
++
++ if (0 == (dwrq->flags & IW_ENCODE_NOKEY)) {
++ if (dwrq->length > 29)
++ dwrq->length = 29; /* restrict it */
++
++ if (dwrq->length > 13) {
++ /* 29*8 == 232, WEP256 */
++ adev->wep_keys[index].size = 29;
++ } else if (dwrq->length > 5) {
++ /* 13*8 == 104bit, WEP128 */
++ adev->wep_keys[index].size = 13;
++ } else if (dwrq->length > 0) {
++ /* 5*8 == 40bit, WEP64 */
++ adev->wep_keys[index].size = 5;
++ } else {
++ /* disable key */
++ adev->wep_keys[index].size = 0;
++ }
++
++ memset(adev->wep_keys[index].key, 0,
++ sizeof(adev->wep_keys[index].key));
++ memcpy(adev->wep_keys[index].key, extra, dwrq->length);
++ }
++ } else {
++ /* set transmit key */
++ if ((index >= 0) && (index <= 3))
++ adev->wep_current_index = index;
++ else if (0 == (dwrq->flags & IW_ENCODE_MODE)) {
++ /* complain if we were not just setting
++ * the key mode */
++ result = -EINVAL;
++ goto end_unlock;
++ }
++ }
++
++ adev->wep_enabled = !(dwrq->flags & IW_ENCODE_DISABLED);
++
++ if (dwrq->flags & IW_ENCODE_OPEN) {
++ adev->auth_alg = WLAN_AUTH_ALG_OPENSYSTEM;
++ adev->wep_restricted = 0;
++
++ } else if (dwrq->flags & IW_ENCODE_RESTRICTED) {
++ adev->auth_alg = WLAN_AUTH_ALG_SHAREDKEY;
++ adev->wep_restricted = 1;
++ }
++
++ /* set flag to make sure the card WEP settings get updated */
++ SET_BIT(adev->set_mask, GETSET_WEP);
++
++ log(L_IOCTL, "len=%d, key at 0x%p, flags=0x%X\n",
++ dwrq->length, extra, dwrq->flags);
++
++ for (index = 0; index <= 3; index++) {
++ if (adev->wep_keys[index].size) {
++ log(L_IOCTL, "index=%d, size=%d, key at 0x%p\n",
++ adev->wep_keys[index].index,
++ (int) adev->wep_keys[index].size,
++ adev->wep_keys[index].key);
++ }
++ }
++ result = -EINPROGRESS;
++
++end_unlock:
++ acx_sem_unlock(adev);
++
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acx_ioctl_get_encode
++*/
++static int
++acx_ioctl_get_encode(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ struct iw_point *dwrq = &wrqu->encoding;
++ acx_device_t *adev = ndev2adev(ndev);
++ int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
++
++ FN_ENTER;
++
++ if (adev->wep_enabled == 0) {
++ dwrq->flags = IW_ENCODE_DISABLED;
++ } else {
++ if ((index < 0) || (index > 3))
++ index = (int)adev->wep_current_index;
++
++ dwrq->flags = (adev->wep_restricted == 1) ?
++ IW_ENCODE_RESTRICTED : IW_ENCODE_OPEN;
++ dwrq->length = adev->wep_keys[index].size;
++
++ memcpy(extra, adev->wep_keys[index].key,
++ adev->wep_keys[index].size);
++ }
++
++ /* set the current index */
++ SET_BIT(dwrq->flags, index + 1);
++
++ log(L_IOCTL, "len=%d, key=%p, flags=0x%X\n",
++ dwrq->length, dwrq->pointer,
++ dwrq->flags);
++
++ FN_EXIT1(OK);
++ return OK;
++}
++
++
++/***********************************************************************
++*/
++static int
++acx_ioctl_set_power(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ struct iw_param *vwrq = &wrqu->power;
++ acx_device_t *adev = ndev2adev(ndev);
++ int result = -EINPROGRESS;
++
++ FN_ENTER;
++
++ log(L_IOCTL, "set 802.11 powersave flags=0x%04X\n", vwrq->flags);
++
++ acx_sem_lock(adev);
++
++ if (vwrq->disabled) {
++ CLEAR_BIT(adev->ps_wakeup_cfg, PS_CFG_ENABLE);
++ SET_BIT(adev->set_mask, GETSET_POWER_80211);
++ goto end;
++ }
++ if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
++ u16 ps_timeout = (vwrq->value * 1024) / 1000;
++
++ if (ps_timeout > 255)
++ ps_timeout = 255;
++ log(L_IOCTL, "setting PS timeout value to %d time units "
++ "due to %dus\n", ps_timeout, vwrq->value);
++ adev->ps_hangover_period = ps_timeout;
++ } else if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_PERIOD) {
++ u16 ps_periods = vwrq->value / 1000000;
++
++ if (ps_periods > 255)
++ ps_periods = 255;
++ log(L_IOCTL, "setting PS period value to %d periods "
++ "due to %dus\n", ps_periods, vwrq->value);
++ adev->ps_listen_interval = ps_periods;
++ CLEAR_BIT(adev->ps_wakeup_cfg, PS_CFG_WAKEUP_MODE_MASK);
++ SET_BIT(adev->ps_wakeup_cfg, PS_CFG_WAKEUP_EACH_ITVL);
++ }
++
++ switch (vwrq->flags & IW_POWER_MODE) {
++ /* FIXME: are we doing the right thing here? */
++ case IW_POWER_UNICAST_R:
++ CLEAR_BIT(adev->ps_options, PS_OPT_STILL_RCV_BCASTS);
++ break;
++ case IW_POWER_MULTICAST_R:
++ SET_BIT(adev->ps_options, PS_OPT_STILL_RCV_BCASTS);
++ break;
++ case IW_POWER_ALL_R:
++ SET_BIT(adev->ps_options, PS_OPT_STILL_RCV_BCASTS);
++ break;
++ case IW_POWER_ON:
++ break;
++ default:
++ log(L_IOCTL, "unknown PS mode\n");
++ result = -EINVAL;
++ goto end;
++ }
++
++ SET_BIT(adev->ps_wakeup_cfg, PS_CFG_ENABLE);
++ SET_BIT(adev->set_mask, GETSET_POWER_80211);
++end:
++ acx_sem_unlock(adev);
++
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++*/
++static int
++acx_ioctl_get_power(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ struct iw_param *vwrq = &wrqu->power;
++ acx_device_t *adev = ndev2adev(ndev);
++
++ FN_ENTER;
++
++ log(L_IOCTL, "Get 802.11 Power Save flags = 0x%04X\n", vwrq->flags);
++ vwrq->disabled = ((adev->ps_wakeup_cfg & PS_CFG_ENABLE) == 0);
++ if (vwrq->disabled)
++ goto end;
++
++ if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) {
++ vwrq->value = adev->ps_hangover_period * 1000 / 1024;
++ vwrq->flags = IW_POWER_TIMEOUT;
++ } else {
++ vwrq->value = adev->ps_listen_interval * 1000000;
++ vwrq->flags = IW_POWER_PERIOD|IW_POWER_RELATIVE;
++ }
++ if (adev->ps_options & PS_OPT_STILL_RCV_BCASTS)
++ SET_BIT(vwrq->flags, IW_POWER_ALL_R);
++ else
++ SET_BIT(vwrq->flags, IW_POWER_UNICAST_R);
++end:
++ FN_EXIT1(OK);
++ return OK;
++}
++
++
++/***********************************************************************
++** acx_ioctl_get_txpow
++*/
++static inline int
++acx_ioctl_get_txpow(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ struct iw_param *vwrq = &wrqu->power;
++ acx_device_t *adev = ndev2adev(ndev);
++
++ FN_ENTER;
++
++ vwrq->flags = IW_TXPOW_DBM;
++ vwrq->disabled = 0;
++ vwrq->fixed = 1;
++ vwrq->value = adev->tx_level_dbm;
++
++ log(L_IOCTL, "get txpower:%d dBm\n", adev->tx_level_dbm);
++
++ FN_EXIT1(OK);
++ return OK;
++}
++
++
++/***********************************************************************
++** acx_ioctl_set_txpow
++*/
++static int
++acx_ioctl_set_txpow(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ struct iw_param *vwrq = &wrqu->power;
++ acx_device_t *adev = ndev2adev(ndev);
++ int result;
++
++ FN_ENTER;
++
++ log(L_IOCTL, "set txpower:%d, disabled:%d, flags:0x%04X\n",
++ vwrq->value, vwrq->disabled, vwrq->flags);
++
++ acx_sem_lock(adev);
++
++ if (vwrq->disabled != adev->tx_disabled) {
++ SET_BIT(adev->set_mask, GETSET_TX);
++ }
++
++ adev->tx_disabled = vwrq->disabled;
++ if (vwrq->value == -1) {
++ if (vwrq->disabled) {
++ adev->tx_level_dbm = 0;
++ log(L_IOCTL, "disable radio tx\n");
++ } else {
++ /* adev->tx_level_auto = 1; */
++ log(L_IOCTL, "set tx power auto (NIY)\n");
++ }
++ } else {
++ adev->tx_level_dbm = vwrq->value <= 20 ? vwrq->value : 20;
++ /* adev->tx_level_auto = 0; */
++ log(L_IOCTL, "set txpower=%d dBm\n", adev->tx_level_dbm);
++ }
++ SET_BIT(adev->set_mask, GETSET_TXPOWER);
++
++ result = -EINPROGRESS;
++
++ acx_sem_unlock(adev);
++
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acx_ioctl_get_range
++*/
++static int
++acx_ioctl_get_range(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ struct iw_point *dwrq = &wrqu->data;
++ struct iw_range *range = (struct iw_range *)extra;
++ acx_device_t *adev = ndev2adev(ndev);
++ int i,n;
++
++ FN_ENTER;
++
++ if (!dwrq->pointer)
++ goto end;
++
++ dwrq->length = sizeof(struct iw_range);
++ memset(range, 0, sizeof(struct iw_range));
++ n = 0;
++ for (i = 1; i <= 14; i++) {
++ if (adev->reg_dom_chanmask & (1 << (i - 1))) {
++ range->freq[n].i = i;
++ range->freq[n].m = acx_channel_freq[i - 1] * 100000;
++ range->freq[n].e = 1; /* units are MHz */
++ n++;
++ }
++ }
++ range->num_channels = n;
++ range->num_frequency = n;
++
++ range->min_rts = 0;
++ range->max_rts = 2312;
++
++#if ACX_FRAGMENTATION
++ range->min_frag = 256;
++ range->max_frag = 2312;
++#endif
++
++ range->encoding_size[0] = 5;
++ range->encoding_size[1] = 13;
++ range->encoding_size[2] = 29;
++ range->num_encoding_sizes = 3;
++ range->max_encoding_tokens = 4;
++
++ range->min_pmp = 0;
++ range->max_pmp = 5000000;
++ range->min_pmt = 0;
++ range->max_pmt = 65535 * 1000;
++ range->pmp_flags = IW_POWER_PERIOD;
++ range->pmt_flags = IW_POWER_TIMEOUT;
++ range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R;
++
++ if (IS_ACX100(adev)) { /* ACX100 has direct radio programming - arbitrary levels, so offer a lot */
++ for (i = 0; i <= IW_MAX_TXPOWER - 1; i++)
++ range->txpower[i] = 20 * i / (IW_MAX_TXPOWER - 1);
++ range->num_txpower = IW_MAX_TXPOWER;
++ range->txpower_capa = IW_TXPOW_DBM;
++ }
++ else {
++ int count = min(IW_MAX_TXPOWER, (int)adev->cfgopt_power_levels.len);
++ for (i = 0; i <= count; i++)
++ range->txpower[i] = adev->cfgopt_power_levels.list[i];
++ range->num_txpower = count;
++ /* this list is given in mW */
++ range->txpower_capa = IW_TXPOW_MWATT;
++ }
++
++ range->we_version_compiled = WIRELESS_EXT;
++ range->we_version_source = 0x9;
++
++ range->retry_capa = IW_RETRY_LIMIT;
++ range->retry_flags = IW_RETRY_LIMIT;
++ range->min_retry = 1;
++ range->max_retry = 255;
++
++ range->r_time_flags = IW_RETRY_LIFETIME;
++ range->min_r_time = 0;
++ /* FIXME: lifetime ranges and orders of magnitude are strange?? */
++ range->max_r_time = 65535;
++
++ if (IS_USB(adev))
++ range->sensitivity = 0;
++ else if (IS_ACX111(adev))
++ range->sensitivity = 3;
++ else
++ range->sensitivity = 255;
++
++ for (i=0; i < adev->rate_supported_len; i++) {
++ range->bitrate[i] = (adev->rate_supported[i] & ~0x80) * 500000;
++ /* never happens, but keep it, to be safe: */
++ if (range->bitrate[i] == 0)
++ break;
++ }
++ range->num_bitrates = i;
++
++ range->max_qual.qual = 100;
++ range->max_qual.level = 100;
++ range->max_qual.noise = 100;
++ /* TODO: better values */
++ range->avg_qual.qual = 90;
++ range->avg_qual.level = 80;
++ range->avg_qual.noise = 2;
++
++end:
++ FN_EXIT1(OK);
++ return OK;
++}
++
++
++/***********************************************************************
++** Private functions
++*/
++
++/***********************************************************************
++** acx_ioctl_get_nick
++*/
++static inline int
++acx_ioctl_get_nick(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ struct iw_point *dwrq = &wrqu->data;
++ acx_device_t *adev = ndev2adev(ndev);
++
++ strcpy(extra, adev->nick);
++ dwrq->length = strlen(extra) + 1;
++
++ return OK;
++}
++
++
++/***********************************************************************
++** acx_ioctl_set_nick
++*/
++static int
++acx_ioctl_set_nick(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ struct iw_point *dwrq = &wrqu->data;
++ acx_device_t *adev = ndev2adev(ndev);
++ int result;
++
++ FN_ENTER;
++
++ acx_sem_lock(adev);
++
++ if (dwrq->length > IW_ESSID_MAX_SIZE + 1) {
++ result = -E2BIG;
++ goto end_unlock;
++ }
++
++ /* extra includes trailing \0, so it's ok */
++ strcpy(adev->nick, extra);
++ result = OK;
++
++end_unlock:
++ acx_sem_unlock(adev);
++
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acx_ioctl_get_retry
++*/
++static int
++acx_ioctl_get_retry(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ struct iw_param *vwrq = &wrqu->retry;
++ acx_device_t *adev = ndev2adev(ndev);
++ unsigned int type = vwrq->flags & IW_RETRY_TYPE;
++ unsigned int modifier = vwrq->flags & IW_RETRY_MODIFIER;
++ int result;
++
++ FN_ENTER;
++
++ acx_sem_lock(adev);
++
++ /* return the short retry number by default */
++ if (type == IW_RETRY_LIFETIME) {
++ vwrq->flags = IW_RETRY_LIFETIME;
++ vwrq->value = adev->msdu_lifetime;
++ } else if (modifier == IW_RETRY_MAX) {
++ vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
++ vwrq->value = adev->long_retry;
++ } else {
++ vwrq->flags = IW_RETRY_LIMIT;
++ if (adev->long_retry != adev->short_retry)
++ SET_BIT(vwrq->flags, IW_RETRY_MIN);
++ vwrq->value = adev->short_retry;
++ }
++
++ /* can't be disabled */
++ vwrq->disabled = (u8)0;
++ result = OK;
++
++ acx_sem_unlock(adev);
++
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acx_ioctl_set_retry
++*/
++static int
++acx_ioctl_set_retry(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ struct iw_param *vwrq = &wrqu->retry;
++ acx_device_t *adev = ndev2adev(ndev);
++ int result;
++
++ FN_ENTER;
++
++ if (!vwrq) {
++ result = -EFAULT;
++ goto end;
++ }
++ if (vwrq->disabled) {
++ result = -EINVAL;
++ goto end;
++ }
++
++ acx_sem_lock(adev);
++
++ result = -EINVAL;
++ if (IW_RETRY_LIMIT == (vwrq->flags & IW_RETRY_TYPE)) {
++ printk("old retry limits: short %d long %d\n",
++ adev->short_retry, adev->long_retry);
++ if (vwrq->flags & IW_RETRY_MAX) {
++ adev->long_retry = vwrq->value;
++ } else if (vwrq->flags & IW_RETRY_MIN) {
++ adev->short_retry = vwrq->value;
++ } else {
++ /* no modifier: set both */
++ adev->long_retry = vwrq->value;
++ adev->short_retry = vwrq->value;
++ }
++ printk("new retry limits: short %d long %d\n",
++ adev->short_retry, adev->long_retry);
++ SET_BIT(adev->set_mask, GETSET_RETRY);
++ result = -EINPROGRESS;
++ }
++ else if (vwrq->flags & IW_RETRY_LIFETIME) {
++ adev->msdu_lifetime = vwrq->value;
++ printk("new MSDU lifetime: %d\n", adev->msdu_lifetime);
++ SET_BIT(adev->set_mask, SET_MSDU_LIFETIME);
++ result = -EINPROGRESS;
++ }
++
++ acx_sem_unlock(adev);
++end:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/************************ private ioctls ******************************/
++
++
++/***********************************************************************
++** acx_ioctl_set_debug
++*/
++#if ACX_DEBUG
++static int
++acx_ioctl_set_debug(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ unsigned int debug_new = *((unsigned int *)extra);
++ int result = -EINVAL;
++
++ log(L_ANY, "setting debug from %04X to %04X\n", acx_debug, debug_new);
++ acx_debug = debug_new;
++
++ result = OK;
++ return result;
++
++}
++#endif
++
++
++/***********************************************************************
++** acx_ioctl_list_reg_domain
++*/
++static int
++acx_ioctl_list_reg_domain(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ int i = 1;
++ const char * const *entry = acx_reg_domain_strings;
++
++ printk("dom# chan# domain/country\n");
++ while (*entry)
++ printk("%4d %s\n", i++, *entry++);
++ return OK;
++}
++
++
++/***********************************************************************
++** acx_ioctl_set_reg_domain
++*/
++static int
++acx_ioctl_set_reg_domain(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ int result;
++
++ FN_ENTER;
++
++ if ((*extra < 1) || ((size_t)*extra > acx_reg_domain_ids_len)) {
++ result = -EINVAL;
++ goto end;
++ }
++
++ acx_sem_lock(adev);
++
++ adev->reg_dom_id = acx_reg_domain_ids[*extra - 1];
++ SET_BIT(adev->set_mask, GETSET_REG_DOMAIN);
++
++ result = -EINPROGRESS;
++
++ acx_sem_unlock(adev);
++end:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acx_ioctl_get_reg_domain
++*/
++static int
++acx_ioctl_get_reg_domain(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ int dom,i;
++
++ /* no locking */
++ dom = adev->reg_dom_id;
++
++ for (i = 1; i <= acx_reg_domain_ids_len; i++) {
++ if (acx_reg_domain_ids[i-1] == dom) {
++ log(L_IOCTL, "regulatory domain is currently set "
++ "to %d (0x%X): %s\n", i, dom,
++ acx_reg_domain_strings[i-1]);
++ *extra = i;
++ break;
++ }
++ }
++
++ return OK;
++}
++
++
++/***********************************************************************
++** acx_ioctl_set_short_preamble
++*/
++static const char * const
++preamble_modes[] = {
++ "off",
++ "on",
++ "auto (peer capability dependent)",
++ "unknown mode, error"
++};
++
++static int
++acx_ioctl_set_short_preamble(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ int i;
++ int result;
++
++ FN_ENTER;
++
++ if ((unsigned char)*extra > 2) {
++ result = -EINVAL;
++ goto end;
++ }
++
++ acx_sem_lock(adev);
++
++ adev->preamble_mode = (u8)*extra;
++ switch (adev->preamble_mode) {
++ case 0: /* long */
++ adev->preamble_cur = 0;
++ break;
++ case 1:
++ /* short, kick incapable peers */
++ adev->preamble_cur = 1;
++ for (i = 0; i < VEC_SIZE(adev->sta_list); i++) {
++ client_t *clt = &adev->sta_list[i];
++ if (!clt->used) continue;
++ if (!(clt->cap_info & WF_MGMT_CAP_SHORT)) {
++ clt->used = CLIENT_EMPTY_SLOT_0;
++ }
++ }
++ switch (adev->mode) {
++ case ACX_MODE_2_STA:
++ if (adev->ap_client && !adev->ap_client->used) {
++ /* We kicked our AP :) */
++ SET_BIT(adev->set_mask, GETSET_RESCAN);
++ }
++ }
++ break;
++ case 2: /* auto. short only if all peers are short-capable */
++ adev->preamble_cur = 1;
++ for (i = 0; i < VEC_SIZE(adev->sta_list); i++) {
++ client_t *clt = &adev->sta_list[i];
++ if (!clt->used) continue;
++ if (!(clt->cap_info & WF_MGMT_CAP_SHORT)) {
++ adev->preamble_cur = 0;
++ break;
++ }
++ }
++ break;
++ }
++ printk("new short preamble setting: configured %s, active %s\n",
++ preamble_modes[adev->preamble_mode],
++ preamble_modes[adev->preamble_cur]);
++ result = OK;
++
++ acx_sem_unlock(adev);
++end:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acx_ioctl_get_short_preamble
++*/
++static int
++acx_ioctl_get_short_preamble(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++
++ acx_sem_lock(adev);
++
++ printk("current short preamble setting: configured %s, active %s\n",
++ preamble_modes[adev->preamble_mode],
++ preamble_modes[adev->preamble_cur]);
++
++ *extra = (char)adev->preamble_mode;
++
++ acx_sem_unlock(adev);
++
++ return OK;
++}
++
++
++/***********************************************************************
++** acx_ioctl_set_antenna
++**
++** TX and RX antenna can be set separately but this function good
++** for testing 0-4 bits
++*/
++static int
++acx_ioctl_set_antenna(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++
++ acx_sem_lock(adev);
++
++ printk("old antenna value: 0x%02X (COMBINED bit mask)\n"
++ "Rx antenna selection:\n"
++ "0x00 ant. 1\n"
++ "0x40 ant. 2\n"
++ "0x80 full diversity\n"
++ "0xc0 partial diversity\n"
++ "0x0f dwell time mask (in units of us)\n"
++ "Tx antenna selection:\n"
++ "0x00 ant. 2\n" /* yep, those ARE reversed! */
++ "0x20 ant. 1\n"
++ "new antenna value: 0x%02X\n",
++ adev->antenna, (u8)*extra);
++
++ adev->antenna = (u8)*extra;
++ SET_BIT(adev->set_mask, GETSET_ANTENNA);
++
++ acx_sem_unlock(adev);
++
++ return -EINPROGRESS;
++}
++
++
++/***********************************************************************
++** acx_ioctl_get_antenna
++*/
++static int
++acx_ioctl_get_antenna(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++
++ /* no locking. it's pointless to lock a single load */
++ printk("current antenna value: 0x%02X (COMBINED bit mask)\n"
++ "Rx antenna selection:\n"
++ "0x00 ant. 1\n"
++ "0x40 ant. 2\n"
++ "0x80 full diversity\n"
++ "0xc0 partial diversity\n"
++ "Tx antenna selection:\n"
++ "0x00 ant. 2\n" /* yep, those ARE reversed! */
++ "0x20 ant. 1\n", adev->antenna);
++
++ return 0;
++}
++
++
++/***********************************************************************
++** acx_ioctl_set_rx_antenna
++**
++** 0 = antenna1; 1 = antenna2; 2 = full diversity; 3 = partial diversity
++** Could anybody test which antenna is the external one?
++*/
++static int
++acx_ioctl_set_rx_antenna(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ int result;
++
++ FN_ENTER;
++
++ if (*extra > 3) {
++ result = -EINVAL;
++ goto end;
++ }
++
++ printk("old antenna value: 0x%02X\n", adev->antenna);
++
++ acx_sem_lock(adev);
++
++ adev->antenna &= 0x3f;
++ SET_BIT(adev->antenna, (*extra << 6));
++ SET_BIT(adev->set_mask, GETSET_ANTENNA);
++ printk("new antenna value: 0x%02X\n", adev->antenna);
++ result = -EINPROGRESS;
++
++ acx_sem_unlock(adev);
++end:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acx_ioctl_set_tx_antenna
++**
++** Arguments: 0 == antenna2; 1 == antenna1;
++** Could anybody test which antenna is the external one?
++*/
++static int
++acx_ioctl_set_tx_antenna(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ int result;
++
++ FN_ENTER;
++
++ if (*extra > 1) {
++ result = -EINVAL;
++ goto end;
++ }
++
++ printk("old antenna value: 0x%02X\n", adev->antenna);
++
++ acx_sem_lock(adev);
++
++ adev->antenna &= ~0x30;
++ SET_BIT(adev->antenna, ((*extra & 0x01) << 5));
++ SET_BIT(adev->set_mask, GETSET_ANTENNA);
++ printk("new antenna value: 0x%02X\n", adev->antenna);
++ result = -EINPROGRESS;
++
++ acx_sem_unlock(adev);
++end:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acx_ioctl_wlansniff
++**
++** can we just remove this in favor of monitor mode? --vda
++*/
++static int
++acx_ioctl_wlansniff(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ unsigned int *params = (unsigned int*)extra;
++ unsigned int enable = (unsigned int)(params[0] > 0);
++ int result;
++
++ FN_ENTER;
++
++ acx_sem_lock(adev);
++
++ /* not using printk() here, since it distorts kismet display
++ * when printk messages activated */
++ log(L_IOCTL, "setting monitor to: 0x%02X\n", params[0]);
++
++ switch (params[0]) {
++ case 0:
++ /* no monitor mode. hmm, should we simply ignore it
++ * or go back to enabling adev->netdev->type ARPHRD_ETHER? */
++ break;
++ case 1:
++ adev->monitor_type = ARPHRD_IEEE80211_PRISM;
++ break;
++ case 2:
++ adev->monitor_type = ARPHRD_IEEE80211;
++ break;
++ }
++
++ if (params[0]) {
++ adev->mode = ACX_MODE_MONITOR;
++ SET_BIT(adev->set_mask, GETSET_MODE);
++ }
++
++ if (enable) {
++ adev->channel = params[1];
++ SET_BIT(adev->set_mask, GETSET_RX);
++ }
++ result = -EINPROGRESS;
++
++ acx_sem_unlock(adev);
++
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acx_ioctl_unknown11
++** FIXME: looks like some sort of "iwpriv kick_sta MAC" but it's broken
++*/
++static int
++acx_ioctl_unknown11(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++#ifdef BROKEN
++ struct iw_param *vwrq = &wrqu->param;
++ acx_device_t *adev = ndev2adev(ndev);
++ unsigned long flags;
++ client_t client;
++ int result;
++
++ acx_sem_lock(adev);
++ acx_lock(adev, flags);
++
++ acx_l_transmit_disassoc(adev, &client);
++ result = OK;
++
++ acx_unlock(adev, flags);
++ acx_sem_unlock(adev);
++
++ return result;
++#endif
++ return -EINVAL;
++}
++
++
++/***********************************************************************
++** debug helper function to be able to debug various issues relatively easily
++*/
++static int
++acx_ioctl_dbg_set_masks(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ const unsigned int *params = (unsigned int*)extra;
++ int result;
++
++ acx_sem_lock(adev);
++
++ log(L_IOCTL, "setting flags in settings mask: "
++ "get_mask %08X set_mask %08X\n"
++ "before: get_mask %08X set_mask %08X\n",
++ params[0], params[1],
++ adev->get_mask, adev->set_mask);
++ SET_BIT(adev->get_mask, params[0]);
++ SET_BIT(adev->set_mask, params[1]);
++ log(L_IOCTL, "after: get_mask %08X set_mask %08X\n",
++ adev->get_mask, adev->set_mask);
++ result = -EINPROGRESS; /* immediately call commit handler */
++
++ acx_sem_unlock(adev);
++
++ return result;
++}
++
++
++/***********************************************************************
++* acx_ioctl_set_rates
++*
++* This ioctl takes string parameter. Examples:
++* iwpriv wlan0 SetRates "1,2"
++* use 1 and 2 Mbit rates, both are in basic rate set
++* iwpriv wlan0 SetRates "1,2 5,11"
++* use 1,2,5.5,11 Mbit rates. 1 and 2 are basic
++* iwpriv wlan0 SetRates "1,2 5c,11c"
++* same ('c' means 'CCK modulation' and it is a default for 5 and 11)
++* iwpriv wlan0 SetRates "1,2 5p,11p"
++* use 1,2,5.5,11 Mbit, 1,2 are basic. 5 and 11 are using PBCC
++* iwpriv wlan0 SetRates "1,2,5,11 22p"
++* use 1,2,5.5,11,22 Mbit. 1,2,5.5 and 11 are basic. 22 is using PBCC
++* (this is the maximum acx100 can do (modulo x4 mode))
++* iwpriv wlan0 SetRates "1,2,5,11 22"
++* same. 802.11 defines only PBCC modulation
++* for 22 and 33 Mbit rates, so there is no ambiguity
++* iwpriv wlan0 SetRates "1,2,5,11 6o,9o,12o,18o,24o,36o,48o,54o"
++* 1,2,5.5 and 11 are basic. 11g OFDM rates are enabled but
++* they are not in basic rate set. 22 Mbit is disabled.
++* iwpriv wlan0 SetRates "1,2,5,11 6,9,12,18,24,36,48,54"
++* same. OFDM is default for 11g rates except 22 and 33 Mbit,
++* thus 'o' is optional
++* iwpriv wlan0 SetRates "1,2,5,11 6d,9d,12d,18d,24d,36d,48d,54d"
++* 1,2,5.5 and 11 are basic. 11g CCK-OFDM rates are enabled
++* (acx111 does not support CCK-OFDM, driver will reject this cmd)
++* iwpriv wlan0 SetRates "6,9,12 18,24,36,48,54"
++* 6,9,12 are basic, rest of 11g rates is enabled. Using OFDM
++*/
++#include "setrate.c"
++
++/* disallow: 33Mbit (unsupported by hw) */
++/* disallow: CCKOFDM (unsupported by hw) */
++static int
++acx111_supported(int mbit, int modulation, void *opaque)
++{
++ if (mbit==33) return -ENOTSUPP;
++ if (modulation==DOT11_MOD_CCKOFDM) return -ENOTSUPP;
++ return OK;
++}
++
++static const u16
++acx111mask[] = {
++ [DOT11_RATE_1 ] = RATE111_1 ,
++ [DOT11_RATE_2 ] = RATE111_2 ,
++ [DOT11_RATE_5 ] = RATE111_5 ,
++ [DOT11_RATE_11] = RATE111_11,
++ [DOT11_RATE_22] = RATE111_22,
++ /* [DOT11_RATE_33] = */
++ [DOT11_RATE_6 ] = RATE111_6 ,
++ [DOT11_RATE_9 ] = RATE111_9 ,
++ [DOT11_RATE_12] = RATE111_12,
++ [DOT11_RATE_18] = RATE111_18,
++ [DOT11_RATE_24] = RATE111_24,
++ [DOT11_RATE_36] = RATE111_36,
++ [DOT11_RATE_48] = RATE111_48,
++ [DOT11_RATE_54] = RATE111_54,
++};
++
++static u32
++acx111_gen_mask(int mbit, int modulation, void *opaque)
++{
++ /* lower 16 bits show selected 1, 2, CCK and OFDM rates */
++ /* upper 16 bits show selected PBCC rates */
++ u32 m = acx111mask[rate_mbit2enum(mbit)];
++ if (modulation==DOT11_MOD_PBCC)
++ return m<<16;
++ return m;
++}
++
++static int
++verify_rate(u32 rate, int chip_type)
++{
++ /* never happens. be paranoid */
++ if (!rate) return -EINVAL;
++
++ /* disallow: mixing PBCC and CCK at 5 and 11Mbit
++ ** (can be supported, but needs complicated handling in tx code) */
++ if (( rate & ((RATE111_11+RATE111_5)<<16) )
++ && ( rate & (RATE111_11+RATE111_5) )
++ ) {
++ return -ENOTSUPP;
++ }
++ if (CHIPTYPE_ACX100 == chip_type) {
++ if ( rate & ~(RATE111_ACX100_COMPAT+(RATE111_ACX100_COMPAT<<16)) )
++ return -ENOTSUPP;
++ }
++ return 0;
++}
++
++static int
++acx_ioctl_set_rates(struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ unsigned long flags;
++ int result;
++ u32 brate = 0, orate = 0; /* basic, operational rate set */
++
++ FN_ENTER;
++
++ log(L_IOCTL, "set_rates %s\n", extra);
++ result = fill_ratemasks(extra, &brate, &orate,
++ acx111_supported, acx111_gen_mask, 0);
++ if (result) goto end;
++ SET_BIT(orate, brate);
++ log(L_IOCTL, "brate %08X orate %08X\n", brate, orate);
++
++ result = verify_rate(brate, adev->chip_type);
++ if (result) goto end;
++ result = verify_rate(orate, adev->chip_type);
++ if (result) goto end;
++
++ acx_sem_lock(adev);
++ acx_lock(adev, flags);
++
++ adev->rate_basic = brate;
++ adev->rate_oper = orate;
++ /* TODO: ideally, we shall monitor highest basic rate
++ ** which was successfully sent to every peer
++ ** (say, last we checked, everybody could hear 5.5 Mbits)
++ ** and use that for bcasts when we want to reach all peers.
++ ** For beacons, we probably shall use lowest basic rate
++ ** because we want to reach all *potential* new peers too */
++ adev->rate_bcast = 1 << lowest_bit(brate);
++ if (IS_ACX100(adev))
++ adev->rate_bcast100 = acx_rate111to100(adev->rate_bcast);
++ adev->rate_auto = !has_only_one_bit(orate);
++ acx_l_update_client_rates(adev, orate);
++ /* TODO: get rid of ratevector, build it only when needed */
++ acx_l_update_ratevector(adev);
++
++ /* Do/don't do tx rate fallback; beacon contents and rate */
++ SET_BIT(adev->set_mask, SET_RATE_FALLBACK|SET_TEMPLATES);
++ result = -EINPROGRESS;
++
++ acx_unlock(adev, flags);
++ acx_sem_unlock(adev);
++end:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acx_ioctl_get_phy_chan_busy_percentage
++*/
++static int
++acx_ioctl_get_phy_chan_busy_percentage(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ struct {
++ u16 type;
++ u16 len;
++ u32 busytime;
++ u32 totaltime;
++ } ACX_PACKED usage;
++ int result;
++
++ acx_sem_lock(adev);
++
++ if (OK != acx_s_interrogate(adev, &usage, ACX1xx_IE_MEDIUM_USAGE)) {
++ result = NOT_OK;
++ goto end_unlock;
++ }
++
++ usage.busytime = le32_to_cpu(usage.busytime);
++ usage.totaltime = le32_to_cpu(usage.totaltime);
++
++ /* yes, this is supposed to be "Medium" (singular of media),
++ not "average"! OK, reword the message to make it obvious... */
++ printk("%s: busy percentage of medium (since last invocation): %d%% "
++ "(%u of %u microseconds)\n",
++ ndev->name,
++ usage.busytime / ((usage.totaltime / 100) + 1),
++ usage.busytime, usage.totaltime);
++
++ result = OK;
++
++end_unlock:
++ acx_sem_unlock(adev);
++
++ return result;
++}
++
++
++/***********************************************************************
++** acx_ioctl_set_ed_threshold
++*/
++static inline int
++acx_ioctl_set_ed_threshold(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++
++ acx_sem_lock(adev);
++
++ printk("old ED threshold value: %d\n", adev->ed_threshold);
++ adev->ed_threshold = (unsigned char)*extra;
++ printk("new ED threshold value: %d\n", (unsigned char)*extra);
++ SET_BIT(adev->set_mask, GETSET_ED_THRESH);
++
++ acx_sem_unlock(adev);
++
++ return -EINPROGRESS;
++}
++
++
++/***********************************************************************
++** acx_ioctl_set_cca
++*/
++static inline int
++acx_ioctl_set_cca(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ int result;
++
++ acx_sem_lock(adev);
++
++ printk("old CCA value: 0x%02X\n", adev->cca);
++ adev->cca = (unsigned char)*extra;
++ printk("new CCA value: 0x%02X\n", (unsigned char)*extra);
++ SET_BIT(adev->set_mask, GETSET_CCA);
++ result = -EINPROGRESS;
++
++ acx_sem_unlock(adev);
++
++ return result;
++}
++
++
++/***********************************************************************
++*/
++static const char * const
++scan_modes[] = { "active", "passive", "background" };
++
++static void
++acx_print_scan_params(acx_device_t *adev, const char* head)
++{
++ printk("%s: %smode %d (%s), min chan time %dTU, "
++ "max chan time %dTU, max scan rate byte: %d\n",
++ adev->ndev->name, head,
++ adev->scan_mode, scan_modes[adev->scan_mode],
++ adev->scan_probe_delay, adev->scan_duration, adev->scan_rate);
++}
++
++static int
++acx_ioctl_set_scan_params(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ int result;
++ const int *params = (int *)extra;
++
++ acx_sem_lock(adev);
++
++ acx_print_scan_params(adev, "old scan parameters: ");
++ if ((params[0] != -1) && (params[0] >= 0) && (params[0] <= 2))
++ adev->scan_mode = params[0];
++ if (params[1] != -1)
++ adev->scan_probe_delay = params[1];
++ if (params[2] != -1)
++ adev->scan_duration = params[2];
++ if ((params[3] != -1) && (params[3] <= 255))
++ adev->scan_rate = params[3];
++ acx_print_scan_params(adev, "new scan parameters: ");
++ SET_BIT(adev->set_mask, GETSET_RESCAN);
++ result = -EINPROGRESS;
++
++ acx_sem_unlock(adev);
++
++ return result;
++}
++
++static int
++acx_ioctl_get_scan_params(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ int result;
++ int *params = (int *)extra;
++
++ acx_sem_lock(adev);
++
++ acx_print_scan_params(adev, "current scan parameters: ");
++ params[0] = adev->scan_mode;
++ params[1] = adev->scan_probe_delay;
++ params[2] = adev->scan_duration;
++ params[3] = adev->scan_rate;
++ result = OK;
++
++ acx_sem_unlock(adev);
++
++ return result;
++}
++
++
++/***********************************************************************
++*/
++static int
++acx100_ioctl_set_led_power(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ static const char * const led_modes[] = { "off", "on", "LinkQuality" };
++
++ acx_device_t *adev = ndev2adev(ndev);
++ int result;
++
++ acx_sem_lock(adev);
++
++ printk("%s: power LED status: old %d (%s), ",
++ ndev->name,
++ adev->led_power,
++ led_modes[adev->led_power]);
++ adev->led_power = extra[0];
++ if (adev->led_power > 2) adev->led_power = 2;
++ printk("new %d (%s)\n",
++ adev->led_power,
++ led_modes[adev->led_power]);
++
++ if (adev->led_power == 2) {
++ printk("%s: max link quality setting: old %d, ",
++ ndev->name, adev->brange_max_quality);
++ if (extra[1])
++ adev->brange_max_quality = extra[1];
++ printk("new %d\n", adev->brange_max_quality);
++ }
++
++ SET_BIT(adev->set_mask, GETSET_LED_POWER);
++
++ result = -EINPROGRESS;
++
++ acx_sem_unlock(adev);
++
++ return result;
++}
++
++
++/***********************************************************************
++*/
++static inline int
++acx100_ioctl_get_led_power(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++
++ acx_sem_lock(adev);
++
++ extra[0] = adev->led_power;
++ if (adev->led_power == 2)
++ extra[1] = adev->brange_max_quality;
++ else
++ extra[1] = -1;
++
++ acx_sem_unlock(adev);
++
++ return OK;
++}
++
++
++/***********************************************************************
++*/
++static int
++acx111_ioctl_info(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ struct iw_param *vwrq = &wrqu->param;
++ if (!IS_PCI(ndev2adev(ndev)))
++ return OK;
++ return acx111pci_ioctl_info(ndev, info, vwrq, extra);
++}
++
++
++/***********************************************************************
++*/
++static int
++acx100_ioctl_set_phy_amp_bias(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ union iwreq_data *wrqu,
++ char *extra)
++{
++ struct iw_param *vwrq = &wrqu->param;
++ if (IS_USB(ndev2adev(ndev))) {
++ printk("acx: set_phy_amp_bias() is not supported on USB\n");
++ return OK;
++ }
++#ifdef ACX_MEM
++ return acx100mem_ioctl_set_phy_amp_bias(ndev, info, vwrq, extra);
++#else
++ return acx100pci_ioctl_set_phy_amp_bias(ndev, info, vwrq, extra);
++#endif
++}
++
++
++/***********************************************************************
++*/
++static const iw_handler acx_ioctl_handler[] =
++{
++ acx_ioctl_commit, /* SIOCSIWCOMMIT */
++ acx_ioctl_get_name, /* SIOCGIWNAME */
++ NULL, /* SIOCSIWNWID */
++ NULL, /* SIOCGIWNWID */
++ acx_ioctl_set_freq, /* SIOCSIWFREQ */
++ acx_ioctl_get_freq, /* SIOCGIWFREQ */
++ acx_ioctl_set_mode, /* SIOCSIWMODE */
++ acx_ioctl_get_mode, /* SIOCGIWMODE */
++ acx_ioctl_set_sens, /* SIOCSIWSENS */
++ acx_ioctl_get_sens, /* SIOCGIWSENS */
++ NULL, /* SIOCSIWRANGE */
++ acx_ioctl_get_range, /* SIOCGIWRANGE */
++ NULL, /* SIOCSIWPRIV */
++ NULL, /* SIOCGIWPRIV */
++ NULL, /* SIOCSIWSTATS */
++ NULL, /* SIOCGIWSTATS */
++#if IW_HANDLER_VERSION > 4
++ iw_handler_set_spy, /* SIOCSIWSPY */
++ iw_handler_get_spy, /* SIOCGIWSPY */
++ iw_handler_set_thrspy, /* SIOCSIWTHRSPY */
++ iw_handler_get_thrspy, /* SIOCGIWTHRSPY */
++#else /* IW_HANDLER_VERSION > 4 */
++#ifdef WIRELESS_SPY
++ NULL /* acx_ioctl_set_spy FIXME */, /* SIOCSIWSPY */
++ NULL /* acx_ioctl_get_spy */, /* SIOCGIWSPY */
++#else /* WSPY */
++ NULL, /* SIOCSIWSPY */
++ NULL, /* SIOCGIWSPY */
++#endif /* WSPY */
++ NULL, /* [nothing] */
++ NULL, /* [nothing] */
++#endif /* IW_HANDLER_VERSION > 4 */
++ acx_ioctl_set_ap, /* SIOCSIWAP */
++ acx_ioctl_get_ap, /* SIOCGIWAP */
++ NULL, /* [nothing] */
++ acx_ioctl_get_aplist, /* SIOCGIWAPLIST */
++ acx_ioctl_set_scan, /* SIOCSIWSCAN */
++ acx_ioctl_get_scan, /* SIOCGIWSCAN */
++ acx_ioctl_set_essid, /* SIOCSIWESSID */
++ acx_ioctl_get_essid, /* SIOCGIWESSID */
++ acx_ioctl_set_nick, /* SIOCSIWNICKN */
++ acx_ioctl_get_nick, /* SIOCGIWNICKN */
++ NULL, /* [nothing] */
++ NULL, /* [nothing] */
++ acx_ioctl_set_rate, /* SIOCSIWRATE */
++ acx_ioctl_get_rate, /* SIOCGIWRATE */
++ acx_ioctl_set_rts, /* SIOCSIWRTS */
++ acx_ioctl_get_rts, /* SIOCGIWRTS */
++#if ACX_FRAGMENTATION
++ acx_ioctl_set_frag, /* SIOCSIWFRAG */
++ acx_ioctl_get_frag, /* SIOCGIWFRAG */
++#else
++ NULL, /* SIOCSIWFRAG */
++ NULL, /* SIOCGIWFRAG */
++#endif
++ acx_ioctl_set_txpow, /* SIOCSIWTXPOW */
++ acx_ioctl_get_txpow, /* SIOCGIWTXPOW */
++ acx_ioctl_set_retry, /* SIOCSIWRETRY */
++ acx_ioctl_get_retry, /* SIOCGIWRETRY */
++ acx_ioctl_set_encode, /* SIOCSIWENCODE */
++ acx_ioctl_get_encode, /* SIOCGIWENCODE */
++ acx_ioctl_set_power, /* SIOCSIWPOWER */
++ acx_ioctl_get_power, /* SIOCGIWPOWER */
++};
++
++
++/***********************************************************************
++*/
++
++/* if you plan to reorder something, make sure to reorder all other places
++ * accordingly! */
++/* SET/GET convention: SETs must have even position, GETs odd */
++#define ACX100_IOCTL SIOCIWFIRSTPRIV
++enum {
++ ACX100_IOCTL_DEBUG = ACX100_IOCTL,
++ ACX100_IOCTL_GET__________UNUSED1,
++ ACX100_IOCTL_SET_PLED,
++ ACX100_IOCTL_GET_PLED,
++ ACX100_IOCTL_SET_RATES,
++ ACX100_IOCTL_LIST_DOM,
++ ACX100_IOCTL_SET_DOM,
++ ACX100_IOCTL_GET_DOM,
++ ACX100_IOCTL_SET_SCAN_PARAMS,
++ ACX100_IOCTL_GET_SCAN_PARAMS,
++ ACX100_IOCTL_SET_PREAMB,
++ ACX100_IOCTL_GET_PREAMB,
++ ACX100_IOCTL_SET_ANT,
++ ACX100_IOCTL_GET_ANT,
++ ACX100_IOCTL_RX_ANT,
++ ACX100_IOCTL_TX_ANT,
++ ACX100_IOCTL_SET_PHY_AMP_BIAS,
++ ACX100_IOCTL_GET_PHY_CHAN_BUSY,
++ ACX100_IOCTL_SET_ED,
++ ACX100_IOCTL_GET__________UNUSED3,
++ ACX100_IOCTL_SET_CCA,
++ ACX100_IOCTL_GET__________UNUSED4,
++ ACX100_IOCTL_MONITOR,
++ ACX100_IOCTL_TEST,
++ ACX100_IOCTL_DBG_SET_MASKS,
++ ACX111_IOCTL_INFO,
++ ACX100_IOCTL_DBG_SET_IO,
++ ACX100_IOCTL_DBG_GET_IO
++};
++
++
++static const iw_handler acx_ioctl_private_handler[] =
++{
++#if ACX_DEBUG
++[ACX100_IOCTL_DEBUG - ACX100_IOCTL] = acx_ioctl_set_debug,
++#endif
++[ACX100_IOCTL_SET_PLED - ACX100_IOCTL] = acx100_ioctl_set_led_power,
++[ACX100_IOCTL_GET_PLED - ACX100_IOCTL] = acx100_ioctl_get_led_power,
++[ACX100_IOCTL_SET_RATES - ACX100_IOCTL] = acx_ioctl_set_rates,
++[ACX100_IOCTL_LIST_DOM - ACX100_IOCTL] = acx_ioctl_list_reg_domain,
++[ACX100_IOCTL_SET_DOM - ACX100_IOCTL] = acx_ioctl_set_reg_domain,
++[ACX100_IOCTL_GET_DOM - ACX100_IOCTL] = acx_ioctl_get_reg_domain,
++[ACX100_IOCTL_SET_SCAN_PARAMS - ACX100_IOCTL] = acx_ioctl_set_scan_params,
++[ACX100_IOCTL_GET_SCAN_PARAMS - ACX100_IOCTL] = acx_ioctl_get_scan_params,
++[ACX100_IOCTL_SET_PREAMB - ACX100_IOCTL] = acx_ioctl_set_short_preamble,
++[ACX100_IOCTL_GET_PREAMB - ACX100_IOCTL] = acx_ioctl_get_short_preamble,
++[ACX100_IOCTL_SET_ANT - ACX100_IOCTL] = acx_ioctl_set_antenna,
++[ACX100_IOCTL_GET_ANT - ACX100_IOCTL] = acx_ioctl_get_antenna,
++[ACX100_IOCTL_RX_ANT - ACX100_IOCTL] = acx_ioctl_set_rx_antenna,
++[ACX100_IOCTL_TX_ANT - ACX100_IOCTL] = acx_ioctl_set_tx_antenna,
++[ACX100_IOCTL_SET_PHY_AMP_BIAS - ACX100_IOCTL] = acx100_ioctl_set_phy_amp_bias,
++[ACX100_IOCTL_GET_PHY_CHAN_BUSY - ACX100_IOCTL] = acx_ioctl_get_phy_chan_busy_percentage,
++[ACX100_IOCTL_SET_ED - ACX100_IOCTL] = acx_ioctl_set_ed_threshold,
++[ACX100_IOCTL_SET_CCA - ACX100_IOCTL] = acx_ioctl_set_cca,
++[ACX100_IOCTL_MONITOR - ACX100_IOCTL] = acx_ioctl_wlansniff,
++[ACX100_IOCTL_TEST - ACX100_IOCTL] = acx_ioctl_unknown11,
++[ACX100_IOCTL_DBG_SET_MASKS - ACX100_IOCTL] = acx_ioctl_dbg_set_masks,
++[ACX111_IOCTL_INFO - ACX100_IOCTL] = acx111_ioctl_info,
++};
++
++
++static const struct iw_priv_args acx_ioctl_private_args[] = {
++#if ACX_DEBUG
++{ cmd : ACX100_IOCTL_DEBUG,
++ set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
++ get_args : 0,
++ name : "SetDebug" },
++#endif
++{ cmd : ACX100_IOCTL_SET_PLED,
++ set_args : IW_PRIV_TYPE_BYTE | 2,
++ get_args : 0,
++ name : "SetLEDPower" },
++{ cmd : ACX100_IOCTL_GET_PLED,
++ set_args : 0,
++ get_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 2,
++ name : "GetLEDPower" },
++{ cmd : ACX100_IOCTL_SET_RATES,
++ set_args : IW_PRIV_TYPE_CHAR | 256,
++ get_args : 0,
++ name : "SetRates" },
++{ cmd : ACX100_IOCTL_LIST_DOM,
++ set_args : 0,
++ get_args : 0,
++ name : "ListRegDomain" },
++{ cmd : ACX100_IOCTL_SET_DOM,
++ set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
++ get_args : 0,
++ name : "SetRegDomain" },
++{ cmd : ACX100_IOCTL_GET_DOM,
++ set_args : 0,
++ get_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
++ name : "GetRegDomain" },
++{ cmd : ACX100_IOCTL_SET_SCAN_PARAMS,
++ set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 4,
++ get_args : 0,
++ name : "SetScanParams" },
++{ cmd : ACX100_IOCTL_GET_SCAN_PARAMS,
++ set_args : 0,
++ get_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 4,
++ name : "GetScanParams" },
++{ cmd : ACX100_IOCTL_SET_PREAMB,
++ set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
++ get_args : 0,
++ name : "SetSPreamble" },
++{ cmd : ACX100_IOCTL_GET_PREAMB,
++ set_args : 0,
++ get_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
++ name : "GetSPreamble" },
++{ cmd : ACX100_IOCTL_SET_ANT,
++ set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
++ get_args : 0,
++ name : "SetAntenna" },
++{ cmd : ACX100_IOCTL_GET_ANT,
++ set_args : 0,
++ get_args : 0,
++ name : "GetAntenna" },
++{ cmd : ACX100_IOCTL_RX_ANT,
++ set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
++ get_args : 0,
++ name : "SetRxAnt" },
++{ cmd : ACX100_IOCTL_TX_ANT,
++ set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
++ get_args : 0,
++ name : "SetTxAnt" },
++{ cmd : ACX100_IOCTL_SET_PHY_AMP_BIAS,
++ set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
++ get_args : 0,
++ name : "SetPhyAmpBias"},
++{ cmd : ACX100_IOCTL_GET_PHY_CHAN_BUSY,
++ set_args : 0,
++ get_args : 0,
++ name : "GetPhyChanBusy" },
++{ cmd : ACX100_IOCTL_SET_ED,
++ set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
++ get_args : 0,
++ name : "SetED" },
++{ cmd : ACX100_IOCTL_SET_CCA,
++ set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1,
++ get_args : 0,
++ name : "SetCCA" },
++{ cmd : ACX100_IOCTL_MONITOR,
++ set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2,
++ get_args : 0,
++ name : "monitor" },
++{ cmd : ACX100_IOCTL_TEST,
++ set_args : 0,
++ get_args : 0,
++ name : "Test" },
++{ cmd : ACX100_IOCTL_DBG_SET_MASKS,
++ set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2,
++ get_args : 0,
++ name : "DbgSetMasks" },
++{ cmd : ACX111_IOCTL_INFO,
++ set_args : 0,
++ get_args : 0,
++ name : "GetAcx111Info" },
++{ cmd : ACX100_IOCTL_DBG_SET_IO,
++ set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 4,
++ get_args : 0,
++ name : "DbgSetIO" },
++{ cmd : ACX100_IOCTL_DBG_GET_IO,
++ set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 3,
++ get_args : 0,
++ name : "DbgGetIO" },
++};
++
++
++const struct iw_handler_def acx_ioctl_handler_def =
++{
++ .num_standard = VEC_SIZE(acx_ioctl_handler),
++ .num_private = VEC_SIZE(acx_ioctl_private_handler),
++ .num_private_args = VEC_SIZE(acx_ioctl_private_args),
++ .standard = (iw_handler *) acx_ioctl_handler,
++ .private = (iw_handler *) acx_ioctl_private_handler,
++ .private_args = (struct iw_priv_args *) acx_ioctl_private_args,
++#if IW_HANDLER_VERSION > 5
++ .get_wireless_stats = acx_e_get_wireless_stats
++#endif /* IW > 5 */
++};
+Index: linux-2.6.23/drivers/net/wireless/acx/Kconfig
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23/drivers/net/wireless/acx/Kconfig 2008-01-20 21:13:40.000000000 +0000
+@@ -0,0 +1,113 @@
++config ACX
++ tristate "TI acx100/acx111 802.11b/g wireless chipsets"
++ depends on NET_RADIO && EXPERIMENTAL
++ select FW_LOADER
++ ---help---
++ A driver for 802.11b/g wireless cards based on
++ Texas Instruments acx100 and acx111 chipsets.
++
++ This driver supports Host AP mode that allows
++ your computer to act as an IEEE 802.11 access point.
++ This driver is new and experimental.
++
++ Texas Instruments did not take part in development of this driver
++ in any way, shape or form.
++
++ The driver can be compiled as a module and will be named "acx".
++
++config ACX_PCI
++ bool "TI acx100/acx111 802.11b/g PCI"
++ depends on ACX && PCI
++ ---help---
++ Include PCI and CardBus support in acx.
++
++ acx chipsets need their firmware loaded at startup.
++ You will need to provide a firmware image via hotplug.
++
++ Firmware may be in a form of single image 40-100kb in size
++ (a 'combined' firmware) or two images - main image
++ (again 40-100kb) and radio image (~10kb or less).
++
++ Firmware images are requested from hotplug using following names:
++
++ tiacx100 - main firmware image for acx100 chipset
++ tiacx100rNN - radio acx100 firmware for radio type NN
++ tiacx100cNN - combined acx100 firmware for radio type NN
++ tiacx111 - main acx111 firmware
++ tiacx111rNN - radio acx111 firmware for radio type NN
++ tiacx111cNN - combined acx111 firmware for radio type NN
++
++ Driver will attempt to load combined image first.
++ If no such image is found, it will try to load main image
++ and radio image instead.
++
++ Firmware files are not covered by GPL and are not distributed
++ with this driver for legal reasons.
++
++config ACX_USB
++ bool "TI acx100/acx111 802.11b/g USB"
++ depends on ACX && (USB=y || USB=ACX)
++ ---help---
++ Include USB support in acx.
++
++ There is only one currently known device in this category,
++ D-Link DWL-120+, but newer devices seem to be on the horizon.
++
++ acx chipsets need their firmware loaded at startup.
++ You will need to provide a firmware image via hotplug.
++
++ Firmware for USB device is requested from hotplug
++ by the 'tiacx100usb' name.
++
++ Firmware files are not covered by GPL and are not distributed
++ with this driver for legal reasons.
++
++config ACX_MEM
++ bool "TI acx100/acx111 802.11b/g memory mapped slave 16 interface"
++ depends on ACX
++ ---help---
++ acx chipsets need their firmware loaded at startup.
++ You will need to provide a firmware image via hotplug.
++
++ Firmware for USB device is requested from hotplug
++ by the 'tiacx100usb' name.
++
++ Firmware files are not covered by GPL and are not distributed
++ with this driver for legal reasons.
++
++config ACX_CS
++ bool "TI acx100/acx111 802.11b/g cardbus interface"
++ depends on ACX
++ ---help---
++ acx chipsets need their firmware loaded at startup.
++ You will need to provide a firmware image via hotplug.
++
++ This driver is based on memory mapped driver.
++
++ Firmware files are not covered by GPL and are not distributed
++ with this driver for legal reasons.
++
++config ACX_HX4700
++ tristate "ACX support for the iPAQ hx4700 using ACX_MEM"
++ depends on HX4700_CORE && ACX_MEM
++ ---help---
++ Include memory interface support in acx for the iPAQ hx4700.
++
++config ACX_HTCUNIVERSAL
++ tristate "ACX support for the HTC Universal using ACX_MEM"
++ depends on HTCUNIVERSAL_CORE && HTC_ASIC3 && ACX_MEM
++ ---help---
++ Include memory interface support in acx for the HTC Universal.
++
++config ACX_HTCSABLE
++ tristate "ACX support for the HTC Sable (IPAQ hw6915) using ACX_MEM"
++ depends on MACH_HW6900 && HTC_ASIC3 && ACX_MEM
++ ---help---
++ Include memory interface support in acx for the HTC Sable (IPAQ hw6915).
++
++config ACX_RX3000
++ tristate "ACX support for the iPAQ RX3000 using ACX_MEM"
++ depends on MACH_RX3715 && ACX_MEM && LEDS_ASIC3
++ ---help---
++ Include memory interface support in acx for the IPAQ RX3000.
++
+Index: linux-2.6.23/drivers/net/wireless/acx/Makefile
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23/drivers/net/wireless/acx/Makefile 2008-01-20 21:13:40.000000000 +0000
+@@ -0,0 +1,21 @@
++#obj-m += acx.o
++
++#acx-obj-y += pci.o
++#acx-obj-y += usb.o
++
++#acx-objs := wlan.o conv.o ioctl.o common.o $(acx-obj-y)
++
++# Use this if you have proper Kconfig integration:
++
++obj-$(CONFIG_ACX) += acx.o
++obj-$(CONFIG_ACX_HX4700) += hx4700_acx.o
++obj-$(CONFIG_ACX_HTCUNIVERSAL) += htcuniversal_acx.o
++obj-$(CONFIG_ACX_HTCSABLE) += htcsable_acx.o
++obj-$(CONFIG_ACX_RX3000) += rx3000_acx.o
++#
++acx-obj-$(CONFIG_ACX_PCI) += pci.o
++acx-obj-$(CONFIG_ACX_USB) += usb.o
++acx-obj-$(CONFIG_ACX_MEM) += mem.o
++acx-obj-$(CONFIG_ACX_CS) += cs.o
++#
++acx-objs := wlan.o conv.o ioctl.o common.o $(acx-obj-y)
+Index: linux-2.6.23/drivers/net/wireless/acx/mem.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23/drivers/net/wireless/acx/mem.c 2008-01-20 21:13:40.000000000 +0000
+@@ -0,0 +1,5363 @@
++/***********************************************************************
++** Copyright (C) 2003 ACX100 Open Source Project
++**
++** The contents of this file are subject to the Mozilla Public
++** License Version 1.1 (the "License"); you may not use this file
++** except in compliance with the License. You may obtain a copy of
++** the License at http://www.mozilla.org/MPL/
++**
++** Software distributed under the License is distributed on an "AS
++** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
++** implied. See the License for the specific language governing
++** rights and limitations under the License.
++**
++** Alternatively, the contents of this file may be used under the
++** terms of the GNU Public License version 2 (the "GPL"), in which
++** case the provisions of the GPL are applicable instead of the
++** above. If you wish to allow the use of your version of this file
++** only under the terms of the GPL and not to allow others to use
++** your version of this file under the MPL, indicate your decision
++** by deleting the provisions above and replace them with the notice
++** and other provisions required by the GPL. If you do not delete
++** the provisions above, a recipient may use your version of this
++** file under either the MPL or the GPL.
++** ---------------------------------------------------------------------
++** Inquiries regarding the ACX100 Open Source Project can be
++** made directly to:
++**
++** acx100-users@lists.sf.net
++** http://acx100.sf.net
++** ---------------------------------------------------------------------
++**
++** Slave memory interface support:
++**
++** Todd Blumer - SDG Systems
++** Bill Reese - HP
++** Eric McCorkle - Shadowsun
++*/
++#define ACX_MEM 1
++
++/*
++ * non-zero makes it dump the ACX memory to the console then
++ * panic when you cat /proc/driver/acx_wlan0_diag
++ */
++#define DUMP_MEM_DEFINED 1
++
++#define DUMP_MEM_DURING_DIAG 0
++#define DUMP_IF_SLOW 0
++
++#define PATCH_AROUND_BAD_SPOTS 1
++#define HX4700_FIRMWARE_CHECKSUM 0x0036862e
++#define HX4700_ALTERNATE_FIRMWARE_CHECKSUM 0x00368a75
++
++#include <linux/version.h>
++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18)
++#include <linux/config.h>
++#endif
++
++/* Linux 2.6.18+ uses <linux/utsrelease.h> */
++#ifndef UTS_RELEASE
++#include <linux/utsrelease.h>
++#endif
++
++#include <linux/compiler.h> /* required for Lx 2.6.8 ?? */
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/sched.h>
++#include <linux/types.h>
++#include <linux/skbuff.h>
++#include <linux/slab.h>
++#include <linux/if_arp.h>
++#include <linux/irq.h>
++#include <linux/rtnetlink.h>
++#include <linux/wireless.h>
++#include <net/iw_handler.h>
++#include <linux/netdevice.h>
++#include <linux/ioport.h>
++#include <linux/pci.h>
++#include <linux/platform_device.h>
++#include <linux/pm.h>
++#include <linux/vmalloc.h>
++#include <linux/delay.h>
++#include <linux/workqueue.h>
++#include <linux/inetdevice.h>
++
++#include "acx.h"
++#include "acx_hw.h"
++
++/***********************************************************************
++*/
++
++#define CARD_EEPROM_ID_SIZE 6
++
++#include <asm/io.h>
++
++#define REG_ACX_VENDOR_ID 0x900
++/*
++ * This is the vendor id on the HX4700, anyway
++ */
++#define ACX_VENDOR_ID 0x8400104c
++
++typedef enum {
++ ACX_SOFT_RESET = 0,
++
++ ACX_SLV_REG_ADDR,
++ ACX_SLV_REG_DATA,
++ ACX_SLV_REG_ADATA,
++
++ ACX_SLV_MEM_CP,
++ ACX_SLV_MEM_ADDR,
++ ACX_SLV_MEM_DATA,
++ ACX_SLV_MEM_CTL,
++} acxreg_t;
++
++/***********************************************************************
++*/
++static void acxmem_i_tx_timeout(struct net_device *ndev);
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
++static irqreturn_t acxmem_i_interrupt(int irq, void *dev_id);
++#else
++static irqreturn_t acxmem_i_interrupt(int irq, void *dev_id, struct pt_regs *regs);
++#endif
++static void acxmem_i_set_multicast_list(struct net_device *ndev);
++
++static int acxmem_e_open(struct net_device *ndev);
++static int acxmem_e_close(struct net_device *ndev);
++static void acxmem_s_up(struct net_device *ndev);
++static void acxmem_s_down(struct net_device *ndev);
++
++static void dump_acxmem (acx_device_t *adev, u32 start, int length);
++static int acxmem_complete_hw_reset (acx_device_t *adev);
++static void acxmem_s_delete_dma_regions(acx_device_t *adev);
++
++static struct platform_device *resume_pdev;
++
++static int
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11)
++acxmem_e_suspend(struct platform_device *pdev, pm_message_t state);
++#else
++acxmem_e_suspend(struct device *pdev, u32 state);
++#endif
++static void
++fw_resumer(struct work_struct *notused);
++//fw_resumer( void *data );
++
++static int acx_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
++{
++ struct net_device *ndev = ptr;
++ acx_device_t *adev = ndev2adev(ndev);
++
++ /*
++ * Upper level ioctl() handlers send a NETDEV_CHANGEADDR if the MAC address changes.
++ */
++
++ if (NETDEV_CHANGEADDR == event) {
++ /*
++ * the upper layers put the new MAC address in ndev->dev_addr; we just copy
++ * it over and update the ACX with it.
++ */
++ MAC_COPY(adev->dev_addr, adev->ndev->dev_addr);
++ adev->set_mask |= GETSET_STATION_ID;
++ acx_s_update_card_settings (adev);
++ }
++
++ return 0;
++}
++
++static struct notifier_block acx_netdev_notifier = {
++ .notifier_call = acx_netdev_event,
++};
++
++/***********************************************************************
++** Register access
++*/
++
++/* Pick one */
++/* #define INLINE_IO static */
++#define INLINE_IO static inline
++
++INLINE_IO u32
++read_id_register (acx_device_t *adev)
++{
++ writel (0x24, &adev->iobase[ACX_SLV_REG_ADDR]);
++ return readl (&adev->iobase[ACX_SLV_REG_DATA]);
++}
++
++INLINE_IO u32
++read_reg32(acx_device_t *adev, unsigned int offset)
++{
++ u32 val;
++ u32 addr;
++
++ if (offset > IO_ACX_ECPU_CTRL)
++ addr = offset;
++ else
++ addr = adev->io[offset];
++
++ if (addr < 0x20) {
++ return readl(((u8*)adev->iobase) + addr);
++ }
++
++ writel( addr, &adev->iobase[ACX_SLV_REG_ADDR] );
++ val = readl( &adev->iobase[ACX_SLV_REG_DATA] );
++
++ return val;
++}
++
++INLINE_IO u16
++read_reg16(acx_device_t *adev, unsigned int offset)
++{
++ u16 lo;
++ u32 addr;
++
++ if (offset > IO_ACX_ECPU_CTRL)
++ addr = offset;
++ else
++ addr = adev->io[offset];
++
++ if (addr < 0x20) {
++ return readw(((u8 *) adev->iobase) + addr);
++ }
++
++ writel( addr, &adev->iobase[ACX_SLV_REG_ADDR] );
++ lo = readw( (u16 *)&adev->iobase[ACX_SLV_REG_DATA] );
++
++ return lo;
++}
++
++INLINE_IO u8
++read_reg8(acx_device_t *adev, unsigned int offset)
++{
++ u8 lo;
++ u32 addr;
++
++ if (offset > IO_ACX_ECPU_CTRL)
++ addr = offset;
++ else
++ addr = adev->io[offset];
++
++ if (addr < 0x20)
++ return readb(((u8 *)adev->iobase) + addr);
++
++ writel( addr, &adev->iobase[ACX_SLV_REG_ADDR] );
++ lo = readw( (u8 *)&adev->iobase[ACX_SLV_REG_DATA] );
++
++ return (u8)lo;
++}
++
++INLINE_IO void
++write_reg32(acx_device_t *adev, unsigned int offset, u32 val)
++{
++ u32 addr;
++
++ if (offset > IO_ACX_ECPU_CTRL)
++ addr = offset;
++ else
++ addr = adev->io[offset];
++
++ if (addr < 0x20) {
++ writel(val, ((u8*)adev->iobase) + addr);
++ return;
++ }
++
++ writel( addr, &adev->iobase[ACX_SLV_REG_ADDR] );
++ writel( val, &adev->iobase[ACX_SLV_REG_DATA] );
++}
++
++INLINE_IO void
++write_reg16(acx_device_t *adev, unsigned int offset, u16 val)
++{
++ u32 addr;
++
++ if (offset > IO_ACX_ECPU_CTRL)
++ addr = offset;
++ else
++ addr = adev->io[offset];
++
++ if (addr < 0x20) {
++ writew(val, ((u8 *)adev->iobase) + addr);
++ return;
++ }
++ writel( addr, &adev->iobase[ACX_SLV_REG_ADDR] );
++ writew( val, (u16 *) &adev->iobase[ACX_SLV_REG_DATA] );
++}
++
++INLINE_IO void
++write_reg8(acx_device_t *adev, unsigned int offset, u8 val)
++{
++ u32 addr;
++
++ if (offset > IO_ACX_ECPU_CTRL)
++ addr = offset;
++ else
++ addr = adev->io[offset];
++
++ if (addr < 0x20) {
++ writeb(val, ((u8 *) adev->iobase) + addr);
++ return;
++ }
++ writel( addr, &adev->iobase[ACX_SLV_REG_ADDR] );
++ writeb( val, (u8 *)&adev->iobase[ACX_SLV_REG_DATA] );
++}
++
++/* Handle PCI posting properly:
++ * Make sure that writes reach the adapter in case they require to be executed
++ * *before* the next write, by reading a random (and safely accessible) register.
++ * This call has to be made if there is no read following (which would flush the data
++ * to the adapter), yet the written data has to reach the adapter immediately. */
++INLINE_IO void
++write_flush(acx_device_t *adev)
++{
++ /* readb(adev->iobase + adev->io[IO_ACX_INFO_MAILBOX_OFFS]); */
++ /* faster version (accesses the first register, IO_ACX_SOFT_RESET,
++ * which should also be safe): */
++ (void) readl(adev->iobase);
++}
++
++INLINE_IO void
++set_regbits (acx_device_t *adev, unsigned int offset, u32 bits) {
++ u32 tmp;
++
++ tmp = read_reg32 (adev, offset);
++ tmp = tmp | bits;
++ write_reg32 (adev, offset, tmp);
++ write_flush (adev);
++}
++
++INLINE_IO void
++clear_regbits (acx_device_t *adev, unsigned int offset, u32 bits) {
++ u32 tmp;
++
++ tmp = read_reg32 (adev, offset);
++ tmp = tmp & ~bits;
++ write_reg32 (adev, offset, tmp);
++ write_flush (adev);
++}
++
++/*
++ * Copy from PXA memory to the ACX memory. This assumes both the PXA and ACX
++ * addresses are 32 bit aligned. Count is in bytes.
++ */
++INLINE_IO void
++write_slavemem32 (acx_device_t *adev, u32 slave_address, u32 val)
++{
++ write_reg32 (adev, IO_ACX_SLV_MEM_CTL, 0x0);
++ write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, slave_address);
++ udelay (10);
++ write_reg32 (adev, IO_ACX_SLV_MEM_DATA, val);
++}
++
++INLINE_IO u32
++read_slavemem32 (acx_device_t *adev, u32 slave_address)
++{
++ u32 val;
++
++ write_reg32 (adev, IO_ACX_SLV_MEM_CTL, 0x0);
++ write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, slave_address);
++ udelay (10);
++ val = read_reg32 (adev, IO_ACX_SLV_MEM_DATA);
++
++ return val;
++}
++
++INLINE_IO void
++write_slavemem8 (acx_device_t *adev, u32 slave_address, u8 val)
++{
++ u32 data;
++ u32 base;
++ int offset;
++
++ /*
++ * Get the word containing the target address and the byte offset in that word.
++ */
++ base = slave_address & ~3;
++ offset = (slave_address & 3) * 8;
++
++ data = read_slavemem32 (adev, base);
++ data &= ~(0xff << offset);
++ data |= val << offset;
++ write_slavemem32 (adev, base, data);
++}
++
++INLINE_IO u8
++read_slavemem8 (acx_device_t *adev, u32 slave_address)
++{
++ u8 val;
++ u32 base;
++ u32 data;
++ int offset;
++
++ base = slave_address & ~3;
++ offset = (slave_address & 3) * 8;
++
++ data = read_slavemem32 (adev, base);
++
++ val = (data >> offset) & 0xff;
++
++ return val;
++}
++
++/*
++ * doesn't split across word boundaries
++ */
++INLINE_IO void
++write_slavemem16 (acx_device_t *adev, u32 slave_address, u16 val)
++{
++ u32 data;
++ u32 base;
++ int offset;
++
++ /*
++ * Get the word containing the target address and the byte offset in that word.
++ */
++ base = slave_address & ~3;
++ offset = (slave_address & 3) * 8;
++
++ data = read_slavemem32 (adev, base);
++ data &= ~(0xffff << offset);
++ data |= val << offset;
++ write_slavemem32 (adev, base, data);
++}
++
++/*
++ * doesn't split across word boundaries
++ */
++INLINE_IO u16
++read_slavemem16 (acx_device_t *adev, u32 slave_address)
++{
++ u16 val;
++ u32 base;
++ u32 data;
++ int offset;
++
++ base = slave_address & ~3;
++ offset = (slave_address & 3) * 8;
++
++ data = read_slavemem32 (adev, base);
++
++ val = (data >> offset) & 0xffff;
++
++ return val;
++}
++
++/*
++ * Copy from slave memory
++ *
++ * TODO - rewrite using address autoincrement, handle partial words
++ */
++void
++copy_from_slavemem (acx_device_t *adev, u8 *destination, u32 source, int count) {
++ u32 tmp = 0;
++ u8 *ptmp = (u8 *) &tmp;
++
++ /*
++ * Right now I'm making the assumption that the destination is aligned, but
++ * I'd better check.
++ */
++ if ((u32) destination & 3) {
++ printk ("acx copy_from_slavemem: warning! destination not word-aligned!\n");
++ }
++
++ while (count >= 4) {
++ write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, source);
++ udelay (10);
++ *((u32 *) destination) = read_reg32 (adev, IO_ACX_SLV_MEM_DATA);
++ count -= 4;
++ source += 4;
++ destination += 4;
++ }
++
++ /*
++ * If the word reads above didn't satisfy the count, read one more word
++ * and transfer a byte at a time until the request is satisfied.
++ */
++ if (count) {
++ write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, source);
++ udelay (10);
++ tmp = read_reg32 (adev, IO_ACX_SLV_MEM_DATA);
++ while (count--) {
++ *destination++ = *ptmp++;
++ }
++ }
++}
++
++/*
++ * Copy to slave memory
++ *
++ * TODO - rewrite using autoincrement, handle partial words
++ */
++void
++copy_to_slavemem (acx_device_t *adev, u32 destination, u8 *source, int count)
++{
++ u32 tmp = 0;
++ u8* ptmp = (u8 *) &tmp;
++ static u8 src[512]; /* make static to avoid huge stack objects */
++
++ /*
++ * For now, make sure the source is word-aligned by copying it to a word-aligned
++ * buffer. Someday rewrite to avoid the extra copy.
++ */
++ if (count > sizeof (src)) {
++ printk ("acx copy_to_slavemem: Warning! buffer overflow!\n");
++ count = sizeof (src);
++ }
++ memcpy (src, source, count);
++ source = src;
++
++ while (count >= 4) {
++ write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, destination);
++ udelay (10);
++ write_reg32 (adev, IO_ACX_SLV_MEM_DATA, *((u32 *) source));
++ count -= 4;
++ source += 4;
++ destination += 4;
++ }
++
++ /*
++ * If there are leftovers read the next word from the acx and merge in
++ * what they want to write.
++ */
++ if (count) {
++ write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, destination);
++ udelay (10);
++ tmp = read_reg32 (adev, IO_ACX_SLV_MEM_DATA);
++ while (count--) {
++ *ptmp++ = *source++;
++ }
++ /*
++ * reset address in case we're currently in auto-increment mode
++ */
++ write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, destination);
++ udelay (10);
++ write_reg32 (adev, IO_ACX_SLV_MEM_DATA, tmp);
++ udelay (10);
++ }
++
++}
++
++/*
++ * Block copy to slave buffers using memory block chain mode. Copies to the ACX
++ * transmit buffer structure with minimal intervention on our part.
++ * Interrupts should be disabled when calling this.
++ */
++void
++chaincopy_to_slavemem (acx_device_t *adev, u32 destination, u8 *source, int count)
++{
++ u32 val;
++ u32 *data = (u32 *) source;
++ static u8 aligned_source[WLAN_A4FR_MAXLEN_WEP_FCS];
++
++ /*
++ * Warn if the pointers don't look right. Destination must fit in [23:5] with
++ * zero elsewhere and source should be 32 bit aligned.
++ * This should never happen since we're in control of both, but I want to know about
++ * it if it does.
++ */
++ if ((destination & 0x00ffffe0) != destination) {
++ printk ("acx chaincopy: destination block 0x%04x not aligned!\n", destination);
++ }
++ if (count > sizeof aligned_source) {
++ printk( KERN_ERR "chaincopy_to_slavemem overflow!\n" );
++ count = sizeof aligned_source;
++ }
++ if ((u32) source & 3) {
++ memcpy (aligned_source, source, count);
++ data = (u32 *) aligned_source;
++ }
++
++ /*
++ * SLV_MEM_CTL[17:16] = memory block chain mode with auto-increment
++ * SLV_MEM_CTL[5:2] = offset to data portion = 1 word
++ */
++ val = 2 << 16 | 1 << 2;
++ writel (val, &adev->iobase[ACX_SLV_MEM_CTL]);
++
++ /*
++ * SLV_MEM_CP[23:5] = start of 1st block
++ * SLV_MEM_CP[3:2] = offset to memblkptr = 0
++ */
++ val = destination & 0x00ffffe0;
++ writel (val, &adev->iobase[ACX_SLV_MEM_CP]);
++
++ /*
++ * SLV_MEM_ADDR[23:2] = SLV_MEM_CTL[5:2] + SLV_MEM_CP[23:5]
++ */
++ val = (destination & 0x00ffffe0) + (1<<2);
++ writel (val, &adev->iobase[ACX_SLV_MEM_ADDR]);
++
++ /*
++ * Write the data to the slave data register, rounding up to the end
++ * of the word containing the last byte (hence the > 0)
++ */
++ while (count > 0) {
++ writel (*data++, &adev->iobase[ACX_SLV_MEM_DATA]);
++ count -= 4;
++ }
++}
++
++
++/*
++ * Block copy from slave buffers using memory block chain mode. Copies from the ACX
++ * receive buffer structures with minimal intervention on our part.
++ * Interrupts should be disabled when calling this.
++ */
++void
++chaincopy_from_slavemem (acx_device_t *adev, u8 *destination, u32 source, int count)
++{
++ u32 val;
++ u32 *data = (u32 *) destination;
++ static u8 aligned_destination[WLAN_A4FR_MAXLEN_WEP_FCS];
++ int saved_count = count;
++
++ /*
++ * Warn if the pointers don't look right. Destination must fit in [23:5] with
++ * zero elsewhere and source should be 32 bit aligned.
++ * Turns out the network stack sends unaligned things, so fix them before
++ * copying to the ACX.
++ */
++ if ((source & 0x00ffffe0) != source) {
++ printk ("acx chaincopy: source block 0x%04x not aligned!\n", source);
++ dump_acxmem (adev, 0, 0x10000);
++ }
++ if ((u32) destination & 3) {
++ //printk ("acx chaincopy: data destination not word aligned!\n");
++ data = (u32 *) aligned_destination;
++ if (count > sizeof aligned_destination) {
++ printk( KERN_ERR "chaincopy_from_slavemem overflow!\n" );
++ count = sizeof aligned_destination;
++ }
++ }
++
++ /*
++ * SLV_MEM_CTL[17:16] = memory block chain mode with auto-increment
++ * SLV_MEM_CTL[5:2] = offset to data portion = 1 word
++ */
++ val = (2 << 16) | (1 << 2);
++ writel (val, &adev->iobase[ACX_SLV_MEM_CTL]);
++
++ /*
++ * SLV_MEM_CP[23:5] = start of 1st block
++ * SLV_MEM_CP[3:2] = offset to memblkptr = 0
++ */
++ val = source & 0x00ffffe0;
++ writel (val, &adev->iobase[ACX_SLV_MEM_CP]);
++
++ /*
++ * SLV_MEM_ADDR[23:2] = SLV_MEM_CTL[5:2] + SLV_MEM_CP[23:5]
++ */
++ val = (source & 0x00ffffe0) + (1<<2);
++ writel (val, &adev->iobase[ACX_SLV_MEM_ADDR]);
++
++ /*
++ * Read the data from the slave data register, rounding up to the end
++ * of the word containing the last byte (hence the > 0)
++ */
++ while (count > 0) {
++ *data++ = readl (&adev->iobase[ACX_SLV_MEM_DATA]);
++ count -= 4;
++ }
++
++ /*
++ * If the destination wasn't aligned, we would have saved it in
++ * the aligned buffer, so copy it where it should go.
++ */
++ if ((u32) destination & 3) {
++ memcpy (destination, aligned_destination, saved_count);
++ }
++}
++
++char
++printable (char c)
++{
++ return ((c >= 20) && (c < 127)) ? c : '.';
++}
++
++#if DUMP_MEM_DEFINED > 0
++static void
++dump_acxmem (acx_device_t *adev, u32 start, int length)
++{
++ int i;
++ u8 buf[16];
++
++ while (length > 0) {
++ printk ("%04x ", start);
++ copy_from_slavemem (adev, buf, start, 16);
++ for (i = 0; (i < 16) && (i < length); i++) {
++ printk ("%02x ", buf[i]);
++ }
++ for (i = 0; (i < 16) && (i < length); i++) {
++ printk ("%c", printable (buf[i]));
++ }
++ printk ("\n");
++ start += 16;
++ length -= 16;
++ }
++}
++#endif
++
++static void
++enable_acx_irq(acx_device_t *adev);
++static void
++disable_acx_irq(acx_device_t *adev);
++
++/*
++ * Return an acx pointer to the next transmit data block.
++ */
++u32
++allocate_acx_txbuf_space (acx_device_t *adev, int count) {
++ u32 block, next, last_block;
++ int blocks_needed;
++ unsigned long flags;
++
++ spin_lock_irqsave(&adev->txbuf_lock, flags);
++ /*
++ * Take 4 off the memory block size to account for the reserved word at the start of
++ * the block.
++ */
++ blocks_needed = count / (adev->memblocksize - 4);
++ if (count % (adev->memblocksize - 4))
++ blocks_needed++;
++
++ if (blocks_needed <= adev->acx_txbuf_blocks_free) {
++ /*
++ * Take blocks at the head of the free list.
++ */
++ last_block = block = adev->acx_txbuf_free;
++
++ /*
++ * Follow block pointers through the requested number of blocks both to
++ * find the new head of the free list and to set the flags for the blocks
++ * appropriately.
++ */
++ while (blocks_needed--) {
++ /*
++ * Keep track of the last block of the allocation
++ */
++ last_block = adev->acx_txbuf_free;
++
++ /*
++ * Make sure the end control flag is not set.
++ */
++ next = read_slavemem32 (adev, adev->acx_txbuf_free) & 0x7ffff;
++ write_slavemem32 (adev, adev->acx_txbuf_free, next);
++
++ /*
++ * Update the new head of the free list
++ */
++ adev->acx_txbuf_free = next << 5;
++ adev->acx_txbuf_blocks_free--;
++
++ }
++
++ /*
++ * Flag the last block both by clearing out the next pointer
++ * and marking the control field.
++ */
++ write_slavemem32 (adev, last_block, 0x02000000);
++
++ /*
++ * If we're out of buffers make sure the free list pointer is NULL
++ */
++ if (!adev->acx_txbuf_blocks_free) {
++ adev->acx_txbuf_free = 0;
++ }
++ }
++ else {
++ block = 0;
++ }
++ spin_unlock_irqrestore (&adev->txbuf_lock, flags);
++ return block;
++}
++
++/*
++ * Return buffer space back to the pool by following the next pointers until we find
++ * the block marked as the end. Point the last block to the head of the free list,
++ * then update the head of the free list to point to the newly freed memory.
++ * This routine gets called in interrupt context, so it shouldn't block to protect
++ * the integrity of the linked list. The ISR already holds the lock.
++ */
++void
++reclaim_acx_txbuf_space (acx_device_t *adev, u32 blockptr) {
++ u32 cur, last, next;
++ unsigned long flags;
++
++ spin_lock_irqsave (&adev->txbuf_lock, flags);
++ if ((blockptr >= adev->acx_txbuf_start) &&
++ (blockptr <= adev->acx_txbuf_start +
++ (adev->acx_txbuf_numblocks - 1) * adev->memblocksize)) {
++ cur = blockptr;
++ do {
++ last = cur;
++ next = read_slavemem32 (adev, cur);
++
++ /*
++ * Advance to the next block in this allocation
++ */
++ cur = (next & 0x7ffff) << 5;
++
++ /*
++ * This block now counts as free.
++ */
++ adev->acx_txbuf_blocks_free++;
++ } while (!(next & 0x02000000));
++
++ /*
++ * last now points to the last block of that allocation. Update the pointer
++ * in that block to point to the free list and reset the free list to the
++ * first block of the free call. If there were no free blocks, make sure
++ * the new end of the list marks itself as truly the end.
++ */
++ if (adev->acx_txbuf_free) {
++ write_slavemem32 (adev, last, adev->acx_txbuf_free >> 5);
++ }
++ else {
++ write_slavemem32 (adev, last, 0x02000000);
++ }
++ adev->acx_txbuf_free = blockptr;
++ }
++ spin_unlock_irqrestore(&adev->txbuf_lock, flags);
++}
++
++/*
++ * Initialize the pieces managing the transmit buffer pool on the ACX. The transmit
++ * buffer is a circular queue with one 32 bit word reserved at the beginning of each
++ * block. The upper 13 bits are a control field, of which only 0x02000000 has any
++ * meaning. The lower 19 bits are the address of the next block divided by 32.
++ */
++void
++init_acx_txbuf (acx_device_t *adev) {
++
++ /*
++ * acx100_s_init_memory_pools set up txbuf_start and txbuf_numblocks for us.
++ * All we need to do is reset the rest of the bookeeping.
++ */
++
++ adev->acx_txbuf_free = adev->acx_txbuf_start;
++ adev->acx_txbuf_blocks_free = adev->acx_txbuf_numblocks;
++
++ /*
++ * Initialization leaves the last transmit pool block without a pointer back to
++ * the head of the list, but marked as the end of the list. That's how we want
++ * to see it, too, so leave it alone. This is only ever called after a firmware
++ * reset, so the ACX memory is in the state we want.
++ */
++
++}
++
++INLINE_IO int
++adev_present(acx_device_t *adev)
++{
++ /* fast version (accesses the first register, IO_ACX_SOFT_RESET,
++ * which should be safe): */
++ return readl(adev->iobase) != 0xffffffff;
++}
++
++/***********************************************************************
++*/
++static inline txdesc_t*
++get_txdesc(acx_device_t *adev, int index)
++{
++ return (txdesc_t*) (((u8*)adev->txdesc_start) + index * adev->txdesc_size);
++}
++
++static inline txdesc_t*
++advance_txdesc(acx_device_t *adev, txdesc_t* txdesc, int inc)
++{
++ return (txdesc_t*) (((u8*)txdesc) + inc * adev->txdesc_size);
++}
++
++static txhostdesc_t*
++get_txhostdesc(acx_device_t *adev, txdesc_t* txdesc)
++{
++ int index = (u8*)txdesc - (u8*)adev->txdesc_start;
++ if (unlikely(ACX_DEBUG && (index % adev->txdesc_size))) {
++ printk("bad txdesc ptr %p\n", txdesc);
++ return NULL;
++ }
++ index /= adev->txdesc_size;
++ if (unlikely(ACX_DEBUG && (index >= TX_CNT))) {
++ printk("bad txdesc ptr %p\n", txdesc);
++ return NULL;
++ }
++ return &adev->txhostdesc_start[index*2];
++}
++
++static inline client_t*
++get_txc(acx_device_t *adev, txdesc_t* txdesc)
++{
++ int index = (u8*)txdesc - (u8*)adev->txdesc_start;
++ if (unlikely(ACX_DEBUG && (index % adev->txdesc_size))) {
++ printk("bad txdesc ptr %p\n", txdesc);
++ return NULL;
++ }
++ index /= adev->txdesc_size;
++ if (unlikely(ACX_DEBUG && (index >= TX_CNT))) {
++ printk("bad txdesc ptr %p\n", txdesc);
++ return NULL;
++ }
++ return adev->txc[index];
++}
++
++static inline u16
++get_txr(acx_device_t *adev, txdesc_t* txdesc)
++{
++ int index = (u8*)txdesc - (u8*)adev->txdesc_start;
++ index /= adev->txdesc_size;
++ return adev->txr[index];
++}
++
++static inline void
++put_txcr(acx_device_t *adev, txdesc_t* txdesc, client_t* c, u16 r111)
++{
++ int index = (u8*)txdesc - (u8*)adev->txdesc_start;
++ if (unlikely(ACX_DEBUG && (index % adev->txdesc_size))) {
++ printk("bad txdesc ptr %p\n", txdesc);
++ return;
++ }
++ index /= adev->txdesc_size;
++ if (unlikely(ACX_DEBUG && (index >= TX_CNT))) {
++ printk("bad txdesc ptr %p\n", txdesc);
++ return;
++ }
++ adev->txc[index] = c;
++ adev->txr[index] = r111;
++}
++
++
++/***********************************************************************
++** EEPROM and PHY read/write helpers
++*/
++/***********************************************************************
++** acxmem_read_eeprom_byte
++**
++** Function called to read an octet in the EEPROM.
++**
++** This function is used by acxmem_e_probe to check if the
++** connected card is a legal one or not.
++**
++** Arguments:
++** adev ptr to acx_device structure
++** addr address to read in the EEPROM
++** charbuf ptr to a char. This is where the read octet
++** will be stored
++*/
++int
++acxmem_read_eeprom_byte(acx_device_t *adev, u32 addr, u8 *charbuf)
++{
++ int result;
++ int count;
++
++ write_reg32(adev, IO_ACX_EEPROM_CFG, 0);
++ write_reg32(adev, IO_ACX_EEPROM_ADDR, addr);
++ write_flush(adev);
++ write_reg32(adev, IO_ACX_EEPROM_CTL, 2);
++
++ count = 0xffff;
++ while (read_reg16(adev, IO_ACX_EEPROM_CTL)) {
++ /* scheduling away instead of CPU burning loop
++ * doesn't seem to work here at all:
++ * awful delay, sometimes also failure.
++ * Doesn't matter anyway (only small delay). */
++ if (unlikely(!--count)) {
++ printk("%s: timeout waiting for EEPROM read\n",
++ adev->ndev->name);
++ result = NOT_OK;
++ goto fail;
++ }
++ cpu_relax();
++ }
++
++ *charbuf = read_reg8(adev, IO_ACX_EEPROM_DATA);
++ log(L_DEBUG, "EEPROM at 0x%04X = 0x%02X\n", addr, *charbuf);
++ result = OK;
++
++fail:
++ return result;
++}
++
++
++/***********************************************************************
++** We don't lock hw accesses here since we never r/w eeprom in IRQ
++** Note: this function sleeps only because of GFP_KERNEL alloc
++*/
++#ifdef UNUSED
++int
++acxmem_s_write_eeprom(acx_device_t *adev, u32 addr, u32 len, const u8 *charbuf)
++{
++ u8 *data_verify = NULL;
++ unsigned long flags;
++ int count, i;
++ int result = NOT_OK;
++ u16 gpio_orig;
++
++ printk("acx: WARNING! I would write to EEPROM now. "
++ "Since I really DON'T want to unless you know "
++ "what you're doing (THIS CODE WILL PROBABLY "
++ "NOT WORK YET!), I will abort that now. And "
++ "definitely make sure to make a "
++ "/proc/driver/acx_wlan0_eeprom backup copy first!!! "
++ "(the EEPROM content includes the PCI config header!! "
++ "If you kill important stuff, then you WILL "
++ "get in trouble and people DID get in trouble already)\n");
++ return OK;
++
++ FN_ENTER;
++
++ data_verify = kmalloc(len, GFP_KERNEL);
++ if (!data_verify) {
++ goto end;
++ }
++
++ /* first we need to enable the OE (EEPROM Output Enable) GPIO line
++ * to be able to write to the EEPROM.
++ * NOTE: an EEPROM writing success has been reported,
++ * but you probably have to modify GPIO_OUT, too,
++ * and you probably need to activate a different GPIO
++ * line instead! */
++ gpio_orig = read_reg16(adev, IO_ACX_GPIO_OE);
++ write_reg16(adev, IO_ACX_GPIO_OE, gpio_orig & ~1);
++ write_flush(adev);
++
++ /* ok, now start writing the data out */
++ for (i = 0; i < len; i++) {
++ write_reg32(adev, IO_ACX_EEPROM_CFG, 0);
++ write_reg32(adev, IO_ACX_EEPROM_ADDR, addr + i);
++ write_reg32(adev, IO_ACX_EEPROM_DATA, *(charbuf + i));
++ write_flush(adev);
++ write_reg32(adev, IO_ACX_EEPROM_CTL, 1);
++
++ count = 0xffff;
++ while (read_reg16(adev, IO_ACX_EEPROM_CTL)) {
++ if (unlikely(!--count)) {
++ printk("WARNING, DANGER!!! "
++ "Timeout waiting for EEPROM write\n");
++ goto end;
++ }
++ cpu_relax();
++ }
++ }
++
++ /* disable EEPROM writing */
++ write_reg16(adev, IO_ACX_GPIO_OE, gpio_orig);
++ write_flush(adev);
++
++ /* now start a verification run */
++ for (i = 0; i < len; i++) {
++ write_reg32(adev, IO_ACX_EEPROM_CFG, 0);
++ write_reg32(adev, IO_ACX_EEPROM_ADDR, addr + i);
++ write_flush(adev);
++ write_reg32(adev, IO_ACX_EEPROM_CTL, 2);
++
++ count = 0xffff;
++ while (read_reg16(adev, IO_ACX_EEPROM_CTL)) {
++ if (unlikely(!--count)) {
++ printk("timeout waiting for EEPROM read\n");
++ goto end;
++ }
++ cpu_relax();
++ }
++
++ data_verify[i] = read_reg16(adev, IO_ACX_EEPROM_DATA);
++ }
++
++ if (0 == memcmp(charbuf, data_verify, len))
++ result = OK; /* read data matches, success */
++
++end:
++ kfree(data_verify);
++ FN_EXIT1(result);
++ return result;
++}
++#endif /* UNUSED */
++
++
++/***********************************************************************
++** acxmem_s_read_phy_reg
++**
++** Messing with rx/tx disabling and enabling here
++** (write_reg32(adev, IO_ACX_ENABLE, 0b000000xx)) kills traffic
++*/
++int
++acxmem_s_read_phy_reg(acx_device_t *adev, u32 reg, u8 *charbuf)
++{
++ int result = NOT_OK;
++ int count;
++
++ FN_ENTER;
++
++ write_reg32(adev, IO_ACX_PHY_ADDR, reg);
++ write_flush(adev);
++ write_reg32(adev, IO_ACX_PHY_CTL, 2);
++
++ count = 0xffff;
++ while (read_reg32(adev, IO_ACX_PHY_CTL)) {
++ /* scheduling away instead of CPU burning loop
++ * doesn't seem to work here at all:
++ * awful delay, sometimes also failure.
++ * Doesn't matter anyway (only small delay). */
++ if (unlikely(!--count)) {
++ printk("%s: timeout waiting for phy read\n",
++ adev->ndev->name);
++ *charbuf = 0;
++ goto fail;
++ }
++ cpu_relax();
++ }
++
++ log(L_DEBUG, "count was %u\n", count);
++ *charbuf = read_reg8(adev, IO_ACX_PHY_DATA);
++
++ log(L_DEBUG, "radio PHY at 0x%04X = 0x%02X\n", *charbuf, reg);
++ result = OK;
++ goto fail; /* silence compiler warning */
++fail:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++*/
++int
++acxmem_s_write_phy_reg(acx_device_t *adev, u32 reg, u8 value)
++{
++ int count;
++ FN_ENTER;
++
++ /* mprusko said that 32bit accesses result in distorted sensitivity
++ * on his card. Unconfirmed, looks like it's not true (most likely since we
++ * now properly flush writes). */
++ write_reg32(adev, IO_ACX_PHY_DATA, value);
++ write_reg32(adev, IO_ACX_PHY_ADDR, reg);
++ write_flush(adev);
++ write_reg32(adev, IO_ACX_PHY_CTL, 1);
++ write_flush(adev);
++
++ count = 0xffff;
++ while (read_reg32(adev, IO_ACX_PHY_CTL)) {
++ /* scheduling away instead of CPU burning loop
++ * doesn't seem to work here at all:
++ * awful delay, sometimes also failure.
++ * Doesn't matter anyway (only small delay). */
++ if (unlikely(!--count)) {
++ printk("%s: timeout waiting for phy read\n",
++ adev->ndev->name);
++ goto fail;
++ }
++ cpu_relax();
++ }
++
++ log(L_DEBUG, "radio PHY write 0x%02X at 0x%04X\n", value, reg);
++ fail:
++ FN_EXIT1(OK);
++ return OK;
++}
++
++
++#define NO_AUTO_INCREMENT 1
++
++/***********************************************************************
++** acxmem_s_write_fw
++**
++** Write the firmware image into the card.
++**
++** Arguments:
++** adev wlan device structure
++** fw_image firmware image.
++**
++** Returns:
++** 1 firmware image corrupted
++** 0 success
++*/
++static int
++acxmem_s_write_fw(acx_device_t *adev, const firmware_image_t *fw_image, u32 offset)
++{
++ int len, size, checkMismatch = -1;
++ u32 sum, v32, tmp, id;
++ /* we skip the first four bytes which contain the control sum */
++ const u8 *p = (u8*)fw_image + 4;
++
++ /* start the image checksum by adding the image size value */
++ sum = p[0]+p[1]+p[2]+p[3];
++ p += 4;
++
++#ifdef NOPE
++#if NO_AUTO_INCREMENT
++ write_reg32(adev, IO_ACX_SLV_MEM_CTL, 0); /* use basic mode */
++#else
++ write_reg32(adev, IO_ACX_SLV_MEM_CTL, 1); /* use autoincrement mode */
++ write_reg32(adev, IO_ACX_SLV_MEM_ADDR, offset); /* configure start address */
++ write_flush(adev);
++#endif
++#endif
++ len = 0;
++ size = le32_to_cpu(fw_image->size) & (~3);
++
++ while (likely(len < size)) {
++ v32 = be32_to_cpu(*(u32*)p);
++ sum += p[0]+p[1]+p[2]+p[3];
++ p += 4;
++ len += 4;
++
++#ifdef NOPE
++#if NO_AUTO_INCREMENT
++ write_reg32(adev, IO_ACX_SLV_MEM_ADDR, offset + len - 4);
++ write_flush(adev);
++#endif
++ write_reg32(adev, IO_ACX_SLV_MEM_DATA, v32);
++ write_flush(adev);
++#endif
++ write_slavemem32 (adev, offset + len - 4, v32);
++
++ id = read_id_register (adev);
++
++ /*
++ * check the data written
++ */
++ tmp = read_slavemem32 (adev, offset + len - 4);
++ if (checkMismatch && (tmp != v32)) {
++ printk ("first data mismatch at 0x%08x good 0x%08x bad 0x%08x id 0x%08x\n",
++ offset + len - 4, v32, tmp, id);
++ checkMismatch = 0;
++ }
++ }
++ log(L_DEBUG, "firmware written, size:%d sum1:%x sum2:%x\n",
++ size, sum, le32_to_cpu(fw_image->chksum));
++
++ /* compare our checksum with the stored image checksum */
++ return (sum != le32_to_cpu(fw_image->chksum));
++}
++
++
++/***********************************************************************
++** acxmem_s_validate_fw
++**
++** Compare the firmware image given with
++** the firmware image written into the card.
++**
++** Arguments:
++** adev wlan device structure
++** fw_image firmware image.
++**
++** Returns:
++** NOT_OK firmware image corrupted or not correctly written
++** OK success
++*/
++static int
++acxmem_s_validate_fw(acx_device_t *adev, const firmware_image_t *fw_image,
++ u32 offset)
++{
++ u32 sum, v32, w32;
++ int len, size;
++ int result = OK;
++ /* we skip the first four bytes which contain the control sum */
++ const u8 *p = (u8*)fw_image + 4;
++
++ /* start the image checksum by adding the image size value */
++ sum = p[0]+p[1]+p[2]+p[3];
++ p += 4;
++
++ write_reg32(adev, IO_ACX_SLV_END_CTL, 0);
++
++#if NO_AUTO_INCREMENT
++ write_reg32(adev, IO_ACX_SLV_MEM_CTL, 0); /* use basic mode */
++#else
++ write_reg32(adev, IO_ACX_SLV_MEM_CTL, 1); /* use autoincrement mode */
++ write_reg32(adev, IO_ACX_SLV_MEM_ADDR, offset); /* configure start address */
++#endif
++
++ len = 0;
++ size = le32_to_cpu(fw_image->size) & (~3);
++
++ while (likely(len < size)) {
++ v32 = be32_to_cpu(*(u32*)p);
++ p += 4;
++ len += 4;
++
++#ifdef NOPE
++#if NO_AUTO_INCREMENT
++ write_reg32(adev, IO_ACX_SLV_MEM_ADDR, offset + len - 4);
++#endif
++ udelay(10);
++ w32 = read_reg32(adev, IO_ACX_SLV_MEM_DATA);
++#endif
++ w32 = read_slavemem32 (adev, offset + len - 4);
++
++ if (unlikely(w32 != v32)) {
++ printk("acx: FATAL: firmware upload: "
++ "data parts at offset %d don't match\n(0x%08X vs. 0x%08X)!\n"
++ "I/O timing issues or defective memory, with DWL-xx0+? "
++ "ACX_IO_WIDTH=16 may help. Please report\n",
++ len, v32, w32);
++ result = NOT_OK;
++ break;
++ }
++
++ sum += (u8)w32 + (u8)(w32>>8) + (u8)(w32>>16) + (u8)(w32>>24);
++ }
++
++ /* sum control verification */
++ if (result != NOT_OK) {
++ if (sum != le32_to_cpu(fw_image->chksum)) {
++ printk("acx: FATAL: firmware upload: "
++ "checksums don't match!\n");
++ result = NOT_OK;
++ }
++ }
++
++ return result;
++}
++
++
++/***********************************************************************
++** acxmem_s_upload_fw
++**
++** Called from acx_reset_dev
++*/
++static int
++acxmem_s_upload_fw(acx_device_t *adev)
++{
++ firmware_image_t *fw_image = NULL;
++ int res = NOT_OK;
++ int try;
++ u32 file_size;
++ char *filename = "WLANGEN.BIN";
++#ifdef PATCH_AROUND_BAD_SPOTS
++ u32 offset;
++ int i;
++ /*
++ * arm-linux-objdump -d patch.bin, or
++ * od -Ax -t x4 patch.bin after finding the bounds
++ * of the .text section with arm-linux-objdump -s patch.bin
++ */
++ u32 patch[] = {
++ 0xe584c030, 0xe59fc008,
++ 0xe92d1000, 0xe59fc004, 0xe8bd8000, 0x0000080c,
++ 0x0000aa68, 0x605a2200, 0x2c0a689c, 0x2414d80a,
++ 0x2f00689f, 0x1c27d007, 0x06241e7c, 0x2f000e24,
++ 0xe000d1f6, 0x602e6018, 0x23036468, 0x480203db,
++ 0x60ca6003, 0xbdf0750a, 0xffff0808
++ };
++#endif
++
++ FN_ENTER;
++ /* No combined image; tell common we need the radio firmware, too */
++ adev->need_radio_fw = 1;
++
++ fw_image = acx_s_read_fw(adev->dev, filename, &file_size);
++ if (!fw_image) {
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++ }
++
++ for (try = 1; try <= 5; try++) {
++ res = acxmem_s_write_fw(adev, fw_image, 0);
++ log(L_DEBUG|L_INIT, "acx_write_fw (main): %d\n", res);
++ if (OK == res) {
++ res = acxmem_s_validate_fw(adev, fw_image, 0);
++ log(L_DEBUG|L_INIT, "acx_validate_fw "
++ "(main): %d\n", res);
++ }
++
++ if (OK == res) {
++ SET_BIT(adev->dev_state_mask, ACX_STATE_FW_LOADED);
++ break;
++ }
++ printk("acx: firmware upload attempt #%d FAILED, "
++ "retrying...\n", try);
++ acx_s_msleep(1000); /* better wait for a while... */
++ }
++
++#ifdef PATCH_AROUND_BAD_SPOTS
++ /*
++ * Only want to do this if the firmware is exactly what we expect for an
++ * iPaq 4700; otherwise, bad things would ensue.
++ */
++ if ((HX4700_FIRMWARE_CHECKSUM == fw_image->chksum) ||
++ (HX4700_ALTERNATE_FIRMWARE_CHECKSUM == fw_image->chksum)) {
++ /*
++ * Put the patch after the main firmware image. 0x950c contains
++ * the ACX's idea of the end of the firmware. Use that location to
++ * load ours (which depends on that location being 0xab58) then
++ * update that location to point to after ours.
++ */
++
++ offset = read_slavemem32 (adev, 0x950c);
++
++ log (L_DEBUG, "acx: patching in at 0x%04x\n", offset);
++
++ for (i = 0; i < sizeof(patch) / sizeof(patch[0]); i++) {
++ write_slavemem32 (adev, offset, patch[i]);
++ offset += sizeof(u32);
++ }
++
++ /*
++ * Patch the instruction at 0x0804 to branch to our ARM patch at 0xab58
++ */
++ write_slavemem32 (adev, 0x0804, 0xea000000 + (0xab58-0x0804-8)/4);
++
++ /*
++ * Patch the instructions at 0x1f40 to branch to our Thumb patch at 0xab74
++ *
++ * 4a00 ldr r2, [pc, #0]
++ * 4710 bx r2
++ * .data 0xab74+1
++ */
++ write_slavemem32 (adev, 0x1f40, 0x47104a00);
++ write_slavemem32 (adev, 0x1f44, 0x0000ab74+1);
++
++ /*
++ * Bump the end of the firmware up to beyond our patch.
++ */
++ write_slavemem32 (adev, 0x950c, offset);
++
++ }
++#endif
++
++ vfree(fw_image);
++
++ FN_EXIT1(res);
++ return res;
++}
++
++
++/***********************************************************************
++** acxmem_s_upload_radio
++**
++** Uploads the appropriate radio module firmware into the card.
++*/
++int
++acxmem_s_upload_radio(acx_device_t *adev)
++{
++ acx_ie_memmap_t mm;
++ firmware_image_t *radio_image;
++ acx_cmd_radioinit_t radioinit;
++ int res = NOT_OK;
++ int try;
++ u32 offset;
++ u32 size;
++ char filename[sizeof("RADIONN.BIN")];
++
++ if (!adev->need_radio_fw) return OK;
++
++ FN_ENTER;
++
++ acx_s_interrogate(adev, &mm, ACX1xx_IE_MEMORY_MAP);
++ offset = le32_to_cpu(mm.CodeEnd);
++
++ snprintf(filename, sizeof(filename), "RADIO%02x.BIN",
++ adev->radio_type);
++ radio_image = acx_s_read_fw(adev->dev, filename, &size);
++ if (!radio_image) {
++ printk("acx: can't load radio module '%s'\n", filename);
++ goto fail;
++ }
++
++ acx_s_issue_cmd(adev, ACX1xx_CMD_SLEEP, NULL, 0);
++
++ for (try = 1; try <= 5; try++) {
++ res = acxmem_s_write_fw(adev, radio_image, offset);
++ log(L_DEBUG|L_INIT, "acx_write_fw (radio): %d\n", res);
++ if (OK == res) {
++ res = acxmem_s_validate_fw(adev, radio_image, offset);
++ log(L_DEBUG|L_INIT, "acx_validate_fw (radio): %d\n", res);
++ }
++
++ if (OK == res)
++ break;
++ printk("acx: radio firmware upload attempt #%d FAILED, "
++ "retrying...\n", try);
++ acx_s_msleep(1000); /* better wait for a while... */
++ }
++
++ acx_s_issue_cmd(adev, ACX1xx_CMD_WAKE, NULL, 0);
++ radioinit.offset = cpu_to_le32(offset);
++
++ /* no endian conversion needed, remains in card CPU area: */
++ radioinit.len = radio_image->size;
++
++ vfree(radio_image);
++
++ if (OK != res)
++ goto fail;
++
++ /* will take a moment so let's have a big timeout */
++ acx_s_issue_cmd_timeo(adev, ACX1xx_CMD_RADIOINIT,
++ &radioinit, sizeof(radioinit), CMD_TIMEOUT_MS(1000));
++
++ res = acx_s_interrogate(adev, &mm, ACX1xx_IE_MEMORY_MAP);
++
++fail:
++ FN_EXIT1(res);
++ return res;
++}
++
++/***********************************************************************
++** acxmem_l_reset_mac
++**
++** MAC will be reset
++** Call context: reset_dev
++*/
++static void
++acxmem_l_reset_mac(acx_device_t *adev)
++{
++ int count;
++ FN_ENTER;
++
++ /* halt eCPU */
++ set_regbits (adev, IO_ACX_ECPU_CTRL, 0x1);
++
++ /* now do soft reset of eCPU, set bit */
++ set_regbits (adev, IO_ACX_SOFT_RESET, 0x1);
++ log(L_DEBUG, "%s: enable soft reset...\n", __func__);
++
++ /* Windows driver sleeps here for a while with this sequence */
++ for (count = 0; count < 200; count++) {
++ udelay (50);
++ }
++
++ /* now clear bit again: deassert eCPU reset */
++ log(L_DEBUG, "%s: disable soft reset and go to init mode...\n", __func__);
++ clear_regbits (adev, IO_ACX_SOFT_RESET, 0x1);
++
++ /* now start a burst read from initial EEPROM */
++ set_regbits (adev, IO_ACX_EE_START, 0x1);
++
++ /*
++ * Windows driver sleeps here for a while with this sequence
++ */
++ for (count = 0; count < 200; count++) {
++ udelay (50);
++ }
++
++ /* Windows driver writes 0x10000 to register 0x808 here */
++
++ write_reg32 (adev, 0x808, 0x10000);
++
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acxmem_s_verify_init
++*/
++static int
++acxmem_s_verify_init(acx_device_t *adev)
++{
++ int result = NOT_OK;
++ unsigned long timeout;
++
++ FN_ENTER;
++
++ timeout = jiffies + 2*HZ;
++ for (;;) {
++ u32 irqstat = read_reg32(adev, IO_ACX_IRQ_STATUS_NON_DES);
++ if ((irqstat != 0xFFFFFFFF) && (irqstat & HOST_INT_FCS_THRESHOLD)) {
++ result = OK;
++ write_reg32(adev, IO_ACX_IRQ_ACK, HOST_INT_FCS_THRESHOLD);
++ break;
++ }
++ if (time_after(jiffies, timeout))
++ break;
++ /* Init may take up to ~0.5 sec total */
++ acx_s_msleep(50);
++ }
++
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** A few low-level helpers
++**
++** Note: these functions are not protected by lock
++** and thus are never allowed to be called from IRQ.
++** Also they must not race with fw upload which uses same hw regs
++*/
++
++/***********************************************************************
++** acxmem_write_cmd_type_status
++*/
++
++static inline void
++acxmem_write_cmd_type_status(acx_device_t *adev, u16 type, u16 status)
++{
++ write_slavemem32 (adev, (u32) adev->cmd_area, type | (status << 16));
++ write_flush(adev);
++}
++
++
++/***********************************************************************
++** acxmem_read_cmd_type_status
++*/
++static u32
++acxmem_read_cmd_type_status(acx_device_t *adev)
++{
++ u32 cmd_type, cmd_status;
++
++ cmd_type = read_slavemem32 (adev, (u32) adev->cmd_area);
++
++ cmd_status = (cmd_type >> 16);
++ cmd_type = (u16)cmd_type;
++
++ log(L_CTL, "cmd_type:%04X cmd_status:%04X [%s]\n",
++ cmd_type, cmd_status,
++ acx_cmd_status_str(cmd_status));
++
++ return cmd_status;
++}
++
++
++/***********************************************************************
++** acxmem_s_reset_dev
++**
++** Arguments:
++** netdevice that contains the adev variable
++** Returns:
++** NOT_OK on fail
++** OK on success
++** Side effects:
++** device is hard reset
++** Call context:
++** acxmem_e_probe
++** Comment:
++** This resets the device using low level hardware calls
++** as well as uploads and verifies the firmware to the card
++*/
++
++static inline void
++init_mboxes(acx_device_t *adev)
++{
++ u32 cmd_offs, info_offs;
++
++ cmd_offs = read_reg32(adev, IO_ACX_CMD_MAILBOX_OFFS);
++ info_offs = read_reg32(adev, IO_ACX_INFO_MAILBOX_OFFS);
++ adev->cmd_area = (u8*) cmd_offs;
++ adev->info_area = (u8*) info_offs;
++ /*
++ log(L_DEBUG, "iobase2=%p\n"
++ */
++ log( L_DEBUG, "cmd_mbox_offset=%X cmd_area=%p\n"
++ "info_mbox_offset=%X info_area=%p\n",
++ cmd_offs, adev->cmd_area,
++ info_offs, adev->info_area);
++}
++
++
++static inline void
++read_eeprom_area(acx_device_t *adev)
++{
++#if ACX_DEBUG > 1
++ int offs;
++ u8 tmp;
++
++ for (offs = 0x8c; offs < 0xb9; offs++)
++ acxmem_read_eeprom_byte(adev, offs, &tmp);
++#endif
++}
++
++static int
++acxmem_s_reset_dev(acx_device_t *adev)
++{
++ const char* msg = "";
++ unsigned long flags;
++ int result = NOT_OK;
++ u16 hardware_info;
++ u16 ecpu_ctrl;
++ int count;
++ u32 tmp;
++
++ FN_ENTER;
++ /*
++ write_reg32 (adev, IO_ACX_SLV_MEM_CP, 0);
++ */
++ /* reset the device to make sure the eCPU is stopped
++ * to upload the firmware correctly */
++
++ acx_lock(adev, flags);
++
++ /* Windows driver does some funny things here */
++ /*
++ * clear bit 0x200 in register 0x2A0
++ */
++ clear_regbits (adev, 0x2A0, 0x200);
++
++ /*
++ * Set bit 0x200 in ACX_GPIO_OUT
++ */
++ set_regbits (adev, IO_ACX_GPIO_OUT, 0x200);
++
++ /*
++ * read register 0x900 until its value is 0x8400104C, sleeping
++ * in between reads if it's not immediate
++ */
++ tmp = read_reg32 (adev, REG_ACX_VENDOR_ID);
++ count = 500;
++ while (count-- && (tmp != ACX_VENDOR_ID)) {
++ mdelay (10);
++ tmp = read_reg32 (adev, REG_ACX_VENDOR_ID);
++ }
++
++ /* end what Windows driver does */
++
++ acxmem_l_reset_mac(adev);
++
++ ecpu_ctrl = read_reg32(adev, IO_ACX_ECPU_CTRL) & 1;
++ if (!ecpu_ctrl) {
++ msg = "eCPU is already running. ";
++ goto end_unlock;
++ }
++
++#ifdef WE_DONT_NEED_THAT_DO_WE
++ if (read_reg16(adev, IO_ACX_SOR_CFG) & 2) {
++ /* eCPU most likely means "embedded CPU" */
++ msg = "eCPU did not start after boot from flash. ";
++ goto end_unlock;
++ }
++
++ /* check sense on reset flags */
++ if (read_reg16(adev, IO_ACX_SOR_CFG) & 0x10) {
++ printk("%s: eCPU did not start after boot (SOR), "
++ "is this fatal?\n", adev->ndev->name);
++ }
++#endif
++ /* scan, if any, is stopped now, setting corresponding IRQ bit */
++ adev->irq_status |= HOST_INT_SCAN_COMPLETE;
++
++ acx_unlock(adev, flags);
++
++ /* need to know radio type before fw load */
++ /* Need to wait for arrival of this information in a loop,
++ * most probably since eCPU runs some init code from EEPROM
++ * (started burst read in reset_mac()) which also
++ * sets the radio type ID */
++
++ count = 0xffff;
++ do {
++ hardware_info = read_reg16(adev, IO_ACX_EEPROM_INFORMATION);
++ if (!--count) {
++ msg = "eCPU didn't indicate radio type";
++ goto end_fail;
++ }
++ cpu_relax();
++ } while (!(hardware_info & 0xff00)); /* radio type still zero? */
++ printk("ACX radio type 0x%02x\n", (hardware_info >> 8) & 0xff);
++ /* printk("DEBUG: count %d\n", count); */
++ adev->form_factor = hardware_info & 0xff;
++ adev->radio_type = hardware_info >> 8;
++
++ /* load the firmware */
++ if (OK != acxmem_s_upload_fw(adev))
++ goto end_fail;
++
++ /* acx_s_msleep(10); this one really shouldn't be required */
++
++ /* now start eCPU by clearing bit */
++ clear_regbits (adev, IO_ACX_ECPU_CTRL, 0x1);
++ log(L_DEBUG, "booted eCPU up and waiting for completion...\n");
++
++ /* Windows driver clears bit 0x200 in register 0x2A0 here */
++ clear_regbits (adev, 0x2A0, 0x200);
++
++ /* Windows driver sets bit 0x200 in ACX_GPIO_OUT here */
++ set_regbits (adev, IO_ACX_GPIO_OUT, 0x200);
++ /* wait for eCPU bootup */
++ if (OK != acxmem_s_verify_init(adev)) {
++ msg = "timeout waiting for eCPU. ";
++ goto end_fail;
++ }
++ log(L_DEBUG, "eCPU has woken up, card is ready to be configured\n");
++ init_mboxes(adev);
++ acxmem_write_cmd_type_status(adev, ACX1xx_CMD_RESET, 0);
++
++ /* test that EEPROM is readable */
++ read_eeprom_area(adev);
++
++ result = OK;
++ goto end;
++
++/* Finish error message. Indicate which function failed */
++end_unlock:
++ acx_unlock(adev, flags);
++end_fail:
++ printk("acx: %sreset_dev() FAILED\n", msg);
++end:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acxmem_s_issue_cmd_timeo
++**
++** Sends command to fw, extract result
++**
++** NB: we do _not_ take lock inside, so be sure to not touch anything
++** which may interfere with IRQ handler operation
++**
++** TODO: busy wait is a bit silly, so:
++** 1) stop doing many iters - go to sleep after first
++** 2) go to waitqueue based approach: wait, not poll!
++*/
++#undef FUNC
++#define FUNC "issue_cmd"
++
++#if !ACX_DEBUG
++int
++acxmem_s_issue_cmd_timeo(
++ acx_device_t *adev,
++ unsigned int cmd,
++ void *buffer,
++ unsigned buflen,
++ unsigned cmd_timeout)
++{
++#else
++int
++acxmem_s_issue_cmd_timeo_debug(
++ acx_device_t *adev,
++ unsigned cmd,
++ void *buffer,
++ unsigned buflen,
++ unsigned cmd_timeout,
++ const char* cmdstr)
++{
++ unsigned long start = jiffies;
++#endif
++ const char *devname;
++ unsigned counter;
++ u16 irqtype;
++ int i, j;
++ u8 *p;
++ u16 cmd_status;
++ unsigned long timeout;
++
++ FN_ENTER;
++
++ devname = adev->ndev->name;
++ if (!devname || !devname[0] || devname[4]=='%')
++ devname = "acx";
++
++ log(L_CTL, FUNC"(cmd:%s,buflen:%u,timeout:%ums,type:0x%04X)\n",
++ cmdstr, buflen, cmd_timeout,
++ buffer ? le16_to_cpu(((acx_ie_generic_t *)buffer)->type) : -1);
++
++ if (!(adev->dev_state_mask & ACX_STATE_FW_LOADED)) {
++ printk("%s: "FUNC"(): firmware is not loaded yet, "
++ "cannot execute commands!\n", devname);
++ goto bad;
++ }
++
++ if ((acx_debug & L_DEBUG) && (cmd != ACX1xx_CMD_INTERROGATE)) {
++ printk("input buffer (len=%u):\n", buflen);
++ acx_dump_bytes(buffer, buflen);
++ }
++
++ /* wait for firmware to become idle for our command submission */
++ timeout = HZ/5;
++ counter = (timeout * 1000 / HZ) - 1; /* in ms */
++ timeout += jiffies;
++ do {
++ cmd_status = acxmem_read_cmd_type_status(adev);
++ /* Test for IDLE state */
++ if (!cmd_status)
++ break;
++ if (counter % 8 == 0) {
++ if (time_after(jiffies, timeout)) {
++ counter = 0;
++ break;
++ }
++ /* we waited 8 iterations, no luck. Sleep 8 ms */
++ acx_s_msleep(8);
++ }
++ } while (likely(--counter));
++
++ if (!counter) {
++ /* the card doesn't get idle, we're in trouble */
++ printk("%s: "FUNC"(): cmd_status is not IDLE: 0x%04X!=0\n",
++ devname, cmd_status);
++#if DUMP_IF_SLOW > 0
++ dump_acxmem (adev, 0, 0x10000);
++ panic ("not idle");
++#endif
++ goto bad;
++ } else if (counter < 190) { /* if waited >10ms... */
++ log(L_CTL|L_DEBUG, FUNC"(): waited for IDLE %dms. "
++ "Please report\n", 199 - counter);
++ }
++
++ /* now write the parameters of the command if needed */
++ if (buffer && buflen) {
++ /* if it's an INTERROGATE command, just pass the length
++ * of parameters to read, as data */
++#if CMD_DISCOVERY
++ if (cmd == ACX1xx_CMD_INTERROGATE)
++ memset_io(adev->cmd_area + 4, 0xAA, buflen);
++#endif
++ /*
++ * slave memory version
++ */
++ copy_to_slavemem (adev, (u32) (adev->cmd_area + 4), buffer,
++ (cmd == ACX1xx_CMD_INTERROGATE) ? 4 : buflen);
++ }
++ /* now write the actual command type */
++ acxmem_write_cmd_type_status(adev, cmd, 0);
++
++ /* clear CMD_COMPLETE bit. can be set only by IRQ handler: */
++ adev->irq_status &= ~HOST_INT_CMD_COMPLETE;
++
++ /* execute command */
++ write_reg16(adev, IO_ACX_INT_TRIG, INT_TRIG_CMD);
++ write_flush(adev);
++
++ /* wait for firmware to process command */
++
++ /* Ensure nonzero and not too large timeout.
++ ** Also converts e.g. 100->99, 200->199
++ ** which is nice but not essential */
++ cmd_timeout = (cmd_timeout-1) | 1;
++ if (unlikely(cmd_timeout > 1199))
++ cmd_timeout = 1199;
++
++ /* we schedule away sometimes (timeout can be large) */
++ counter = cmd_timeout;
++ timeout = jiffies + cmd_timeout * HZ / 1000;
++ do {
++ if (!adev->irqs_active) { /* IRQ disabled: poll */
++ irqtype = read_reg16(adev, IO_ACX_IRQ_STATUS_NON_DES);
++ if (irqtype & HOST_INT_CMD_COMPLETE) {
++ write_reg16(adev, IO_ACX_IRQ_ACK,
++ HOST_INT_CMD_COMPLETE);
++ break;
++ }
++ } else { /* Wait when IRQ will set the bit */
++ irqtype = adev->irq_status;
++ if (irqtype & HOST_INT_CMD_COMPLETE)
++ break;
++ }
++
++ if (counter % 8 == 0) {
++ if (time_after(jiffies, timeout)) {
++ counter = 0;
++ break;
++ }
++ /* we waited 8 iterations, no luck. Sleep 8 ms */
++ acx_s_msleep(8);
++ }
++ } while (likely(--counter));
++
++ /* save state for debugging */
++ cmd_status = acxmem_read_cmd_type_status(adev);
++
++ /* put the card in IDLE state */
++ acxmem_write_cmd_type_status(adev, ACX1xx_CMD_RESET, 0);
++
++ if (!counter) { /* timed out! */
++ printk("%s: "FUNC"(): timed out %s for CMD_COMPLETE. "
++ "irq bits:0x%04X irq_status:0x%04X timeout:%dms "
++ "cmd_status:%d (%s)\n",
++ devname, (adev->irqs_active) ? "waiting" : "polling",
++ irqtype, adev->irq_status, cmd_timeout,
++ cmd_status, acx_cmd_status_str(cmd_status));
++ printk("%s: "FUNC"(): device irq status 0x%04x\n",
++ devname, read_reg16(adev, IO_ACX_IRQ_STATUS_NON_DES));
++ printk("%s: "FUNC"(): IO_ACX_IRQ_MASK 0x%04x IO_ACX_FEMR 0x%04x\n",
++ devname,
++ read_reg16 (adev, IO_ACX_IRQ_MASK),
++ read_reg16 (adev, IO_ACX_FEMR));
++ if (read_reg16 (adev, IO_ACX_IRQ_MASK) == 0xffff) {
++ printk ("acxmem: firmware probably hosed - reloading\n");
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11)
++ {
++ pm_message_t state;
++ acxmem_e_suspend (resume_pdev, state);
++ }
++#else
++ acxmem_e_suspend (adev->dev, 0);
++#endif
++ {
++ struct work_struct *notused;
++ fw_resumer (notused);
++ }
++ }
++
++ goto bad;
++ } else if (cmd_timeout - counter > 30) { /* if waited >30ms... */
++ log(L_CTL|L_DEBUG, FUNC"(): %s for CMD_COMPLETE %dms. "
++ "count:%d. Please report\n",
++ (adev->irqs_active) ? "waited" : "polled",
++ cmd_timeout - counter, counter);
++ }
++
++ if (1 != cmd_status) { /* it is not a 'Success' */
++ printk("%s: "FUNC"(): cmd_status is not SUCCESS: %d (%s). "
++ "Took %dms of %d\n",
++ devname, cmd_status, acx_cmd_status_str(cmd_status),
++ cmd_timeout - counter, cmd_timeout);
++ /* zero out result buffer
++ * WARNING: this will trash stack in case of illegally large input
++ * length! */
++ if (buflen > 388) {
++ /*
++ * 388 is maximum command length
++ */
++ printk ("invalid length 0x%08x\n", buflen);
++ buflen = 388;
++ }
++ p = (u8 *) buffer;
++ for (i = 0; i < buflen; i+= 16) {
++ printk ("%04x:", i);
++ for (j = 0; (j < 16) && (i+j < buflen); j++) {
++ printk (" %02x", *p++);
++ }
++ printk ("\n");
++ }
++
++ if (buffer && buflen)
++ memset(buffer, 0, buflen);
++ goto bad;
++ }
++
++ /* read in result parameters if needed */
++ if (buffer && buflen && (cmd == ACX1xx_CMD_INTERROGATE)) {
++ copy_from_slavemem (adev, buffer, (u32) (adev->cmd_area + 4), buflen);
++ if (acx_debug & L_DEBUG) {
++ printk("output buffer (len=%u): ", buflen);
++ acx_dump_bytes(buffer, buflen);
++ }
++ }
++
++/* ok: */
++ log(L_CTL, FUNC"(%s): took %ld jiffies to complete\n",
++ cmdstr, jiffies - start);
++ FN_EXIT1(OK);
++ return OK;
++
++bad:
++ /* Give enough info so that callers can avoid
++ ** printing their own diagnostic messages */
++#if ACX_DEBUG
++ printk("%s: "FUNC"(cmd:%s) FAILED\n", devname, cmdstr);
++#else
++ printk("%s: "FUNC"(cmd:0x%04X) FAILED\n", devname, cmd);
++#endif
++ dump_stack();
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++}
++
++
++/***********************************************************************
++*/
++#if defined(NONESSENTIAL_FEATURES)
++typedef struct device_id {
++ unsigned char id[6];
++ char *descr;
++ char *type;
++} device_id_t;
++
++static const device_id_t
++device_ids[] =
++{
++ {
++ {'G', 'l', 'o', 'b', 'a', 'l'},
++ NULL,
++ NULL,
++ },
++ {
++ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
++ "uninitialized",
++ "SpeedStream SS1021 or Gigafast WF721-AEX"
++ },
++ {
++ {0x80, 0x81, 0x82, 0x83, 0x84, 0x85},
++ "non-standard",
++ "DrayTek Vigor 520"
++ },
++ {
++ {'?', '?', '?', '?', '?', '?'},
++ "non-standard",
++ "Level One WPC-0200"
++ },
++ {
++ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
++ "empty",
++ "DWL-650+ variant"
++ }
++};
++
++static void
++acx_show_card_eeprom_id(acx_device_t *adev)
++{
++ unsigned char buffer[CARD_EEPROM_ID_SIZE];
++ int i;
++
++ memset(&buffer, 0, CARD_EEPROM_ID_SIZE);
++ /* use direct EEPROM access */
++ for (i = 0; i < CARD_EEPROM_ID_SIZE; i++) {
++ if (OK != acxmem_read_eeprom_byte(adev,
++ ACX100_EEPROM_ID_OFFSET + i,
++ &buffer[i])) {
++ printk("acx: reading EEPROM FAILED\n");
++ break;
++ }
++ }
++
++ for (i = 0; i < VEC_SIZE(device_ids); i++) {
++ if (!memcmp(&buffer, device_ids[i].id, CARD_EEPROM_ID_SIZE)) {
++ if (device_ids[i].descr) {
++ printk("acx: EEPROM card ID string check "
++ "found %s card ID: is this %s?\n",
++ device_ids[i].descr, device_ids[i].type);
++ }
++ break;
++ }
++ }
++ if (i == VEC_SIZE(device_ids)) {
++ printk("acx: EEPROM card ID string check found "
++ "unknown card: expected 'Global', got '%.*s\'. "
++ "Please report\n", CARD_EEPROM_ID_SIZE, buffer);
++ }
++}
++#endif /* NONESSENTIAL_FEATURES */
++
++/***********************************************************************
++** acxmem_free_desc_queues
++**
++** Releases the queues that have been allocated, the
++** others have been initialised to NULL so this
++** function can be used if only part of the queues were allocated.
++*/
++
++void
++acxmem_free_desc_queues(acx_device_t *adev)
++{
++#define ACX_FREE_QUEUE(size, ptr, phyaddr) \
++ if (ptr) { \
++ kfree(ptr); \
++ ptr = NULL; \
++ size = 0; \
++ }
++
++ FN_ENTER;
++
++ ACX_FREE_QUEUE(adev->txhostdesc_area_size, adev->txhostdesc_start, adev->txhostdesc_startphy);
++ ACX_FREE_QUEUE(adev->txbuf_area_size, adev->txbuf_start, adev->txbuf_startphy);
++
++ adev->txdesc_start = NULL;
++
++ ACX_FREE_QUEUE(adev->rxhostdesc_area_size, adev->rxhostdesc_start, adev->rxhostdesc_startphy);
++ ACX_FREE_QUEUE(adev->rxbuf_area_size, adev->rxbuf_start, adev->rxbuf_startphy);
++
++ adev->rxdesc_start = NULL;
++
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acxmem_s_delete_dma_regions
++*/
++static void
++acxmem_s_delete_dma_regions(acx_device_t *adev)
++{
++ unsigned long flags;
++
++ FN_ENTER;
++ /* disable radio Tx/Rx. Shouldn't we use the firmware commands
++ * here instead? Or are we that much down the road that it's no
++ * longer possible here? */
++ /*
++ * slave memory interface really doesn't like this.
++ */
++ /*
++ write_reg16(adev, IO_ACX_ENABLE, 0);
++ */
++
++ acx_s_msleep(100);
++
++ acx_lock(adev, flags);
++ acxmem_free_desc_queues(adev);
++ acx_unlock(adev, flags);
++
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acxmem_e_probe
++**
++** Probe routine called when a PCI device w/ matching ID is found.
++** Here's the sequence:
++** - Allocate the PCI resources.
++** - Read the PCMCIA attribute memory to make sure we have a WLAN card
++** - Reset the MAC
++** - Initialize the dev and wlan data
++** - Initialize the MAC
++**
++** pdev - ptr to pci device structure containing info about pci configuration
++** id - ptr to the device id entry that matched this device
++*/
++static const u16
++IO_ACX100[] =
++{
++ 0x0000, /* IO_ACX_SOFT_RESET */
++
++ 0x0014, /* IO_ACX_SLV_MEM_ADDR */
++ 0x0018, /* IO_ACX_SLV_MEM_DATA */
++ 0x001c, /* IO_ACX_SLV_MEM_CTL */
++ 0x0020, /* IO_ACX_SLV_END_CTL */
++
++ 0x0034, /* IO_ACX_FEMR */
++
++ 0x007c, /* IO_ACX_INT_TRIG */
++ 0x0098, /* IO_ACX_IRQ_MASK */
++ 0x00a4, /* IO_ACX_IRQ_STATUS_NON_DES */
++ 0x00a8, /* IO_ACX_IRQ_STATUS_CLEAR */
++ 0x00ac, /* IO_ACX_IRQ_ACK */
++ 0x00b0, /* IO_ACX_HINT_TRIG */
++
++ 0x0104, /* IO_ACX_ENABLE */
++
++ 0x0250, /* IO_ACX_EEPROM_CTL */
++ 0x0254, /* IO_ACX_EEPROM_ADDR */
++ 0x0258, /* IO_ACX_EEPROM_DATA */
++ 0x025c, /* IO_ACX_EEPROM_CFG */
++
++ 0x0268, /* IO_ACX_PHY_ADDR */
++ 0x026c, /* IO_ACX_PHY_DATA */
++ 0x0270, /* IO_ACX_PHY_CTL */
++
++ 0x0290, /* IO_ACX_GPIO_OE */
++
++ 0x0298, /* IO_ACX_GPIO_OUT */
++
++ 0x02a4, /* IO_ACX_CMD_MAILBOX_OFFS */
++ 0x02a8, /* IO_ACX_INFO_MAILBOX_OFFS */
++ 0x02ac, /* IO_ACX_EEPROM_INFORMATION */
++
++ 0x02d0, /* IO_ACX_EE_START */
++ 0x02d4, /* IO_ACX_SOR_CFG */
++ 0x02d8 /* IO_ACX_ECPU_CTRL */
++};
++
++static const u16
++IO_ACX111[] =
++{
++ 0x0000, /* IO_ACX_SOFT_RESET */
++
++ 0x0014, /* IO_ACX_SLV_MEM_ADDR */
++ 0x0018, /* IO_ACX_SLV_MEM_DATA */
++ 0x001c, /* IO_ACX_SLV_MEM_CTL */
++ 0x0020, /* IO_ACX_SLV_MEM_CP */
++
++ 0x0034, /* IO_ACX_FEMR */
++
++ 0x00b4, /* IO_ACX_INT_TRIG */
++ 0x00d4, /* IO_ACX_IRQ_MASK */
++ /* we do mean NON_DES (0xf0), not NON_DES_MASK which is at 0xe0: */
++ 0x00f0, /* IO_ACX_IRQ_STATUS_NON_DES */
++ 0x00e4, /* IO_ACX_IRQ_STATUS_CLEAR */
++ 0x00e8, /* IO_ACX_IRQ_ACK */
++ 0x00ec, /* IO_ACX_HINT_TRIG */
++
++ 0x01d0, /* IO_ACX_ENABLE */
++
++ 0x0338, /* IO_ACX_EEPROM_CTL */
++ 0x033c, /* IO_ACX_EEPROM_ADDR */
++ 0x0340, /* IO_ACX_EEPROM_DATA */
++ 0x0344, /* IO_ACX_EEPROM_CFG */
++
++ 0x0350, /* IO_ACX_PHY_ADDR */
++ 0x0354, /* IO_ACX_PHY_DATA */
++ 0x0358, /* IO_ACX_PHY_CTL */
++
++ 0x0374, /* IO_ACX_GPIO_OE */
++
++ 0x037c, /* IO_ACX_GPIO_OUT */
++
++ 0x0388, /* IO_ACX_CMD_MAILBOX_OFFS */
++ 0x038c, /* IO_ACX_INFO_MAILBOX_OFFS */
++ 0x0390, /* IO_ACX_EEPROM_INFORMATION */
++
++ 0x0100, /* IO_ACX_EE_START */
++ 0x0104, /* IO_ACX_SOR_CFG */
++ 0x0108, /* IO_ACX_ECPU_CTRL */
++};
++
++static void
++dummy_netdev_init(struct net_device *ndev) {}
++
++/*
++ * Most of the acx specific pieces of hardware reset.
++ */
++static int
++acxmem_complete_hw_reset (acx_device_t *adev)
++{
++ acx111_ie_configoption_t co;
++
++ /* NB: read_reg() reads may return bogus data before reset_dev(),
++ * since the firmware which directly controls large parts of the I/O
++ * registers isn't initialized yet.
++ * acx100 seems to be more affected than acx111 */
++ if (OK != acxmem_s_reset_dev (adev))
++ return -1;
++
++ if (IS_ACX100(adev)) {
++ /* ACX100: configopt struct in cmd mailbox - directly after reset */
++ copy_from_slavemem (adev, (u8*) &co, (u32) adev->cmd_area, sizeof (co));
++ }
++
++ if (OK != acx_s_init_mac(adev))
++ return -3;
++
++ if (IS_ACX111(adev)) {
++ /* ACX111: configopt struct needs to be queried after full init */
++ acx_s_interrogate(adev, &co, ACX111_IE_CONFIG_OPTIONS);
++ }
++
++ /*
++ * Set up transmit buffer administration
++ */
++ init_acx_txbuf (adev);
++
++ /*
++ * Windows driver writes 0x01000000 to register 0x288, RADIO_CTL, if the form factor
++ * is 3. It also write protects the EEPROM by writing 1<<9 to GPIO_OUT
++ */
++ if (adev->form_factor == 3) {
++ set_regbits (adev, 0x288, 0x01000000);
++ set_regbits (adev, 0x298, 1<<9);
++ }
++
++/* TODO: merge them into one function, they are called just once and are the same for pci & usb */
++ if (OK != acxmem_read_eeprom_byte(adev, 0x05, &adev->eeprom_version))
++ return -2;
++
++ acx_s_parse_configoption(adev, &co);
++ acx_s_get_firmware_version(adev); /* needs to be after acx_s_init_mac() */
++ acx_display_hardware_details(adev);
++
++ return 0;
++}
++
++static int __devinit
++acxmem_e_probe(struct platform_device *pdev)
++{
++ struct acx_hardware_data *hwdata = pdev->dev.platform_data;
++ acx_device_t *adev = NULL;
++ struct net_device *ndev = NULL;
++ const char *chip_name;
++ int result = -EIO;
++ int err;
++ int i;
++ unsigned long addr_size=0;
++ u8 chip_type;
++
++ FN_ENTER;
++ (void) hwdata->start_hw();
++
++ /* FIXME: prism54 calls pci_set_mwi() here,
++ * should we do/support the same? */
++
++ /* chiptype is u8 but id->driver_data is ulong
++ ** Works for now (possible values are 1 and 2) */
++ chip_type = CHIPTYPE_ACX100;
++ /* acx100 and acx111 have different PCI memory regions */
++ if (chip_type == CHIPTYPE_ACX100) {
++ chip_name = "ACX100";
++ } else if (chip_type == CHIPTYPE_ACX111) {
++ chip_name = "ACX111";
++ } else {
++ printk("acx: unknown chip type 0x%04X\n", chip_type);
++ goto fail_unknown_chiptype;
++ }
++
++ printk("acx: found %s-based wireless network card\n", chip_name);
++ log(L_ANY, "initial debug setting is 0x%04X\n", acx_debug);
++
++ ndev = alloc_netdev(sizeof(*adev), "wlan%d", dummy_netdev_init);
++ /* (NB: memsets to 0 entire area) */
++ if (!ndev) {
++ printk("acx: no memory for netdevice struct\n");
++ goto fail_alloc_netdev;
++ }
++
++ platform_set_drvdata (pdev, ndev);
++
++ ether_setup(ndev);
++
++ /*
++ * use platform_data resources that were provided
++ */
++ ndev->irq = 0;
++ for (i=0; i<pdev->num_resources; i++) {
++ if (pdev->resource[i].flags == IORESOURCE_IRQ) {
++ ndev->irq = pdev->resource[i].start;
++ }
++ else if (pdev->resource[i].flags == IORESOURCE_MEM) {
++ ndev->base_addr = pdev->resource[i].start;
++ addr_size = pdev->resource[i].end - pdev->resource[i].start;
++ }
++ }
++ if (addr_size == 0 || ndev->irq == 0)
++ goto fail_hw_params;
++ ndev->open = &acxmem_e_open;
++ ndev->stop = &acxmem_e_close;
++ pdev->dev.release = &acxmem_e_release;
++ ndev->hard_start_xmit = &acx_i_start_xmit;
++ ndev->get_stats = &acx_e_get_stats;
++#if IW_HANDLER_VERSION <= 5
++ ndev->get_wireless_stats = &acx_e_get_wireless_stats;
++#endif
++ ndev->wireless_handlers = (struct iw_handler_def *)&acx_ioctl_handler_def;
++ ndev->set_multicast_list = &acxmem_i_set_multicast_list;
++ ndev->tx_timeout = &acxmem_i_tx_timeout;
++ ndev->change_mtu = &acx_e_change_mtu;
++ ndev->watchdog_timeo = 4 * HZ;
++
++ adev = ndev2adev(ndev);
++ spin_lock_init(&adev->lock); /* initial state: unlocked */
++ spin_lock_init(&adev->txbuf_lock);
++ /* We do not start with downed sem: we want PARANOID_LOCKING to work */
++ sema_init(&adev->sem, 1); /* initial state: 1 (upped) */
++ /* since nobody can see new netdev yet, we can as well
++ ** just _presume_ that we're under sem (instead of actually taking it): */
++ /* acx_sem_lock(adev); */
++ adev->dev = &pdev->dev;
++ adev->ndev = ndev;
++ adev->dev_type = DEVTYPE_MEM;
++ adev->chip_type = chip_type;
++ adev->chip_name = chip_name;
++ adev->io = (CHIPTYPE_ACX100 == chip_type) ? IO_ACX100 : IO_ACX111;
++ adev->membase = (volatile u32 *) ndev->base_addr;
++ adev->iobase = (volatile u32 *) ioremap_nocache (ndev->base_addr, addr_size);
++ /* to find crashes due to weird driver access
++ * to unconfigured interface (ifup) */
++ adev->mgmt_timer.function = (void (*)(unsigned long))0x0000dead;
++
++#if defined(NONESSENTIAL_FEATURES)
++ acx_show_card_eeprom_id(adev);
++#endif /* NONESSENTIAL_FEATURES */
++
++#ifdef SET_MODULE_OWNER
++ SET_MODULE_OWNER(ndev);
++#endif
++ SET_NETDEV_DEV(ndev, &pdev->dev);
++
++ log(L_IRQ|L_INIT, "using IRQ %d\n", ndev->irq);
++
++ /* ok, pci setup is finished, now start initializing the card */
++
++ if (OK != acxmem_complete_hw_reset (adev))
++ goto fail_reset;
++
++ /*
++ * Set up default things for most of the card settings.
++ */
++ acx_s_set_defaults(adev);
++
++ /* Register the card, AFTER everything else has been set up,
++ * since otherwise an ioctl could step on our feet due to
++ * firmware operations happening in parallel or uninitialized data */
++ err = register_netdev(ndev);
++ if (OK != err) {
++ printk("acx: register_netdev() FAILED: %d\n", err);
++ goto fail_register_netdev;
++ }
++
++ acx_proc_register_entries(ndev);
++
++ /* Now we have our device, so make sure the kernel doesn't try
++ * to send packets even though we're not associated to a network yet */
++ acx_stop_queue(ndev, "on probe");
++ acx_carrier_off(ndev, "on probe");
++
++ /*
++ * Set up a default monitor type so that poor combinations of initialization
++ * sequences in monitor mode don't end up destroying the hardware type.
++ */
++ adev->monitor_type = ARPHRD_ETHER;
++
++ /*
++ * Register to receive inetaddr notifier changes. This will allow us to
++ * catch if the user changes the MAC address of the interface.
++ */
++ register_netdevice_notifier(&acx_netdev_notifier);
++
++ /* after register_netdev() userspace may start working with dev
++ * (in particular, on other CPUs), we only need to up the sem */
++ /* acx_sem_unlock(adev); */
++
++ printk("acx "ACX_RELEASE": net device %s, driver compiled "
++ "against wireless extensions %d and Linux %s\n",
++ ndev->name, WIRELESS_EXT, UTS_RELEASE);
++
++#if CMD_DISCOVERY
++ great_inquisitor(adev);
++#endif
++
++ result = OK;
++ goto done;
++
++ /* error paths: undo everything in reverse order... */
++
++fail_register_netdev:
++
++ acxmem_s_delete_dma_regions(adev);
++
++fail_reset:
++fail_hw_params:
++ free_netdev(ndev);
++fail_alloc_netdev:
++fail_unknown_chiptype:
++
++
++done:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acxmem_e_remove
++**
++** Shut device down (if not hot unplugged)
++** and deallocate PCI resources for the acx chip.
++**
++** pdev - ptr to PCI device structure containing info about pci configuration
++*/
++static int __devexit
++acxmem_e_remove(struct platform_device *pdev)
++{
++ struct acx_hardware_data *hwdata = pdev->dev.platform_data;
++ struct net_device *ndev;
++ acx_device_t *adev;
++ unsigned long flags;
++
++ FN_ENTER;
++
++ ndev = (struct net_device*) platform_get_drvdata(pdev);
++ if (!ndev) {
++ log(L_DEBUG, "%s: card is unused. Skipping any release code\n",
++ __func__);
++ goto end;
++ }
++
++ adev = ndev2adev(ndev);
++
++ /* If device wasn't hot unplugged... */
++ if (adev_present(adev)) {
++
++ acx_sem_lock(adev);
++
++ /* disable both Tx and Rx to shut radio down properly */
++ acx_s_issue_cmd(adev, ACX1xx_CMD_DISABLE_TX, NULL, 0);
++ acx_s_issue_cmd(adev, ACX1xx_CMD_DISABLE_RX, NULL, 0);
++
++#ifdef REDUNDANT
++ /* put the eCPU to sleep to save power
++ * Halting is not possible currently,
++ * since not supported by all firmware versions */
++ acx_s_issue_cmd(adev, ACX100_CMD_SLEEP, NULL, 0);
++#endif
++ acx_lock(adev, flags);
++
++ /* disable power LED to save power :-) */
++ log(L_INIT, "switching off power LED to save power\n");
++ acxmem_l_power_led(adev, 0);
++
++ /* stop our eCPU */
++ if (IS_ACX111(adev)) {
++ /* FIXME: does this actually keep halting the eCPU?
++ * I don't think so...
++ */
++ acxmem_l_reset_mac(adev);
++ } else {
++ u16 temp;
++
++ /* halt eCPU */
++ temp = read_reg16(adev, IO_ACX_ECPU_CTRL) | 0x1;
++ write_reg16(adev, IO_ACX_ECPU_CTRL, temp);
++ write_flush(adev);
++ }
++
++ acx_unlock(adev, flags);
++
++ acx_sem_unlock(adev);
++ }
++
++
++ /*
++ * Unregister the notifier chain
++ */
++ unregister_netdevice_notifier(&acx_netdev_notifier);
++
++ /* unregister the device to not let the kernel
++ * (e.g. ioctls) access a half-deconfigured device
++ * NB: this will cause acxmem_e_close() to be called,
++ * thus we shouldn't call it under sem! */
++ log(L_INIT, "removing device %s\n", ndev->name);
++ unregister_netdev(ndev);
++
++ /* unregister_netdev ensures that no references to us left.
++ * For paranoid reasons we continue to follow the rules */
++ acx_sem_lock(adev);
++
++ if (adev->dev_state_mask & ACX_STATE_IFACE_UP) {
++ acxmem_s_down(ndev);
++ CLEAR_BIT(adev->dev_state_mask, ACX_STATE_IFACE_UP);
++ }
++
++ acx_proc_unregister_entries(ndev);
++
++ acxmem_s_delete_dma_regions(adev);
++
++ /* finally, clean up PCI bus state */
++ if (adev->iobase) iounmap((void *)adev->iobase);
++
++ acx_sem_unlock(adev);
++
++ /* Free netdev (quite late,
++ * since otherwise we might get caught off-guard
++ * by a netdev timeout handler execution
++ * expecting to see a working dev...) */
++ free_netdev(ndev);
++
++ (void) hwdata->stop_hw();
++
++ printk ("e_remove done\n");
++end:
++ FN_EXIT0;
++
++ return 0;
++}
++
++
++/***********************************************************************
++** TODO: PM code needs to be fixed / debugged / tested.
++*/
++#ifdef CONFIG_PM
++static int
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11)
++acxmem_e_suspend(struct platform_device *pdev, pm_message_t state)
++#else
++acxmem_e_suspend(struct device *pdev, u32 state)
++#endif
++{
++ struct net_device *ndev = platform_get_drvdata(pdev);
++ acx_device_t *adev;
++ struct acx_hardware_data *hwdata;
++
++ FN_ENTER;
++ printk("acx: suspend handler is experimental!\n");
++ printk("sus: dev %p\n", ndev);
++
++ if (!netif_running(ndev))
++ goto end;
++
++ adev = ndev2adev(ndev);
++ printk("sus: adev %p\n", adev);
++
++ hwdata = adev->dev->platform_data;
++
++ acx_sem_lock(adev);
++
++ netif_device_detach(ndev); /* this one cannot sleep */
++ acxmem_s_down(ndev);
++ /* down() does not set it to 0xffff, but here we really want that */
++ write_reg16(adev, IO_ACX_IRQ_MASK, 0xffff);
++ write_reg16(adev, IO_ACX_FEMR, 0x0);
++ acxmem_s_delete_dma_regions(adev);
++
++ /*
++ * Turn the ACX chip off.
++ */
++ hwdata->stop_hw();
++
++ acx_sem_unlock(adev);
++end:
++ FN_EXIT0;
++ return OK;
++}
++
++
++
++static void
++fw_resumer(struct work_struct *notused)
++{
++ struct platform_device *pdev = resume_pdev;
++ struct net_device *ndev = platform_get_drvdata(pdev);
++ acx_device_t *adev;
++ struct acx_hardware_data *hwdata;
++
++ printk("acx: resume handler is experimental!\n");
++ printk("rsm: got dev %p\n", ndev);
++
++ if (!netif_running(ndev))
++ return;
++
++ adev = ndev2adev(ndev);
++ printk("rsm: got adev %p\n", adev);
++
++ acx_sem_lock(adev);
++
++ hwdata = adev->dev->platform_data;
++
++ /*
++ * Turn on the ACX.
++ */
++ hwdata->start_hw();
++
++ acxmem_complete_hw_reset (adev);
++
++ /*
++ * done by acx_s_set_defaults for initial startup
++ */
++ acxmem_set_interrupt_mask(adev);
++
++ printk ("rsm: bringing up interface\n");
++ SET_BIT (adev->set_mask, GETSET_ALL);
++ acxmem_s_up(ndev);
++ printk("rsm: acx up done\n");
++
++ /* now even reload all card parameters as they were before suspend,
++ * and possibly be back in the network again already :-)
++ */
++ /* - most settings updated in acxmem_s_up()
++ if (ACX_STATE_IFACE_UP & adev->dev_state_mask) {
++ adev->set_mask = GETSET_ALL;
++ acx_s_update_card_settings(adev);
++ printk("rsm: settings updated\n");
++ }
++ */
++ netif_device_attach(ndev);
++ printk("rsm: device attached\n");
++
++ acx_sem_unlock(adev);
++}
++
++DECLARE_WORK( fw_resume_work, fw_resumer );
++
++static int
++acxmem_e_resume(struct platform_device *pdev)
++{
++ FN_ENTER;
++
++ resume_pdev = pdev;
++ schedule_work( &fw_resume_work );
++
++ FN_EXIT0;
++ return OK;
++}
++#endif /* CONFIG_PM */
++
++
++/***********************************************************************
++** acxmem_s_up
++**
++** This function is called by acxmem_e_open (when ifconfig sets the device as up)
++**
++** Side effects:
++** - Enables on-card interrupt requests
++** - calls acx_s_start
++*/
++
++static void
++enable_acx_irq(acx_device_t *adev)
++{
++ FN_ENTER;
++ write_reg16(adev, IO_ACX_IRQ_MASK, adev->irq_mask);
++ write_reg16(adev, IO_ACX_FEMR, 0x8000);
++ adev->irqs_active = 1;
++ FN_EXIT0;
++}
++
++static void
++acxmem_s_up(struct net_device *ndev)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ unsigned long flags;
++
++ FN_ENTER;
++
++ acx_lock(adev, flags);
++ enable_acx_irq(adev);
++ acx_unlock(adev, flags);
++
++ /* acx fw < 1.9.3.e has a hardware timer, and older drivers
++ ** used to use it. But we don't do that anymore, our OS
++ ** has reliable software timers */
++ init_timer(&adev->mgmt_timer);
++ adev->mgmt_timer.function = acx_i_timer;
++ adev->mgmt_timer.data = (unsigned long)adev;
++
++ /* Need to set ACX_STATE_IFACE_UP first, or else
++ ** timer won't be started by acx_set_status() */
++ SET_BIT(adev->dev_state_mask, ACX_STATE_IFACE_UP);
++ switch (adev->mode) {
++ case ACX_MODE_0_ADHOC:
++ case ACX_MODE_2_STA:
++ /* actual scan cmd will happen in start() */
++ acx_set_status(adev, ACX_STATUS_1_SCANNING); break;
++ case ACX_MODE_3_AP:
++ case ACX_MODE_MONITOR:
++ acx_set_status(adev, ACX_STATUS_4_ASSOCIATED); break;
++ }
++
++ acx_s_start(adev);
++
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acxmem_s_down
++**
++** This disables the netdevice
++**
++** Side effects:
++** - disables on-card interrupt request
++*/
++
++static void
++disable_acx_irq(acx_device_t *adev)
++{
++ FN_ENTER;
++
++ /* I guess mask is not 0xffff because acx100 won't signal
++ ** cmd completion then (needed for ifup).
++ ** Someone with acx100 please confirm */
++ write_reg16(adev, IO_ACX_IRQ_MASK, adev->irq_mask_off);
++ write_reg16(adev, IO_ACX_FEMR, 0x0);
++ adev->irqs_active = 0;
++ FN_EXIT0;
++}
++
++static void
++acxmem_s_down(struct net_device *ndev)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ unsigned long flags;
++
++ FN_ENTER;
++
++ /* Disable IRQs first, so that IRQs cannot race with us */
++ /* then wait until interrupts have finished executing on other CPUs */
++ acx_lock(adev, flags);
++ disable_acx_irq(adev);
++ synchronize_irq(adev->pdev->irq);
++ acx_unlock(adev, flags);
++
++ /* we really don't want to have an asynchronous tasklet disturb us
++ ** after something vital for its job has been shut down, so
++ ** end all remaining work now.
++ **
++ ** NB: carrier_off (done by set_status below) would lead to
++ ** not yet fully understood deadlock in FLUSH_SCHEDULED_WORK().
++ ** That's why we do FLUSH first.
++ **
++ ** NB2: we have a bad locking bug here: FLUSH_SCHEDULED_WORK()
++ ** waits for acx_e_after_interrupt_task to complete if it is running
++ ** on another CPU, but acx_e_after_interrupt_task
++ ** will sleep on sem forever, because it is taken by us!
++ ** Work around that by temporary sem unlock.
++ ** This will fail miserably if we'll be hit by concurrent
++ ** iwconfig or something in between. TODO! */
++ acx_sem_unlock(adev);
++ FLUSH_SCHEDULED_WORK();
++ acx_sem_lock(adev);
++
++ /* This is possible:
++ ** FLUSH_SCHEDULED_WORK -> acx_e_after_interrupt_task ->
++ ** -> set_status(ASSOCIATED) -> wake_queue()
++ ** That's why we stop queue _after_ FLUSH_SCHEDULED_WORK
++ ** lock/unlock is just paranoia, maybe not needed */
++ acx_lock(adev, flags);
++ acx_stop_queue(ndev, "on ifdown");
++ acx_set_status(adev, ACX_STATUS_0_STOPPED);
++ acx_unlock(adev, flags);
++
++ /* kernel/timer.c says it's illegal to del_timer_sync()
++ ** a timer which restarts itself. We guarantee this cannot
++ ** ever happen because acx_i_timer() never does this if
++ ** status is ACX_STATUS_0_STOPPED */
++ del_timer_sync(&adev->mgmt_timer);
++
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acxmem_e_open
++**
++** Called as a result of SIOCSIFFLAGS ioctl changing the flags bit IFF_UP
++** from clear to set. In other words: ifconfig up.
++**
++** Returns:
++** 0 success
++** >0 f/w reported error
++** <0 driver reported error
++*/
++static int
++acxmem_e_open(struct net_device *ndev)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ int result = OK;
++
++ FN_ENTER;
++
++ acx_sem_lock(adev);
++
++ acx_init_task_scheduler(adev);
++
++/* TODO: pci_set_power_state(pdev, PCI_D0); ? */
++
++ /* request shared IRQ handler */
++ if (request_irq(ndev->irq, acxmem_i_interrupt, SA_INTERRUPT, ndev->name, ndev)) {
++ printk("%s: request_irq FAILED\n", ndev->name);
++ result = -EAGAIN;
++ goto done;
++ }
++ set_irq_type (ndev->irq, IRQT_FALLING);
++ log(L_DEBUG|L_IRQ, "request_irq %d successful\n", ndev->irq);
++
++ /* ifup device */
++ acxmem_s_up(ndev);
++
++ /* We don't currently have to do anything else.
++ * The setup of the MAC should be subsequently completed via
++ * the mlme commands.
++ * Higher layers know we're ready from dev->start==1 and
++ * dev->tbusy==0. Our rx path knows to pass up received/
++ * frames because of dev->flags&IFF_UP is true.
++ */
++done:
++ acx_sem_unlock(adev);
++
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acxmem_e_close
++**
++** Called as a result of SIOCSIIFFLAGS ioctl changing the flags bit IFF_UP
++** from set to clear. I.e. called by "ifconfig DEV down"
++**
++** Returns:
++** 0 success
++** >0 f/w reported error
++** <0 driver reported error
++*/
++static int
++acxmem_e_close(struct net_device *ndev)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++
++ FN_ENTER;
++
++ acx_sem_lock(adev);
++
++ /* ifdown device */
++ CLEAR_BIT(adev->dev_state_mask, ACX_STATE_IFACE_UP);
++ if (netif_device_present(ndev)) {
++ acxmem_s_down(ndev);
++ }
++
++ /* disable all IRQs, release shared IRQ handler */
++ write_reg16(adev, IO_ACX_IRQ_MASK, 0xffff);
++ write_reg16(adev, IO_ACX_FEMR, 0x0);
++ free_irq(ndev->irq, ndev);
++
++/* TODO: pci_set_power_state(pdev, PCI_D3hot); ? */
++
++ /* We currently don't have to do anything else.
++ * Higher layers know we're not ready from dev->start==0 and
++ * dev->tbusy==1. Our rx path knows to not pass up received
++ * frames because of dev->flags&IFF_UP is false.
++ */
++ acx_sem_unlock(adev);
++
++ log(L_INIT, "closed device\n");
++ FN_EXIT0;
++ return OK;
++}
++
++
++/***********************************************************************
++** acxmem_i_tx_timeout
++**
++** Called from network core. Must not sleep!
++*/
++static void
++acxmem_i_tx_timeout(struct net_device *ndev)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ unsigned long flags;
++ unsigned int tx_num_cleaned;
++
++ FN_ENTER;
++
++ acx_lock(adev, flags);
++
++ /* clean processed tx descs, they may have been completely full */
++ tx_num_cleaned = acxmem_l_clean_txdesc(adev);
++
++ /* nothing cleaned, yet (almost) no free buffers available?
++ * --> clean all tx descs, no matter which status!!
++ * Note that I strongly suspect that doing emergency cleaning
++ * may confuse the firmware. This is a last ditch effort to get
++ * ANYTHING to work again...
++ *
++ * TODO: it's best to simply reset & reinit hw from scratch...
++ */
++ if ((adev->tx_free <= TX_EMERG_CLEAN) && (tx_num_cleaned == 0)) {
++ printk("%s: FAILED to free any of the many full tx buffers. "
++ "Switching to emergency freeing. "
++ "Please report!\n", ndev->name);
++ acxmem_l_clean_txdesc_emergency(adev);
++ }
++
++ if (acx_queue_stopped(ndev) && (ACX_STATUS_4_ASSOCIATED == adev->status))
++ acx_wake_queue(ndev, "after tx timeout");
++
++ /* stall may have happened due to radio drift, so recalib radio */
++ acx_schedule_task(adev, ACX_AFTER_IRQ_CMD_RADIO_RECALIB);
++
++ /* do unimportant work last */
++ printk("%s: tx timeout!\n", ndev->name);
++ adev->stats.tx_errors++;
++
++ acx_unlock(adev, flags);
++
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acxmem_i_set_multicast_list
++** FIXME: most likely needs refinement
++*/
++static void
++acxmem_i_set_multicast_list(struct net_device *ndev)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ unsigned long flags;
++
++ FN_ENTER;
++
++ acx_lock(adev, flags);
++
++ /* firmwares don't have allmulti capability,
++ * so just use promiscuous mode instead in this case. */
++ if (ndev->flags & (IFF_PROMISC|IFF_ALLMULTI)) {
++ SET_BIT(adev->rx_config_1, RX_CFG1_RCV_PROMISCUOUS);
++ CLEAR_BIT(adev->rx_config_1, RX_CFG1_FILTER_ALL_MULTI);
++ SET_BIT(adev->set_mask, SET_RXCONFIG);
++ /* let kernel know in case *we* needed to set promiscuous */
++ ndev->flags |= (IFF_PROMISC|IFF_ALLMULTI);
++ } else {
++ CLEAR_BIT(adev->rx_config_1, RX_CFG1_RCV_PROMISCUOUS);
++ SET_BIT(adev->rx_config_1, RX_CFG1_FILTER_ALL_MULTI);
++ SET_BIT(adev->set_mask, SET_RXCONFIG);
++ ndev->flags &= ~(IFF_PROMISC|IFF_ALLMULTI);
++ }
++
++ /* cannot update card settings directly here, atomic context */
++ acx_schedule_task(adev, ACX_AFTER_IRQ_UPDATE_CARD_CFG);
++
++ acx_unlock(adev, flags);
++
++ FN_EXIT0;
++}
++
++
++/***************************************************************
++** acxmem_l_process_rxdesc
++**
++** Called directly and only from the IRQ handler
++*/
++
++#if !ACX_DEBUG
++static inline void log_rxbuffer(const acx_device_t *adev) {}
++#else
++static void
++log_rxbuffer(const acx_device_t *adev)
++{
++ register const struct rxhostdesc *rxhostdesc;
++ int i;
++ /* no FN_ENTER here, we don't want that */
++
++ rxhostdesc = adev->rxhostdesc_start;
++ if (unlikely(!rxhostdesc)) return;
++ for (i = 0; i < RX_CNT; i++) {
++ if ((rxhostdesc->Ctl_16 & cpu_to_le16(DESC_CTL_HOSTOWN))
++ && (rxhostdesc->Status & cpu_to_le32(DESC_STATUS_FULL)))
++ printk("rx: buf %d full\n", i);
++ rxhostdesc++;
++ }
++}
++#endif
++
++static void
++acxmem_l_process_rxdesc(acx_device_t *adev)
++{
++ register rxhostdesc_t *hostdesc;
++ register rxdesc_t *rxdesc;
++ unsigned count, tail;
++ u32 addr;
++ u8 Ctl_8;
++
++ FN_ENTER;
++
++ if (unlikely(acx_debug & L_BUFR))
++ log_rxbuffer(adev);
++
++ /* First, have a loop to determine the first descriptor that's
++ * full, just in case there's a mismatch between our current
++ * rx_tail and the full descriptor we're supposed to handle. */
++ tail = adev->rx_tail;
++ count = RX_CNT;
++ while (1) {
++ hostdesc = &adev->rxhostdesc_start[tail];
++ rxdesc = &adev->rxdesc_start[tail];
++ /* advance tail regardless of outcome of the below test */
++ tail = (tail + 1) % RX_CNT;
++
++ /*
++ * Unlike the PCI interface, where the ACX can write directly to
++ * the host descriptors, on the slave memory interface we have to
++ * pull these. All we really need to do is check the Ctl_8 field
++ * in the rx descriptor on the ACX, which should be 0x11000000 if
++ * we should process it.
++ */
++ Ctl_8 = hostdesc->Ctl_16 = read_slavemem8 (adev, (u32) &(rxdesc->Ctl_8));
++ if ((Ctl_8 & DESC_CTL_HOSTOWN) &&
++ (Ctl_8 & DESC_CTL_ACXDONE))
++ break; /* found it! */
++
++ if (unlikely(!--count)) /* hmm, no luck: all descs empty, bail out */
++ goto end;
++ }
++
++ /* now process descriptors, starting with the first we figured out */
++ while (1) {
++ log(L_BUFR, "rx: tail=%u Ctl_8=%02X\n", tail, Ctl_8);
++ /*
++ * If the ACX has CTL_RECLAIM set on this descriptor there
++ * is no buffer associated; it just wants us to tell it to
++ * reclaim the memory.
++ */
++ if (!(Ctl_8 & DESC_CTL_RECLAIM)) {
++
++ /*
++ * slave interface - pull data now
++ */
++ hostdesc->length = read_slavemem16 (adev, (u32) &(rxdesc->total_length));
++
++ /*
++ * hostdesc->data is an rxbuffer_t, which includes header information,
++ * but the length in the data packet doesn't. The header information
++ * takes up an additional 12 bytes, so add that to the length we copy.
++ */
++ addr = read_slavemem32 (adev, (u32) &(rxdesc->ACXMemPtr));
++ if (addr) {
++ /*
++ * How can &(rxdesc->ACXMemPtr) above ever be zero? Looks like we
++ * get that now and then - try to trap it for debug.
++ */
++ if (addr & 0xffff0000) {
++ printk("rxdesc 0x%08x\n", (u32) rxdesc);
++ dump_acxmem (adev, 0, 0x10000);
++ panic ("Bad access!");
++ }
++ chaincopy_from_slavemem (adev, (u8 *) hostdesc->data, addr,
++ hostdesc->length +
++ (u32) &((rxbuffer_t *)0)->hdr_a3);
++ acx_l_process_rxbuf(adev, hostdesc->data);
++ }
++ }
++ else {
++ printk ("rx reclaim only!\n");
++ }
++
++ hostdesc->Status = 0;
++
++ /*
++ * Let the ACX know we're done.
++ */
++ CLEAR_BIT (Ctl_8, DESC_CTL_HOSTOWN);
++ SET_BIT (Ctl_8, DESC_CTL_HOSTDONE);
++ SET_BIT (Ctl_8, DESC_CTL_RECLAIM);
++ write_slavemem8 (adev, (u32) &rxdesc->Ctl_8, Ctl_8);
++
++ /*
++ * Now tell the ACX we've finished with the receive buffer so
++ * it can finish the reclaim.
++ */
++ write_reg16 (adev, IO_ACX_INT_TRIG, INT_TRIG_RXPRC);
++
++ /* ok, descriptor is handled, now check the next descriptor */
++ hostdesc = &adev->rxhostdesc_start[tail];
++ rxdesc = &adev->rxdesc_start[tail];
++
++ Ctl_8 = hostdesc->Ctl_16 = read_slavemem8 (adev, (u32) &(rxdesc->Ctl_8));
++
++ /* if next descriptor is empty, then bail out */
++ if (!(Ctl_8 & DESC_CTL_HOSTOWN) || !(Ctl_8 & DESC_CTL_ACXDONE))
++ break;
++
++ tail = (tail + 1) % RX_CNT;
++ }
++end:
++ adev->rx_tail = tail;
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acxmem_i_interrupt
++**
++** IRQ handler (atomic context, must not sleep, blah, blah)
++*/
++
++/* scan is complete. all frames now on the receive queue are valid */
++#define INFO_SCAN_COMPLETE 0x0001
++#define INFO_WEP_KEY_NOT_FOUND 0x0002
++/* hw has been reset as the result of a watchdog timer timeout */
++#define INFO_WATCH_DOG_RESET 0x0003
++/* failed to send out NULL frame from PS mode notification to AP */
++/* recommended action: try entering 802.11 PS mode again */
++#define INFO_PS_FAIL 0x0004
++/* encryption/decryption process on a packet failed */
++#define INFO_IV_ICV_FAILURE 0x0005
++
++/* Info mailbox format:
++2 bytes: type
++2 bytes: status
++more bytes may follow
++ rumors say about status:
++ 0x0000 info available (set by hw)
++ 0x0001 information received (must be set by host)
++ 0x1000 info available, mailbox overflowed (messages lost) (set by hw)
++ but in practice we've seen:
++ 0x9000 when we did not set status to 0x0001 on prev message
++ 0x1001 when we did set it
++ 0x0000 was never seen
++ conclusion: this is really a bitfield:
++ 0x1000 is 'info available' bit
++ 'mailbox overflowed' bit is 0x8000, not 0x1000
++ value of 0x0000 probably means that there are no messages at all
++ P.S. I dunno how in hell hw is supposed to notice that messages are lost -
++ it does NOT clear bit 0x0001, and this bit will probably stay forever set
++ after we set it once. Let's hope this will be fixed in firmware someday
++*/
++
++static void
++handle_info_irq(acx_device_t *adev)
++{
++#if ACX_DEBUG
++ static const char * const info_type_msg[] = {
++ "(unknown)",
++ "scan complete",
++ "WEP key not found",
++ "internal watchdog reset was done",
++ "failed to send powersave (NULL frame) notification to AP",
++ "encrypt/decrypt on a packet has failed",
++ "TKIP tx keys disabled",
++ "TKIP rx keys disabled",
++ "TKIP rx: key ID not found",
++ "???",
++ "???",
++ "???",
++ "???",
++ "???",
++ "???",
++ "???",
++ "TKIP IV value exceeds thresh"
++ };
++#endif
++ u32 info_type, info_status;
++
++ info_type = read_slavemem32 (adev, (u32) adev->info_area);
++
++ info_status = (info_type >> 16);
++ info_type = (u16)info_type;
++
++ /* inform fw that we have read this info message */
++ write_slavemem32(adev, (u32) adev->info_area, info_type | 0x00010000);
++ write_reg16(adev, IO_ACX_INT_TRIG, INT_TRIG_INFOACK);
++ write_flush(adev);
++
++ log(L_CTL, "info_type:%04X info_status:%04X\n",
++ info_type, info_status);
++
++ log(L_IRQ, "got Info IRQ: status %04X type %04X: %s\n",
++ info_status, info_type,
++ info_type_msg[(info_type >= VEC_SIZE(info_type_msg)) ?
++ 0 : info_type]
++ );
++}
++
++
++static void
++log_unusual_irq(u16 irqtype) {
++ /*
++ if (!printk_ratelimit())
++ return;
++ */
++
++ printk("acx: got");
++ if (irqtype & HOST_INT_TX_XFER) {
++ printk(" Tx_Xfer");
++ }
++ if (irqtype & HOST_INT_RX_COMPLETE) {
++ printk(" Rx_Complete");
++ }
++ if (irqtype & HOST_INT_DTIM) {
++ printk(" DTIM");
++ }
++ if (irqtype & HOST_INT_BEACON) {
++ printk(" Beacon");
++ }
++ if (irqtype & HOST_INT_TIMER) {
++ log(L_IRQ, " Timer");
++ }
++ if (irqtype & HOST_INT_KEY_NOT_FOUND) {
++ printk(" Key_Not_Found");
++ }
++ if (irqtype & HOST_INT_IV_ICV_FAILURE) {
++ printk(" IV_ICV_Failure (crypto)");
++ }
++ /* HOST_INT_CMD_COMPLETE */
++ /* HOST_INT_INFO */
++ if (irqtype & HOST_INT_OVERFLOW) {
++ printk(" Overflow");
++ }
++ if (irqtype & HOST_INT_PROCESS_ERROR) {
++ printk(" Process_Error");
++ }
++ /* HOST_INT_SCAN_COMPLETE */
++ if (irqtype & HOST_INT_FCS_THRESHOLD) {
++ printk(" FCS_Threshold");
++ }
++ if (irqtype & HOST_INT_UNKNOWN) {
++ printk(" Unknown");
++ }
++ printk(" IRQ(s)\n");
++}
++
++
++static void
++update_link_quality_led(acx_device_t *adev)
++{
++ int qual;
++
++ qual = acx_signal_determine_quality(adev->wstats.qual.level, adev->wstats.qual.noise);
++ if (qual > adev->brange_max_quality)
++ qual = adev->brange_max_quality;
++
++ if (time_after(jiffies, adev->brange_time_last_state_change +
++ (HZ/2 - HZ/2 * (unsigned long)qual / adev->brange_max_quality ) )) {
++ acxmem_l_power_led(adev, (adev->brange_last_state == 0));
++ adev->brange_last_state ^= 1; /* toggle */
++ adev->brange_time_last_state_change = jiffies;
++ }
++}
++
++
++#define MAX_IRQLOOPS_PER_JIFFY (20000/HZ) /* a la orinoco.c */
++
++static irqreturn_t
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
++acxmem_i_interrupt(int irq, void *dev_id)
++#else
++acxmwm_i_interrupt(int irq, void *dev_id, struct pt_regs *regs)
++#endif
++{
++ acx_device_t *adev;
++ unsigned long flags;
++ unsigned int irqcount = MAX_IRQLOOPS_PER_JIFFY;
++ register u16 irqtype;
++ u16 unmasked;
++
++ adev = ndev2adev((struct net_device*)dev_id);
++
++ /* LOCKING: can just spin_lock() since IRQs are disabled anyway.
++ * I am paranoid */
++ acx_lock(adev, flags);
++
++ unmasked = read_reg16(adev, IO_ACX_IRQ_STATUS_CLEAR);
++ if (unlikely(0xffff == unmasked)) {
++ /* 0xffff value hints at missing hardware,
++ * so don't do anything.
++ * Not very clean, but other drivers do the same... */
++ log(L_IRQ, "IRQ type:FFFF - device removed? IRQ_NONE\n");
++ goto none;
++ }
++
++ /* We will check only "interesting" IRQ types */
++ irqtype = unmasked & ~adev->irq_mask;
++ if (!irqtype) {
++ /* We are on a shared IRQ line and it wasn't our IRQ */
++ log(L_IRQ, "IRQ type:%04X, mask:%04X - all are masked, IRQ_NONE\n",
++ unmasked, adev->irq_mask);
++ goto none;
++ }
++
++ /* Done here because IRQ_NONEs taking three lines of log
++ ** drive me crazy */
++ FN_ENTER;
++
++#define IRQ_ITERATE 1
++#if IRQ_ITERATE
++if (jiffies != adev->irq_last_jiffies) {
++ adev->irq_loops_this_jiffy = 0;
++ adev->irq_last_jiffies = jiffies;
++}
++
++/* safety condition; we'll normally abort loop below
++ * in case no IRQ type occurred */
++while (likely(--irqcount)) {
++#endif
++ /* ACK all IRQs ASAP */
++ write_reg16(adev, IO_ACX_IRQ_ACK, 0xffff);
++
++ log(L_IRQ, "IRQ type:%04X, mask:%04X, type & ~mask:%04X\n",
++ unmasked, adev->irq_mask, irqtype);
++
++ /* Handle most important IRQ types first */
++ if (irqtype & HOST_INT_RX_DATA) {
++ log(L_IRQ, "got Rx_Data IRQ\n");
++ acxmem_l_process_rxdesc(adev);
++ }
++ if (irqtype & HOST_INT_TX_COMPLETE) {
++ log(L_IRQ, "got Tx_Complete IRQ\n");
++ /* don't clean up on each Tx complete, wait a bit
++ * unless we're going towards full, in which case
++ * we do it immediately, too (otherwise we might lockup
++ * with a full Tx buffer if we go into
++ * acxmem_l_clean_txdesc() at a time when we won't wakeup
++ * the net queue in there for some reason...) */
++ if (adev->tx_free <= TX_START_CLEAN) {
++#if TX_CLEANUP_IN_SOFTIRQ
++ acx_schedule_task(adev, ACX_AFTER_IRQ_TX_CLEANUP);
++#else
++ acxmem_l_clean_txdesc(adev);
++#endif
++ }
++ }
++
++ /* Less frequent ones */
++ if (irqtype & (0
++ | HOST_INT_CMD_COMPLETE
++ | HOST_INT_INFO
++ | HOST_INT_SCAN_COMPLETE
++ )) {
++ if (irqtype & HOST_INT_CMD_COMPLETE) {
++ log(L_IRQ, "got Command_Complete IRQ\n");
++ /* save the state for the running issue_cmd() */
++ SET_BIT(adev->irq_status, HOST_INT_CMD_COMPLETE);
++ }
++ if (irqtype & HOST_INT_INFO) {
++ handle_info_irq(adev);
++ }
++ if (irqtype & HOST_INT_SCAN_COMPLETE) {
++ log(L_IRQ, "got Scan_Complete IRQ\n");
++ /* need to do that in process context */
++ acx_schedule_task(adev, ACX_AFTER_IRQ_COMPLETE_SCAN);
++ /* remember that fw is not scanning anymore */
++ SET_BIT(adev->irq_status, HOST_INT_SCAN_COMPLETE);
++ }
++ }
++
++ /* These we just log, but either they happen rarely
++ * or we keep them masked out */
++ if (irqtype & (0
++ /* | HOST_INT_RX_DATA */
++ /* | HOST_INT_TX_COMPLETE */
++ | HOST_INT_TX_XFER
++ | HOST_INT_RX_COMPLETE
++ | HOST_INT_DTIM
++ | HOST_INT_BEACON
++ | HOST_INT_TIMER
++ | HOST_INT_KEY_NOT_FOUND
++ | HOST_INT_IV_ICV_FAILURE
++ /* | HOST_INT_CMD_COMPLETE */
++ /* | HOST_INT_INFO */
++ | HOST_INT_OVERFLOW
++ | HOST_INT_PROCESS_ERROR
++ /* | HOST_INT_SCAN_COMPLETE */
++ | HOST_INT_FCS_THRESHOLD
++ | HOST_INT_UNKNOWN
++ )) {
++ log_unusual_irq(irqtype);
++ }
++
++#if IRQ_ITERATE
++ unmasked = read_reg16(adev, IO_ACX_IRQ_STATUS_CLEAR);
++ irqtype = unmasked & ~adev->irq_mask;
++ /* Bail out if no new IRQ bits or if all are masked out */
++ if (!irqtype)
++ break;
++
++ if (unlikely(++adev->irq_loops_this_jiffy > MAX_IRQLOOPS_PER_JIFFY)) {
++ printk(KERN_ERR "acx: too many interrupts per jiffy!\n");
++ /* Looks like card floods us with IRQs! Try to stop that */
++ write_reg16(adev, IO_ACX_IRQ_MASK, 0xffff);
++ /* This will short-circuit all future attempts to handle IRQ.
++ * We cant do much more... */
++ adev->irq_mask = 0;
++ break;
++ }
++}
++#endif
++ /* Routine to perform blink with range */
++ if (unlikely(adev->led_power == 2))
++ update_link_quality_led(adev);
++
++/* handled: */
++ /* write_flush(adev); - not needed, last op was read anyway */
++ acx_unlock(adev, flags);
++ FN_EXIT0;
++ return IRQ_HANDLED;
++
++none:
++ acx_unlock(adev, flags);
++ return IRQ_NONE;
++}
++
++
++/***********************************************************************
++** acxmem_l_power_led
++*/
++void
++acxmem_l_power_led(acx_device_t *adev, int enable)
++{
++ u16 gpio_pled = IS_ACX111(adev) ? 0x0040 : 0x0800;
++
++ /* A hack. Not moving message rate limiting to adev->xxx
++ * (it's only a debug message after all) */
++ static int rate_limit = 0;
++
++ if (rate_limit++ < 3)
++ log(L_IOCTL, "Please report in case toggling the power "
++ "LED doesn't work for your card!\n");
++ if (enable)
++ write_reg16(adev, IO_ACX_GPIO_OUT,
++ read_reg16(adev, IO_ACX_GPIO_OUT) & ~gpio_pled);
++ else
++ write_reg16(adev, IO_ACX_GPIO_OUT,
++ read_reg16(adev, IO_ACX_GPIO_OUT) | gpio_pled);
++}
++
++
++/***********************************************************************
++** Ioctls
++*/
++
++/***********************************************************************
++*/
++int
++acx111pci_ioctl_info(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++#if ACX_DEBUG > 1
++ acx_device_t *adev = ndev2adev(ndev);
++ rxdesc_t *rxdesc;
++ txdesc_t *txdesc;
++ rxhostdesc_t *rxhostdesc;
++ txhostdesc_t *txhostdesc;
++ struct acx111_ie_memoryconfig memconf;
++ struct acx111_ie_queueconfig queueconf;
++ unsigned long flags;
++ int i;
++ char memmap[0x34];
++ char rxconfig[0x8];
++ char fcserror[0x8];
++ char ratefallback[0x5];
++
++ if ( !(acx_debug & (L_IOCTL|L_DEBUG)) )
++ return OK;
++ /* using printk() since we checked debug flag already */
++
++ acx_sem_lock(adev);
++
++ if (!IS_ACX111(adev)) {
++ printk("acx111-specific function called "
++ "with non-acx111 chip, aborting\n");
++ goto end_ok;
++ }
++
++ /* get Acx111 Memory Configuration */
++ memset(&memconf, 0, sizeof(memconf));
++ /* BTW, fails with 12 (Write only) error code.
++ ** Retained for easy testing of issue_cmd error handling :) */
++ printk ("Interrogating queue config\n");
++ acx_s_interrogate(adev, &memconf, ACX1xx_IE_QUEUE_CONFIG);
++ printk ("done with queue config\n");
++
++ /* get Acx111 Queue Configuration */
++ memset(&queueconf, 0, sizeof(queueconf));
++ printk ("Interrogating mem config options\n");
++ acx_s_interrogate(adev, &queueconf, ACX1xx_IE_MEMORY_CONFIG_OPTIONS);
++ printk ("done with mem config options\n");
++
++ /* get Acx111 Memory Map */
++ memset(memmap, 0, sizeof(memmap));
++ printk ("Interrogating mem map\n");
++ acx_s_interrogate(adev, &memmap, ACX1xx_IE_MEMORY_MAP);
++ printk ("done with mem map\n");
++
++ /* get Acx111 Rx Config */
++ memset(rxconfig, 0, sizeof(rxconfig));
++ printk ("Interrogating rxconfig\n");
++ acx_s_interrogate(adev, &rxconfig, ACX1xx_IE_RXCONFIG);
++ printk ("done with queue rxconfig\n");
++
++ /* get Acx111 fcs error count */
++ memset(fcserror, 0, sizeof(fcserror));
++ printk ("Interrogating fcs err count\n");
++ acx_s_interrogate(adev, &fcserror, ACX1xx_IE_FCS_ERROR_COUNT);
++ printk ("done with err count\n");
++
++ /* get Acx111 rate fallback */
++ memset(ratefallback, 0, sizeof(ratefallback));
++ printk ("Interrogating rate fallback\n");
++ acx_s_interrogate(adev, &ratefallback, ACX1xx_IE_RATE_FALLBACK);
++ printk ("done with rate fallback\n");
++
++ /* force occurrence of a beacon interrupt */
++ /* TODO: comment why is this necessary */
++ write_reg16(adev, IO_ACX_HINT_TRIG, HOST_INT_BEACON);
++
++ /* dump Acx111 Mem Configuration */
++ printk("dump mem config:\n"
++ "data read: %d, struct size: %d\n"
++ "Number of stations: %1X\n"
++ "Memory block size: %1X\n"
++ "tx/rx memory block allocation: %1X\n"
++ "count rx: %X / tx: %X queues\n"
++ "options %1X\n"
++ "fragmentation %1X\n"
++ "Rx Queue 1 Count Descriptors: %X\n"
++ "Rx Queue 1 Host Memory Start: %X\n"
++ "Tx Queue 1 Count Descriptors: %X\n"
++ "Tx Queue 1 Attributes: %X\n",
++ memconf.len, (int) sizeof(memconf),
++ memconf.no_of_stations,
++ memconf.memory_block_size,
++ memconf.tx_rx_memory_block_allocation,
++ memconf.count_rx_queues, memconf.count_tx_queues,
++ memconf.options,
++ memconf.fragmentation,
++ memconf.rx_queue1_count_descs,
++ acx2cpu(memconf.rx_queue1_host_rx_start),
++ memconf.tx_queue1_count_descs,
++ memconf.tx_queue1_attributes);
++
++ /* dump Acx111 Queue Configuration */
++ printk("dump queue head:\n"
++ "data read: %d, struct size: %d\n"
++ "tx_memory_block_address (from card): %X\n"
++ "rx_memory_block_address (from card): %X\n"
++ "rx1_queue address (from card): %X\n"
++ "tx1_queue address (from card): %X\n"
++ "tx1_queue attributes (from card): %X\n",
++ queueconf.len, (int) sizeof(queueconf),
++ queueconf.tx_memory_block_address,
++ queueconf.rx_memory_block_address,
++ queueconf.rx1_queue_address,
++ queueconf.tx1_queue_address,
++ queueconf.tx1_attributes);
++
++ /* dump Acx111 Mem Map */
++ printk("dump mem map:\n"
++ "data read: %d, struct size: %d\n"
++ "Code start: %X\n"
++ "Code end: %X\n"
++ "WEP default key start: %X\n"
++ "WEP default key end: %X\n"
++ "STA table start: %X\n"
++ "STA table end: %X\n"
++ "Packet template start: %X\n"
++ "Packet template end: %X\n"
++ "Queue memory start: %X\n"
++ "Queue memory end: %X\n"
++ "Packet memory pool start: %X\n"
++ "Packet memory pool end: %X\n"
++ "iobase: %p\n"
++ "iobase2: %p\n",
++ *((u16 *)&memmap[0x02]), (int) sizeof(memmap),
++ *((u32 *)&memmap[0x04]),
++ *((u32 *)&memmap[0x08]),
++ *((u32 *)&memmap[0x0C]),
++ *((u32 *)&memmap[0x10]),
++ *((u32 *)&memmap[0x14]),
++ *((u32 *)&memmap[0x18]),
++ *((u32 *)&memmap[0x1C]),
++ *((u32 *)&memmap[0x20]),
++ *((u32 *)&memmap[0x24]),
++ *((u32 *)&memmap[0x28]),
++ *((u32 *)&memmap[0x2C]),
++ *((u32 *)&memmap[0x30]),
++ adev->iobase,
++ adev->iobase2);
++
++ /* dump Acx111 Rx Config */
++ printk("dump rx config:\n"
++ "data read: %d, struct size: %d\n"
++ "rx config: %X\n"
++ "rx filter config: %X\n",
++ *((u16 *)&rxconfig[0x02]), (int) sizeof(rxconfig),
++ *((u16 *)&rxconfig[0x04]),
++ *((u16 *)&rxconfig[0x06]));
++
++ /* dump Acx111 fcs error */
++ printk("dump fcserror:\n"
++ "data read: %d, struct size: %d\n"
++ "fcserrors: %X\n",
++ *((u16 *)&fcserror[0x02]), (int) sizeof(fcserror),
++ *((u32 *)&fcserror[0x04]));
++
++ /* dump Acx111 rate fallback */
++ printk("dump rate fallback:\n"
++ "data read: %d, struct size: %d\n"
++ "ratefallback: %X\n",
++ *((u16 *)&ratefallback[0x02]), (int) sizeof(ratefallback),
++ *((u8 *)&ratefallback[0x04]));
++
++ /* protect against IRQ */
++ acx_lock(adev, flags);
++
++ /* dump acx111 internal rx descriptor ring buffer */
++ rxdesc = adev->rxdesc_start;
++
++ /* loop over complete receive pool */
++ if (rxdesc) for (i = 0; i < RX_CNT; i++) {
++ printk("\ndump internal rxdesc %d:\n"
++ "mem pos %p\n"
++ "next 0x%X\n"
++ "acx mem pointer (dynamic) 0x%X\n"
++ "CTL (dynamic) 0x%X\n"
++ "Rate (dynamic) 0x%X\n"
++ "RxStatus (dynamic) 0x%X\n"
++ "Mod/Pre (dynamic) 0x%X\n",
++ i,
++ rxdesc,
++ acx2cpu(rxdesc->pNextDesc),
++ acx2cpu(rxdesc->ACXMemPtr),
++ rxdesc->Ctl_8,
++ rxdesc->rate,
++ rxdesc->error,
++ rxdesc->SNR);
++ rxdesc++;
++ }
++
++ /* dump host rx descriptor ring buffer */
++
++ rxhostdesc = adev->rxhostdesc_start;
++
++ /* loop over complete receive pool */
++ if (rxhostdesc) for (i = 0; i < RX_CNT; i++) {
++ printk("\ndump host rxdesc %d:\n"
++ "mem pos %p\n"
++ "buffer mem pos 0x%X\n"
++ "buffer mem offset 0x%X\n"
++ "CTL 0x%X\n"
++ "Length 0x%X\n"
++ "next 0x%X\n"
++ "Status 0x%X\n",
++ i,
++ rxhostdesc,
++ acx2cpu(rxhostdesc->data_phy),
++ rxhostdesc->data_offset,
++ le16_to_cpu(rxhostdesc->Ctl_16),
++ le16_to_cpu(rxhostdesc->length),
++ acx2cpu(rxhostdesc->desc_phy_next),
++ rxhostdesc->Status);
++ rxhostdesc++;
++ }
++
++ /* dump acx111 internal tx descriptor ring buffer */
++ txdesc = adev->txdesc_start;
++
++ /* loop over complete transmit pool */
++ if (txdesc) for (i = 0; i < TX_CNT; i++) {
++ printk("\ndump internal txdesc %d:\n"
++ "size 0x%X\n"
++ "mem pos %p\n"
++ "next 0x%X\n"
++ "acx mem pointer (dynamic) 0x%X\n"
++ "host mem pointer (dynamic) 0x%X\n"
++ "length (dynamic) 0x%X\n"
++ "CTL (dynamic) 0x%X\n"
++ "CTL2 (dynamic) 0x%X\n"
++ "Status (dynamic) 0x%X\n"
++ "Rate (dynamic) 0x%X\n",
++ i,
++ (int) sizeof(struct txdesc),
++ txdesc,
++ acx2cpu(txdesc->pNextDesc),
++ acx2cpu(txdesc->AcxMemPtr),
++ acx2cpu(txdesc->HostMemPtr),
++ le16_to_cpu(txdesc->total_length),
++ txdesc->Ctl_8,
++ txdesc->Ctl2_8, txdesc->error,
++ txdesc->u.r1.rate);
++ txdesc = advance_txdesc(adev, txdesc, 1);
++ }
++
++ /* dump host tx descriptor ring buffer */
++
++ txhostdesc = adev->txhostdesc_start;
++
++ /* loop over complete host send pool */
++ if (txhostdesc) for (i = 0; i < TX_CNT * 2; i++) {
++ printk("\ndump host txdesc %d:\n"
++ "mem pos %p\n"
++ "buffer mem pos 0x%X\n"
++ "buffer mem offset 0x%X\n"
++ "CTL 0x%X\n"
++ "Length 0x%X\n"
++ "next 0x%X\n"
++ "Status 0x%X\n",
++ i,
++ txhostdesc,
++ acx2cpu(txhostdesc->data_phy),
++ txhostdesc->data_offset,
++ le16_to_cpu(txhostdesc->Ctl_16),
++ le16_to_cpu(txhostdesc->length),
++ acx2cpu(txhostdesc->desc_phy_next),
++ le32_to_cpu(txhostdesc->Status));
++ txhostdesc++;
++ }
++
++ /* write_reg16(adev, 0xb4, 0x4); */
++
++ acx_unlock(adev, flags);
++end_ok:
++
++ acx_sem_unlock(adev);
++#endif /* ACX_DEBUG */
++ return OK;
++}
++
++
++/***********************************************************************
++*/
++int
++acx100mem_ioctl_set_phy_amp_bias(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ unsigned long flags;
++ u16 gpio_old;
++
++ if (!IS_ACX100(adev)) {
++ /* WARNING!!!
++ * Removing this check *might* damage
++ * hardware, since we're tweaking GPIOs here after all!!!
++ * You've been warned...
++ * WARNING!!! */
++ printk("acx: sorry, setting bias level for non-acx100 "
++ "is not supported yet\n");
++ return OK;
++ }
++
++ if (*extra > 7) {
++ printk("acx: invalid bias parameter, range is 0-7\n");
++ return -EINVAL;
++ }
++
++ acx_sem_lock(adev);
++
++ /* Need to lock accesses to [IO_ACX_GPIO_OUT]:
++ * IRQ handler uses it to update LED */
++ acx_lock(adev, flags);
++ gpio_old = read_reg16(adev, IO_ACX_GPIO_OUT);
++ write_reg16(adev, IO_ACX_GPIO_OUT, (gpio_old & 0xf8ff) | ((u16)*extra << 8));
++ acx_unlock(adev, flags);
++
++ log(L_DEBUG, "gpio_old: 0x%04X\n", gpio_old);
++ printk("%s: PHY power amplifier bias: old:%d, new:%d\n",
++ ndev->name,
++ (gpio_old & 0x0700) >> 8, (unsigned char)*extra);
++
++ acx_sem_unlock(adev);
++
++ return OK;
++}
++
++/***************************************************************
++** acxmem_l_alloc_tx
++** Actually returns a txdesc_t* ptr
++**
++** FIXME: in case of fragments, should allocate multiple descrs
++** after figuring out how many we need and whether we still have
++** sufficiently many.
++*/
++tx_t*
++acxmem_l_alloc_tx(acx_device_t *adev)
++{
++ struct txdesc *txdesc;
++ unsigned head;
++ u8 ctl8;
++ static int txattempts = 0;
++
++ FN_ENTER;
++
++ if (unlikely(!adev->tx_free)) {
++ printk("acx: BUG: no free txdesc left\n");
++ /*
++ * Probably the ACX ignored a transmit attempt and now there's a packet
++ * sitting in the queue we think should be transmitting but the ACX doesn't
++ * know about.
++ * On the first pass, send the ACX a TxProc interrupt to try moving
++ * things along, and if that doesn't work (ie, we get called again) completely
++ * flush the transmit queue.
++ */
++ if (txattempts < 10) {
++ txattempts++;
++ printk ("acx: trying to wake up ACX\n");
++ write_reg16(adev, IO_ACX_INT_TRIG, INT_TRIG_TXPRC);
++ write_flush(adev); }
++ else {
++ txattempts = 0;
++ printk ("acx: flushing transmit queue.\n");
++ acxmem_l_clean_txdesc_emergency (adev);
++ }
++ txdesc = NULL;
++ goto end;
++ }
++
++ /*
++ * Make a quick check to see if there is transmit buffer space on
++ * the ACX. This can't guarantee there is enough space for the packet
++ * since we don't yet know how big it is, but it will prevent at least some
++ * annoyances.
++ */
++ if (!adev->acx_txbuf_blocks_free) {
++ txdesc = NULL;
++ goto end;
++ }
++
++ head = adev->tx_head;
++ /*
++ * txdesc points to ACX memory
++ */
++ txdesc = get_txdesc(adev, head);
++ ctl8 = read_slavemem8 (adev, (u32) &(txdesc->Ctl_8));
++
++ /*
++ * If we don't own the buffer (HOSTOWN) it is certainly not free; however,
++ * we may have previously thought we had enough memory to send
++ * a packet, allocated the buffer then gave up when we found not enough
++ * transmit buffer space on the ACX. In that case, HOSTOWN and
++ * ACXDONE will both be set.
++ */
++ if (unlikely(DESC_CTL_HOSTOWN != (ctl8 & DESC_CTL_HOSTOWN))) {
++ /* whoops, descr at current index is not free, so probably
++ * ring buffer already full */
++ printk("acx: BUG: tx_head:%d Ctl8:0x%02X - failed to find "
++ "free txdesc\n", head, ctl8);
++ txdesc = NULL;
++ goto end;
++ }
++
++ /* Needed in case txdesc won't be eventually submitted for tx */
++ write_slavemem8 (adev, (u32) &(txdesc->Ctl_8), DESC_CTL_ACXDONE_HOSTOWN);
++
++ adev->tx_free--;
++ log(L_BUFT, "tx: got desc %u, %u remain\n",
++ head, adev->tx_free);
++ /* Keep a few free descs between head and tail of tx ring.
++ ** It is not absolutely needed, just feels safer */
++ if (adev->tx_free < TX_STOP_QUEUE) {
++ log(L_BUF, "stop queue (%u tx desc left)\n",
++ adev->tx_free);
++ acx_stop_queue(adev->ndev, NULL);
++ }
++
++ /* returning current descriptor, so advance to next free one */
++ adev->tx_head = (head + 1) % TX_CNT;
++end:
++ FN_EXIT0;
++
++ return (tx_t*)txdesc;
++}
++
++
++/***************************************************************
++** acxmem_l_dealloc_tx
++** Clears out a previously allocatedvoid acxmem_l_dealloc_tx(tx_t *tx_opaque);
++ transmit descriptor. The ACX
++** can get confused if we skip transmit descriptors in the queue,
++** so when we don't need a descriptor return it to its original
++** state and move the queue head pointer back.
++**
++*/
++void
++acxmem_l_dealloc_tx(acx_device_t *adev, tx_t *tx_opaque)
++{
++ /*
++ * txdesc is the address of the descriptor on the ACX.
++ */
++ txdesc_t *txdesc = (txdesc_t*)tx_opaque;
++ txdesc_t tmptxdesc;
++ int index;
++
++ memset (&tmptxdesc, 0, sizeof(tmptxdesc));
++ tmptxdesc.Ctl_8 = DESC_CTL_HOSTOWN | DESC_CTL_FIRSTFRAG;
++ tmptxdesc.u.r1.rate = 0x0a;
++
++ /*
++ * Clear out all of the transmit descriptor except for the next pointer
++ */
++ copy_to_slavemem (adev, (u32) &(txdesc->HostMemPtr),
++ (u8 *) &(tmptxdesc.HostMemPtr),
++ sizeof (tmptxdesc) - sizeof(tmptxdesc.pNextDesc));
++
++ /*
++ * This is only called immediately after we've allocated, so we should
++ * be able to set the head back to this descriptor.
++ */
++ index = ((u8*) txdesc - (u8*)adev->txdesc_start) / adev->txdesc_size;
++ printk ("acx_dealloc: moving head from %d to %d\n", adev->tx_head, index);
++ adev->tx_head = index;
++}
++
++
++/***********************************************************************
++*/
++void*
++acxmem_l_get_txbuf(acx_device_t *adev, tx_t* tx_opaque)
++{
++ return get_txhostdesc(adev, (txdesc_t*)tx_opaque)->data;
++}
++
++
++/***********************************************************************
++** acxmem_l_tx_data
++**
++** Can be called from IRQ (rx -> (AP bridging or mgmt response) -> tx).
++** Can be called from acx_i_start_xmit (data frames from net core).
++**
++** FIXME: in case of fragments, should loop over the number of
++** pre-allocated tx descrs, properly setting up transfer data and
++** CTL_xxx flags according to fragment number.
++*/
++void
++acxmem_update_queue_indicator (acx_device_t *adev, int txqueue)
++{
++#ifdef USING_MORE_THAN_ONE_TRANSMIT_QUEUE
++ u32 indicator;
++ unsigned long flags;
++ int count;
++
++ /*
++ * Can't handle an interrupt while we're fiddling with the ACX's lock,
++ * according to TI. The ACX is supposed to hold fw_lock for at most
++ * 500ns.
++ */
++ local_irq_save (flags);
++
++ /*
++ * Wait for ACX to release the lock (at most 500ns).
++ */
++ count = 0;
++ while (read_slavemem16 (adev, (u32) &(adev->acx_queue_indicator->fw_lock))
++ && (count++ < 50)) {
++ ndelay (10);
++ }
++ if (count < 50) {
++
++ /*
++ * Take out the host lock - anything non-zero will work, so don't worry about
++ * be/le
++ */
++ write_slavemem16 (adev, (u32) &(adev->acx_queue_indicator->host_lock), 1);
++
++ /*
++ * Avoid a race condition
++ */
++ count = 0;
++ while (read_slavemem16 (adev, (u32) &(adev->acx_queue_indicator->fw_lock))
++ && (count++ < 50)) {
++ ndelay (10);
++ }
++
++ if (count < 50) {
++ /*
++ * Mark the queue active
++ */
++ indicator = read_slavemem32 (adev, (u32) &(adev->acx_queue_indicator->indicator));
++ indicator |= cpu_to_le32 (1 << txqueue);
++ write_slavemem32 (adev, (u32) &(adev->acx_queue_indicator->indicator), indicator);
++ }
++
++ /*
++ * Release the host lock
++ */
++ write_slavemem16 (adev, (u32) &(adev->acx_queue_indicator->host_lock), 0);
++
++ }
++
++ /*
++ * Restore interrupts
++ */
++ local_irq_restore (flags);
++#endif
++}
++
++void
++acxmem_l_tx_data(acx_device_t *adev, tx_t* tx_opaque, int len)
++{
++ /*
++ * txdesc is the address on the ACX
++ */
++ txdesc_t *txdesc = (txdesc_t*)tx_opaque;
++ txhostdesc_t *hostdesc1, *hostdesc2;
++ client_t *clt;
++ u16 rate_cur;
++ u8 Ctl_8, Ctl2_8;
++ u32 addr;
++
++ FN_ENTER;
++ /* fw doesn't tx such packets anyhow */
++ if (unlikely(len < WLAN_HDR_A3_LEN))
++ goto end;
++
++ hostdesc1 = get_txhostdesc(adev, txdesc);
++ /* modify flag status in separate variable to be able to write it back
++ * in one big swoop later (also in order to have less device memory
++ * accesses) */
++ Ctl_8 = read_slavemem8 (adev, (u32) &(txdesc->Ctl_8));
++ Ctl2_8 = 0; /* really need to init it to 0, not txdesc->Ctl2_8, it seems */
++
++ hostdesc2 = hostdesc1 + 1;
++
++ /* DON'T simply set Ctl field to 0 here globally,
++ * it needs to maintain a consistent flag status (those are state flags!!),
++ * otherwise it may lead to severe disruption. Only set or reset particular
++ * flags at the exact moment this is needed... */
++
++ /* let chip do RTS/CTS handshaking before sending
++ * in case packet size exceeds threshold */
++ if (len > adev->rts_threshold)
++ SET_BIT(Ctl2_8, DESC_CTL2_RTS);
++ else
++ CLEAR_BIT(Ctl2_8, DESC_CTL2_RTS);
++
++ switch (adev->mode) {
++ case ACX_MODE_0_ADHOC:
++ case ACX_MODE_3_AP:
++ clt = acx_l_sta_list_get(adev, ((wlan_hdr_t*)hostdesc1->data)->a1);
++ break;
++ case ACX_MODE_2_STA:
++ clt = adev->ap_client;
++ break;
++#if 0
++/* testing was done on acx111: */
++ case ACX_MODE_MONITOR:
++ SET_BIT(Ctl2_8, 0
++/* sends CTS to self before packet */
++ + DESC_CTL2_SEQ /* don't increase sequence field */
++/* not working (looks like good fcs is still added) */
++ + DESC_CTL2_FCS /* don't add the FCS */
++/* not tested */
++ + DESC_CTL2_MORE_FRAG
++/* not tested */
++ + DESC_CTL2_RETRY /* don't increase retry field */
++/* not tested */
++ + DESC_CTL2_POWER /* don't increase power mgmt. field */
++/* no effect */
++ + DESC_CTL2_WEP /* encrypt this frame */
++/* not tested */
++ + DESC_CTL2_DUR /* don't increase duration field */
++ );
++ /* fallthrough */
++#endif
++ default: /* ACX_MODE_OFF, ACX_MODE_MONITOR */
++ clt = NULL;
++ break;
++ }
++
++ rate_cur = clt ? clt->rate_cur : adev->rate_bcast;
++ if (unlikely(!rate_cur)) {
++ printk("acx: driver bug! bad ratemask\n");
++ goto end;
++ }
++
++ /* used in tx cleanup routine for auto rate and accounting: */
++ put_txcr(adev, txdesc, clt, rate_cur);
++
++ write_slavemem16 (adev, (u32) &(txdesc->total_length), cpu_to_le16(len));
++ hostdesc2->length = cpu_to_le16(len - WLAN_HDR_A3_LEN);
++ if (IS_ACX111(adev)) {
++ /* note that if !txdesc->do_auto, txrate->cur
++ ** has only one nonzero bit */
++ txdesc->u.r2.rate111 = cpu_to_le16(
++ rate_cur
++ /* WARNING: I was never able to make it work with prism54 AP.
++ ** It was falling down to 1Mbit where shortpre is not applicable,
++ ** and not working at all at "5,11 basic rates only" setting.
++ ** I even didn't see tx packets in radio packet capture.
++ ** Disabled for now --vda */
++ /*| ((clt->shortpre && clt->cur!=RATE111_1) ? RATE111_SHORTPRE : 0) */
++ );
++#ifdef TODO_FIGURE_OUT_WHEN_TO_SET_THIS
++ /* should add this to rate111 above as necessary */
++ | (clt->pbcc511 ? RATE111_PBCC511 : 0)
++#endif
++ hostdesc1->length = cpu_to_le16(len);
++ } else { /* ACX100 */
++ u8 rate_100 = clt ? clt->rate_100 : adev->rate_bcast100;
++ write_slavemem8 (adev, (u32) &(txdesc->u.r1.rate), rate_100);
++#ifdef TODO_FIGURE_OUT_WHEN_TO_SET_THIS
++ if (clt->pbcc511) {
++ if (n == RATE100_5 || n == RATE100_11)
++ n |= RATE100_PBCC511;
++ }
++
++ if (clt->shortpre && (clt->cur != RATE111_1))
++ SET_BIT(Ctl_8, DESC_CTL_SHORT_PREAMBLE); /* set Short Preamble */
++#endif
++ /* set autodma and reclaim and 1st mpdu */
++ SET_BIT(Ctl_8, DESC_CTL_FIRSTFRAG);
++
++#if ACX_FRAGMENTATION
++ /* SET_BIT(Ctl2_8, DESC_CTL2_MORE_FRAG); cannot set it unconditionally, needs to be set for all non-last fragments */
++#endif
++ hostdesc1->length = cpu_to_le16(WLAN_HDR_A3_LEN);
++
++ /*
++ * Since we're not using autodma copy the packet data to the acx now.
++ * Even host descriptors point to the packet header, and the odd indexed
++ * descriptor following points to the packet data.
++ *
++ * The first step is to find free memory in the ACX transmit buffers.
++ * They don't necessarily map one to one with the transmit queue entries,
++ * so search through them starting just after the last one used.
++ */
++ addr = allocate_acx_txbuf_space (adev, len);
++ if (addr) {
++ chaincopy_to_slavemem (adev, addr, hostdesc1->data, len);
++ }
++ else {
++ /*
++ * Bummer. We thought we might have enough room in the transmit
++ * buffers to send this packet, but it turns out we don't. alloc_tx
++ * has already marked this transmit descriptor as HOSTOWN and ACXDONE,
++ * which means the ACX will hang when it gets to this descriptor unless
++ * we do something about it. Having a bubble in the transmit queue just
++ * doesn't seem to work, so we have to reset this transmit queue entry's
++ * state to its original value and back up our head pointer to point
++ * back to this entry.
++ */
++ hostdesc1->length = 0;
++ hostdesc2->length = 0;
++ write_slavemem16 (adev, (u32) &(txdesc->total_length), 0);
++ write_slavemem8 (adev, (u32) &(txdesc->Ctl_8), DESC_CTL_HOSTOWN | DESC_CTL_FIRSTFRAG);
++ adev->tx_head = ((u8*) txdesc - (u8*) adev->txdesc_start) / adev->txdesc_size;
++ goto end;
++ }
++ /*
++ * Tell the ACX where the packet is.
++ */
++ write_slavemem32 (adev, (u32) &(txdesc->AcxMemPtr), addr);
++
++ }
++ /* don't need to clean ack/rts statistics here, already
++ * done on descr cleanup */
++
++ /* clears HOSTOWN and ACXDONE bits, thus telling that the descriptors
++ * are now owned by the acx100; do this as LAST operation */
++ CLEAR_BIT(Ctl_8, DESC_CTL_ACXDONE_HOSTOWN);
++ /* flush writes before we release hostdesc to the adapter here */
++ //wmb();
++
++ /* write back modified flags */
++ /*
++ * At this point Ctl_8 should just be FIRSTFRAG
++ */
++ write_slavemem8 (adev, (u32) &(txdesc->Ctl2_8),Ctl2_8);
++ write_slavemem8 (adev, (u32) &(txdesc->Ctl_8), Ctl_8);
++ /* unused: txdesc->tx_time = cpu_to_le32(jiffies); */
++
++ /*
++ * Update the queue indicator to say there's data on the first queue.
++ */
++ acxmem_update_queue_indicator (adev, 0);
++
++ /* flush writes before we tell the adapter that it's its turn now */
++ mmiowb();
++ write_reg16(adev, IO_ACX_INT_TRIG, INT_TRIG_TXPRC);
++ write_flush(adev);
++
++ /* log the packet content AFTER sending it,
++ * in order to not delay sending any further than absolutely needed
++ * Do separate logs for acx100/111 to have human-readable rates */
++ if (unlikely(acx_debug & (L_XFER|L_DATA))) {
++ u16 fc = ((wlan_hdr_t*)hostdesc1->data)->fc;
++ if (IS_ACX111(adev))
++ printk("tx: pkt (%s): len %d "
++ "rate %04X%s status %u\n",
++ acx_get_packet_type_string(le16_to_cpu(fc)), len,
++ le16_to_cpu(txdesc->u.r2.rate111),
++ (le16_to_cpu(txdesc->u.r2.rate111) & RATE111_SHORTPRE) ? "(SPr)" : "",
++ adev->status);
++ else
++ printk("tx: pkt (%s): len %d rate %03u%s status %u\n",
++ acx_get_packet_type_string(fc), len,
++ read_slavemem8 (adev, (u32) &(txdesc->u.r1.rate)),
++ (Ctl_8 & DESC_CTL_SHORT_PREAMBLE) ? "(SPr)" : "",
++ adev->status);
++
++ if (acx_debug & L_DATA) {
++ printk("tx: 802.11 [%d]: ", len);
++ acx_dump_bytes(hostdesc1->data, len);
++ }
++ }
++end:
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acxmem_l_clean_txdesc
++**
++** This function resets the txdescs' status when the ACX100
++** signals the TX done IRQ (txdescs have been processed), starting with
++** the pool index of the descriptor which we would use next,
++** in order to make sure that we can be as fast as possible
++** in filling new txdescs.
++** Everytime we get called we know where the next packet to be cleaned is.
++*/
++
++#if !ACX_DEBUG
++static inline void log_txbuffer(const acx_device_t *adev) {}
++#else
++static void
++log_txbuffer(acx_device_t *adev)
++{
++ txdesc_t *txdesc;
++ int i;
++ u8 Ctl_8;
++
++ /* no FN_ENTER here, we don't want that */
++ /* no locks here, since it's entirely non-critical code */
++ txdesc = adev->txdesc_start;
++ if (unlikely(!txdesc)) return;
++ printk("tx: desc->Ctl8's:");
++ for (i = 0; i < TX_CNT; i++) {
++ Ctl_8 = read_slavemem8 (adev, (u32) &(txdesc->Ctl_8));
++ printk(" %02X", Ctl_8);
++ txdesc = advance_txdesc(adev, txdesc, 1);
++ }
++ printk("\n");
++}
++#endif
++
++
++static void
++handle_tx_error(acx_device_t *adev, u8 error, unsigned int finger)
++{
++ const char *err = "unknown error";
++
++ /* hmm, should we handle this as a mask
++ * of *several* bits?
++ * For now I think only caring about
++ * individual bits is ok... */
++ switch (error) {
++ case 0x01:
++ err = "no Tx due to error in other fragment";
++ adev->wstats.discard.fragment++;
++ break;
++ case 0x02:
++ err = "Tx aborted";
++ adev->stats.tx_aborted_errors++;
++ break;
++ case 0x04:
++ err = "Tx desc wrong parameters";
++ adev->wstats.discard.misc++;
++ break;
++ case 0x08:
++ err = "WEP key not found";
++ adev->wstats.discard.misc++;
++ break;
++ case 0x10:
++ err = "MSDU lifetime timeout? - try changing "
++ "'iwconfig retry lifetime XXX'";
++ adev->wstats.discard.misc++;
++ break;
++ case 0x20:
++ err = "excessive Tx retries due to either distance "
++ "too high or unable to Tx or Tx frame error - "
++ "try changing 'iwconfig txpower XXX' or "
++ "'sens'itivity or 'retry'";
++ adev->wstats.discard.retries++;
++ /* Tx error 0x20 also seems to occur on
++ * overheating, so I'm not sure whether we
++ * actually want to do aggressive radio recalibration,
++ * since people maybe won't notice then that their hardware
++ * is slowly getting cooked...
++ * Or is it still a safe long distance from utter
++ * radio non-functionality despite many radio recalibs
++ * to final destructive overheating of the hardware?
++ * In this case we really should do recalib here...
++ * I guess the only way to find out is to do a
++ * potentially fatal self-experiment :-\
++ * Or maybe only recalib in case we're using Tx
++ * rate auto (on errors switching to lower speed
++ * --> less heat?) or 802.11 power save mode?
++ *
++ * ok, just do it. */
++ if (++adev->retry_errors_msg_ratelimit % 4 == 0) {
++ if (adev->retry_errors_msg_ratelimit <= 20) {
++ printk("%s: several excessive Tx "
++ "retry errors occurred, attempting "
++ "to recalibrate radio. Radio "
++ "drift might be caused by increasing "
++ "card temperature, please check the card "
++ "before it's too late!\n",
++ adev->ndev->name);
++ if (adev->retry_errors_msg_ratelimit == 20)
++ printk("disabling above message\n");
++ }
++
++ acx_schedule_task(adev, ACX_AFTER_IRQ_CMD_RADIO_RECALIB);
++ }
++ break;
++ case 0x40:
++ err = "Tx buffer overflow";
++ adev->stats.tx_fifo_errors++;
++ break;
++ case 0x80:
++ err = "DMA error";
++ adev->wstats.discard.misc++;
++ break;
++ }
++ adev->stats.tx_errors++;
++ if (adev->stats.tx_errors <= 20)
++ printk("%s: tx error 0x%02X, buf %02u! (%s)\n",
++ adev->ndev->name, error, finger, err);
++ else
++ printk("%s: tx error 0x%02X, buf %02u!\n",
++ adev->ndev->name, error, finger);
++}
++
++
++unsigned int
++acxmem_l_clean_txdesc(acx_device_t *adev)
++{
++ txdesc_t *txdesc;
++ unsigned finger;
++ int num_cleaned;
++ u16 r111;
++ u8 error, ack_failures, rts_failures, rts_ok, r100, Ctl_8;
++ u32 acxmem;
++ txdesc_t tmptxdesc;
++
++ FN_ENTER;
++
++ /*
++ * Set up a template descriptor for re-initialization. The only
++ * things that get set are Ctl_8 and the rate, and the rate defaults
++ * to 1Mbps.
++ */
++ memset (&tmptxdesc, 0, sizeof (tmptxdesc));
++ tmptxdesc.Ctl_8 = DESC_CTL_HOSTOWN | DESC_CTL_FIRSTFRAG;
++ tmptxdesc.u.r1.rate = 0x0a;
++
++ if (unlikely(acx_debug & L_DEBUG))
++ log_txbuffer(adev);
++
++ log(L_BUFT, "tx: cleaning up bufs from %u\n", adev->tx_tail);
++
++ /* We know first descr which is not free yet. We advance it as far
++ ** as we see correct bits set in following descs (if next desc
++ ** is NOT free, we shouldn't advance at all). We know that in
++ ** front of tx_tail may be "holes" with isolated free descs.
++ ** We will catch up when all intermediate descs will be freed also */
++
++ finger = adev->tx_tail;
++ num_cleaned = 0;
++ while (likely(finger != adev->tx_head)) {
++ txdesc = get_txdesc(adev, finger);
++
++ /* If we allocated txdesc on tx path but then decided
++ ** to NOT use it, then it will be left as a free "bubble"
++ ** in the "allocated for tx" part of the ring.
++ ** We may meet it on the next ring pass here. */
++
++ /* stop if not marked as "tx finished" and "host owned" */
++ Ctl_8 = read_slavemem8 (adev, (u32) &(txdesc->Ctl_8));
++ if ((Ctl_8 & DESC_CTL_ACXDONE_HOSTOWN)
++ != DESC_CTL_ACXDONE_HOSTOWN) {
++ if (unlikely(!num_cleaned)) { /* maybe remove completely */
++ log(L_BUFT, "clean_txdesc: tail isn't free. "
++ "tail:%d head:%d\n",
++ adev->tx_tail, adev->tx_head);
++ }
++ break;
++ }
++
++ /* remember desc values... */
++ error = read_slavemem8 (adev, (u32) &(txdesc->error));
++ ack_failures = read_slavemem8 (adev, (u32) &(txdesc->ack_failures));
++ rts_failures = read_slavemem8 (adev, (u32) &(txdesc->u.rts.rts_failures));
++ rts_ok = read_slavemem8 (adev, (u32) &(txdesc->u.rts.rts_ok));
++ r100 = read_slavemem8 (adev, (u32) &(txdesc->u.r1.rate));
++ r111 = le16_to_cpu(read_slavemem16 (adev, (u32) &(txdesc->u.r2.rate111)));
++
++ /* need to check for certain error conditions before we
++ * clean the descriptor: we still need valid descr data here */
++ if (unlikely(0x30 & error)) {
++ /* only send IWEVTXDROP in case of retry or lifetime exceeded;
++ * all other errors mean we screwed up locally */
++ union iwreq_data wrqu;
++ wlan_hdr_t *hdr;
++ txhostdesc_t *hostdesc;
++
++ hostdesc = get_txhostdesc(adev, txdesc);
++ hdr = (wlan_hdr_t *)hostdesc->data;
++ MAC_COPY(wrqu.addr.sa_data, hdr->a1);
++ wireless_send_event(adev->ndev, IWEVTXDROP, &wrqu, NULL);
++ }
++
++ /*
++ * Free up the transmit data buffers
++ */
++ acxmem = read_slavemem32 (adev, (u32) &(txdesc->AcxMemPtr));
++ if (acxmem) {
++ reclaim_acx_txbuf_space (adev, acxmem);
++ }
++
++ /* ...and free the desc by clearing all the fields
++ except the next pointer */
++ copy_to_slavemem (adev,
++ (u32) &(txdesc->HostMemPtr),
++ (u8 *) &(tmptxdesc.HostMemPtr),
++ sizeof (tmptxdesc) - sizeof(tmptxdesc.pNextDesc)
++ );
++
++ adev->tx_free++;
++ num_cleaned++;
++
++ if ((adev->tx_free >= TX_START_QUEUE)
++ && (adev->status == ACX_STATUS_4_ASSOCIATED)
++ && (acx_queue_stopped(adev->ndev))
++ ) {
++ log(L_BUF, "tx: wake queue (avail. Tx desc %u)\n",
++ adev->tx_free);
++ acx_wake_queue(adev->ndev, NULL);
++ }
++
++ /* do error checking, rate handling and logging
++ * AFTER having done the work, it's faster */
++
++ /* do rate handling */
++ if (adev->rate_auto) {
++ struct client *clt = get_txc(adev, txdesc);
++ if (clt) {
++ u16 cur = get_txr(adev, txdesc);
++ if (clt->rate_cur == cur) {
++ acx_l_handle_txrate_auto(adev, clt,
++ cur, /* intended rate */
++ r100, r111, /* actually used rate */
++ (error & 0x30), /* was there an error? */
++ TX_CNT + TX_CLEAN_BACKLOG - adev->tx_free);
++ }
++ }
++ }
++
++ if (unlikely(error))
++ handle_tx_error(adev, error, finger);
++
++ if (IS_ACX111(adev))
++ log(L_BUFT, "tx: cleaned %u: !ACK=%u !RTS=%u RTS=%u r111=%04X\n",
++ finger, ack_failures, rts_failures, rts_ok, r111);
++ else
++ log(L_BUFT, "tx: cleaned %u: !ACK=%u !RTS=%u RTS=%u rate=%u\n",
++ finger, ack_failures, rts_failures, rts_ok, r100);
++
++ /* update pointer for descr to be cleaned next */
++ finger = (finger + 1) % TX_CNT;
++ }
++
++ /* remember last position */
++ adev->tx_tail = finger;
++/* end: */
++ FN_EXIT1(num_cleaned);
++ return num_cleaned;
++}
++
++/* clean *all* Tx descriptors, and regardless of their previous state.
++ * Used for brute-force reset handling. */
++void
++acxmem_l_clean_txdesc_emergency(acx_device_t *adev)
++{
++ txdesc_t *txdesc;
++ int i;
++ u32 acxmem;
++
++ FN_ENTER;
++
++ for (i = 0; i < TX_CNT; i++) {
++ txdesc = get_txdesc(adev, i);
++
++ /* free it */
++ write_slavemem8 (adev, (u32) &(txdesc->ack_failures), 0);
++ write_slavemem8 (adev, (u32) &(txdesc->u.rts.rts_failures), 0);
++ write_slavemem8 (adev, (u32) &(txdesc->u.rts.rts_ok), 0);
++ write_slavemem8 (adev, (u32) &(txdesc->error), 0);
++ write_slavemem8 (adev, (u32) &(txdesc->Ctl_8), DESC_CTL_HOSTOWN);
++
++ /*
++ * Clean up the memory allocated on the ACX for this transmit descriptor.
++ */
++ acxmem = read_slavemem32 (adev, (u32) &(txdesc->AcxMemPtr));
++ if (acxmem) {
++ reclaim_acx_txbuf_space (adev, acxmem);
++ }
++
++ write_slavemem32 (adev, (u32) &(txdesc->AcxMemPtr), 0);
++ }
++
++ adev->tx_free = TX_CNT;
++
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acxmem_s_create_tx_host_desc_queue
++*/
++
++static void*
++allocate(acx_device_t *adev, size_t size, dma_addr_t *phy, const char *msg)
++{
++ void *ptr;
++ ptr = kmalloc (size, GFP_KERNEL);
++ /*
++ * The ACX can't use the physical address, so we'll have to fake it
++ * later and it might be handy to have the virtual address.
++ */
++ *phy = (dma_addr_t) NULL;
++
++ if (ptr) {
++ log(L_DEBUG, "%s sz=%d adr=0x%p phy=0x%08llx\n",
++ msg, (int)size, ptr, (unsigned long long)*phy);
++ memset(ptr, 0, size);
++ return ptr;
++ }
++ printk(KERN_ERR "acx: %s allocation FAILED (%d bytes)\n",
++ msg, (int)size);
++ return NULL;
++}
++
++
++/*
++ * In the generic slave memory access mode, most of the stuff in
++ * the txhostdesc_t is unused. It's only here because the rest of
++ * the ACX driver expects it to be since the PCI version uses indirect
++ * host memory organization with DMA. Since we're not using DMA the
++ * only use we have for the host descriptors is to store the packets
++ * on the way out.
++ */
++static int
++acxmem_s_create_tx_host_desc_queue(acx_device_t *adev)
++{
++ txhostdesc_t *hostdesc;
++ u8 *txbuf;
++ int i;
++
++ FN_ENTER;
++
++ /* allocate TX buffer */
++ adev->txbuf_area_size = TX_CNT * WLAN_A4FR_MAXLEN_WEP_FCS;
++
++ adev->txbuf_start = allocate(adev, adev->txbuf_area_size,
++ &adev->txbuf_startphy, "txbuf_start");
++ if (!adev->txbuf_start)
++ goto fail;
++
++ /* allocate the TX host descriptor queue pool */
++ adev->txhostdesc_area_size = TX_CNT * 2*sizeof(*hostdesc);
++
++ adev->txhostdesc_start = allocate(adev, adev->txhostdesc_area_size,
++ &adev->txhostdesc_startphy, "txhostdesc_start");
++ if (!adev->txhostdesc_start)
++ goto fail;
++
++ /* check for proper alignment of TX host descriptor pool */
++ if ((long) adev->txhostdesc_start & 3) {
++ printk("acx: driver bug: dma alloc returns unaligned address\n");
++ goto fail;
++ }
++
++ hostdesc = adev->txhostdesc_start;
++ txbuf = adev->txbuf_start;
++
++#if 0
++/* Each tx buffer is accessed by hardware via
++** txdesc -> txhostdesc(s) -> txbuffer(s).
++** We use only one txhostdesc per txdesc, but it looks like
++** acx111 is buggy: it accesses second txhostdesc
++** (via hostdesc.desc_phy_next field) even if
++** txdesc->length == hostdesc->length and thus
++** entire packet was placed into first txhostdesc.
++** Due to this bug acx111 hangs unless second txhostdesc
++** has le16_to_cpu(hostdesc.length) = 3 (or larger)
++** Storing NULL into hostdesc.desc_phy_next
++** doesn't seem to help.
++**
++** Update: although it worked on Xterasys XN-2522g
++** with len=3 trick, WG311v2 is even more bogus, doesn't work.
++** Keeping this code (#ifdef'ed out) for documentational purposes.
++*/
++ for (i = 0; i < TX_CNT*2; i++) {
++ hostdesc_phy += sizeof(*hostdesc);
++ if (!(i & 1)) {
++ hostdesc->data_phy = cpu2acx(txbuf_phy);
++ /* hostdesc->data_offset = ... */
++ /* hostdesc->reserved = ... */
++ hostdesc->Ctl_16 = cpu_to_le16(DESC_CTL_HOSTOWN);
++ /* hostdesc->length = ... */
++ hostdesc->desc_phy_next = cpu2acx(hostdesc_phy);
++ hostdesc->pNext = ptr2acx(NULL);
++ /* hostdesc->Status = ... */
++ /* below: non-hardware fields */
++ hostdesc->data = txbuf;
++
++ txbuf += WLAN_A4FR_MAXLEN_WEP_FCS;
++ txbuf_phy += WLAN_A4FR_MAXLEN_WEP_FCS;
++ } else {
++ /* hostdesc->data_phy = ... */
++ /* hostdesc->data_offset = ... */
++ /* hostdesc->reserved = ... */
++ /* hostdesc->Ctl_16 = ... */
++ hostdesc->length = cpu_to_le16(3); /* bug workaround */
++ /* hostdesc->desc_phy_next = ... */
++ /* hostdesc->pNext = ... */
++ /* hostdesc->Status = ... */
++ /* below: non-hardware fields */
++ /* hostdesc->data = ... */
++ }
++ hostdesc++;
++ }
++#endif
++/* We initialize two hostdescs so that they point to adjacent
++** memory areas. Thus txbuf is really just a contiguous memory area */
++ for (i = 0; i < TX_CNT*2; i++) {
++ /* ->data is a non-hardware field: */
++ hostdesc->data = txbuf;
++
++ if (!(i & 1)) {
++ txbuf += WLAN_HDR_A3_LEN;
++ } else {
++ txbuf += WLAN_A4FR_MAXLEN_WEP_FCS - WLAN_HDR_A3_LEN;
++ }
++ hostdesc++;
++ }
++ hostdesc--;
++
++ FN_EXIT1(OK);
++ return OK;
++fail:
++ printk("acx: create_tx_host_desc_queue FAILED\n");
++ /* dealloc will be done by free function on error case */
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++}
++
++
++/***************************************************************
++** acxmem_s_create_rx_host_desc_queue
++*/
++/* the whole size of a data buffer (header plus data body)
++ * plus 32 bytes safety offset at the end */
++#define RX_BUFFER_SIZE (sizeof(rxbuffer_t) + 32)
++
++static int
++acxmem_s_create_rx_host_desc_queue(acx_device_t *adev)
++{
++ rxhostdesc_t *hostdesc;
++ rxbuffer_t *rxbuf;
++ int i;
++
++ FN_ENTER;
++
++ /* allocate the RX host descriptor queue pool */
++ adev->rxhostdesc_area_size = RX_CNT * sizeof(*hostdesc);
++
++ adev->rxhostdesc_start = allocate(adev, adev->rxhostdesc_area_size,
++ &adev->rxhostdesc_startphy, "rxhostdesc_start");
++ if (!adev->rxhostdesc_start)
++ goto fail;
++
++ /* check for proper alignment of RX host descriptor pool */
++ if ((long) adev->rxhostdesc_start & 3) {
++ printk("acx: driver bug: dma alloc returns unaligned address\n");
++ goto fail;
++ }
++
++ /* allocate Rx buffer pool which will be used by the acx
++ * to store the whole content of the received frames in it */
++ adev->rxbuf_area_size = RX_CNT * RX_BUFFER_SIZE;
++
++ adev->rxbuf_start = allocate(adev, adev->rxbuf_area_size,
++ &adev->rxbuf_startphy, "rxbuf_start");
++ if (!adev->rxbuf_start)
++ goto fail;
++
++ rxbuf = adev->rxbuf_start;
++ hostdesc = adev->rxhostdesc_start;
++
++ /* don't make any popular C programming pointer arithmetic mistakes
++ * here, otherwise I'll kill you...
++ * (and don't dare asking me why I'm warning you about that...) */
++ for (i = 0; i < RX_CNT; i++) {
++ hostdesc->data = rxbuf;
++ hostdesc->length = cpu_to_le16(RX_BUFFER_SIZE);
++ rxbuf++;
++ hostdesc++;
++ }
++ hostdesc--;
++ FN_EXIT1(OK);
++ return OK;
++fail:
++ printk("acx: create_rx_host_desc_queue FAILED\n");
++ /* dealloc will be done by free function on error case */
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++}
++
++
++/***************************************************************
++** acxmem_s_create_hostdesc_queues
++*/
++int
++acxmem_s_create_hostdesc_queues(acx_device_t *adev)
++{
++ int result;
++ result = acxmem_s_create_tx_host_desc_queue(adev);
++ if (OK != result) return result;
++ result = acxmem_s_create_rx_host_desc_queue(adev);
++ return result;
++}
++
++
++/***************************************************************
++** acxmem_create_tx_desc_queue
++*/
++static void
++acxmem_create_tx_desc_queue(acx_device_t *adev, u32 tx_queue_start)
++{
++ txdesc_t *txdesc;
++ u32 clr;
++ int i;
++
++ FN_ENTER;
++
++ if (IS_ACX100(adev))
++ adev->txdesc_size = sizeof(*txdesc);
++ else
++ /* the acx111 txdesc is 4 bytes larger */
++ adev->txdesc_size = sizeof(*txdesc) + 4;
++
++ /*
++ * This refers to an ACX address, not one of ours
++ */
++ adev->txdesc_start = (txdesc_t *) tx_queue_start;
++
++ log(L_DEBUG, "adev->txdesc_start=%p\n",
++ adev->txdesc_start);
++
++ adev->tx_free = TX_CNT;
++ /* done by memset: adev->tx_head = 0; */
++ /* done by memset: adev->tx_tail = 0; */
++ txdesc = adev->txdesc_start;
++
++ if (IS_ACX111(adev)) {
++ /* ACX111 has a preinitialized Tx buffer! */
++ /* loop over whole send pool */
++ /* FIXME: do we have to do the hostmemptr stuff here?? */
++ for (i = 0; i < TX_CNT; i++) {
++ txdesc->Ctl_8 = DESC_CTL_HOSTOWN;
++ /* reserve two (hdr desc and payload desc) */
++ txdesc = advance_txdesc(adev, txdesc, 1);
++ }
++ } else {
++ /* ACX100 Tx buffer needs to be initialized by us */
++ /* clear whole send pool. sizeof is safe here (we are acx100) */
++
++ /*
++ * adev->txdesc_start refers to device memory, so we can't write
++ * directly to it.
++ */
++ clr = (u32) adev->txdesc_start;
++ while (clr < (u32) adev->txdesc_start + (TX_CNT * sizeof(*txdesc))) {
++ write_slavemem32 (adev, clr, 0);
++ clr += 4;
++ }
++
++ /* loop over whole send pool */
++ for (i = 0; i < TX_CNT; i++) {
++ log(L_DEBUG, "configure card tx descriptor: 0x%p, "
++ "size: 0x%X\n", txdesc, adev->txdesc_size);
++
++ /* initialise ctl */
++ /*
++ * No auto DMA here
++ */
++ write_slavemem8 (adev, (u32) &(txdesc->Ctl_8),
++ (u8) (DESC_CTL_HOSTOWN | DESC_CTL_FIRSTFRAG));
++ /* done by memset(0): txdesc->Ctl2_8 = 0; */
++
++ /* point to next txdesc */
++ write_slavemem32 (adev, (u32) &(txdesc->pNextDesc),
++ (u32) cpu_to_le32 ((u8 *) txdesc + adev->txdesc_size));
++
++ /* go to the next one */
++ /* ++ is safe here (we are acx100) */
++ txdesc++;
++ }
++ /* go back to the last one */
++ txdesc--;
++ /* and point to the first making it a ring buffer */
++ write_slavemem32 (adev, (u32) &(txdesc->pNextDesc),
++ (u32) cpu_to_le32 (tx_queue_start));
++ }
++ FN_EXIT0;
++}
++
++
++/***************************************************************
++** acxmem_create_rx_desc_queue
++*/
++static void
++acxmem_create_rx_desc_queue(acx_device_t *adev, u32 rx_queue_start)
++{
++ rxdesc_t *rxdesc;
++ u32 mem_offs;
++ int i;
++
++ FN_ENTER;
++
++ /* done by memset: adev->rx_tail = 0; */
++
++ /* ACX111 doesn't need any further config: preconfigures itself.
++ * Simply print ring buffer for debugging */
++ if (IS_ACX111(adev)) {
++ /* rxdesc_start already set here */
++
++ adev->rxdesc_start = (rxdesc_t *) rx_queue_start;
++
++ rxdesc = adev->rxdesc_start;
++ for (i = 0; i < RX_CNT; i++) {
++ log(L_DEBUG, "rx descriptor %d @ 0x%p\n", i, rxdesc);
++ rxdesc = adev->rxdesc_start = (rxdesc_t *)
++ acx2cpu(rxdesc->pNextDesc);
++ }
++ } else {
++ /* we didn't pre-calculate rxdesc_start in case of ACX100 */
++ /* rxdesc_start should be right AFTER Tx pool */
++ adev->rxdesc_start = (rxdesc_t *)
++ ((u8 *) adev->txdesc_start + (TX_CNT * sizeof(txdesc_t)));
++ /* NB: sizeof(txdesc_t) above is valid because we know
++ ** we are in if (acx100) block. Beware of cut-n-pasting elsewhere!
++ ** acx111's txdesc is larger! */
++
++ mem_offs = (u32) adev->rxdesc_start;
++ while (mem_offs < (u32) adev->rxdesc_start + (RX_CNT * sizeof (*rxdesc))) {
++ write_slavemem32 (adev, mem_offs, 0);
++ mem_offs += 4;
++ }
++
++ /* loop over whole receive pool */
++ rxdesc = adev->rxdesc_start;
++ for (i = 0; i < RX_CNT; i++) {
++ log(L_DEBUG, "rx descriptor @ 0x%p\n", rxdesc);
++ /* point to next rxdesc */
++ write_slavemem32 (adev, (u32) &(rxdesc->pNextDesc),
++ (u32) cpu_to_le32 ((u8 *) rxdesc + sizeof(*rxdesc)));
++ /* go to the next one */
++ rxdesc++;
++ }
++ /* go to the last one */
++ rxdesc--;
++
++ /* and point to the first making it a ring buffer */
++ write_slavemem32 (adev, (u32) &(rxdesc->pNextDesc),
++ (u32) cpu_to_le32 (rx_queue_start));
++ }
++ FN_EXIT0;
++}
++
++
++/***************************************************************
++** acxmem_create_desc_queues
++*/
++void
++acxmem_create_desc_queues(acx_device_t *adev, u32 tx_queue_start, u32 rx_queue_start)
++{
++ u32 *p;
++ int i;
++
++ acxmem_create_tx_desc_queue(adev, tx_queue_start);
++ acxmem_create_rx_desc_queue(adev, rx_queue_start);
++ p = (u32 *) adev->acx_queue_indicator;
++ for (i = 0; i < 4; i++) {
++ write_slavemem32 (adev, (u32) p, 0);
++ p++;
++ }
++}
++
++
++/***************************************************************
++** acxmem_s_proc_diag_output
++*/
++char*
++acxmem_s_proc_diag_output(char *p, acx_device_t *adev)
++{
++ const char *rtl, *thd, *ttl;
++ txdesc_t *txdesc;
++ u8 Ctl_8;
++ rxdesc_t *rxdesc;
++ int i;
++ u32 tmp;
++ txdesc_t txd;
++ u8 buf[0x200];
++ int j, k;
++
++ FN_ENTER;
++
++#if DUMP_MEM_DURING_DIAG > 0
++ dump_acxmem (adev, 0, 0x10000);
++ panic ("dump finished");
++#endif
++
++ p += sprintf(p, "** Rx buf **\n");
++ rxdesc = adev->rxdesc_start;
++ if (rxdesc) for (i = 0; i < RX_CNT; i++) {
++ rtl = (i == adev->rx_tail) ? " [tail]" : "";
++ Ctl_8 = read_slavemem8 (adev, (u32) &(rxdesc->Ctl_8));
++ if (Ctl_8 & DESC_CTL_HOSTOWN)
++ p += sprintf(p, "%02u (%02x) FULL%s\n", i, Ctl_8, rtl);
++ else
++ p += sprintf(p, "%02u (%02x) empty%s\n", i, Ctl_8, rtl);
++ rxdesc++;
++ }
++ p += sprintf(p, "** Tx buf (free %d, Linux netqueue %s) **\n", adev->tx_free,
++ acx_queue_stopped(adev->ndev) ? "STOPPED" : "running");
++
++ p += sprintf(p, "** Tx buf %d blocks total, %d available, free list head %04x\n",
++ adev->acx_txbuf_numblocks, adev->acx_txbuf_blocks_free, adev->acx_txbuf_free);
++ txdesc = adev->txdesc_start;
++ if (txdesc) {
++ for (i = 0; i < TX_CNT; i++) {
++ thd = (i == adev->tx_head) ? " [head]" : "";
++ ttl = (i == adev->tx_tail) ? " [tail]" : "";
++ copy_from_slavemem (adev, (u8 *) &txd, (u32) txdesc, sizeof (txd));
++ Ctl_8 = read_slavemem8 (adev, (u32) &(txdesc->Ctl_8));
++ if (Ctl_8 & DESC_CTL_ACXDONE)
++ p += sprintf(p, "%02u ready to free (%02X)%s%s", i, Ctl_8, thd, ttl);
++ else if (Ctl_8 & DESC_CTL_HOSTOWN)
++ p += sprintf(p, "%02u available (%02X)%s%s", i, Ctl_8, thd, ttl);
++ else
++ p += sprintf(p, "%02u busy (%02X)%s%s", i, Ctl_8, thd, ttl);
++ tmp = read_slavemem32 (adev, (u32) &(txdesc->AcxMemPtr));
++ if (tmp) {
++ p += sprintf (p, " %04x", tmp);
++ while ((tmp = read_slavemem32 (adev, (u32) tmp)) != 0x02000000) {
++ tmp <<= 5;
++ p += sprintf (p, " %04x", tmp);
++ }
++ }
++ p += sprintf (p, "\n");
++ p += sprintf (p, " %04x: %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %02x %02x %02x %02x\n"
++ "%02x %02x %02x %02x %04x\n",
++ (u32) txdesc,
++ txd.pNextDesc.v, txd.HostMemPtr.v, txd.AcxMemPtr.v, txd.tx_time,
++ txd.total_length, txd.Reserved,
++ txd.dummy[0], txd.dummy[1], txd.dummy[2], txd.dummy[3],
++ txd.Ctl_8, txd.Ctl2_8, txd.error, txd.ack_failures,
++ txd.u.rts.rts_failures, txd.u.rts.rts_ok, txd.u.r1.rate, txd.u.r1.queue_ctrl,
++ txd.queue_info
++ );
++ if (txd.AcxMemPtr.v) {
++ copy_from_slavemem (adev, buf, txd.AcxMemPtr.v, sizeof (buf));
++ for (j = 0; (j < txd.total_length) && (j<(sizeof(buf)-4)); j+=16) {
++ p += sprintf (p, " ");
++ for (k = 0; (k < 16) && (j+k < txd.total_length); k++) {
++ p += sprintf (p, " %02x", buf[j+k+4]);
++ }
++ p += sprintf (p, "\n");
++ }
++ }
++ txdesc = advance_txdesc(adev, txdesc, 1);
++ }
++ }
++
++ p += sprintf(p,
++ "\n"
++ "** Generic slave data **\n"
++ "irq_mask 0x%04x irq_status 0x%04x irq on acx 0x%04x\n"
++ "txbuf_start 0x%p, txbuf_area_size %u\n"
++ "txdesc_size %u, txdesc_start 0x%p\n"
++ "txhostdesc_start 0x%p, txhostdesc_area_size %u\n"
++ "txbuf start 0x%04x, txbuf size %d\n"
++ "rxdesc_start 0x%p\n"
++ "rxhostdesc_start 0x%p, rxhostdesc_area_size %u\n"
++ "rxbuf_start 0x%p, rxbuf_area_size %u\n",
++ adev->irq_mask, adev->irq_status, read_reg32(adev, IO_ACX_IRQ_STATUS_NON_DES),
++ adev->txbuf_start, adev->txbuf_area_size,
++ adev->txdesc_size, adev->txdesc_start,
++ adev->txhostdesc_start, adev->txhostdesc_area_size,
++ adev->acx_txbuf_start, adev->acx_txbuf_numblocks * adev->memblocksize,
++ adev->rxdesc_start,
++ adev->rxhostdesc_start, adev->rxhostdesc_area_size,
++ adev->rxbuf_start, adev->rxbuf_area_size);
++ FN_EXIT0;
++ return p;
++}
++
++
++/***********************************************************************
++*/
++int
++acxmem_proc_eeprom_output(char *buf, acx_device_t *adev)
++{
++ char *p = buf;
++ int i;
++
++ FN_ENTER;
++
++ for (i = 0; i < 0x400; i++) {
++ acxmem_read_eeprom_byte(adev, i, p++);
++ }
++
++ FN_EXIT1(p - buf);
++ return p - buf;
++}
++
++
++/***********************************************************************
++*/
++void
++acxmem_set_interrupt_mask(acx_device_t *adev)
++{
++ if (IS_ACX111(adev)) {
++ adev->irq_mask = (u16) ~(0
++ | HOST_INT_RX_DATA
++ | HOST_INT_TX_COMPLETE
++ /* | HOST_INT_TX_XFER */
++ /* | HOST_INT_RX_COMPLETE */
++ /* | HOST_INT_DTIM */
++ /* | HOST_INT_BEACON */
++ /* | HOST_INT_TIMER */
++ /* | HOST_INT_KEY_NOT_FOUND */
++ | HOST_INT_IV_ICV_FAILURE
++ | HOST_INT_CMD_COMPLETE
++ | HOST_INT_INFO
++ | HOST_INT_OVERFLOW
++ /* | HOST_INT_PROCESS_ERROR */
++ | HOST_INT_SCAN_COMPLETE
++ | HOST_INT_FCS_THRESHOLD
++ | HOST_INT_UNKNOWN
++ );
++ /* Or else acx100 won't signal cmd completion, right? */
++ adev->irq_mask_off = (u16)~( HOST_INT_CMD_COMPLETE ); /* 0xfdff */
++ } else {
++ adev->irq_mask = (u16) ~(0
++ | HOST_INT_RX_DATA
++ | HOST_INT_TX_COMPLETE
++ /* | HOST_INT_TX_XFER */
++ /* | HOST_INT_RX_COMPLETE */
++ /* | HOST_INT_DTIM */
++ /* | HOST_INT_BEACON */
++ /* | HOST_INT_TIMER */
++ /* | HOST_INT_KEY_NOT_FOUND */
++ /* | HOST_INT_IV_ICV_FAILURE */
++ | HOST_INT_CMD_COMPLETE
++ | HOST_INT_INFO
++ /* | HOST_INT_OVERFLOW */
++ /* | HOST_INT_PROCESS_ERROR */
++ | HOST_INT_SCAN_COMPLETE
++ /* | HOST_INT_FCS_THRESHOLD */
++ /* | HOST_INT_BEACON_MISSED */
++ );
++ adev->irq_mask_off = (u16)~( HOST_INT_UNKNOWN ); /* 0x7fff */
++ }
++}
++
++
++/***********************************************************************
++*/
++int
++acx100mem_s_set_tx_level(acx_device_t *adev, u8 level_dbm)
++{
++ struct acx111_ie_tx_level tx_level;
++
++ /* since it can be assumed that at least the Maxim radio has a
++ * maximum power output of 20dBm and since it also can be
++ * assumed that these values drive the DAC responsible for
++ * setting the linear Tx level, I'd guess that these values
++ * should be the corresponding linear values for a dBm value,
++ * in other words: calculate the values from that formula:
++ * Y [dBm] = 10 * log (X [mW])
++ * then scale the 0..63 value range onto the 1..100mW range (0..20 dBm)
++ * and you're done...
++ * Hopefully that's ok, but you never know if we're actually
++ * right... (especially since Windows XP doesn't seem to show
++ * actual Tx dBm values :-P) */
++
++ /* NOTE: on Maxim, value 30 IS 30mW, and value 10 IS 10mW - so the
++ * values are EXACTLY mW!!! Not sure about RFMD and others,
++ * though... */
++ static const u8 dbm2val_maxim[21] = {
++ 63, 63, 63, 62,
++ 61, 61, 60, 60,
++ 59, 58, 57, 55,
++ 53, 50, 47, 43,
++ 38, 31, 23, 13,
++ 0
++ };
++ static const u8 dbm2val_rfmd[21] = {
++ 0, 0, 0, 1,
++ 2, 2, 3, 3,
++ 4, 5, 6, 8,
++ 10, 13, 16, 20,
++ 25, 32, 41, 50,
++ 63
++ };
++ const u8 *table;
++
++ switch (adev->radio_type) {
++ case RADIO_MAXIM_0D:
++ table = &dbm2val_maxim[0];
++ break;
++ case RADIO_RFMD_11:
++ case RADIO_RALINK_15:
++ table = &dbm2val_rfmd[0];
++ break;
++ default:
++ printk("%s: unknown/unsupported radio type, "
++ "cannot modify tx power level yet!\n",
++ adev->ndev->name);
++ return NOT_OK;
++ }
++ /*
++ * The hx4700 EEPROM, at least, only supports 1 power setting. The configure
++ * routine matches the PA bias with the gain, so just use its default value.
++ * The values are: 0x2b for the gain and 0x03 for the PA bias. The firmware
++ * writes the gain level to the Tx gain control DAC and the PA bias to the Maxim
++ * radio's PA bias register. The firmware limits itself to 0 - 64 when writing to the
++ * gain control DAC.
++ *
++ * Physically between the ACX and the radio, higher Tx gain control DAC values result
++ * in less power output; 0 volts to the Maxim radio results in the highest output power
++ * level, which I'm assuming matches up with 0 in the Tx Gain DAC register.
++ *
++ * Although there is only the 1 power setting, one of the radio firmware functions adjusts
++ * the transmit power level up and down. That function is called by the ACX FIQ handler
++ * under certain conditions.
++ */
++ tx_level.level = 1;
++ //return acx_s_configure(adev, &tx_level, ACX1xx_IE_DOT11_TX_POWER_LEVEL);
++
++ printk("%s: changing radio power level to %u dBm (%u)\n",
++ adev->ndev->name, level_dbm, table[level_dbm]);
++ acxmem_s_write_phy_reg(adev, 0x11, table[level_dbm]);
++
++ return 0;
++}
++
++
++static struct platform_driver
++acxmem_drv_id = {
++ .driver = {
++ .name = "acx-mem",
++ },
++ .probe = acxmem_e_probe,
++ .remove = __devexit_p(acxmem_e_remove),
++#ifdef CONFIG_PM
++ .suspend = acxmem_e_suspend,
++ .resume = acxmem_e_resume
++#endif /* CONFIG_PM */
++};
++
++
++/***********************************************************************
++** acxmem_e_init_module
++**
++** Module initialization routine, called once at module load time
++*/
++int __init
++acxmem_e_init_module(void)
++{
++ int res;
++
++ FN_ENTER;
++
++#if (ACX_IO_WIDTH==32)
++ printk("acx: compiled to use 32bit I/O access. "
++ "I/O timing issues might occur, such as "
++ "non-working firmware upload. Report them\n");
++#else
++ printk("acx: compiled to use 16bit I/O access only "
++ "(compatibility mode)\n");
++#endif
++
++#ifdef __LITTLE_ENDIAN
++#define ENDIANNESS_STRING "running on a little-endian CPU\n"
++#else
++#define ENDIANNESS_STRING "running on a BIG-ENDIAN CPU\n"
++#endif
++ log(L_INIT,
++ ENDIANNESS_STRING
++ "PCI module " ACX_RELEASE " initialized, "
++ "waiting for cards to probe...\n"
++ );
++
++ res = platform_driver_register (&acxmem_drv_id);
++ FN_EXIT1(res);
++ return res;
++}
++
++
++/***********************************************************************
++** acxmem_e_cleanup_module
++**
++** Called at module unload time. This is our last chance to
++** clean up after ourselves.
++*/
++void __exit
++acxmem_e_cleanup_module(void)
++{
++ FN_ENTER;
++
++ printk ("cleanup_module\n");
++ platform_driver_unregister( &acxmem_drv_id );
++
++ FN_EXIT0;
++}
++
++void acxmem_e_release(struct device *dev) {
++}
++
++MODULE_AUTHOR( "Todd Blumer <todd@sdgsystems.com>" );
++MODULE_DESCRIPTION( "ACX Slave Memory Driver" );
++MODULE_LICENSE( "GPL" );
++
+Index: linux-2.6.23/drivers/net/wireless/acx/pci.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23/drivers/net/wireless/acx/pci.c 2008-01-20 21:13:40.000000000 +0000
+@@ -0,0 +1,4234 @@
++/***********************************************************************
++** Copyright (C) 2003 ACX100 Open Source Project
++**
++** The contents of this file are subject to the Mozilla Public
++** License Version 1.1 (the "License"); you may not use this file
++** except in compliance with the License. You may obtain a copy of
++** the License at http://www.mozilla.org/MPL/
++**
++** Software distributed under the License is distributed on an "AS
++** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
++** implied. See the License for the specific language governing
++** rights and limitations under the License.
++**
++** Alternatively, the contents of this file may be used under the
++** terms of the GNU Public License version 2 (the "GPL"), in which
++** case the provisions of the GPL are applicable instead of the
++** above. If you wish to allow the use of your version of this file
++** only under the terms of the GPL and not to allow others to use
++** your version of this file under the MPL, indicate your decision
++** by deleting the provisions above and replace them with the notice
++** and other provisions required by the GPL. If you do not delete
++** the provisions above, a recipient may use your version of this
++** file under either the MPL or the GPL.
++** ---------------------------------------------------------------------
++** Inquiries regarding the ACX100 Open Source Project can be
++** made directly to:
++**
++** acx100-users@lists.sf.net
++** http://acx100.sf.net
++** ---------------------------------------------------------------------
++*/
++#define ACX_PCI 1
++
++#include <linux/version.h>
++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18)
++#include <linux/config.h>
++#endif
++
++/* Linux 2.6.18+ uses <linux/utsrelease.h> */
++#ifndef UTS_RELEASE
++#include <linux/utsrelease.h>
++#endif
++
++#include <linux/compiler.h> /* required for Lx 2.6.8 ?? */
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/sched.h>
++#include <linux/types.h>
++#include <linux/skbuff.h>
++#include <linux/slab.h>
++#include <linux/if_arp.h>
++#include <linux/rtnetlink.h>
++#include <linux/wireless.h>
++#include <net/iw_handler.h>
++#include <linux/netdevice.h>
++#include <linux/ioport.h>
++#include <linux/pci.h>
++#include <linux/pm.h>
++#include <linux/vmalloc.h>
++#include <linux/dma-mapping.h>
++
++#include "acx.h"
++
++
++/***********************************************************************
++*/
++#define PCI_TYPE (PCI_USES_MEM | PCI_ADDR0 | PCI_NO_ACPI_WAKE)
++#define PCI_ACX100_REGION1 0x01
++#define PCI_ACX100_REGION1_SIZE 0x1000 /* Memory size - 4K bytes */
++#define PCI_ACX100_REGION2 0x02
++#define PCI_ACX100_REGION2_SIZE 0x10000 /* Memory size - 64K bytes */
++
++#define PCI_ACX111_REGION1 0x00
++#define PCI_ACX111_REGION1_SIZE 0x2000 /* Memory size - 8K bytes */
++#define PCI_ACX111_REGION2 0x01
++#define PCI_ACX111_REGION2_SIZE 0x20000 /* Memory size - 128K bytes */
++
++/* Texas Instruments Vendor ID */
++#define PCI_VENDOR_ID_TI 0x104c
++
++/* ACX100 22Mb/s WLAN controller */
++#define PCI_DEVICE_ID_TI_TNETW1100A 0x8400
++#define PCI_DEVICE_ID_TI_TNETW1100B 0x8401
++
++/* ACX111 54Mb/s WLAN controller */
++#define PCI_DEVICE_ID_TI_TNETW1130 0x9066
++
++/* PCI Class & Sub-Class code, Network-'Other controller' */
++#define PCI_CLASS_NETWORK_OTHERS 0x0280
++
++#define CARD_EEPROM_ID_SIZE 6
++
++#ifndef PCI_D0
++/* From include/linux/pci.h */
++#define PCI_D0 0
++#define PCI_D1 1
++#define PCI_D2 2
++#define PCI_D3hot 3
++#define PCI_D3cold 4
++#define PCI_UNKNOWN 5
++#define PCI_POWER_ERROR -1
++#endif
++
++
++/***********************************************************************
++*/
++static void acxpci_i_tx_timeout(struct net_device *ndev);
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
++static irqreturn_t acxpci_i_interrupt(int irq, void *dev_id);
++#else
++static irqreturn_t acxpci_i_interrupt(int irq, void *dev_id, struct pt_regs *regs);
++#endif
++static void acxpci_i_set_multicast_list(struct net_device *ndev);
++
++static int acxpci_e_open(struct net_device *ndev);
++static int acxpci_e_close(struct net_device *ndev);
++static void acxpci_s_up(struct net_device *ndev);
++static void acxpci_s_down(struct net_device *ndev);
++
++
++/***********************************************************************
++** Register access
++*/
++
++/* Pick one */
++/* #define INLINE_IO static */
++#define INLINE_IO static inline
++
++INLINE_IO u32
++read_reg32(acx_device_t *adev, unsigned int offset)
++{
++#if ACX_IO_WIDTH == 32
++ return readl((u8 *)adev->iobase + adev->io[offset]);
++#else
++ return readw((u8 *)adev->iobase + adev->io[offset])
++ + (readw((u8 *)adev->iobase + adev->io[offset] + 2) << 16);
++#endif
++}
++
++INLINE_IO u16
++read_reg16(acx_device_t *adev, unsigned int offset)
++{
++ return readw((u8 *)adev->iobase + adev->io[offset]);
++}
++
++INLINE_IO u8
++read_reg8(acx_device_t *adev, unsigned int offset)
++{
++ return readb((u8 *)adev->iobase + adev->io[offset]);
++}
++
++INLINE_IO void
++write_reg32(acx_device_t *adev, unsigned int offset, u32 val)
++{
++#if ACX_IO_WIDTH == 32
++ writel(val, (u8 *)adev->iobase + adev->io[offset]);
++#else
++ writew(val & 0xffff, (u8 *)adev->iobase + adev->io[offset]);
++ writew(val >> 16, (u8 *)adev->iobase + adev->io[offset] + 2);
++#endif
++}
++
++INLINE_IO void
++write_reg16(acx_device_t *adev, unsigned int offset, u16 val)
++{
++ writew(val, (u8 *)adev->iobase + adev->io[offset]);
++}
++
++INLINE_IO void
++write_reg8(acx_device_t *adev, unsigned int offset, u8 val)
++{
++ writeb(val, (u8 *)adev->iobase + adev->io[offset]);
++}
++
++/* Handle PCI posting properly:
++ * Make sure that writes reach the adapter in case they require to be executed
++ * *before* the next write, by reading a random (and safely accessible) register.
++ * This call has to be made if there is no read following (which would flush the data
++ * to the adapter), yet the written data has to reach the adapter immediately. */
++INLINE_IO void
++write_flush(acx_device_t *adev)
++{
++ /* readb(adev->iobase + adev->io[IO_ACX_INFO_MAILBOX_OFFS]); */
++ /* faster version (accesses the first register, IO_ACX_SOFT_RESET,
++ * which should also be safe): */
++ readb(adev->iobase);
++}
++
++INLINE_IO int
++adev_present(acx_device_t *adev)
++{
++ /* fast version (accesses the first register, IO_ACX_SOFT_RESET,
++ * which should be safe): */
++ return readl(adev->iobase) != 0xffffffff;
++}
++
++
++/***********************************************************************
++*/
++static inline txdesc_t*
++get_txdesc(acx_device_t *adev, int index)
++{
++ return (txdesc_t*) (((u8*)adev->txdesc_start) + index * adev->txdesc_size);
++}
++
++static inline txdesc_t*
++advance_txdesc(acx_device_t *adev, txdesc_t* txdesc, int inc)
++{
++ return (txdesc_t*) (((u8*)txdesc) + inc * adev->txdesc_size);
++}
++
++static txhostdesc_t*
++get_txhostdesc(acx_device_t *adev, txdesc_t* txdesc)
++{
++ int index = (u8*)txdesc - (u8*)adev->txdesc_start;
++ if (unlikely(ACX_DEBUG && (index % adev->txdesc_size))) {
++ printk("bad txdesc ptr %p\n", txdesc);
++ return NULL;
++ }
++ index /= adev->txdesc_size;
++ if (unlikely(ACX_DEBUG && (index >= TX_CNT))) {
++ printk("bad txdesc ptr %p\n", txdesc);
++ return NULL;
++ }
++ return &adev->txhostdesc_start[index*2];
++}
++
++static inline client_t*
++get_txc(acx_device_t *adev, txdesc_t* txdesc)
++{
++ int index = (u8*)txdesc - (u8*)adev->txdesc_start;
++ if (unlikely(ACX_DEBUG && (index % adev->txdesc_size))) {
++ printk("bad txdesc ptr %p\n", txdesc);
++ return NULL;
++ }
++ index /= adev->txdesc_size;
++ if (unlikely(ACX_DEBUG && (index >= TX_CNT))) {
++ printk("bad txdesc ptr %p\n", txdesc);
++ return NULL;
++ }
++ return adev->txc[index];
++}
++
++static inline u16
++get_txr(acx_device_t *adev, txdesc_t* txdesc)
++{
++ int index = (u8*)txdesc - (u8*)adev->txdesc_start;
++ index /= adev->txdesc_size;
++ return adev->txr[index];
++}
++
++static inline void
++put_txcr(acx_device_t *adev, txdesc_t* txdesc, client_t* c, u16 r111)
++{
++ int index = (u8*)txdesc - (u8*)adev->txdesc_start;
++ if (unlikely(ACX_DEBUG && (index % adev->txdesc_size))) {
++ printk("bad txdesc ptr %p\n", txdesc);
++ return;
++ }
++ index /= adev->txdesc_size;
++ if (unlikely(ACX_DEBUG && (index >= TX_CNT))) {
++ printk("bad txdesc ptr %p\n", txdesc);
++ return;
++ }
++ adev->txc[index] = c;
++ adev->txr[index] = r111;
++}
++
++
++/***********************************************************************
++** EEPROM and PHY read/write helpers
++*/
++/***********************************************************************
++** acxpci_read_eeprom_byte
++**
++** Function called to read an octet in the EEPROM.
++**
++** This function is used by acxpci_e_probe to check if the
++** connected card is a legal one or not.
++**
++** Arguments:
++** adev ptr to acx_device structure
++** addr address to read in the EEPROM
++** charbuf ptr to a char. This is where the read octet
++** will be stored
++*/
++int
++acxpci_read_eeprom_byte(acx_device_t *adev, u32 addr, u8 *charbuf)
++{
++ int result;
++ int count;
++
++ write_reg32(adev, IO_ACX_EEPROM_CFG, 0);
++ write_reg32(adev, IO_ACX_EEPROM_ADDR, addr);
++ write_flush(adev);
++ write_reg32(adev, IO_ACX_EEPROM_CTL, 2);
++
++ count = 0xffff;
++ while (read_reg16(adev, IO_ACX_EEPROM_CTL)) {
++ /* scheduling away instead of CPU burning loop
++ * doesn't seem to work here at all:
++ * awful delay, sometimes also failure.
++ * Doesn't matter anyway (only small delay). */
++ if (unlikely(!--count)) {
++ printk("%s: timeout waiting for EEPROM read\n",
++ adev->ndev->name);
++ result = NOT_OK;
++ goto fail;
++ }
++ cpu_relax();
++ }
++
++ *charbuf = read_reg8(adev, IO_ACX_EEPROM_DATA);
++ log(L_DEBUG, "EEPROM at 0x%04X = 0x%02X\n", addr, *charbuf);
++ result = OK;
++
++fail:
++ return result;
++}
++
++
++/***********************************************************************
++** We don't lock hw accesses here since we never r/w eeprom in IRQ
++** Note: this function sleeps only because of GFP_KERNEL alloc
++*/
++#ifdef UNUSED
++int
++acxpci_s_write_eeprom(acx_device_t *adev, u32 addr, u32 len, const u8 *charbuf)
++{
++ u8 *data_verify = NULL;
++ unsigned long flags;
++ int count, i;
++ int result = NOT_OK;
++ u16 gpio_orig;
++
++ printk("acx: WARNING! I would write to EEPROM now. "
++ "Since I really DON'T want to unless you know "
++ "what you're doing (THIS CODE WILL PROBABLY "
++ "NOT WORK YET!), I will abort that now. And "
++ "definitely make sure to make a "
++ "/proc/driver/acx_wlan0_eeprom backup copy first!!! "
++ "(the EEPROM content includes the PCI config header!! "
++ "If you kill important stuff, then you WILL "
++ "get in trouble and people DID get in trouble already)\n");
++ return OK;
++
++ FN_ENTER;
++
++ data_verify = kmalloc(len, GFP_KERNEL);
++ if (!data_verify) {
++ goto end;
++ }
++
++ /* first we need to enable the OE (EEPROM Output Enable) GPIO line
++ * to be able to write to the EEPROM.
++ * NOTE: an EEPROM writing success has been reported,
++ * but you probably have to modify GPIO_OUT, too,
++ * and you probably need to activate a different GPIO
++ * line instead! */
++ gpio_orig = read_reg16(adev, IO_ACX_GPIO_OE);
++ write_reg16(adev, IO_ACX_GPIO_OE, gpio_orig & ~1);
++ write_flush(adev);
++
++ /* ok, now start writing the data out */
++ for (i = 0; i < len; i++) {
++ write_reg32(adev, IO_ACX_EEPROM_CFG, 0);
++ write_reg32(adev, IO_ACX_EEPROM_ADDR, addr + i);
++ write_reg32(adev, IO_ACX_EEPROM_DATA, *(charbuf + i));
++ write_flush(adev);
++ write_reg32(adev, IO_ACX_EEPROM_CTL, 1);
++
++ count = 0xffff;
++ while (read_reg16(adev, IO_ACX_EEPROM_CTL)) {
++ if (unlikely(!--count)) {
++ printk("WARNING, DANGER!!! "
++ "Timeout waiting for EEPROM write\n");
++ goto end;
++ }
++ cpu_relax();
++ }
++ }
++
++ /* disable EEPROM writing */
++ write_reg16(adev, IO_ACX_GPIO_OE, gpio_orig);
++ write_flush(adev);
++
++ /* now start a verification run */
++ for (i = 0; i < len; i++) {
++ write_reg32(adev, IO_ACX_EEPROM_CFG, 0);
++ write_reg32(adev, IO_ACX_EEPROM_ADDR, addr + i);
++ write_flush(adev);
++ write_reg32(adev, IO_ACX_EEPROM_CTL, 2);
++
++ count = 0xffff;
++ while (read_reg16(adev, IO_ACX_EEPROM_CTL)) {
++ if (unlikely(!--count)) {
++ printk("timeout waiting for EEPROM read\n");
++ goto end;
++ }
++ cpu_relax();
++ }
++
++ data_verify[i] = read_reg16(adev, IO_ACX_EEPROM_DATA);
++ }
++
++ if (0 == memcmp(charbuf, data_verify, len))
++ result = OK; /* read data matches, success */
++
++end:
++ kfree(data_verify);
++ FN_EXIT1(result);
++ return result;
++}
++#endif /* UNUSED */
++
++
++/***********************************************************************
++** acxpci_s_read_phy_reg
++**
++** Messing with rx/tx disabling and enabling here
++** (write_reg32(adev, IO_ACX_ENABLE, 0b000000xx)) kills traffic
++*/
++int
++acxpci_s_read_phy_reg(acx_device_t *adev, u32 reg, u8 *charbuf)
++{
++ int result = NOT_OK;
++ int count;
++
++ FN_ENTER;
++
++ write_reg32(adev, IO_ACX_PHY_ADDR, reg);
++ write_flush(adev);
++ write_reg32(adev, IO_ACX_PHY_CTL, 2);
++
++ count = 0xffff;
++ while (read_reg32(adev, IO_ACX_PHY_CTL)) {
++ /* scheduling away instead of CPU burning loop
++ * doesn't seem to work here at all:
++ * awful delay, sometimes also failure.
++ * Doesn't matter anyway (only small delay). */
++ if (unlikely(!--count)) {
++ printk("%s: timeout waiting for phy read\n",
++ adev->ndev->name);
++ *charbuf = 0;
++ goto fail;
++ }
++ cpu_relax();
++ }
++
++ log(L_DEBUG, "count was %u\n", count);
++ *charbuf = read_reg8(adev, IO_ACX_PHY_DATA);
++
++ log(L_DEBUG, "radio PHY at 0x%04X = 0x%02X\n", *charbuf, reg);
++ result = OK;
++ goto fail; /* silence compiler warning */
++fail:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++*/
++int
++acxpci_s_write_phy_reg(acx_device_t *adev, u32 reg, u8 value)
++{
++ FN_ENTER;
++
++ /* mprusko said that 32bit accesses result in distorted sensitivity
++ * on his card. Unconfirmed, looks like it's not true (most likely since we
++ * now properly flush writes). */
++ write_reg32(adev, IO_ACX_PHY_DATA, value);
++ write_reg32(adev, IO_ACX_PHY_ADDR, reg);
++ write_flush(adev);
++ write_reg32(adev, IO_ACX_PHY_CTL, 1);
++ write_flush(adev);
++ log(L_DEBUG, "radio PHY write 0x%02X at 0x%04X\n", value, reg);
++
++ FN_EXIT1(OK);
++ return OK;
++}
++
++
++#define NO_AUTO_INCREMENT 1
++
++/***********************************************************************
++** acxpci_s_write_fw
++**
++** Write the firmware image into the card.
++**
++** Arguments:
++** adev wlan device structure
++** fw_image firmware image.
++**
++** Returns:
++** 1 firmware image corrupted
++** 0 success
++*/
++static int
++acxpci_s_write_fw(acx_device_t *adev, const firmware_image_t *fw_image, u32 offset)
++{
++ int len, size;
++ u32 sum, v32;
++ /* we skip the first four bytes which contain the control sum */
++ const u8 *p = (u8*)fw_image + 4;
++
++ /* start the image checksum by adding the image size value */
++ sum = p[0]+p[1]+p[2]+p[3];
++ p += 4;
++
++ write_reg32(adev, IO_ACX_SLV_END_CTL, 0);
++
++#if NO_AUTO_INCREMENT
++ write_reg32(adev, IO_ACX_SLV_MEM_CTL, 0); /* use basic mode */
++#else
++ write_reg32(adev, IO_ACX_SLV_MEM_CTL, 1); /* use autoincrement mode */
++ write_reg32(adev, IO_ACX_SLV_MEM_ADDR, offset); /* configure start address */
++ write_flush(adev);
++#endif
++
++ len = 0;
++ size = le32_to_cpu(fw_image->size) & (~3);
++
++ while (likely(len < size)) {
++ v32 = be32_to_cpu(*(u32*)p);
++ sum += p[0]+p[1]+p[2]+p[3];
++ p += 4;
++ len += 4;
++
++#if NO_AUTO_INCREMENT
++ write_reg32(adev, IO_ACX_SLV_MEM_ADDR, offset + len - 4);
++ write_flush(adev);
++#endif
++ write_reg32(adev, IO_ACX_SLV_MEM_DATA, v32);
++ }
++
++ log(L_DEBUG, "firmware written, size:%d sum1:%x sum2:%x\n",
++ size, sum, le32_to_cpu(fw_image->chksum));
++
++ /* compare our checksum with the stored image checksum */
++ return (sum != le32_to_cpu(fw_image->chksum));
++}
++
++
++/***********************************************************************
++** acxpci_s_validate_fw
++**
++** Compare the firmware image given with
++** the firmware image written into the card.
++**
++** Arguments:
++** adev wlan device structure
++** fw_image firmware image.
++**
++** Returns:
++** NOT_OK firmware image corrupted or not correctly written
++** OK success
++*/
++static int
++acxpci_s_validate_fw(acx_device_t *adev, const firmware_image_t *fw_image,
++ u32 offset)
++{
++ u32 sum, v32, w32;
++ int len, size;
++ int result = OK;
++ /* we skip the first four bytes which contain the control sum */
++ const u8 *p = (u8*)fw_image + 4;
++
++ /* start the image checksum by adding the image size value */
++ sum = p[0]+p[1]+p[2]+p[3];
++ p += 4;
++
++ write_reg32(adev, IO_ACX_SLV_END_CTL, 0);
++
++#if NO_AUTO_INCREMENT
++ write_reg32(adev, IO_ACX_SLV_MEM_CTL, 0); /* use basic mode */
++#else
++ write_reg32(adev, IO_ACX_SLV_MEM_CTL, 1); /* use autoincrement mode */
++ write_reg32(adev, IO_ACX_SLV_MEM_ADDR, offset); /* configure start address */
++#endif
++
++ len = 0;
++ size = le32_to_cpu(fw_image->size) & (~3);
++
++ while (likely(len < size)) {
++ v32 = be32_to_cpu(*(u32*)p);
++ p += 4;
++ len += 4;
++
++#if NO_AUTO_INCREMENT
++ write_reg32(adev, IO_ACX_SLV_MEM_ADDR, offset + len - 4);
++#endif
++ w32 = read_reg32(adev, IO_ACX_SLV_MEM_DATA);
++
++ if (unlikely(w32 != v32)) {
++ printk("acx: FATAL: firmware upload: "
++ "data parts at offset %d don't match (0x%08X vs. 0x%08X)! "
++ "I/O timing issues or defective memory, with DWL-xx0+? "
++ "ACX_IO_WIDTH=16 may help. Please report\n",
++ len, v32, w32);
++ result = NOT_OK;
++ break;
++ }
++
++ sum += (u8)w32 + (u8)(w32>>8) + (u8)(w32>>16) + (u8)(w32>>24);
++ }
++
++ /* sum control verification */
++ if (result != NOT_OK) {
++ if (sum != le32_to_cpu(fw_image->chksum)) {
++ printk("acx: FATAL: firmware upload: "
++ "checksums don't match!\n");
++ result = NOT_OK;
++ }
++ }
++
++ return result;
++}
++
++
++/***********************************************************************
++** acxpci_s_upload_fw
++**
++** Called from acx_reset_dev
++*/
++static int
++acxpci_s_upload_fw(acx_device_t *adev)
++{
++ firmware_image_t *fw_image = NULL;
++ int res = NOT_OK;
++ int try;
++ u32 file_size;
++ char filename[sizeof("tiacx1NNcNN")];
++
++ FN_ENTER;
++
++ /* print exact chipset and radio ID to make sure people really get a clue on which files exactly they are supposed to provide,
++ * since firmware loading is the biggest enduser PITA with these chipsets.
++ * Not printing radio ID in 0xHEX in order to not confuse them into wrong file naming */
++ printk( "acx: need to load firmware for acx1%02d chipset with radio ID %02x, please provide via firmware hotplug:\n"
++ "acx: either one file only (<c>ombined firmware image file, radio-specific) or two files (radio-less base image file *plus* separate <r>adio-specific extension file)\n",
++ IS_ACX111(adev)*11, adev->radio_type);
++
++ /* Try combined, then main image */
++ adev->need_radio_fw = 0;
++ snprintf(filename, sizeof(filename), "tiacx1%02dc%02X",
++ IS_ACX111(adev)*11, adev->radio_type);
++
++ fw_image = acx_s_read_fw(&adev->pdev->dev, filename, &file_size);
++ if (!fw_image) {
++ adev->need_radio_fw = 1;
++ filename[sizeof("tiacx1NN")-1] = '\0';
++ fw_image = acx_s_read_fw(&adev->pdev->dev, filename, &file_size);
++ if (!fw_image) {
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++ }
++ }
++
++ for (try = 1; try <= 5; try++) {
++ res = acxpci_s_write_fw(adev, fw_image, 0);
++ log(L_DEBUG|L_INIT, "acx_write_fw (main/combined): %d\n", res);
++ if (OK == res) {
++ res = acxpci_s_validate_fw(adev, fw_image, 0);
++ log(L_DEBUG|L_INIT, "acx_validate_fw "
++ "(main/combined): %d\n", res);
++ }
++
++ if (OK == res) {
++ SET_BIT(adev->dev_state_mask, ACX_STATE_FW_LOADED);
++ break;
++ }
++ printk("acx: firmware upload attempt #%d FAILED, "
++ "retrying...\n", try);
++ acx_s_msleep(1000); /* better wait for a while... */
++ }
++
++ vfree(fw_image);
++
++ FN_EXIT1(res);
++ return res;
++}
++
++
++/***********************************************************************
++** acxpci_s_upload_radio
++**
++** Uploads the appropriate radio module firmware into the card.
++*/
++int
++acxpci_s_upload_radio(acx_device_t *adev)
++{
++ acx_ie_memmap_t mm;
++ firmware_image_t *radio_image;
++ acx_cmd_radioinit_t radioinit;
++ int res = NOT_OK;
++ int try;
++ u32 offset;
++ u32 size;
++ char filename[sizeof("tiacx1NNrNN")];
++
++ if (!adev->need_radio_fw) return OK;
++
++ FN_ENTER;
++
++ acx_s_interrogate(adev, &mm, ACX1xx_IE_MEMORY_MAP);
++ offset = le32_to_cpu(mm.CodeEnd);
++
++ snprintf(filename, sizeof(filename), "tiacx1%02dr%02X",
++ IS_ACX111(adev)*11,
++ adev->radio_type);
++ radio_image = acx_s_read_fw(&adev->pdev->dev, filename, &size);
++ if (!radio_image) {
++ printk("acx: can't load radio module '%s'\n", filename);
++ goto fail;
++ }
++
++ acx_s_issue_cmd(adev, ACX1xx_CMD_SLEEP, NULL, 0);
++
++ for (try = 1; try <= 5; try++) {
++ res = acxpci_s_write_fw(adev, radio_image, offset);
++ log(L_DEBUG|L_INIT, "acx_write_fw (radio): %d\n", res);
++ if (OK == res) {
++ res = acxpci_s_validate_fw(adev, radio_image, offset);
++ log(L_DEBUG|L_INIT, "acx_validate_fw (radio): %d\n", res);
++ }
++
++ if (OK == res)
++ break;
++ printk("acx: radio firmware upload attempt #%d FAILED, "
++ "retrying...\n", try);
++ acx_s_msleep(1000); /* better wait for a while... */
++ }
++
++ acx_s_issue_cmd(adev, ACX1xx_CMD_WAKE, NULL, 0);
++ radioinit.offset = cpu_to_le32(offset);
++ /* no endian conversion needed, remains in card CPU area: */
++ radioinit.len = radio_image->size;
++
++ vfree(radio_image);
++
++ if (OK != res)
++ goto fail;
++
++ /* will take a moment so let's have a big timeout */
++ acx_s_issue_cmd_timeo(adev, ACX1xx_CMD_RADIOINIT,
++ &radioinit, sizeof(radioinit), CMD_TIMEOUT_MS(1000));
++
++ res = acx_s_interrogate(adev, &mm, ACX1xx_IE_MEMORY_MAP);
++fail:
++ FN_EXIT1(res);
++ return res;
++}
++
++
++/***********************************************************************
++** acxpci_l_reset_mac
++**
++** MAC will be reset
++** Call context: reset_dev
++*/
++static void
++acxpci_l_reset_mac(acx_device_t *adev)
++{
++ u16 temp;
++
++ FN_ENTER;
++
++ /* halt eCPU */
++ temp = read_reg16(adev, IO_ACX_ECPU_CTRL) | 0x1;
++ write_reg16(adev, IO_ACX_ECPU_CTRL, temp);
++
++ /* now do soft reset of eCPU, set bit */
++ temp = read_reg16(adev, IO_ACX_SOFT_RESET) | 0x1;
++ log(L_DEBUG, "%s: enable soft reset...\n", __func__);
++ write_reg16(adev, IO_ACX_SOFT_RESET, temp);
++ write_flush(adev);
++
++ /* now clear bit again: deassert eCPU reset */
++ log(L_DEBUG, "%s: disable soft reset and go to init mode...\n", __func__);
++ write_reg16(adev, IO_ACX_SOFT_RESET, temp & ~0x1);
++
++ /* now start a burst read from initial EEPROM */
++ temp = read_reg16(adev, IO_ACX_EE_START) | 0x1;
++ write_reg16(adev, IO_ACX_EE_START, temp);
++ write_flush(adev);
++
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acxpci_s_verify_init
++*/
++static int
++acxpci_s_verify_init(acx_device_t *adev)
++{
++ int result = NOT_OK;
++ unsigned long timeout;
++
++ FN_ENTER;
++
++ timeout = jiffies + 2*HZ;
++ for (;;) {
++ u16 irqstat = read_reg16(adev, IO_ACX_IRQ_STATUS_NON_DES);
++ if (irqstat & HOST_INT_FCS_THRESHOLD) {
++ result = OK;
++ write_reg16(adev, IO_ACX_IRQ_ACK, HOST_INT_FCS_THRESHOLD);
++ break;
++ }
++ if (time_after(jiffies, timeout))
++ break;
++ /* Init may take up to ~0.5 sec total */
++ acx_s_msleep(50);
++ }
++
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** A few low-level helpers
++**
++** Note: these functions are not protected by lock
++** and thus are never allowed to be called from IRQ.
++** Also they must not race with fw upload which uses same hw regs
++*/
++
++/***********************************************************************
++** acxpci_write_cmd_type_status
++*/
++
++static inline void
++acxpci_write_cmd_type_status(acx_device_t *adev, u16 type, u16 status)
++{
++ writel(type | (status << 16), adev->cmd_area);
++ write_flush(adev);
++}
++
++
++/***********************************************************************
++** acxpci_read_cmd_type_status
++*/
++static u32
++acxpci_read_cmd_type_status(acx_device_t *adev)
++{
++ u32 cmd_type, cmd_status;
++
++ cmd_type = readl(adev->cmd_area);
++ cmd_status = (cmd_type >> 16);
++ cmd_type = (u16)cmd_type;
++
++ log(L_CTL, "cmd_type:%04X cmd_status:%04X [%s]\n",
++ cmd_type, cmd_status,
++ acx_cmd_status_str(cmd_status));
++
++ return cmd_status;
++}
++
++
++/***********************************************************************
++** acxpci_s_reset_dev
++**
++** Arguments:
++** netdevice that contains the adev variable
++** Returns:
++** NOT_OK on fail
++** OK on success
++** Side effects:
++** device is hard reset
++** Call context:
++** acxpci_e_probe
++** Comment:
++** This resets the device using low level hardware calls
++** as well as uploads and verifies the firmware to the card
++*/
++
++static inline void
++init_mboxes(acx_device_t *adev)
++{
++ u32 cmd_offs, info_offs;
++
++ cmd_offs = read_reg32(adev, IO_ACX_CMD_MAILBOX_OFFS);
++ info_offs = read_reg32(adev, IO_ACX_INFO_MAILBOX_OFFS);
++ adev->cmd_area = (u8 *)adev->iobase2 + cmd_offs;
++ adev->info_area = (u8 *)adev->iobase2 + info_offs;
++ log(L_DEBUG, "iobase2=%p\n"
++ "cmd_mbox_offset=%X cmd_area=%p\n"
++ "info_mbox_offset=%X info_area=%p\n",
++ adev->iobase2,
++ cmd_offs, adev->cmd_area,
++ info_offs, adev->info_area);
++}
++
++
++static inline void
++read_eeprom_area(acx_device_t *adev)
++{
++#if ACX_DEBUG > 1
++ int offs;
++ u8 tmp;
++
++ for (offs = 0x8c; offs < 0xb9; offs++)
++ acxpci_read_eeprom_byte(adev, offs, &tmp);
++#endif
++}
++
++
++static int
++acxpci_s_reset_dev(acx_device_t *adev)
++{
++ const char* msg = "";
++ unsigned long flags;
++ int result = NOT_OK;
++ u16 hardware_info;
++ u16 ecpu_ctrl;
++ int count;
++
++ FN_ENTER;
++
++ /* reset the device to make sure the eCPU is stopped
++ * to upload the firmware correctly */
++
++ acx_lock(adev, flags);
++
++ acxpci_l_reset_mac(adev);
++
++ ecpu_ctrl = read_reg16(adev, IO_ACX_ECPU_CTRL) & 1;
++ if (!ecpu_ctrl) {
++ msg = "eCPU is already running. ";
++ goto end_unlock;
++ }
++
++#ifdef WE_DONT_NEED_THAT_DO_WE
++ if (read_reg16(adev, IO_ACX_SOR_CFG) & 2) {
++ /* eCPU most likely means "embedded CPU" */
++ msg = "eCPU did not start after boot from flash. ";
++ goto end_unlock;
++ }
++
++ /* check sense on reset flags */
++ if (read_reg16(adev, IO_ACX_SOR_CFG) & 0x10) {
++ printk("%s: eCPU did not start after boot (SOR), "
++ "is this fatal?\n", adev->ndev->name);
++ }
++#endif
++ /* scan, if any, is stopped now, setting corresponding IRQ bit */
++ adev->irq_status |= HOST_INT_SCAN_COMPLETE;
++
++ acx_unlock(adev, flags);
++
++ /* need to know radio type before fw load */
++ /* Need to wait for arrival of this information in a loop,
++ * most probably since eCPU runs some init code from EEPROM
++ * (started burst read in reset_mac()) which also
++ * sets the radio type ID */
++
++ count = 0xffff;
++ do {
++ hardware_info = read_reg16(adev, IO_ACX_EEPROM_INFORMATION);
++ if (!--count) {
++ msg = "eCPU didn't indicate radio type";
++ goto end_fail;
++ }
++ cpu_relax();
++ } while (!(hardware_info & 0xff00)); /* radio type still zero? */
++
++ /* printk("DEBUG: count %d\n", count); */
++ adev->form_factor = hardware_info & 0xff;
++ adev->radio_type = hardware_info >> 8;
++
++ /* load the firmware */
++ if (OK != acxpci_s_upload_fw(adev))
++ goto end_fail;
++
++ /* acx_s_msleep(10); this one really shouldn't be required */
++
++ /* now start eCPU by clearing bit */
++ write_reg16(adev, IO_ACX_ECPU_CTRL, ecpu_ctrl & ~0x1);
++ log(L_DEBUG, "booted eCPU up and waiting for completion...\n");
++
++ /* wait for eCPU bootup */
++ if (OK != acxpci_s_verify_init(adev)) {
++ msg = "timeout waiting for eCPU. ";
++ goto end_fail;
++ }
++ log(L_DEBUG, "eCPU has woken up, card is ready to be configured\n");
++
++ init_mboxes(adev);
++ acxpci_write_cmd_type_status(adev, 0, 0);
++
++ /* test that EEPROM is readable */
++ read_eeprom_area(adev);
++
++ result = OK;
++ goto end;
++
++/* Finish error message. Indicate which function failed */
++end_unlock:
++ acx_unlock(adev, flags);
++end_fail:
++ printk("acx: %sreset_dev() FAILED\n", msg);
++end:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acxpci_s_issue_cmd_timeo
++**
++** Sends command to fw, extract result
++**
++** NB: we do _not_ take lock inside, so be sure to not touch anything
++** which may interfere with IRQ handler operation
++**
++** TODO: busy wait is a bit silly, so:
++** 1) stop doing many iters - go to sleep after first
++** 2) go to waitqueue based approach: wait, not poll!
++*/
++#undef FUNC
++#define FUNC "issue_cmd"
++
++#if !ACX_DEBUG
++int
++acxpci_s_issue_cmd_timeo(
++ acx_device_t *adev,
++ unsigned int cmd,
++ void *buffer,
++ unsigned buflen,
++ unsigned cmd_timeout)
++{
++#else
++int
++acxpci_s_issue_cmd_timeo_debug(
++ acx_device_t *adev,
++ unsigned cmd,
++ void *buffer,
++ unsigned buflen,
++ unsigned cmd_timeout,
++ const char* cmdstr)
++{
++ unsigned long start = jiffies;
++#endif
++ const char *devname;
++ unsigned counter;
++ u16 irqtype;
++ u16 cmd_status;
++ unsigned long timeout;
++
++ FN_ENTER;
++
++ devname = adev->ndev->name;
++ if (!devname || !devname[0] || devname[4]=='%')
++ devname = "acx";
++
++ log(L_CTL, FUNC"(cmd:%s,buflen:%u,timeout:%ums,type:0x%04X)\n",
++ cmdstr, buflen, cmd_timeout,
++ buffer ? le16_to_cpu(((acx_ie_generic_t *)buffer)->type) : -1);
++
++ if (!(adev->dev_state_mask & ACX_STATE_FW_LOADED)) {
++ printk("%s: "FUNC"(): firmware is not loaded yet, "
++ "cannot execute commands!\n", devname);
++ goto bad;
++ }
++
++ if ((acx_debug & L_DEBUG) && (cmd != ACX1xx_CMD_INTERROGATE)) {
++ printk("input buffer (len=%u):\n", buflen);
++ acx_dump_bytes(buffer, buflen);
++ }
++
++ /* wait for firmware to become idle for our command submission */
++ timeout = HZ/5;
++ counter = (timeout * 1000 / HZ) - 1; /* in ms */
++ timeout += jiffies;
++ do {
++ cmd_status = acxpci_read_cmd_type_status(adev);
++ /* Test for IDLE state */
++ if (!cmd_status)
++ break;
++ if (counter % 8 == 0) {
++ if (time_after(jiffies, timeout)) {
++ counter = 0;
++ break;
++ }
++ /* we waited 8 iterations, no luck. Sleep 8 ms */
++ acx_s_msleep(8);
++ }
++ } while (likely(--counter));
++
++ if (!counter) {
++ /* the card doesn't get idle, we're in trouble */
++ printk("%s: "FUNC"(): cmd_status is not IDLE: 0x%04X!=0\n",
++ devname, cmd_status);
++ goto bad;
++ } else if (counter < 190) { /* if waited >10ms... */
++ log(L_CTL|L_DEBUG, FUNC"(): waited for IDLE %dms. "
++ "Please report\n", 199 - counter);
++ }
++
++ /* now write the parameters of the command if needed */
++ if (buffer && buflen) {
++ /* if it's an INTERROGATE command, just pass the length
++ * of parameters to read, as data */
++#if CMD_DISCOVERY
++ if (cmd == ACX1xx_CMD_INTERROGATE)
++ memset_io(adev->cmd_area + 4, 0xAA, buflen);
++#endif
++ /* adev->cmd_area points to PCI device's memory, not to RAM! */
++ memcpy_toio(adev->cmd_area + 4, buffer,
++ (cmd == ACX1xx_CMD_INTERROGATE) ? 4 : buflen);
++ }
++ /* now write the actual command type */
++ acxpci_write_cmd_type_status(adev, cmd, 0);
++ /* execute command */
++ write_reg16(adev, IO_ACX_INT_TRIG, INT_TRIG_CMD);
++ write_flush(adev);
++
++ /* wait for firmware to process command */
++
++ /* Ensure nonzero and not too large timeout.
++ ** Also converts e.g. 100->99, 200->199
++ ** which is nice but not essential */
++ cmd_timeout = (cmd_timeout-1) | 1;
++ if (unlikely(cmd_timeout > 1199))
++ cmd_timeout = 1199;
++ /* clear CMD_COMPLETE bit. can be set only by IRQ handler: */
++ adev->irq_status &= ~HOST_INT_CMD_COMPLETE;
++
++ /* we schedule away sometimes (timeout can be large) */
++ counter = cmd_timeout;
++ timeout = jiffies + cmd_timeout * HZ / 1000;
++ do {
++ if (!adev->irqs_active) { /* IRQ disabled: poll */
++ irqtype = read_reg16(adev, IO_ACX_IRQ_STATUS_NON_DES);
++ if (irqtype & HOST_INT_CMD_COMPLETE) {
++ write_reg16(adev, IO_ACX_IRQ_ACK,
++ HOST_INT_CMD_COMPLETE);
++ break;
++ }
++ } else { /* Wait when IRQ will set the bit */
++ irqtype = adev->irq_status;
++ if (irqtype & HOST_INT_CMD_COMPLETE)
++ break;
++ }
++
++ if (counter % 8 == 0) {
++ if (time_after(jiffies, timeout)) {
++ counter = 0;
++ break;
++ }
++ /* we waited 8 iterations, no luck. Sleep 8 ms */
++ acx_s_msleep(8);
++ }
++ } while (likely(--counter));
++
++ /* save state for debugging */
++ cmd_status = acxpci_read_cmd_type_status(adev);
++
++ /* put the card in IDLE state */
++ acxpci_write_cmd_type_status(adev, 0, 0);
++
++ if (!counter) { /* timed out! */
++ printk("%s: "FUNC"(): timed out %s for CMD_COMPLETE. "
++ "irq bits:0x%04X irq_status:0x%04X timeout:%dms "
++ "cmd_status:%d (%s)\n",
++ devname, (adev->irqs_active) ? "waiting" : "polling",
++ irqtype, adev->irq_status, cmd_timeout,
++ cmd_status, acx_cmd_status_str(cmd_status));
++ goto bad;
++ } else if (cmd_timeout - counter > 30) { /* if waited >30ms... */
++ log(L_CTL|L_DEBUG, FUNC"(): %s for CMD_COMPLETE %dms. "
++ "count:%d. Please report\n",
++ (adev->irqs_active) ? "waited" : "polled",
++ cmd_timeout - counter, counter);
++ }
++
++ if (1 != cmd_status) { /* it is not a 'Success' */
++ printk("%s: "FUNC"(): cmd_status is not SUCCESS: %d (%s). "
++ "Took %dms of %d\n",
++ devname, cmd_status, acx_cmd_status_str(cmd_status),
++ cmd_timeout - counter, cmd_timeout);
++ /* zero out result buffer
++ * WARNING: this will trash stack in case of illegally large input
++ * length! */
++ if (buffer && buflen)
++ memset(buffer, 0, buflen);
++ goto bad;
++ }
++
++ /* read in result parameters if needed */
++ if (buffer && buflen && (cmd == ACX1xx_CMD_INTERROGATE)) {
++ /* adev->cmd_area points to PCI device's memory, not to RAM! */
++ memcpy_fromio(buffer, adev->cmd_area + 4, buflen);
++ if (acx_debug & L_DEBUG) {
++ printk("output buffer (len=%u): ", buflen);
++ acx_dump_bytes(buffer, buflen);
++ }
++ }
++/* ok: */
++ log(L_CTL, FUNC"(%s): took %ld jiffies to complete\n",
++ cmdstr, jiffies - start);
++ FN_EXIT1(OK);
++ return OK;
++
++bad:
++ /* Give enough info so that callers can avoid
++ ** printing their own diagnostic messages */
++#if ACX_DEBUG
++ printk("%s: "FUNC"(cmd:%s) FAILED\n", devname, cmdstr);
++#else
++ printk("%s: "FUNC"(cmd:0x%04X) FAILED\n", devname, cmd);
++#endif
++ dump_stack();
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++}
++
++
++/***********************************************************************
++*/
++#ifdef NONESSENTIAL_FEATURES
++typedef struct device_id {
++ unsigned char id[6];
++ char *descr;
++ char *type;
++} device_id_t;
++
++static const device_id_t
++device_ids[] =
++{
++ {
++ {'G', 'l', 'o', 'b', 'a', 'l'},
++ NULL,
++ NULL,
++ },
++ {
++ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
++ "uninitialized",
++ "SpeedStream SS1021 or Gigafast WF721-AEX"
++ },
++ {
++ {0x80, 0x81, 0x82, 0x83, 0x84, 0x85},
++ "non-standard",
++ "DrayTek Vigor 520"
++ },
++ {
++ {'?', '?', '?', '?', '?', '?'},
++ "non-standard",
++ "Level One WPC-0200"
++ },
++ {
++ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
++ "empty",
++ "DWL-650+ variant"
++ }
++};
++
++static void
++acx_show_card_eeprom_id(acx_device_t *adev)
++{
++ unsigned char buffer[CARD_EEPROM_ID_SIZE];
++ int i;
++
++ memset(&buffer, 0, CARD_EEPROM_ID_SIZE);
++ /* use direct EEPROM access */
++ for (i = 0; i < CARD_EEPROM_ID_SIZE; i++) {
++ if (OK != acxpci_read_eeprom_byte(adev,
++ ACX100_EEPROM_ID_OFFSET + i,
++ &buffer[i])) {
++ printk("acx: reading EEPROM FAILED\n");
++ break;
++ }
++ }
++
++ for (i = 0; i < VEC_SIZE(device_ids); i++) {
++ if (!memcmp(&buffer, device_ids[i].id, CARD_EEPROM_ID_SIZE)) {
++ if (device_ids[i].descr) {
++ printk("acx: EEPROM card ID string check "
++ "found %s card ID: is this %s?\n",
++ device_ids[i].descr, device_ids[i].type);
++ }
++ break;
++ }
++ }
++ if (i == VEC_SIZE(device_ids)) {
++ printk("acx: EEPROM card ID string check found "
++ "unknown card: expected 'Global', got '%.*s\'. "
++ "Please report\n", CARD_EEPROM_ID_SIZE, buffer);
++ }
++}
++#endif /* NONESSENTIAL_FEATURES */
++
++
++/***********************************************************************
++** acxpci_free_desc_queues
++**
++** Releases the queues that have been allocated, the
++** others have been initialised to NULL so this
++** function can be used if only part of the queues were allocated.
++*/
++
++static inline void
++free_coherent(struct pci_dev *hwdev, size_t size,
++ void *vaddr, dma_addr_t dma_handle)
++{
++ dma_free_coherent(hwdev == NULL ? NULL : &hwdev->dev,
++ size, vaddr, dma_handle);
++}
++
++void
++acxpci_free_desc_queues(acx_device_t *adev)
++{
++#define ACX_FREE_QUEUE(size, ptr, phyaddr) \
++ if (ptr) { \
++ free_coherent(0, size, ptr, phyaddr); \
++ ptr = NULL; \
++ size = 0; \
++ }
++
++ FN_ENTER;
++
++ ACX_FREE_QUEUE(adev->txhostdesc_area_size, adev->txhostdesc_start, adev->txhostdesc_startphy);
++ ACX_FREE_QUEUE(adev->txbuf_area_size, adev->txbuf_start, adev->txbuf_startphy);
++
++ adev->txdesc_start = NULL;
++
++ ACX_FREE_QUEUE(adev->rxhostdesc_area_size, adev->rxhostdesc_start, adev->rxhostdesc_startphy);
++ ACX_FREE_QUEUE(adev->rxbuf_area_size, adev->rxbuf_start, adev->rxbuf_startphy);
++
++ adev->rxdesc_start = NULL;
++
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acxpci_s_delete_dma_regions
++*/
++static void
++acxpci_s_delete_dma_regions(acx_device_t *adev)
++{
++ unsigned long flags;
++
++ FN_ENTER;
++ /* disable radio Tx/Rx. Shouldn't we use the firmware commands
++ * here instead? Or are we that much down the road that it's no
++ * longer possible here? */
++ write_reg16(adev, IO_ACX_ENABLE, 0);
++
++ acx_s_msleep(100);
++
++ acx_lock(adev, flags);
++ acxpci_free_desc_queues(adev);
++ acx_unlock(adev, flags);
++
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acxpci_e_probe
++**
++** Probe routine called when a PCI device w/ matching ID is found.
++** Here's the sequence:
++** - Allocate the PCI resources.
++** - Read the PCMCIA attribute memory to make sure we have a WLAN card
++** - Reset the MAC
++** - Initialize the dev and wlan data
++** - Initialize the MAC
++**
++** pdev - ptr to pci device structure containing info about pci configuration
++** id - ptr to the device id entry that matched this device
++*/
++static const u16
++IO_ACX100[] =
++{
++ 0x0000, /* IO_ACX_SOFT_RESET */
++
++ 0x0014, /* IO_ACX_SLV_MEM_ADDR */
++ 0x0018, /* IO_ACX_SLV_MEM_DATA */
++ 0x001c, /* IO_ACX_SLV_MEM_CTL */
++ 0x0020, /* IO_ACX_SLV_END_CTL */
++
++ 0x0034, /* IO_ACX_FEMR */
++
++ 0x007c, /* IO_ACX_INT_TRIG */
++ 0x0098, /* IO_ACX_IRQ_MASK */
++ 0x00a4, /* IO_ACX_IRQ_STATUS_NON_DES */
++ 0x00a8, /* IO_ACX_IRQ_STATUS_CLEAR */
++ 0x00ac, /* IO_ACX_IRQ_ACK */
++ 0x00b0, /* IO_ACX_HINT_TRIG */
++
++ 0x0104, /* IO_ACX_ENABLE */
++
++ 0x0250, /* IO_ACX_EEPROM_CTL */
++ 0x0254, /* IO_ACX_EEPROM_ADDR */
++ 0x0258, /* IO_ACX_EEPROM_DATA */
++ 0x025c, /* IO_ACX_EEPROM_CFG */
++
++ 0x0268, /* IO_ACX_PHY_ADDR */
++ 0x026c, /* IO_ACX_PHY_DATA */
++ 0x0270, /* IO_ACX_PHY_CTL */
++
++ 0x0290, /* IO_ACX_GPIO_OE */
++
++ 0x0298, /* IO_ACX_GPIO_OUT */
++
++ 0x02a4, /* IO_ACX_CMD_MAILBOX_OFFS */
++ 0x02a8, /* IO_ACX_INFO_MAILBOX_OFFS */
++ 0x02ac, /* IO_ACX_EEPROM_INFORMATION */
++
++ 0x02d0, /* IO_ACX_EE_START */
++ 0x02d4, /* IO_ACX_SOR_CFG */
++ 0x02d8 /* IO_ACX_ECPU_CTRL */
++};
++
++static const u16
++IO_ACX111[] =
++{
++ 0x0000, /* IO_ACX_SOFT_RESET */
++
++ 0x0014, /* IO_ACX_SLV_MEM_ADDR */
++ 0x0018, /* IO_ACX_SLV_MEM_DATA */
++ 0x001c, /* IO_ACX_SLV_MEM_CTL */
++ 0x0020, /* IO_ACX_SLV_END_CTL */
++
++ 0x0034, /* IO_ACX_FEMR */
++
++ 0x00b4, /* IO_ACX_INT_TRIG */
++ 0x00d4, /* IO_ACX_IRQ_MASK */
++ /* we do mean NON_DES (0xf0), not NON_DES_MASK which is at 0xe0: */
++ 0x00f0, /* IO_ACX_IRQ_STATUS_NON_DES */
++ 0x00e4, /* IO_ACX_IRQ_STATUS_CLEAR */
++ 0x00e8, /* IO_ACX_IRQ_ACK */
++ 0x00ec, /* IO_ACX_HINT_TRIG */
++
++ 0x01d0, /* IO_ACX_ENABLE */
++
++ 0x0338, /* IO_ACX_EEPROM_CTL */
++ 0x033c, /* IO_ACX_EEPROM_ADDR */
++ 0x0340, /* IO_ACX_EEPROM_DATA */
++ 0x0344, /* IO_ACX_EEPROM_CFG */
++
++ 0x0350, /* IO_ACX_PHY_ADDR */
++ 0x0354, /* IO_ACX_PHY_DATA */
++ 0x0358, /* IO_ACX_PHY_CTL */
++
++ 0x0374, /* IO_ACX_GPIO_OE */
++
++ 0x037c, /* IO_ACX_GPIO_OUT */
++
++ 0x0388, /* IO_ACX_CMD_MAILBOX_OFFS */
++ 0x038c, /* IO_ACX_INFO_MAILBOX_OFFS */
++ 0x0390, /* IO_ACX_EEPROM_INFORMATION */
++
++ 0x0100, /* IO_ACX_EE_START */
++ 0x0104, /* IO_ACX_SOR_CFG */
++ 0x0108, /* IO_ACX_ECPU_CTRL */
++};
++
++static void
++dummy_netdev_init(struct net_device *ndev) {}
++
++static int __devinit
++acxpci_e_probe(struct pci_dev *pdev, const struct pci_device_id *id)
++{
++ acx111_ie_configoption_t co;
++ unsigned long mem_region1 = 0;
++ unsigned long mem_region2 = 0;
++ unsigned long mem_region1_size;
++ unsigned long mem_region2_size;
++ unsigned long phymem1;
++ unsigned long phymem2;
++ void *mem1 = NULL;
++ void *mem2 = NULL;
++ acx_device_t *adev = NULL;
++ struct net_device *ndev = NULL;
++ const char *chip_name;
++ int result = -EIO;
++ int err;
++ u8 chip_type;
++
++ FN_ENTER;
++
++ /* Enable the PCI device */
++ if (pci_enable_device(pdev)) {
++ printk("acx: pci_enable_device() FAILED\n");
++ result = -ENODEV;
++ goto fail_pci_enable_device;
++ }
++
++ /* enable busmastering (required for CardBus) */
++ pci_set_master(pdev);
++
++ /* FIXME: prism54 calls pci_set_mwi() here,
++ * should we do/support the same? */
++
++ /* chiptype is u8 but id->driver_data is ulong
++ ** Works for now (possible values are 1 and 2) */
++ chip_type = (u8)id->driver_data;
++ /* acx100 and acx111 have different PCI memory regions */
++ if (chip_type == CHIPTYPE_ACX100) {
++ chip_name = "ACX100";
++ mem_region1 = PCI_ACX100_REGION1;
++ mem_region1_size = PCI_ACX100_REGION1_SIZE;
++
++ mem_region2 = PCI_ACX100_REGION2;
++ mem_region2_size = PCI_ACX100_REGION2_SIZE;
++ } else if (chip_type == CHIPTYPE_ACX111) {
++ chip_name = "ACX111";
++ mem_region1 = PCI_ACX111_REGION1;
++ mem_region1_size = PCI_ACX111_REGION1_SIZE;
++
++ mem_region2 = PCI_ACX111_REGION2;
++ mem_region2_size = PCI_ACX111_REGION2_SIZE;
++ } else {
++ printk("acx: unknown chip type 0x%04X\n", chip_type);
++ goto fail_unknown_chiptype;
++ }
++
++ /* Figure out our resources */
++ phymem1 = pci_resource_start(pdev, mem_region1);
++ phymem2 = pci_resource_start(pdev, mem_region2);
++ if (!request_mem_region(phymem1, pci_resource_len(pdev, mem_region1), "acx_1")) {
++ printk("acx: cannot reserve PCI memory region 1 (are you sure "
++ "you have CardBus support in kernel?)\n");
++ goto fail_request_mem_region1;
++ }
++ if (!request_mem_region(phymem2, pci_resource_len(pdev, mem_region2), "acx_2")) {
++ printk("acx: cannot reserve PCI memory region 2\n");
++ goto fail_request_mem_region2;
++ }
++
++ /* this used to be ioremap(), but ioremap_nocache()
++ * is much less risky, right? (and slower?)
++ * FIXME: we may want to go back to cached variant if it's
++ * certain that our code really properly handles
++ * cached operation (memory barriers, volatile?, ...)
++ * (but always keep this comment here regardless!)
++ * Possibly make this a driver config setting? */
++
++ mem1 = ioremap_nocache(phymem1, mem_region1_size);
++ if (!mem1) {
++ printk("acx: ioremap() FAILED\n");
++ goto fail_ioremap1;
++ }
++ mem2 = ioremap_nocache(phymem2, mem_region2_size);
++ if (!mem2) {
++ printk("acx: ioremap() #2 FAILED\n");
++ goto fail_ioremap2;
++ }
++
++ printk("acx: found %s-based wireless network card at %s, irq:%d, "
++ "phymem1:0x%lX, phymem2:0x%lX, mem1:0x%p, mem1_size:%ld, "
++ "mem2:0x%p, mem2_size:%ld\n",
++ chip_name, pci_name(pdev), pdev->irq, phymem1, phymem2,
++ mem1, mem_region1_size,
++ mem2, mem_region2_size);
++ log(L_ANY, "initial debug setting is 0x%04X\n", acx_debug);
++
++ if (0 == pdev->irq) {
++ printk("acx: can't use IRQ 0\n");
++ goto fail_irq;
++ }
++
++ ndev = alloc_netdev(sizeof(*adev), "wlan%d", dummy_netdev_init);
++ /* (NB: memsets to 0 entire area) */
++ if (!ndev) {
++ printk("acx: no memory for netdevice struct\n");
++ goto fail_alloc_netdev;
++ }
++
++ ether_setup(ndev);
++ ndev->open = &acxpci_e_open;
++ ndev->stop = &acxpci_e_close;
++ ndev->hard_start_xmit = &acx_i_start_xmit;
++ ndev->get_stats = &acx_e_get_stats;
++#if IW_HANDLER_VERSION <= 5
++ ndev->get_wireless_stats = &acx_e_get_wireless_stats;
++#endif
++ ndev->wireless_handlers = (struct iw_handler_def *)&acx_ioctl_handler_def;
++ ndev->set_multicast_list = &acxpci_i_set_multicast_list;
++ ndev->tx_timeout = &acxpci_i_tx_timeout;
++ ndev->change_mtu = &acx_e_change_mtu;
++ ndev->watchdog_timeo = 4 * HZ;
++ ndev->irq = pdev->irq;
++ ndev->base_addr = pci_resource_start(pdev, 0);
++
++ adev = ndev2adev(ndev);
++ spin_lock_init(&adev->lock); /* initial state: unlocked */
++ /* We do not start with downed sem: we want PARANOID_LOCKING to work */
++ sema_init(&adev->sem, 1); /* initial state: 1 (upped) */
++ /* since nobody can see new netdev yet, we can as well
++ ** just _presume_ that we're under sem (instead of actually taking it): */
++ /* acx_sem_lock(adev); */
++ adev->pdev = pdev;
++ adev->ndev = ndev;
++ adev->dev_type = DEVTYPE_PCI;
++ adev->chip_type = chip_type;
++ adev->chip_name = chip_name;
++ adev->io = (CHIPTYPE_ACX100 == chip_type) ? IO_ACX100 : IO_ACX111;
++ adev->membase = phymem1;
++ adev->iobase = mem1;
++ adev->membase2 = phymem2;
++ adev->iobase2 = mem2;
++ /* to find crashes due to weird driver access
++ * to unconfigured interface (ifup) */
++ adev->mgmt_timer.function = (void (*)(unsigned long))0x0000dead;
++
++#ifdef NONESSENTIAL_FEATURES
++ acx_show_card_eeprom_id(adev);
++#endif /* NONESSENTIAL_FEATURES */
++
++#ifdef SET_MODULE_OWNER
++ SET_MODULE_OWNER(ndev);
++#endif
++ SET_NETDEV_DEV(ndev, &pdev->dev);
++
++ log(L_IRQ|L_INIT, "using IRQ %d\n", pdev->irq);
++
++ /* need to be able to restore PCI state after a suspend */
++ pci_save_state(pdev);
++ pci_set_drvdata(pdev, ndev);
++
++ /* ok, pci setup is finished, now start initializing the card */
++
++ /* NB: read_reg() reads may return bogus data before reset_dev(),
++ * since the firmware which directly controls large parts of the I/O
++ * registers isn't initialized yet.
++ * acx100 seems to be more affected than acx111 */
++ if (OK != acxpci_s_reset_dev(adev))
++ goto fail_reset;
++
++ if (IS_ACX100(adev)) {
++ /* ACX100: configopt struct in cmd mailbox - directly after reset */
++ memcpy_fromio(&co, adev->cmd_area, sizeof(co));
++ }
++
++ if (OK != acx_s_init_mac(adev))
++ goto fail_init_mac;
++
++ if (IS_ACX111(adev)) {
++ /* ACX111: configopt struct needs to be queried after full init */
++ acx_s_interrogate(adev, &co, ACX111_IE_CONFIG_OPTIONS);
++ }
++
++/* TODO: merge them into one function, they are called just once and are the same for pci & usb */
++ if (OK != acxpci_read_eeprom_byte(adev, 0x05, &adev->eeprom_version))
++ goto fail_read_eeprom_version;
++
++ acx_s_parse_configoption(adev, &co);
++ acx_s_set_defaults(adev);
++ acx_s_get_firmware_version(adev); /* needs to be after acx_s_init_mac() */
++ acx_display_hardware_details(adev);
++
++ /* Register the card, AFTER everything else has been set up,
++ * since otherwise an ioctl could step on our feet due to
++ * firmware operations happening in parallel or uninitialized data */
++ err = register_netdev(ndev);
++ if (OK != err) {
++ printk("acx: register_netdev() FAILED: %d\n", err);
++ goto fail_register_netdev;
++ }
++
++ acx_proc_register_entries(ndev);
++
++ /* Now we have our device, so make sure the kernel doesn't try
++ * to send packets even though we're not associated to a network yet */
++ acx_stop_queue(ndev, "on probe");
++ acx_carrier_off(ndev, "on probe");
++
++ /* after register_netdev() userspace may start working with dev
++ * (in particular, on other CPUs), we only need to up the sem */
++ /* acx_sem_unlock(adev); */
++
++ printk("acx "ACX_RELEASE": net device %s, driver compiled "
++ "against wireless extensions %d and Linux %s\n",
++ ndev->name, WIRELESS_EXT, UTS_RELEASE);
++
++#if CMD_DISCOVERY
++ great_inquisitor(adev);
++#endif
++
++ result = OK;
++ goto done;
++
++ /* error paths: undo everything in reverse order... */
++
++fail_register_netdev:
++
++ acxpci_s_delete_dma_regions(adev);
++ pci_set_drvdata(pdev, NULL);
++
++fail_init_mac:
++fail_read_eeprom_version:
++fail_reset:
++
++ free_netdev(ndev);
++fail_alloc_netdev:
++fail_irq:
++
++ iounmap(mem2);
++fail_ioremap2:
++
++ iounmap(mem1);
++fail_ioremap1:
++
++ release_mem_region(pci_resource_start(pdev, mem_region2),
++ pci_resource_len(pdev, mem_region2));
++fail_request_mem_region2:
++
++ release_mem_region(pci_resource_start(pdev, mem_region1),
++ pci_resource_len(pdev, mem_region1));
++fail_request_mem_region1:
++fail_unknown_chiptype:
++
++ pci_disable_device(pdev);
++fail_pci_enable_device:
++
++ pci_set_power_state(pdev, PCI_D3hot);
++
++done:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acxpci_e_remove
++**
++** Shut device down (if not hot unplugged)
++** and deallocate PCI resources for the acx chip.
++**
++** pdev - ptr to PCI device structure containing info about pci configuration
++*/
++static void __devexit
++acxpci_e_remove(struct pci_dev *pdev)
++{
++ struct net_device *ndev;
++ acx_device_t *adev;
++ unsigned long mem_region1, mem_region2;
++ unsigned long flags;
++
++ FN_ENTER;
++
++ ndev = (struct net_device*) pci_get_drvdata(pdev);
++ if (!ndev) {
++ log(L_DEBUG, "%s: card is unused. Skipping any release code\n",
++ __func__);
++ goto end;
++ }
++
++ adev = ndev2adev(ndev);
++
++ /* If device wasn't hot unplugged... */
++ if (adev_present(adev)) {
++
++ acx_sem_lock(adev);
++
++ /* disable both Tx and Rx to shut radio down properly */
++ acx_s_issue_cmd(adev, ACX1xx_CMD_DISABLE_TX, NULL, 0);
++ acx_s_issue_cmd(adev, ACX1xx_CMD_DISABLE_RX, NULL, 0);
++
++#ifdef REDUNDANT
++ /* put the eCPU to sleep to save power
++ * Halting is not possible currently,
++ * since not supported by all firmware versions */
++ acx_s_issue_cmd(adev, ACX100_CMD_SLEEP, NULL, 0);
++#endif
++ acx_lock(adev, flags);
++ /* disable power LED to save power :-) */
++ log(L_INIT, "switching off power LED to save power\n");
++ acxpci_l_power_led(adev, 0);
++ /* stop our eCPU */
++ if (IS_ACX111(adev)) {
++ /* FIXME: does this actually keep halting the eCPU?
++ * I don't think so...
++ */
++ acxpci_l_reset_mac(adev);
++ } else {
++ u16 temp;
++ /* halt eCPU */
++ temp = read_reg16(adev, IO_ACX_ECPU_CTRL) | 0x1;
++ write_reg16(adev, IO_ACX_ECPU_CTRL, temp);
++ write_flush(adev);
++ }
++ acx_unlock(adev, flags);
++
++ acx_sem_unlock(adev);
++ }
++
++ /* unregister the device to not let the kernel
++ * (e.g. ioctls) access a half-deconfigured device
++ * NB: this will cause acxpci_e_close() to be called,
++ * thus we shouldn't call it under sem! */
++ log(L_INIT, "removing device %s\n", ndev->name);
++ unregister_netdev(ndev);
++
++ /* unregister_netdev ensures that no references to us left.
++ * For paranoid reasons we continue to follow the rules */
++ acx_sem_lock(adev);
++
++ if (adev->dev_state_mask & ACX_STATE_IFACE_UP) {
++ acxpci_s_down(ndev);
++ CLEAR_BIT(adev->dev_state_mask, ACX_STATE_IFACE_UP);
++ }
++
++ acx_proc_unregister_entries(ndev);
++
++ if (IS_ACX100(adev)) {
++ mem_region1 = PCI_ACX100_REGION1;
++ mem_region2 = PCI_ACX100_REGION2;
++ } else {
++ mem_region1 = PCI_ACX111_REGION1;
++ mem_region2 = PCI_ACX111_REGION2;
++ }
++
++ /* finally, clean up PCI bus state */
++ acxpci_s_delete_dma_regions(adev);
++ if (adev->iobase) iounmap(adev->iobase);
++ if (adev->iobase2) iounmap(adev->iobase2);
++ release_mem_region(pci_resource_start(pdev, mem_region1),
++ pci_resource_len(pdev, mem_region1));
++ release_mem_region(pci_resource_start(pdev, mem_region2),
++ pci_resource_len(pdev, mem_region2));
++ pci_disable_device(pdev);
++
++ /* remove dev registration */
++ pci_set_drvdata(pdev, NULL);
++
++ acx_sem_unlock(adev);
++
++ /* Free netdev (quite late,
++ * since otherwise we might get caught off-guard
++ * by a netdev timeout handler execution
++ * expecting to see a working dev...) */
++ free_netdev(ndev);
++
++ /* put device into ACPI D3 mode (shutdown) */
++ pci_set_power_state(pdev, PCI_D3hot);
++
++end:
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** TODO: PM code needs to be fixed / debugged / tested.
++*/
++#ifdef CONFIG_PM
++static int
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11)
++acxpci_e_suspend(struct pci_dev *pdev, pm_message_t state)
++#else
++acxpci_e_suspend(struct pci_dev *pdev, u32 state)
++#endif
++{
++ struct net_device *ndev = pci_get_drvdata(pdev);
++ acx_device_t *adev;
++
++ FN_ENTER;
++ printk("acx: suspend handler is experimental!\n");
++ printk("sus: dev %p\n", ndev);
++
++ if (!netif_running(ndev))
++ goto end;
++
++ adev = ndev2adev(ndev);
++ printk("sus: adev %p\n", adev);
++
++ acx_sem_lock(adev);
++
++ netif_device_detach(ndev); /* this one cannot sleep */
++ acxpci_s_down(ndev);
++ /* down() does not set it to 0xffff, but here we really want that */
++ write_reg16(adev, IO_ACX_IRQ_MASK, 0xffff);
++ write_reg16(adev, IO_ACX_FEMR, 0x0);
++ acxpci_s_delete_dma_regions(adev);
++ pci_save_state(pdev);
++ pci_set_power_state(pdev, PCI_D3hot);
++
++ acx_sem_unlock(adev);
++end:
++ FN_EXIT0;
++ return OK;
++}
++
++
++static int
++acxpci_e_resume(struct pci_dev *pdev)
++{
++ struct net_device *ndev = pci_get_drvdata(pdev);
++ acx_device_t *adev;
++
++ FN_ENTER;
++
++ printk("acx: resume handler is experimental!\n");
++ printk("rsm: got dev %p\n", ndev);
++
++ if (!netif_running(ndev))
++ goto end;
++
++ adev = ndev2adev(ndev);
++ printk("rsm: got adev %p\n", adev);
++
++ acx_sem_lock(adev);
++
++ pci_set_power_state(pdev, PCI_D0);
++ printk("rsm: power state PCI_D0 set\n");
++ pci_restore_state(pdev);
++ printk("rsm: PCI state restored\n");
++
++ if (OK != acxpci_s_reset_dev(adev))
++ goto end_unlock;
++ printk("rsm: device reset done\n");
++ if (OK != acx_s_init_mac(adev))
++ goto end_unlock;
++ printk("rsm: init MAC done\n");
++
++ acxpci_s_up(ndev);
++ printk("rsm: acx up done\n");
++
++ /* now even reload all card parameters as they were before suspend,
++ * and possibly be back in the network again already :-) */
++ if (ACX_STATE_IFACE_UP & adev->dev_state_mask) {
++ adev->set_mask = GETSET_ALL;
++ acx_s_update_card_settings(adev);
++ printk("rsm: settings updated\n");
++ }
++ netif_device_attach(ndev);
++ printk("rsm: device attached\n");
++
++end_unlock:
++ acx_sem_unlock(adev);
++end:
++ /* we need to return OK here anyway, right? */
++ FN_EXIT0;
++ return OK;
++}
++#endif /* CONFIG_PM */
++
++
++/***********************************************************************
++** acxpci_s_up
++**
++** This function is called by acxpci_e_open (when ifconfig sets the device as up)
++**
++** Side effects:
++** - Enables on-card interrupt requests
++** - calls acx_s_start
++*/
++
++static void
++enable_acx_irq(acx_device_t *adev)
++{
++ FN_ENTER;
++ write_reg16(adev, IO_ACX_IRQ_MASK, adev->irq_mask);
++ write_reg16(adev, IO_ACX_FEMR, 0x8000);
++ adev->irqs_active = 1;
++ FN_EXIT0;
++}
++
++static void
++acxpci_s_up(struct net_device *ndev)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ unsigned long flags;
++
++ FN_ENTER;
++
++ acx_lock(adev, flags);
++ enable_acx_irq(adev);
++ acx_unlock(adev, flags);
++
++ /* acx fw < 1.9.3.e has a hardware timer, and older drivers
++ ** used to use it. But we don't do that anymore, our OS
++ ** has reliable software timers */
++ init_timer(&adev->mgmt_timer);
++ adev->mgmt_timer.function = acx_i_timer;
++ adev->mgmt_timer.data = (unsigned long)adev;
++
++ /* Need to set ACX_STATE_IFACE_UP first, or else
++ ** timer won't be started by acx_set_status() */
++ SET_BIT(adev->dev_state_mask, ACX_STATE_IFACE_UP);
++ switch (adev->mode) {
++ case ACX_MODE_0_ADHOC:
++ case ACX_MODE_2_STA:
++ /* actual scan cmd will happen in start() */
++ acx_set_status(adev, ACX_STATUS_1_SCANNING); break;
++ case ACX_MODE_3_AP:
++ case ACX_MODE_MONITOR:
++ acx_set_status(adev, ACX_STATUS_4_ASSOCIATED); break;
++ }
++
++ acx_s_start(adev);
++
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acxpci_s_down
++**
++** NB: device may be already hot unplugged if called from acxpci_e_remove()
++**
++** Disables on-card interrupt request, stops softirq and timer, stops queue,
++** sets status == STOPPED
++*/
++
++static void
++disable_acx_irq(acx_device_t *adev)
++{
++ FN_ENTER;
++
++ /* I guess mask is not 0xffff because acx100 won't signal
++ ** cmd completion then (needed for ifup).
++ ** Someone with acx100 please confirm */
++ write_reg16(adev, IO_ACX_IRQ_MASK, adev->irq_mask_off);
++ write_reg16(adev, IO_ACX_FEMR, 0x0);
++ adev->irqs_active = 0;
++ FN_EXIT0;
++}
++
++static void
++acxpci_s_down(struct net_device *ndev)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ unsigned long flags;
++
++ FN_ENTER;
++
++ /* Disable IRQs first, so that IRQs cannot race with us */
++ /* then wait until interrupts have finished executing on other CPUs */
++ acx_lock(adev, flags);
++ disable_acx_irq(adev);
++ synchronize_irq(adev->pdev->irq);
++ acx_unlock(adev, flags);
++
++ /* we really don't want to have an asynchronous tasklet disturb us
++ ** after something vital for its job has been shut down, so
++ ** end all remaining work now.
++ **
++ ** NB: carrier_off (done by set_status below) would lead to
++ ** not yet fully understood deadlock in FLUSH_SCHEDULED_WORK().
++ ** That's why we do FLUSH first.
++ **
++ ** NB2: we have a bad locking bug here: FLUSH_SCHEDULED_WORK()
++ ** waits for acx_e_after_interrupt_task to complete if it is running
++ ** on another CPU, but acx_e_after_interrupt_task
++ ** will sleep on sem forever, because it is taken by us!
++ ** Work around that by temporary sem unlock.
++ ** This will fail miserably if we'll be hit by concurrent
++ ** iwconfig or something in between. TODO! */
++ acx_sem_unlock(adev);
++ FLUSH_SCHEDULED_WORK();
++ acx_sem_lock(adev);
++
++ /* This is possible:
++ ** FLUSH_SCHEDULED_WORK -> acx_e_after_interrupt_task ->
++ ** -> set_status(ASSOCIATED) -> wake_queue()
++ ** That's why we stop queue _after_ FLUSH_SCHEDULED_WORK
++ ** lock/unlock is just paranoia, maybe not needed */
++ acx_lock(adev, flags);
++ acx_stop_queue(ndev, "on ifdown");
++ acx_set_status(adev, ACX_STATUS_0_STOPPED);
++ acx_unlock(adev, flags);
++
++ /* kernel/timer.c says it's illegal to del_timer_sync()
++ ** a timer which restarts itself. We guarantee this cannot
++ ** ever happen because acx_i_timer() never does this if
++ ** status is ACX_STATUS_0_STOPPED */
++ del_timer_sync(&adev->mgmt_timer);
++
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acxpci_e_open
++**
++** Called as a result of SIOCSIFFLAGS ioctl changing the flags bit IFF_UP
++** from clear to set. In other words: ifconfig up.
++**
++** Returns:
++** 0 success
++** >0 f/w reported error
++** <0 driver reported error
++*/
++static int
++acxpci_e_open(struct net_device *ndev)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ int result = OK;
++
++ FN_ENTER;
++
++ acx_sem_lock(adev);
++
++ acx_init_task_scheduler(adev);
++
++/* TODO: pci_set_power_state(pdev, PCI_D0); ? */
++
++ /* request shared IRQ handler */
++ if (request_irq(ndev->irq, acxpci_i_interrupt, SA_SHIRQ, ndev->name, ndev)) {
++ printk("%s: request_irq FAILED\n", ndev->name);
++ result = -EAGAIN;
++ goto done;
++ }
++ log(L_DEBUG|L_IRQ, "request_irq %d successful\n", ndev->irq);
++
++ /* ifup device */
++ acxpci_s_up(ndev);
++
++ /* We don't currently have to do anything else.
++ * The setup of the MAC should be subsequently completed via
++ * the mlme commands.
++ * Higher layers know we're ready from dev->start==1 and
++ * dev->tbusy==0. Our rx path knows to pass up received/
++ * frames because of dev->flags&IFF_UP is true.
++ */
++done:
++ acx_sem_unlock(adev);
++
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acxpci_e_close
++**
++** Called as a result of SIOCSIIFFLAGS ioctl changing the flags bit IFF_UP
++** from set to clear. I.e. called by "ifconfig DEV down"
++**
++** Returns:
++** 0 success
++** >0 f/w reported error
++** <0 driver reported error
++*/
++static int
++acxpci_e_close(struct net_device *ndev)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++
++ FN_ENTER;
++
++ acx_sem_lock(adev);
++
++ /* ifdown device */
++ CLEAR_BIT(adev->dev_state_mask, ACX_STATE_IFACE_UP);
++ if (netif_device_present(ndev)) {
++ acxpci_s_down(ndev);
++ }
++
++ /* disable all IRQs, release shared IRQ handler */
++ write_reg16(adev, IO_ACX_IRQ_MASK, 0xffff);
++ write_reg16(adev, IO_ACX_FEMR, 0x0);
++ free_irq(ndev->irq, ndev);
++
++/* TODO: pci_set_power_state(pdev, PCI_D3hot); ? */
++
++ /* We currently don't have to do anything else.
++ * Higher layers know we're not ready from dev->start==0 and
++ * dev->tbusy==1. Our rx path knows to not pass up received
++ * frames because of dev->flags&IFF_UP is false.
++ */
++ acx_sem_unlock(adev);
++
++ log(L_INIT, "closed device\n");
++ FN_EXIT0;
++ return OK;
++}
++
++
++/***********************************************************************
++** acxpci_i_tx_timeout
++**
++** Called from network core. Must not sleep!
++*/
++static void
++acxpci_i_tx_timeout(struct net_device *ndev)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ unsigned long flags;
++ unsigned int tx_num_cleaned;
++
++ FN_ENTER;
++
++ acx_lock(adev, flags);
++
++ /* clean processed tx descs, they may have been completely full */
++ tx_num_cleaned = acxpci_l_clean_txdesc(adev);
++
++ /* nothing cleaned, yet (almost) no free buffers available?
++ * --> clean all tx descs, no matter which status!!
++ * Note that I strongly suspect that doing emergency cleaning
++ * may confuse the firmware. This is a last ditch effort to get
++ * ANYTHING to work again...
++ *
++ * TODO: it's best to simply reset & reinit hw from scratch...
++ */
++ if ((adev->tx_free <= TX_EMERG_CLEAN) && (tx_num_cleaned == 0)) {
++ printk("%s: FAILED to free any of the many full tx buffers. "
++ "Switching to emergency freeing. "
++ "Please report!\n", ndev->name);
++ acxpci_l_clean_txdesc_emergency(adev);
++ }
++
++ if (acx_queue_stopped(ndev) && (ACX_STATUS_4_ASSOCIATED == adev->status))
++ acx_wake_queue(ndev, "after tx timeout");
++
++ /* stall may have happened due to radio drift, so recalib radio */
++ acx_schedule_task(adev, ACX_AFTER_IRQ_CMD_RADIO_RECALIB);
++
++ /* do unimportant work last */
++ printk("%s: tx timeout!\n", ndev->name);
++ adev->stats.tx_errors++;
++
++ acx_unlock(adev, flags);
++
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acxpci_i_set_multicast_list
++** FIXME: most likely needs refinement
++*/
++static void
++acxpci_i_set_multicast_list(struct net_device *ndev)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ unsigned long flags;
++
++ FN_ENTER;
++
++ acx_lock(adev, flags);
++
++ /* firmwares don't have allmulti capability,
++ * so just use promiscuous mode instead in this case. */
++ if (ndev->flags & (IFF_PROMISC|IFF_ALLMULTI)) {
++ SET_BIT(adev->rx_config_1, RX_CFG1_RCV_PROMISCUOUS);
++ CLEAR_BIT(adev->rx_config_1, RX_CFG1_FILTER_ALL_MULTI);
++ SET_BIT(adev->set_mask, SET_RXCONFIG);
++ /* let kernel know in case *we* needed to set promiscuous */
++ ndev->flags |= (IFF_PROMISC|IFF_ALLMULTI);
++ } else {
++ CLEAR_BIT(adev->rx_config_1, RX_CFG1_RCV_PROMISCUOUS);
++ SET_BIT(adev->rx_config_1, RX_CFG1_FILTER_ALL_MULTI);
++ SET_BIT(adev->set_mask, SET_RXCONFIG);
++ ndev->flags &= ~(IFF_PROMISC|IFF_ALLMULTI);
++ }
++
++ /* cannot update card settings directly here, atomic context */
++ acx_schedule_task(adev, ACX_AFTER_IRQ_UPDATE_CARD_CFG);
++
++ acx_unlock(adev, flags);
++
++ FN_EXIT0;
++}
++
++
++/***************************************************************
++** acxpci_l_process_rxdesc
++**
++** Called directly and only from the IRQ handler
++*/
++
++#if !ACX_DEBUG
++static inline void log_rxbuffer(const acx_device_t *adev) {}
++#else
++static void
++log_rxbuffer(const acx_device_t *adev)
++{
++ register const struct rxhostdesc *rxhostdesc;
++ int i;
++ /* no FN_ENTER here, we don't want that */
++
++ rxhostdesc = adev->rxhostdesc_start;
++ if (unlikely(!rxhostdesc)) return;
++ for (i = 0; i < RX_CNT; i++) {
++ if ((rxhostdesc->Ctl_16 & cpu_to_le16(DESC_CTL_HOSTOWN))
++ && (rxhostdesc->Status & cpu_to_le32(DESC_STATUS_FULL)))
++ printk("rx: buf %d full\n", i);
++ rxhostdesc++;
++ }
++}
++#endif
++
++static void
++acxpci_l_process_rxdesc(acx_device_t *adev)
++{
++ register rxhostdesc_t *hostdesc;
++ unsigned count, tail;
++
++ FN_ENTER;
++
++ if (unlikely(acx_debug & L_BUFR))
++ log_rxbuffer(adev);
++
++ /* First, have a loop to determine the first descriptor that's
++ * full, just in case there's a mismatch between our current
++ * rx_tail and the full descriptor we're supposed to handle. */
++ tail = adev->rx_tail;
++ count = RX_CNT;
++ while (1) {
++ hostdesc = &adev->rxhostdesc_start[tail];
++ /* advance tail regardless of outcome of the below test */
++ tail = (tail + 1) % RX_CNT;
++
++ if ((hostdesc->Ctl_16 & cpu_to_le16(DESC_CTL_HOSTOWN))
++ && (hostdesc->Status & cpu_to_le32(DESC_STATUS_FULL)))
++ break; /* found it! */
++
++ if (unlikely(!--count)) /* hmm, no luck: all descs empty, bail out */
++ goto end;
++ }
++
++ /* now process descriptors, starting with the first we figured out */
++ while (1) {
++ log(L_BUFR, "rx: tail=%u Ctl_16=%04X Status=%08X\n",
++ tail, hostdesc->Ctl_16, hostdesc->Status);
++
++ acx_l_process_rxbuf(adev, hostdesc->data);
++
++ hostdesc->Status = 0;
++ /* flush all writes before adapter sees CTL_HOSTOWN change */
++ wmb();
++ /* Host no longer owns this, needs to be LAST */
++ CLEAR_BIT(hostdesc->Ctl_16, cpu_to_le16(DESC_CTL_HOSTOWN));
++
++ /* ok, descriptor is handled, now check the next descriptor */
++ hostdesc = &adev->rxhostdesc_start[tail];
++
++ /* if next descriptor is empty, then bail out */
++ if (!(hostdesc->Ctl_16 & cpu_to_le16(DESC_CTL_HOSTOWN))
++ || !(hostdesc->Status & cpu_to_le32(DESC_STATUS_FULL)))
++ break;
++
++ tail = (tail + 1) % RX_CNT;
++ }
++end:
++ adev->rx_tail = tail;
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acxpci_i_interrupt
++**
++** IRQ handler (atomic context, must not sleep, blah, blah)
++*/
++
++/* scan is complete. all frames now on the receive queue are valid */
++#define INFO_SCAN_COMPLETE 0x0001
++#define INFO_WEP_KEY_NOT_FOUND 0x0002
++/* hw has been reset as the result of a watchdog timer timeout */
++#define INFO_WATCH_DOG_RESET 0x0003
++/* failed to send out NULL frame from PS mode notification to AP */
++/* recommended action: try entering 802.11 PS mode again */
++#define INFO_PS_FAIL 0x0004
++/* encryption/decryption process on a packet failed */
++#define INFO_IV_ICV_FAILURE 0x0005
++
++/* Info mailbox format:
++2 bytes: type
++2 bytes: status
++more bytes may follow
++ rumors say about status:
++ 0x0000 info available (set by hw)
++ 0x0001 information received (must be set by host)
++ 0x1000 info available, mailbox overflowed (messages lost) (set by hw)
++ but in practice we've seen:
++ 0x9000 when we did not set status to 0x0001 on prev message
++ 0x1001 when we did set it
++ 0x0000 was never seen
++ conclusion: this is really a bitfield:
++ 0x1000 is 'info available' bit
++ 'mailbox overflowed' bit is 0x8000, not 0x1000
++ value of 0x0000 probably means that there are no messages at all
++ P.S. I dunno how in hell hw is supposed to notice that messages are lost -
++ it does NOT clear bit 0x0001, and this bit will probably stay forever set
++ after we set it once. Let's hope this will be fixed in firmware someday
++*/
++
++static void
++handle_info_irq(acx_device_t *adev)
++{
++#if ACX_DEBUG
++ static const char * const info_type_msg[] = {
++ "(unknown)",
++ "scan complete",
++ "WEP key not found",
++ "internal watchdog reset was done",
++ "failed to send powersave (NULL frame) notification to AP",
++ "encrypt/decrypt on a packet has failed",
++ "TKIP tx keys disabled",
++ "TKIP rx keys disabled",
++ "TKIP rx: key ID not found",
++ "???",
++ "???",
++ "???",
++ "???",
++ "???",
++ "???",
++ "???",
++ "TKIP IV value exceeds thresh"
++ };
++#endif
++ u32 info_type, info_status;
++
++ info_type = readl(adev->info_area);
++ info_status = (info_type >> 16);
++ info_type = (u16)info_type;
++
++ /* inform fw that we have read this info message */
++ writel(info_type | 0x00010000, adev->info_area);
++ write_reg16(adev, IO_ACX_INT_TRIG, INT_TRIG_INFOACK);
++ write_flush(adev);
++
++ log(L_CTL, "info_type:%04X info_status:%04X\n",
++ info_type, info_status);
++
++ log(L_IRQ, "got Info IRQ: status %04X type %04X: %s\n",
++ info_status, info_type,
++ info_type_msg[(info_type >= VEC_SIZE(info_type_msg)) ?
++ 0 : info_type]
++ );
++}
++
++
++static void
++log_unusual_irq(u16 irqtype) {
++ /*
++ if (!printk_ratelimit())
++ return;
++ */
++
++ printk("acx: got");
++ if (irqtype & HOST_INT_RX_DATA) {
++ printk(" Rx_Data");
++ }
++ /* HOST_INT_TX_COMPLETE */
++ if (irqtype & HOST_INT_TX_XFER) {
++ printk(" Tx_Xfer");
++ }
++ /* HOST_INT_RX_COMPLETE */
++ if (irqtype & HOST_INT_DTIM) {
++ printk(" DTIM");
++ }
++ if (irqtype & HOST_INT_BEACON) {
++ printk(" Beacon");
++ }
++ if (irqtype & HOST_INT_TIMER) {
++ log(L_IRQ, " Timer");
++ }
++ if (irqtype & HOST_INT_KEY_NOT_FOUND) {
++ printk(" Key_Not_Found");
++ }
++ if (irqtype & HOST_INT_IV_ICV_FAILURE) {
++ printk(" IV_ICV_Failure (crypto)");
++ }
++ /* HOST_INT_CMD_COMPLETE */
++ /* HOST_INT_INFO */
++ if (irqtype & HOST_INT_OVERFLOW) {
++ printk(" Overflow");
++ }
++ if (irqtype & HOST_INT_PROCESS_ERROR) {
++ printk(" Process_Error");
++ }
++ /* HOST_INT_SCAN_COMPLETE */
++ if (irqtype & HOST_INT_FCS_THRESHOLD) {
++ printk(" FCS_Threshold");
++ }
++ if (irqtype & HOST_INT_UNKNOWN) {
++ printk(" Unknown");
++ }
++ printk(" IRQ(s)\n");
++}
++
++
++static void
++update_link_quality_led(acx_device_t *adev)
++{
++ int qual;
++
++ qual = acx_signal_determine_quality(adev->wstats.qual.level, adev->wstats.qual.noise);
++ if (qual > adev->brange_max_quality)
++ qual = adev->brange_max_quality;
++
++ if (time_after(jiffies, adev->brange_time_last_state_change +
++ (HZ/2 - HZ/2 * (unsigned long)qual / adev->brange_max_quality ) )) {
++ acxpci_l_power_led(adev, (adev->brange_last_state == 0));
++ adev->brange_last_state ^= 1; /* toggle */
++ adev->brange_time_last_state_change = jiffies;
++ }
++}
++
++
++#define MAX_IRQLOOPS_PER_JIFFY (20000/HZ) /* a la orinoco.c */
++
++static irqreturn_t
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
++acxpci_i_interrupt(int irq, void *dev_id)
++#else
++acxpci_i_interrupt(int irq, void *dev_id, struct pt_regs *regs)
++#endif
++{
++ acx_device_t *adev;
++ unsigned long flags;
++ unsigned int irqcount = MAX_IRQLOOPS_PER_JIFFY;
++ register u16 irqtype;
++ u16 unmasked;
++
++ adev = ndev2adev((struct net_device*)dev_id);
++
++ /* LOCKING: can just spin_lock() since IRQs are disabled anyway.
++ * I am paranoid */
++ acx_lock(adev, flags);
++
++ unmasked = read_reg16(adev, IO_ACX_IRQ_STATUS_CLEAR);
++ if (unlikely(0xffff == unmasked)) {
++ /* 0xffff value hints at missing hardware,
++ * so don't do anything.
++ * Not very clean, but other drivers do the same... */
++ log(L_IRQ, "IRQ type:FFFF - device removed? IRQ_NONE\n");
++ goto none;
++ }
++
++ /* We will check only "interesting" IRQ types */
++ irqtype = unmasked & ~adev->irq_mask;
++ if (!irqtype) {
++ /* We are on a shared IRQ line and it wasn't our IRQ */
++ log(L_IRQ, "IRQ type:%04X, mask:%04X - all are masked, IRQ_NONE\n",
++ unmasked, adev->irq_mask);
++ goto none;
++ }
++
++ /* Done here because IRQ_NONEs taking three lines of log
++ ** drive me crazy */
++ FN_ENTER;
++
++#define IRQ_ITERATE 1
++#if IRQ_ITERATE
++if (jiffies != adev->irq_last_jiffies) {
++ adev->irq_loops_this_jiffy = 0;
++ adev->irq_last_jiffies = jiffies;
++}
++
++/* safety condition; we'll normally abort loop below
++ * in case no IRQ type occurred */
++while (likely(--irqcount)) {
++#endif
++ /* ACK all IRQs ASAP */
++ write_reg16(adev, IO_ACX_IRQ_ACK, 0xffff);
++
++ log(L_IRQ, "IRQ type:%04X, mask:%04X, type & ~mask:%04X\n",
++ unmasked, adev->irq_mask, irqtype);
++
++ /* Handle most important IRQ types first */
++ if (irqtype & HOST_INT_RX_COMPLETE) {
++ log(L_IRQ, "got Rx_Complete IRQ\n");
++ acxpci_l_process_rxdesc(adev);
++ }
++ if (irqtype & HOST_INT_TX_COMPLETE) {
++ log(L_IRQ, "got Tx_Complete IRQ\n");
++ /* don't clean up on each Tx complete, wait a bit
++ * unless we're going towards full, in which case
++ * we do it immediately, too (otherwise we might lockup
++ * with a full Tx buffer if we go into
++ * acxpci_l_clean_txdesc() at a time when we won't wakeup
++ * the net queue in there for some reason...) */
++ if (adev->tx_free <= TX_START_CLEAN) {
++#if TX_CLEANUP_IN_SOFTIRQ
++ acx_schedule_task(adev, ACX_AFTER_IRQ_TX_CLEANUP);
++#else
++ acxpci_l_clean_txdesc(adev);
++#endif
++ }
++ }
++
++ /* Less frequent ones */
++ if (irqtype & (0
++ | HOST_INT_CMD_COMPLETE
++ | HOST_INT_INFO
++ | HOST_INT_SCAN_COMPLETE
++ )) {
++ if (irqtype & HOST_INT_CMD_COMPLETE) {
++ log(L_IRQ, "got Command_Complete IRQ\n");
++ /* save the state for the running issue_cmd() */
++ SET_BIT(adev->irq_status, HOST_INT_CMD_COMPLETE);
++ }
++ if (irqtype & HOST_INT_INFO) {
++ handle_info_irq(adev);
++ }
++ if (irqtype & HOST_INT_SCAN_COMPLETE) {
++ log(L_IRQ, "got Scan_Complete IRQ\n");
++ /* need to do that in process context */
++ acx_schedule_task(adev, ACX_AFTER_IRQ_COMPLETE_SCAN);
++ /* remember that fw is not scanning anymore */
++ SET_BIT(adev->irq_status, HOST_INT_SCAN_COMPLETE);
++ }
++ }
++
++ /* These we just log, but either they happen rarely
++ * or we keep them masked out */
++ if (irqtype & (0
++ | HOST_INT_RX_DATA
++ /* | HOST_INT_TX_COMPLETE */
++ | HOST_INT_TX_XFER
++ /* | HOST_INT_RX_COMPLETE */
++ | HOST_INT_DTIM
++ | HOST_INT_BEACON
++ | HOST_INT_TIMER
++ | HOST_INT_KEY_NOT_FOUND
++ | HOST_INT_IV_ICV_FAILURE
++ /* | HOST_INT_CMD_COMPLETE */
++ /* | HOST_INT_INFO */
++ | HOST_INT_OVERFLOW
++ | HOST_INT_PROCESS_ERROR
++ /* | HOST_INT_SCAN_COMPLETE */
++ | HOST_INT_FCS_THRESHOLD
++ | HOST_INT_UNKNOWN
++ )) {
++ log_unusual_irq(irqtype);
++ }
++
++#if IRQ_ITERATE
++ unmasked = read_reg16(adev, IO_ACX_IRQ_STATUS_CLEAR);
++ irqtype = unmasked & ~adev->irq_mask;
++ /* Bail out if no new IRQ bits or if all are masked out */
++ if (!irqtype)
++ break;
++
++ if (unlikely(++adev->irq_loops_this_jiffy > MAX_IRQLOOPS_PER_JIFFY)) {
++ printk(KERN_ERR "acx: too many interrupts per jiffy!\n");
++ /* Looks like card floods us with IRQs! Try to stop that */
++ write_reg16(adev, IO_ACX_IRQ_MASK, 0xffff);
++ /* This will short-circuit all future attempts to handle IRQ.
++ * We cant do much more... */
++ adev->irq_mask = 0;
++ break;
++ }
++}
++#endif
++ /* Routine to perform blink with range */
++ if (unlikely(adev->led_power == 2))
++ update_link_quality_led(adev);
++
++/* handled: */
++ /* write_flush(adev); - not needed, last op was read anyway */
++ acx_unlock(adev, flags);
++ FN_EXIT0;
++ return IRQ_HANDLED;
++
++none:
++ acx_unlock(adev, flags);
++ return IRQ_NONE;
++}
++
++
++/***********************************************************************
++** acxpci_l_power_led
++*/
++void
++acxpci_l_power_led(acx_device_t *adev, int enable)
++{
++ u16 gpio_pled = IS_ACX111(adev) ? 0x0040 : 0x0800;
++
++ /* A hack. Not moving message rate limiting to adev->xxx
++ * (it's only a debug message after all) */
++ static int rate_limit = 0;
++
++ if (rate_limit++ < 3)
++ log(L_IOCTL, "Please report in case toggling the power "
++ "LED doesn't work for your card!\n");
++ if (enable)
++ write_reg16(adev, IO_ACX_GPIO_OUT,
++ read_reg16(adev, IO_ACX_GPIO_OUT) & ~gpio_pled);
++ else
++ write_reg16(adev, IO_ACX_GPIO_OUT,
++ read_reg16(adev, IO_ACX_GPIO_OUT) | gpio_pled);
++}
++
++
++/***********************************************************************
++** Ioctls
++*/
++
++/***********************************************************************
++*/
++int
++acx111pci_ioctl_info(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++#if ACX_DEBUG > 1
++ acx_device_t *adev = ndev2adev(ndev);
++ rxdesc_t *rxdesc;
++ txdesc_t *txdesc;
++ rxhostdesc_t *rxhostdesc;
++ txhostdesc_t *txhostdesc;
++ struct acx111_ie_memoryconfig memconf;
++ struct acx111_ie_queueconfig queueconf;
++ unsigned long flags;
++ int i;
++ char memmap[0x34];
++ char rxconfig[0x8];
++ char fcserror[0x8];
++ char ratefallback[0x5];
++
++ if ( !(acx_debug & (L_IOCTL|L_DEBUG)) )
++ return OK;
++ /* using printk() since we checked debug flag already */
++
++ acx_sem_lock(adev);
++
++ if (!IS_ACX111(adev)) {
++ printk("acx111-specific function called "
++ "with non-acx111 chip, aborting\n");
++ goto end_ok;
++ }
++
++ /* get Acx111 Memory Configuration */
++ memset(&memconf, 0, sizeof(memconf));
++ /* BTW, fails with 12 (Write only) error code.
++ ** Retained for easy testing of issue_cmd error handling :) */
++ acx_s_interrogate(adev, &memconf, ACX1xx_IE_QUEUE_CONFIG);
++
++ /* get Acx111 Queue Configuration */
++ memset(&queueconf, 0, sizeof(queueconf));
++ acx_s_interrogate(adev, &queueconf, ACX1xx_IE_MEMORY_CONFIG_OPTIONS);
++
++ /* get Acx111 Memory Map */
++ memset(memmap, 0, sizeof(memmap));
++ acx_s_interrogate(adev, &memmap, ACX1xx_IE_MEMORY_MAP);
++
++ /* get Acx111 Rx Config */
++ memset(rxconfig, 0, sizeof(rxconfig));
++ acx_s_interrogate(adev, &rxconfig, ACX1xx_IE_RXCONFIG);
++
++ /* get Acx111 fcs error count */
++ memset(fcserror, 0, sizeof(fcserror));
++ acx_s_interrogate(adev, &fcserror, ACX1xx_IE_FCS_ERROR_COUNT);
++
++ /* get Acx111 rate fallback */
++ memset(ratefallback, 0, sizeof(ratefallback));
++ acx_s_interrogate(adev, &ratefallback, ACX1xx_IE_RATE_FALLBACK);
++
++ /* force occurrence of a beacon interrupt */
++ /* TODO: comment why is this necessary */
++ write_reg16(adev, IO_ACX_HINT_TRIG, HOST_INT_BEACON);
++
++ /* dump Acx111 Mem Configuration */
++ printk("dump mem config:\n"
++ "data read: %d, struct size: %d\n"
++ "Number of stations: %1X\n"
++ "Memory block size: %1X\n"
++ "tx/rx memory block allocation: %1X\n"
++ "count rx: %X / tx: %X queues\n"
++ "options %1X\n"
++ "fragmentation %1X\n"
++ "Rx Queue 1 Count Descriptors: %X\n"
++ "Rx Queue 1 Host Memory Start: %X\n"
++ "Tx Queue 1 Count Descriptors: %X\n"
++ "Tx Queue 1 Attributes: %X\n",
++ memconf.len, (int) sizeof(memconf),
++ memconf.no_of_stations,
++ memconf.memory_block_size,
++ memconf.tx_rx_memory_block_allocation,
++ memconf.count_rx_queues, memconf.count_tx_queues,
++ memconf.options,
++ memconf.fragmentation,
++ memconf.rx_queue1_count_descs,
++ acx2cpu(memconf.rx_queue1_host_rx_start),
++ memconf.tx_queue1_count_descs,
++ memconf.tx_queue1_attributes);
++
++ /* dump Acx111 Queue Configuration */
++ printk("dump queue head:\n"
++ "data read: %d, struct size: %d\n"
++ "tx_memory_block_address (from card): %X\n"
++ "rx_memory_block_address (from card): %X\n"
++ "rx1_queue address (from card): %X\n"
++ "tx1_queue address (from card): %X\n"
++ "tx1_queue attributes (from card): %X\n",
++ queueconf.len, (int) sizeof(queueconf),
++ queueconf.tx_memory_block_address,
++ queueconf.rx_memory_block_address,
++ queueconf.rx1_queue_address,
++ queueconf.tx1_queue_address,
++ queueconf.tx1_attributes);
++
++ /* dump Acx111 Mem Map */
++ printk("dump mem map:\n"
++ "data read: %d, struct size: %d\n"
++ "Code start: %X\n"
++ "Code end: %X\n"
++ "WEP default key start: %X\n"
++ "WEP default key end: %X\n"
++ "STA table start: %X\n"
++ "STA table end: %X\n"
++ "Packet template start: %X\n"
++ "Packet template end: %X\n"
++ "Queue memory start: %X\n"
++ "Queue memory end: %X\n"
++ "Packet memory pool start: %X\n"
++ "Packet memory pool end: %X\n"
++ "iobase: %p\n"
++ "iobase2: %p\n",
++ *((u16 *)&memmap[0x02]), (int) sizeof(memmap),
++ *((u32 *)&memmap[0x04]),
++ *((u32 *)&memmap[0x08]),
++ *((u32 *)&memmap[0x0C]),
++ *((u32 *)&memmap[0x10]),
++ *((u32 *)&memmap[0x14]),
++ *((u32 *)&memmap[0x18]),
++ *((u32 *)&memmap[0x1C]),
++ *((u32 *)&memmap[0x20]),
++ *((u32 *)&memmap[0x24]),
++ *((u32 *)&memmap[0x28]),
++ *((u32 *)&memmap[0x2C]),
++ *((u32 *)&memmap[0x30]),
++ adev->iobase,
++ adev->iobase2);
++
++ /* dump Acx111 Rx Config */
++ printk("dump rx config:\n"
++ "data read: %d, struct size: %d\n"
++ "rx config: %X\n"
++ "rx filter config: %X\n",
++ *((u16 *)&rxconfig[0x02]), (int) sizeof(rxconfig),
++ *((u16 *)&rxconfig[0x04]),
++ *((u16 *)&rxconfig[0x06]));
++
++ /* dump Acx111 fcs error */
++ printk("dump fcserror:\n"
++ "data read: %d, struct size: %d\n"
++ "fcserrors: %X\n",
++ *((u16 *)&fcserror[0x02]), (int) sizeof(fcserror),
++ *((u32 *)&fcserror[0x04]));
++
++ /* dump Acx111 rate fallback */
++ printk("dump rate fallback:\n"
++ "data read: %d, struct size: %d\n"
++ "ratefallback: %X\n",
++ *((u16 *)&ratefallback[0x02]), (int) sizeof(ratefallback),
++ *((u8 *)&ratefallback[0x04]));
++
++ /* protect against IRQ */
++ acx_lock(adev, flags);
++
++ /* dump acx111 internal rx descriptor ring buffer */
++ rxdesc = adev->rxdesc_start;
++
++ /* loop over complete receive pool */
++ if (rxdesc) for (i = 0; i < RX_CNT; i++) {
++ printk("\ndump internal rxdesc %d:\n"
++ "mem pos %p\n"
++ "next 0x%X\n"
++ "acx mem pointer (dynamic) 0x%X\n"
++ "CTL (dynamic) 0x%X\n"
++ "Rate (dynamic) 0x%X\n"
++ "RxStatus (dynamic) 0x%X\n"
++ "Mod/Pre (dynamic) 0x%X\n",
++ i,
++ rxdesc,
++ acx2cpu(rxdesc->pNextDesc),
++ acx2cpu(rxdesc->ACXMemPtr),
++ rxdesc->Ctl_8,
++ rxdesc->rate,
++ rxdesc->error,
++ rxdesc->SNR);
++ rxdesc++;
++ }
++
++ /* dump host rx descriptor ring buffer */
++
++ rxhostdesc = adev->rxhostdesc_start;
++
++ /* loop over complete receive pool */
++ if (rxhostdesc) for (i = 0; i < RX_CNT; i++) {
++ printk("\ndump host rxdesc %d:\n"
++ "mem pos %p\n"
++ "buffer mem pos 0x%X\n"
++ "buffer mem offset 0x%X\n"
++ "CTL 0x%X\n"
++ "Length 0x%X\n"
++ "next 0x%X\n"
++ "Status 0x%X\n",
++ i,
++ rxhostdesc,
++ acx2cpu(rxhostdesc->data_phy),
++ rxhostdesc->data_offset,
++ le16_to_cpu(rxhostdesc->Ctl_16),
++ le16_to_cpu(rxhostdesc->length),
++ acx2cpu(rxhostdesc->desc_phy_next),
++ rxhostdesc->Status);
++ rxhostdesc++;
++ }
++
++ /* dump acx111 internal tx descriptor ring buffer */
++ txdesc = adev->txdesc_start;
++
++ /* loop over complete transmit pool */
++ if (txdesc) for (i = 0; i < TX_CNT; i++) {
++ printk("\ndump internal txdesc %d:\n"
++ "size 0x%X\n"
++ "mem pos %p\n"
++ "next 0x%X\n"
++ "acx mem pointer (dynamic) 0x%X\n"
++ "host mem pointer (dynamic) 0x%X\n"
++ "length (dynamic) 0x%X\n"
++ "CTL (dynamic) 0x%X\n"
++ "CTL2 (dynamic) 0x%X\n"
++ "Status (dynamic) 0x%X\n"
++ "Rate (dynamic) 0x%X\n",
++ i,
++ (int) sizeof(struct txdesc),
++ txdesc,
++ acx2cpu(txdesc->pNextDesc),
++ acx2cpu(txdesc->AcxMemPtr),
++ acx2cpu(txdesc->HostMemPtr),
++ le16_to_cpu(txdesc->total_length),
++ txdesc->Ctl_8,
++ txdesc->Ctl2_8, txdesc->error,
++ txdesc->u.r1.rate);
++ txdesc = advance_txdesc(adev, txdesc, 1);
++ }
++
++ /* dump host tx descriptor ring buffer */
++
++ txhostdesc = adev->txhostdesc_start;
++
++ /* loop over complete host send pool */
++ if (txhostdesc) for (i = 0; i < TX_CNT * 2; i++) {
++ printk("\ndump host txdesc %d:\n"
++ "mem pos %p\n"
++ "buffer mem pos 0x%X\n"
++ "buffer mem offset 0x%X\n"
++ "CTL 0x%X\n"
++ "Length 0x%X\n"
++ "next 0x%X\n"
++ "Status 0x%X\n",
++ i,
++ txhostdesc,
++ acx2cpu(txhostdesc->data_phy),
++ txhostdesc->data_offset,
++ le16_to_cpu(txhostdesc->Ctl_16),
++ le16_to_cpu(txhostdesc->length),
++ acx2cpu(txhostdesc->desc_phy_next),
++ le32_to_cpu(txhostdesc->Status));
++ txhostdesc++;
++ }
++
++ /* write_reg16(adev, 0xb4, 0x4); */
++
++ acx_unlock(adev, flags);
++end_ok:
++
++ acx_sem_unlock(adev);
++#endif /* ACX_DEBUG */
++ return OK;
++}
++
++
++/***********************************************************************
++*/
++int
++acx100pci_ioctl_set_phy_amp_bias(
++ struct net_device *ndev,
++ struct iw_request_info *info,
++ struct iw_param *vwrq,
++ char *extra)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ unsigned long flags;
++ u16 gpio_old;
++
++ if (!IS_ACX100(adev)) {
++ /* WARNING!!!
++ * Removing this check *might* damage
++ * hardware, since we're tweaking GPIOs here after all!!!
++ * You've been warned...
++ * WARNING!!! */
++ printk("acx: sorry, setting bias level for non-acx100 "
++ "is not supported yet\n");
++ return OK;
++ }
++
++ if (*extra > 7) {
++ printk("acx: invalid bias parameter, range is 0-7\n");
++ return -EINVAL;
++ }
++
++ acx_sem_lock(adev);
++
++ /* Need to lock accesses to [IO_ACX_GPIO_OUT]:
++ * IRQ handler uses it to update LED */
++ acx_lock(adev, flags);
++ gpio_old = read_reg16(adev, IO_ACX_GPIO_OUT);
++ write_reg16(adev, IO_ACX_GPIO_OUT, (gpio_old & 0xf8ff) | ((u16)*extra << 8));
++ acx_unlock(adev, flags);
++
++ log(L_DEBUG, "gpio_old: 0x%04X\n", gpio_old);
++ printk("%s: PHY power amplifier bias: old:%d, new:%d\n",
++ ndev->name,
++ (gpio_old & 0x0700) >> 8, (unsigned char)*extra);
++
++ acx_sem_unlock(adev);
++
++ return OK;
++}
++
++
++/***************************************************************
++** acxpci_l_alloc_tx
++** Actually returns a txdesc_t* ptr
++**
++** FIXME: in case of fragments, should allocate multiple descrs
++** after figuring out how many we need and whether we still have
++** sufficiently many.
++*/
++tx_t*
++acxpci_l_alloc_tx(acx_device_t *adev)
++{
++ struct txdesc *txdesc;
++ unsigned head;
++ u8 ctl8;
++
++ FN_ENTER;
++
++ if (unlikely(!adev->tx_free)) {
++ printk("acx: BUG: no free txdesc left\n");
++ txdesc = NULL;
++ goto end;
++ }
++
++ head = adev->tx_head;
++ txdesc = get_txdesc(adev, head);
++ ctl8 = txdesc->Ctl_8;
++
++ /* 2005-10-11: there were several bug reports on this happening
++ ** but now cause seems to be understood & fixed */
++ if (unlikely(DESC_CTL_HOSTOWN != (ctl8 & DESC_CTL_ACXDONE_HOSTOWN))) {
++ /* whoops, descr at current index is not free, so probably
++ * ring buffer already full */
++ printk("acx: BUG: tx_head:%d Ctl8:0x%02X - failed to find "
++ "free txdesc\n", head, ctl8);
++ txdesc = NULL;
++ goto end;
++ }
++
++ /* Needed in case txdesc won't be eventually submitted for tx */
++ txdesc->Ctl_8 = DESC_CTL_ACXDONE_HOSTOWN;
++
++ adev->tx_free--;
++ log(L_BUFT, "tx: got desc %u, %u remain\n",
++ head, adev->tx_free);
++ /* Keep a few free descs between head and tail of tx ring.
++ ** It is not absolutely needed, just feels safer */
++ if (adev->tx_free < TX_STOP_QUEUE) {
++ log(L_BUF, "stop queue (%u tx desc left)\n",
++ adev->tx_free);
++ acx_stop_queue(adev->ndev, NULL);
++ }
++
++ /* returning current descriptor, so advance to next free one */
++ adev->tx_head = (head + 1) % TX_CNT;
++end:
++ FN_EXIT0;
++
++ return (tx_t*)txdesc;
++}
++
++
++/***********************************************************************
++*/
++void*
++acxpci_l_get_txbuf(acx_device_t *adev, tx_t* tx_opaque)
++{
++ return get_txhostdesc(adev, (txdesc_t*)tx_opaque)->data;
++}
++
++
++/***********************************************************************
++** acxpci_l_tx_data
++**
++** Can be called from IRQ (rx -> (AP bridging or mgmt response) -> tx).
++** Can be called from acx_i_start_xmit (data frames from net core).
++**
++** FIXME: in case of fragments, should loop over the number of
++** pre-allocated tx descrs, properly setting up transfer data and
++** CTL_xxx flags according to fragment number.
++*/
++void
++acxpci_l_tx_data(acx_device_t *adev, tx_t* tx_opaque, int len)
++{
++ txdesc_t *txdesc = (txdesc_t*)tx_opaque;
++ txhostdesc_t *hostdesc1, *hostdesc2;
++ client_t *clt;
++ u16 rate_cur;
++ u8 Ctl_8, Ctl2_8;
++
++ FN_ENTER;
++
++ /* fw doesn't tx such packets anyhow */
++ if (unlikely(len < WLAN_HDR_A3_LEN))
++ goto end;
++
++ hostdesc1 = get_txhostdesc(adev, txdesc);
++ /* modify flag status in separate variable to be able to write it back
++ * in one big swoop later (also in order to have less device memory
++ * accesses) */
++ Ctl_8 = txdesc->Ctl_8;
++ Ctl2_8 = 0; /* really need to init it to 0, not txdesc->Ctl2_8, it seems */
++
++ hostdesc2 = hostdesc1 + 1;
++
++ /* DON'T simply set Ctl field to 0 here globally,
++ * it needs to maintain a consistent flag status (those are state flags!!),
++ * otherwise it may lead to severe disruption. Only set or reset particular
++ * flags at the exact moment this is needed... */
++
++ /* let chip do RTS/CTS handshaking before sending
++ * in case packet size exceeds threshold */
++ if (len > adev->rts_threshold)
++ SET_BIT(Ctl2_8, DESC_CTL2_RTS);
++ else
++ CLEAR_BIT(Ctl2_8, DESC_CTL2_RTS);
++
++ switch (adev->mode) {
++ case ACX_MODE_0_ADHOC:
++ case ACX_MODE_3_AP:
++ clt = acx_l_sta_list_get(adev, ((wlan_hdr_t*)hostdesc1->data)->a1);
++ break;
++ case ACX_MODE_2_STA:
++ clt = adev->ap_client;
++ break;
++#if 0
++/* testing was done on acx111: */
++ case ACX_MODE_MONITOR:
++ SET_BIT(Ctl2_8, 0
++/* sends CTS to self before packet */
++ + DESC_CTL2_SEQ /* don't increase sequence field */
++/* not working (looks like good fcs is still added) */
++ + DESC_CTL2_FCS /* don't add the FCS */
++/* not tested */
++ + DESC_CTL2_MORE_FRAG
++/* not tested */
++ + DESC_CTL2_RETRY /* don't increase retry field */
++/* not tested */
++ + DESC_CTL2_POWER /* don't increase power mgmt. field */
++/* no effect */
++ + DESC_CTL2_WEP /* encrypt this frame */
++/* not tested */
++ + DESC_CTL2_DUR /* don't increase duration field */
++ );
++ /* fallthrough */
++#endif
++ default: /* ACX_MODE_OFF, ACX_MODE_MONITOR */
++ clt = NULL;
++ break;
++ }
++
++ rate_cur = clt ? clt->rate_cur : adev->rate_bcast;
++ if (unlikely(!rate_cur)) {
++ printk("acx: driver bug! bad ratemask\n");
++ goto end;
++ }
++
++ /* used in tx cleanup routine for auto rate and accounting: */
++ put_txcr(adev, txdesc, clt, rate_cur);
++
++ txdesc->total_length = cpu_to_le16(len);
++ hostdesc2->length = cpu_to_le16(len - WLAN_HDR_A3_LEN);
++ if (IS_ACX111(adev)) {
++ /* note that if !txdesc->do_auto, txrate->cur
++ ** has only one nonzero bit */
++ txdesc->u.r2.rate111 = cpu_to_le16(
++ rate_cur
++ /* WARNING: I was never able to make it work with prism54 AP.
++ ** It was falling down to 1Mbit where shortpre is not applicable,
++ ** and not working at all at "5,11 basic rates only" setting.
++ ** I even didn't see tx packets in radio packet capture.
++ ** Disabled for now --vda */
++ /*| ((clt->shortpre && clt->cur!=RATE111_1) ? RATE111_SHORTPRE : 0) */
++ );
++#ifdef TODO_FIGURE_OUT_WHEN_TO_SET_THIS
++ /* should add this to rate111 above as necessary */
++ | (clt->pbcc511 ? RATE111_PBCC511 : 0)
++#endif
++ hostdesc1->length = cpu_to_le16(len);
++ } else { /* ACX100 */
++ u8 rate_100 = clt ? clt->rate_100 : adev->rate_bcast100;
++ txdesc->u.r1.rate = rate_100;
++#ifdef TODO_FIGURE_OUT_WHEN_TO_SET_THIS
++ if (clt->pbcc511) {
++ if (n == RATE100_5 || n == RATE100_11)
++ n |= RATE100_PBCC511;
++ }
++
++ if (clt->shortpre && (clt->cur != RATE111_1))
++ SET_BIT(Ctl_8, DESC_CTL_SHORT_PREAMBLE); /* set Short Preamble */
++#endif
++ /* set autodma and reclaim and 1st mpdu */
++ SET_BIT(Ctl_8, DESC_CTL_AUTODMA | DESC_CTL_RECLAIM | DESC_CTL_FIRSTFRAG);
++#if ACX_FRAGMENTATION
++ /* SET_BIT(Ctl2_8, DESC_CTL2_MORE_FRAG); cannot set it unconditionally, needs to be set for all non-last fragments */
++#endif
++ hostdesc1->length = cpu_to_le16(WLAN_HDR_A3_LEN);
++ }
++ /* don't need to clean ack/rts statistics here, already
++ * done on descr cleanup */
++
++ /* clears HOSTOWN and ACXDONE bits, thus telling that the descriptors
++ * are now owned by the acx100; do this as LAST operation */
++ CLEAR_BIT(Ctl_8, DESC_CTL_ACXDONE_HOSTOWN);
++ /* flush writes before we release hostdesc to the adapter here */
++ wmb();
++ CLEAR_BIT(hostdesc1->Ctl_16, cpu_to_le16(DESC_CTL_HOSTOWN));
++ CLEAR_BIT(hostdesc2->Ctl_16, cpu_to_le16(DESC_CTL_HOSTOWN));
++
++ /* write back modified flags */
++ txdesc->Ctl2_8 = Ctl2_8;
++ txdesc->Ctl_8 = Ctl_8;
++ /* unused: txdesc->tx_time = cpu_to_le32(jiffies); */
++
++ /* flush writes before we tell the adapter that it's its turn now */
++ mmiowb();
++ write_reg16(adev, IO_ACX_INT_TRIG, INT_TRIG_TXPRC);
++ write_flush(adev);
++
++ /* log the packet content AFTER sending it,
++ * in order to not delay sending any further than absolutely needed
++ * Do separate logs for acx100/111 to have human-readable rates */
++ if (unlikely(acx_debug & (L_XFER|L_DATA))) {
++ u16 fc = ((wlan_hdr_t*)hostdesc1->data)->fc;
++ if (IS_ACX111(adev))
++ printk("tx: pkt (%s): len %d "
++ "rate %04X%s status %u\n",
++ acx_get_packet_type_string(le16_to_cpu(fc)), len,
++ le16_to_cpu(txdesc->u.r2.rate111),
++ (le16_to_cpu(txdesc->u.r2.rate111) & RATE111_SHORTPRE) ? "(SPr)" : "",
++ adev->status);
++ else
++ printk("tx: pkt (%s): len %d rate %03u%s status %u\n",
++ acx_get_packet_type_string(fc), len,
++ txdesc->u.r1.rate,
++ (Ctl_8 & DESC_CTL_SHORT_PREAMBLE) ? "(SPr)" : "",
++ adev->status);
++
++ if (acx_debug & L_DATA) {
++ printk("tx: 802.11 [%d]: ", len);
++ acx_dump_bytes(hostdesc1->data, len);
++ }
++ }
++end:
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acxpci_l_clean_txdesc
++**
++** This function resets the txdescs' status when the ACX100
++** signals the TX done IRQ (txdescs have been processed), starting with
++** the pool index of the descriptor which we would use next,
++** in order to make sure that we can be as fast as possible
++** in filling new txdescs.
++** Everytime we get called we know where the next packet to be cleaned is.
++*/
++
++#if !ACX_DEBUG
++static inline void log_txbuffer(const acx_device_t *adev) {}
++#else
++static void
++log_txbuffer(acx_device_t *adev)
++{
++ txdesc_t *txdesc;
++ int i;
++
++ /* no FN_ENTER here, we don't want that */
++ /* no locks here, since it's entirely non-critical code */
++ txdesc = adev->txdesc_start;
++ if (unlikely(!txdesc)) return;
++ printk("tx: desc->Ctl8's:");
++ for (i = 0; i < TX_CNT; i++) {
++ printk(" %02X", txdesc->Ctl_8);
++ txdesc = advance_txdesc(adev, txdesc, 1);
++ }
++ printk("\n");
++}
++#endif
++
++
++static void
++handle_tx_error(acx_device_t *adev, u8 error, unsigned int finger)
++{
++ const char *err = "unknown error";
++
++ /* hmm, should we handle this as a mask
++ * of *several* bits?
++ * For now I think only caring about
++ * individual bits is ok... */
++ switch (error) {
++ case 0x01:
++ err = "no Tx due to error in other fragment";
++ adev->wstats.discard.fragment++;
++ break;
++ case 0x02:
++ err = "Tx aborted";
++ adev->stats.tx_aborted_errors++;
++ break;
++ case 0x04:
++ err = "Tx desc wrong parameters";
++ adev->wstats.discard.misc++;
++ break;
++ case 0x08:
++ err = "WEP key not found";
++ adev->wstats.discard.misc++;
++ break;
++ case 0x10:
++ err = "MSDU lifetime timeout? - try changing "
++ "'iwconfig retry lifetime XXX'";
++ adev->wstats.discard.misc++;
++ break;
++ case 0x20:
++ err = "excessive Tx retries due to either distance "
++ "too high or unable to Tx or Tx frame error - "
++ "try changing 'iwconfig txpower XXX' or "
++ "'sens'itivity or 'retry'";
++ adev->wstats.discard.retries++;
++ /* Tx error 0x20 also seems to occur on
++ * overheating, so I'm not sure whether we
++ * actually want to do aggressive radio recalibration,
++ * since people maybe won't notice then that their hardware
++ * is slowly getting cooked...
++ * Or is it still a safe long distance from utter
++ * radio non-functionality despite many radio recalibs
++ * to final destructive overheating of the hardware?
++ * In this case we really should do recalib here...
++ * I guess the only way to find out is to do a
++ * potentially fatal self-experiment :-\
++ * Or maybe only recalib in case we're using Tx
++ * rate auto (on errors switching to lower speed
++ * --> less heat?) or 802.11 power save mode?
++ *
++ * ok, just do it. */
++ if (++adev->retry_errors_msg_ratelimit % 4 == 0) {
++ if (adev->retry_errors_msg_ratelimit <= 20) {
++ printk("%s: several excessive Tx "
++ "retry errors occurred, attempting "
++ "to recalibrate radio. Radio "
++ "drift might be caused by increasing "
++ "card temperature, please check the card "
++ "before it's too late!\n",
++ adev->ndev->name);
++ if (adev->retry_errors_msg_ratelimit == 20)
++ printk("disabling above message\n");
++ }
++
++ acx_schedule_task(adev, ACX_AFTER_IRQ_CMD_RADIO_RECALIB);
++ }
++ break;
++ case 0x40:
++ err = "Tx buffer overflow";
++ adev->stats.tx_fifo_errors++;
++ break;
++ case 0x80:
++ /* possibly ACPI C-state powersaving related!!!
++ * (DMA timeout due to excessively high wakeup
++ * latency after C-state activation!?)
++ * Disable C-State powersaving and try again,
++ * then PLEASE REPORT, I'm VERY interested in
++ * whether my theory is correct that this is
++ * actually the problem here.
++ * In that case, use new Linux idle wakeup latency
++ * requirements kernel API to prevent this issue. */
++ err = "DMA error";
++ adev->wstats.discard.misc++;
++ break;
++ }
++ adev->stats.tx_errors++;
++ if (adev->stats.tx_errors <= 20)
++ printk("%s: tx error 0x%02X, buf %02u! (%s)\n",
++ adev->ndev->name, error, finger, err);
++ else
++ printk("%s: tx error 0x%02X, buf %02u!\n",
++ adev->ndev->name, error, finger);
++}
++
++
++unsigned int
++acxpci_l_clean_txdesc(acx_device_t *adev)
++{
++ txdesc_t *txdesc;
++ unsigned finger;
++ int num_cleaned;
++ u16 r111;
++ u8 error, ack_failures, rts_failures, rts_ok, r100;
++
++ FN_ENTER;
++
++ if (unlikely(acx_debug & L_DEBUG))
++ log_txbuffer(adev);
++
++ log(L_BUFT, "tx: cleaning up bufs from %u\n", adev->tx_tail);
++
++ /* We know first descr which is not free yet. We advance it as far
++ ** as we see correct bits set in following descs (if next desc
++ ** is NOT free, we shouldn't advance at all). We know that in
++ ** front of tx_tail may be "holes" with isolated free descs.
++ ** We will catch up when all intermediate descs will be freed also */
++
++ finger = adev->tx_tail;
++ num_cleaned = 0;
++ while (likely(finger != adev->tx_head)) {
++ txdesc = get_txdesc(adev, finger);
++
++ /* If we allocated txdesc on tx path but then decided
++ ** to NOT use it, then it will be left as a free "bubble"
++ ** in the "allocated for tx" part of the ring.
++ ** We may meet it on the next ring pass here. */
++
++ /* stop if not marked as "tx finished" and "host owned" */
++ if ((txdesc->Ctl_8 & DESC_CTL_ACXDONE_HOSTOWN)
++ != DESC_CTL_ACXDONE_HOSTOWN) {
++ if (unlikely(!num_cleaned)) { /* maybe remove completely */
++ log(L_BUFT, "clean_txdesc: tail isn't free. "
++ "tail:%d head:%d\n",
++ adev->tx_tail, adev->tx_head);
++ }
++ break;
++ }
++
++ /* remember desc values... */
++ error = txdesc->error;
++ ack_failures = txdesc->ack_failures;
++ rts_failures = txdesc->rts_failures;
++ rts_ok = txdesc->rts_ok;
++ r100 = txdesc->u.r1.rate;
++ r111 = le16_to_cpu(txdesc->u.r2.rate111);
++
++ /* need to check for certain error conditions before we
++ * clean the descriptor: we still need valid descr data here */
++ if (unlikely(0x30 & error)) {
++ /* only send IWEVTXDROP in case of retry or lifetime exceeded;
++ * all other errors mean we screwed up locally */
++ union iwreq_data wrqu;
++ wlan_hdr_t *hdr;
++ txhostdesc_t *hostdesc;
++
++ hostdesc = get_txhostdesc(adev, txdesc);
++ hdr = (wlan_hdr_t *)hostdesc->data;
++ MAC_COPY(wrqu.addr.sa_data, hdr->a1);
++ wireless_send_event(adev->ndev, IWEVTXDROP, &wrqu, NULL);
++ }
++
++ /* ...and free the desc */
++ txdesc->error = 0;
++ txdesc->ack_failures = 0;
++ txdesc->rts_failures = 0;
++ txdesc->rts_ok = 0;
++ /* signal host owning it LAST, since ACX already knows that this
++ ** descriptor is finished since it set Ctl_8 accordingly. */
++ txdesc->Ctl_8 = DESC_CTL_HOSTOWN;
++
++ adev->tx_free++;
++ num_cleaned++;
++
++ if ((adev->tx_free >= TX_START_QUEUE)
++ && (adev->status == ACX_STATUS_4_ASSOCIATED)
++ && (acx_queue_stopped(adev->ndev))
++ ) {
++ log(L_BUF, "tx: wake queue (avail. Tx desc %u)\n",
++ adev->tx_free);
++ acx_wake_queue(adev->ndev, NULL);
++ }
++
++ /* do error checking, rate handling and logging
++ * AFTER having done the work, it's faster */
++
++ /* do rate handling */
++ if (adev->rate_auto) {
++ struct client *clt = get_txc(adev, txdesc);
++ if (clt) {
++ u16 cur = get_txr(adev, txdesc);
++ if (clt->rate_cur == cur) {
++ acx_l_handle_txrate_auto(adev, clt,
++ cur, /* intended rate */
++ r100, r111, /* actually used rate */
++ (error & 0x30), /* was there an error? */
++ TX_CNT + TX_CLEAN_BACKLOG - adev->tx_free);
++ }
++ }
++ }
++
++ if (unlikely(error))
++ handle_tx_error(adev, error, finger);
++
++ if (IS_ACX111(adev))
++ log(L_BUFT, "tx: cleaned %u: !ACK=%u !RTS=%u RTS=%u r111=%04X\n",
++ finger, ack_failures, rts_failures, rts_ok, r111);
++ else
++ log(L_BUFT, "tx: cleaned %u: !ACK=%u !RTS=%u RTS=%u rate=%u\n",
++ finger, ack_failures, rts_failures, rts_ok, r100);
++
++ /* update pointer for descr to be cleaned next */
++ finger = (finger + 1) % TX_CNT;
++ }
++
++ /* remember last position */
++ adev->tx_tail = finger;
++/* end: */
++ FN_EXIT1(num_cleaned);
++ return num_cleaned;
++}
++
++/* clean *all* Tx descriptors, and regardless of their previous state.
++ * Used for brute-force reset handling. */
++void
++acxpci_l_clean_txdesc_emergency(acx_device_t *adev)
++{
++ txdesc_t *txdesc;
++ int i;
++
++ FN_ENTER;
++
++ for (i = 0; i < TX_CNT; i++) {
++ txdesc = get_txdesc(adev, i);
++
++ /* free it */
++ txdesc->ack_failures = 0;
++ txdesc->rts_failures = 0;
++ txdesc->rts_ok = 0;
++ txdesc->error = 0;
++ txdesc->Ctl_8 = DESC_CTL_HOSTOWN;
++ }
++
++ adev->tx_free = TX_CNT;
++
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acxpci_s_create_tx_host_desc_queue
++*/
++
++static void*
++allocate(acx_device_t *adev, size_t size, dma_addr_t *phy, const char *msg)
++{
++ void *ptr;
++
++ ptr = dma_alloc_coherent(adev->pdev ? &adev->pdev->dev : NULL,
++ size, phy, GFP_KERNEL);
++
++ if (ptr) {
++ log(L_DEBUG, "%s sz=%d adr=0x%p phy=0x%08llx\n",
++ msg, (int)size, ptr, (unsigned long long)*phy);
++ memset(ptr, 0, size);
++ return ptr;
++ }
++ printk(KERN_ERR "acx: %s allocation FAILED (%d bytes)\n",
++ msg, (int)size);
++ return NULL;
++}
++
++
++static int
++acxpci_s_create_tx_host_desc_queue(acx_device_t *adev)
++{
++ txhostdesc_t *hostdesc;
++ u8 *txbuf;
++ dma_addr_t hostdesc_phy;
++ dma_addr_t txbuf_phy;
++ int i;
++
++ FN_ENTER;
++
++ /* allocate TX buffer */
++ adev->txbuf_area_size = TX_CNT * WLAN_A4FR_MAXLEN_WEP_FCS;
++ adev->txbuf_start = allocate(adev, adev->txbuf_area_size,
++ &adev->txbuf_startphy, "txbuf_start");
++ if (!adev->txbuf_start)
++ goto fail;
++
++ /* allocate the TX host descriptor queue pool */
++ adev->txhostdesc_area_size = TX_CNT * 2*sizeof(*hostdesc);
++ adev->txhostdesc_start = allocate(adev, adev->txhostdesc_area_size,
++ &adev->txhostdesc_startphy, "txhostdesc_start");
++ if (!adev->txhostdesc_start)
++ goto fail;
++ /* check for proper alignment of TX host descriptor pool */
++ if ((long) adev->txhostdesc_start & 3) {
++ printk("acx: driver bug: dma alloc returns unaligned address\n");
++ goto fail;
++ }
++
++ hostdesc = adev->txhostdesc_start;
++ hostdesc_phy = adev->txhostdesc_startphy;
++ txbuf = adev->txbuf_start;
++ txbuf_phy = adev->txbuf_startphy;
++
++#if 0
++/* Each tx buffer is accessed by hardware via
++** txdesc -> txhostdesc(s) -> txbuffer(s).
++** We use only one txhostdesc per txdesc, but it looks like
++** acx111 is buggy: it accesses second txhostdesc
++** (via hostdesc.desc_phy_next field) even if
++** txdesc->length == hostdesc->length and thus
++** entire packet was placed into first txhostdesc.
++** Due to this bug acx111 hangs unless second txhostdesc
++** has le16_to_cpu(hostdesc.length) = 3 (or larger)
++** Storing NULL into hostdesc.desc_phy_next
++** doesn't seem to help.
++**
++** Update: although it worked on Xterasys XN-2522g
++** with len=3 trick, WG311v2 is even more bogus, doesn't work.
++** Keeping this code (#ifdef'ed out) for documentational purposes.
++*/
++ for (i = 0; i < TX_CNT*2; i++) {
++ hostdesc_phy += sizeof(*hostdesc);
++ if (!(i & 1)) {
++ hostdesc->data_phy = cpu2acx(txbuf_phy);
++ /* hostdesc->data_offset = ... */
++ /* hostdesc->reserved = ... */
++ hostdesc->Ctl_16 = cpu_to_le16(DESC_CTL_HOSTOWN);
++ /* hostdesc->length = ... */
++ hostdesc->desc_phy_next = cpu2acx(hostdesc_phy);
++ hostdesc->pNext = ptr2acx(NULL);
++ /* hostdesc->Status = ... */
++ /* below: non-hardware fields */
++ hostdesc->data = txbuf;
++
++ txbuf += WLAN_A4FR_MAXLEN_WEP_FCS;
++ txbuf_phy += WLAN_A4FR_MAXLEN_WEP_FCS;
++ } else {
++ /* hostdesc->data_phy = ... */
++ /* hostdesc->data_offset = ... */
++ /* hostdesc->reserved = ... */
++ /* hostdesc->Ctl_16 = ... */
++ hostdesc->length = cpu_to_le16(3); /* bug workaround */
++ /* hostdesc->desc_phy_next = ... */
++ /* hostdesc->pNext = ... */
++ /* hostdesc->Status = ... */
++ /* below: non-hardware fields */
++ /* hostdesc->data = ... */
++ }
++ hostdesc++;
++ }
++#endif
++/* We initialize two hostdescs so that they point to adjacent
++** memory areas. Thus txbuf is really just a contiguous memory area */
++ for (i = 0; i < TX_CNT*2; i++) {
++ hostdesc_phy += sizeof(*hostdesc);
++
++ hostdesc->data_phy = cpu2acx(txbuf_phy);
++ /* done by memset(0): hostdesc->data_offset = 0; */
++ /* hostdesc->reserved = ... */
++ hostdesc->Ctl_16 = cpu_to_le16(DESC_CTL_HOSTOWN);
++ /* hostdesc->length = ... */
++ hostdesc->desc_phy_next = cpu2acx(hostdesc_phy);
++ /* done by memset(0): hostdesc->pNext = ptr2acx(NULL); */
++ /* hostdesc->Status = ... */
++ /* ->data is a non-hardware field: */
++ hostdesc->data = txbuf;
++
++ if (!(i & 1)) {
++ txbuf += WLAN_HDR_A3_LEN;
++ txbuf_phy += WLAN_HDR_A3_LEN;
++ } else {
++ txbuf += WLAN_A4FR_MAXLEN_WEP_FCS - WLAN_HDR_A3_LEN;
++ txbuf_phy += WLAN_A4FR_MAXLEN_WEP_FCS - WLAN_HDR_A3_LEN;
++ }
++ hostdesc++;
++ }
++ hostdesc--;
++ hostdesc->desc_phy_next = cpu2acx(adev->txhostdesc_startphy);
++
++ FN_EXIT1(OK);
++ return OK;
++fail:
++ printk("acx: create_tx_host_desc_queue FAILED\n");
++ /* dealloc will be done by free function on error case */
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++}
++
++
++/***************************************************************
++** acxpci_s_create_rx_host_desc_queue
++*/
++/* the whole size of a data buffer (header plus data body)
++ * plus 32 bytes safety offset at the end */
++#define RX_BUFFER_SIZE (sizeof(rxbuffer_t) + 32)
++
++static int
++acxpci_s_create_rx_host_desc_queue(acx_device_t *adev)
++{
++ rxhostdesc_t *hostdesc;
++ rxbuffer_t *rxbuf;
++ dma_addr_t hostdesc_phy;
++ dma_addr_t rxbuf_phy;
++ int i;
++
++ FN_ENTER;
++
++ /* allocate the RX host descriptor queue pool */
++ adev->rxhostdesc_area_size = RX_CNT * sizeof(*hostdesc);
++ adev->rxhostdesc_start = allocate(adev, adev->rxhostdesc_area_size,
++ &adev->rxhostdesc_startphy, "rxhostdesc_start");
++ if (!adev->rxhostdesc_start)
++ goto fail;
++ /* check for proper alignment of RX host descriptor pool */
++ if ((long) adev->rxhostdesc_start & 3) {
++ printk("acx: driver bug: dma alloc returns unaligned address\n");
++ goto fail;
++ }
++
++ /* allocate Rx buffer pool which will be used by the acx
++ * to store the whole content of the received frames in it */
++ adev->rxbuf_area_size = RX_CNT * RX_BUFFER_SIZE;
++ adev->rxbuf_start = allocate(adev, adev->rxbuf_area_size,
++ &adev->rxbuf_startphy, "rxbuf_start");
++ if (!adev->rxbuf_start)
++ goto fail;
++
++ rxbuf = adev->rxbuf_start;
++ rxbuf_phy = adev->rxbuf_startphy;
++ hostdesc = adev->rxhostdesc_start;
++ hostdesc_phy = adev->rxhostdesc_startphy;
++
++ /* don't make any popular C programming pointer arithmetic mistakes
++ * here, otherwise I'll kill you...
++ * (and don't dare asking me why I'm warning you about that...) */
++ for (i = 0; i < RX_CNT; i++) {
++ hostdesc->data = rxbuf;
++ hostdesc->data_phy = cpu2acx(rxbuf_phy);
++ hostdesc->length = cpu_to_le16(RX_BUFFER_SIZE);
++ CLEAR_BIT(hostdesc->Ctl_16, cpu_to_le16(DESC_CTL_HOSTOWN));
++ rxbuf++;
++ rxbuf_phy += sizeof(*rxbuf);
++ hostdesc_phy += sizeof(*hostdesc);
++ hostdesc->desc_phy_next = cpu2acx(hostdesc_phy);
++ hostdesc++;
++ }
++ hostdesc--;
++ hostdesc->desc_phy_next = cpu2acx(adev->rxhostdesc_startphy);
++ FN_EXIT1(OK);
++ return OK;
++fail:
++ printk("acx: create_rx_host_desc_queue FAILED\n");
++ /* dealloc will be done by free function on error case */
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++}
++
++
++/***************************************************************
++** acxpci_s_create_hostdesc_queues
++*/
++int
++acxpci_s_create_hostdesc_queues(acx_device_t *adev)
++{
++ int result;
++ result = acxpci_s_create_tx_host_desc_queue(adev);
++ if (OK != result) return result;
++ result = acxpci_s_create_rx_host_desc_queue(adev);
++ return result;
++}
++
++
++/***************************************************************
++** acxpci_create_tx_desc_queue
++*/
++static void
++acxpci_create_tx_desc_queue(acx_device_t *adev, u32 tx_queue_start)
++{
++ txdesc_t *txdesc;
++ txhostdesc_t *hostdesc;
++ dma_addr_t hostmemptr;
++ u32 mem_offs;
++ int i;
++
++ FN_ENTER;
++
++ if (IS_ACX100(adev))
++ adev->txdesc_size = sizeof(*txdesc);
++ else
++ /* the acx111 txdesc is 4 bytes larger */
++ adev->txdesc_size = sizeof(*txdesc) + 4;
++
++ adev->txdesc_start = (txdesc_t *) (adev->iobase2 + tx_queue_start);
++
++ log(L_DEBUG, "adev->iobase2=%p\n"
++ "tx_queue_start=%08X\n"
++ "adev->txdesc_start=%p\n",
++ adev->iobase2,
++ tx_queue_start,
++ adev->txdesc_start);
++
++ adev->tx_free = TX_CNT;
++ /* done by memset: adev->tx_head = 0; */
++ /* done by memset: adev->tx_tail = 0; */
++ txdesc = adev->txdesc_start;
++ mem_offs = tx_queue_start;
++ hostmemptr = adev->txhostdesc_startphy;
++ hostdesc = adev->txhostdesc_start;
++
++ if (IS_ACX111(adev)) {
++ /* ACX111 has a preinitialized Tx buffer! */
++ /* loop over whole send pool */
++ /* FIXME: do we have to do the hostmemptr stuff here?? */
++ for (i = 0; i < TX_CNT; i++) {
++ txdesc->HostMemPtr = ptr2acx(hostmemptr);
++ txdesc->Ctl_8 = DESC_CTL_HOSTOWN;
++ /* reserve two (hdr desc and payload desc) */
++ hostdesc += 2;
++ hostmemptr += 2 * sizeof(*hostdesc);
++ txdesc = advance_txdesc(adev, txdesc, 1);
++ }
++ } else {
++ /* ACX100 Tx buffer needs to be initialized by us */
++ /* clear whole send pool. sizeof is safe here (we are acx100) */
++ memset(adev->txdesc_start, 0, TX_CNT * sizeof(*txdesc));
++
++ /* loop over whole send pool */
++ for (i = 0; i < TX_CNT; i++) {
++ log(L_DEBUG, "configure card tx descriptor: 0x%p, "
++ "size: 0x%X\n", txdesc, adev->txdesc_size);
++
++ /* pointer to hostdesc memory */
++ txdesc->HostMemPtr = ptr2acx(hostmemptr);
++ /* initialise ctl */
++ txdesc->Ctl_8 = ( DESC_CTL_HOSTOWN | DESC_CTL_RECLAIM
++ | DESC_CTL_AUTODMA | DESC_CTL_FIRSTFRAG);
++ /* done by memset(0): txdesc->Ctl2_8 = 0; */
++ /* point to next txdesc */
++ txdesc->pNextDesc = cpu2acx(mem_offs + adev->txdesc_size);
++ /* reserve two (hdr desc and payload desc) */
++ hostdesc += 2;
++ hostmemptr += 2 * sizeof(*hostdesc);
++ /* go to the next one */
++ mem_offs += adev->txdesc_size;
++ /* ++ is safe here (we are acx100) */
++ txdesc++;
++ }
++ /* go back to the last one */
++ txdesc--;
++ /* and point to the first making it a ring buffer */
++ txdesc->pNextDesc = cpu2acx(tx_queue_start);
++ }
++ FN_EXIT0;
++}
++
++
++/***************************************************************
++** acxpci_create_rx_desc_queue
++*/
++static void
++acxpci_create_rx_desc_queue(acx_device_t *adev, u32 rx_queue_start)
++{
++ rxdesc_t *rxdesc;
++ u32 mem_offs;
++ int i;
++
++ FN_ENTER;
++
++ /* done by memset: adev->rx_tail = 0; */
++
++ /* ACX111 doesn't need any further config: preconfigures itself.
++ * Simply print ring buffer for debugging */
++ if (IS_ACX111(adev)) {
++ /* rxdesc_start already set here */
++
++ adev->rxdesc_start = (rxdesc_t *) ((u8 *)adev->iobase2 + rx_queue_start);
++
++ rxdesc = adev->rxdesc_start;
++ for (i = 0; i < RX_CNT; i++) {
++ log(L_DEBUG, "rx descriptor %d @ 0x%p\n", i, rxdesc);
++ rxdesc = adev->rxdesc_start = (rxdesc_t *)
++ (adev->iobase2 + acx2cpu(rxdesc->pNextDesc));
++ }
++ } else {
++ /* we didn't pre-calculate rxdesc_start in case of ACX100 */
++ /* rxdesc_start should be right AFTER Tx pool */
++ adev->rxdesc_start = (rxdesc_t *)
++ ((u8 *) adev->txdesc_start + (TX_CNT * sizeof(txdesc_t)));
++ /* NB: sizeof(txdesc_t) above is valid because we know
++ ** we are in if (acx100) block. Beware of cut-n-pasting elsewhere!
++ ** acx111's txdesc is larger! */
++
++ memset(adev->rxdesc_start, 0, RX_CNT * sizeof(*rxdesc));
++
++ /* loop over whole receive pool */
++ rxdesc = adev->rxdesc_start;
++ mem_offs = rx_queue_start;
++ for (i = 0; i < RX_CNT; i++) {
++ log(L_DEBUG, "rx descriptor @ 0x%p\n", rxdesc);
++ rxdesc->Ctl_8 = DESC_CTL_RECLAIM | DESC_CTL_AUTODMA;
++ /* point to next rxdesc */
++ rxdesc->pNextDesc = cpu2acx(mem_offs + sizeof(*rxdesc));
++ /* go to the next one */
++ mem_offs += sizeof(*rxdesc);
++ rxdesc++;
++ }
++ /* go to the last one */
++ rxdesc--;
++
++ /* and point to the first making it a ring buffer */
++ rxdesc->pNextDesc = cpu2acx(rx_queue_start);
++ }
++ FN_EXIT0;
++}
++
++
++/***************************************************************
++** acxpci_create_desc_queues
++*/
++void
++acxpci_create_desc_queues(acx_device_t *adev, u32 tx_queue_start, u32 rx_queue_start)
++{
++ acxpci_create_tx_desc_queue(adev, tx_queue_start);
++ acxpci_create_rx_desc_queue(adev, rx_queue_start);
++}
++
++
++/***************************************************************
++** acxpci_s_proc_diag_output
++*/
++char*
++acxpci_s_proc_diag_output(char *p, acx_device_t *adev)
++{
++ const char *rtl, *thd, *ttl;
++ rxhostdesc_t *rxhostdesc;
++ txdesc_t *txdesc;
++ int i;
++
++ FN_ENTER;
++
++ p += sprintf(p, "** Rx buf **\n");
++ rxhostdesc = adev->rxhostdesc_start;
++ if (rxhostdesc) for (i = 0; i < RX_CNT; i++) {
++ rtl = (i == adev->rx_tail) ? " [tail]" : "";
++ if ((rxhostdesc->Ctl_16 & cpu_to_le16(DESC_CTL_HOSTOWN))
++ && (rxhostdesc->Status & cpu_to_le32(DESC_STATUS_FULL)) )
++ p += sprintf(p, "%02u FULL%s\n", i, rtl);
++ else
++ p += sprintf(p, "%02u empty%s\n", i, rtl);
++ rxhostdesc++;
++ }
++ p += sprintf(p, "** Tx buf (free %d, Linux netqueue %s) **\n", adev->tx_free,
++ acx_queue_stopped(adev->ndev) ? "STOPPED" : "running");
++ txdesc = adev->txdesc_start;
++ if (txdesc) for (i = 0; i < TX_CNT; i++) {
++ thd = (i == adev->tx_head) ? " [head]" : "";
++ ttl = (i == adev->tx_tail) ? " [tail]" : "";
++ if (txdesc->Ctl_8 & DESC_CTL_ACXDONE)
++ p += sprintf(p, "%02u free (%02X)%s%s\n", i, txdesc->Ctl_8, thd, ttl);
++ else
++ p += sprintf(p, "%02u tx (%02X)%s%s\n", i, txdesc->Ctl_8, thd, ttl);
++ txdesc = advance_txdesc(adev, txdesc, 1);
++ }
++ p += sprintf(p,
++ "\n"
++ "** PCI data **\n"
++ "txbuf_start %p, txbuf_area_size %u, txbuf_startphy %08llx\n"
++ "txdesc_size %u, txdesc_start %p\n"
++ "txhostdesc_start %p, txhostdesc_area_size %u, txhostdesc_startphy %08llx\n"
++ "rxdesc_start %p\n"
++ "rxhostdesc_start %p, rxhostdesc_area_size %u, rxhostdesc_startphy %08llx\n"
++ "rxbuf_start %p, rxbuf_area_size %u, rxbuf_startphy %08llx\n",
++ adev->txbuf_start, adev->txbuf_area_size,
++ (unsigned long long)adev->txbuf_startphy,
++ adev->txdesc_size, adev->txdesc_start,
++ adev->txhostdesc_start, adev->txhostdesc_area_size,
++ (unsigned long long)adev->txhostdesc_startphy,
++ adev->rxdesc_start,
++ adev->rxhostdesc_start, adev->rxhostdesc_area_size,
++ (unsigned long long)adev->rxhostdesc_startphy,
++ adev->rxbuf_start, adev->rxbuf_area_size,
++ (unsigned long long)adev->rxbuf_startphy);
++
++ FN_EXIT0;
++ return p;
++}
++
++
++/***********************************************************************
++*/
++int
++acxpci_proc_eeprom_output(char *buf, acx_device_t *adev)
++{
++ char *p = buf;
++ int i;
++
++ FN_ENTER;
++
++ for (i = 0; i < 0x400; i++) {
++ acxpci_read_eeprom_byte(adev, i, p++);
++ }
++
++ FN_EXIT1(p - buf);
++ return p - buf;
++}
++
++
++/***********************************************************************
++*/
++void
++acxpci_set_interrupt_mask(acx_device_t *adev)
++{
++ if (IS_ACX111(adev)) {
++ adev->irq_mask = (u16) ~(0
++ /* | HOST_INT_RX_DATA */
++ | HOST_INT_TX_COMPLETE
++ /* | HOST_INT_TX_XFER */
++ | HOST_INT_RX_COMPLETE
++ /* | HOST_INT_DTIM */
++ /* | HOST_INT_BEACON */
++ /* | HOST_INT_TIMER */
++ /* | HOST_INT_KEY_NOT_FOUND */
++ | HOST_INT_IV_ICV_FAILURE
++ | HOST_INT_CMD_COMPLETE
++ | HOST_INT_INFO
++ /* | HOST_INT_OVERFLOW */
++ /* | HOST_INT_PROCESS_ERROR */
++ | HOST_INT_SCAN_COMPLETE
++ | HOST_INT_FCS_THRESHOLD
++ /* | HOST_INT_UNKNOWN */
++ );
++ /* Or else acx100 won't signal cmd completion, right? */
++ adev->irq_mask_off = (u16)~( HOST_INT_CMD_COMPLETE ); /* 0xfdff */
++ } else {
++ adev->irq_mask = (u16) ~(0
++ /* | HOST_INT_RX_DATA */
++ | HOST_INT_TX_COMPLETE
++ /* | HOST_INT_TX_XFER */
++ | HOST_INT_RX_COMPLETE
++ /* | HOST_INT_DTIM */
++ /* | HOST_INT_BEACON */
++ /* | HOST_INT_TIMER */
++ /* | HOST_INT_KEY_NOT_FOUND */
++ /* | HOST_INT_IV_ICV_FAILURE */
++ | HOST_INT_CMD_COMPLETE
++ | HOST_INT_INFO
++ /* | HOST_INT_OVERFLOW */
++ /* | HOST_INT_PROCESS_ERROR */
++ | HOST_INT_SCAN_COMPLETE
++ /* | HOST_INT_FCS_THRESHOLD */
++ /* | HOST_INT_UNKNOWN */
++ );
++ adev->irq_mask_off = (u16)~( HOST_INT_UNKNOWN ); /* 0x7fff */
++ }
++}
++
++
++/***********************************************************************
++*/
++int
++acx100pci_s_set_tx_level(acx_device_t *adev, u8 level_dbm)
++{
++ /* since it can be assumed that at least the Maxim radio has a
++ * maximum power output of 20dBm and since it also can be
++ * assumed that these values drive the DAC responsible for
++ * setting the linear Tx level, I'd guess that these values
++ * should be the corresponding linear values for a dBm value,
++ * in other words: calculate the values from that formula:
++ * Y [dBm] = 10 * log (X [mW])
++ * then scale the 0..63 value range onto the 1..100mW range (0..20 dBm)
++ * and you're done...
++ * Hopefully that's ok, but you never know if we're actually
++ * right... (especially since Windows XP doesn't seem to show
++ * actual Tx dBm values :-P) */
++
++ /* NOTE: on Maxim, value 30 IS 30mW, and value 10 IS 10mW - so the
++ * values are EXACTLY mW!!! Not sure about RFMD and others,
++ * though... */
++ static const u8 dbm2val_maxim[21] = {
++ 63, 63, 63, 62,
++ 61, 61, 60, 60,
++ 59, 58, 57, 55,
++ 53, 50, 47, 43,
++ 38, 31, 23, 13,
++ 0
++ };
++ static const u8 dbm2val_rfmd[21] = {
++ 0, 0, 0, 1,
++ 2, 2, 3, 3,
++ 4, 5, 6, 8,
++ 10, 13, 16, 20,
++ 25, 32, 41, 50,
++ 63
++ };
++ const u8 *table;
++
++ switch (adev->radio_type) {
++ case RADIO_MAXIM_0D:
++ table = &dbm2val_maxim[0];
++ break;
++ case RADIO_RFMD_11:
++ case RADIO_RALINK_15:
++ table = &dbm2val_rfmd[0];
++ break;
++ default:
++ printk("%s: unknown/unsupported radio type, "
++ "cannot modify tx power level yet!\n",
++ adev->ndev->name);
++ return NOT_OK;
++ }
++ printk("%s: changing radio power level to %u dBm (%u)\n",
++ adev->ndev->name, level_dbm, table[level_dbm]);
++ acxpci_s_write_phy_reg(adev, 0x11, table[level_dbm]);
++ return OK;
++}
++
++
++/***********************************************************************
++** Data for init_module/cleanup_module
++*/
++static const struct pci_device_id
++acxpci_id_tbl[] __devinitdata = {
++ {
++ .vendor = PCI_VENDOR_ID_TI,
++ .device = PCI_DEVICE_ID_TI_TNETW1100A,
++ .subvendor = PCI_ANY_ID,
++ .subdevice = PCI_ANY_ID,
++ .driver_data = CHIPTYPE_ACX100,
++ },
++ {
++ .vendor = PCI_VENDOR_ID_TI,
++ .device = PCI_DEVICE_ID_TI_TNETW1100B,
++ .subvendor = PCI_ANY_ID,
++ .subdevice = PCI_ANY_ID,
++ .driver_data = CHIPTYPE_ACX100,
++ },
++ {
++ .vendor = PCI_VENDOR_ID_TI,
++ .device = PCI_DEVICE_ID_TI_TNETW1130,
++ .subvendor = PCI_ANY_ID,
++ .subdevice = PCI_ANY_ID,
++ .driver_data = CHIPTYPE_ACX111,
++ },
++ {
++ .vendor = 0,
++ .device = 0,
++ .subvendor = 0,
++ .subdevice = 0,
++ .driver_data = 0,
++ }
++};
++
++MODULE_DEVICE_TABLE(pci, acxpci_id_tbl);
++
++/* FIXME: checks should be removed once driver is included in the kernel */
++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 11)
++/* pci_name() got introduced at start of 2.6.x,
++ * got mandatory (slot_name member removed) in 2.6.11-bk1 */
++#define pci_name(x) x->slot_name
++#endif
++
++static struct pci_driver
++acxpci_drv_id = {
++ .name = "acx_pci",
++ .id_table = acxpci_id_tbl,
++ .probe = acxpci_e_probe,
++ .remove = __devexit_p(acxpci_e_remove),
++#ifdef CONFIG_PM
++ .suspend = acxpci_e_suspend,
++ .resume = acxpci_e_resume
++#endif /* CONFIG_PM */
++};
++
++
++/***********************************************************************
++** acxpci_e_init_module
++**
++** Module initialization routine, called once at module load time
++*/
++int __init
++acxpci_e_init_module(void)
++{
++ int res;
++
++ FN_ENTER;
++
++#if (ACX_IO_WIDTH==32)
++ printk("acx: compiled to use 32bit I/O access. "
++ "I/O timing issues might occur, such as "
++ "non-working firmware upload. Report them\n");
++#else
++ printk("acx: compiled to use 16bit I/O access only "
++ "(compatibility mode)\n");
++#endif
++
++#ifdef __LITTLE_ENDIAN
++#define ENDIANNESS_STRING "running on a little-endian CPU\n"
++#else
++#define ENDIANNESS_STRING "running on a BIG-ENDIAN CPU\n"
++#endif
++ log(L_INIT,
++ ENDIANNESS_STRING
++ "PCI module " ACX_RELEASE " initialized, "
++ "waiting for cards to probe...\n"
++ );
++
++ res = pci_register_driver(&acxpci_drv_id);
++ FN_EXIT1(res);
++ return res;
++}
++
++
++/***********************************************************************
++** acxpci_e_cleanup_module
++**
++** Called at module unload time. This is our last chance to
++** clean up after ourselves.
++*/
++void __exit
++acxpci_e_cleanup_module(void)
++{
++ FN_ENTER;
++
++ pci_unregister_driver(&acxpci_drv_id);
++
++ FN_EXIT0;
++}
+Index: linux-2.6.23/drivers/net/wireless/acx/rx3000_acx.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23/drivers/net/wireless/acx/rx3000_acx.c 2008-01-20 21:13:40.000000000 +0000
+@@ -0,0 +1,110 @@
++/*
++ * WLAN (TI TNETW1100B) support in the HP iPAQ RX3000
++ *
++ * Copyright (c) 2006 SDG Systems, LLC
++ * Copyright (c) 2006 Roman Moravcik
++ *
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License. See the file COPYING in the main directory of this archive for
++ * more details.
++ *
++ * Based on hx4700_acx.c
++ */
++
++
++#include <linux/kernel.h>
++#include <linux/platform_device.h>
++#include <linux/delay.h>
++#include <linux/dpm.h>
++#include <linux/leds.h>
++
++#include <asm/hardware.h>
++
++#include <asm/arch/regs-gpio.h>
++#include <linux/mfd/asic3_base.h>
++#include <asm/arch/rx3000.h>
++#include <asm/arch/rx3000-asic3.h>
++#include <asm/io.h>
++
++#include "acx_hw.h"
++
++extern struct platform_device s3c_device_asic3;
++
++static int rx3000_wlan_start(void)
++{
++ DPM_DEBUG("rx3000_acx: Turning on\n");
++ asic3_set_gpio_out_b(&s3c_device_asic3.dev, ASIC3_GPB3, ASIC3_GPB3);
++ mdelay(20);
++ asic3_set_gpio_out_c(&s3c_device_asic3.dev, ASIC3_GPC13, ASIC3_GPC13);
++ mdelay(20);
++ asic3_set_gpio_out_c(&s3c_device_asic3.dev, ASIC3_GPC11, ASIC3_GPC11);
++ mdelay(100);
++ asic3_set_gpio_out_b(&s3c_device_asic3.dev, ASIC3_GPB3, ASIC3_GPB3);
++ mdelay(20);
++ s3c2410_gpio_cfgpin(S3C2410_GPA15, S3C2410_GPA15_nGCS4);
++ mdelay(100);
++ s3c2410_gpio_setpin(S3C2410_GPA11, 0);
++ mdelay(50);
++ s3c2410_gpio_setpin(S3C2410_GPA11, 1);
++ led_trigger_event_shared(rx3000_radio_trig, LED_FULL);
++ return 0;
++}
++
++static int rx3000_wlan_stop(void)
++{
++ DPM_DEBUG("rx3000_acx: Turning off\n");
++ s3c2410_gpio_setpin(S3C2410_GPA15, 1);
++ s3c2410_gpio_cfgpin(S3C2410_GPA15, S3C2410_GPA15_OUT);
++ asic3_set_gpio_out_b(&s3c_device_asic3.dev, ASIC3_GPB3, 0);
++ asic3_set_gpio_out_c(&s3c_device_asic3.dev, ASIC3_GPC13, 0);
++ asic3_set_gpio_out_c(&s3c_device_asic3.dev, ASIC3_GPC11, 0);
++ led_trigger_event_shared(rx3000_radio_trig, LED_OFF);
++ return 0;
++}
++
++static struct resource acx_resources[] = {
++ [0] = {
++ .start = RX3000_PA_WLAN,
++ .end = RX3000_PA_WLAN + 0x20,
++ .flags = IORESOURCE_MEM,
++ },
++ [1] = {
++ .start = IRQ_EINT16,
++ .end = IRQ_EINT16,
++ .flags = IORESOURCE_IRQ,
++ },
++};
++
++static struct acx_hardware_data acx_data = {
++ .start_hw = rx3000_wlan_start,
++ .stop_hw = rx3000_wlan_stop,
++};
++
++static struct platform_device acx_device = {
++ .name = "acx-mem",
++ .dev = {
++ .platform_data = &acx_data,
++ },
++ .num_resources = ARRAY_SIZE(acx_resources),
++ .resource = acx_resources,
++};
++
++static int __init rx3000_wlan_init(void)
++{
++ printk("rx3000_wlan_init: acx-mem platform_device_register\n");
++ return platform_device_register(&acx_device);
++}
++
++
++static void __exit rx3000_wlan_exit(void)
++{
++ platform_device_unregister(&acx_device);
++}
++
++module_init(rx3000_wlan_init);
++module_exit(rx3000_wlan_exit);
++
++MODULE_AUTHOR("Todd Blumer <todd@sdgsystems.com>, Roman Moravcik <roman.moravcik@gmail.com>");
++MODULE_DESCRIPTION("WLAN driver for HP iPAQ RX3000");
++MODULE_LICENSE("GPL");
++
+Index: linux-2.6.23/drivers/net/wireless/acx/setrate.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23/drivers/net/wireless/acx/setrate.c 2008-01-20 21:13:40.000000000 +0000
+@@ -0,0 +1,213 @@
++/* TODO: stop #including, move into wireless.c
++ * until then, keep in sync copies in prism54/ and acx/ dirs
++ * code+data size: less than 1k */
++
++enum {
++ DOT11_RATE_1,
++ DOT11_RATE_2,
++ DOT11_RATE_5,
++ DOT11_RATE_11,
++ DOT11_RATE_22,
++ DOT11_RATE_33,
++ DOT11_RATE_6,
++ DOT11_RATE_9,
++ DOT11_RATE_12,
++ DOT11_RATE_18,
++ DOT11_RATE_24,
++ DOT11_RATE_36,
++ DOT11_RATE_48,
++ DOT11_RATE_54
++};
++enum {
++ DOT11_MOD_DBPSK,
++ DOT11_MOD_DQPSK,
++ DOT11_MOD_CCK,
++ DOT11_MOD_OFDM,
++ DOT11_MOD_CCKOFDM,
++ DOT11_MOD_PBCC
++};
++static const u8 ratelist[] = { 1,2,5,11,22,33,6,9,12,18,24,36,48,54 };
++static const u8 dot11ratebyte[] = { 1*2,2*2,11,11*2,22*2,33*2,6*2,9*2,12*2,18*2,24*2,36*2,48*2,54*2 };
++static const u8 default_modulation[] = {
++ DOT11_MOD_DBPSK,
++ DOT11_MOD_DQPSK,
++ DOT11_MOD_CCK,
++ DOT11_MOD_CCK,
++ DOT11_MOD_PBCC,
++ DOT11_MOD_PBCC,
++ DOT11_MOD_OFDM,
++ DOT11_MOD_OFDM,
++ DOT11_MOD_OFDM,
++ DOT11_MOD_OFDM,
++ DOT11_MOD_OFDM,
++ DOT11_MOD_OFDM,
++ DOT11_MOD_OFDM,
++ DOT11_MOD_OFDM
++};
++
++static /* TODO: remove 'static' when moved to wireless.c */
++int
++rate_mbit2enum(int n) {
++ int i=0;
++ while(i<sizeof(ratelist)) {
++ if(n==ratelist[i]) return i;
++ i++;
++ }
++ return -EINVAL;
++}
++
++static int
++get_modulation(int r_enum, char suffix) {
++ if(suffix==',' || suffix==' ' || suffix=='\0') {
++ /* could shorten default_mod by 8 bytes:
++ if(r_enum>=DOT11_RATE_6) return DOT11_MOD_OFDM; */
++ return default_modulation[r_enum];
++ }
++ if(suffix=='c') {
++ if(r_enum<DOT11_RATE_5 || r_enum>DOT11_RATE_11) return -EINVAL;
++ return DOT11_MOD_CCK;
++ }
++ if(suffix=='p') {
++ if(r_enum<DOT11_RATE_5 || r_enum>DOT11_RATE_33) return -EINVAL;
++ return DOT11_MOD_PBCC;
++ }
++ if(suffix=='o') {
++ if(r_enum<DOT11_RATE_6) return -EINVAL;
++ return DOT11_MOD_OFDM;
++ }
++ if(suffix=='d') {
++ if(r_enum<DOT11_RATE_6) return -EINVAL;
++ return DOT11_MOD_CCKOFDM;
++ }
++ return -EINVAL;
++}
++
++#ifdef UNUSED
++static int
++fill_ratevector(const char **pstr, u8 *vector, int size,
++ int (*supported)(int mbit, int mod, void *opaque), void *opaque, int or_mask)
++{
++ unsigned long rate_mbit;
++ int rate_enum,mod;
++ const char *str = *pstr;
++ char c;
++
++ do {
++ rate_mbit = simple_strtoul(str, (char**)&str, 10);
++ if(rate_mbit>INT_MAX) return -EINVAL;
++
++ rate_enum = rate_mbit2enum(rate_mbit);
++ if(rate_enum<0) return rate_enum;
++
++ c = *str;
++ mod = get_modulation(rate_enum, c);
++ if(mod<0) return mod;
++
++ if(c>='a' && c<='z') c = *++str;
++ if(c!=',' && c!=' ' && c!='\0') return -EINVAL;
++
++ if(supported) {
++ int r = supported(rate_mbit, mod, opaque);
++ if(r) return r;
++ }
++
++ *vector++ = dot11ratebyte[rate_enum] | or_mask;
++
++ size--;
++ str++;
++ } while(size>0 && c==',');
++
++ if(size<1) return -E2BIG;
++ *vector=0; /* TODO: sort, remove dups? */
++
++ *pstr = str-1;
++ return 0;
++}
++
++static /* TODO: remove 'static' when moved to wireless.c */
++int
++fill_ratevectors(const char *str, u8 *brate, u8 *orate, int size,
++ int (*supported)(int mbit, int mod, void *opaque), void *opaque)
++{
++ int r;
++
++ r = fill_ratevector(&str, brate, size, supported, opaque, 0x80);
++ if(r) return r;
++
++ orate[0] = 0;
++ if(*str==' ') {
++ str++;
++ r = fill_ratevector(&str, orate, size, supported, opaque, 0);
++ if(r) return r;
++ /* TODO: sanitize, e.g. remove/error on rates already in basic rate set? */
++ }
++ if(*str)
++ return -EINVAL;
++
++ return 0;
++}
++#endif
++
++/* TODO: use u64 masks? */
++
++static int
++fill_ratemask(const char **pstr, u32* mask,
++ int (*supported)(int mbit, int mod,void *opaque),
++ u32 (*gen_mask)(int mbit, int mod,void *opaque),
++ void *opaque)
++{
++ unsigned long rate_mbit;
++ int rate_enum,mod;
++ u32 m = 0;
++ const char *str = *pstr;
++ char c;
++
++ do {
++ rate_mbit = simple_strtoul(str, (char**)&str, 10);
++ if(rate_mbit>INT_MAX) return -EINVAL;
++
++ rate_enum = rate_mbit2enum(rate_mbit);
++ if(rate_enum<0) return rate_enum;
++
++ c = *str;
++ mod = get_modulation(rate_enum, c);
++ if(mod<0) return mod;
++
++ if(c>='a' && c<='z') c = *++str;
++ if(c!=',' && c!=' ' && c!='\0') return -EINVAL;
++
++ if(supported) {
++ int r = supported(rate_mbit, mod, opaque);
++ if(r) return r;
++ }
++
++ m |= gen_mask(rate_mbit, mod, opaque);
++ str++;
++ } while(c==',');
++
++ *pstr = str-1;
++ *mask |= m;
++ return 0;
++}
++
++static /* TODO: remove 'static' when moved to wireless.c */
++int
++fill_ratemasks(const char *str, u32 *bmask, u32 *omask,
++ int (*supported)(int mbit, int mod,void *opaque),
++ u32 (*gen_mask)(int mbit, int mod,void *opaque),
++ void *opaque)
++{
++ int r;
++
++ r = fill_ratemask(&str, bmask, supported, gen_mask, opaque);
++ if(r) return r;
++
++ if(*str==' ') {
++ str++;
++ r = fill_ratemask(&str, omask, supported, gen_mask, opaque);
++ if(r) return r;
++ }
++ if(*str)
++ return -EINVAL;
++ return 0;
++}
+Index: linux-2.6.23/drivers/net/wireless/acx/usb.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23/drivers/net/wireless/acx/usb.c 2008-01-20 21:13:40.000000000 +0000
+@@ -0,0 +1,1922 @@
++/***********************************************************************
++** Copyright (C) 2003 ACX100 Open Source Project
++**
++** The contents of this file are subject to the Mozilla Public
++** License Version 1.1 (the "License"); you may not use this file
++** except in compliance with the License. You may obtain a copy of
++** the License at http://www.mozilla.org/MPL/
++**
++** Software distributed under the License is distributed on an "AS
++** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
++** implied. See the License for the specific language governing
++** rights and limitations under the License.
++**
++** Alternatively, the contents of this file may be used under the
++** terms of the GNU Public License version 2 (the "GPL"), in which
++** case the provisions of the GPL are applicable instead of the
++** above. If you wish to allow the use of your version of this file
++** only under the terms of the GPL and not to allow others to use
++** your version of this file under the MPL, indicate your decision
++** by deleting the provisions above and replace them with the notice
++** and other provisions required by the GPL. If you do not delete
++** the provisions above, a recipient may use your version of this
++** file under either the MPL or the GPL.
++** ---------------------------------------------------------------------
++** Inquiries regarding the ACX100 Open Source Project can be
++** made directly to:
++**
++** acx100-users@lists.sf.net
++** http://acx100.sf.net
++** ---------------------------------------------------------------------
++*/
++
++/***********************************************************************
++** USB support for TI ACX100 based devices. Many parts are taken from
++** the PCI driver.
++**
++** Authors:
++** Martin Wawro <martin.wawro AT uni-dortmund.de>
++** Andreas Mohr <andi AT lisas.de>
++**
++** LOCKING
++** callback functions called by USB core are running in interrupt context
++** and thus have names with _i_.
++*/
++#define ACX_USB 1
++
++#include <linux/version.h>
++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18)
++#include <linux/config.h>
++#endif
++#include <linux/types.h>
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/kernel.h>
++#include <linux/usb.h>
++#include <linux/netdevice.h>
++#include <linux/rtnetlink.h>
++#include <linux/etherdevice.h>
++#include <linux/wireless.h>
++#include <net/iw_handler.h>
++#include <linux/vmalloc.h>
++
++#include "acx.h"
++
++
++/***********************************************************************
++*/
++/* number of endpoints of an interface */
++#define NUM_EP(intf) (intf)->altsetting[0].desc.bNumEndpoints
++#define EP(intf, nr) (intf)->altsetting[0].endpoint[(nr)].desc
++#define GET_DEV(udev) usb_get_dev((udev))
++#define PUT_DEV(udev) usb_put_dev((udev))
++#define SET_NETDEV_OWNER(ndev, owner) /* not needed anymore ??? */
++
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,14)
++/* removed in 2.6.14. We will use fake value for now */
++#define URB_ASYNC_UNLINK 0
++#endif
++
++
++/***********************************************************************
++*/
++/* ACX100 (TNETW1100) USB device: D-Link DWL-120+ */
++#define ACX100_VENDOR_ID 0x2001
++#define ACX100_PRODUCT_ID_UNBOOTED 0x3B01
++#define ACX100_PRODUCT_ID_BOOTED 0x3B00
++
++/* TNETW1450 USB devices */
++#define VENDOR_ID_DLINK 0x07b8 /* D-Link Corp. */
++#define PRODUCT_ID_WUG2400 0xb21a /* AboCom WUG2400 or SafeCom SWLUT-54125 */
++#define VENDOR_ID_AVM_GMBH 0x057c
++#define PRODUCT_ID_AVM_WLAN_USB 0x5601
++#define PRODUCT_ID_AVM_WLAN_USB_si 0x6201 /* "self install" named Version: driver kills kernel on inbound scans from fritz box ??? */
++#define VENDOR_ID_ZCOM 0x0cde
++#define PRODUCT_ID_ZCOM_XG750 0x0017 /* not tested yet */
++#define VENDOR_ID_TI 0x0451
++#define PRODUCT_ID_TI_UNKNOWN 0x60c5 /* not tested yet */
++
++#define ACX_USB_CTRL_TIMEOUT 5500 /* steps in ms */
++
++/* Buffer size for fw upload, same for both ACX100 USB and TNETW1450 */
++#define USB_RWMEM_MAXLEN 2048
++
++/* The number of bulk URBs to use */
++#define ACX_TX_URB_CNT 8
++#define ACX_RX_URB_CNT 2
++
++/* Should be sent to the bulkout endpoint */
++#define ACX_USB_REQ_UPLOAD_FW 0x10
++#define ACX_USB_REQ_ACK_CS 0x11
++#define ACX_USB_REQ_CMD 0x12
++
++/***********************************************************************
++** Prototypes
++*/
++static int acxusb_e_probe(struct usb_interface *, const struct usb_device_id *);
++static void acxusb_e_disconnect(struct usb_interface *);
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
++static void acxusb_i_complete_tx(struct urb *);
++static void acxusb_i_complete_rx(struct urb *);
++#else
++static void acxusb_i_complete_tx(struct urb *, struct pt_regs *);
++static void acxusb_i_complete_rx(struct urb *, struct pt_regs *);
++#endif
++static int acxusb_e_open(struct net_device *);
++static int acxusb_e_close(struct net_device *);
++static void acxusb_i_set_rx_mode(struct net_device *);
++static int acxusb_boot(struct usb_device *, int is_tnetw1450, int *radio_type);
++
++static void acxusb_l_poll_rx(acx_device_t *adev, usb_rx_t* rx);
++
++static void acxusb_i_tx_timeout(struct net_device *);
++
++/* static void dump_device(struct usb_device *); */
++/* static void dump_device_descriptor(struct usb_device_descriptor *); */
++/* static void dump_config_descriptor(struct usb_config_descriptor *); */
++
++/***********************************************************************
++** Module Data
++*/
++#define TXBUFSIZE sizeof(usb_txbuffer_t)
++/*
++ * Now, this is just plain lying, but the device insists in giving us
++ * huge packets. We supply extra space after rxbuffer. Need to understand
++ * it better...
++ */
++#define RXBUFSIZE (sizeof(rxbuffer_t) + \
++ (sizeof(usb_rx_t) - sizeof(struct usb_rx_plain)))
++
++static const struct usb_device_id
++acxusb_ids[] = {
++ { USB_DEVICE(ACX100_VENDOR_ID, ACX100_PRODUCT_ID_BOOTED) },
++ { USB_DEVICE(ACX100_VENDOR_ID, ACX100_PRODUCT_ID_UNBOOTED) },
++ { USB_DEVICE(VENDOR_ID_DLINK, PRODUCT_ID_WUG2400) },
++ { USB_DEVICE(VENDOR_ID_AVM_GMBH, PRODUCT_ID_AVM_WLAN_USB) },
++ { USB_DEVICE(VENDOR_ID_AVM_GMBH, PRODUCT_ID_AVM_WLAN_USB_si) },
++ { USB_DEVICE(VENDOR_ID_ZCOM, PRODUCT_ID_ZCOM_XG750) },
++ { USB_DEVICE(VENDOR_ID_TI, PRODUCT_ID_TI_UNKNOWN) },
++ {}
++};
++
++MODULE_DEVICE_TABLE(usb, acxusb_ids);
++
++/* USB driver data structure as required by the kernel's USB core */
++static struct usb_driver
++acxusb_driver = {
++ .name = "acx_usb",
++ .probe = acxusb_e_probe,
++ .disconnect = acxusb_e_disconnect,
++ .id_table = acxusb_ids
++};
++
++
++/***********************************************************************
++** USB helper
++**
++** ldd3 ch13 says:
++** When the function is usb_kill_urb, the urb lifecycle is stopped. This
++** function is usually used when the device is disconnected from the system,
++** in the disconnect callback. For some drivers, the usb_unlink_urb function
++** should be used to tell the USB core to stop an urb. This function does not
++** wait for the urb to be fully stopped before returning to the caller.
++** This is useful for stoppingthe urb while in an interrupt handler or when
++** a spinlock is held, as waiting for a urb to fully stop requires the ability
++** for the USB core to put the calling process to sleep. This function requires
++** that the URB_ASYNC_UNLINK flag value be set in the urb that is being asked
++** to be stopped in order to work properly.
++**
++** (URB_ASYNC_UNLINK is obsolete, usb_unlink_urb will always be
++** asynchronous while usb_kill_urb is synchronous and should be called
++** directly (drivers/usb/core/urb.c))
++**
++** In light of this, timeout is just for paranoid reasons...
++*
++* Actually, it's useful for debugging. If we reach timeout, we're doing
++* something wrong with the urbs.
++*/
++static void
++acxusb_unlink_urb(struct urb* urb)
++{
++ if (!urb)
++ return;
++
++ if (urb->status == -EINPROGRESS) {
++ int timeout = 10;
++
++ usb_unlink_urb(urb);
++ while (--timeout && urb->status == -EINPROGRESS) {
++ mdelay(1);
++ }
++ if (!timeout) {
++ printk("acx_usb: urb unlink timeout!\n");
++ }
++ }
++}
++
++
++/***********************************************************************
++** EEPROM and PHY read/write helpers
++*/
++/***********************************************************************
++** acxusb_s_read_phy_reg
++*/
++int
++acxusb_s_read_phy_reg(acx_device_t *adev, u32 reg, u8 *charbuf)
++{
++ /* mem_read_write_t mem; */
++
++ FN_ENTER;
++
++ printk("%s doesn't seem to work yet, disabled.\n", __func__);
++
++ /*
++ mem.addr = cpu_to_le16(reg);
++ mem.type = cpu_to_le16(0x82);
++ mem.len = cpu_to_le32(4);
++ acx_s_issue_cmd(adev, ACX1xx_CMD_MEM_READ, &mem, sizeof(mem));
++ *charbuf = mem.data;
++ log(L_DEBUG, "read radio PHY[0x%04X]=0x%02X\n", reg, *charbuf);
++ */
++
++ FN_EXIT1(OK);
++ return OK;
++}
++
++
++/***********************************************************************
++*/
++int
++acxusb_s_write_phy_reg(acx_device_t *adev, u32 reg, u8 value)
++{
++ mem_read_write_t mem;
++
++ FN_ENTER;
++
++ mem.addr = cpu_to_le16(reg);
++ mem.type = cpu_to_le16(0x82);
++ mem.len = cpu_to_le32(4);
++ mem.data = value;
++ acx_s_issue_cmd(adev, ACX1xx_CMD_MEM_WRITE, &mem, sizeof(mem));
++ log(L_DEBUG, "write radio PHY[0x%04X]=0x%02X\n", reg, value);
++
++ FN_EXIT1(OK);
++ return OK;
++}
++
++
++/***********************************************************************
++** acxusb_s_issue_cmd_timeo
++** Excecutes a command in the command mailbox
++**
++** buffer = a pointer to the data.
++** The data must not include 4 byte command header
++*/
++
++/* TODO: ideally we shall always know how much we need
++** and this shall be 0 */
++#define BOGUS_SAFETY_PADDING 0x40
++
++#undef FUNC
++#define FUNC "issue_cmd"
++
++#if !ACX_DEBUG
++int
++acxusb_s_issue_cmd_timeo(
++ acx_device_t *adev,
++ unsigned cmd,
++ void *buffer,
++ unsigned buflen,
++ unsigned timeout)
++{
++#else
++int
++acxusb_s_issue_cmd_timeo_debug(
++ acx_device_t *adev,
++ unsigned cmd,
++ void *buffer,
++ unsigned buflen,
++ unsigned timeout,
++ const char* cmdstr)
++{
++#endif
++ /* USB ignores timeout param */
++
++ struct usb_device *usbdev;
++ struct {
++ u16 cmd;
++ u16 status;
++ u8 data[1];
++ } ACX_PACKED *loc;
++ const char *devname;
++ int acklen, blocklen, inpipe, outpipe;
++ int cmd_status;
++ int result;
++
++ FN_ENTER;
++
++ devname = adev->ndev->name;
++ /* no "wlan%%d: ..." please */
++ if (!devname || !devname[0] || devname[4]=='%')
++ devname = "acx";
++
++ log(L_CTL, FUNC"(cmd:%s,buflen:%u,type:0x%04X)\n",
++ cmdstr, buflen,
++ buffer ? le16_to_cpu(((acx_ie_generic_t *)buffer)->type) : -1);
++
++ loc = kmalloc(buflen + 4 + BOGUS_SAFETY_PADDING, GFP_KERNEL);
++ if (!loc) {
++ printk("%s: "FUNC"(): no memory for data buffer\n", devname);
++ goto bad;
++ }
++
++ /* get context from acx_device */
++ usbdev = adev->usbdev;
++
++ /* check which kind of command was issued */
++ loc->cmd = cpu_to_le16(cmd);
++ loc->status = 0;
++
++/* NB: buflen == frmlen + 4
++**
++** Interrogate: write 8 bytes: (cmd,status,rid,frmlen), then
++** read (cmd,status,rid,frmlen,data[frmlen]) back
++**
++** Configure: write (cmd,status,rid,frmlen,data[frmlen])
++**
++** Possibly bogus special handling of ACX1xx_IE_SCAN_STATUS removed
++*/
++
++ /* now write the parameters of the command if needed */
++ acklen = buflen + 4 + BOGUS_SAFETY_PADDING;
++ blocklen = buflen;
++ if (buffer && buflen) {
++ /* if it's an INTERROGATE command, just pass the length
++ * of parameters to read, as data */
++ if (cmd == ACX1xx_CMD_INTERROGATE) {
++ blocklen = 4;
++ acklen = buflen + 4;
++ }
++ memcpy(loc->data, buffer, blocklen);
++ }
++ blocklen += 4; /* account for cmd,status */
++
++ /* obtain the I/O pipes */
++ outpipe = usb_sndctrlpipe(usbdev, 0);
++ inpipe = usb_rcvctrlpipe(usbdev, 0);
++ log(L_CTL, "ctrl inpipe=0x%X outpipe=0x%X\n", inpipe, outpipe);
++ log(L_CTL, "sending USB control msg (out) (blocklen=%d)\n", blocklen);
++ if (acx_debug & L_DATA)
++ acx_dump_bytes(loc, blocklen);
++
++ result = usb_control_msg(usbdev, outpipe,
++ ACX_USB_REQ_CMD, /* request */
++ USB_TYPE_VENDOR|USB_DIR_OUT, /* requesttype */
++ 0, /* value */
++ 0, /* index */
++ loc, /* dataptr */
++ blocklen, /* size */
++ ACX_USB_CTRL_TIMEOUT /* timeout in ms */
++ );
++
++ if (result == -ENODEV) {
++ log(L_CTL, "no device present (unplug?)\n");
++ goto good;
++ }
++
++ log(L_CTL, "wrote %d bytes\n", result);
++ if (result < 0) {
++ goto bad;
++ }
++
++ /* check for device acknowledge */
++ log(L_CTL, "sending USB control msg (in) (acklen=%d)\n", acklen);
++ loc->status = 0; /* delete old status flag -> set to IDLE */
++ /* shall we zero out the rest? */
++ result = usb_control_msg(usbdev, inpipe,
++ ACX_USB_REQ_CMD, /* request */
++ USB_TYPE_VENDOR|USB_DIR_IN, /* requesttype */
++ 0, /* value */
++ 0, /* index */
++ loc, /* dataptr */
++ acklen, /* size */
++ ACX_USB_CTRL_TIMEOUT /* timeout in ms */
++ );
++ if (result < 0) {
++ printk("%s: "FUNC"(): USB read error %d\n", devname, result);
++ goto bad;
++ }
++ if (acx_debug & L_CTL) {
++ printk("read %d bytes: ", result);
++ acx_dump_bytes(loc, result);
++ }
++
++/*
++ check for result==buflen+4? Was seen:
++
++interrogate(type:ACX100_IE_DOT11_ED_THRESHOLD,len:4)
++issue_cmd(cmd:ACX1xx_CMD_INTERROGATE,buflen:8,type:4111)
++ctrl inpipe=0x80000280 outpipe=0x80000200
++sending USB control msg (out) (blocklen=8)
++01 00 00 00 0F 10 04 00
++wrote 8 bytes
++sending USB control msg (in) (acklen=12) sizeof(loc->data
++read 4 bytes <==== MUST BE 12!!
++*/
++
++ cmd_status = le16_to_cpu(loc->status);
++ if (cmd_status != 1) {
++ printk("%s: "FUNC"(): cmd_status is not SUCCESS: %d (%s)\n",
++ devname, cmd_status, acx_cmd_status_str(cmd_status));
++ /* TODO: goto bad; ? */
++ }
++ if ((cmd == ACX1xx_CMD_INTERROGATE) && buffer && buflen) {
++ memcpy(buffer, loc->data, buflen);
++ log(L_CTL, "response frame: cmd=0x%04X status=%d\n",
++ le16_to_cpu(loc->cmd),
++ cmd_status);
++ }
++good:
++ kfree(loc);
++ FN_EXIT1(OK);
++ return OK;
++bad:
++ /* Give enough info so that callers can avoid
++ ** printing their own diagnostic messages */
++#if ACX_DEBUG
++ printk("%s: "FUNC"(cmd:%s) FAILED\n", devname, cmdstr);
++#else
++ printk("%s: "FUNC"(cmd:0x%04X) FAILED\n", devname, cmd);
++#endif
++ dump_stack();
++ kfree(loc);
++ FN_EXIT1(NOT_OK);
++ return NOT_OK;
++}
++
++
++/***********************************************************************
++** acxusb_boot()
++** Inputs:
++** usbdev -> Pointer to kernel's usb_device structure
++**
++** Returns:
++** (int) Errorcode or 0 on success
++**
++** This function triggers the loading of the firmware image from harddisk
++** and then uploads the firmware to the USB device. After uploading the
++** firmware and transmitting the checksum, the device resets and appears
++** as a new device on the USB bus (the device we can finally deal with)
++*/
++static inline int
++acxusb_fw_needs_padding(firmware_image_t *fw_image, unsigned int usb_maxlen)
++{
++ unsigned int num_xfers = ((fw_image->size - 1) / usb_maxlen) + 1;
++
++ return ((num_xfers % 2) == 0);
++}
++
++static int
++acxusb_boot(struct usb_device *usbdev, int is_tnetw1450, int *radio_type)
++{
++ char filename[sizeof("tiacx1NNusbcRR")];
++
++ firmware_image_t *fw_image = NULL;
++ char *usbbuf;
++ unsigned int offset;
++ unsigned int blk_len, inpipe, outpipe;
++ u32 num_processed;
++ u32 img_checksum, sum;
++ u32 file_size;
++ int result = -EIO;
++ int i;
++
++ FN_ENTER;
++
++ /* dump_device(usbdev); */
++
++ usbbuf = kmalloc(USB_RWMEM_MAXLEN, GFP_KERNEL);
++ if (!usbbuf) {
++ printk(KERN_ERR "acx: no memory for USB transfer buffer (%d bytes)\n", USB_RWMEM_MAXLEN);
++ result = -ENOMEM;
++ goto end;
++ }
++ if (is_tnetw1450) {
++ /* Obtain the I/O pipes */
++ outpipe = usb_sndbulkpipe(usbdev, 1);
++ inpipe = usb_rcvbulkpipe(usbdev, 2);
++
++ printk(KERN_DEBUG "wait for device ready\n");
++ for (i = 0; i <= 2; i++) {
++ result = usb_bulk_msg(usbdev, inpipe,
++ usbbuf,
++ USB_RWMEM_MAXLEN,
++ &num_processed,
++ 2000
++ );
++
++ if ((*(u32 *)&usbbuf[4] == 0x40000001)
++ && (*(u16 *)&usbbuf[2] == 0x1)
++ && ((*(u16 *)usbbuf & 0x3fff) == 0)
++ && ((*(u16 *)usbbuf & 0xc000) == 0xc000))
++ break;
++ msleep(10);
++ }
++ if (i == 2)
++ goto fw_end;
++
++ *radio_type = usbbuf[8];
++ } else {
++ /* Obtain the I/O pipes */
++ outpipe = usb_sndctrlpipe(usbdev, 0);
++ inpipe = usb_rcvctrlpipe(usbdev, 0);
++
++ /* FIXME: shouldn't be hardcoded */
++ *radio_type = RADIO_MAXIM_0D;
++ }
++
++ snprintf(filename, sizeof(filename), "tiacx1%02dusbc%02X",
++ is_tnetw1450 * 11, *radio_type);
++
++ fw_image = acx_s_read_fw(&usbdev->dev, filename, &file_size);
++ if (!fw_image) {
++ result = -EIO;
++ goto end;
++ }
++ log(L_INIT, "firmware size: %d bytes\n", file_size);
++
++ img_checksum = le32_to_cpu(fw_image->chksum);
++
++ if (is_tnetw1450) {
++ u8 cmdbuf[20];
++ const u8 *p;
++ u8 need_padding;
++ u32 tmplen, val;
++
++ memset(cmdbuf, 0, 16);
++
++ need_padding = acxusb_fw_needs_padding(fw_image, USB_RWMEM_MAXLEN);
++ tmplen = need_padding ? file_size-4 : file_size-8;
++ *(u16 *)&cmdbuf[0] = 0xc000;
++ *(u16 *)&cmdbuf[2] = 0x000b;
++ *(u32 *)&cmdbuf[4] = tmplen;
++ *(u32 *)&cmdbuf[8] = file_size-8;
++ *(u32 *)&cmdbuf[12] = img_checksum;
++
++ result = usb_bulk_msg(usbdev, outpipe, cmdbuf, 16, &num_processed, HZ);
++ if (result < 0)
++ goto fw_end;
++
++ p = (const u8 *)&fw_image->size;
++
++ /* first calculate checksum for image size part */
++ sum = p[0]+p[1]+p[2]+p[3];
++ p += 4;
++
++ /* now continue checksum for firmware data part */
++ tmplen = le32_to_cpu(fw_image->size);
++ for (i = 0; i < tmplen /* image size */; i++) {
++ sum += *p++;
++ }
++
++ if (sum != le32_to_cpu(fw_image->chksum)) {
++ printk("acx: FATAL: firmware upload: "
++ "checksums don't match! "
++ "(0x%08x vs. 0x%08x)\n",
++ sum, fw_image->chksum);
++ goto fw_end;
++ }
++
++ offset = 8;
++ while (offset < file_size) {
++ blk_len = file_size - offset;
++ if (blk_len > USB_RWMEM_MAXLEN) {
++ blk_len = USB_RWMEM_MAXLEN;
++ }
++
++ log(L_INIT, "uploading firmware (%d bytes, offset=%d)\n",
++ blk_len, offset);
++ memcpy(usbbuf, ((u8 *)fw_image) + offset, blk_len);
++
++ p = usbbuf;
++ for (i = 0; i < blk_len; i += 4) {
++ *(u32 *)p = be32_to_cpu(*(u32 *)p);
++ p += 4;
++ }
++
++ result = usb_bulk_msg(usbdev, outpipe, usbbuf, blk_len, &num_processed, HZ);
++ if ((result < 0) || (num_processed != blk_len))
++ goto fw_end;
++ offset += blk_len;
++ }
++ if (need_padding) {
++ printk(KERN_DEBUG "send padding\n");
++ memset(usbbuf, 0, 4);
++ result = usb_bulk_msg(usbdev, outpipe, usbbuf, 4, &num_processed, HZ);
++ if ((result < 0) || (num_processed != 4))
++ goto fw_end;
++ }
++ printk(KERN_DEBUG "read firmware upload result\n");
++ memset(cmdbuf, 0, 20); /* additional memset */
++ result = usb_bulk_msg(usbdev, inpipe, cmdbuf, 20, &num_processed, 2000);
++ if (result < 0)
++ goto fw_end;
++ if (*(u32 *)&cmdbuf[4] == 0x40000003)
++ goto fw_end;
++ if (*(u32 *)&cmdbuf[4])
++ goto fw_end;
++ if (*(u16 *)&cmdbuf[16] != 1)
++ goto fw_end;
++
++ val = *(u32 *)&cmdbuf[0];
++ if ((val & 0x3fff)
++ || ((val & 0xc000) != 0xc000))
++ goto fw_end;
++
++ val = *(u32 *)&cmdbuf[8];
++ if (val & 2) {
++ result = usb_bulk_msg(usbdev, inpipe, cmdbuf, 20, &num_processed, 2000);
++ if (result < 0)
++ goto fw_end;
++ val = *(u32 *)&cmdbuf[8];
++ }
++ /* yup, no "else" here! */
++ if (val & 1) {
++ memset(usbbuf, 0, 4);
++ result = usb_bulk_msg(usbdev, outpipe, usbbuf, 4, &num_processed, HZ);
++ if ((result < 0) || (!num_processed))
++ goto fw_end;
++ }
++
++ printk("TNETW1450 firmware upload successful!\n");
++ result = 0;
++ goto end;
++fw_end:
++ result = -EIO;
++ goto end;
++ } else {
++ /* ACX100 USB */
++
++ /* now upload the firmware, slice the data into blocks */
++ offset = 8;
++ while (offset < file_size) {
++ blk_len = file_size - offset;
++ if (blk_len > USB_RWMEM_MAXLEN) {
++ blk_len = USB_RWMEM_MAXLEN;
++ }
++ log(L_INIT, "uploading firmware (%d bytes, offset=%d)\n",
++ blk_len, offset);
++ memcpy(usbbuf, ((u8 *)fw_image) + offset, blk_len);
++ result = usb_control_msg(usbdev, outpipe,
++ ACX_USB_REQ_UPLOAD_FW,
++ USB_TYPE_VENDOR|USB_DIR_OUT,
++ (file_size - 8) & 0xffff, /* value */
++ (file_size - 8) >> 16, /* index */
++ usbbuf, /* dataptr */
++ blk_len, /* size */
++ 3000 /* timeout in ms */
++ );
++ offset += blk_len;
++ if (result < 0) {
++ printk(KERN_ERR "acx: error %d during upload "
++ "of firmware, aborting\n", result);
++ goto end;
++ }
++ }
++
++ /* finally, send the checksum and reboot the device */
++ /* does this trigger the reboot? */
++ result = usb_control_msg(usbdev, outpipe,
++ ACX_USB_REQ_UPLOAD_FW,
++ USB_TYPE_VENDOR|USB_DIR_OUT,
++ img_checksum & 0xffff, /* value */
++ img_checksum >> 16, /* index */
++ NULL, /* dataptr */
++ 0, /* size */
++ 3000 /* timeout in ms */
++ );
++ if (result < 0) {
++ printk(KERN_ERR "acx: error %d during tx of checksum, "
++ "aborting\n", result);
++ goto end;
++ }
++ result = usb_control_msg(usbdev, inpipe,
++ ACX_USB_REQ_ACK_CS,
++ USB_TYPE_VENDOR|USB_DIR_IN,
++ img_checksum & 0xffff, /* value */
++ img_checksum >> 16, /* index */
++ usbbuf, /* dataptr */
++ 8, /* size */
++ 3000 /* timeout in ms */
++ );
++ if (result < 0) {
++ printk(KERN_ERR "acx: error %d during ACK of checksum, "
++ "aborting\n", result);
++ goto end;
++ }
++ if (*usbbuf != 0x10) {
++ printk(KERN_ERR "acx: invalid checksum?\n");
++ result = -EINVAL;
++ goto end;
++ }
++ result = 0;
++ }
++
++end:
++ vfree(fw_image);
++ kfree(usbbuf);
++
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/* FIXME: maybe merge it with usual eeprom reading, into common code? */
++static void
++acxusb_s_read_eeprom_version(acx_device_t *adev)
++{
++ u8 eeprom_ver[0x8];
++
++ memset(eeprom_ver, 0, sizeof(eeprom_ver));
++ acx_s_interrogate(adev, &eeprom_ver, ACX1FF_IE_EEPROM_VER);
++
++ /* FIXME: which one of those values to take? */
++ adev->eeprom_version = eeprom_ver[5];
++}
++
++
++/*
++ * temporary helper function to at least fill important cfgopt members with
++ * useful replacement values until we figure out how one manages to fetch
++ * the configoption struct in the USB device case...
++ */
++static int
++acxusb_s_fill_configoption(acx_device_t *adev)
++{
++ adev->cfgopt_probe_delay = 200;
++ adev->cfgopt_dot11CCAModes = 4;
++ adev->cfgopt_dot11Diversity = 1;
++ adev->cfgopt_dot11ShortPreambleOption = 1;
++ adev->cfgopt_dot11PBCCOption = 1;
++ adev->cfgopt_dot11ChannelAgility = 0;
++ adev->cfgopt_dot11PhyType = 5;
++ adev->cfgopt_dot11TempType = 1;
++ return OK;
++}
++
++
++/***********************************************************************
++** acxusb_e_probe()
++**
++** This function is invoked by the kernel's USB core whenever a new device is
++** attached to the system or the module is loaded. It is presented a usb_device
++** structure from which information regarding the device is obtained and evaluated.
++** In case this driver is able to handle one of the offered devices, it returns
++** a non-null pointer to a driver context and thereby claims the device.
++*/
++
++static void
++dummy_netdev_init(struct net_device *ndev) {}
++
++static int
++acxusb_e_probe(struct usb_interface *intf, const struct usb_device_id *devID)
++{
++ struct usb_device *usbdev = interface_to_usbdev(intf);
++ acx_device_t *adev = NULL;
++ struct net_device *ndev = NULL;
++ struct usb_config_descriptor *config;
++ struct usb_endpoint_descriptor *epdesc;
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11)
++ struct usb_host_endpoint *ep;
++#endif
++ struct usb_interface_descriptor *ifdesc;
++ const char* msg;
++ int numconfigs, numfaces, numep;
++ int result = OK;
++ int i;
++ int radio_type;
++ /* this one needs to be more precise in case there appears a TNETW1450 from the same vendor */
++ int is_tnetw1450 = (usbdev->descriptor.idVendor != ACX100_VENDOR_ID);
++
++ FN_ENTER;
++
++ if (is_tnetw1450) {
++ /* Boot the device (i.e. upload the firmware) */
++ acxusb_boot(usbdev, is_tnetw1450, &radio_type);
++
++ /* TNETW1450-based cards will continue right away with
++ * the same USB ID after booting */
++ } else {
++ /* First check if this is the "unbooted" hardware */
++ if (usbdev->descriptor.idProduct == ACX100_PRODUCT_ID_UNBOOTED) {
++
++ /* Boot the device (i.e. upload the firmware) */
++ acxusb_boot(usbdev, is_tnetw1450, &radio_type);
++
++ /* DWL-120+ will first boot the firmware,
++ * then later have a *separate* probe() run
++ * since its USB ID will have changed after
++ * firmware boot!
++ * Since the first probe() run has no
++ * other purpose than booting the firmware,
++ * simply return immediately.
++ */
++ log(L_INIT, "finished booting, returning from probe()\n");
++ result = OK; /* success */
++ goto end;
++ }
++ else
++ /* device not unbooted, but invalid USB ID!? */
++ if (usbdev->descriptor.idProduct != ACX100_PRODUCT_ID_BOOTED)
++ goto end_nodev;
++ }
++
++/* Ok, so it's our device and it has already booted */
++
++ /* Allocate memory for a network device */
++
++ ndev = alloc_netdev(sizeof(*adev), "wlan%d", dummy_netdev_init);
++ /* (NB: memsets to 0 entire area) */
++ if (!ndev) {
++ msg = "acx: no memory for netdev\n";
++ goto end_nomem;
++ }
++
++ /* Register the callbacks for the network device functions */
++
++ ether_setup(ndev);
++ ndev->open = &acxusb_e_open;
++ ndev->stop = &acxusb_e_close;
++ ndev->hard_start_xmit = (void *)&acx_i_start_xmit;
++ ndev->get_stats = (void *)&acx_e_get_stats;
++#if IW_HANDLER_VERSION <= 5
++ ndev->get_wireless_stats = (void *)&acx_e_get_wireless_stats;
++#endif
++ ndev->wireless_handlers = (struct iw_handler_def *)&acx_ioctl_handler_def;
++ ndev->set_multicast_list = (void *)&acxusb_i_set_rx_mode;
++#ifdef HAVE_TX_TIMEOUT
++ ndev->tx_timeout = &acxusb_i_tx_timeout;
++ ndev->watchdog_timeo = 4 * HZ;
++#endif
++ ndev->change_mtu = &acx_e_change_mtu;
++ SET_MODULE_OWNER(ndev);
++
++ /* Setup private driver context */
++
++ adev = ndev2adev(ndev);
++ adev->ndev = ndev;
++
++ adev->dev_type = DEVTYPE_USB;
++ adev->radio_type = radio_type;
++ if (is_tnetw1450) {
++ /* well, actually it's a TNETW1450, but since it
++ * seems to be sufficiently similar to TNETW1130,
++ * I don't want to change large amounts of code now */
++ adev->chip_type = CHIPTYPE_ACX111;
++ } else {
++ adev->chip_type = CHIPTYPE_ACX100;
++ }
++
++ adev->usbdev = usbdev;
++ spin_lock_init(&adev->lock); /* initial state: unlocked */
++ sema_init(&adev->sem, 1); /* initial state: 1 (upped) */
++
++ /* Check that this is really the hardware we know about.
++ ** If not sure, at least notify the user that he
++ ** may be in trouble...
++ */
++ numconfigs = (int)usbdev->descriptor.bNumConfigurations;
++ if (numconfigs != 1)
++ printk("acx: number of configurations is %d, "
++ "this driver only knows how to handle 1, "
++ "be prepared for surprises\n", numconfigs);
++
++ config = &usbdev->config->desc;
++ numfaces = config->bNumInterfaces;
++ if (numfaces != 1)
++ printk("acx: number of interfaces is %d, "
++ "this driver only knows how to handle 1, "
++ "be prepared for surprises\n", numfaces);
++
++ ifdesc = &intf->altsetting->desc;
++ numep = ifdesc->bNumEndpoints;
++ log(L_DEBUG, "# of endpoints: %d\n", numep);
++
++ if (is_tnetw1450) {
++ adev->bulkoutep = 1;
++ adev->bulkinep = 2;
++ } else {
++ /* obtain information about the endpoint
++ ** addresses, begin with some default values
++ */
++ adev->bulkoutep = 1;
++ adev->bulkinep = 1;
++ for (i = 0; i < numep; i++) {
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11)
++ ep = usbdev->ep_in[i];
++ if (!ep)
++ continue;
++ epdesc = &ep->desc;
++#else
++ epdesc = usb_epnum_to_ep_desc(usbdev, i);
++ if (!epdesc)
++ continue;
++#endif
++ if (epdesc->bmAttributes & USB_ENDPOINT_XFER_BULK) {
++ if (epdesc->bEndpointAddress & 0x80)
++ adev->bulkinep = epdesc->bEndpointAddress & 0xF;
++ else
++ adev->bulkoutep = epdesc->bEndpointAddress & 0xF;
++ }
++ }
++ }
++ log(L_DEBUG, "bulkout ep: 0x%X\n", adev->bulkoutep);
++ log(L_DEBUG, "bulkin ep: 0x%X\n", adev->bulkinep);
++
++ /* already done by memset: adev->rxtruncsize = 0; */
++ log(L_DEBUG, "TXBUFSIZE=%d RXBUFSIZE=%d\n",
++ (int) TXBUFSIZE, (int) RXBUFSIZE);
++
++ /* Allocate the RX/TX containers. */
++ adev->usb_tx = kmalloc(sizeof(usb_tx_t) * ACX_TX_URB_CNT, GFP_KERNEL);
++ if (!adev->usb_tx) {
++ msg = "acx: no memory for tx container";
++ goto end_nomem;
++ }
++ adev->usb_rx = kmalloc(sizeof(usb_rx_t) * ACX_RX_URB_CNT, GFP_KERNEL);
++ if (!adev->usb_rx) {
++ msg = "acx: no memory for rx container";
++ goto end_nomem;
++ }
++
++ /* Setup URBs for bulk-in/out messages */
++ for (i = 0; i < ACX_RX_URB_CNT; i++) {
++ adev->usb_rx[i].urb = usb_alloc_urb(0, GFP_KERNEL);
++ if (!adev->usb_rx[i].urb) {
++ msg = "acx: no memory for input URB\n";
++ goto end_nomem;
++ }
++ adev->usb_rx[i].urb->status = 0;
++ adev->usb_rx[i].adev = adev;
++ adev->usb_rx[i].busy = 0;
++ }
++
++ for (i = 0; i< ACX_TX_URB_CNT; i++) {
++ adev->usb_tx[i].urb = usb_alloc_urb(0, GFP_KERNEL);
++ if (!adev->usb_tx[i].urb) {
++ msg = "acx: no memory for output URB\n";
++ goto end_nomem;
++ }
++ adev->usb_tx[i].urb->status = 0;
++ adev->usb_tx[i].adev = adev;
++ adev->usb_tx[i].busy = 0;
++ }
++ adev->tx_free = ACX_TX_URB_CNT;
++
++ usb_set_intfdata(intf, adev);
++ SET_NETDEV_DEV(ndev, &intf->dev);
++
++ /* TODO: move all of fw cmds to open()? But then we won't know our MAC addr
++ until ifup (it's available via reading ACX1xx_IE_DOT11_STATION_ID)... */
++
++ /* put acx out of sleep mode and initialize it */
++ acx_s_issue_cmd(adev, ACX1xx_CMD_WAKE, NULL, 0);
++
++ result = acx_s_init_mac(adev);
++ if (result)
++ goto end;
++
++ /* TODO: see similar code in pci.c */
++ acxusb_s_read_eeprom_version(adev);
++ acxusb_s_fill_configoption(adev);
++ acx_s_set_defaults(adev);
++ acx_s_get_firmware_version(adev);
++ acx_display_hardware_details(adev);
++
++ /* Register the network device */
++ log(L_INIT, "registering network device\n");
++ result = register_netdev(ndev);
++ if (result) {
++ msg = "acx: failed to register USB network device "
++ "(error %d)\n";
++ goto end_nomem;
++ }
++
++ acx_proc_register_entries(ndev);
++
++ acx_stop_queue(ndev, "on probe");
++ acx_carrier_off(ndev, "on probe");
++
++ printk("acx: USB module " ACX_RELEASE " loaded successfully\n");
++
++#if CMD_DISCOVERY
++ great_inquisitor(adev);
++#endif
++
++ /* Everything went OK, we are happy now */
++ result = OK;
++ goto end;
++
++end_nomem:
++ printk(msg, result);
++
++ if (ndev) {
++ if (adev->usb_rx) {
++ for (i = 0; i < ACX_RX_URB_CNT; i++)
++ usb_free_urb(adev->usb_rx[i].urb);
++ kfree(adev->usb_rx);
++ }
++ if (adev->usb_tx) {
++ for (i = 0; i < ACX_TX_URB_CNT; i++)
++ usb_free_urb(adev->usb_tx[i].urb);
++ kfree(adev->usb_tx);
++ }
++ free_netdev(ndev);
++ }
++
++ result = -ENOMEM;
++ goto end;
++
++end_nodev:
++ /* no device we could handle, return error. */
++ result = -EIO;
++
++end:
++ FN_EXIT1(result);
++ return result;
++}
++
++
++/***********************************************************************
++** acxusb_e_disconnect()
++**
++** This function is invoked whenever the user pulls the plug from the USB
++** device or the module is removed from the kernel. In these cases, the
++** network devices have to be taken down and all allocated memory has
++** to be freed.
++*/
++static void
++acxusb_e_disconnect(struct usb_interface *intf)
++{
++ acx_device_t *adev = usb_get_intfdata(intf);
++ unsigned long flags;
++ int i;
++
++ FN_ENTER;
++
++ /* No WLAN device... no sense */
++ if (!adev)
++ goto end;
++
++ /* Unregister network device
++ *
++ * If the interface is up, unregister_netdev() will take
++ * care of calling our close() function, which takes
++ * care of unlinking the urbs, sending the device to
++ * sleep, etc...
++ * This can't be called with sem or lock held because
++ * _close() will try to grab it as well if it's called,
++ * deadlocking the machine.
++ */
++ unregister_netdev(adev->ndev);
++
++ acx_sem_lock(adev);
++ acx_lock(adev, flags);
++ /* This device exists no more */
++ usb_set_intfdata(intf, NULL);
++ acx_proc_unregister_entries(adev->ndev);
++
++ /*
++ * Here we only free them. _close() took care of
++ * unlinking them.
++ */
++ for (i = 0; i < ACX_RX_URB_CNT; ++i) {
++ usb_free_urb(adev->usb_rx[i].urb);
++ }
++ for (i = 0; i< ACX_TX_URB_CNT; ++i) {
++ usb_free_urb(adev->usb_tx[i].urb);
++ }
++
++ /* Freeing containers */
++ kfree(adev->usb_rx);
++ kfree(adev->usb_tx);
++
++ acx_unlock(adev, flags);
++ acx_sem_unlock(adev);
++
++ free_netdev(adev->ndev);
++end:
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acxusb_e_open()
++** This function is called when the user sets up the network interface.
++** It initializes a management timer, sets up the USB card and starts
++** the network tx queue and USB receive.
++*/
++static int
++acxusb_e_open(struct net_device *ndev)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ unsigned long flags;
++ int i;
++
++ FN_ENTER;
++
++ acx_sem_lock(adev);
++
++ /* put the ACX100 out of sleep mode */
++ acx_s_issue_cmd(adev, ACX1xx_CMD_WAKE, NULL, 0);
++
++ acx_init_task_scheduler(adev);
++
++ init_timer(&adev->mgmt_timer);
++ adev->mgmt_timer.function = acx_i_timer;
++ adev->mgmt_timer.data = (unsigned long)adev;
++
++ /* acx_s_start needs it */
++ SET_BIT(adev->dev_state_mask, ACX_STATE_IFACE_UP);
++ acx_s_start(adev);
++
++ /* don't acx_start_queue() here, we need to associate first */
++
++ acx_lock(adev, flags);
++ for (i = 0; i < ACX_RX_URB_CNT; i++) {
++ adev->usb_rx[i].urb->status = 0;
++ }
++
++ acxusb_l_poll_rx(adev, &adev->usb_rx[0]);
++
++ acx_unlock(adev, flags);
++
++ acx_sem_unlock(adev);
++
++ FN_EXIT0;
++ return 0;
++}
++
++
++/***********************************************************************
++** acxusb_e_close()
++**
++** This function stops the network functionality of the interface (invoked
++** when the user calls ifconfig <wlan> down). The tx queue is halted and
++** the device is marked as down. In case there were any pending USB bulk
++** transfers, these are unlinked (asynchronously). The module in-use count
++** is also decreased in this function.
++*/
++static int
++acxusb_e_close(struct net_device *ndev)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ unsigned long flags;
++ int i;
++
++ FN_ENTER;
++
++#ifdef WE_STILL_DONT_CARE_ABOUT_IT
++ /* Transmit a disassociate frame */
++ lock
++ acx_l_transmit_disassoc(adev, &client);
++ unlock
++#endif
++
++ acx_sem_lock(adev);
++
++ CLEAR_BIT(adev->dev_state_mask, ACX_STATE_IFACE_UP);
++
++/* Code below is remarkably similar to acxpci_s_down(). Maybe we can merge them? */
++
++ /* Make sure we don't get any more rx requests */
++ acx_s_issue_cmd(adev, ACX1xx_CMD_DISABLE_RX, NULL, 0);
++ acx_s_issue_cmd(adev, ACX1xx_CMD_DISABLE_TX, NULL, 0);
++
++ /*
++ * We must do FLUSH *without* holding sem to avoid a deadlock.
++ * See pci.c:acxpci_s_down() for deails.
++ */
++ acx_sem_unlock(adev);
++ FLUSH_SCHEDULED_WORK();
++ acx_sem_lock(adev);
++
++ /* Power down the device */
++ acx_s_issue_cmd(adev, ACX1xx_CMD_SLEEP, NULL, 0);
++
++ /* Stop the transmit queue, mark the device as DOWN */
++ acx_lock(adev, flags);
++ acx_stop_queue(ndev, "on ifdown");
++ acx_set_status(adev, ACX_STATUS_0_STOPPED);
++ /* stop pending rx/tx urb transfers */
++ for (i = 0; i < ACX_TX_URB_CNT; i++) {
++ acxusb_unlink_urb(adev->usb_tx[i].urb);
++ adev->usb_tx[i].busy = 0;
++ }
++ for (i = 0; i < ACX_RX_URB_CNT; i++) {
++ acxusb_unlink_urb(adev->usb_rx[i].urb);
++ adev->usb_rx[i].busy = 0;
++ }
++ adev->tx_free = ACX_TX_URB_CNT;
++ acx_unlock(adev, flags);
++
++ /* Must do this outside of lock */
++ del_timer_sync(&adev->mgmt_timer);
++
++ acx_sem_unlock(adev);
++
++ FN_EXIT0;
++ return 0;
++}
++
++
++/***********************************************************************
++** acxusb_l_poll_rx
++** This function (re)initiates a bulk-in USB transfer on a given urb
++*/
++static void
++acxusb_l_poll_rx(acx_device_t *adev, usb_rx_t* rx)
++{
++ struct usb_device *usbdev;
++ struct urb *rxurb;
++ int errcode, rxnum;
++ unsigned int inpipe;
++
++ FN_ENTER;
++
++ rxurb = rx->urb;
++ usbdev = adev->usbdev;
++
++ rxnum = rx - adev->usb_rx;
++
++ inpipe = usb_rcvbulkpipe(usbdev, adev->bulkinep);
++ if (unlikely(rxurb->status == -EINPROGRESS)) {
++ printk(KERN_ERR "acx: error, rx triggered while rx urb in progress\n");
++ /* FIXME: this is nasty, receive is being cancelled by this code
++ * on the other hand, this should not happen anyway...
++ */
++ usb_unlink_urb(rxurb);
++ } else
++ if (unlikely(rxurb->status == -ECONNRESET)) {
++ log(L_USBRXTX, "acx_usb: _poll_rx: connection reset\n");
++ goto end;
++ }
++ rxurb->actual_length = 0;
++ usb_fill_bulk_urb(rxurb, usbdev, inpipe,
++ &rx->bulkin, /* dataptr */
++ RXBUFSIZE, /* size */
++ acxusb_i_complete_rx, /* handler */
++ rx /* handler param */
++ );
++ rxurb->transfer_flags = URB_ASYNC_UNLINK;
++
++ /* ATOMIC: we may be called from complete_rx() usb callback */
++ errcode = usb_submit_urb(rxurb, GFP_ATOMIC);
++ /* FIXME: evaluate the error code! */
++ log(L_USBRXTX, "SUBMIT RX (%d) inpipe=0x%X size=%d errcode=%d\n",
++ rxnum, inpipe, (int) RXBUFSIZE, errcode);
++end:
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acxusb_i_complete_rx()
++** Inputs:
++** urb -> pointer to USB request block
++** regs -> pointer to register-buffer for syscalls (see asm/ptrace.h)
++**
++** This function is invoked by USB subsystem whenever a bulk receive
++** request returns.
++** The received data is then committed to the network stack and the next
++** USB receive is triggered.
++*/
++static void
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
++acxusb_i_complete_rx(struct urb *urb)
++#else
++acxusb_i_complete_rx(struct urb *urb, struct pt_regs *regs)
++#endif
++{
++ acx_device_t *adev;
++ rxbuffer_t *ptr;
++ rxbuffer_t *inbuf;
++ usb_rx_t *rx;
++ unsigned long flags;
++ int size, remsize, packetsize, rxnum;
++
++ FN_ENTER;
++
++ BUG_ON(!urb->context);
++
++ rx = (usb_rx_t *)urb->context;
++ adev = rx->adev;
++
++ acx_lock(adev, flags);
++
++ /*
++ * Happens on disconnect or close. Don't play with the urb.
++ * Don't resubmit it. It will get unlinked by close()
++ */
++ if (unlikely(!(adev->dev_state_mask & ACX_STATE_IFACE_UP))) {
++ log(L_USBRXTX, "rx: device is down, not doing anything\n");
++ goto end_unlock;
++ }
++
++ inbuf = &rx->bulkin;
++ size = urb->actual_length;
++ remsize = size;
++ rxnum = rx - adev->usb_rx;
++
++ log(L_USBRXTX, "RETURN RX (%d) status=%d size=%d\n",
++ rxnum, urb->status, size);
++
++ /* Send the URB that's waiting. */
++ log(L_USBRXTX, "rxnum=%d, sending=%d\n", rxnum, rxnum^1);
++ acxusb_l_poll_rx(adev, &adev->usb_rx[rxnum^1]);
++
++ if (unlikely(size > sizeof(rxbuffer_t)))
++ printk("acx_usb: rx too large: %d, please report\n", size);
++
++ /* check if the transfer was aborted */
++ switch (urb->status) {
++ case 0: /* No error */
++ break;
++ case -EOVERFLOW:
++ printk(KERN_ERR "acx: rx data overrun\n");
++ adev->rxtruncsize = 0; /* Not valid anymore. */
++ goto end_unlock;
++ case -ECONNRESET:
++ adev->rxtruncsize = 0;
++ goto end_unlock;
++ case -ESHUTDOWN: /* rmmod */
++ adev->rxtruncsize = 0;
++ goto end_unlock;
++ default:
++ adev->rxtruncsize = 0;
++ adev->stats.rx_errors++;
++ printk("acx: rx error (urb status=%d)\n", urb->status);
++ goto end_unlock;
++ }
++
++ if (unlikely(!size))
++ printk("acx: warning, encountered zerolength rx packet\n");
++
++ if (urb->transfer_buffer != inbuf)
++ goto end_unlock;
++
++ /* check if previous frame was truncated
++ ** FIXME: this code can only handle truncation
++ ** of consecutive packets!
++ */
++ ptr = inbuf;
++ if (adev->rxtruncsize) {
++ int tail_size;
++
++ ptr = &adev->rxtruncbuf;
++ packetsize = RXBUF_BYTES_USED(ptr);
++ if (acx_debug & L_USBRXTX) {
++ printk("handling truncated frame (truncsize=%d size=%d "
++ "packetsize(from trunc)=%d)\n",
++ adev->rxtruncsize, size, packetsize);
++ acx_dump_bytes(ptr, RXBUF_HDRSIZE);
++ acx_dump_bytes(inbuf, RXBUF_HDRSIZE);
++ }
++
++ /* bytes needed for rxtruncbuf completion: */
++ tail_size = packetsize - adev->rxtruncsize;
++
++ if (size < tail_size) {
++ /* there is not enough data to complete this packet,
++ ** simply append the stuff to the truncation buffer
++ */
++ memcpy(((char *)ptr) + adev->rxtruncsize, inbuf, size);
++ adev->rxtruncsize += size;
++ remsize = 0;
++ } else {
++ /* ok, this data completes the previously
++ ** truncated packet. copy it into a descriptor
++ ** and give it to the rest of the stack */
++
++ /* append tail to previously truncated part
++ ** NB: adev->rxtruncbuf (pointed to by ptr) can't
++ ** overflow because this is already checked before
++ ** truncation buffer was filled. See below,
++ ** "if (packetsize > sizeof(rxbuffer_t))..." code */
++ memcpy(((char *)ptr) + adev->rxtruncsize, inbuf, tail_size);
++
++ if (acx_debug & L_USBRXTX) {
++ printk("full trailing packet + 12 bytes:\n");
++ acx_dump_bytes(inbuf, tail_size + RXBUF_HDRSIZE);
++ }
++ acx_l_process_rxbuf(adev, ptr);
++ adev->rxtruncsize = 0;
++ ptr = (rxbuffer_t *) (((char *)inbuf) + tail_size);
++ remsize -= tail_size;
++ }
++ log(L_USBRXTX, "post-merge size=%d remsize=%d\n",
++ size, remsize);
++ }
++
++ /* size = USB data block size
++ ** remsize = unprocessed USB bytes left
++ ** ptr = current pos in USB data block
++ */
++ while (remsize) {
++ if (remsize < RXBUF_HDRSIZE) {
++ printk("acx: truncated rx header (%d bytes)!\n",
++ remsize);
++ if (ACX_DEBUG)
++ acx_dump_bytes(ptr, remsize);
++ break;
++ }
++
++ packetsize = RXBUF_BYTES_USED(ptr);
++ log(L_USBRXTX, "packet with packetsize=%d\n", packetsize);
++
++ if (RXBUF_IS_TXSTAT(ptr)) {
++ /* do rate handling */
++ usb_txstatus_t *stat = (void*)ptr;
++ u16 client_no = (u16)stat->hostdata;
++
++ log(L_USBRXTX, "tx: stat: mac_cnt_rcvd:%04X "
++ "queue_index:%02X mac_status:%02X hostdata:%08X "
++ "rate:%u ack_failures:%02X rts_failures:%02X "
++ "rts_ok:%02X\n",
++ stat->mac_cnt_rcvd,
++ stat->queue_index, stat->mac_status, stat->hostdata,
++ stat->rate, stat->ack_failures, stat->rts_failures,
++ stat->rts_ok);
++
++ if (adev->rate_auto && client_no < VEC_SIZE(adev->sta_list)) {
++ client_t *clt = &adev->sta_list[client_no];
++ u16 cur = stat->hostdata >> 16;
++
++ if (clt && clt->rate_cur == cur) {
++ acx_l_handle_txrate_auto(adev, clt,
++ cur, /* intended rate */
++ stat->rate, 0, /* actually used rate */
++ stat->mac_status, /* error? */
++ ACX_TX_URB_CNT - adev->tx_free);
++ }
++ }
++ goto next;
++ }
++
++ if (packetsize > sizeof(rxbuffer_t)) {
++ printk("acx: packet exceeds max wlan "
++ "frame size (%d > %d). size=%d\n",
++ packetsize, (int) sizeof(rxbuffer_t), size);
++ if (ACX_DEBUG)
++ acx_dump_bytes(ptr, 16);
++ /* FIXME: put some real error-handling in here! */
++ break;
++ }
++
++ if (packetsize > remsize) {
++ /* frame truncation handling */
++ if (acx_debug & L_USBRXTX) {
++ printk("need to truncate packet, "
++ "packetsize=%d remsize=%d "
++ "size=%d bytes:",
++ packetsize, remsize, size);
++ acx_dump_bytes(ptr, RXBUF_HDRSIZE);
++ }
++ memcpy(&adev->rxtruncbuf, ptr, remsize);
++ adev->rxtruncsize = remsize;
++ break;
++ }
++
++ /* packetsize <= remsize */
++ /* now handle the received data */
++ acx_l_process_rxbuf(adev, ptr);
++next:
++ ptr = (rxbuffer_t *)(((char *)ptr) + packetsize);
++ remsize -= packetsize;
++ if ((acx_debug & L_USBRXTX) && remsize) {
++ printk("more than one packet in buffer, "
++ "second packet hdr:");
++ acx_dump_bytes(ptr, RXBUF_HDRSIZE);
++ }
++ }
++
++end_unlock:
++ acx_unlock(adev, flags);
++/* end: */
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++** acxusb_i_complete_tx()
++** Inputs:
++** urb -> pointer to USB request block
++** regs -> pointer to register-buffer for syscalls (see asm/ptrace.h)
++**
++** This function is invoked upon termination of a USB transfer.
++*/
++static void
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)
++acxusb_i_complete_tx(struct urb *urb)
++#else
++acxusb_i_complete_tx(struct urb *urb, struct pt_regs *regs)
++#endif
++{
++ acx_device_t *adev;
++ usb_tx_t *tx;
++ unsigned long flags;
++ int txnum;
++
++ FN_ENTER;
++
++ BUG_ON(!urb->context);
++
++ tx = (usb_tx_t *)urb->context;
++ adev = tx->adev;
++
++ txnum = tx - adev->usb_tx;
++
++ acx_lock(adev, flags);
++
++ /*
++ * If the iface isn't up, we don't have any right
++ * to play with them. The urb may get unlinked.
++ */
++ if (unlikely(!(adev->dev_state_mask & ACX_STATE_IFACE_UP))) {
++ log(L_USBRXTX, "tx: device is down, not doing anything\n");
++ goto end_unlock;
++ }
++
++ log(L_USBRXTX, "RETURN TX (%d): status=%d size=%d\n",
++ txnum, urb->status, urb->actual_length);
++
++ /* handle USB transfer errors */
++ switch (urb->status) {
++ case 0: /* No error */
++ break;
++ case -ESHUTDOWN:
++ goto end_unlock;
++ break;
++ case -ECONNRESET:
++ goto end_unlock;
++ break;
++ /* FIXME: real error-handling code here please */
++ default:
++ printk(KERN_ERR "acx: tx error, urb status=%d\n", urb->status);
++ /* FIXME: real error-handling code here please */
++ }
++
++ /* free the URB and check for more data */
++ tx->busy = 0;
++ adev->tx_free++;
++ if ((adev->tx_free >= TX_START_QUEUE)
++ && (adev->status == ACX_STATUS_4_ASSOCIATED)
++ && (acx_queue_stopped(adev->ndev))
++ ) {
++ log(L_BUF, "tx: wake queue (%u free txbufs)\n",
++ adev->tx_free);
++ acx_wake_queue(adev->ndev, NULL);
++ }
++
++end_unlock:
++ acx_unlock(adev, flags);
++/* end: */
++ FN_EXIT0;
++}
++
++
++/***************************************************************
++** acxusb_l_alloc_tx
++** Actually returns a usb_tx_t* ptr
++*/
++tx_t*
++acxusb_l_alloc_tx(acx_device_t *adev)
++{
++ usb_tx_t *tx;
++ unsigned head;
++
++ FN_ENTER;
++
++ head = adev->tx_head;
++ do {
++ head = (head + 1) % ACX_TX_URB_CNT;
++ if (!adev->usb_tx[head].busy) {
++ log(L_USBRXTX, "allocated tx %d\n", head);
++ tx = &adev->usb_tx[head];
++ tx->busy = 1;
++ adev->tx_free--;
++ /* Keep a few free descs between head and tail of tx ring.
++ ** It is not absolutely needed, just feels safer */
++ if (adev->tx_free < TX_STOP_QUEUE) {
++ log(L_BUF, "tx: stop queue "
++ "(%u free txbufs)\n", adev->tx_free);
++ acx_stop_queue(adev->ndev, NULL);
++ }
++ goto end;
++ }
++ } while (likely(head!=adev->tx_head));
++ tx = NULL;
++ printk_ratelimited("acx: tx buffers full\n");
++end:
++ adev->tx_head = head;
++ FN_EXIT0;
++ return (tx_t*)tx;
++}
++
++
++/***************************************************************
++** Used if alloc_tx()'ed buffer needs to be cancelled without doing tx
++*/
++void
++acxusb_l_dealloc_tx(tx_t *tx_opaque)
++{
++ usb_tx_t* tx = (usb_tx_t*)tx_opaque;
++ tx->busy = 0;
++}
++
++
++/***************************************************************
++*/
++void*
++acxusb_l_get_txbuf(acx_device_t *adev, tx_t* tx_opaque)
++{
++ usb_tx_t* tx = (usb_tx_t*)tx_opaque;
++ return &tx->bulkout.data;
++}
++
++
++/***************************************************************
++** acxusb_l_tx_data
++**
++** Can be called from IRQ (rx -> (AP bridging or mgmt response) -> tx).
++** Can be called from acx_i_start_xmit (data frames from net core).
++*/
++void
++acxusb_l_tx_data(acx_device_t *adev, tx_t* tx_opaque, int wlanpkt_len)
++{
++ struct usb_device *usbdev;
++ struct urb* txurb;
++ usb_tx_t* tx;
++ usb_txbuffer_t* txbuf;
++ client_t *clt;
++ wlan_hdr_t* whdr;
++ unsigned int outpipe;
++ int ucode, txnum;
++
++ FN_ENTER;
++
++ tx = ((usb_tx_t *)tx_opaque);
++ txurb = tx->urb;
++ txbuf = &tx->bulkout;
++ whdr = (wlan_hdr_t *)txbuf->data;
++ txnum = tx - adev->usb_tx;
++
++ log(L_DEBUG, "using buf#%d free=%d len=%d\n",
++ txnum, adev->tx_free, wlanpkt_len);
++
++ switch (adev->mode) {
++ case ACX_MODE_0_ADHOC:
++ case ACX_MODE_3_AP:
++ clt = acx_l_sta_list_get(adev, whdr->a1);
++ break;
++ case ACX_MODE_2_STA:
++ clt = adev->ap_client;
++ break;
++ default: /* ACX_MODE_OFF, ACX_MODE_MONITOR */
++ clt = NULL;
++ break;
++ }
++
++ if (unlikely(clt && !clt->rate_cur)) {
++ printk("acx: driver bug! bad ratemask\n");
++ goto end;
++ }
++
++ /* fill the USB transfer header */
++ txbuf->desc = cpu_to_le16(USB_TXBUF_TXDESC);
++ txbuf->mpdu_len = cpu_to_le16(wlanpkt_len);
++ txbuf->queue_index = 1;
++ if (clt) {
++ txbuf->rate = clt->rate_100;
++ txbuf->hostdata = (clt - adev->sta_list) | (clt->rate_cur << 16);
++ } else {
++ txbuf->rate = adev->rate_bcast100;
++ txbuf->hostdata = ((u16)-1) | (adev->rate_bcast << 16);
++ }
++ txbuf->ctrl1 = DESC_CTL_FIRSTFRAG;
++ if (1 == adev->preamble_cur)
++ SET_BIT(txbuf->ctrl1, DESC_CTL_SHORT_PREAMBLE);
++ txbuf->ctrl2 = 0;
++ txbuf->data_len = cpu_to_le16(wlanpkt_len);
++
++ if (unlikely(acx_debug & L_DATA)) {
++ printk("dump of bulk out urb:\n");
++ acx_dump_bytes(txbuf, wlanpkt_len + USB_TXBUF_HDRSIZE);
++ }
++
++ if (unlikely(txurb->status == -EINPROGRESS)) {
++ printk("acx: trying to submit tx urb while already in progress\n");
++ }
++
++ /* now schedule the USB transfer */
++ usbdev = adev->usbdev;
++ outpipe = usb_sndbulkpipe(usbdev, adev->bulkoutep);
++
++ usb_fill_bulk_urb(txurb, usbdev, outpipe,
++ txbuf, /* dataptr */
++ wlanpkt_len + USB_TXBUF_HDRSIZE, /* size */
++ acxusb_i_complete_tx, /* handler */
++ tx /* handler param */
++ );
++
++ txurb->transfer_flags = URB_ASYNC_UNLINK|URB_ZERO_PACKET;
++ ucode = usb_submit_urb(txurb, GFP_ATOMIC);
++ log(L_USBRXTX, "SUBMIT TX (%d): outpipe=0x%X buf=%p txsize=%d "
++ "rate=%u errcode=%d\n", txnum, outpipe, txbuf,
++ wlanpkt_len + USB_TXBUF_HDRSIZE, txbuf->rate, ucode);
++
++ if (unlikely(ucode)) {
++ printk(KERN_ERR "acx: submit_urb() error=%d txsize=%d\n",
++ ucode, wlanpkt_len + USB_TXBUF_HDRSIZE);
++
++ /* on error, just mark the frame as done and update
++ ** the statistics
++ */
++ adev->stats.tx_errors++;
++ tx->busy = 0;
++ adev->tx_free++;
++ /* needed? if (adev->tx_free > TX_START_QUEUE) acx_wake_queue(...) */
++ }
++end:
++ FN_EXIT0;
++}
++
++
++/***********************************************************************
++*/
++static void
++acxusb_i_set_rx_mode(struct net_device *ndev)
++{
++}
++
++
++/***********************************************************************
++*/
++#ifdef HAVE_TX_TIMEOUT
++static void
++acxusb_i_tx_timeout(struct net_device *ndev)
++{
++ acx_device_t *adev = ndev2adev(ndev);
++ unsigned long flags;
++ int i;
++
++ FN_ENTER;
++
++ acx_lock(adev, flags);
++ /* unlink the URBs */
++ for (i = 0; i < ACX_TX_URB_CNT; i++) {
++ acxusb_unlink_urb(adev->usb_tx[i].urb);
++ adev->usb_tx[i].busy = 0;
++ }
++ adev->tx_free = ACX_TX_URB_CNT;
++ /* TODO: stats update */
++ acx_unlock(adev, flags);
++
++ FN_EXIT0;
++}
++#endif
++
++
++/***********************************************************************
++** init_module()
++**
++** This function is invoked upon loading of the kernel module.
++** It registers itself at the kernel's USB subsystem.
++**
++** Returns: Errorcode on failure, 0 on success
++*/
++int __init
++acxusb_e_init_module(void)
++{
++ log(L_INIT, "USB module " ACX_RELEASE " initialized, "
++ "probing for devices...\n");
++ return usb_register(&acxusb_driver);
++}
++
++
++
++/***********************************************************************
++** cleanup_module()
++**
++** This function is invoked as last step of the module unloading. It simply
++** deregisters this module at the kernel's USB subsystem.
++*/
++void __exit
++acxusb_e_cleanup_module()
++{
++ usb_deregister(&acxusb_driver);
++}
++
++
++/***********************************************************************
++** DEBUG STUFF
++*/
++#if ACX_DEBUG
++
++#ifdef UNUSED
++static void
++dump_device(struct usb_device *usbdev)
++{
++ int i;
++ struct usb_config_descriptor *cd;
++
++ printk("acx device dump:\n");
++ printk(" devnum: %d\n", usbdev->devnum);
++ printk(" speed: %d\n", usbdev->speed);
++ printk(" tt: 0x%X\n", (unsigned int)(usbdev->tt));
++ printk(" ttport: %d\n", (unsigned int)(usbdev->ttport));
++ printk(" toggle[0]: 0x%X toggle[1]: 0x%X\n", (unsigned int)(usbdev->toggle[0]), (unsigned int)(usbdev->toggle[1]));
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11)
++ /* This saw a change after 2.6.10 */
++ printk(" ep_in wMaxPacketSize: ");
++ for (i = 0; i < 16; ++i)
++ if (usbdev->ep_in[i] != NULL)
++ printk("%d:%d ", i, usbdev->ep_in[i]->desc.wMaxPacketSize);
++ printk("\n");
++ printk(" ep_out wMaxPacketSize: ");
++ for (i = 0; i < VEC_SIZE(usbdev->ep_out); ++i)
++ if (usbdev->ep_out[i] != NULL)
++ printk("%d:%d ", i, usbdev->ep_out[i]->desc.wMaxPacketSize);
++ printk("\n");
++#else
++ printk(" epmaxpacketin: ");
++ for (i = 0; i < 16; i++)
++ printk("%d ", usbdev->epmaxpacketin[i]);
++ printk("\n");
++ printk(" epmaxpacketout: ");
++ for (i = 0; i < 16; i++)
++ printk("%d ", usbdev->epmaxpacketout[i]);
++ printk("\n");
++#endif
++ printk(" parent: 0x%X\n", (unsigned int)usbdev->parent);
++ printk(" bus: 0x%X\n", (unsigned int)usbdev->bus);
++#ifdef NO_DATATYPE
++ printk(" configs: ");
++ for (i = 0; i < usbdev->descriptor.bNumConfigurations; i++)
++ printk("0x%X ", usbdev->config[i]);
++ printk("\n");
++#endif
++ printk(" actconfig: %p\n", usbdev->actconfig);
++ dump_device_descriptor(&usbdev->descriptor);
++
++ cd = &usbdev->config->desc;
++ dump_config_descriptor(cd);
++}
++
++
++/***********************************************************************
++*/
++static void
++dump_config_descriptor(struct usb_config_descriptor *cd)
++{
++ printk("Configuration Descriptor:\n");
++ if (!cd) {
++ printk("NULL\n");
++ return;
++ }
++ printk(" bLength: %d (0x%X)\n", cd->bLength, cd->bLength);
++ printk(" bDescriptorType: %d (0x%X)\n", cd->bDescriptorType, cd->bDescriptorType);
++ printk(" bNumInterfaces: %d (0x%X)\n", cd->bNumInterfaces, cd->bNumInterfaces);
++ printk(" bConfigurationValue: %d (0x%X)\n", cd->bConfigurationValue, cd->bConfigurationValue);
++ printk(" iConfiguration: %d (0x%X)\n", cd->iConfiguration, cd->iConfiguration);
++ printk(" bmAttributes: %d (0x%X)\n", cd->bmAttributes, cd->bmAttributes);
++ /* printk(" MaxPower: %d (0x%X)\n", cd->bMaxPower, cd->bMaxPower); */
++}
++
++
++static void
++dump_device_descriptor(struct usb_device_descriptor *dd)
++{
++ printk("Device Descriptor:\n");
++ if (!dd) {
++ printk("NULL\n");
++ return;
++ }
++ printk(" bLength: %d (0x%X)\n", dd->bLength, dd->bLength);
++ printk(" bDescriptortype: %d (0x%X)\n", dd->bDescriptorType, dd->bDescriptorType);
++ printk(" bcdUSB: %d (0x%X)\n", dd->bcdUSB, dd->bcdUSB);
++ printk(" bDeviceClass: %d (0x%X)\n", dd->bDeviceClass, dd->bDeviceClass);
++ printk(" bDeviceSubClass: %d (0x%X)\n", dd->bDeviceSubClass, dd->bDeviceSubClass);
++ printk(" bDeviceProtocol: %d (0x%X)\n", dd->bDeviceProtocol, dd->bDeviceProtocol);
++ printk(" bMaxPacketSize0: %d (0x%X)\n", dd->bMaxPacketSize0, dd->bMaxPacketSize0);
++ printk(" idVendor: %d (0x%X)\n", dd->idVendor, dd->idVendor);
++ printk(" idProduct: %d (0x%X)\n", dd->idProduct, dd->idProduct);
++ printk(" bcdDevice: %d (0x%X)\n", dd->bcdDevice, dd->bcdDevice);
++ printk(" iManufacturer: %d (0x%X)\n", dd->iManufacturer, dd->iManufacturer);
++ printk(" iProduct: %d (0x%X)\n", dd->iProduct, dd->iProduct);
++ printk(" iSerialNumber: %d (0x%X)\n", dd->iSerialNumber, dd->iSerialNumber);
++ printk(" bNumConfigurations: %d (0x%X)\n", dd->bNumConfigurations, dd->bNumConfigurations);
++}
++#endif /* UNUSED */
++
++#endif /* ACX_DEBUG */
+Index: linux-2.6.23/drivers/net/wireless/acx/wlan.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23/drivers/net/wireless/acx/wlan.c 2008-01-20 21:13:40.000000000 +0000
+@@ -0,0 +1,424 @@
++/***********************************************************************
++** Copyright (C) 2003 ACX100 Open Source Project
++**
++** The contents of this file are subject to the Mozilla Public
++** License Version 1.1 (the "License"); you may not use this file
++** except in compliance with the License. You may obtain a copy of
++** the License at http://www.mozilla.org/MPL/
++**
++** Software distributed under the License is distributed on an "AS
++** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
++** implied. See the License for the specific language governing
++** rights and limitations under the License.
++**
++** Alternatively, the contents of this file may be used under the
++** terms of the GNU Public License version 2 (the "GPL"), in which
++** case the provisions of the GPL are applicable instead of the
++** above. If you wish to allow the use of your version of this file
++** only under the terms of the GPL and not to allow others to use
++** your version of this file under the MPL, indicate your decision
++** by deleting the provisions above and replace them with the notice
++** and other provisions required by the GPL. If you do not delete
++** the provisions above, a recipient may use your version of this
++** file under either the MPL or the GPL.
++** ---------------------------------------------------------------------
++** Inquiries regarding the ACX100 Open Source Project can be
++** made directly to:
++**
++** acx100-users@lists.sf.net
++** http://acx100.sf.net
++** ---------------------------------------------------------------------
++*/
++
++/***********************************************************************
++** This code is based on elements which are
++** Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
++** info@linux-wlan.com
++** http://www.linux-wlan.com
++*/
++
++#include <linux/version.h>
++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18)
++#include <linux/config.h>
++#endif
++#include <linux/types.h>
++#include <linux/if_arp.h>
++#include <linux/wireless.h>
++#include <net/iw_handler.h>
++
++#include "acx.h"
++
++
++/***********************************************************************
++*/
++#define LOG_BAD_EID(hdr,len,ie_ptr) acx_log_bad_eid(hdr, len, ((wlan_ie_t*)ie_ptr))
++
++#define IE_EID(ie_ptr) (((wlan_ie_t*)(ie_ptr))->eid)
++#define IE_LEN(ie_ptr) (((wlan_ie_t*)(ie_ptr))->len)
++#define OFFSET(hdr,off) (WLAN_HDR_A3_DATAP(hdr) + (off))
++
++
++/***********************************************************************
++** wlan_mgmt_decode_XXX
++**
++** Given a complete frame in f->hdr, sets the pointers in f to
++** the areas that correspond to the parts of the frame.
++**
++** Assumptions:
++** 1) f->len and f->hdr are already set
++** 2) f->len is the length of the MAC header + data, the FCS
++** is NOT included
++** 3) all members except len and hdr are zero
++** Arguments:
++** f frame structure
++**
++** Returns:
++** nothing
++**
++** Side effects:
++** frame structure members are pointing at their
++** respective portions of the frame buffer.
++*/
++void
++wlan_mgmt_decode_beacon(wlan_fr_beacon_t * f)
++{
++ u8 *ie_ptr;
++ u8 *end = (u8*)f->hdr + f->len;
++
++ f->type = WLAN_FSTYPE_BEACON;
++
++ /*-- Fixed Fields ----*/
++ f->ts = (u64 *) OFFSET(f->hdr, WLAN_BEACON_OFF_TS);
++ f->bcn_int = (u16 *) OFFSET(f->hdr, WLAN_BEACON_OFF_BCN_INT);
++ f->cap_info = (u16 *) OFFSET(f->hdr, WLAN_BEACON_OFF_CAPINFO);
++
++ /*-- Information elements */
++ ie_ptr = OFFSET(f->hdr, WLAN_BEACON_OFF_SSID);
++ while (ie_ptr < end) {
++ switch (IE_EID(ie_ptr)) {
++ case WLAN_EID_SSID:
++ f->ssid = (wlan_ie_ssid_t *) ie_ptr;
++ break;
++ case WLAN_EID_SUPP_RATES:
++ f->supp_rates = (wlan_ie_supp_rates_t *) ie_ptr;
++ break;
++ case WLAN_EID_EXT_RATES:
++ f->ext_rates = (wlan_ie_supp_rates_t *) ie_ptr;
++ break;
++ case WLAN_EID_FH_PARMS:
++ f->fh_parms = (wlan_ie_fh_parms_t *) ie_ptr;
++ break;
++ case WLAN_EID_DS_PARMS:
++ f->ds_parms = (wlan_ie_ds_parms_t *) ie_ptr;
++ break;
++ case WLAN_EID_CF_PARMS:
++ f->cf_parms = (wlan_ie_cf_parms_t *) ie_ptr;
++ break;
++ case WLAN_EID_IBSS_PARMS:
++ f->ibss_parms = (wlan_ie_ibss_parms_t *) ie_ptr;
++ break;
++ case WLAN_EID_TIM:
++ f->tim = (wlan_ie_tim_t *) ie_ptr;
++ break;
++ case WLAN_EID_ERP_INFO:
++ f->erp = (wlan_ie_erp_t *) ie_ptr;
++ break;
++
++ case WLAN_EID_COUNTRY:
++ /* was seen: 07 06 47 42 20 01 0D 14 */
++ case WLAN_EID_PWR_CONSTRAINT:
++ /* was seen by Ashwin Mansinghka <ashwin_man@yahoo.com> from
++ Atheros-based PCI card in AP mode using madwifi drivers: */
++ /* 20 01 00 */
++ case WLAN_EID_NONERP:
++ /* was seen from WRT54GS with OpenWrt: 2F 01 07 */
++ case WLAN_EID_UNKNOWN128:
++ /* was seen by Jacek Jablonski <conexion2000@gmail.com> from Orinoco AP */
++ /* 80 06 00 60 1D 2C 3B 00 */
++ case WLAN_EID_UNKNOWN133:
++ /* was seen by David Bronaugh <dbronaugh@linuxboxen.org> from ???? */
++ /* 85 1E 00 00 84 12 07 00 FF 00 11 00 61 70 63 31 */
++ /* 63 73 72 30 34 32 00 00 00 00 00 00 00 00 00 25 */
++ case WLAN_EID_UNKNOWN223:
++ /* was seen by Carlos Martin <carlosmn@gmail.com> from ???? */
++ /* DF 20 01 1E 04 00 00 00 06 63 09 02 FF 0F 30 30 */
++ /* 30 42 36 42 33 34 30 39 46 31 00 00 00 00 00 00 00 00 */
++ case WLAN_EID_GENERIC:
++ /* WPA: hostap code:
++ if (pos[1] >= 4 &&
++ pos[2] == 0x00 && pos[3] == 0x50 &&
++ pos[4] == 0xf2 && pos[5] == 1) {
++ wpa = pos;
++ wpa_len = pos[1] + 2;
++ }
++ TI x4 mode: seen DD 04 08 00 28 00
++ (08 00 28 is TI's OUI)
++ last byte is probably 0/1 - disabled/enabled
++ */
++ case WLAN_EID_RSN:
++ /* hostap does something with it:
++ rsn = pos;
++ rsn_len = pos[1] + 2;
++ */
++ break;
++
++ default:
++ LOG_BAD_EID(f->hdr, f->len, ie_ptr);
++ break;
++ }
++ ie_ptr = ie_ptr + 2 + IE_LEN(ie_ptr);
++ }
++}
++
++
++#ifdef UNUSED
++void wlan_mgmt_decode_ibssatim(wlan_fr_ibssatim_t * f)
++{
++ f->type = WLAN_FSTYPE_ATIM;
++ /*-- Fixed Fields ----*/
++ /*-- Information elements */
++}
++#endif /* UNUSED */
++
++void
++wlan_mgmt_decode_disassoc(wlan_fr_disassoc_t * f)
++{
++ f->type = WLAN_FSTYPE_DISASSOC;
++
++ /*-- Fixed Fields ----*/
++ f->reason = (u16 *) OFFSET(f->hdr, WLAN_DISASSOC_OFF_REASON);
++
++ /*-- Information elements */
++}
++
++
++void
++wlan_mgmt_decode_assocreq(wlan_fr_assocreq_t * f)
++{
++ u8 *ie_ptr;
++ u8 *end = (u8*)f->hdr + f->len;
++
++
++ f->type = WLAN_FSTYPE_ASSOCREQ;
++
++ /*-- Fixed Fields ----*/
++ f->cap_info = (u16 *) OFFSET(f->hdr, WLAN_ASSOCREQ_OFF_CAP_INFO);
++ f->listen_int = (u16 *) OFFSET(f->hdr, WLAN_ASSOCREQ_OFF_LISTEN_INT);
++
++ /*-- Information elements */
++ ie_ptr = OFFSET(f->hdr, WLAN_ASSOCREQ_OFF_SSID);
++ while (ie_ptr < end) {
++ switch (IE_EID(ie_ptr)) {
++ case WLAN_EID_SSID:
++ f->ssid = (wlan_ie_ssid_t *) ie_ptr;
++ break;
++ case WLAN_EID_SUPP_RATES:
++ f->supp_rates = (wlan_ie_supp_rates_t *) ie_ptr;
++ break;
++ case WLAN_EID_EXT_RATES:
++ f->ext_rates = (wlan_ie_supp_rates_t *) ie_ptr;
++ break;
++ default:
++ LOG_BAD_EID(f->hdr, f->len, ie_ptr);
++ break;
++ }
++ ie_ptr = ie_ptr + 2 + IE_LEN(ie_ptr);
++ }
++}
++
++
++void
++wlan_mgmt_decode_assocresp(wlan_fr_assocresp_t * f)
++{
++ f->type = WLAN_FSTYPE_ASSOCRESP;
++
++ /*-- Fixed Fields ----*/
++ f->cap_info = (u16 *) OFFSET(f->hdr, WLAN_ASSOCRESP_OFF_CAP_INFO);
++ f->status = (u16 *) OFFSET(f->hdr, WLAN_ASSOCRESP_OFF_STATUS);
++ f->aid = (u16 *) OFFSET(f->hdr, WLAN_ASSOCRESP_OFF_AID);
++
++ /*-- Information elements */
++ f->supp_rates = (wlan_ie_supp_rates_t *)
++ OFFSET(f->hdr, WLAN_ASSOCRESP_OFF_SUPP_RATES);
++}
++
++
++#ifdef UNUSED
++void
++wlan_mgmt_decode_reassocreq(wlan_fr_reassocreq_t * f)
++{
++ u8 *ie_ptr;
++ u8 *end = (u8*)f->hdr + f->len;
++
++ f->type = WLAN_FSTYPE_REASSOCREQ;
++
++ /*-- Fixed Fields ----*/
++ f->cap_info = (u16 *) OFFSET(f->hdr, WLAN_REASSOCREQ_OFF_CAP_INFO);
++ f->listen_int = (u16 *) OFFSET(f->hdr, WLAN_REASSOCREQ_OFF_LISTEN_INT);
++ f->curr_ap = (u8 *) OFFSET(f->hdr, WLAN_REASSOCREQ_OFF_CURR_AP);
++
++ /*-- Information elements */
++ ie_ptr = OFFSET(f->hdr, WLAN_REASSOCREQ_OFF_SSID);
++ while (ie_ptr < end) {
++ switch (IE_EID(ie_ptr)) {
++ case WLAN_EID_SSID:
++ f->ssid = (wlan_ie_ssid_t *) ie_ptr;
++ break;
++ case WLAN_EID_SUPP_RATES:
++ f->supp_rates = (wlan_ie_supp_rates_t *) ie_ptr;
++ break;
++ case WLAN_EID_EXT_RATES:
++ f->ext_rates = (wlan_ie_supp_rates_t *) ie_ptr;
++ break;
++ default:
++ LOG_BAD_EID(f->hdr, f->len, ie_ptr);
++ break;
++ }
++ ie_ptr = ie_ptr + 2 + IE_LEN(ie_ptr);
++ }
++}
++
++
++void
++wlan_mgmt_decode_reassocresp(wlan_fr_reassocresp_t * f)
++{
++ f->type = WLAN_FSTYPE_REASSOCRESP;
++
++ /*-- Fixed Fields ----*/
++ f->cap_info = (u16 *) OFFSET(f->hdr, WLAN_REASSOCRESP_OFF_CAP_INFO);
++ f->status = (u16 *) OFFSET(f->hdr, WLAN_REASSOCRESP_OFF_STATUS);
++ f->aid = (u16 *) OFFSET(f->hdr, WLAN_REASSOCRESP_OFF_AID);
++
++ /*-- Information elements */
++ f->supp_rates = (wlan_ie_supp_rates_t *)
++ OFFSET(f->hdr, WLAN_REASSOCRESP_OFF_SUPP_RATES);
++}
++
++
++void
++wlan_mgmt_decode_probereq(wlan_fr_probereq_t * f)
++{
++ u8 *ie_ptr;
++ u8 *end = (u8*)f->hdr + f->len;
++
++ f->type = WLAN_FSTYPE_PROBEREQ;
++
++ /*-- Fixed Fields ----*/
++
++ /*-- Information elements */
++ ie_ptr = OFFSET(f->hdr, WLAN_PROBEREQ_OFF_SSID);
++ while (ie_ptr < end) {
++ switch (IE_EID(ie_ptr)) {
++ case WLAN_EID_SSID:
++ f->ssid = (wlan_ie_ssid_t *) ie_ptr;
++ break;
++ case WLAN_EID_SUPP_RATES:
++ f->supp_rates = (wlan_ie_supp_rates_t *) ie_ptr;
++ break;
++ case WLAN_EID_EXT_RATES:
++ f->ext_rates = (wlan_ie_supp_rates_t *) ie_ptr;
++ break;
++ default:
++ LOG_BAD_EID(f->hdr, f->len, ie_ptr);
++ break;
++ }
++ ie_ptr = ie_ptr + 2 + IE_LEN(ie_ptr);
++ }
++}
++#endif /* UNUSED */
++
++
++/* TODO: decoding of beacon and proberesp can be merged (similar structure) */
++void
++wlan_mgmt_decode_proberesp(wlan_fr_proberesp_t * f)
++{
++ u8 *ie_ptr;
++ u8 *end = (u8*)f->hdr + f->len;
++
++ f->type = WLAN_FSTYPE_PROBERESP;
++
++ /*-- Fixed Fields ----*/
++ f->ts = (u64 *) OFFSET(f->hdr, WLAN_PROBERESP_OFF_TS);
++ f->bcn_int = (u16 *) OFFSET(f->hdr, WLAN_PROBERESP_OFF_BCN_INT);
++ f->cap_info = (u16 *) OFFSET(f->hdr, WLAN_PROBERESP_OFF_CAP_INFO);
++
++ /*-- Information elements */
++ ie_ptr = OFFSET(f->hdr, WLAN_PROBERESP_OFF_SSID);
++ while (ie_ptr < end) {
++ switch (IE_EID(ie_ptr)) {
++ case WLAN_EID_SSID:
++ f->ssid = (wlan_ie_ssid_t *) ie_ptr;
++ break;
++ case WLAN_EID_SUPP_RATES:
++ f->supp_rates = (wlan_ie_supp_rates_t *) ie_ptr;
++ break;
++ case WLAN_EID_EXT_RATES:
++ f->ext_rates = (wlan_ie_supp_rates_t *) ie_ptr;
++ break;
++ case WLAN_EID_FH_PARMS:
++ f->fh_parms = (wlan_ie_fh_parms_t *) ie_ptr;
++ break;
++ case WLAN_EID_DS_PARMS:
++ f->ds_parms = (wlan_ie_ds_parms_t *) ie_ptr;
++ break;
++ case WLAN_EID_CF_PARMS:
++ f->cf_parms = (wlan_ie_cf_parms_t *) ie_ptr;
++ break;
++ case WLAN_EID_IBSS_PARMS:
++ f->ibss_parms = (wlan_ie_ibss_parms_t *) ie_ptr;
++ break;
++#ifdef DONT_DO_IT_ADD_REAL_HANDLING_INSTEAD
++ case WLAN_EID_COUNTRY:
++ break;
++ ...
++#endif
++#ifdef SENT_HERE_BY_OPENWRT
++ /* should those be trapped or handled?? */
++ case WLAN_EID_ERP_INFO:
++ break;
++ case WLAN_EID_NONERP:
++ break;
++ case WLAN_EID_GENERIC:
++ break;
++#endif
++ default:
++ LOG_BAD_EID(f->hdr, f->len, ie_ptr);
++ break;
++ }
++
++ ie_ptr = ie_ptr + 2 + IE_LEN(ie_ptr);
++ }
++}
++
++
++void
++wlan_mgmt_decode_authen(wlan_fr_authen_t * f)
++{
++ u8 *ie_ptr;
++ u8 *end = (u8*)f->hdr + f->len;
++
++ f->type = WLAN_FSTYPE_AUTHEN;
++
++ /*-- Fixed Fields ----*/
++ f->auth_alg = (u16 *) OFFSET(f->hdr, WLAN_AUTHEN_OFF_AUTH_ALG);
++ f->auth_seq = (u16 *) OFFSET(f->hdr, WLAN_AUTHEN_OFF_AUTH_SEQ);
++ f->status = (u16 *) OFFSET(f->hdr, WLAN_AUTHEN_OFF_STATUS);
++
++ /*-- Information elements */
++ ie_ptr = OFFSET(f->hdr, WLAN_AUTHEN_OFF_CHALLENGE);
++ if ((ie_ptr < end) && (IE_EID(ie_ptr) == WLAN_EID_CHALLENGE)) {
++ f->challenge = (wlan_ie_challenge_t *) ie_ptr;
++ }
++}
++
++
++void
++wlan_mgmt_decode_deauthen(wlan_fr_deauthen_t * f)
++{
++ f->type = WLAN_FSTYPE_DEAUTHEN;
++
++ /*-- Fixed Fields ----*/
++ f->reason = (u16 *) OFFSET(f->hdr, WLAN_DEAUTHEN_OFF_REASON);
++
++ /*-- Information elements */
++}
+Index: linux-2.6.23/drivers/net/wireless/acx/wlan_compat.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23/drivers/net/wireless/acx/wlan_compat.h 2008-01-20 21:13:40.000000000 +0000
+@@ -0,0 +1,260 @@
++/***********************************************************************
++** Copyright (C) 2003 ACX100 Open Source Project
++**
++** The contents of this file are subject to the Mozilla Public
++** License Version 1.1 (the "License"); you may not use this file
++** except in compliance with the License. You may obtain a copy of
++** the License at http://www.mozilla.org/MPL/
++**
++** Software distributed under the License is distributed on an "AS
++** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
++** implied. See the License for the specific language governing
++** rights and limitations under the License.
++**
++** Alternatively, the contents of this file may be used under the
++** terms of the GNU Public License version 2 (the "GPL"), in which
++** case the provisions of the GPL are applicable instead of the
++** above. If you wish to allow the use of your version of this file
++** only under the terms of the GPL and not to allow others to use
++** your version of this file under the MPL, indicate your decision
++** by deleting the provisions above and replace them with the notice
++** and other provisions required by the GPL. If you do not delete
++** the provisions above, a recipient may use your version of this
++** file under either the MPL or the GPL.
++** ---------------------------------------------------------------------
++** Inquiries regarding the ACX100 Open Source Project can be
++** made directly to:
++**
++** acx100-users@lists.sf.net
++** http://acx100.sf.net
++** ---------------------------------------------------------------------
++*/
++
++/***********************************************************************
++** This code is based on elements which are
++** Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
++** info@linux-wlan.com
++** http://www.linux-wlan.com
++*/
++
++/*=============================================================*/
++/*------ Establish Platform Identity --------------------------*/
++/*=============================================================*/
++/* Key macros: */
++/* WLAN_CPU_FAMILY */
++#define WLAN_Ix86 1
++#define WLAN_PPC 2
++#define WLAN_Ix96 3
++#define WLAN_ARM 4
++#define WLAN_ALPHA 5
++#define WLAN_MIPS 6
++#define WLAN_HPPA 7
++#define WLAN_SPARC 8
++#define WLAN_SH 9
++#define WLAN_x86_64 10
++/* WLAN_CPU_CORE */
++#define WLAN_I386CORE 1
++#define WLAN_PPCCORE 2
++#define WLAN_I296 3
++#define WLAN_ARMCORE 4
++#define WLAN_ALPHACORE 5
++#define WLAN_MIPSCORE 6
++#define WLAN_HPPACORE 7
++/* WLAN_CPU_PART */
++#define WLAN_I386PART 1
++#define WLAN_MPC860 2
++#define WLAN_MPC823 3
++#define WLAN_I296SA 4
++#define WLAN_PPCPART 5
++#define WLAN_ARMPART 6
++#define WLAN_ALPHAPART 7
++#define WLAN_MIPSPART 8
++#define WLAN_HPPAPART 9
++/* WLAN_SYSARCH */
++#define WLAN_PCAT 1
++#define WLAN_MBX 2
++#define WLAN_RPX 3
++#define WLAN_LWARCH 4
++#define WLAN_PMAC 5
++#define WLAN_SKIFF 6
++#define WLAN_BITSY 7
++#define WLAN_ALPHAARCH 7
++#define WLAN_MIPSARCH 9
++#define WLAN_HPPAARCH 10
++/* WLAN_HOSTIF (generally set on the command line, not detected) */
++#define WLAN_PCMCIA 1
++#define WLAN_ISA 2
++#define WLAN_PCI 3
++#define WLAN_USB 4
++#define WLAN_PLX 5
++
++/* Note: the PLX HOSTIF above refers to some vendors implementations for */
++/* PCI. It's a PLX chip that is a PCI to PCMCIA adapter, but it */
++/* isn't a real PCMCIA host interface adapter providing all the */
++/* card&socket services. */
++
++#ifdef __powerpc__
++#ifndef __ppc__
++#define __ppc__
++#endif
++#endif
++
++#if (defined(CONFIG_PPC) || defined(CONFIG_8xx))
++#ifndef __ppc__
++#define __ppc__
++#endif
++#endif
++
++#if defined(__x86_64__)
++ #define WLAN_CPU_FAMILY WLAN_x86_64
++ #define WLAN_SYSARCH WLAN_PCAT
++#elif defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__)
++ #define WLAN_CPU_FAMILY WLAN_Ix86
++ #define WLAN_CPU_CORE WLAN_I386CORE
++ #define WLAN_CPU_PART WLAN_I386PART
++ #define WLAN_SYSARCH WLAN_PCAT
++#elif defined(__ppc__)
++ #define WLAN_CPU_FAMILY WLAN_PPC
++ #define WLAN_CPU_CORE WLAN_PPCCORE
++ #if defined(CONFIG_MBX)
++ #define WLAN_CPU_PART WLAN_MPC860
++ #define WLAN_SYSARCH WLAN_MBX
++ #elif defined(CONFIG_RPXLITE)
++ #define WLAN_CPU_PART WLAN_MPC823
++ #define WLAN_SYSARCH WLAN_RPX
++ #elif defined(CONFIG_RPXCLASSIC)
++ #define WLAN_CPU_PART WLAN_MPC860
++ #define WLAN_SYSARCH WLAN_RPX
++ #else
++ #define WLAN_CPU_PART WLAN_PPCPART
++ #define WLAN_SYSARCH WLAN_PMAC
++ #endif
++#elif defined(__arm__)
++ #define WLAN_CPU_FAMILY WLAN_ARM
++ #define WLAN_CPU_CORE WLAN_ARMCORE
++ #define WLAN_CPU_PART WLAN_ARM_PART
++ #define WLAN_SYSARCH WLAN_SKIFF
++#elif defined(__alpha__)
++ #define WLAN_CPU_FAMILY WLAN_ALPHA
++ #define WLAN_CPU_CORE WLAN_ALPHACORE
++ #define WLAN_CPU_PART WLAN_ALPHAPART
++ #define WLAN_SYSARCH WLAN_ALPHAARCH
++#elif defined(__mips__)
++ #define WLAN_CPU_FAMILY WLAN_MIPS
++ #define WLAN_CPU_CORE WLAN_MIPSCORE
++ #define WLAN_CPU_PART WLAN_MIPSPART
++ #define WLAN_SYSARCH WLAN_MIPSARCH
++#elif defined(__hppa__)
++ #define WLAN_CPU_FAMILY WLAN_HPPA
++ #define WLAN_CPU_CORE WLAN_HPPACORE
++ #define WLAN_CPU_PART WLAN_HPPAPART
++ #define WLAN_SYSARCH WLAN_HPPAARCH
++#elif defined(__sparc__)
++ #define WLAN_CPU_FAMILY WLAN_SPARC
++ #define WLAN_SYSARCH WLAN_SPARC
++#elif defined(__sh__)
++ #define WLAN_CPU_FAMILY WLAN_SH
++ #define WLAN_SYSARCH WLAN_SHARCH
++ #ifndef __LITTLE_ENDIAN__
++ #define __LITTLE_ENDIAN__
++ #endif
++#else
++ #error "No CPU identified!"
++#endif
++
++/*
++ Some big endian machines implicitly do all I/O in little endian mode.
++
++ In particular:
++ Linux/PPC on PowerMacs (PCI)
++ Arm/Intel Xscale (PCI)
++
++ This may also affect PLX boards and other BE &| PPC platforms;
++ as new ones are discovered, add them below.
++*/
++
++#if ((WLAN_SYSARCH == WLAN_SKIFF) || (WLAN_SYSARCH == WLAN_PMAC))
++#define REVERSE_ENDIAN
++#endif
++
++/*=============================================================*/
++/*------ Hardware Portability Macros --------------------------*/
++/*=============================================================*/
++#if (WLAN_CPU_FAMILY == WLAN_PPC)
++#define wlan_inw(a) in_be16((unsigned short *)((a)+_IO_BASE))
++#define wlan_inw_le16_to_cpu(a) inw((a))
++#define wlan_outw(v,a) out_be16((unsigned short *)((a)+_IO_BASE), (v))
++#define wlan_outw_cpu_to_le16(v,a) outw((v),(a))
++#else
++#define wlan_inw(a) inw((a))
++#define wlan_inw_le16_to_cpu(a) __cpu_to_le16(inw((a)))
++#define wlan_outw(v,a) outw((v),(a))
++#define wlan_outw_cpu_to_le16(v,a) outw(__cpu_to_le16((v)),(a))
++#endif
++
++/*=============================================================*/
++/*------ Bit settings -----------------------------------------*/
++/*=============================================================*/
++#define ieee2host16(n) __le16_to_cpu(n)
++#define ieee2host32(n) __le32_to_cpu(n)
++#define host2ieee16(n) __cpu_to_le16(n)
++#define host2ieee32(n) __cpu_to_le32(n)
++
++/* for constants */
++#ifdef __LITTLE_ENDIAN
++ #define IEEE16(a,n) a = n, a##i = n,
++#else
++ #ifdef __BIG_ENDIAN
++ /* shifts would produce gcc warnings. Oh well... */
++ #define IEEE16(a,n) a = n, a##i = ((n&0xff)*256 + ((n&0xff00)/256)),
++ #else
++ #error give me endianness or give me death
++ #endif
++#endif
++
++/*=============================================================*/
++/*------ Compiler Portability Macros --------------------------*/
++/*=============================================================*/
++#define WLAN_PACKED __attribute__ ((packed))
++
++/* Interrupt handler backwards compatibility stuff */
++#ifndef IRQ_NONE
++#define IRQ_NONE
++#define IRQ_HANDLED
++typedef void irqreturn_t;
++#endif
++
++#ifndef ARPHRD_IEEE80211_PRISM
++#define ARPHRD_IEEE80211_PRISM 802
++#endif
++
++#define ETH_P_80211_RAW (ETH_P_ECONET + 1)
++
++/*============================================================================*
++ * Constants *
++ *============================================================================*/
++#define WLAN_IEEE_OUI_LEN 3
++
++/*============================================================================*
++ * Types *
++ *============================================================================*/
++
++/* local ether header type */
++typedef struct wlan_ethhdr {
++ u8 daddr[ETH_ALEN];
++ u8 saddr[ETH_ALEN];
++ u16 type;
++} WLAN_PACKED wlan_ethhdr_t;
++
++/* local llc header type */
++typedef struct wlan_llc {
++ u8 dsap;
++ u8 ssap;
++ u8 ctl;
++} WLAN_PACKED wlan_llc_t;
++
++/* local snap header type */
++typedef struct wlan_snap {
++ u8 oui[WLAN_IEEE_OUI_LEN];
++ u16 type;
++} WLAN_PACKED wlan_snap_t;
+Index: linux-2.6.23/drivers/net/wireless/acx/wlan_hdr.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23/drivers/net/wireless/acx/wlan_hdr.h 2008-01-20 21:13:40.000000000 +0000
+@@ -0,0 +1,497 @@
++/***********************************************************************
++** Copyright (C) 2003 ACX100 Open Source Project
++**
++** The contents of this file are subject to the Mozilla Public
++** License Version 1.1 (the "License"); you may not use this file
++** except in compliance with the License. You may obtain a copy of
++** the License at http://www.mozilla.org/MPL/
++**
++** Software distributed under the License is distributed on an "AS
++** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
++** implied. See the License for the specific language governing
++** rights and limitations under the License.
++**
++** Alternatively, the contents of this file may be used under the
++** terms of the GNU Public License version 2 (the "GPL"), in which
++** case the provisions of the GPL are applicable instead of the
++** above. If you wish to allow the use of your version of this file
++** only under the terms of the GPL and not to allow others to use
++** your version of this file under the MPL, indicate your decision
++** by deleting the provisions above and replace them with the notice
++** and other provisions required by the GPL. If you do not delete
++** the provisions above, a recipient may use your version of this
++** file under either the MPL or the GPL.
++** ---------------------------------------------------------------------
++** Inquiries regarding the ACX100 Open Source Project can be
++** made directly to:
++**
++** acx100-users@lists.sf.net
++** http://acx100.sf.net
++** ---------------------------------------------------------------------
++*/
++
++/***********************************************************************
++** This code is based on elements which are
++** Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
++** info@linux-wlan.com
++** http://www.linux-wlan.com
++*/
++
++/* mini-doc
++
++Here are all 11b/11g/11a rates and modulations:
++
++ 11b 11g 11a
++ --- --- ---
++ 1 |B |B |
++ 2 |Q |Q |
++ 5.5|Cp |C p|
++ 6 | |Od |O
++ 9 | |od |o
++11 |Cp |C p|
++12 | |Od |O
++18 | |od |o
++22 | | p|
++24 | |Od |O
++33 | | p|
++36 | |od |o
++48 | |od |o
++54 | |od |o
++
++Mandatory:
++ B - DBPSK (Differential Binary Phase Shift Keying)
++ Q - DQPSK (Differential Quaternary Phase Shift Keying)
++ C - CCK (Complementary Code Keying, a form of DSSS
++ (Direct Sequence Spread Spectrum) modulation)
++ O - OFDM (Orthogonal Frequency Division Multiplexing)
++Optional:
++ o - OFDM
++ d - CCK-OFDM (also known as DSSS-OFDM)
++ p - PBCC (Packet Binary Convolutional Coding)
++
++The term CCK-OFDM may be used interchangeably with DSSS-OFDM
++(the IEEE 802.11g-2003 standard uses the latter terminology).
++In the CCK-OFDM, the PLCP header of the frame uses the CCK form of DSSS,
++while the PLCP payload (the MAC frame) is modulated using OFDM.
++
++Basically, you must use CCK-OFDM if you have mixed 11b/11g environment,
++or else (pure OFDM) 11b equipment may not realize that AP
++is sending a packet and start sending its own one.
++Sadly, looks like acx111 does not support CCK-OFDM, only pure OFDM.
++
++Re PBCC: avoid using it. It makes sense only if you have
++TI "11b+" hardware. You _must_ use PBCC in order to reach 22Mbps on it.
++
++Preambles:
++
++Long preamble (at 1Mbit rate, takes 144 us):
++ 16 bytes ones
++ 2 bytes 0xF3A0 (lsb sent first)
++PLCP header follows (at 1Mbit also):
++ 1 byte Signal: speed, in 0.1Mbit units, except for:
++ 33Mbit: 33 (instead of 330 - doesn't fit in octet)
++ all CCK-OFDM rates: 30
++ 1 byte Service
++ 0,1,4: reserved
++ 2: 1=locked clock
++ 3: 1=PBCC
++ 5: Length Extension (PBCC 22,33Mbit (11g only)) <-
++ 6: Length Extension (PBCC 22,33Mbit (11g only)) <- BLACK MAGIC HERE
++ 7: Length Extension <-
++ 2 bytes Length (time needed to tx this frame)
++ a) 5.5 Mbit/s CCK
++ Length = octets*8/5.5, rounded up to integer
++ b) 11 Mbit/s CCK
++ Length = octets*8/11, rounded up to integer
++ Service bit 7:
++ 0 = rounding took less than 8/11
++ 1 = rounding took more than or equal to 8/11
++ c) 5.5 Mbit/s PBCC
++ Length = (octets+1)*8/5.5, rounded up to integer
++ d) 11 Mbit/s PBCC
++ Length = (octets+1)*8/11, rounded up to integer
++ Service bit 7:
++ 0 = rounding took less than 8/11
++ 1 = rounding took more than or equal to 8/11
++ e) 22 Mbit/s PBCC
++ Length = (octets+1)*8/22, rounded up to integer
++ Service bits 6,7:
++ 00 = rounding took less than 8/22ths
++ 01 = rounding took 8/22...15/22ths
++ 10 = rounding took 16/22ths or more.
++ f) 33 Mbit/s PBCC
++ Length = (octets+1)*8/33, rounded up to integer
++ Service bits 5,6,7:
++ 000 rounding took less than 8/33
++ 001 rounding took 8/33...15/33
++ 010 rounding took 16/33...23/33
++ 011 rounding took 24/33...31/33
++ 100 rounding took 32/33 or more
++ 2 bytes CRC
++
++PSDU follows (up to 2346 bytes at selected rate)
++
++While Signal value alone is not enough to determine rate and modulation,
++Signal+Service is always sufficient.
++
++Short preamble (at 1Mbit rate, takes 72 us):
++ 7 bytes zeroes
++ 2 bytes 0x05CF (lsb sent first)
++PLCP header follows *at 2Mbit/s*. Format is the same as in long preamble.
++PSDU follows (up to 2346 bytes at selected rate)
++
++OFDM preamble is completely different, uses OFDM
++modulation from the start and thus easily identifiable.
++Not shown here.
++*/
++
++
++/***********************************************************************
++** Constants
++*/
++
++#define WLAN_HDR_A3_LEN 24
++#define WLAN_HDR_A4_LEN 30
++/* IV structure:
++** 3 bytes: Initialization Vector (24 bits)
++** 1 byte: 0..5: padding, must be 0; 6..7: key selector (0-3)
++*/
++#define WLAN_WEP_IV_LEN 4
++/* 802.11 says 2312 but looks like 2312 is a max size of _WEPed data_ */
++#define WLAN_DATA_MAXLEN 2304
++#define WLAN_WEP_ICV_LEN 4
++#define WLAN_FCS_LEN 4
++#define WLAN_A3FR_MAXLEN (WLAN_HDR_A3_LEN + WLAN_DATA_MAXLEN)
++#define WLAN_A4FR_MAXLEN (WLAN_HDR_A4_LEN + WLAN_DATA_MAXLEN)
++#define WLAN_A3FR_MAXLEN_FCS (WLAN_HDR_A3_LEN + WLAN_DATA_MAXLEN + 4)
++#define WLAN_A4FR_MAXLEN_FCS (WLAN_HDR_A4_LEN + WLAN_DATA_MAXLEN + 4)
++#define WLAN_A3FR_MAXLEN_WEP (WLAN_A3FR_MAXLEN + 8)
++#define WLAN_A4FR_MAXLEN_WEP (WLAN_A4FR_MAXLEN + 8)
++#define WLAN_A3FR_MAXLEN_WEP_FCS (WLAN_A3FR_MAXLEN_FCS + 8)
++#define WLAN_A4FR_MAXLEN_WEP_FCS (WLAN_A4FR_MAXLEN_FCS + 8)
++
++#define WLAN_BSS_TS_LEN 8
++#define WLAN_SSID_MAXLEN 32
++#define WLAN_BEACON_FR_MAXLEN (WLAN_HDR_A3_LEN + 334)
++#define WLAN_ATIM_FR_MAXLEN (WLAN_HDR_A3_LEN + 0)
++#define WLAN_DISASSOC_FR_MAXLEN (WLAN_HDR_A3_LEN + 2)
++#define WLAN_ASSOCREQ_FR_MAXLEN (WLAN_HDR_A3_LEN + 48)
++#define WLAN_ASSOCRESP_FR_MAXLEN (WLAN_HDR_A3_LEN + 16)
++#define WLAN_REASSOCREQ_FR_MAXLEN (WLAN_HDR_A3_LEN + 54)
++#define WLAN_REASSOCRESP_FR_MAXLEN (WLAN_HDR_A3_LEN + 16)
++#define WLAN_PROBEREQ_FR_MAXLEN (WLAN_HDR_A3_LEN + 44)
++#define WLAN_PROBERESP_FR_MAXLEN (WLAN_HDR_A3_LEN + 78)
++#define WLAN_AUTHEN_FR_MAXLEN (WLAN_HDR_A3_LEN + 261)
++#define WLAN_DEAUTHEN_FR_MAXLEN (WLAN_HDR_A3_LEN + 2)
++#define WLAN_CHALLENGE_IE_LEN 130
++#define WLAN_CHALLENGE_LEN 128
++#define WLAN_WEP_MAXKEYLEN 13
++#define WLAN_WEP_NKEYS 4
++
++/*--- Frame Control Field -------------------------------------*/
++/* Frame Types */
++#define WLAN_FTYPE_MGMT 0x00
++#define WLAN_FTYPE_CTL 0x01
++#define WLAN_FTYPE_DATA 0x02
++
++/* Frame subtypes */
++/* Management */
++#define WLAN_FSTYPE_ASSOCREQ 0x00
++#define WLAN_FSTYPE_ASSOCRESP 0x01
++#define WLAN_FSTYPE_REASSOCREQ 0x02
++#define WLAN_FSTYPE_REASSOCRESP 0x03
++#define WLAN_FSTYPE_PROBEREQ 0x04
++#define WLAN_FSTYPE_PROBERESP 0x05
++#define WLAN_FSTYPE_BEACON 0x08
++#define WLAN_FSTYPE_ATIM 0x09
++#define WLAN_FSTYPE_DISASSOC 0x0a
++#define WLAN_FSTYPE_AUTHEN 0x0b
++#define WLAN_FSTYPE_DEAUTHEN 0x0c
++
++/* Control */
++#define WLAN_FSTYPE_PSPOLL 0x0a
++#define WLAN_FSTYPE_RTS 0x0b
++#define WLAN_FSTYPE_CTS 0x0c
++#define WLAN_FSTYPE_ACK 0x0d
++#define WLAN_FSTYPE_CFEND 0x0e
++#define WLAN_FSTYPE_CFENDCFACK 0x0f
++
++/* Data */
++#define WLAN_FSTYPE_DATAONLY 0x00
++#define WLAN_FSTYPE_DATA_CFACK 0x01
++#define WLAN_FSTYPE_DATA_CFPOLL 0x02
++#define WLAN_FSTYPE_DATA_CFACK_CFPOLL 0x03
++#define WLAN_FSTYPE_NULL 0x04
++#define WLAN_FSTYPE_CFACK 0x05
++#define WLAN_FSTYPE_CFPOLL 0x06
++#define WLAN_FSTYPE_CFACK_CFPOLL 0x07
++
++/*--- FC Constants v. 2.0 ------------------------------------*/
++/* Each constant is defined twice: WF_CONST is in host */
++/* byteorder, WF_CONSTi is in ieee byteorder. */
++/* Usage: */
++/* printf("the frame subtype is %X", WF_FC_FTYPEi & rx.fc); */
++/* tx.fc = WF_FTYPE_CTLi | WF_FSTYPE_RTSi; */
++/*------------------------------------------------------------*/
++
++enum {
++/*--- Frame Control Field -------------------------------------*/
++/* Protocol version: always 0 for current 802.11 standards */
++IEEE16(WF_FC_PVER, 0x0003)
++IEEE16(WF_FC_FTYPE, 0x000c)
++IEEE16(WF_FC_FSTYPE, 0x00f0)
++IEEE16(WF_FC_TODS, 0x0100)
++IEEE16(WF_FC_FROMDS, 0x0200)
++IEEE16(WF_FC_FROMTODS, 0x0300)
++IEEE16(WF_FC_MOREFRAG, 0x0400)
++IEEE16(WF_FC_RETRY, 0x0800)
++/* Indicates PS mode in which STA will be after successful completion
++** of current frame exchange sequence. Always 0 for AP frames */
++IEEE16(WF_FC_PWRMGT, 0x1000)
++/* What MoreData=1 means:
++** From AP to STA in PS mode: don't sleep yet, I have more frames for you
++** From Contention-Free (CF) Pollable STA in response to a CF-Poll:
++** STA has buffered frames for transmission in response to next CF-Poll
++** Bcast/mcast frames transmitted from AP:
++** when additional bcast/mcast frames remain to be transmitted by AP
++** during this beacon interval
++** In all other cases MoreData=0 */
++IEEE16(WF_FC_MOREDATA, 0x2000)
++IEEE16(WF_FC_ISWEP, 0x4000)
++IEEE16(WF_FC_ORDER, 0x8000)
++
++/* Frame Types */
++IEEE16(WF_FTYPE_MGMT, 0x00)
++IEEE16(WF_FTYPE_CTL, 0x04)
++IEEE16(WF_FTYPE_DATA, 0x08)
++
++/* Frame subtypes */
++/* Management */
++IEEE16(WF_FSTYPE_ASSOCREQ, 0x00)
++IEEE16(WF_FSTYPE_ASSOCRESP, 0x10)
++IEEE16(WF_FSTYPE_REASSOCREQ, 0x20)
++IEEE16(WF_FSTYPE_REASSOCRESP, 0x30)
++IEEE16(WF_FSTYPE_PROBEREQ, 0x40)
++IEEE16(WF_FSTYPE_PROBERESP, 0x50)
++IEEE16(WF_FSTYPE_BEACON, 0x80)
++IEEE16(WF_FSTYPE_ATIM, 0x90)
++IEEE16(WF_FSTYPE_DISASSOC, 0xa0)
++IEEE16(WF_FSTYPE_AUTHEN, 0xb0)
++IEEE16(WF_FSTYPE_DEAUTHEN, 0xc0)
++
++/* Control */
++IEEE16(WF_FSTYPE_PSPOLL, 0xa0)
++IEEE16(WF_FSTYPE_RTS, 0xb0)
++IEEE16(WF_FSTYPE_CTS, 0xc0)
++IEEE16(WF_FSTYPE_ACK, 0xd0)
++IEEE16(WF_FSTYPE_CFEND, 0xe0)
++IEEE16(WF_FSTYPE_CFENDCFACK, 0xf0)
++
++/* Data */
++IEEE16(WF_FSTYPE_DATAONLY, 0x00)
++IEEE16(WF_FSTYPE_DATA_CFACK, 0x10)
++IEEE16(WF_FSTYPE_DATA_CFPOLL, 0x20)
++IEEE16(WF_FSTYPE_DATA_CFACK_CFPOLL, 0x30)
++IEEE16(WF_FSTYPE_NULL, 0x40)
++IEEE16(WF_FSTYPE_CFACK, 0x50)
++IEEE16(WF_FSTYPE_CFPOLL, 0x60)
++IEEE16(WF_FSTYPE_CFACK_CFPOLL, 0x70)
++};
++
++
++/***********************************************************************
++** Macros
++*/
++
++/*--- Duration Macros ----------------------------------------*/
++/* Macros to get/set the bitfields of the Duration Field */
++/* - the duration value is only valid when bit15 is zero */
++/* - the firmware handles these values, so I'm not going */
++/* to use these macros right now. */
++/*------------------------------------------------------------*/
++
++/*--- Sequence Control Macros -------------------------------*/
++/* Macros to get/set the bitfields of the Sequence Control */
++/* Field. */
++/*------------------------------------------------------------*/
++#define WLAN_GET_SEQ_FRGNUM(n) ((u16)(n) & 0x000f)
++#define WLAN_GET_SEQ_SEQNUM(n) (((u16)(n) & 0xfff0) >> 4)
++
++/*--- Data ptr macro -----------------------------------------*/
++/* Creates a u8* to the data portion of a frame */
++/* Assumes you're passing in a ptr to the beginning of the hdr*/
++/*------------------------------------------------------------*/
++#define WLAN_HDR_A3_DATAP(p) (((u8*)(p)) + WLAN_HDR_A3_LEN)
++#define WLAN_HDR_A4_DATAP(p) (((u8*)(p)) + WLAN_HDR_A4_LEN)
++
++
++/***********************************************************************
++** Types
++*/
++
++/* 802.11 header type
++**
++** Note the following:
++** a1 *always* is receiver's mac or bcast/mcast
++** a2 *always* is transmitter's mac, if a2 exists
++** seq: [0:3] frag#, [4:15] seq# - used for dup detection
++** (dups from retries have same seq#) */
++typedef struct wlan_hdr {
++ u16 fc;
++ u16 dur;
++ u8 a1[ETH_ALEN];
++ u8 a2[ETH_ALEN];
++ u8 a3[ETH_ALEN];
++ u16 seq;
++ u8 a4[ETH_ALEN];
++} WLAN_PACKED wlan_hdr_t;
++
++/* Separate structs for use if frame type is known */
++typedef struct wlan_hdr_a3 {
++ u16 fc;
++ u16 dur;
++ u8 a1[ETH_ALEN];
++ u8 a2[ETH_ALEN];
++ u8 a3[ETH_ALEN];
++ u16 seq;
++} WLAN_PACKED wlan_hdr_a3_t;
++
++typedef struct wlan_hdr_mgmt {
++ u16 fc;
++ u16 dur;
++ u8 da[ETH_ALEN];
++ u8 sa[ETH_ALEN];
++ u8 bssid[ETH_ALEN];
++ u16 seq;
++} WLAN_PACKED wlan_hdr_mgmt_t;
++
++#ifdef NOT_NEEDED_YET
++typedef struct { /* ad-hoc peer->peer (to/from DS = 0/0) */
++ u16 fc;
++ u16 dur;
++ u8 da[ETH_ALEN];
++ u8 sa[ETH_ALEN];
++ u8 bssid[ETH_ALEN];
++ u16 seq;
++} WLAN_PACKED ibss;
++typedef struct { /* ap->sta (to/from DS = 0/1) */
++ u16 fc;
++ u16 dur;
++ u8 da[ETH_ALEN];
++ u8 bssid[ETH_ALEN];
++ u8 sa[ETH_ALEN];
++ u16 seq;
++} WLAN_PACKED fromap;
++typedef struct { /* sta->ap (to/from DS = 1/0) */
++ u16 fc;
++ u16 dur;
++ u8 bssid[ETH_ALEN];
++ u8 sa[ETH_ALEN];
++ u8 da[ETH_ALEN];
++ u16 seq;
++} WLAN_PACKED toap;
++typedef struct { /* wds->wds (to/from DS = 1/1), the only 4addr pkt */
++ u16 fc;
++ u16 dur;
++ u8 ra[ETH_ALEN];
++ u8 ta[ETH_ALEN];
++ u8 da[ETH_ALEN];
++ u16 seq;
++ u8 sa[ETH_ALEN];
++} WLAN_PACKED wds;
++typedef struct { /* all management packets */
++ u16 fc;
++ u16 dur;
++ u8 da[ETH_ALEN];
++ u8 sa[ETH_ALEN];
++ u8 bssid[ETH_ALEN];
++ u16 seq;
++} WLAN_PACKED mgmt;
++typedef struct { /* has no body, just a FCS */
++ u16 fc;
++ u16 dur;
++ u8 ra[ETH_ALEN];
++ u8 ta[ETH_ALEN];
++} WLAN_PACKED rts;
++typedef struct { /* has no body, just a FCS */
++ u16 fc;
++ u16 dur;
++ u8 ra[ETH_ALEN];
++} WLAN_PACKED cts;
++typedef struct { /* has no body, just a FCS */
++ u16 fc;
++ u16 dur;
++ u8 ra[ETH_ALEN];
++} WLAN_PACKED ack;
++typedef struct { /* has no body, just a FCS */
++ u16 fc;
++ /* NB: this one holds Assoc ID in dur field: */
++ u16 aid;
++ u8 bssid[ETH_ALEN];
++ u8 ta[ETH_ALEN];
++} WLAN_PACKED pspoll;
++typedef struct { /* has no body, just a FCS */
++ u16 fc;
++ u16 dur;
++ u8 ra[ETH_ALEN];
++ u8 bssid[ETH_ALEN];
++} WLAN_PACKED cfend;
++typedef struct { /* has no body, just a FCS */
++ u16 fc;
++ u16 dur;
++ u8 ra[ETH_ALEN];
++ u8 bssid[ETH_ALEN];
++} WLAN_PACKED cfendcfack;
++#endif
++
++/* Prism header emulation (monitor mode) */
++typedef struct wlanitem_u32 {
++ u32 did;
++ u16 status;
++ u16 len;
++ u32 data;
++} WLAN_PACKED wlanitem_u32_t;
++#define WLANITEM_STATUS_data_ok 0
++#define WLANITEM_STATUS_no_value 1
++#define WLANITEM_STATUS_invalid_itemname 2
++#define WLANITEM_STATUS_invalid_itemdata 3
++#define WLANITEM_STATUS_missing_itemdata 4
++#define WLANITEM_STATUS_incomplete_itemdata 5
++#define WLANITEM_STATUS_invalid_msg_did 6
++#define WLANITEM_STATUS_invalid_mib_did 7
++#define WLANITEM_STATUS_missing_conv_func 8
++#define WLANITEM_STATUS_string_too_long 9
++#define WLANITEM_STATUS_data_out_of_range 10
++#define WLANITEM_STATUS_string_too_short 11
++#define WLANITEM_STATUS_missing_valid_func 12
++#define WLANITEM_STATUS_unknown 13
++#define WLANITEM_STATUS_invalid_did 14
++#define WLANITEM_STATUS_missing_print_func 15
++
++#define WLAN_DEVNAMELEN_MAX 16
++typedef struct wlansniffrm {
++ u32 msgcode;
++ u32 msglen;
++ u8 devname[WLAN_DEVNAMELEN_MAX];
++ wlanitem_u32_t hosttime;
++ wlanitem_u32_t mactime;
++ wlanitem_u32_t channel;
++ wlanitem_u32_t rssi;
++ wlanitem_u32_t sq;
++ wlanitem_u32_t signal;
++ wlanitem_u32_t noise;
++ wlanitem_u32_t rate;
++ wlanitem_u32_t istx; /* tx? 0:no 1:yes */
++ wlanitem_u32_t frmlen;
++} WLAN_PACKED wlansniffrm_t;
++#define WLANSNIFFFRM 0x0041
++#define WLANSNIFFFRM_hosttime 0x1041
++#define WLANSNIFFFRM_mactime 0x2041
++#define WLANSNIFFFRM_channel 0x3041
++#define WLANSNIFFFRM_rssi 0x4041
++#define WLANSNIFFFRM_sq 0x5041
++#define WLANSNIFFFRM_signal 0x6041
++#define WLANSNIFFFRM_noise 0x7041
++#define WLANSNIFFFRM_rate 0x8041
++#define WLANSNIFFFRM_istx 0x9041
++#define WLANSNIFFFRM_frmlen 0xA041
+Index: linux-2.6.23/drivers/net/wireless/acx/wlan_mgmt.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23/drivers/net/wireless/acx/wlan_mgmt.h 2008-01-20 21:13:40.000000000 +0000
+@@ -0,0 +1,582 @@
++/***********************************************************************
++** Copyright (C) 2003 ACX100 Open Source Project
++**
++** The contents of this file are subject to the Mozilla Public
++** License Version 1.1 (the "License"); you may not use this file
++** except in compliance with the License. You may obtain a copy of
++** the License at http://www.mozilla.org/MPL/
++**
++** Software distributed under the License is distributed on an "AS
++** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
++** implied. See the License for the specific language governing
++** rights and limitations under the License.
++**
++** Alternatively, the contents of this file may be used under the
++** terms of the GNU Public License version 2 (the "GPL"), in which
++** case the provisions of the GPL are applicable instead of the
++** above. If you wish to allow the use of your version of this file
++** only under the terms of the GPL and not to allow others to use
++** your version of this file under the MPL, indicate your decision
++** by deleting the provisions above and replace them with the notice
++** and other provisions required by the GPL. If you do not delete
++** the provisions above, a recipient may use your version of this
++** file under either the MPL or the GPL.
++** ---------------------------------------------------------------------
++** Inquiries regarding the ACX100 Open Source Project can be
++** made directly to:
++**
++** acx100-users@lists.sf.net
++** http://acx100.sf.net
++** ---------------------------------------------------------------------
++*/
++
++/***********************************************************************
++** This code is based on elements which are
++** Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved.
++** info@linux-wlan.com
++** http://www.linux-wlan.com
++*/
++
++/***********************************************************************
++** Constants
++*/
++
++/*-- Information Element IDs --------------------*/
++#define WLAN_EID_SSID 0
++#define WLAN_EID_SUPP_RATES 1
++#define WLAN_EID_FH_PARMS 2
++#define WLAN_EID_DS_PARMS 3
++#define WLAN_EID_CF_PARMS 4
++#define WLAN_EID_TIM 5
++#define WLAN_EID_IBSS_PARMS 6
++#define WLAN_EID_COUNTRY 7 /* 802.11d */
++#define WLAN_EID_FH_HOP_PARMS 8 /* 802.11d */
++#define WLAN_EID_FH_TABLE 9 /* 802.11d */
++#define WLAN_EID_REQUEST 10 /* 802.11d */
++/*-- values 11-15 reserved --*/
++#define WLAN_EID_CHALLENGE 16
++/*-- values 17-31 reserved for challenge text extension --*/
++#define WLAN_EID_PWR_CONSTRAINT 32 /* 11h PowerConstraint */
++#define WLAN_EID_ERP_INFO 42 /* was seen from WRT54GS with OpenWrt */
++#define WLAN_EID_NONERP 47 /* was seen from WRT54GS with OpenWrt */
++#define WLAN_EID_RSN 48
++#define WLAN_EID_EXT_RATES 50
++#define WLAN_EID_UNKNOWN128 128
++#define WLAN_EID_UNKNOWN133 133
++#define WLAN_EID_GENERIC 221 /* was seen from WRT54GS with OpenWrt */
++#define WLAN_EID_UNKNOWN223 223
++
++#if 0
++#define WLAN_EID_PWR_CAP 33 /* 11h PowerCapability */
++#define WLAN_EID_TPC_REQUEST 34 /* 11h TPC Request */
++#define WLAN_EID_TPC_REPORT 35 /* 11h TPC Report */
++#define WLAN_EID_SUPP_CHANNELS 36 /* 11h Supported Channels */
++#define WLAN_EID_CHANNEL_SWITCH 37 /* 11h ChannelSwitch */
++#define WLAN_EID_MEASURE_REQUEST 38 /* 11h MeasurementRequest */
++#define WLAN_EID_MEASURE_REPORT 39 /* 11h MeasurementReport */
++#define WLAN_EID_QUIET_ID 40 /* 11h Quiet */
++#define WLAN_EID_IBSS_DFS_ID 41 /* 11h IBSS_DFS */
++#endif
++
++/*-- Reason Codes -------------------------------*/
++#define WLAN_MGMT_REASON_RSVD 0
++#define WLAN_MGMT_REASON_UNSPEC 1
++#define WLAN_MGMT_REASON_PRIOR_AUTH_INVALID 2
++#define WLAN_MGMT_REASON_DEAUTH_LEAVING 3
++#define WLAN_MGMT_REASON_DISASSOC_INACTIVE 4
++#define WLAN_MGMT_REASON_DISASSOC_AP_BUSY 5
++#define WLAN_MGMT_REASON_CLASS2_NONAUTH 6
++#define WLAN_MGMT_REASON_CLASS3_NONASSOC 7
++#define WLAN_MGMT_REASON_DISASSOC_STA_HASLEFT 8
++#define WLAN_MGMT_REASON_CANT_ASSOC_NONAUTH 9
++
++/*-- Status Codes -------------------------------*/
++#define WLAN_MGMT_STATUS_SUCCESS 0
++#define WLAN_MGMT_STATUS_UNSPEC_FAILURE 1
++#define WLAN_MGMT_STATUS_CAPS_UNSUPPORTED 10
++#define WLAN_MGMT_STATUS_REASSOC_NO_ASSOC 11
++#define WLAN_MGMT_STATUS_ASSOC_DENIED_UNSPEC 12
++#define WLAN_MGMT_STATUS_UNSUPPORTED_AUTHALG 13
++#define WLAN_MGMT_STATUS_RX_AUTH_NOSEQ 14
++#define WLAN_MGMT_STATUS_CHALLENGE_FAIL 15
++#define WLAN_MGMT_STATUS_AUTH_TIMEOUT 16
++#define WLAN_MGMT_STATUS_ASSOC_DENIED_BUSY 17
++#define WLAN_MGMT_STATUS_ASSOC_DENIED_RATES 18
++/* p80211b additions */
++#define WLAN_MGMT_STATUS_ASSOC_DENIED_NOSHORT 19
++#define WLAN_MGMT_STATUS_ASSOC_DENIED_NOPBCC 20
++#define WLAN_MGMT_STATUS_ASSOC_DENIED_NOAGILITY 21
++
++/*-- Auth Algorithm Field ---------------------------*/
++#define WLAN_AUTH_ALG_OPENSYSTEM 0
++#define WLAN_AUTH_ALG_SHAREDKEY 1
++
++/*-- Management Frame Field Offsets -------------*/
++/* Note: Not all fields are listed because of variable lengths */
++/* Note: These offsets are from the start of the frame data */
++
++#define WLAN_BEACON_OFF_TS 0
++#define WLAN_BEACON_OFF_BCN_INT 8
++#define WLAN_BEACON_OFF_CAPINFO 10
++#define WLAN_BEACON_OFF_SSID 12
++
++#define WLAN_DISASSOC_OFF_REASON 0
++
++#define WLAN_ASSOCREQ_OFF_CAP_INFO 0
++#define WLAN_ASSOCREQ_OFF_LISTEN_INT 2
++#define WLAN_ASSOCREQ_OFF_SSID 4
++
++#define WLAN_ASSOCRESP_OFF_CAP_INFO 0
++#define WLAN_ASSOCRESP_OFF_STATUS 2
++#define WLAN_ASSOCRESP_OFF_AID 4
++#define WLAN_ASSOCRESP_OFF_SUPP_RATES 6
++
++#define WLAN_REASSOCREQ_OFF_CAP_INFO 0
++#define WLAN_REASSOCREQ_OFF_LISTEN_INT 2
++#define WLAN_REASSOCREQ_OFF_CURR_AP 4
++#define WLAN_REASSOCREQ_OFF_SSID 10
++
++#define WLAN_REASSOCRESP_OFF_CAP_INFO 0
++#define WLAN_REASSOCRESP_OFF_STATUS 2
++#define WLAN_REASSOCRESP_OFF_AID 4
++#define WLAN_REASSOCRESP_OFF_SUPP_RATES 6
++
++#define WLAN_PROBEREQ_OFF_SSID 0
++
++#define WLAN_PROBERESP_OFF_TS 0
++#define WLAN_PROBERESP_OFF_BCN_INT 8
++#define WLAN_PROBERESP_OFF_CAP_INFO 10
++#define WLAN_PROBERESP_OFF_SSID 12
++
++#define WLAN_AUTHEN_OFF_AUTH_ALG 0
++#define WLAN_AUTHEN_OFF_AUTH_SEQ 2
++#define WLAN_AUTHEN_OFF_STATUS 4
++#define WLAN_AUTHEN_OFF_CHALLENGE 6
++
++#define WLAN_DEAUTHEN_OFF_REASON 0
++
++enum {
++IEEE16(WF_MGMT_CAP_ESS, 0x0001)
++IEEE16(WF_MGMT_CAP_IBSS, 0x0002)
++/* In (re)assoc request frames by STA:
++** Pollable=0, PollReq=0: STA is not CF-Pollable
++** 0 1: STA is CF-Pollable, not requesting to be placed on the CF-Polling list
++** 1 0: STA is CF-Pollable, requesting to be placed on the CF-Polling list
++** 1 1: STA is CF-Pollable, requesting never to be polled
++** In beacon, proberesp, (re)assoc resp frames by AP:
++** 0 0: No point coordinator at AP
++** 0 1: Point coordinator at AP for delivery only (no polling)
++** 1 0: Point coordinator at AP for delivery and polling
++** 1 1: Reserved */
++IEEE16(WF_MGMT_CAP_CFPOLLABLE, 0x0004)
++IEEE16(WF_MGMT_CAP_CFPOLLREQ, 0x0008)
++/* 1=non-WEP data frames are disallowed */
++IEEE16(WF_MGMT_CAP_PRIVACY, 0x0010)
++/* In beacon, proberesp, (re)assocresp by AP/AdHoc:
++** 1=use of shortpre is allowed ("I can receive shortpre") */
++IEEE16(WF_MGMT_CAP_SHORT, 0x0020)
++IEEE16(WF_MGMT_CAP_PBCC, 0x0040)
++IEEE16(WF_MGMT_CAP_AGILITY, 0x0080)
++/* In (re)assoc request frames by STA:
++** 1=short slot time implemented and enabled
++** NB: AP shall use long slot time beginning at the next Beacon after assoc
++** of STA with this bit set to 0
++** In beacon, proberesp, (re)assoc resp frames by AP:
++** currently used slot time value: 0/1 - long/short */
++IEEE16(WF_MGMT_CAP_SHORTSLOT, 0x0400)
++/* In (re)assoc request frames by STA: 1=CCK-OFDM is implemented and enabled
++** In beacon, proberesp, (re)assoc resp frames by AP/AdHoc:
++** 1=CCK-OFDM is allowed */
++IEEE16(WF_MGMT_CAP_CCKOFDM, 0x2000)
++};
++
++
++/***********************************************************************
++** Types
++*/
++
++/* Information Element types */
++
++/* prototype structure, all IEs start with these members */
++typedef struct wlan_ie {
++ u8 eid;
++ u8 len;
++} WLAN_PACKED wlan_ie_t;
++
++/*-- Service Set Identity (SSID) -----------------*/
++typedef struct wlan_ie_ssid {
++ u8 eid;
++ u8 len;
++ u8 ssid[1]; /* may be zero */
++} WLAN_PACKED wlan_ie_ssid_t;
++
++/*-- Supported Rates -----------------------------*/
++typedef struct wlan_ie_supp_rates {
++ u8 eid;
++ u8 len;
++ u8 rates[1]; /* had better be at LEAST one! */
++} WLAN_PACKED wlan_ie_supp_rates_t;
++
++/*-- FH Parameter Set ----------------------------*/
++typedef struct wlan_ie_fh_parms {
++ u8 eid;
++ u8 len;
++ u16 dwell;
++ u8 hopset;
++ u8 hoppattern;
++ u8 hopindex;
++} WLAN_PACKED wlan_ie_fh_parms_t;
++
++/*-- DS Parameter Set ----------------------------*/
++typedef struct wlan_ie_ds_parms {
++ u8 eid;
++ u8 len;
++ u8 curr_ch;
++} WLAN_PACKED wlan_ie_ds_parms_t;
++
++/*-- CF Parameter Set ----------------------------*/
++typedef struct wlan_ie_cf_parms {
++ u8 eid;
++ u8 len;
++ u8 cfp_cnt;
++ u8 cfp_period;
++ u16 cfp_maxdur;
++ u16 cfp_durremaining;
++} WLAN_PACKED wlan_ie_cf_parms_t;
++
++/*-- TIM ------------------------------------------*/
++typedef struct wlan_ie_tim {
++ u8 eid;
++ u8 len;
++ u8 dtim_cnt;
++ u8 dtim_period;
++ u8 bitmap_ctl;
++ u8 virt_bm[1];
++} WLAN_PACKED wlan_ie_tim_t;
++
++/*-- IBSS Parameter Set ---------------------------*/
++typedef struct wlan_ie_ibss_parms {
++ u8 eid;
++ u8 len;
++ u16 atim_win;
++} WLAN_PACKED wlan_ie_ibss_parms_t;
++
++/*-- Challenge Text ------------------------------*/
++typedef struct wlan_ie_challenge {
++ u8 eid;
++ u8 len;
++ u8 challenge[1];
++} WLAN_PACKED wlan_ie_challenge_t;
++
++/*-- ERP (42) -------------------------------------*/
++typedef struct wlan_ie_erp {
++ u8 eid;
++ u8 len;
++ /* bit 0:Non ERP present
++ ** 1:Use Protection
++ ** 2:Barker Preamble mode
++ ** 3-7:reserved */
++ u8 erp;
++} WLAN_PACKED wlan_ie_erp_t;
++
++/* Types for parsing mgmt frames */
++
++/* prototype structure, all mgmt frame types will start with these members */
++typedef struct wlan_fr_mgmt {
++ u16 type;
++ u16 len; /* DOES NOT include FCS */
++ wlan_hdr_t *hdr;
++ /* used for target specific data, skb in Linux */
++ /*-- fixed fields -----------*/
++ /*-- info elements ----------*/
++} WLAN_PACKED wlan_fr_mgmt_t;
++
++/*-- Beacon ---------------------------------------*/
++typedef struct wlan_fr_beacon {
++ u16 type;
++ u16 len;
++ wlan_hdr_t *hdr;
++ /*-- fixed fields -----------*/
++ u64 *ts;
++ u16 *bcn_int;
++ u16 *cap_info;
++ /*-- info elements ----------*/
++ wlan_ie_ssid_t *ssid;
++ wlan_ie_supp_rates_t *supp_rates;
++ wlan_ie_supp_rates_t *ext_rates;
++ wlan_ie_fh_parms_t *fh_parms;
++ wlan_ie_ds_parms_t *ds_parms;
++ wlan_ie_cf_parms_t *cf_parms;
++ wlan_ie_ibss_parms_t *ibss_parms;
++ wlan_ie_tim_t *tim; /* in beacon only, not proberesp */
++ wlan_ie_erp_t *erp; /* in beacon only, not proberesp */
++} wlan_fr_beacon_t;
++#define wlan_fr_proberesp wlan_fr_beacon
++#define wlan_fr_proberesp_t wlan_fr_beacon_t
++
++/*-- IBSS ATIM ------------------------------------*/
++typedef struct wlan_fr_ibssatim {
++ u16 type;
++ u16 len;
++ wlan_hdr_t *hdr;
++ /*-- fixed fields -----------*/
++ /*-- info elements ----------*/
++ /* this frame type has a null body */
++} wlan_fr_ibssatim_t;
++
++/*-- Disassociation -------------------------------*/
++typedef struct wlan_fr_disassoc {
++ u16 type;
++ u16 len;
++ wlan_hdr_t *hdr;
++ /*-- fixed fields -----------*/
++ u16 *reason;
++ /*-- info elements ----------*/
++} wlan_fr_disassoc_t;
++
++/*-- Association Request --------------------------*/
++typedef struct wlan_fr_assocreq {
++ u16 type;
++ u16 len;
++ wlan_hdr_t *hdr;
++ /*-- fixed fields -----------*/
++ u16 *cap_info;
++ u16 *listen_int;
++ /*-- info elements ----------*/
++ wlan_ie_ssid_t *ssid;
++ wlan_ie_supp_rates_t *supp_rates;
++ wlan_ie_supp_rates_t *ext_rates;
++} wlan_fr_assocreq_t;
++
++/*-- Association Response -------------------------*/
++typedef struct wlan_fr_assocresp {
++ u16 type;
++ u16 len;
++ wlan_hdr_t *hdr;
++ /*-- fixed fields -----------*/
++ u16 *cap_info;
++ u16 *status;
++ u16 *aid;
++ /*-- info elements ----------*/
++ wlan_ie_supp_rates_t *supp_rates;
++ wlan_ie_supp_rates_t *ext_rates;
++} wlan_fr_assocresp_t;
++
++/*-- Reassociation Request ------------------------*/
++typedef struct wlan_fr_reassocreq {
++ u16 type;
++ u16 len;
++ wlan_hdr_t *hdr;
++ /*-- fixed fields -----------*/
++ u16 *cap_info;
++ u16 *listen_int;
++ u8 *curr_ap;
++ /*-- info elements ----------*/
++ wlan_ie_ssid_t *ssid;
++ wlan_ie_supp_rates_t *supp_rates;
++ wlan_ie_supp_rates_t *ext_rates;
++} wlan_fr_reassocreq_t;
++
++/*-- Reassociation Response -----------------------*/
++typedef struct wlan_fr_reassocresp {
++ u16 type;
++ u16 len;
++ wlan_hdr_t *hdr;
++ /*-- fixed fields -----------*/
++ u16 *cap_info;
++ u16 *status;
++ u16 *aid;
++ /*-- info elements ----------*/
++ wlan_ie_supp_rates_t *supp_rates;
++ wlan_ie_supp_rates_t *ext_rates;
++} wlan_fr_reassocresp_t;
++
++/*-- Probe Request --------------------------------*/
++typedef struct wlan_fr_probereq {
++ u16 type;
++ u16 len;
++ wlan_hdr_t *hdr;
++ /*-- fixed fields -----------*/
++ /*-- info elements ----------*/
++ wlan_ie_ssid_t *ssid;
++ wlan_ie_supp_rates_t *supp_rates;
++ wlan_ie_supp_rates_t *ext_rates;
++} wlan_fr_probereq_t;
++
++/*-- Authentication -------------------------------*/
++typedef struct wlan_fr_authen {
++ u16 type;
++ u16 len;
++ wlan_hdr_t *hdr;
++ /*-- fixed fields -----------*/
++ u16 *auth_alg;
++ u16 *auth_seq;
++ u16 *status;
++ /*-- info elements ----------*/
++ wlan_ie_challenge_t *challenge;
++} wlan_fr_authen_t;
++
++/*-- Deauthenication -----------------------------*/
++typedef struct wlan_fr_deauthen {
++ u16 type;
++ u16 len;
++ wlan_hdr_t *hdr;
++ /*-- fixed fields -----------*/
++ u16 *reason;
++ /*-- info elements ----------*/
++} wlan_fr_deauthen_t;
++
++/* Types for building mgmt frames */
++
++/* Warning. Several types used in below structs are
++** in fact variable length. Use structs with such fields with caution */
++typedef struct auth_frame_body {
++ u16 auth_alg;
++ u16 auth_seq;
++ u16 status;
++ wlan_ie_challenge_t challenge;
++} WLAN_PACKED auth_frame_body_t;
++
++typedef struct assocresp_frame_body {
++ u16 cap_info;
++ u16 status;
++ u16 aid;
++ wlan_ie_supp_rates_t rates;
++} WLAN_PACKED assocresp_frame_body_t;
++
++typedef struct reassocreq_frame_body {
++ u16 cap_info;
++ u16 listen_int;
++ u8 current_ap[ETH_ALEN];
++ wlan_ie_ssid_t ssid;
++/* access to this one is disabled since ssid_t is variable length: */
++ /* wlan_ie_supp_rates_t rates; */
++} WLAN_PACKED reassocreq_frame_body_t;
++
++typedef struct reassocresp_frame_body {
++ u16 cap_info;
++ u16 status;
++ u16 aid;
++ wlan_ie_supp_rates_t rates;
++} WLAN_PACKED reassocresp_frame_body_t;
++
++typedef struct deauthen_frame_body {
++ u16 reason;
++} WLAN_PACKED deauthen_frame_body_t;
++
++typedef struct disassoc_frame_body {
++ u16 reason;
++} WLAN_PACKED disassoc_frame_body_t;
++
++typedef struct probereq_frame_body {
++ wlan_ie_ssid_t ssid;
++ wlan_ie_supp_rates_t rates;
++} WLAN_PACKED probereq_frame_body_t;
++
++typedef struct proberesp_frame_body {
++ u8 timestamp[8];
++ u16 beacon_int;
++ u16 cap_info;
++ wlan_ie_ssid_t ssid;
++/* access to these is disabled since ssid_t is variable length: */
++ /* wlan_ie_supp_rates_t rates; */
++ /* fhps_t fhps; */
++ /* dsps_t dsps; */
++ /* cfps_t cfps; */
++} WLAN_PACKED proberesp_frame_body_t;
++
++
++/***********************************************************************
++** Functions
++*/
++
++/* Helpers for parsing mgmt frames */
++void wlan_mgmt_decode_ibssatim(wlan_fr_ibssatim_t *f);
++void wlan_mgmt_decode_assocreq(wlan_fr_assocreq_t *f);
++void wlan_mgmt_decode_assocresp(wlan_fr_assocresp_t *f);
++void wlan_mgmt_decode_authen(wlan_fr_authen_t *f);
++void wlan_mgmt_decode_beacon(wlan_fr_beacon_t *f);
++void wlan_mgmt_decode_deauthen(wlan_fr_deauthen_t *f);
++void wlan_mgmt_decode_disassoc(wlan_fr_disassoc_t *f);
++void wlan_mgmt_decode_probereq(wlan_fr_probereq_t *f);
++void wlan_mgmt_decode_proberesp(wlan_fr_proberesp_t *f);
++void wlan_mgmt_decode_reassocreq(wlan_fr_reassocreq_t *f);
++void wlan_mgmt_decode_reassocresp(wlan_fr_reassocresp_t *f);
++
++/* Helpers for building mgmt frames */
++static inline u8*
++wlan_fill_ie_ssid(u8 *p, int len, const char *ssid)
++{
++ struct wlan_ie_ssid *ie = (void*)p;
++ ie->eid = WLAN_EID_SSID;
++ ie->len = len;
++ memcpy(ie->ssid, ssid, len);
++ return p + len + 2;
++}
++/* This controls whether we create 802.11g 'ext supported rates' IEs
++** or just create overlong 'supported rates' IEs instead
++** (non-11g compliant) */
++#define WE_OBEY_802_11G 1
++static inline u8*
++wlan_fill_ie_rates(u8 *p, int len, const u8 *rates)
++{
++ struct wlan_ie_supp_rates *ie = (void*)p;
++#if WE_OBEY_802_11G
++ if (len > 8 ) len = 8;
++#endif
++ /* supported rates (1 to 8 octets) */
++ ie->eid = WLAN_EID_SUPP_RATES;
++ ie->len = len;
++ memcpy(ie->rates, rates, len);
++ return p + len + 2;
++}
++/* This one wouldn't create an IE at all if not needed */
++static inline u8*
++wlan_fill_ie_rates_ext(u8 *p, int len, const u8 *rates)
++{
++ struct wlan_ie_supp_rates *ie = (void*)p;
++#if !WE_OBEY_802_11G
++ return p;
++#endif
++ len -= 8;
++ if (len <= 0) return p;
++ /* ext supported rates */
++ ie->eid = WLAN_EID_EXT_RATES;
++ ie->len = len;
++ memcpy(ie->rates, rates+8, len);
++ return p + len + 2;
++}
++static inline u8*
++wlan_fill_ie_ds_parms(u8 *p, int channel)
++{
++ struct wlan_ie_ds_parms *ie = (void*)p;
++ ie->eid = WLAN_EID_DS_PARMS;
++ ie->len = 1;
++ ie->curr_ch = channel;
++ return p + sizeof(*ie);
++}
++static inline u8*
++wlan_fill_ie_ibss_parms(u8 *p, int atim_win)
++{
++ struct wlan_ie_ibss_parms *ie = (void*)p;
++ ie->eid = WLAN_EID_IBSS_PARMS;
++ ie->len = 2;
++ ie->atim_win = atim_win;
++ return p + sizeof(*ie);
++}
++static inline u8*
++wlan_fill_ie_tim(u8 *p, int rem, int period, int bcast,
++ int ofs, int len, const u8 *vbm)
++{
++ struct wlan_ie_tim *ie = (void*)p;
++ ie->eid = WLAN_EID_TIM;
++ ie->len = len + 3;
++ ie->dtim_cnt = rem;
++ ie->dtim_period = period;
++ ie->bitmap_ctl = ofs | (bcast!=0);
++ if (vbm)
++ memcpy(ie->virt_bm, vbm, len); /* min 1 byte */
++ else
++ ie->virt_bm[0] = 0;
++ return p + len + 3 + 2;
++}
+Index: linux-2.6.23/drivers/net/wireless/Kconfig
+===================================================================
+--- linux-2.6.23.orig/drivers/net/wireless/Kconfig 2008-01-20 21:13:17.000000000 +0000
++++ linux-2.6.23/drivers/net/wireless/Kconfig 2008-01-20 21:15:12.000000000 +0000
+@@ -5,6 +5,36 @@
+ menu "Wireless LAN"
+ depends on !S390
+
++config NET_RADIO
++ bool "Wireless LAN drivers (non-hamradio) & Wireless Extensions"
++ select WIRELESS_EXT
++ ---help---
++ Support for wireless LANs and everything having to do with radio,
++ but not with amateur radio or FM broadcasting.
++
++ Saying Y here also enables the Wireless Extensions (creates
++ /proc/net/wireless and enables iwconfig access). The Wireless
++ Extension is a generic API allowing a driver to expose to the user
++ space configuration and statistics specific to common Wireless LANs.
++ The beauty of it is that a single set of tool can support all the
++ variations of Wireless LANs, regardless of their type (as long as
++ the driver supports Wireless Extension). Another advantage is that
++ these parameters may be changed on the fly without restarting the
++ driver (or Linux). If you wish to use Wireless Extensions with
++ wireless PCMCIA (PC-) cards, you need to say Y here; you can fetch
++ the tools from
++ <http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html>.
++
++config NET_WIRELESS_RTNETLINK
++ bool "Wireless Extension API over RtNetlink"
++ depends on NET_RADIO
++ ---help---
++ Support the Wireless Extension API over the RtNetlink socket
++ in addition to the traditional ioctl interface (selected above).
++
++ For now, few tools use this facility, but it might grow in the
++ future. The only downside is that it adds 4.5 kB to your kernel.
++
+ config WLAN_PRE80211
+ bool "Wireless LAN (pre-802.11)"
+ depends on NETDEVICES
+@@ -650,6 +680,7 @@ config P54_PCI
+
+ source "drivers/net/wireless/iwlwifi/Kconfig"
+ source "drivers/net/wireless/hostap/Kconfig"
++source "drivers/net/wireless/acx/Kconfig"
+ source "drivers/net/wireless/bcm43xx/Kconfig"
+ source "drivers/net/wireless/b43/Kconfig"
+ source "drivers/net/wireless/b43legacy/Kconfig"
+Index: linux-2.6.23/drivers/net/wireless/Makefile
+===================================================================
+--- linux-2.6.23.orig/drivers/net/wireless/Makefile 2008-01-20 21:13:17.000000000 +0000
++++ linux-2.6.23/drivers/net/wireless/Makefile 2008-01-20 21:13:40.000000000 +0000
+@@ -34,6 +34,8 @@ obj-$(CONFIG_PCMCIA_ATMEL) += atmel
+
+ obj-$(CONFIG_PRISM54) += prism54/
+
++obj-$(CONFIG_ACX) += acx/
++
+ obj-$(CONFIG_HOSTAP) += hostap/
+ obj-$(CONFIG_BCM43XX) += bcm43xx/
+ obj-$(CONFIG_B43) += b43/
diff --git a/recipes/linux/linux-rp-2.6.26/htcuni.patch b/recipes/linux/linux-rp-2.6.26/htcuni.patch
new file mode 100644
index 0000000000..783f55b064
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.26/htcuni.patch
@@ -0,0 +1,7899 @@
+---
+ arch/arm/Kconfig | 2
+ arch/arm/mach-pxa/Kconfig | 89 +
+ arch/arm/mach-pxa/Makefile | 1
+ arch/arm/mach-pxa/generic.c | 13
+ arch/arm/mach-pxa/htcuniversal/Makefile | 19
+ arch/arm/mach-pxa/htcuniversal/htcuniversal.c | 468 +++++
+ arch/arm/mach-pxa/htcuniversal/htcuniversal_ak4641.c | 917 +++++++++++
+ arch/arm/mach-pxa/htcuniversal/htcuniversal_ak4641.h | 65
+ arch/arm/mach-pxa/htcuniversal/htcuniversal_asic3_leds.c | 143 +
+ arch/arm/mach-pxa/htcuniversal/htcuniversal_bl.c | 61
+ arch/arm/mach-pxa/htcuniversal/htcuniversal_bt.c | 135 +
+ arch/arm/mach-pxa/htcuniversal/htcuniversal_bt.h | 17
+ arch/arm/mach-pxa/htcuniversal/htcuniversal_buttons.c | 87 +
+ arch/arm/mach-pxa/htcuniversal/htcuniversal_core.c | 226 ++
+ arch/arm/mach-pxa/htcuniversal/htcuniversal_lcd.c | 212 ++
+ arch/arm/mach-pxa/htcuniversal/htcuniversal_phone.c | 167 ++
+ arch/arm/mach-pxa/htcuniversal/htcuniversal_phone.h | 16
+ arch/arm/mach-pxa/htcuniversal/htcuniversal_pm.c | 69
+ arch/arm/mach-pxa/htcuniversal/htcuniversal_power2.c | 97 +
+ arch/arm/mach-pxa/htcuniversal/htcuniversal_ts2.c | 490 ++++++
+ arch/arm/mach-pxa/htcuniversal/htcuniversal_udc.c | 71
+ arch/arm/mach-pxa/htcuniversal/tsc2046_ts.h | 20
+ drivers/input/keyboard/Kconfig | 7
+ drivers/input/keyboard/Makefile | 1
+ drivers/input/keyboard/asic3_keys.c | 131 +
+ drivers/leds/Kconfig | 7
+ drivers/leds/Makefile | 1
+ drivers/leds/leds-asic3.c | 189 ++
+ drivers/mfd/Kconfig | 10
+ drivers/mfd/Makefile | 2
+ drivers/mfd/asic3_base.c | 1208 +++++++++++++++
+ drivers/mfd/soc-core.c | 106 +
+ drivers/mfd/soc-core.h | 30
+ drivers/mmc/host/Kconfig | 6
+ drivers/mmc/host/Makefile | 1
+ drivers/mmc/host/asic3_mmc.c | 900 +++++++++++
+ drivers/mmc/host/asic3_mmc.h | 25
+ drivers/serial/pxa.c | 22
+ include/asm-arm/arch-pxa/clock.h | 27
+ include/asm-arm/arch-pxa/htcuniversal-asic.h | 213 ++
+ include/asm-arm/arch-pxa/htcuniversal-gpio.h | 220 ++
+ include/asm-arm/arch-pxa/htcuniversal-init.h | 14
+ include/asm-arm/arch-pxa/htcuniversal.h | 3
+ include/asm-arm/arch-pxa/irqs.h | 2
+ include/asm-arm/arch-pxa/pxa-pm_ll.h | 6
+ include/asm-arm/arch-pxa/pxa-regs.h | 2
+ include/asm-arm/arch-pxa/serial.h | 78
+ include/asm-arm/hardware/asic3_keys.h | 18
+ include/asm-arm/hardware/asic3_leds.h | 34
+ include/asm-arm/hardware/ipaq-asic3.h | 602 +++++++
+ include/linux/backlight.h | 7
+ include/linux/gpiodev.h | 44
+ include/linux/input_pda.h | 47
+ include/linux/ioport.h | 1
+ include/linux/soc/asic3_base.h | 104 +
+ include/linux/soc/tmio_mmc.h | 17
+ 56 files changed, 7469 insertions(+), 1 deletion(-)
+
+Index: linux-2.6.26-rc4/arch/arm/mach-pxa/htcuniversal/Makefile
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26-rc4/arch/arm/mach-pxa/htcuniversal/Makefile 2008-06-01 18:49:45.000000000 +0100
+@@ -0,0 +1,19 @@
++#
++# Makefile for HTC Universal
++#
++
++snd-htcuniversal-ak4641-objs := htcuniversal_ak4641.o
++
++obj-$(CONFIG_MACH_HTCUNIVERSAL) += htcuniversal.o
++obj-$(CONFIG_HTCUNIVERSAL_CORE) += htcuniversal_core.o
++obj-$(CONFIG_HTCUNIVERSAL_POWER) += htcuniversal_power2.o
++obj-$(CONFIG_HTCUNIVERSAL_LCD) += htcuniversal_lcd.o
++obj-$(CONFIG_HTCUNIVERSAL_BACKLIGHT) += htcuniversal_bl.o
++obj-$(CONFIG_HTCUNIVERSAL_TS2) += htcuniversal_ts2.o
++obj-$(CONFIG_HTCUNIVERSAL_BUTTONS) += htcuniversal_buttons.o
++obj-$(CONFIG_HTCUNIVERSAL_BLUETOOTH) += htcuniversal_bt.o
++obj-$(CONFIG_HTCUNIVERSAL_PHONE) += htcuniversal_phone.o
++obj-$(CONFIG_HTCUNIVERSAL_ASIC3_LEDS) += htcuniversal_asic3_leds.o
++obj-$(CONFIG_HTCUNIVERSAL_UDC) += htcuniversal_udc.o
++
++obj-$(CONFIG_HTCUNIVERSAL_AK4641) += htcuniversal_ak4641.o
+Index: linux-2.6.26-rc4/arch/arm/mach-pxa/htcuniversal/htcuniversal.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26-rc4/arch/arm/mach-pxa/htcuniversal/htcuniversal.c 2008-06-01 18:49:45.000000000 +0100
+@@ -0,0 +1,468 @@
++/*
++ * Hardware definitions for HTC Universal
++ *
++ * Copyright (c) 2006 Oleg Gusev
++ *
++ * Use consistent with the GNU GPL is permitted,
++ * provided that this copyright notice is
++ * preserved in its entirety in all copies and derived works.
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/platform_device.h>
++#include <linux/irq.h>
++#include <linux/input.h>
++#include <linux/gpio_keys.h>
++#include <linux/soc/asic3_base.h>
++
++#include <asm/mach-types.h>
++#include <asm/hardware.h>
++#include <asm/setup.h>
++
++#include <asm/mach/irq.h>
++#include <asm/mach/arch.h>
++
++#include <asm/arch/bitfield.h>
++#include <asm/arch/pxa-regs.h>
++#include <asm/arch/serial.h>
++#include <asm/arch/pxa27x_keyboard.h>
++#include <asm/arch/pxafb.h>
++#include <asm/arch/irda.h>
++#include <asm/arch/ohci.h>
++
++#include <asm/arch/htcuniversal.h>
++#include <asm/arch/htcuniversal-gpio.h>
++#include <asm/arch/htcuniversal-init.h>
++#include <asm/arch/htcuniversal-asic.h>
++
++#include <asm/hardware/ipaq-asic3.h>
++
++#include "../generic.h"
++
++#include "htcuniversal_bt.h"
++#include "htcuniversal_phone.h"
++#include "tsc2046_ts.h"
++
++/*
++ * IRDA
++ */
++
++static void htcuniversal_irda_transceiver_mode(struct device *dev, int mode)
++{
++ /* */
++}
++
++static struct pxaficp_platform_data htcuniversal_ficp_platform_data = {
++ .transceiver_cap = IR_SIRMODE | IR_FIRMODE,
++ .transceiver_mode = htcuniversal_irda_transceiver_mode,
++};
++
++/*
++ * Bluetooth - Relies on other loadable modules, like ASIC3 and Core,
++ * so make the calls indirectly through pointers. Requires that the
++ * htcuniversal_bt module be loaded before any attempt to use
++ * bluetooth (obviously).
++ */
++
++static struct htcuniversal_bt_funcs bt_funcs;
++
++static void
++htcuniversal_bt_configure( int state )
++{
++ if (bt_funcs.configure != NULL)
++ bt_funcs.configure( state );
++}
++
++static struct htcuniversal_phone_funcs phone_funcs;
++
++static void
++htcuniversal_phone_configure( int state )
++{
++ if (phone_funcs.configure != NULL)
++ phone_funcs.configure( state );
++}
++
++//void htcuniversal_ll_pm_init(void);
++
++extern struct platform_device htcuniversal_bl;
++static struct platform_device htcuniversal_lcd = { .name = "htcuniversal_lcd", };
++//static struct platform_device htcuniversal_kbd = { .name = "htcuniversal_kbd", };
++static struct platform_device htcuniversal_buttons = { .name = "htcuniversal_buttons", };
++//static struct platform_device htcuniversal_ts = { .name = "htcuniversal_ts", };
++//static struct platform_device htcuniversal_bt = { .name = "htcuniversal_bt", };
++//static struct platform_device htcuniversal_phone = { .name = "htcuniversal_phone", };
++static struct platform_device htcuniversal_power = { .name = "htcuniversal_power", };
++static struct platform_device htcuniversal_udc = { .name = "htcuniversal_udc", };
++
++static struct tsc2046_mach_info htcuniversal_ts_platform_data = {
++ .port = 1,
++ .clock = CKEN_SSP1,
++ .pwrbit_X = 1,
++ .pwrbit_Y = 1,
++ .irq = 0 /* asic3 irq */
++};
++
++static struct platform_device htcuniversal_ts = {
++ .name = "htcuniversal_ts",
++ .dev = {
++ .platform_data = &htcuniversal_ts_platform_data,
++ },
++};
++
++
++/* Bluetooth */
++
++static struct platform_device htcuniversal_bt = {
++ .name = "htcuniversal_bt",
++ .id = -1,
++ .dev = {
++ .platform_data = &bt_funcs,
++ },
++};
++
++static struct platform_device htcuniversal_phone = {
++ .name = "htcuniversal_phone",
++ .id = -1,
++ .dev = {
++ .platform_data = &phone_funcs,
++ },
++};
++
++/* PXA2xx Keys */
++
++static struct gpio_keys_button htcuniversal_button_table[] = {
++ { KEY_POWER, GPIO_NR_HTCUNIVERSAL_KEY_ON_N, 1 },
++};
++
++static struct gpio_keys_platform_data htcuniversal_pxa_keys_data = {
++ .buttons = htcuniversal_button_table,
++ .nbuttons = ARRAY_SIZE(htcuniversal_button_table),
++};
++
++static struct platform_device htcuniversal_pxa_keys = {
++ .name = "gpio-keys",
++ .dev = {
++ .platform_data = &htcuniversal_pxa_keys_data,
++ },
++ .id = -1,
++};
++
++/****************************************************************
++ * Keyboard
++ ****************************************************************/
++
++static struct pxa27x_keyboard_platform_data htcuniversal_kbd = {
++ .nr_rows = 8,
++ .nr_cols = 8,
++ .keycodes = {
++ {
++ /* row 0 */
++ KEY_ENTER,
++ KEY_MINUS,
++ KEY_ESC,
++ KEY_1,
++ KEY_TAB,
++ KEY_CAPSLOCK,
++ KEY_LEFTSHIFT,
++ KEY_RIGHTALT, /* Fn */
++ }, { /* row 1 */
++ KEY_COMMA,
++ KEY_EQUAL,
++ KEY_F1,
++ KEY_2,
++ KEY_Q,
++ KEY_A,
++ KEY_Z,
++ KEY_LEFTCTRL,
++ }, { /* row 2 */
++ KEY_UP,
++ KEY_I,
++ KEY_F2,
++ KEY_3,
++ KEY_W,
++ KEY_S,
++ KEY_X,
++ KEY_F6,
++ }, { /* row 3 */
++ KEY_DOT,
++ KEY_O,
++ KEY_F3,
++ KEY_4,
++ KEY_E,
++ KEY_D,
++ KEY_C,
++ KEY_LEFTALT,
++ }, { /* row 4 */
++ KEY_F9,
++ KEY_P,
++ KEY_F4,
++ KEY_5,
++ KEY_R,
++ KEY_F,
++ KEY_V,
++ KEY_SPACE,
++ }, { /* row 5 */
++ KEY_RIGHT,
++ KEY_BACKSPACE,
++ KEY_F5,
++ KEY_6,
++ KEY_T,
++ KEY_G,
++ KEY_B,
++ KEY_F7,
++ }, { /* row 6 */
++ KEY_F9,
++ KEY_K,
++ KEY_9,
++ KEY_7,
++ KEY_Y,
++ KEY_H,
++ KEY_N,
++ KEY_LEFT,
++ }, { /* row 7 */
++ KEY_F10,
++ KEY_L,
++ KEY_0,
++ KEY_8,
++ KEY_U,
++ KEY_J,
++ KEY_M,
++ KEY_DOWN,
++ },
++ },
++ .gpio_modes = {
++ GPIO_NR_HTCUNIVERSAL_KP_MKIN0_MD,
++ GPIO_NR_HTCUNIVERSAL_KP_MKIN1_MD,
++ GPIO_NR_HTCUNIVERSAL_KP_MKIN2_MD,
++ GPIO_NR_HTCUNIVERSAL_KP_MKIN3_MD,
++ GPIO_NR_HTCUNIVERSAL_KP_MKIN4_MD,
++ GPIO_NR_HTCUNIVERSAL_KP_MKIN5_MD,
++ GPIO_NR_HTCUNIVERSAL_KP_MKIN6_MD,
++ GPIO_NR_HTCUNIVERSAL_KP_MKIN7_MD,
++ GPIO_NR_HTCUNIVERSAL_KP_MKOUT0_MD,
++ GPIO_NR_HTCUNIVERSAL_KP_MKOUT1_MD,
++ GPIO_NR_HTCUNIVERSAL_KP_MKOUT2_MD,
++ GPIO_NR_HTCUNIVERSAL_KP_MKOUT3_MD,
++ GPIO_NR_HTCUNIVERSAL_KP_MKOUT4_MD,
++ GPIO_NR_HTCUNIVERSAL_KP_MKOUT5_MD,
++ GPIO_NR_HTCUNIVERSAL_KP_MKOUT6_MD,
++ GPIO_NR_HTCUNIVERSAL_KP_MKOUT7_MD,
++ },
++};
++
++static struct platform_device htcuniversal_pxa_keyboard = {
++ .name = "pxa27x-keyboard",
++ .id = -1,
++ .dev = {
++ .platform_data = &htcuniversal_kbd,
++ },
++};
++/* Core Hardware Functions */
++
++struct platform_device htcuniversal_core = {
++ .name = "htcuniversal_core",
++ .id = 0,
++ .dev = {
++ .platform_data = NULL,
++ },
++};
++
++static struct platform_device *devices[] __initdata = {
++ &htcuniversal_core,
++// &htcuniversal_flash,
++ &htcuniversal_pxa_keyboard,
++ &htcuniversal_pxa_keys,
++};
++
++static struct platform_device *htcuniversal_asic3_devices[] __initdata = {
++ &htcuniversal_lcd,
++#ifdef CONFIG_HTCUNIVERSAL_BACKLIGHT
++ &htcuniversal_bl,
++#endif
++ &htcuniversal_buttons,
++ &htcuniversal_ts,
++ &htcuniversal_bt,
++ &htcuniversal_phone,
++ &htcuniversal_power,
++ &htcuniversal_udc,
++};
++
++static struct asic3_platform_data htcuniversal_asic3_platform_data = {
++
++ /* Setting ASIC3 GPIO registers to the below initialization states
++ * HTC Universal asic3 information:
++ * http://wiki.xda-developers.com/index.php?pagename=UniversalASIC3
++ * http://wiki.xda-developers.com/index.php?pagename=ASIC3
++ *
++ * dir: Direction of the GPIO pin. 0: input, 1: output.
++ * If unknown, set as output to avoid power consuming floating input nodes
++ * init: Initial state of the GPIO bits
++ *
++ * These registers are configured as they are on Wince.
++ */
++ .gpio_a = {
++ .dir = (1<<GPIOA_LCD_PWR5_ON) |
++ (1<<GPIOA_FLASHLIGHT) |
++ (1<<GPIOA_UNKNOWN9) |
++ (1<<GPIOA_SPK_PWR2_ON) |
++ (1<<GPIOA_UNKNOWN4) |
++ (1<<GPIOA_EARPHONE_PWR_ON)|
++ (1<<GPIOA_AUDIO_PWR_ON) |
++ (1<<GPIOA_SPK_PWR1_ON) |
++ (1<<GPIOA_I2C_EN),
++ .init = (1<<GPIOA_LCD_PWR5_ON) |
++ (1<<GPIOA_I2C_EN),
++ .sleep_out = 0x0000,
++ .batt_fault_out = 0x0000,
++ .alt_function = 0x0000,
++ .sleep_conf = 0x000c,
++ },
++ .gpio_b = {
++ .dir = 0xc142,
++ .init = 0x8842, // TODO: 0x0900
++ .sleep_out = 0x0000,
++ .batt_fault_out = 0x0000,
++ .alt_function = 0x0000,
++ .sleep_conf = 0x000c,
++ },
++ .gpio_c = {
++ .dir = 0xc7e7,
++ .init = 0xc6e0, // TODO: 0x8000
++ .sleep_out = 0x0000,
++ .batt_fault_out = 0x0000,
++ .alt_function = 0x0007, // GPIOC_LED_RED | GPIOC_LED_GREEN | GPIOC_LED_BLUE
++ .sleep_conf = 0x000c,
++ },
++ .gpio_d = {
++ .dir = 0xffc0,
++ .init = 0x7840, // TODO: 0x0000
++ .sleep_out = 0x0000,
++ .batt_fault_out = 0x0000,
++ .alt_function = 0x0000,
++ .sleep_conf = 0x0008,
++ },
++ .bus_shift = 1,
++ .irq_base = HTCUNIVERSAL_ASIC3_IRQ_BASE,
++
++ .child_platform_devs = htcuniversal_asic3_devices,
++ .num_child_platform_devs = ARRAY_SIZE(htcuniversal_asic3_devices),
++};
++
++static struct resource htcuniversal_asic3_resources[] = {
++ [0] = {
++ .start = HTCUNIVERSAL_ASIC3_GPIO_PHYS,
++ .end = HTCUNIVERSAL_ASIC3_GPIO_PHYS + IPAQ_ASIC3_MAP_SIZE,
++ .flags = IORESOURCE_MEM,
++ },
++ [1] = {
++ .start = HTCUNIVERSAL_IRQ(ASIC3_EXT_INT),
++ .end = HTCUNIVERSAL_IRQ(ASIC3_EXT_INT),
++ .flags = IORESOURCE_IRQ,
++ },
++ [2] = {
++ .start = HTCUNIVERSAL_ASIC3_MMC_PHYS,
++ .end = HTCUNIVERSAL_ASIC3_MMC_PHYS + IPAQ_ASIC3_MAP_SIZE,
++ .flags = IORESOURCE_MEM,
++ },
++ [3] = {
++ .start = HTCUNIVERSAL_IRQ(ASIC3_SDIO_INT_N),
++ .flags = IORESOURCE_IRQ,
++ },
++};
++
++struct platform_device htcuniversal_asic3 = {
++ .name = "asic3",
++ .id = 0,
++ .num_resources = ARRAY_SIZE(htcuniversal_asic3_resources),
++ .resource = htcuniversal_asic3_resources,
++ .dev = { .platform_data = &htcuniversal_asic3_platform_data, },
++};
++EXPORT_SYMBOL(htcuniversal_asic3);
++
++static struct pxafb_mode_info htcuniversal_lcd_modes[] = {
++{
++ .pixclock = 96153,
++ .xres = 480,
++ .yres = 640,
++ .bpp = 16,
++ .hsync_len = 4,
++ .vsync_len = 1,
++ .left_margin = 20,
++ .right_margin = 8,
++ .upper_margin = 7,
++ .lower_margin = 8,
++
++// .sync = FB_SYNC_HOR_LOW_ACT|FB_SYNC_VERT_LOW_ACT,
++
++},
++};
++
++static struct pxafb_mach_info sony_acx526akm = {
++ .modes = htcuniversal_lcd_modes,
++ .num_modes = ARRAY_SIZE(htcuniversal_lcd_modes),
++
++ /* fixme: use constants defined in pxafb.h */
++ .lccr0 = 0x00000080,
++ .lccr3 = 0x00400000,
++// .lccr4 = 0x80000000,
++};
++
++static void __init htcuniversal_init_irq(void)
++{
++ pxa27x_init_irq();
++}
++
++static struct platform_pxa_serial_funcs htcuniversal_pxa_bt_funcs = {
++ .configure = htcuniversal_bt_configure,
++};
++static struct platform_pxa_serial_funcs htcuniversal_pxa_phone_funcs = {
++ .configure = htcuniversal_phone_configure,
++};
++
++/* USB OHCI */
++
++static int htcuniversal_ohci_init(struct device *dev)
++{
++ /* missing GPIO setup here */
++
++ /* got the value from wince */
++ UHCHR=UHCHR_CGR;
++
++ return 0;
++}
++
++static struct pxaohci_platform_data htcuniversal_ohci_platform_data = {
++ .port_mode = PMM_PERPORT_MODE,
++ .init = htcuniversal_ohci_init,
++};
++
++static void __init htcuniversal_map_io(void)
++{
++ pxa_map_io();
++
++ pxa_set_btuart_info(&htcuniversal_pxa_bt_funcs);
++ pxa_set_ffuart_info(&htcuniversal_pxa_phone_funcs);
++}
++
++static void __init htcuniversal_init(void)
++{
++ set_pxa_fb_info(&sony_acx526akm);
++
++ platform_device_register(&htcuniversal_asic3);
++ platform_add_devices(devices, ARRAY_SIZE(devices) );
++ pxa_set_ficp_info(&htcuniversal_ficp_platform_data);
++ pxa_set_ohci_info(&htcuniversal_ohci_platform_data);
++}
++
++MACHINE_START(HTCUNIVERSAL, "HTC Universal")
++ /* Maintainer xanadux.org */
++ .phys_io = 0x40000000,
++ .io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc,
++ .boot_params = 0xa0000100,
++ .map_io = htcuniversal_map_io,
++ .init_irq = htcuniversal_init_irq,
++ .init_machine = htcuniversal_init,
++ .timer = &pxa_timer,
++MACHINE_END
+Index: linux-2.6.26-rc4/arch/arm/mach-pxa/htcuniversal/htcuniversal_ak4641.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26-rc4/arch/arm/mach-pxa/htcuniversal/htcuniversal_ak4641.c 2008-06-01 18:49:45.000000000 +0100
+@@ -0,0 +1,917 @@
++/*
++ * Audio support for codec Asahi Kasei AK4641
++ *
++ * 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.
++ *
++ * Copyright (c) 2006 Giorgio Padrin <giorgio@mandarinlogiq.org>
++ *
++ * History:
++ *
++ * 2006-03 Written -- Giorgio Padrin
++ * 2006-09 Test and debug on machine (HP hx4700) -- Elshin Roman <roxmail@list.ru>
++ *
++ * AK4641 codec device driver
++ *
++ * Copyright (c) 2005 SDG Systems, LLC
++ *
++ * Based on code:
++ * Copyright (c) 2002 Hewlett-Packard Company
++ * Copyright (c) 2000 Nicolas Pitre <nico@cam.org>
++ * Copyright (c) 2000 Lernout & Hauspie Speech Products, N.V.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License.
++ */
++
++#include <sound/driver.h>
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/types.h>
++#include <linux/string.h>
++#include <linux/slab.h>
++#include <linux/errno.h>
++#include <linux/ioctl.h>
++#include <linux/delay.h>
++#include <linux/i2c.h>
++
++#include <sound/core.h>
++#include <sound/control.h>
++#include <sound/initval.h>
++#include <sound/info.h>
++
++#include "htcuniversal_ak4641.h"
++
++/* Registers */
++#define R_PM1 0x00
++#define R_PM2 0x01
++#define R_SEL1 0x02
++#define R_SEL2 0x03
++#define R_MODE1 0x04
++#define R_MODE2 0x05
++#define R_DAC 0x06
++#define R_MIC 0x07
++#define REG_TIMER 0x08
++#define REG_ALC1 0x09
++#define REG_ALC2 0x0a
++#define R_PGA 0x0b
++#define R_ATTL 0x0c
++#define R_ATTR 0x0d
++#define REG_VOL 0x0e
++#define R_STATUS 0x0f
++#define REG_EQLO 0x10
++#define REG_EQMID 0x11
++#define REG_EQHI 0x12
++#define REG_BTIF 0x13
++
++/* Register flags */
++/* REG_PWR1 */
++#define R_PM1_PMADC 0x01
++#define R_PM1_PMMIC 0x02
++#define REG_PWR1_PMAUX 0x04
++#define REG_PWR1_PMMO 0x08
++#define R_PM1_PMLO 0x10
++/* unused 0x20 */
++/* unused 0x40 */
++#define R_PM1_PMVCM 0x80
++
++/* REG_PWR2 */
++#define R_PM2_PMDAC 0x01
++/* unused 0x02 */
++/* unused 0x04 */
++#define R_PM2_PMMO2 0x08
++#define REG_PWR2_MCKAC 0x10
++/* unused 0x20 */
++/* unused 0x40 */
++#define R_PM2_MCKPD 0x80
++
++/* REG_SEL1 */
++#define R_SEL1_PSMO2 0x01
++/* unused 0x02 */
++/* unused 0x04 */
++/* unused 0x08 */
++#define REG_SEL1_MICM 0x10
++#define REG_SEL1_DACM 0x20
++#define REG_SEL1_PSMO 0x40
++#define REG_SEL1_MOGN 0x80
++
++/* REG_SEL2 */
++#define R_SEL2_PSLOR 0x01
++#define R_SEL2_PSLOL 0x02
++#define REG_SEL2_AUXSI 0x04
++/* unused 0x08 */
++#define REG_SEL2_MICL 0x10
++#define REG_SEL2_AUXL 0x20
++/* unused 0x40 */
++#define R_SEL2_DACL 0x80
++
++/* REG_MODE1 */
++#define REG_MODE1_DIF0 0x01
++#define REG_MODE1_DIF1 0x02
++/* unused 0x04 */
++/* unused 0x08 */
++/* unused 0x10 */
++/* unused 0x20 */
++/* unused 0x40 */
++/* unused 0x80 */
++
++/* REG_MODE2 */
++/* unused 0x01 */
++#define REG_MODE2_LOOP 0x02
++#define REG_MODE2_HPM 0x04
++/* unused 0x08 */
++/* unused 0x10 */
++#define REG_MODE2_MCK0 0x20
++#define REG_MODE2_MCK1 0x40
++/* unused 0x80 */
++
++/* REG_DAC */
++#define REG_DAC_DEM0 0x01
++#define REG_DAC_DEM1 0x02
++#define REG_DAC_EQ 0x04
++/* unused 0x08 */
++#define R_DAC_DATTC 0x10
++#define R_DAC_SMUTE 0x20
++#define REG_DAC_TM 0x40
++/* unused 0x80 */
++
++/* REG_MIC */
++#define R_MIC_MGAIN 0x01
++#define R_MIC_MSEL 0x02
++#define R_MIC_MICAD 0x04
++#define R_MIC_MPWRI 0x08
++#define R_MIC_MPWRE 0x10
++#define REG_MIC_AUXAD 0x20
++/* unused 0x40 */
++/* unused 0x80 */
++
++/* REG_TIMER */
++
++#define REG_TIMER_LTM0 0x01
++#define REG_TIMER_LTM1 0x02
++#define REG_TIMER_WTM0 0x04
++#define REG_TIMER_WTM1 0x08
++#define REG_TIMER_ZTM0 0x10
++#define REG_TIMER_ZTM1 0x20
++/* unused 0x40 */
++/* unused 0x80 */
++
++#define REG_ALC1_LMTH 0x01
++#define REG_ALC1_RATT 0x02
++#define REG_ALC1_LMAT0 0x04
++#define REG_ALC1_LMAT1 0x08
++#define REG_ALC1_ZELM 0x10
++#define REG_ALC1_ALC1 0x20
++/* unused 0x40 */
++/* unused 0x80 */
++
++/* REG_ALC2 */
++
++/* REG_PGA */
++
++/* REG_ATTL */
++
++/* REG_ATTR */
++
++/* REG_VOL */
++#define REG_VOL_ATTM 0x80
++
++/* REG_STATUS */
++#define R_STATUS_DTMIC 0x01
++
++/* REG_EQ controls use 4 bits for each of 5 EQ levels */
++
++/* Bluetooth not yet implemented */
++#define REG_BTIF_PMAD2 0x01
++#define REG_BTIF_PMDA2 0x02
++#define REG_BTIF_PMBIF 0x04
++#define REG_BTIF_ADC2 0x08
++#define REG_BTIF_DAC2 0x10
++#define REG_BTIF_BTFMT0 0x20
++#define REG_BTIF_BTFMT1 0x40
++/* unused 0x80 */
++
++/* begin {{ I2C }} */
++
++static struct i2c_driver snd_ak4641_i2c_driver = {
++ .driver = {
++ .name = "ak4641-i2c"
++ },
++};
++
++static int snd_ak4641_i2c_init(void)
++{
++ return i2c_add_driver(&snd_ak4641_i2c_driver);
++}
++
++static void snd_ak4641_i2c_free(void)
++{
++ i2c_del_driver(&snd_ak4641_i2c_driver);
++}
++
++static inline int snd_ak4641_i2c_probe(struct snd_ak4641 *ak)
++{
++ if (ak->i2c_client.adapter == NULL) return -EINVAL;
++ ak->i2c_client.addr = 0x12;
++ if (i2c_smbus_xfer(ak->i2c_client.adapter, ak->i2c_client.addr,
++ 0, 0, 0, I2C_SMBUS_QUICK, NULL) < 0)
++ return -ENODEV;
++ else return 0;
++}
++
++static int snd_ak4641_i2c_attach(struct snd_ak4641 *ak)
++{
++ int ret = 0;
++ if ((ret = snd_ak4641_i2c_probe(ak)) < 0) return ret;
++ snprintf(ak->i2c_client.name, sizeof(ak->i2c_client.name),
++ "ak4641-i2c at %d-%04x",
++ i2c_adapter_id(ak->i2c_client.adapter), ak->i2c_client.addr);
++ return i2c_attach_client(&ak->i2c_client);
++}
++
++static void snd_ak4641_i2c_detach(struct snd_ak4641 *ak)
++{
++ i2c_detach_client(&ak->i2c_client);
++}
++
++/* end {{ I2C }} */
++
++
++/* begin {{ Registers & Cache Ops }} */
++
++static int snd_ak4641_hwsync(struct snd_ak4641 *ak, int read, u8 reg)
++{
++ struct i2c_msg msgs[2];
++ u8 buf[2];
++ int ret;
++
++ snd_assert(reg < ARRAY_SIZE(ak->regs), return -EINVAL);
++
++ /* setup i2c msgs */
++ msgs[0].addr = ak->i2c_client.addr;
++ msgs[0].flags = 0;
++ msgs[0].buf = buf;
++ if (!read)
++ msgs[0].len = 2;
++ else {
++ msgs[1].flags = I2C_M_RD;
++ msgs[1].addr = msgs[0].addr;
++ msgs[1].buf = msgs[0].buf + 1;
++ msgs[0].len = 1;
++ msgs[1].len = 1;
++ }
++
++ buf[0] = reg;
++
++ /* regs[reg] -> buffer, on write */
++ if (!read) buf[1] = ak->regs[reg];
++
++ /* i2c transfer */
++ ret = i2c_transfer(ak->i2c_client.adapter, msgs, read ? 2 : 1);
++ if (ret != (read ? 2 : 1)) return ret; /* transfer error */ //@@ error ret < 0, or not ?
++
++ /* regs[reg] <- buffer, on read */
++ if (read) ak->regs[reg] = buf[1];
++
++ return 0;
++}
++
++static inline int snd_ak4641_hwsync_read(struct snd_ak4641 *ak, u8 reg)
++{
++ return snd_ak4641_hwsync(ak, 1, reg);
++}
++
++static inline int snd_ak4641_hwsync_write(struct snd_ak4641 *ak, u8 reg)
++{
++ return snd_ak4641_hwsync(ak, 0, reg);
++}
++
++static int snd_ak4641_hwsync_read_all(struct snd_ak4641 *ak)
++{
++ u8 reg;
++ for (reg = 0; reg < ARRAY_SIZE(ak->regs); reg++)
++ if (snd_ak4641_hwsync_read(ak, reg) < 0) return -1;
++ return 0;
++}
++
++static int snd_ak4641_hwsync_write_all(struct snd_ak4641 *ak)
++{
++ u8 reg;
++ for (reg = 0; reg < ARRAY_SIZE(ak->regs); reg++)
++ if (snd_ak4641_hwsync_write(ak, reg) < 0) return -1;
++ return 0;
++}
++
++static int snd_ak4641_reg_changed(struct snd_ak4641 *ak, u8 reg)
++{
++ if ((reg != R_PGA && ak->powered_on) ||
++ (reg == R_PGA && (ak->regs[R_PM1] & R_PM1_PMMIC)))
++ return snd_ak4641_hwsync_write(ak, reg);
++ return 0;
++}
++
++/* end {{ Registers & Cache Ops }}*/
++
++
++static inline void snd_ak4641_lock(struct snd_ak4641 *ak)
++{
++ down(&ak->sem);
++}
++
++static inline void snd_ak4641_unlock(struct snd_ak4641 *ak)
++{
++ up(&ak->sem);
++}
++
++#define WRITE_MASK(i, val, mask) (((i) & ~(mask)) | ((val) & (mask)))
++
++
++/* begin {{ Controls }} */
++
++#define INV_RANGE(val, mask) \
++ (~(val) & (mask))
++
++/*-begin----------------------------------------------------------*/
++static int snd_ak4641_actl_playback_volume_info(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_info *uinfo)
++{
++ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
++ uinfo->count = 2;
++ uinfo->value.integer.min = 0;
++ uinfo->value.integer.max = 0xff;
++ return 0;
++}
++
++static int snd_ak4641_actl_playback_volume_get(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ struct snd_ak4641 *ak = (struct snd_ak4641 *) kcontrol->private_data;
++
++ snd_ak4641_lock(ak);
++ ucontrol->value.integer.value[0] = INV_RANGE(ak->regs[R_ATTL], 0xff);
++ ucontrol->value.integer.value[1] = INV_RANGE(ak->regs[R_ATTR], 0xff);
++ snd_ak4641_unlock(ak);
++ return 0;
++}
++
++static int snd_ak4641_actl_playback_volume_put(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ struct snd_ak4641 *ak = (struct snd_ak4641 *) kcontrol->private_data;
++
++ snd_ak4641_lock(ak);
++ ak->regs[R_ATTL] = INV_RANGE(ucontrol->value.integer.value[0], 0xff);
++ ak->regs[R_ATTR] = INV_RANGE(ucontrol->value.integer.value[1], 0xff);
++ snd_ak4641_reg_changed(ak, R_ATTL);
++ snd_ak4641_reg_changed(ak, R_ATTR);
++ snd_ak4641_unlock(ak);
++ return 0;
++}
++/*-end------------------------------------------------------------*/
++
++/*-begin----------------------------------------------------------*/
++static int snd_ak4641_actl_mic_gain_info(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_info *uinfo)
++{
++ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
++ uinfo->count = 1;
++ uinfo->value.integer.min = 0;
++ uinfo->value.integer.max = 0x7f;
++ return 0;
++}
++
++static int snd_ak4641_actl_mic_gain_get(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ struct snd_ak4641 *ak = (struct snd_ak4641 *) kcontrol->private_data;
++
++ ucontrol->value.integer.value[0] = ak->regs[R_PGA];
++ return 0;
++}
++
++static int snd_ak4641_actl_mic_gain_put(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ struct snd_ak4641 *ak = (struct snd_ak4641 *) kcontrol->private_data;
++
++ snd_ak4641_lock(ak);
++ ak->regs[R_PGA] = ucontrol->value.integer.value[0];
++ snd_ak4641_reg_changed(ak, R_PGA);
++ snd_ak4641_unlock(ak);
++ return 0;
++}
++/*-end------------------------------------------------------------*/
++
++#define ACTL(ctl_name, _name) \
++static struct snd_kcontrol_new snd_ak4641_actl_ ## ctl_name = \
++{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = _name, \
++ .info = snd_ak4641_actl_ ## ctl_name ## _info, \
++ .get = snd_ak4641_actl_ ## ctl_name ## _get, .put = snd_ak4641_actl_ ## ctl_name ## _put };
++
++ACTL(playback_volume, "Master Playback Volume")
++ACTL(mic_gain, "Mic Capture Gain")
++
++struct snd_ak4641_uctl_bool {
++ int (*get) (struct snd_ak4641 *uda);
++ int (*set) (struct snd_ak4641 *uda, int on);
++};
++
++static int snd_ak4641_actl_bool_info(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_info *uinfo)
++{
++ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
++ uinfo->count = 1;
++ return 0;
++}
++
++static int snd_ak4641_actl_bool_get(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ struct snd_ak4641 *ak = (struct snd_ak4641 *) kcontrol->private_data;
++ struct snd_ak4641_uctl_bool *uctl =
++ (struct snd_ak4641_uctl_bool *) kcontrol->private_value;
++
++ ucontrol->value.integer.value[0] = uctl->get(ak);
++ return 0;
++}
++
++static int snd_ak4641_actl_bool_put(struct snd_kcontrol *kcontrol,
++ struct snd_ctl_elem_value *ucontrol)
++{
++ struct snd_ak4641 *ak = (struct snd_ak4641 *) kcontrol->private_data;
++ struct snd_ak4641_uctl_bool *uctl =
++ (struct snd_ak4641_uctl_bool *) kcontrol->private_value;
++
++ return uctl->set(ak, ucontrol->value.integer.value[0]);
++}
++
++/*-begin----------------------------------------------------------*/
++static int snd_ak4641_uctl_playback_switch_get(struct snd_ak4641 *ak)
++{
++ return (ak->regs[R_DAC] & R_DAC_SMUTE) == 0x00;
++}
++
++static int snd_ak4641_uctl_playback_switch_set(struct snd_ak4641 *ak, int on)
++{
++ snd_ak4641_lock(ak);
++ ak->regs[R_DAC] = WRITE_MASK(ak->regs[R_DAC],
++ on ? 0x00 : R_DAC_SMUTE, R_DAC_SMUTE);
++ snd_ak4641_reg_changed(ak, R_DAC);
++ snd_ak4641_unlock(ak);
++ return 0;
++}
++/*-end------------------------------------------------------------*/
++
++/*-begin----------------------------------------------------------*/
++static int snd_ak4641_uctl_mic_boost_get(struct snd_ak4641 *ak)
++{
++ return (ak->regs[R_MIC] & R_MIC_MGAIN) == R_MIC_MGAIN;
++}
++
++static int snd_ak4641_uctl_mic_boost_set(struct snd_ak4641 *ak, int on)
++{
++ snd_ak4641_lock(ak);
++ ak->regs[R_MIC] = WRITE_MASK(ak->regs[R_MIC],
++ on ? R_MIC_MGAIN : 0x00, R_MIC_MGAIN);
++ snd_ak4641_reg_changed(ak, R_MIC);
++ snd_ak4641_unlock(ak);
++ return 0;
++}
++/*-end------------------------------------------------------------*/
++
++/*-begin----------------------------------------------------------*/
++static int snd_ak4641_uctl_mono_out_get(struct snd_ak4641 *ak)
++{
++ printk("mono_out status 0x%8.8x -> 0x%8.8x\n",ak->regs[R_SEL1], ak->regs[R_SEL1] & REG_SEL1_PSMO);
++ return (ak->regs[R_SEL1] & REG_SEL1_PSMO) == REG_SEL1_PSMO;
++}
++
++static int snd_ak4641_uctl_mono_out_set(struct snd_ak4641 *ak, int on)
++{
++ printk("phone mic enable called. on=%d\n",on);
++ snd_ak4641_lock(ak);
++ ak->regs[R_PM1] = WRITE_MASK(ak->regs[R_PM1], on ? R_PM1_PMMIC : 0x00, R_PM1_PMMIC);
++ ak->regs[R_PM1] = WRITE_MASK(ak->regs[R_PM1], on ? REG_PWR1_PMMO : 0x00, REG_PWR1_PMMO);
++ snd_ak4641_reg_changed(ak, R_PM1);
++
++ snd_ak4641_hwsync_write(ak, R_PGA); /* mic PGA gain is reset when PMMIC = 0 */
++
++ /* internal mic */
++ ak->regs[R_MIC] = WRITE_MASK(ak->regs[R_MIC], on ? R_MIC_MPWRI : 0x0, R_MIC_MPWRI);
++ ak->regs[R_MIC] = WRITE_MASK(ak->regs[R_MIC], 0x0, R_MIC_MSEL);
++ snd_ak4641_hwsync_write(ak, R_MIC);
++
++// ak->regs[REG_BTIF] = WRITE_MASK(ak->regs[REG_BTIF], 0x0, REG_BTIF_DAC2);
++// snd_ak4641_hwsync_write(ak, REG_BTIF);
++ /* */
++// ak->regs[REG_VOL] = WRITE_MASK(ak->regs[REG_VOL], on ? REG_VOL_ATTM : 0x00, REG_VOL_ATTM);
++// ak->regs[R_SEL1] = WRITE_MASK(ak->regs[R_SEL1], on ? REG_SEL1_MOGN : 0x00, REG_SEL1_MOGN);
++ ak->regs[R_SEL1] = WRITE_MASK(ak->regs[R_SEL1], on ? REG_SEL1_MICM : 0x00, REG_SEL1_MICM);
++ ak->regs[R_SEL1] = WRITE_MASK(ak->regs[R_SEL1], on ? REG_SEL1_PSMO : 0x00, REG_SEL1_PSMO);
++ snd_ak4641_reg_changed(ak, R_SEL1);
++ snd_ak4641_unlock(ak);
++ return 0;
++}
++/*-end------------------------------------------------------------*/
++
++#define ACTL_BOOL(ctl_name, _name) \
++static struct snd_ak4641_uctl_bool snd_ak4641_actl_ ## ctl_name ## _pvalue = \
++{ .get = snd_ak4641_uctl_ ## ctl_name ## _get, \
++ .set = snd_ak4641_uctl_ ## ctl_name ## _set }; \
++static struct snd_kcontrol_new snd_ak4641_actl_ ## ctl_name = \
++{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = _name, .info = snd_ak4641_actl_bool_info, \
++ .get = snd_ak4641_actl_bool_get, .put = snd_ak4641_actl_bool_put, \
++ .private_value = (unsigned long) &snd_ak4641_actl_ ## ctl_name ## _pvalue };
++
++ACTL_BOOL(playback_switch, "Master Playback Switch")
++ACTL_BOOL(mic_boost, "Mic Boost (+20dB)")
++ACTL_BOOL(mono_out, "Phone mic enable")
++
++static void snd_ak4641_headphone_on(struct snd_ak4641 *ak, int on);
++static void snd_ak4641_speaker_on(struct snd_ak4641 *ak, int on);
++static void snd_ak4641_select_mic(struct snd_ak4641 *ak);
++
++void snd_ak4641_hp_connected(struct snd_ak4641 *ak, int connected)
++{
++ snd_ak4641_lock(ak);
++ if (connected != ak->hp_connected) {
++ ak->hp_connected = connected;
++
++ /* headphone or speaker, on playback */
++ if (ak->playback_on) {
++ if (connected) {
++ snd_ak4641_headphone_on(ak, 1);
++ snd_ak4641_speaker_on(ak, 0);
++ } else {
++ snd_ak4641_speaker_on(ak, 1);
++ snd_ak4641_headphone_on(ak, 0);
++ }
++ }
++
++ /* headset or internal mic, on capture */
++ if (ak->capture_on)
++ snd_ak4641_select_mic(ak);
++ }
++ snd_ak4641_unlock(ak);
++}
++
++/* end {{ Controls }} */
++
++
++/* begin {{ Headphone Detected Notification }} */
++
++static void snd_ak4641_hp_detected_w_fn(void *p)
++{
++ struct snd_ak4641 *ak = (struct snd_ak4641 *)p;
++
++ snd_ak4641_hp_connected(ak, ak->hp_detected.detected);
++}
++
++void snd_ak4641_hp_detected(struct snd_ak4641 *ak, int detected)
++{
++ if (detected != ak->hp_detected.detected) {
++ ak->hp_detected.detected = detected;
++ queue_work(ak->hp_detected.wq, &ak->hp_detected.w);
++ }
++}
++
++static int snd_ak4641_hp_detected_init(struct snd_ak4641 *ak)
++{
++ INIT_WORK(&ak->hp_detected.w, snd_ak4641_hp_detected_w_fn);
++ ak->hp_detected.detected = ak->hp_connected;
++ ak->hp_detected.wq = create_singlethread_workqueue("ak4641");
++ if (ak->hp_detected.wq) return 0;
++ else return -1;
++}
++
++static void snd_ak4641_hp_detected_free(struct snd_ak4641 *ak)
++{
++ destroy_workqueue(ak->hp_detected.wq);
++}
++
++/* end {{ Headphone Detected Notification }} */
++
++
++/* begin {{ Codec Control }} */
++
++static void snd_ak4641_headphone_on(struct snd_ak4641 *ak, int on)
++{
++ if (on) {
++ ak->regs[R_PM1] = WRITE_MASK(ak->regs[R_PM1], R_PM1_PMLO, R_PM1_PMLO);
++ snd_ak4641_hwsync_write(ak, R_PM1);
++ ak->headphone_out_on(1);
++ ak->regs[R_SEL2] = WRITE_MASK(ak->regs[R_SEL2],
++ R_SEL2_PSLOL | R_SEL2_PSLOR,
++ R_SEL2_PSLOL | R_SEL2_PSLOR);
++ snd_ak4641_hwsync_write(ak, R_SEL2);
++ } else {
++ ak->regs[R_SEL2] = WRITE_MASK(ak->regs[R_SEL2],
++ 0x00, R_SEL2_PSLOL | R_SEL2_PSLOR);
++ snd_ak4641_hwsync_write(ak, R_SEL2);
++ ak->headphone_out_on(0);
++ ak->regs[R_PM1] = WRITE_MASK(ak->regs[R_PM1], 0x00, R_PM1_PMLO);
++ snd_ak4641_hwsync_write(ak, R_PM1);
++ }
++}
++
++static void snd_ak4641_speaker_on(struct snd_ak4641 *ak, int on)
++{
++ if (on) {
++ ak->regs[R_PM1] = WRITE_MASK(ak->regs[R_PM1], R_PM1_PMLO, R_PM1_PMLO);
++ snd_ak4641_hwsync_write(ak, R_PM1);
++ ak->speaker_out_on(1);
++ ak->regs[R_SEL2] = WRITE_MASK(ak->regs[R_SEL2],
++ R_SEL2_PSLOL | R_SEL2_PSLOR,
++ R_SEL2_PSLOL | R_SEL2_PSLOR);
++ snd_ak4641_hwsync_write(ak, R_SEL2);
++ } else {
++ ak->regs[R_SEL2] = WRITE_MASK(ak->regs[R_SEL2],
++ 0x00, R_SEL2_PSLOL | R_SEL2_PSLOR);
++ snd_ak4641_hwsync_write(ak, R_SEL2);
++ ak->speaker_out_on(0);
++ ak->regs[R_PM1] = WRITE_MASK(ak->regs[R_PM1], 0x00, R_PM1_PMLO);
++ snd_ak4641_hwsync_write(ak, R_PM1);
++ }
++}
++
++static inline int snd_ak4641_power_on(struct snd_ak4641 *ak)
++{
++ ak->reset_pin(1);
++ ak->power_on_chip(1);
++ msleep(1);
++ ak->reset_pin(0);
++ ak->powered_on = 1;
++ return 0;
++}
++
++static inline int snd_ak4641_power_off(struct snd_ak4641 *ak)
++{
++ ak->powered_on = 0;
++ ak->power_on_chip(0);
++ return 0;
++}
++
++static inline void snd_ak4641_headphone_out_on(struct snd_ak4641 *ak, int on)
++{
++ if (ak->headphone_out_on) ak->headphone_out_on(on);
++}
++
++static inline void snd_ak4641_speaker_out_on(struct snd_ak4641 *ak, int on)
++{
++ if (ak->speaker_out_on) ak->speaker_out_on(on);
++}
++
++static int snd_ak4641_playback_on(struct snd_ak4641 *ak)
++{
++ if (ak->playback_on) return 0;
++
++ ak->regs[R_PM2] = WRITE_MASK(ak->regs[R_PM2],
++ R_PM2_PMDAC, R_PM2_MCKPD | R_PM2_PMDAC);
++ ak->regs[R_PM1] = WRITE_MASK(ak->regs[R_PM1], R_PM1_PMLO, R_PM1_PMLO);
++ snd_ak4641_hwsync_write(ak, R_PM2);
++ snd_ak4641_hwsync_write(ak, R_PM1);
++ if (ak->hp_connected) snd_ak4641_headphone_on(ak, 1);
++ else snd_ak4641_speaker_on(ak, 1);
++
++ ak->playback_on = 1;
++
++ return 0;
++}
++
++static int snd_ak4641_playback_off(struct snd_ak4641 *ak)
++{
++ if (!ak->playback_on) return 0;
++
++ ak->playback_on = 0;
++
++ if (ak->hp_connected) snd_ak4641_headphone_on(ak, 0);
++ else snd_ak4641_speaker_on(ak, 0);
++ ak->regs[R_PM1] = WRITE_MASK(ak->regs[R_PM1], 0x00, R_PM1_PMLO);
++ ak->regs[R_PM2] = WRITE_MASK(ak->regs[R_PM2],
++ (!ak->capture_on ? R_PM2_MCKPD : 0x00) | R_PM2_PMDAC,
++ R_PM2_MCKPD | R_PM2_PMDAC);
++ snd_ak4641_hwsync_write(ak, R_PM1);
++ snd_ak4641_hwsync_write(ak, R_PM2);
++
++ return 0;
++}
++
++static void snd_ak4641_select_mic(struct snd_ak4641 *ak)
++{
++ int mic = 0;
++ u8 r_mic;
++
++ if (ak->hp_connected) {
++ /* check headset mic */
++ ak->regs[R_MIC] = WRITE_MASK(ak->regs[R_MIC], R_MIC_MPWRE, R_MIC_MPWRE);
++ snd_ak4641_hwsync_write(ak, R_MIC);
++ snd_ak4641_hwsync_read(ak, R_STATUS);
++ mic = (ak->regs[R_STATUS] & R_STATUS_DTMIC) == R_STATUS_DTMIC;
++
++ printk("htcuniversal_ak4641_select_mic: mic=%d\n",mic);
++
++ r_mic = WRITE_MASK(ak->regs[R_MIC],
++ R_MIC_MSEL | (ak->capture_on ? R_MIC_MPWRE : 0x00),
++ R_MIC_MSEL | R_MIC_MPWRI | R_MIC_MPWRE);
++ }
++ else
++ r_mic = WRITE_MASK(ak->regs[R_MIC],
++ 0x00 | (ak->capture_on ? R_MIC_MPWRI : 0x00),
++ R_MIC_MSEL | R_MIC_MPWRI | R_MIC_MPWRE);
++
++ if (r_mic != ak->regs[R_MIC]) {
++ ak->regs[R_MIC] = r_mic;
++ snd_ak4641_hwsync_write(ak, R_MIC);
++ }
++}
++
++static int snd_ak4641_capture_on(struct snd_ak4641 *ak)
++{
++ if (ak->capture_on) return 0;
++
++ if (!ak->playback_on) {
++ ak->regs[R_PM2] = WRITE_MASK(ak->regs[R_PM2], 0x00, R_PM2_MCKPD);
++ snd_ak4641_hwsync_write(ak, R_PM2);
++ }
++ ak->regs[R_PM1] = WRITE_MASK(ak->regs[R_PM1], R_PM1_PMMIC | R_PM1_PMADC,
++ R_PM1_PMMIC | R_PM1_PMADC);
++ snd_ak4641_hwsync_write(ak, R_PM1);
++ snd_ak4641_hwsync_write(ak, R_PGA); /* mic PGA gain is reset when PMMIC = 0 */
++
++ ak->capture_on = 1;
++
++ snd_ak4641_select_mic(ak);
++
++ msleep(47); /* accounts for ADC init cycle, time enough for fs >= 44.1 kHz */
++
++ return 0;
++}
++
++static int snd_ak4641_capture_off(struct snd_ak4641 *ak)
++{
++ if (!ak->capture_on) return 0;
++
++ ak->regs[R_MIC] = WRITE_MASK(ak->regs[R_MIC],
++ 0x00, R_MIC_MPWRI | R_MIC_MPWRE | R_MIC_MSEL);
++ ak->regs[R_PM1] = WRITE_MASK(ak->regs[R_PM1], 0x00, R_PM1_PMMIC | R_PM1_PMADC);
++ snd_ak4641_hwsync_write(ak, R_MIC);
++ snd_ak4641_hwsync_write(ak, R_PM1);
++ if (!ak->playback_on) {
++ ak->regs[R_PM2] = WRITE_MASK(ak->regs[R_PM2], R_PM2_MCKPD, R_PM2_MCKPD);
++ snd_ak4641_hwsync_write(ak, R_PM2);
++ }
++
++ ak->capture_on = 0;
++
++ return 0;
++}
++
++int snd_ak4641_open_stream(struct snd_ak4641 *ak, int stream)
++{
++ snd_ak4641_lock(ak);
++ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
++ ak->playback_stream_opened = 1;
++ snd_ak4641_playback_on(ak);
++ } else {
++ ak->capture_stream_opened = 1;
++ snd_ak4641_capture_on(ak);
++ }
++ snd_ak4641_unlock(ak);
++ return 0;
++}
++
++int snd_ak4641_close_stream(struct snd_ak4641 *ak, int stream)
++{
++ snd_ak4641_lock(ak);
++ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
++ ak->playback_stream_opened = 0;
++ snd_ak4641_playback_off(ak);
++ } else {
++ ak->capture_stream_opened = 0;
++ snd_ak4641_capture_off(ak);
++ }
++ snd_ak4641_unlock(ak);
++ return 0;
++}
++
++static int snd_ak4641_init_regs(struct snd_ak4641 *ak)
++{
++ snd_ak4641_hwsync_read_all(ak);
++
++ //@@ MEMO: add some configs
++
++ ak->regs[R_PM1] = WRITE_MASK(ak->regs[R_PM1], R_PM1_PMVCM, R_PM1_PMVCM);
++ ak->regs[R_DAC] = WRITE_MASK(ak->regs[R_DAC], 0x00, R_DAC_DATTC);
++ snd_ak4641_hwsync_write(ak, R_PM1);
++ snd_ak4641_hwsync_write(ak, R_DAC);
++
++ return 0;
++}
++
++int snd_ak4641_suspend(struct snd_ak4641 *ak, pm_message_t state)
++{
++ snd_ak4641_lock(ak);
++ if (ak->playback_on) snd_ak4641_playback_off(ak);
++ if (ak->capture_on) snd_ak4641_capture_off(ak);
++ snd_ak4641_power_off(ak);
++ snd_ak4641_unlock(ak);
++ return 0;
++}
++
++int snd_ak4641_resume(struct snd_ak4641 *ak)
++{
++ snd_ak4641_lock(ak);
++ snd_ak4641_power_on(ak);
++ snd_ak4641_hwsync_write_all(ak);
++ if (ak->playback_stream_opened) snd_ak4641_playback_on(ak);
++ if (ak->capture_stream_opened) snd_ak4641_capture_on(ak);
++ snd_ak4641_unlock(ak);
++ return 0;
++}
++
++static void snd_ak4641_init_ak(struct snd_ak4641 *ak)
++{
++ init_MUTEX(&ak->sem);
++ ak->i2c_client.driver = &snd_ak4641_i2c_driver;
++}
++
++int snd_ak4641_activate(struct snd_ak4641 *ak)
++{
++ int ret = 0;
++
++ snd_ak4641_init_ak(ak);
++ snd_ak4641_lock(ak);
++ snd_ak4641_power_on(ak);
++ if ((ret = snd_ak4641_i2c_attach(ak)) < 0)
++ goto failed_i2c_attach;
++ snd_ak4641_init_regs(ak);
++ if ((ret = snd_ak4641_hp_detected_init(ak)) < 0)
++ goto failed_hp_detected_init;
++ snd_ak4641_unlock(ak);
++ return 0;
++
++ failed_hp_detected_init:
++ snd_ak4641_i2c_detach(ak);
++ failed_i2c_attach:
++ snd_ak4641_power_off(ak);
++ snd_ak4641_unlock(ak);
++ return ret;
++}
++
++void snd_ak4641_deactivate(struct snd_ak4641 *ak)
++{
++ snd_ak4641_lock(ak);
++ snd_ak4641_hp_detected_free(ak);
++ snd_ak4641_i2c_detach(ak);
++ snd_ak4641_power_off(ak);
++ snd_ak4641_unlock(ak);
++}
++
++int snd_ak4641_add_mixer_controls(struct snd_ak4641 *ak, struct snd_card *card)
++{
++ snd_ak4641_lock(ak);
++ snd_ctl_add(card, snd_ctl_new1(&snd_ak4641_actl_playback_volume, ak));
++ snd_ctl_add(card, snd_ctl_new1(&snd_ak4641_actl_playback_switch, ak));
++ snd_ctl_add(card, snd_ctl_new1(&snd_ak4641_actl_mic_gain, ak));
++ snd_ctl_add(card, snd_ctl_new1(&snd_ak4641_actl_mic_boost, ak));
++ snd_ctl_add(card, snd_ctl_new1(&snd_ak4641_actl_mono_out, ak));
++ snd_ak4641_unlock(ak);
++ return 0;
++}
++
++/* end {{ Codec Control }} */
++
++
++/* begin {{ Module }} */
++
++static int __init snd_ak4641_module_on_load(void)
++{
++ snd_ak4641_i2c_init();
++ return 0;
++}
++
++static void __exit snd_ak4641_module_on_unload(void)
++{
++ snd_ak4641_i2c_free();
++}
++
++module_init(snd_ak4641_module_on_load);
++module_exit(snd_ak4641_module_on_unload);
++
++EXPORT_SYMBOL(snd_ak4641_activate);
++EXPORT_SYMBOL(snd_ak4641_deactivate);
++EXPORT_SYMBOL(snd_ak4641_add_mixer_controls);
++EXPORT_SYMBOL(snd_ak4641_open_stream);
++EXPORT_SYMBOL(snd_ak4641_close_stream);
++EXPORT_SYMBOL(snd_ak4641_suspend);
++EXPORT_SYMBOL(snd_ak4641_resume);
++EXPORT_SYMBOL(snd_ak4641_hp_connected);
++EXPORT_SYMBOL(snd_ak4641_hp_detected);
++
++MODULE_AUTHOR("Giorgio Padrin");
++MODULE_DESCRIPTION("Audio support for codec Asahi Kasei AK4641");
++MODULE_LICENSE("GPL");
++
++/* end {{ Module }} */
+Index: linux-2.6.26-rc4/arch/arm/mach-pxa/htcuniversal/htcuniversal_ak4641.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26-rc4/arch/arm/mach-pxa/htcuniversal/htcuniversal_ak4641.h 2008-06-01 18:49:45.000000000 +0100
+@@ -0,0 +1,65 @@
++/*
++ * Audio support for codec Asahi Kasei AK4641
++ *
++ * 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.
++ *
++ * Copyright (c) 2006 Giorgio Padrin <giorgio@mandarinlogiq.org>
++ */
++
++#ifndef __SOUND_AK4641_H
++#define __SOUND_AK4641_H
++
++#include <linux/i2c.h>
++
++struct snd_ak4641 {
++ struct semaphore sem;
++
++ u8 regs[0x14]; /* registers cache */
++
++ unsigned int
++ powered_on:1,
++ playback_on:1,
++ playback_stream_opened:1,
++ capture_on:1,
++ capture_stream_opened:1;
++
++ unsigned int
++ hp_connected:1;
++
++ /* -- configuration (to fill before activation) -- */
++ void (*power_on_chip)(int on);
++ void (*reset_pin)(int on);
++ void (*headphone_out_on)(int on);
++ void (*speaker_out_on)(int on);
++
++ struct i2c_client i2c_client; /* to fill .adapter */
++ /* ----------------------------------------------- */
++
++ struct {
++ int detected;
++ struct workqueue_struct *wq;
++ struct work_struct w;
++ } hp_detected;
++};
++
++
++/* Note: opening, closing, suspending and resuming a stream
++ * require the clocks (MCLK and I2S ones) running
++ */
++
++/* don't forget to specify I2C adapter in i2c_client field */
++int snd_ak4641_activate(struct snd_ak4641 *ak);
++
++void snd_ak4641_deactivate(struct snd_ak4641 *ak);
++int snd_ak4641_add_mixer_controls(struct snd_ak4641 *ak, struct snd_card *card);
++int snd_ak4641_open_stream(struct snd_ak4641 *ak, int stream);
++int snd_ak4641_close_stream(struct snd_ak4641 *ak, int stream);
++int snd_ak4641_suspend(struct snd_ak4641 *ak, pm_message_t state);
++int snd_ak4641_resume(struct snd_ak4641 *ak);
++
++void snd_ak4641_hp_connected(struct snd_ak4641 *ak, int connected); /* non atomic context */
++void snd_ak4641_hp_detected(struct snd_ak4641 *ak, int detected); /* atomic context */
++
++#endif /* __SOUND_AK4641_H */
+Index: linux-2.6.26-rc4/arch/arm/mach-pxa/htcuniversal/htcuniversal_asic3_leds.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26-rc4/arch/arm/mach-pxa/htcuniversal/htcuniversal_asic3_leds.c 2008-06-01 18:49:45.000000000 +0100
+@@ -0,0 +1,143 @@
++/*
++ * LEDs support for the HP iPaq hx4700
++ *
++ * Copyright (c) 2006 Anton Vorontsov <cbou@mail.ru>
++ *
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License. See the file COPYING in the main directory of this archive for
++ * more details.
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/platform_device.h>
++#include <linux/leds.h>
++#include <linux/soc/asic3_base.h>
++
++#include <asm/hardware/ipaq-asic3.h>
++#include <asm/mach-types.h>
++#include <asm/hardware/asic3_leds.h>
++#include <asm/arch/htcuniversal-asic.h>
++
++//FIXME
++//DEFINE_LED_TRIGGER_SHARED_GLOBAL(htcuniversal_radio_trig);
++//EXPORT_LED_TRIGGER_SHARED(htcuniversal_radio_trig);
++
++static struct asic3_led htcuniversal_leds[] = {
++ {
++ .led_cdev = {
++ .name = "htcuniversal:red",
++ .default_trigger = "htcuniversal-charging",
++ },
++ .hw_num = 2,
++
++ },
++ {
++ .led_cdev = {
++ .name = "htcuniversal:green",
++ .default_trigger = "htcuniversal-chargefull",
++ },
++ .hw_num = 1,
++ },
++ {
++ .led_cdev = {
++ .name = "htcuniversal:wifi-bt",
++ .default_trigger = "htcuniversal-radio",
++ },
++ .hw_num = 0,
++ },
++ {
++ .led_cdev = {
++ .name = "htcuniversal:phonebuttons",
++ .default_trigger = "htcuniversal-phonebuttons",
++ },
++ .hw_num = -1,
++ .gpio_num = ('D'-'A')*16+GPIOD_BL_KEYP_PWR_ON,
++ },
++ {
++ .led_cdev = {
++ .name = "htcuniversal:vibra",
++ .default_trigger = "htcuniversal-vibra",
++ },
++ .hw_num = -1,
++ .gpio_num = ('D'-'A')*16+GPIOD_VIBRA_PWR_ON,
++ },
++ {
++ .led_cdev = {
++ .name = "htcuniversal:flashlight1",
++ .default_trigger = "htcuniversal-flashlight1",
++ },
++ .hw_num = -1,
++ .gpio_num = ('A'-'A')*16+GPIOA_FLASHLIGHT,
++ },
++ {
++ .led_cdev = {
++ .name = "htcuniversal:kbdbacklight",
++ .default_trigger = "htcuniversal-kbdbacklight",
++ },
++ .hw_num = -1,
++ .gpio_num = ('D'-'A')*16+GPIOD_BL_KEYB_PWR_ON,
++ },
++};
++
++void htcuniversal_leds_release(struct device *dev)
++{
++ return;
++}
++
++static
++struct asic3_leds_machinfo htcuniversal_leds_machinfo = {
++ .num_leds = ARRAY_SIZE(htcuniversal_leds),
++ .leds = htcuniversal_leds,
++ .asic3_pdev = &htcuniversal_asic3,
++};
++
++static
++struct platform_device htcuniversal_leds_pdev = {
++ .name = "asic3-leds",
++ .dev = {
++ .platform_data = &htcuniversal_leds_machinfo,
++ .release = htcuniversal_leds_release,
++ },
++};
++
++static
++int __init htcuniversal_leds_init(void)
++{
++ int ret;
++ printk("htcuniversal LEDs Driver\n");
++// led_trigger_register_shared("htcuniversal-radio", &htcuniversal_radio_trig);
++
++ ret = asic3_leds_register();
++ if (ret) goto asic3_leds_failed;
++
++ ret = platform_device_register(&htcuniversal_leds_pdev);
++ if (ret) goto platform_device_failed;
++
++ goto success;
++
++platform_device_failed:
++ asic3_leds_unregister();
++asic3_leds_failed:
++// led_trigger_unregister_shared(htcuniversal_radio_trig);
++ printk("htcuniversal LEDs Driver failed to init");
++success:
++ return ret;
++}
++
++static
++void __exit htcuniversal_leds_exit(void)
++{
++// led_trigger_unregister_shared(htcuniversal_radio_trig);
++ platform_device_unregister(&htcuniversal_leds_pdev);
++ asic3_leds_unregister();
++ return;
++}
++
++module_init(htcuniversal_leds_init);
++module_exit(htcuniversal_leds_exit);
++
++MODULE_AUTHOR("Anton Vorontsov <cbou@mail.ru>");
++MODULE_DESCRIPTION("htcuniversal LEDs driver");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.26-rc4/arch/arm/mach-pxa/htcuniversal/htcuniversal_bl.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26-rc4/arch/arm/mach-pxa/htcuniversal/htcuniversal_bl.c 2008-06-01 18:49:45.000000000 +0100
+@@ -0,0 +1,61 @@
++/*
++ * Use consistent with the GNU GPL is permitted,
++ * provided that this copyright notice is
++ * preserved in its entirety in all copies and derived works.
++ *
++ * Copyright (C) 2006 Paul Sokolosvky
++ * Based on code from older versions of htcuniversal_lcd.c
++ *
++ */
++
++#include <linux/types.h>
++#include <linux/platform_device.h>
++#include <asm/arch/hardware.h> /* for pxa-regs.h (__REG) */
++#include <asm/arch/pxa-regs.h>
++#include <asm/mach-types.h> /* machine_is_htcuniversal */
++//#include <linux/corgi_bl.h>
++#include <linux/backlight.h>
++#include <linux/err.h>
++
++#include <asm/arch/htcuniversal-gpio.h>
++#include <asm/arch/htcuniversal-asic.h>
++#include <asm/hardware/ipaq-asic3.h>
++#include <linux/soc/asic3_base.h>
++
++#define HTCUNIVERSAL_MAX_INTENSITY 0xc7
++
++static void htcuniversal_set_bl_intensity(int intensity)
++{
++ PWM_CTRL1 = 1; /* pre-scaler */
++ PWM_PWDUTY1 = intensity; /* duty cycle */
++ PWM_PERVAL1 = HTCUNIVERSAL_MAX_INTENSITY+1; /* period */
++
++ if (intensity > 0) {
++ pxa_set_cken(CKEN_PWM1, 1);
++ asic3_set_gpio_out_d(&htcuniversal_asic3.dev,
++ (1<<GPIOD_FL_PWR_ON), (1<<GPIOD_FL_PWR_ON));
++ } else {
++ pxa_set_cken(CKEN_PWM1, 0);
++ asic3_set_gpio_out_d(&htcuniversal_asic3.dev,
++ (1<<GPIOD_FL_PWR_ON), 0);
++ }
++}
++
++
++static struct generic_bl_info htcuniversal_bl_machinfo = {
++ .default_intensity = HTCUNIVERSAL_MAX_INTENSITY / 4,
++ .limit_mask = 0xff,
++ .max_intensity = HTCUNIVERSAL_MAX_INTENSITY,
++ .set_bl_intensity = htcuniversal_set_bl_intensity,
++};
++
++struct platform_device htcuniversal_bl = {
++ .name = "corgi-bl",
++ .dev = {
++ .platform_data = &htcuniversal_bl_machinfo,
++ },
++};
++
++MODULE_AUTHOR("Paul Sokolovsky <pmiscml@gmail.com>");
++MODULE_DESCRIPTION("Backlight driver for HTC Universal");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.26-rc4/arch/arm/mach-pxa/htcuniversal/htcuniversal_bt.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26-rc4/arch/arm/mach-pxa/htcuniversal/htcuniversal_bt.c 2008-06-01 18:49:45.000000000 +0100
+@@ -0,0 +1,135 @@
++/* Bluetooth interface driver for TI BRF6150 on HX4700
++ *
++ * Copyright (c) 2005 SDG Systems, LLC
++ *
++ * 2005-04-21 Todd Blumer Created.
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/delay.h>
++#include <linux/platform_device.h>
++#include <linux/soc/asic3_base.h>
++
++#include <asm/hardware.h>
++#include <asm/arch/serial.h>
++#include <asm/hardware/ipaq-asic3.h>
++#include <asm/arch/htcuniversal-gpio.h>
++#include <asm/arch/htcuniversal-asic.h>
++
++#include "htcuniversal_bt.h"
++
++static uint use_led=1;
++
++static void
++htcuniversal_bt_configure( int state )
++{
++ int tries;
++
++ printk( KERN_NOTICE "htcuniversal configure bluetooth: %d\n", state );
++ switch (state) {
++
++ case PXA_UART_CFG_PRE_STARTUP:
++ break;
++
++ case PXA_UART_CFG_POST_STARTUP:
++ /* pre-serial-up hardware configuration */
++ htcuniversal_egpio_enable(1<<EGPIO5_BT_3V3_ON);
++ mdelay(50);
++ asic3_set_gpio_out_c(&htcuniversal_asic3.dev, 1<<GPIOC_BT_PWR_ON, 1<<GPIOC_BT_PWR_ON);
++ mdelay(10);
++ asic3_set_gpio_out_c(&htcuniversal_asic3.dev, 1<<GPIOC_BT_RESET, 0);
++ mdelay(10);
++ asic3_set_gpio_out_c(&htcuniversal_asic3.dev, 1<<GPIOC_BT_RESET, 1<<GPIOC_BT_RESET);
++ mdelay(10);
++
++ /*
++ * BRF6150's RTS goes low when firmware is ready
++ * so check for CTS=1 (nCTS=0 -> CTS=1). Typical 150ms
++ */
++ tries = 0;
++ do {
++ mdelay(10);
++ } while ((BTMSR & MSR_CTS) == 0 && tries++ < 50);
++ if (use_led) {
++// htcuniversal_set_led(2, 16, 16);
++ }
++ break;
++
++ case PXA_UART_CFG_PRE_SHUTDOWN:
++ htcuniversal_egpio_disable(1<<EGPIO5_BT_3V3_ON );
++ mdelay(50);
++// htcuniversal_clear_led(2);
++ asic3_set_gpio_out_c(&htcuniversal_asic3.dev, 1<<GPIOC_BT_PWR_ON, 0);
++ break;
++
++ default:
++ break;
++ }
++}
++
++
++static int
++htcuniversal_bt_probe( struct platform_device *dev )
++{
++ struct htcuniversal_bt_funcs *funcs = dev->dev.platform_data;
++
++ /* configure bluetooth UART */
++ pxa_gpio_mode( GPIO_NR_HTCUNIVERSAL_BT_RXD_MD );
++ pxa_gpio_mode( GPIO_NR_HTCUNIVERSAL_BT_TXD_MD );
++ pxa_gpio_mode( GPIO_NR_HTCUNIVERSAL_BT_UART_CTS_MD );
++ pxa_gpio_mode( GPIO_NR_HTCUNIVERSAL_BT_UART_RTS_MD );
++
++ funcs->configure = htcuniversal_bt_configure;
++
++ /* Make sure the LED is off */
++// htcuniversal_clear_led(2);
++
++ return 0;
++}
++
++static int
++htcuniversal_bt_remove( struct platform_device *dev )
++{
++ struct htcuniversal_bt_funcs *funcs = dev->dev.platform_data;
++
++ funcs->configure = NULL;
++
++ /* Make sure the LED is off */
++// htcuniversal_clear_led(2);
++
++ return 0;
++}
++
++static struct platform_driver bt_driver = {
++ .driver = {
++ .name = "htcuniversal_bt",
++ },
++ .probe = htcuniversal_bt_probe,
++ .remove = htcuniversal_bt_remove,
++};
++
++module_param(use_led, uint, 0);
++
++static int __init
++htcuniversal_bt_init( void )
++{
++ printk(KERN_NOTICE "htcuniversal Bluetooth Driver\n");
++ return platform_driver_register( &bt_driver );
++}
++
++static void __exit
++htcuniversal_bt_exit( void )
++{
++ platform_driver_unregister( &bt_driver );
++}
++
++module_init( htcuniversal_bt_init );
++module_exit( htcuniversal_bt_exit );
++
++MODULE_AUTHOR("Todd Blumer, SDG Systems, LLC");
++MODULE_DESCRIPTION("HTC Universal Bluetooth Support Driver");
++MODULE_LICENSE("GPL");
++
++/* vim600: set noexpandtab sw=8 ts=8 :*/
++
+Index: linux-2.6.26-rc4/arch/arm/mach-pxa/htcuniversal/htcuniversal_bt.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26-rc4/arch/arm/mach-pxa/htcuniversal/htcuniversal_bt.h 2008-06-01 18:49:45.000000000 +0100
+@@ -0,0 +1,17 @@
++/*
++ * Bluetooth support file for calling bluetooth configuration functions
++ *
++ * Copyright (c) 2005 SDG Systems, LLC
++ *
++ * 2005-06 Todd Blumer Initial Revision
++ */
++
++#ifndef _HTCUNIVERSAL_BT_H
++#define _HTCUNIVERSAL_BT_H
++
++struct htcuniversal_bt_funcs {
++ void (*configure) ( int state );
++};
++
++
++#endif
+Index: linux-2.6.26-rc4/arch/arm/mach-pxa/htcuniversal/htcuniversal_buttons.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26-rc4/arch/arm/mach-pxa/htcuniversal/htcuniversal_buttons.c 2008-06-01 18:49:45.000000000 +0100
+@@ -0,0 +1,87 @@
++/*
++ * Buttons driver for HTC Universal
++ *
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License.
++ *
++ * Copyright (C) 2005 Pawel Kolodziejski
++ * Copyright (C) 2003 Joshua Wise
++ *
++ */
++
++#include <linux/input.h>
++#include <linux/input_pda.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/interrupt.h>
++#include <linux/irq.h>
++#include <linux/platform_device.h>
++#include <linux/gpio_keys.h>
++#include <linux/soc/asic3_base.h>
++#include <asm/mach-types.h>
++#include <asm/hardware/asic3_keys.h>
++#include <asm/arch/htcuniversal-gpio.h>
++#include <asm/arch/htcuniversal-asic.h>
++
++static struct asic3_keys_button asic3_buttons[] = {
++//{KEY_SCREEN, ASIC3_GPIOA_IRQ_BASE+GPIOA_COVER_ROTATE_N, 1, "screen_cover", EV_SW},
++//{KEY_SWITCHVIDEOMODE, ASIC3_GPIOB_IRQ_BASE+GPIOB_CLAMSHELL_N, 1, "clamshell_rotate", EV_SW},
++//{KEY_KBDILLUMTOGGLE, ASIC3_GPIOB_IRQ_BASE+GPIOB_NIGHT_SENSOR, 1, "night_sensor", EV_SW},
++{SW_LID, ASIC3_GPIOA_IRQ_BASE+GPIOA_COVER_ROTATE_N, 1, "screen_cover", EV_SW},
++{SW_TABLET_MODE, ASIC3_GPIOB_IRQ_BASE+GPIOB_CLAMSHELL_N, 1, "clamshell_rotate", EV_SW},
++//{SW_NIGHT_SENSOR, ASIC3_GPIOB_IRQ_BASE+GPIOB_NIGHT_SENSOR, 1, "night_sensor", EV_SW},
++{KEY_F10, ASIC3_GPIOA_IRQ_BASE+GPIOA_BUTTON_BACKLIGHT_N, 1, "backlight_button"},
++{KEY_RECORD, ASIC3_GPIOA_IRQ_BASE+GPIOA_BUTTON_RECORD_N, 1, "record_button"},
++{KEY_CAMERA, ASIC3_GPIOA_IRQ_BASE+GPIOA_BUTTON_CAMERA_N, 1, "camera_button"},
++{KEY_VOLUMEDOWN, ASIC3_GPIOA_IRQ_BASE+GPIOA_VOL_UP_N, 1, "volume_slider_down"},
++{KEY_VOLUMEUP, ASIC3_GPIOA_IRQ_BASE+GPIOA_VOL_DOWN_N, 1, "volume_slider_up"},
++{KEY_KPENTER, ASIC3_GPIOD_IRQ_BASE+GPIOD_KEY_OK_N, 1, "select"},
++{KEY_RIGHT, ASIC3_GPIOD_IRQ_BASE+GPIOD_KEY_RIGHT_N, 1, "right"},
++{KEY_LEFT, ASIC3_GPIOD_IRQ_BASE+GPIOD_KEY_LEFT_N, 1, "left"},
++{KEY_DOWN, ASIC3_GPIOD_IRQ_BASE+GPIOD_KEY_DOWN_N, 1, "down"},
++{KEY_UP, ASIC3_GPIOD_IRQ_BASE+GPIOD_KEY_UP_N, 1, "up"},
++};
++
++static struct asic3_keys_platform_data asic3_keys_data = {
++ .buttons = asic3_buttons,
++ .nbuttons = ARRAY_SIZE(asic3_buttons),
++ .asic3_dev = &htcuniversal_asic3.dev,
++};
++
++static struct platform_device htcuniversal_keys_asic3 = {
++ .name = "asic3-keys",
++ .dev = { .platform_data = &asic3_keys_data, }
++};
++
++static int __init htcuniversal_buttons_probe(struct platform_device *dev)
++{
++ platform_device_register(&htcuniversal_keys_asic3);
++ return 0;
++}
++
++static struct platform_driver htcuniversal_buttons_driver = {
++ .driver = {
++ .name = "htcuniversal_buttons",
++ },
++ .probe = htcuniversal_buttons_probe,
++};
++
++static int __init htcuniversal_buttons_init(void)
++{
++ if (!machine_is_htcuniversal())
++ return -ENODEV;
++
++ return platform_driver_register(&htcuniversal_buttons_driver);
++}
++
++static void __exit htcuniversal_buttons_exit(void)
++{
++ platform_driver_unregister(&htcuniversal_buttons_driver);
++}
++
++module_init(htcuniversal_buttons_init);
++module_exit(htcuniversal_buttons_exit);
++
++MODULE_AUTHOR ("Joshua Wise, Pawel Kolodziejski, Paul Sokolosvky");
++MODULE_DESCRIPTION ("Buttons support for HTC Universal");
++MODULE_LICENSE ("GPL");
+Index: linux-2.6.26-rc4/arch/arm/mach-pxa/htcuniversal/htcuniversal_core.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26-rc4/arch/arm/mach-pxa/htcuniversal/htcuniversal_core.c 2008-06-01 18:49:45.000000000 +0100
+@@ -0,0 +1,226 @@
++/* Core Hardware driver for Hx4700 (Serial, ASIC3, EGPIOs)
++ *
++ * Copyright (c) 2005 SDG Systems, LLC
++ *
++ * 2005-03-29 Todd Blumer Converted basic structure to support hx4700
++ * 2005-04-30 Todd Blumer Add IRDA code from H2200
++ */
++
++#include <linux/module.h>
++#include <linux/version.h>
++#include <linux/interrupt.h>
++#include <linux/platform_device.h>
++#include <linux/delay.h>
++#include <linux/pm.h>
++#include <linux/irq.h>
++
++#include <asm/io.h>
++#include <asm/mach/irq.h>
++#include <asm/arch/pxa-regs.h>
++#include <asm/arch/pxa-pm_ll.h>
++#include <asm/arch/htcuniversal-gpio.h>
++#include <asm/arch/htcuniversal-asic.h>
++
++#include <linux/soc/asic3_base.h>
++#include <asm/hardware/ipaq-asic3.h>
++
++volatile u_int16_t *egpios;
++u_int16_t egpio_reg;
++
++static int htc_bootloader = 0; /* Is the stock HTC bootloader installed? */
++
++/*
++ * may make sense to put egpios elsewhere, but they're here now
++ * since they share some of the same address space with the TI WLAN
++ *
++ * EGPIO register is write-only
++ */
++
++void
++htcuniversal_egpio_enable( u_int16_t bits )
++{
++ unsigned long flags;
++
++ local_irq_save(flags);
++
++ egpio_reg |= bits;
++ *egpios = egpio_reg;
++
++ local_irq_restore(flags);
++}
++EXPORT_SYMBOL_GPL(htcuniversal_egpio_enable);
++
++void
++htcuniversal_egpio_disable( u_int16_t bits )
++{
++ unsigned long flags;
++
++ local_irq_save(flags);
++
++ egpio_reg &= ~bits;
++ *egpios = egpio_reg;
++
++ local_irq_restore(flags);
++}
++EXPORT_SYMBOL_GPL(htcuniversal_egpio_disable);
++
++#ifdef CONFIG_PM
++
++//void htcuniversal_ll_pm_init(void);
++
++static int htcuniversal_suspend(struct platform_device *dev, pm_message_t state)
++{
++ /* Turn off external clocks here, because htcuniversal_power and asic3_mmc
++ * scared to do so to not hurt each other. (-5 mA) */
++
++
++ /* 0x20c2 is HTC clock value
++ * CLOCK_CDEX_SOURCE 2
++ * CLOCK_CDEX_SPI 0
++ * CLOCK_CDEX_OWM 0
++ *
++ * CLOCK_CDEX_PWM0 0
++ * CLOCK_CDEX_PWM1 0
++ * CLOCK_CDEX_LED0 1
++ * CLOCK_CDEX_LED1 1
++ *
++ * CLOCK_CDEX_LED2 0
++ * CLOCK_CDEX_SD_HOST 0
++ * CLOCK_CDEX_SD_BUS 0
++ * CLOCK_CDEX_SMBUS 0
++ *
++ * CLOCK_CDEX_CONTROL_CX 0
++ * CLOCK_CDEX_EX0 1
++ * CLOCK_CDEX_EX1 0
++ * */
++ asic3_set_clock_cdex(&htcuniversal_asic3.dev, 0xffff, CLOCK_CDEX_SOURCE1
++ |CLOCK_CDEX_LED0
++ |CLOCK_CDEX_LED1
++ |CLOCK_CDEX_LED2
++ |CLOCK_CDEX_EX0
++ |CLOCK_CDEX_EX1);
++
++ *egpios = 0; /* turn off all egpio power */
++
++ /* Wake up enable. */
++ PWER = PWER_GPIO0
++ | PWER_GPIO1 /* reset */
++ | PWER_GPIO9 /* USB */
++ | PWER_GPIO10 /* AC on USB */
++ | PWER_GPIO14 /* ASIC3 mux */
++ | PWER_RTC;
++ /* Wake up on falling edge. */
++ PFER = PWER_GPIO0
++ | PWER_GPIO1
++ | PWER_GPIO9
++ | PWER_GPIO10
++ | PWER_GPIO14;
++
++ /* Wake up on rising edge. */
++ PRER = PWER_GPIO0
++ | PWER_GPIO1
++ | PWER_GPIO9
++ | PWER_GPIO10;
++ /* 3.6864 MHz oscillator power-down enable */
++ PCFR = PCFR_OPDE | PCFR_PI2CEN | PCFR_GPROD | PCFR_GPR_EN;
++
++ PGSR0 = 0x09088004;
++ PGSR1 = 0x00020002;
++ PGSR2 = 0x8001c000;
++ PGSR3 = 0x00106284;
++
++ PSLR = 0xcc000000;
++
++#if 0
++ /*
++ * If we're using bootldr and not the stock HTC bootloader,
++ * we want to wake up periodically to see if the charge is full while
++ * it is suspended. We do this with the OS timer 4 in the pxa270.
++ */
++ if (!htc_bootloader) {
++ OMCR4 = 0x4b; /* Periodic, self-resetting, 1-second timer */
++ OSMR4 = 5; /* Wake up bootldr after x seconds so it can
++ figure out what to do with the LEDs. */
++ OIER |= 0x10; /* Enable interrupt source for Timer 4 */
++ OSCR4 = 0; /* This starts the timer */
++ }
++#endif
++
++ asic3_set_extcf_select(&htcuniversal_asic3.dev, ASIC3_EXTCF_OWM_EN, 0);
++
++ return 0;
++}
++
++static int htcuniversal_resume(struct platform_device *dev)
++{
++ htcuniversal_egpio_enable(0);
++
++ return 0;
++}
++#else
++# define htcuniversal_suspend NULL
++# define htcuniversal_resume NULL
++#endif
++
++static int
++htcuniversal_core_probe( struct platform_device *dev )
++{
++
++ printk( KERN_NOTICE "HTC Universal Core Hardware Driver\n" );
++
++ egpios = (volatile u_int16_t *)ioremap_nocache(HTCUNIVERSAL_EGPIO_BASE, sizeof *egpios );
++ if (!egpios)
++ return -ENODEV;
++ else
++ printk( KERN_NOTICE "HTC Universal Core: egpio at phy=0x%8.8x is at virt=0x%p\n",
++ HTCUNIVERSAL_EGPIO_BASE, egpios );
++
++ printk("Using stock HTC first stage bootloader\n");
++ htc_bootloader = 1;
++
++// htcuniversal_ll_pm_init();
++
++ return 0;
++}
++
++static int
++htcuniversal_core_remove( struct platform_device *dev )
++{
++
++ if (egpios != NULL)
++ iounmap( (void *)egpios );
++
++ return 0;
++}
++
++static struct platform_driver htcuniversal_core_driver = {
++ .driver = {
++ .name = "htcuniversal_core",
++ },
++ .probe = htcuniversal_core_probe,
++ .remove = htcuniversal_core_remove,
++ .suspend = htcuniversal_suspend,
++ .resume = htcuniversal_resume,
++};
++
++static int __init
++htcuniversal_core_init( void )
++{
++ return platform_driver_register( &htcuniversal_core_driver );
++}
++
++
++static void __exit
++htcuniversal_core_exit( void )
++{
++ platform_driver_unregister( &htcuniversal_core_driver );
++}
++
++module_init( htcuniversal_core_init );
++module_exit( htcuniversal_core_exit );
++
++MODULE_AUTHOR("Todd Blumer, SDG Systems, LLC");
++MODULE_DESCRIPTION("HTC Universal Core Hardware Driver");
++MODULE_LICENSE("GPL");
++
++/* vim600: set noexpandtab sw=8 ts=8 :*/
+Index: linux-2.6.26-rc4/arch/arm/mach-pxa/htcuniversal/htcuniversal_lcd.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26-rc4/arch/arm/mach-pxa/htcuniversal/htcuniversal_lcd.c 2008-06-01 18:49:45.000000000 +0100
+@@ -0,0 +1,212 @@
++/*
++ * Use consistent with the GNU GPL is permitted,
++ * provided that this copyright notice is
++ * preserved in its entirety in all copies and derived works.
++ *
++ * History:
++ *
++ * 2004-03-01 Eddi De Pieri Adapted for htcuniversal using h3900_lcd.c
++ * 2004 Shawn Anderson Lcd hacking on htcuniversal
++ * see h3900_lcd.c for more history.
++ *
++ */
++
++#include <linux/types.h>
++#include <asm/arch/hardware.h> /* for pxa-regs.h (__REG) */
++#include <linux/platform_device.h>
++#include <asm/arch/pxa-regs.h> /* LCCR[0,1,2,3]* */
++#include <asm/arch/bitfield.h> /* for pxa-regs.h (Fld, etc) */
++#include <asm/arch/pxafb.h> /* pxafb_mach_info, set_pxa_fb_info */
++#include <asm/mach-types.h> /* machine_is_htcuniversal */
++#include <linux/lcd.h> /* lcd_device */
++#include <linux/err.h>
++#include <linux/delay.h>
++
++#include <asm/arch/htcuniversal-gpio.h>
++#include <asm/arch/htcuniversal-asic.h>
++#include <asm/hardware/ipaq-asic3.h>
++#include <linux/soc/asic3_base.h>
++
++static int saved_lcdpower=-1;
++
++static int powerup_lcd(void)
++{
++ printk( KERN_INFO "htcuniversal powerup_lcd: called\n");
++
++ asic3_set_gpio_out_c(&htcuniversal_asic3.dev, 1<<GPIOC_LCD_PWR1_ON, 0);
++ asic3_set_gpio_out_c(&htcuniversal_asic3.dev, 1<<GPIOC_LCD_PWR2_ON, 0);
++ asic3_set_gpio_out_b(&htcuniversal_asic3.dev, 1<<GPIOB_LCD_PWR3_ON, 0);
++ asic3_set_gpio_out_d(&htcuniversal_asic3.dev, 1<<GPIOD_LCD_PWR4_ON, 0);
++ asic3_set_gpio_out_a(&htcuniversal_asic3.dev, 1<<GPIOA_LCD_PWR5_ON, 0);
++#if 1
++ LCCR4|=LCCR4_PCDDIV;
++#endif
++ pxa_set_cken(CKEN_LCD, 0);
++
++ mdelay(100);
++ asic3_set_gpio_out_a(&htcuniversal_asic3.dev, 1<<GPIOA_LCD_PWR5_ON, 1<<GPIOA_LCD_PWR5_ON);
++ mdelay(5);
++ asic3_set_gpio_out_b(&htcuniversal_asic3.dev, 1<<GPIOB_LCD_PWR3_ON, 1<<GPIOB_LCD_PWR3_ON);
++ mdelay(2);
++ asic3_set_gpio_out_c(&htcuniversal_asic3.dev, 1<<GPIOC_LCD_PWR1_ON, 1<<GPIOC_LCD_PWR1_ON);
++ mdelay(2);
++ asic3_set_gpio_out_c(&htcuniversal_asic3.dev, 1<<GPIOC_LCD_PWR2_ON, 1<<GPIOC_LCD_PWR2_ON);
++ mdelay(20);
++ asic3_set_gpio_out_d(&htcuniversal_asic3.dev, 1<<GPIOD_LCD_PWR4_ON, 1<<GPIOD_LCD_PWR4_ON);
++ mdelay(1);
++ pxa_set_cken(CKEN_LCD, 1);
++
++ SET_HTCUNIVERSAL_GPIO(LCD1,1);
++ SET_HTCUNIVERSAL_GPIO(LCD2,1);
++ return 0;
++}
++
++static int powerdown_lcd(void)
++{
++ printk( KERN_INFO "htcuniversal powerdown_lcd: called\n");
++
++#if 1
++ asic3_set_gpio_out_c(&htcuniversal_asic3.dev, 1<<GPIOC_LCD_PWR2_ON, 0);
++ mdelay(100);
++ asic3_set_gpio_out_d(&htcuniversal_asic3.dev, 1<<GPIOD_LCD_PWR4_ON, 0);
++ mdelay(10);
++ asic3_set_gpio_out_a(&htcuniversal_asic3.dev, 1<<GPIOA_LCD_PWR5_ON, 0);
++ mdelay(1);
++ asic3_set_gpio_out_b(&htcuniversal_asic3.dev, 1<<GPIOB_LCD_PWR3_ON, 0);
++ mdelay(1);
++ asic3_set_gpio_out_c(&htcuniversal_asic3.dev, 1<<GPIOC_LCD_PWR1_ON, 0);
++ pxa_set_cken(CKEN_LCD, 0);
++
++ SET_HTCUNIVERSAL_GPIO(LCD1,0);
++ SET_HTCUNIVERSAL_GPIO(LCD2,0);
++#else
++ pxa_set_cken(CKEN_LCD, 0);
++
++ SET_HTCUNIVERSAL_GPIO(LCD1,0);
++ SET_HTCUNIVERSAL_GPIO(LCD2,0);
++
++ asic3_set_gpio_out_c(&htcuniversal_asic3.dev, 1<<GPIOC_LCD_PWR2_ON, 0);
++ mdelay(100);
++ asic3_set_gpio_out_d(&htcuniversal_asic3.dev, 1<<GPIOD_LCD_PWR4_ON, 0);
++ mdelay(10);
++ asic3_set_gpio_out_a(&htcuniversal_asic3.dev, 1<<GPIOA_LCD_PWR5_ON, 0);
++ mdelay(1);
++ asic3_set_gpio_out_b(&htcuniversal_asic3.dev, 1<<GPIOB_LCD_PWR3_ON, 0);
++ mdelay(1);
++ asic3_set_gpio_out_c(&htcuniversal_asic3.dev, 1<<GPIOC_LCD_PWR1_ON, 0);
++#endif
++ return 0;
++}
++
++static int htcuniversal_lcd_set_power(struct lcd_device *lm, int power)
++{
++ /* Enable or disable power to the LCD (0: on; 4: off) */
++
++ if ( power < 1 ) {
++
++ powerup_lcd();
++
++ } else {
++
++ powerdown_lcd();
++
++ }
++
++ saved_lcdpower=power;
++
++ return 0;
++}
++
++static int htcuniversal_lcd_get_power(struct lcd_device *lm)
++{
++ /* Get the LCD panel power status (0: full on, 1..3: controller
++ * power on, flat panel power off, 4: full off) */
++
++ if (saved_lcdpower == -1)
++ {
++ htcuniversal_lcd_set_power(lm, 4);
++ saved_lcdpower=4;
++ }
++
++ return saved_lcdpower;
++}
++
++static struct lcd_ops htcuniversal_lcd_properties =
++{
++ .get_power = htcuniversal_lcd_get_power,
++ .set_power = htcuniversal_lcd_set_power,
++};
++
++static struct lcd_device *htcuniversal_lcd_dev;
++
++static int htcuniversal_lcd_probe(struct platform_device * dev)
++{
++ htcuniversal_lcd_dev = lcd_device_register("pxa2xx-fb", &dev->dev, NULL,
++ &htcuniversal_lcd_properties);
++ if (IS_ERR(htcuniversal_lcd_dev)) {
++ printk("htcuniversal_lcd_probe: error registering devices\n");
++ return -1;
++ }
++
++ return 0;
++}
++
++static int htcuniversal_lcd_remove(struct platform_device * dev)
++{
++ htcuniversal_lcd_set_power(htcuniversal_lcd_dev, 4);
++ lcd_device_unregister(htcuniversal_lcd_dev);
++
++ return 0;
++}
++
++static int htcuniversal_lcd_suspend(struct platform_device * dev, pm_message_t state)
++{
++// printk("htcuniversal_lcd_suspend: called.\n");
++ htcuniversal_lcd_set_power(htcuniversal_lcd_dev, 4);
++ return 0;
++}
++
++static int htcuniversal_lcd_resume(struct platform_device * dev)
++{
++// printk("htcuniversal_lcd_resume: called.\n");
++
++ /* */
++#if 1
++ LCCR4|=LCCR4_PCDDIV;
++#endif
++
++ htcuniversal_lcd_set_power(htcuniversal_lcd_dev, 0);
++ return 0;
++}
++
++static struct platform_driver htcuniversal_lcd_driver = {
++ .driver = {
++ .name = "htcuniversal_lcd",
++ },
++ .probe = htcuniversal_lcd_probe,
++ .remove = htcuniversal_lcd_remove,
++ .suspend = htcuniversal_lcd_suspend,
++ .resume = htcuniversal_lcd_resume,
++};
++
++static int htcuniversal_lcd_init(void)
++{
++ if (!machine_is_htcuniversal())
++ return -ENODEV;
++
++ return platform_driver_register(&htcuniversal_lcd_driver);
++}
++
++static void htcuniversal_lcd_exit(void)
++{
++ lcd_device_unregister(htcuniversal_lcd_dev);
++ platform_driver_unregister(&htcuniversal_lcd_driver);
++}
++
++module_init(htcuniversal_lcd_init);
++module_exit(htcuniversal_lcd_exit);
++
++MODULE_AUTHOR("xanadux.org");
++MODULE_DESCRIPTION("Framebuffer driver for HTC Universal");
++MODULE_LICENSE("GPL");
++
+Index: linux-2.6.26-rc4/arch/arm/mach-pxa/htcuniversal/htcuniversal_phone.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26-rc4/arch/arm/mach-pxa/htcuniversal/htcuniversal_phone.c 2008-06-01 18:49:45.000000000 +0100
+@@ -0,0 +1,167 @@
++
++/* Phone interface driver for Qualcomm MSM6250 on HTC Universal
++ *
++ * Copyright (c) 2005 SDG Systems, LLC
++ *
++ * 2005-04-21 Todd Blumer Created.
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/delay.h>
++#include <linux/platform_device.h>
++#include <linux/soc/asic3_base.h>
++
++#include <asm/hardware.h>
++#include <asm/arch/serial.h>
++#include <asm/hardware/ipaq-asic3.h>
++#include <asm/arch/htcuniversal-gpio.h>
++#include <asm/arch/htcuniversal-asic.h>
++
++#include "htcuniversal_phone.h"
++
++static void phone_reset(void)
++{
++ asic3_set_gpio_out_b(&htcuniversal_asic3.dev, 1<<GPIOB_BB_RESET2, 0);
++
++ SET_HTCUNIVERSAL_GPIO(PHONE_RESET,0);
++ asic3_set_gpio_out_d(&htcuniversal_asic3.dev, 1<<GPIOD_BB_RESET1, 0);
++ mdelay(1);
++ asic3_set_gpio_out_d(&htcuniversal_asic3.dev, 1<<GPIOD_BB_RESET1, 1<<GPIOD_BB_RESET1);
++ mdelay(20);
++ SET_HTCUNIVERSAL_GPIO(PHONE_RESET,1);
++ mdelay(200);
++ asic3_set_gpio_out_d(&htcuniversal_asic3.dev, 1<<GPIOD_BB_RESET1, 0);
++}
++
++static void phone_off(void)
++{
++ asic3_set_gpio_out_d(&htcuniversal_asic3.dev, 1<<GPIOD_BB_RESET1, 1<<GPIOD_BB_RESET1);
++ mdelay(2000);
++ asic3_set_gpio_out_d(&htcuniversal_asic3.dev, 1<<GPIOD_BB_RESET1, 0);
++
++ asic3_set_gpio_out_b(&htcuniversal_asic3.dev, 1<<GPIOB_BB_RESET2, 1<<GPIOB_BB_RESET2);
++ SET_HTCUNIVERSAL_GPIO(PHONE_OFF,0);
++}
++
++static void
++htcuniversal_phone_configure( int state )
++{
++ int tries;
++ unsigned short statusb;
++
++ printk( KERN_NOTICE "htcuniversal configure phone: %d\n", state );
++ switch (state) {
++
++ case PXA_UART_CFG_PRE_STARTUP:
++ break;
++
++ case PXA_UART_CFG_POST_STARTUP:
++ /* pre-serial-up hardware configuration */
++
++ SET_HTCUNIVERSAL_GPIO(PHONE_START,0); /* "bootloader" */
++ SET_HTCUNIVERSAL_GPIO(PHONE_UNKNOWN,0); /* not used */
++ SET_HTCUNIVERSAL_GPIO(PHONE_OFF,0); /* PHONE_OFF */
++
++ phone_reset();
++
++ SET_HTCUNIVERSAL_GPIO(PHONE_START,1); /* phone */
++
++ phone_reset();
++
++ asic3_set_gpio_dir_b(&htcuniversal_asic3.dev, 1<<GPIOB_BB_READY, 0);
++ asic3_set_gpio_dir_b(&htcuniversal_asic3.dev, 1<<GPIOB_BB_UNKNOWN3, 0);
++
++ /*
++ */
++ tries = 0;
++ do {
++ mdelay(10);
++ statusb = asic3_get_gpio_status_b( &htcuniversal_asic3.dev );
++ } while ( (statusb & (1<<GPIOB_UMTS_DCD)) == 0 && tries++ < 200);
++
++ printk("UMTS_DCD tries=%d of 200\n",tries);
++
++ tries = 0;
++ do {
++ SET_HTCUNIVERSAL_GPIO(PHONE_OFF,1);
++ mdelay(10);
++ SET_HTCUNIVERSAL_GPIO(PHONE_OFF,0);
++ mdelay(20);
++ statusb = asic3_get_gpio_status_b( &htcuniversal_asic3.dev );
++ } while ( (statusb & (1<<GPIOB_BB_READY)) == 0 && tries++ < 200);
++
++ printk("BB_READY tries=%d of 200\n",tries);
++
++ break;
++
++ case PXA_UART_CFG_PRE_SHUTDOWN:
++
++ phone_off();
++
++ break;
++
++ default:
++ break;
++ }
++}
++
++
++static int
++htcuniversal_phone_probe( struct platform_device *dev )
++{
++ struct htcuniversal_phone_funcs *funcs = dev->dev.platform_data;
++
++ /* configure phone UART */
++ pxa_gpio_mode( GPIO_NR_HTCUNIVERSAL_PHONE_RXD_MD );
++ pxa_gpio_mode( GPIO_NR_HTCUNIVERSAL_PHONE_TXD_MD );
++ pxa_gpio_mode( GPIO_NR_HTCUNIVERSAL_PHONE_UART_CTS_MD );
++ pxa_gpio_mode( GPIO_NR_HTCUNIVERSAL_PHONE_UART_RTS_MD );
++
++ funcs->configure = htcuniversal_phone_configure;
++
++ return 0;
++}
++
++static int
++htcuniversal_phone_remove( struct platform_device *dev )
++{
++ struct htcuniversal_phone_funcs *funcs = dev->dev.platform_data;
++
++ funcs->configure = NULL;
++
++ asic3_set_gpio_dir_b(&htcuniversal_asic3.dev, 1<<GPIOB_BB_READY, 1<<GPIOB_BB_READY);
++ asic3_set_gpio_dir_b(&htcuniversal_asic3.dev, 1<<GPIOB_BB_UNKNOWN3, 1<<GPIOB_BB_UNKNOWN3);
++
++ return 0;
++}
++
++static struct platform_driver phone_driver = {
++ .driver = {
++ .name = "htcuniversal_phone",
++ },
++ .probe = htcuniversal_phone_probe,
++ .remove = htcuniversal_phone_remove,
++};
++
++static int __init
++htcuniversal_phone_init( void )
++{
++ printk(KERN_NOTICE "htcuniversal Phone Driver\n");
++ return platform_driver_register( &phone_driver );
++}
++
++static void __exit
++htcuniversal_phone_exit( void )
++{
++ platform_driver_unregister( &phone_driver );
++}
++
++module_init( htcuniversal_phone_init );
++module_exit( htcuniversal_phone_exit );
++
++MODULE_AUTHOR("Todd Blumer, SDG Systems, LLC");
++MODULE_DESCRIPTION("HTC Universal Phone Support Driver");
++MODULE_LICENSE("GPL");
++
++/* vim600: set noexpandtab sw=8 ts=8 :*/
+Index: linux-2.6.26-rc4/arch/arm/mach-pxa/htcuniversal/htcuniversal_phone.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26-rc4/arch/arm/mach-pxa/htcuniversal/htcuniversal_phone.h 2008-06-01 18:49:45.000000000 +0100
+@@ -0,0 +1,16 @@
++/*
++ * Bluetooth support file for calling bluetooth configuration functions
++ *
++ * Copyright (c) 2005 SDG Systems, LLC
++ *
++ * 2005-06 Todd Blumer Initial Revision
++ */
++
++#ifndef _HTCUNIVERSAL_PHONE_H
++#define _HTCUNIVERSAL_PHONE_H
++
++struct htcuniversal_phone_funcs {
++ void (*configure) ( int state );
++};
++
++#endif
+Index: linux-2.6.26-rc4/arch/arm/mach-pxa/htcuniversal/htcuniversal_pm.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26-rc4/arch/arm/mach-pxa/htcuniversal/htcuniversal_pm.c 2008-06-01 18:49:45.000000000 +0100
+@@ -0,0 +1,69 @@
++/*
++ * MyPal 716 power management support for the original HTC IPL in DoC G3
++ *
++ * Use consistent with the GNU GPL is permitted, provided that this
++ * copyright notice is preserved in its entirety in all copies and
++ * derived works.
++ *
++ * Copyright (C) 2005 Pawel Kolodziejski
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/device.h>
++#include <linux/pm.h>
++
++#include <asm/mach-types.h>
++#include <asm/hardware.h>
++#include <asm/arch/pxa-regs.h>
++#include <asm/arch/pxa-pm_ll.h>
++
++#ifdef CONFIG_PM
++
++static u32 *addr_a0040000;
++static u32 *addr_a0040004;
++static u32 *addr_a0040008;
++static u32 *addr_a004000c;
++
++static u32 save_a0040000;
++static u32 save_a0040004;
++static u32 save_a0040008;
++static u32 save_a004000c;
++
++static void htcuniversal_pxa_ll_pm_suspend(unsigned long resume_addr)
++{
++ save_a0040000 = *addr_a0040000;
++ save_a0040004 = *addr_a0040004;
++ save_a0040008 = *addr_a0040008;
++ save_a004000c = *addr_a004000c;
++
++ /* jump to PSPR */
++ *addr_a0040000 = 0xe3a00101; // mov r0, #0x40000000
++ *addr_a0040004 = 0xe380060f; // orr r0, r0, #0x0f000000
++ *addr_a0040008 = 0xe3800008; // orr r0, r0, #8
++ *addr_a004000c = 0xe590f000; // ldr pc, [r0]
++}
++
++static void htcuniversal_pxa_ll_pm_resume(void)
++{
++ *addr_a0040000 = save_a0040000;
++ *addr_a0040004 = save_a0040004;
++ *addr_a0040008 = save_a0040008;
++ *addr_a004000c = save_a004000c;
++}
++
++static struct pxa_ll_pm_ops htcuniversal_ll_pm_ops = {
++ .suspend = htcuniversal_pxa_ll_pm_suspend,
++ .resume = htcuniversal_pxa_ll_pm_resume,
++};
++
++void htcuniversal_ll_pm_init(void) {
++ addr_a0040000 = phys_to_virt(0xa0040000);
++ addr_a0040004 = phys_to_virt(0xa0040004);
++ addr_a0040008 = phys_to_virt(0xa0040008);
++ addr_a004000c = phys_to_virt(0xa004000c);
++
++ pxa_pm_set_ll_ops(&htcuniversal_ll_pm_ops);
++}
++#endif /* CONFIG_PM */
+Index: linux-2.6.26-rc4/arch/arm/mach-pxa/htcuniversal/htcuniversal_power2.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26-rc4/arch/arm/mach-pxa/htcuniversal/htcuniversal_power2.c 2008-06-01 18:49:45.000000000 +0100
+@@ -0,0 +1,97 @@
++/*
++ * pda_power driver for HTC Universal
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or (at
++ * your option) any later version.
++ *
++ */
++
++#include <linux/platform_device.h>
++#include <linux/module.h>
++#include <linux/pda_power.h>
++#include <linux/soc/asic3_base.h>
++
++#include <asm/mach-types.h>
++#include <asm/hardware.h>
++#include <asm/arch/htcuniversal-gpio.h>
++#include <asm/arch/htcuniversal-asic.h>
++
++static void charge_on(int flags)
++{
++ asic3_set_gpio_out_b(&htcuniversal_asic3.dev, 1<<GPIOB_CHARGE_EN, 0);
++}
++
++static int ac_on(void)
++{
++ return (GET_HTCUNIVERSAL_GPIO(POWER_DET) == 0);
++}
++
++static int usb_on(void)
++{
++ return (GET_HTCUNIVERSAL_GPIO(USB_DET) == 0);
++}
++
++static char *supplicants[] = {
++ "ds2760-battery.0", "backup-battery"
++};
++
++static struct pda_power_pdata power_pdata = {
++ .is_ac_online = ac_on,
++ .is_usb_online = usb_on,
++ .set_charge = charge_on,
++ .supplied_to = supplicants,
++ .num_supplicants = ARRAY_SIZE(supplicants),
++};
++
++static struct resource power_resources[] = {
++ [0] = {
++ .name = "ac",
++ .start = HTCUNIVERSAL_IRQ(POWER_DET),
++ .end = HTCUNIVERSAL_IRQ(POWER_DET),
++ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_LOWEDGE,
++ },
++ [1] = {
++ .name = "usb",
++ .start = HTCUNIVERSAL_IRQ(USB_DET),
++ .end = HTCUNIVERSAL_IRQ(USB_DET),
++ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_LOWEDGE,
++ },
++};
++
++static void dev_release(struct device *dev)
++{
++ return;
++}
++
++static struct platform_device power_dev =
++{
++ .name = "pda-power",
++ .id = -1,
++ .resource = power_resources,
++ .num_resources = ARRAY_SIZE(power_resources),
++ .dev =
++ {
++ .platform_data = &power_pdata,
++ .release = dev_release,
++ },
++};
++
++static int htcuniversal_power_init(void)
++{
++ return platform_device_register(&power_dev);
++}
++
++static void htcuniversal_power_exit(void)
++{
++ platform_device_unregister(&power_dev);
++
++ return;
++}
++
++module_init(htcuniversal_power_init);
++module_exit(htcuniversal_power_exit);
++
++MODULE_DESCRIPTION("Power driver for HTC Universal");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.26-rc4/arch/arm/mach-pxa/htcuniversal/htcuniversal_ts2.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26-rc4/arch/arm/mach-pxa/htcuniversal/htcuniversal_ts2.c 2008-06-01 18:49:45.000000000 +0100
+@@ -0,0 +1,490 @@
++/* Touch screen driver for the TI something-or-other
++ *
++ * Copyright © 2005 SDG Systems, LLC
++ *
++ * Based on code that was based on the SAMCOP driver.
++ * Copyright © 2003, 2004 Compaq Computer Corporation.
++ *
++ * Use consistent with the GNU GPL is permitted,
++ * provided that this copyright notice is
++ * preserved in its entirety in all copies and derived works.
++ *
++ * COMPAQ COMPUTER CORPORATION MAKES NO WARRANTIES, EXPRESSED OR IMPLIED,
++ * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS
++ * FITNESS FOR ANY PARTICULAR PURPOSE.
++ *
++ * Author: Keith Packard <keith.packard@hp.com>
++ * May 2003
++ *
++ * Updates:
++ *
++ * 2004-02-11 Michael Opdenacker Renamed names from samcop to shamcop,
++ * Goal:support HAMCOP and SAMCOP.
++ * 2004-02-14 Michael Opdenacker Temporary fix for device id handling
++ *
++ * 2005-02-18 Aric Blumer Converted basic structure to support hx4700
++ *
++ * 2005-06-07 Aric Blumer Added tssim device handling so we can
++ * hook in the fbvncserver.
++ */
++
++#include <linux/module.h>
++#include <linux/version.h>
++
++#include <linux/init.h>
++#include <linux/fs.h>
++#include <linux/cdev.h>
++#include <linux/interrupt.h>
++#include <linux/sched.h>
++#include <linux/pm.h>
++#include <linux/delay.h>
++#include <linux/input.h>
++#include <linux/platform_device.h>
++#include <linux/irq.h>
++
++#include <asm/arch/hardware.h>
++#include <asm/mach/irq.h>
++#include <asm/io.h>
++
++/* remove me */
++#include <asm/arch/pxa-regs.h>
++#include <asm/arch/htcuniversal-gpio.h>
++#include <asm/arch/htcuniversal-asic.h>
++#include <asm/mach-types.h>
++
++#include <asm/hardware/ipaq-asic3.h>
++#include <linux/soc/asic3_base.h>
++
++
++#include "tsc2046_ts.h"
++
++enum touchscreen_state {
++ STATE_WAIT_FOR_TOUCH, /* Waiting for a PEN interrupt */
++ STATE_SAMPLING /* Actively sampling ADC */
++};
++
++struct touchscreen_data {
++ enum touchscreen_state state;
++ struct timer_list timer;
++ int irq;
++ struct input_dev *input;
++ /* */
++ int port;
++ int clock;
++ int pwrbit_X;
++ int pwrbit_Y;
++ int (*pen_down)(void);
++};
++
++static unsigned long poll_sample_time = 10; /* Sample every 10 milliseconds */
++
++static struct touchscreen_data *ts_data;
++
++static int irqblock;
++
++module_param(poll_sample_time, ulong, 0644);
++MODULE_PARM_DESC(poll_sample_time, "Poll sample time");
++
++static inline void
++report_touchpanel(struct touchscreen_data *ts, int pressure, int x, int y)
++{
++ input_report_abs(ts->input, ABS_PRESSURE, pressure);
++ input_report_abs(ts->input, ABS_X, x);
++ input_report_abs(ts->input, ABS_Y, y);
++ input_sync(ts->input);
++}
++
++static void start_read(struct touchscreen_data *touch);
++
++static irqreturn_t
++pen_isr(int irq, void *irq_desc)
++{
++ struct touchscreen_data *ts = ts_data;
++
++ if(irq == ts->irq /* && !irqblock */) {
++ irqblock = 1;
++
++ /*
++ * Disable the pen interrupt. It's reenabled when the user lifts the
++ * pen.
++ */
++ disable_irq(ts->irq);
++
++ if (ts->state == STATE_WAIT_FOR_TOUCH) {
++ ts->state = STATE_SAMPLING;
++ start_read(ts);
++ } else {
++ /* Shouldn't happen */
++ printk(KERN_ERR "Unexpected ts interrupt\n");
++ }
++
++ }
++ return IRQ_HANDLED;
++}
++
++static void
++ssp_init(int port, int clock)
++{
++
++ pxa_set_cken(clock, 0);
++
++ pxa_gpio_mode(GPIO_NR_HTCUNIVERSAL_TOUCHSCREEN_SPI_CLK_MD);
++ pxa_gpio_mode(GPIO_NR_HTCUNIVERSAL_TOUCHSCREEN_SPI_FRM_MD);
++ pxa_gpio_mode(GPIO_NR_HTCUNIVERSAL_TOUCHSCREEN_SPI_DO_MD);
++ pxa_gpio_mode(GPIO_NR_HTCUNIVERSAL_TOUCHSCREEN_SPI_DI_MD);
++
++ SET_HTCUNIVERSAL_GPIO(SPI_FRM,1);
++
++ /* *** Set up the SPI Registers *** */
++ SSCR0_P(port) =
++ SSCR0_EDSS /* Extended Data Size Select */
++ | SSCR0_SerClkDiv(7) /* Serial Clock Rate */
++ /* Synchronous Serial Enable (Disable for now) */
++ | SSCR0_Motorola /* Motorola SPI Interface */
++ | SSCR0_DataSize(8) /* Data Size Select (24-bit) */
++ ;
++ SSCR1_P(port) = 0;
++ SSPSP_P(port) = 0;
++
++ /* Clear the Status */
++ SSSR_P(port) = SSSR_P(port) & 0x00fcfffc;
++
++ /* Now enable it */
++ SSCR0_P(port) =
++ SSCR0_EDSS /* Extended Data Size Select */
++ | SSCR0_SerClkDiv(7) /* Serial Clock Rate */
++ | SSCR0_SSE /* Synchronous Serial Enable */
++ | SSCR0_Motorola /* Motorola SPI Interface */
++ | SSCR0_DataSize(8) /* Data Size Select (24-bit) */
++ ;
++
++ pxa_set_cken(clock, 1);
++}
++
++static void
++start_read(struct touchscreen_data *touch)
++{
++ unsigned long inc = (poll_sample_time * HZ) / 1000;
++ int i;
++
++ /* Write here to the serial port. We request X and Y only for now.
++ * Then we have to wait for poll_sample_time before we read out the serial
++ * port. Then, when we read it out, we check to see if the pen is still
++ * down. If so, then we issue another request here.
++ */
++#define TS_SAMPLES 7
++
++ /*
++ * We do four samples for each, and throw out the highest and lowest, then
++ * average the other two.
++ */
++
++ for(i = 0; i < TS_SAMPLES; i++) {
++ while(!(SSSR_P(touch->port) & SSSR_TNF))
++ ;
++ /* It's not full. Write the command for X */
++ SSDR_P(touch->port) = (TSC2046_SAMPLE_X|(touch->pwrbit_X))<<16;
++ }
++
++ for(i = 0; i < TS_SAMPLES; i++) {
++ while(!(SSSR_P(touch->port) & SSSR_TNF))
++ ;
++ /* It's not full. Write the command for Y */
++ SSDR_P(touch->port) = (TSC2046_SAMPLE_Y|(touch->pwrbit_Y))<<16;
++ }
++
++ /*
++ * Enable the timer. We should get an interrupt, but we want keep a timer
++ * to ensure that we can detect missing data
++ */
++ mod_timer(&touch->timer, jiffies + inc);
++}
++
++static void
++ts_timer_callback(unsigned long data)
++{
++ struct touchscreen_data *ts = (struct touchscreen_data *)data;
++ int x, a[TS_SAMPLES], y;
++ static int oldx, oldy;
++ int ssrval;
++
++ /*
++ * Check here to see if there is anything in the SPI FIFO. If so,
++ * return it if there has been a change. If not, then we have a
++ * timeout. Generate an erro somehow.
++ */
++ ssrval = SSSR_P(ts->port);
++
++ if(ssrval & SSSR_RNE) { /* Look at Rx Not Empty bit */
++ int number_of_entries_in_fifo;
++
++ /* The FIFO is not emtpy. Good! Now make sure there are at least two
++ * entries. (Should be two exactly.) */
++
++ number_of_entries_in_fifo = ((ssrval >> 12) & 0xf) + 1;
++
++ if(number_of_entries_in_fifo < TS_SAMPLES * 2) {
++ /* Not ready yet. Come back later. */
++ unsigned long inc = (poll_sample_time * HZ) / 1000;
++ mod_timer(&ts->timer, jiffies + inc);
++ return;
++ }
++
++ if(number_of_entries_in_fifo == TS_SAMPLES * 2) {
++ int i, j;
++
++ for(i = 0; i < TS_SAMPLES; i++) {
++ a[i] = SSDR_P(ts->port);
++ }
++ /* Sort them (bubble) */
++ for(j = TS_SAMPLES - 1; j > 0; j--) {
++ for(i = 0; i < j; i++) {
++ if(a[i] > a[i + 1]) {
++ int tmp;
++ tmp = a[i+1];
++ a[i+1] = a[i];
++ a[i] = tmp;
++ }
++ }
++ }
++
++ /* Take the average of the middle two */
++ /* x = (a[TS_SAMPLES/2 - 1] + a[TS_SAMPLES/2] + a[TS_SAMPLES/2+1] + a[TS_SAMPLES/2+2]) >> 2; */
++ x = a[TS_SAMPLES/2];
++
++ for(i = 0; i < TS_SAMPLES; i++) {
++ a[i] = SSDR_P(ts->port);
++ }
++ /* Sort them (bubble) */
++ for(j = TS_SAMPLES - 1; j > 0; j--) {
++ for(i = 0; i < j; i++) {
++ if(a[i] > a[i + 1]) {
++ int tmp;
++ tmp = a[i+1];
++ a[i+1] = a[i];
++ a[i] = tmp;
++ }
++ }
++ }
++
++
++ /* Take the average of the middle two */
++ /* y = (a[TS_SAMPLES/2 - 1] + a[TS_SAMPLES/2] + a[TS_SAMPLES/2+1] + a[TS_SAMPLES/2+2]) >> 2; */
++ y = a[TS_SAMPLES/2];
++ } else {
++ /* We have an error! Too many entries. */
++ printk(KERN_ERR "TS: Expected %d entries. Got %d\n", TS_SAMPLES*2, number_of_entries_in_fifo);
++ /* Try to clear the FIFO */
++ while(number_of_entries_in_fifo--) {
++ (void)SSDR_P(ts->port);
++ }
++
++ if (ts->pen_down())
++ start_read(ts);
++
++ return;
++ }
++ } else {
++ /* Not ready yet. Come back later. */
++ unsigned long inc = (poll_sample_time * HZ) / 1000;
++ mod_timer(&ts->timer, jiffies + inc);
++ return;
++ }
++
++ /*
++ * Now we check to see if the pen is still down. If it is, then call
++ * start_read().
++ */
++ if (ts->pen_down())
++ {
++ /* Still down */
++ if(oldx != x || oldy != y) {
++ oldx = x;
++ oldy = y;
++ report_touchpanel(ts, 1, x, y);
++ }
++ start_read(ts);
++ } else {
++ /* Up */
++ report_touchpanel(ts, 0, 0, 0);
++ irqblock = 0;
++ ts->state = STATE_WAIT_FOR_TOUCH;
++ /* Re-enable pen down interrupt */
++ enable_irq(ts->irq);
++ }
++}
++
++static int pen_down(void)
++{
++ return ( asic3_get_gpio_status_a( &htcuniversal_asic3.dev ) & (1<<GPIOA_TOUCHSCREEN_N)) == 0 ;
++}
++
++static int
++ts_probe (struct platform_device *dev)
++{
++ int retval;
++ struct touchscreen_data *ts;
++ struct tsc2046_mach_info *mach = dev->dev.platform_data;
++
++ printk("htcuniversal: ts_probe\n");
++
++ ts = ts_data = kmalloc (sizeof (*ts), GFP_KERNEL);
++ if (ts == NULL) {
++ printk( KERN_NOTICE "htcuniversal_ts: unable to allocate memory\n" );
++ return -ENOMEM;
++ }
++ memset (ts, 0, sizeof (*ts));
++
++ ts->input = input_allocate_device();
++ if (ts->input == NULL) {
++ printk( KERN_NOTICE "htcuniversal_ts: unable to allocation touchscreen input\n" );
++ kfree(ts);
++ return -ENOMEM;
++ }
++ ts->input->evbit[0] = BIT(EV_ABS);
++ ts->input->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE);
++ ts->input->absmin[ABS_X] = 0;
++ ts->input->absmax[ABS_X] = 32767;
++ ts->input->absmin[ABS_Y] = 0;
++ ts->input->absmax[ABS_Y] = 32767;
++ ts->input->absmin[ABS_PRESSURE] = 0;
++ ts->input->absmax[ABS_PRESSURE] = 1;
++
++ ts->input->name = "htcuniversal_ts";
++ ts->input->phys = "touchscreen/htcuniversal_ts";
++ ts->input->private = ts;
++
++ input_register_device(ts->input);
++
++ ts->timer.function = ts_timer_callback;
++ ts->timer.data = (unsigned long)ts;
++ ts->state = STATE_WAIT_FOR_TOUCH;
++ init_timer (&ts->timer);
++
++ platform_set_drvdata(dev, ts);
++
++ ts->port=-1;
++
++ if (mach) {
++ ts->port = mach->port;
++ ts->clock = mach->clock;
++ ts->pwrbit_X = mach->pwrbit_X;
++ ts->pwrbit_Y = mach->pwrbit_Y;
++
++ /* static irq */
++ if (mach->irq)
++ ts->irq = mach->irq;
++
++ if (mach->pen_down)
++ ts->pen_down=mach->pen_down;
++ }
++
++ if (ts->port == -1)
++ {
++ printk("tsc2046: your device is not supported by this driver\n");
++ return -ENODEV;
++ }
++
++ /* *** Initialize the SSP interface *** */
++ ssp_init(ts->port, ts->clock);
++
++ while(!(SSSR_P(ts->port) & SSSR_TNF))
++ ;
++ SSDR_P(ts->port) = (TSC2046_SAMPLE_X|(ts->pwrbit_X))<<16;
++
++ for(retval = 0; retval < 100; retval++) {
++ if(SSSR_P(ts->port) & SSSR_RNE) {
++ while(SSSR_P(ts->port) & SSSR_RNE) {
++ (void)SSDR_P(ts->port);
++ }
++ break;
++ }
++ mdelay(1);
++ }
++
++ if (machine_is_htcuniversal() )
++ {
++ ts->irq = asic3_irq_base( &htcuniversal_asic3.dev ) + ASIC3_GPIOA_IRQ_BASE + GPIOA_TOUCHSCREEN_N;
++ ts->pen_down=pen_down;
++ }
++
++ retval = request_irq(ts->irq, pen_isr, IRQF_DISABLED, "tsc2046_ts", ts);
++ if(retval) {
++ printk("Unable to get interrupt\n");
++ input_unregister_device (ts->input);
++ return -ENODEV;
++ }
++ set_irq_type(ts->irq, IRQ_TYPE_EDGE_FALLING);
++
++ return 0;
++}
++
++static int
++ts_remove (struct platform_device *dev)
++{
++ struct touchscreen_data *ts = platform_get_drvdata(dev);
++
++ input_unregister_device (ts->input);
++ del_timer_sync (&ts->timer);
++ free_irq (ts->irq, ts);
++ pxa_set_cken(ts->clock, 0);
++
++ kfree(ts);
++ return 0;
++}
++
++static int
++ts_suspend (struct platform_device *dev, pm_message_t state)
++{
++ struct touchscreen_data *ts = platform_get_drvdata(dev);
++
++ disable_irq(ts->irq);
++
++ printk("htcuniversal_ts2_suspend: called.\n");
++ return 0;
++}
++
++static int
++ts_resume (struct platform_device *dev)
++{
++ struct touchscreen_data *ts = platform_get_drvdata(dev);
++
++ ts->state = STATE_WAIT_FOR_TOUCH;
++ ssp_init(ts->port, ts->clock);
++ enable_irq(ts->irq);
++
++ printk("htcuniversal_ts2_resume: called.\n");
++ return 0;
++}
++
++static struct platform_driver ts_driver = {
++ .probe = ts_probe,
++ .remove = ts_remove,
++ .suspend = ts_suspend,
++ .resume = ts_resume,
++ .driver = {
++ .name = "htcuniversal_ts",
++ },
++};
++
++
++static int
++ts_module_init (void)
++{
++ printk(KERN_NOTICE "HTC Universal Touch Screen Driver\n");
++
++ return platform_driver_register(&ts_driver);
++}
++
++static void
++ts_module_cleanup (void)
++{
++ platform_driver_unregister (&ts_driver);
++}
++
++module_init(ts_module_init);
++module_exit(ts_module_cleanup);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Aric Blumer, SDG Systems, LLC");
++MODULE_DESCRIPTION("HTC Universal Touch Screen Driver");
+Index: linux-2.6.26-rc4/arch/arm/mach-pxa/htcuniversal/htcuniversal_udc.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26-rc4/arch/arm/mach-pxa/htcuniversal/htcuniversal_udc.c 2008-06-01 18:49:45.000000000 +0100
+@@ -0,0 +1,71 @@
++
++/*
++ *
++ * htcuniversal_udc.c:
++ * htcuniversal specific code for the pxa27x usb device controller.
++ *
++ * Use consistent with the GNU GPL is permitted.
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/platform_device.h>
++#include <asm/arch/hardware.h>
++#include <asm/arch/pxa-regs.h>
++#include <asm/arch/udc.h>
++#include <linux/soc/asic3_base.h>
++#include <asm/arch/htcuniversal-gpio.h>
++#include <asm/arch/htcuniversal-asic.h>
++
++static void htcuniversal_udc_command(int cmd)
++{
++ switch (cmd) {
++ case PXA2XX_UDC_CMD_DISCONNECT:
++ asic3_set_gpio_out_b(&htcuniversal_asic3.dev,
++ 1<<GPIOB_USB_PUEN, 0);
++// SET_HTCUNIVERSAL_GPIO(USB_PUEN,0);
++ break;
++ case PXA2XX_UDC_CMD_CONNECT:
++ asic3_set_gpio_out_b(&htcuniversal_asic3.dev,
++ 1<<GPIOB_USB_PUEN, 1<<GPIOB_USB_PUEN);
++// SET_HTCUNIVERSAL_GPIO(USB_PUEN,1);
++ break;
++ default:
++ printk("_udc_control: unknown command!\n");
++ break;
++ }
++}
++
++static int htcuniversal_udc_is_connected(void)
++{
++ return (GET_HTCUNIVERSAL_GPIO(USB_DET) != 0);
++}
++
++static struct pxa2xx_udc_mach_info htcuniversal_udc_info __initdata = {
++ .udc_is_connected = htcuniversal_udc_is_connected,
++ .udc_command = htcuniversal_udc_command,
++};
++
++static int htcuniversal_udc_probe(struct platform_device * dev)
++{
++ asic3_set_gpio_dir_b(&htcuniversal_asic3.dev, 1<<GPIOB_USB_PUEN, 1<<GPIOB_USB_PUEN);
++
++ pxa_set_udc_info(&htcuniversal_udc_info);
++ return 0;
++}
++
++static struct platform_driver htcuniversal_udc_driver = {
++ .driver = {
++ .name = "htcuniversal_udc",
++ },
++ .probe = htcuniversal_udc_probe,
++};
++
++static int __init htcuniversal_udc_init(void)
++{
++ return platform_driver_register(&htcuniversal_udc_driver);
++}
++
++module_init(htcuniversal_udc_init);
++MODULE_LICENSE("GPL");
+Index: linux-2.6.26-rc4/arch/arm/mach-pxa/htcuniversal/tsc2046_ts.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26-rc4/arch/arm/mach-pxa/htcuniversal/tsc2046_ts.h 2008-06-01 18:49:45.000000000 +0100
+@@ -0,0 +1,20 @@
++/*
++ * temporary TSC2046 touchscreen hack
++ */
++
++#ifndef _TSC2046_TS_H
++#define _TSC2046_TS_H
++
++struct tsc2046_mach_info {
++ int port;
++ int clock;
++ int pwrbit_X;
++ int pwrbit_Y;
++ int irq;
++ int (*pen_down)(void);
++};
++
++#define TSC2046_SAMPLE_X 0xd0
++#define TSC2046_SAMPLE_Y 0x90
++
++#endif
+Index: linux-2.6.26-rc4/arch/arm/mach-pxa/Kconfig
+===================================================================
+--- linux-2.6.26-rc4.orig/arch/arm/mach-pxa/Kconfig 2008-06-01 18:49:43.000000000 +0100
++++ linux-2.6.26-rc4/arch/arm/mach-pxa/Kconfig 2008-06-01 18:49:45.000000000 +0100
+@@ -147,6 +147,14 @@
+ select PXA27x
+ select IWMMXT
+
++config MACH_HTCUNIVERSAL
++ bool "HTC Universal"
++ select PXA27x
++ help
++ Say Y here if you intend to run this kernel on a
++ HTC Universal. Currently there is only basic support
++ for this PDA.
++
+ endchoice
+
+ choice
+@@ -204,6 +212,86 @@
+
+ endif
+
++if MACH_HTCUNIVERSAL
++
++menu "HTC Universal support"
++
++config HTCUNIVERSAL_CORE
++ tristate "HTC Universal core"
++ depends on MACH_HTCUNIVERSAL
++ help
++ This selection enables HTC Universal core support.
++
++config HTCUNIVERSAL_UDC
++ bool "USB Device Controller support"
++ depends on MACH_HTCUNIVERSAL && HTC_ASIC3 && USB_PXA27X
++ help
++ Enables HTC Universal specific USB detection
++
++config HTCUNIVERSAL_POWER
++ tristate "HTC Universal power"
++ depends on MACH_HTCUNIVERSAL && HTC_ASIC3
++ help
++ This selection enables HTC Universal power monitoring
++ hardware support (through ASIC3).
++
++config HTCUNIVERSAL_BACKLIGHT
++ bool "HTC Universal Backlight"
++ depends on MACH_HTCUNIVERSAL && HTC_ASIC3 && BACKLIGHT_CLASS_DEVICE
++ help
++ This driver provides support for changing power and brightness
++ on HTC Universal LCD backlight.
++
++config HTCUNIVERSAL_LCD
++ tristate "HTC Universal LCD"
++ depends on MACH_HTCUNIVERSAL && HTC_ASIC3 && LCD_CLASS_DEVICE
++ help
++ This driver provides support for changing power and brightness
++ on HTC Universal LCD display.
++
++config HTCUNIVERSAL_TS2
++ tristate "HTC Universal Touchscreen (old)"
++ depends on MACH_HTCUNIVERSAL && HTC_ASIC3
++ help
++ Enable support for the HTC Universal Touchscreen Panel.
++
++config HTCUNIVERSAL_BUTTONS
++ tristate "HTC Universal buttons support"
++ depends on MACH_HTCUNIVERSAL && HTC_ASIC3
++
++config HTCUNIVERSAL_BLUETOOTH
++ tristate "HTC Universal Bluetooth"
++ depends on MACH_HTCUNIVERSAL && HTCUNIVERSAL_CORE && HTC_ASIC3
++ help
++ Enables support for the TI BRF6150 Bluetooth Module
++ in the HTC Universal.
++
++config HTCUNIVERSAL_ASIC3_LEDS
++ tristate "HTC Universal ASIC3 LED support"
++ select LEDS_ASIC3
++ depends on MACH_HTCUNIVERSAL && HTCUNIVERSAL_CORE && HTC_ASIC3
++ ---help---
++ Support for right (colors red+green+(amber)) and left (green+blue) led
++ Off/on hook keys LED backlight
++ Keyboard backlight
++ Vibra
++ Flashlight
++
++config HTCUNIVERSAL_PHONE
++ tristate "HTC Universal Phone"
++ depends on MACH_HTCUNIVERSAL && HTCUNIVERSAL_CORE && HTC_ASIC3
++ help
++ Enables support for the Qualcomm MSM6520 Phone Module
++ in the HTC Universal.
++
++config HTCUNIVERSAL_AK4641
++ depends on SND && I2C
++ tristate "AK4641 chipset support"
++
++endmenu
++
++endif
++
+ endmenu
+
+ config MACH_POODLE
+@@ -289,4 +377,3 @@
+ depends on (PXA25x || PXA27x) && INPUT
+
+ endif
+-
+Index: linux-2.6.26-rc4/arch/arm/mach-pxa/Makefile
+===================================================================
+--- linux-2.6.26-rc4.orig/arch/arm/mach-pxa/Makefile 2008-06-01 18:49:43.000000000 +0100
++++ linux-2.6.26-rc4/arch/arm/mach-pxa/Makefile 2008-06-01 18:50:46.000000000 +0100
+@@ -36,6 +36,7 @@
+ obj-$(CONFIG_MACH_PCM990_BASEBOARD) += pcm990-baseboard.o
+ obj-$(CONFIG_MACH_TOSA) += tosa.o
+ obj-$(CONFIG_MACH_EM_X270) += em-x270.o
++obj-$(CONFIG_MACH_HTCUNIVERSAL) += htcuniversal/
+ obj-$(CONFIG_MACH_MAGICIAN) += magician.o
+ obj-$(CONFIG_ARCH_PXA_ESERIES) += eseries.o
+
+Index: linux-2.6.26-rc4/drivers/leds/Kconfig
+===================================================================
+--- linux-2.6.26-rc4.orig/drivers/leds/Kconfig 2008-05-26 19:08:11.000000000 +0100
++++ linux-2.6.26-rc4/drivers/leds/Kconfig 2008-06-01 18:49:45.000000000 +0100
+@@ -147,6 +147,13 @@
+ To compile this driver as a module, choose M here: the
+ module will be called leds-clevo-mail.
+
++config LEDS_ASIC3
++ tristate "LED Support for the HTC ASIC3 chip"
++ depends on LEDS_CLASS && HTC_ASIC3
++ help
++ This option enables support for the LEDs connected to the
++ HTC ASIC3 chip.
++
+ comment "LED Triggers"
+
+ config LEDS_TRIGGERS
+Index: linux-2.6.26-rc4/drivers/leds/leds-asic3.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26-rc4/drivers/leds/leds-asic3.c 2008-06-01 18:49:45.000000000 +0100
+@@ -0,0 +1,189 @@
++/*
++ * LEDs support for HTC ASIC3 devices.
++ *
++ * Copyright (c) 2006 Anton Vorontsov <cbou@mail.ru>
++ *
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License. See the file COPYING in the main directory of this archive for
++ * more details.
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/platform_device.h>
++#include <linux/leds.h>
++#include "leds.h"
++
++#include <asm/hardware/ipaq-asic3.h>
++#include <linux/soc/asic3_base.h>
++#include <asm/mach-types.h>
++#include <asm/hardware/asic3_leds.h>
++
++#ifdef DEBUG
++#define dbg(msg, ...) printk(msg, __VA_ARGS__)
++#else
++#define dbg(msg, ...)
++#endif
++
++static
++void asic3_leds_set(struct led_classdev *led_cdev, enum led_brightness b)
++{
++ struct asic3_led *led = container_of(led_cdev, struct asic3_led,
++ led_cdev);
++ struct asic3_leds_machinfo *machinfo = led->machinfo;
++ struct device *asic3_dev = &machinfo->asic3_pdev->dev;
++
++ dbg("%s:%s %d(%d)-%s %d\n", __FILE__, __FUNCTION__, led->hw_num,
++ led->gpio_num, led->led_cdev.name, b);
++
++ if (led->hw_num == -1) {
++ asic3_gpio_set_value(asic3_dev, led->gpio_num, b);
++ return;
++ }
++
++ if (b == LED_OFF) {
++ asic3_set_led(asic3_dev, led->hw_num, 0, 16, 6);
++ asic3_set_gpio_out_c(asic3_dev, led->hw_num, 0);
++ }
++ else {
++ asic3_set_gpio_out_c(asic3_dev, led->hw_num, led->hw_num);
++ #ifdef CONFIG_LEDS_TRIGGER_HWTIMER
++ if (led_cdev->trigger && led_cdev->trigger->is_led_supported &&
++ (led_cdev->trigger->is_led_supported(led_cdev) &
++ LED_SUPPORTS_HWTIMER)) {
++ struct hwtimer_data *td = led_cdev->trigger_data;
++ if (!td) return;
++ asic3_set_led(asic3_dev, led->hw_num, td->delay_on/8,
++ (td->delay_on + td->delay_off)/8, 6);
++ }
++ else
++ #endif
++ asic3_set_led(asic3_dev, led->hw_num, 16, 16, 6);
++ }
++
++ return;
++}
++
++static
++int asic3_leds_probe(struct platform_device *pdev)
++{
++ struct asic3_leds_machinfo *machinfo = pdev->dev.platform_data;
++ struct asic3_led *leds = machinfo->leds;
++ int ret, i = 0;
++
++ dbg("%s:%s\n", __FILE__, __FUNCTION__);
++
++ // Turn on clocks early, for the case if trigger would enable
++ // led immediately after led_classdev_register().
++ asic3_set_clock_cdex(&machinfo->asic3_pdev->dev,
++ CLOCK_CDEX_LED0 | CLOCK_CDEX_LED1 | CLOCK_CDEX_LED2,
++ CLOCK_CDEX_LED0 | CLOCK_CDEX_LED1 | CLOCK_CDEX_LED2);
++
++ for (i = 0; i < machinfo->num_leds; i++) {
++ leds[i].machinfo = machinfo;
++ leds[i].led_cdev.brightness_set = asic3_leds_set;
++ ret = led_classdev_register(&pdev->dev, &leds[i].led_cdev);
++ if (ret) {
++ printk(KERN_ERR "Error: can't register %s led\n",
++ leds[i].led_cdev.name);
++ goto out_err;
++ }
++ }
++
++ return 0;
++
++out_err:
++ while (--i >= 0) led_classdev_unregister(&leds[i].led_cdev);
++
++ asic3_set_clock_cdex(&machinfo->asic3_pdev->dev,
++ CLOCK_CDEX_LED0 | CLOCK_CDEX_LED1 | CLOCK_CDEX_LED2,
++ 0 | 0 | 0);
++
++ return ret;
++}
++
++static
++int asic3_leds_remove(struct platform_device *pdev)
++{
++ struct asic3_leds_machinfo *machinfo = pdev->dev.platform_data;
++ struct asic3_led *leds = machinfo->leds;
++ int i = 0;
++
++ dbg("%s:%s\n", __FILE__, __FUNCTION__);
++
++ for (i = 0; i < machinfo->num_leds; i++)
++ led_classdev_unregister(&leds[i].led_cdev);
++
++ asic3_set_clock_cdex(&machinfo->asic3_pdev->dev,
++ CLOCK_CDEX_LED0 | CLOCK_CDEX_LED1 | CLOCK_CDEX_LED2,
++ 0 | 0 | 0);
++
++ return 0;
++}
++
++#ifdef CONFIG_PM
++
++static
++int asic3_leds_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ struct asic3_leds_machinfo *machinfo = pdev->dev.platform_data;
++ struct asic3_led *leds = machinfo->leds;
++ int i = 0;
++
++ dbg("%s:%s\n", __FILE__, __FUNCTION__);
++
++ for (i = 0; i < machinfo->num_leds; i++)
++ led_classdev_suspend(&leds[i].led_cdev);
++
++ return 0;
++}
++
++static
++int asic3_leds_resume(struct platform_device *pdev)
++{
++ struct asic3_leds_machinfo *machinfo = pdev->dev.platform_data;
++ struct asic3_led *leds = machinfo->leds;
++ int i = 0;
++
++ dbg("%s:%s\n", __FILE__, __FUNCTION__);
++
++ for (i = 0; i < machinfo->num_leds; i++)
++ led_classdev_resume(&leds[i].led_cdev);
++
++ return 0;
++}
++
++#endif
++
++static
++struct platform_driver asic3_leds_driver = {
++ .probe = asic3_leds_probe,
++ .remove = asic3_leds_remove,
++#ifdef CONFIG_PM
++ .suspend = asic3_leds_suspend,
++ .resume = asic3_leds_resume,
++#endif
++ .driver = {
++ .name = "asic3-leds",
++ },
++};
++
++int asic3_leds_register(void)
++{
++ dbg("%s:%s\n", __FILE__, __FUNCTION__);
++ return platform_driver_register(&asic3_leds_driver);
++}
++
++void asic3_leds_unregister(void)
++{
++ platform_driver_unregister(&asic3_leds_driver);
++ return;
++}
++
++EXPORT_SYMBOL_GPL(asic3_leds_register);
++EXPORT_SYMBOL_GPL(asic3_leds_unregister);
++
++MODULE_AUTHOR("Anton Vorontsov <cbou@mail.ru>");
++MODULE_DESCRIPTION("HTC ASIC3 LEDs driver");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.26-rc4/drivers/mfd/Kconfig
+===================================================================
+--- linux-2.6.26-rc4.orig/drivers/mfd/Kconfig 2008-06-01 18:49:43.000000000 +0100
++++ linux-2.6.26-rc4/drivers/mfd/Kconfig 2008-06-01 18:49:45.000000000 +0100
+@@ -44,6 +44,16 @@
+ help
+ Support for TI TSC2101 Touchscreen and Audio Codec
+
++config HTC_ASIC3
++ tristate "HTC ASIC3 (iPAQ h1900/h3900/h4000/hx4700/rx3000) support"
++
++config HTC_ASIC3_DS1WM
++ bool "Support HTC ASIC3 builtin DS1WM block"
++ help
++ Choose Y here if you want to include support for ASIC3's builtin
++ W1 controller. Some devices do not use it, and yet other have
++ separate DS1WM controller. For them, choose N.
++
+ endmenu
+
+ menu "Multimedia Capabilities Port drivers"
+Index: linux-2.6.26-rc4/drivers/mfd/Makefile
+===================================================================
+--- linux-2.6.26-rc4.orig/drivers/mfd/Makefile 2008-06-01 18:49:43.000000000 +0100
++++ linux-2.6.26-rc4/drivers/mfd/Makefile 2008-06-01 18:49:45.000000000 +0100
+@@ -2,6 +2,8 @@
+ # Makefile for multifunction miscellaneous devices
+ #
+
++obj-$(CONFIG_HTC_ASIC3) += asic3_base.o soc-core.o
++
+ obj-$(CONFIG_MFD_SM501) += sm501.o
+ obj-$(CONFIG_MFD_ASIC3) += asic3.o
+
+Index: linux-2.6.26-rc4/drivers/mfd/asic3_base.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26-rc4/drivers/mfd/asic3_base.c 2008-06-01 18:49:45.000000000 +0100
+@@ -0,0 +1,1208 @@
++/*
++ * Driver interface to HTC "ASIC3"
++ *
++ * Copyright 2001 Compaq Computer Corporation.
++ * Copyright 2004-2005 Phil Blundell
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * COMPAQ COMPUTER CORPORATION MAKES NO WARRANTIES, EXPRESSED OR IMPLIED,
++ * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS
++ * FITNESS FOR ANY PARTICULAR PURPOSE.
++ *
++ * Author: Andrew Christian
++ * <Andrew.Christian@compaq.com>
++ * October 2001
++ */
++
++#include <linux/module.h>
++#include <linux/version.h>
++#include <linux/platform_device.h>
++#include <linux/delay.h>
++#include <linux/init.h>
++#include <linux/irq.h>
++#include <linux/clk.h>
++#include <linux/ds1wm.h>
++#include <asm/arch/clock.h>
++
++#include <asm/hardware.h>
++#include <asm/irq.h>
++#include <asm/io.h>
++
++#include <asm/hardware/ipaq-asic3.h>
++#include <linux/soc/asic3_base.h>
++#include <linux/soc/tmio_mmc.h>
++#include "soc-core.h"
++
++
++struct asic3_data {
++ void *mapping;
++ unsigned int bus_shift;
++ int irq_base;
++ int irq_nr;
++
++ u16 irq_bothedge[4];
++ struct device *dev;
++
++ struct platform_device *mmc_dev;
++};
++
++static DEFINE_SPINLOCK(asic3_gpio_lock);
++
++static int asic3_remove(struct platform_device *dev);
++
++static inline unsigned long asic3_address(struct device *dev,
++ unsigned int reg)
++{
++ struct asic3_data *adata;
++
++ adata = (struct asic3_data *)dev->driver_data;
++
++ return (unsigned long)adata->mapping + (reg >> (2 - adata->bus_shift));
++}
++
++void asic3_write_register(struct device *dev, unsigned int reg, u32 value)
++{
++ __raw_writew(value, asic3_address(dev, reg));
++}
++EXPORT_SYMBOL(asic3_write_register);
++
++u32 asic3_read_register(struct device *dev, unsigned int reg)
++{
++ return __raw_readw(asic3_address(dev, reg));
++}
++EXPORT_SYMBOL(asic3_read_register);
++
++static inline void __asic3_write_register(struct asic3_data *asic,
++ unsigned int reg, u32 value)
++{
++ __raw_writew(value, (unsigned long)asic->mapping
++ + (reg >> (2 - asic->bus_shift)));
++}
++
++static inline u32 __asic3_read_register(struct asic3_data *asic,
++ unsigned int reg)
++{
++ return __raw_readw((unsigned long)asic->mapping
++ + (reg >> (2 - asic->bus_shift)));
++}
++
++#define ASIC3_GPIO_FN(get_fn_name, set_fn_name, REG) \
++u32 get_fn_name(struct device *dev) \
++{ \
++ return asic3_read_register(dev, REG); \
++} \
++EXPORT_SYMBOL(get_fn_name); \
++ \
++void set_fn_name(struct device *dev, u32 bits, u32 val) \
++{ \
++ unsigned long flags; \
++ \
++ spin_lock_irqsave(&asic3_gpio_lock, flags); \
++ val |= (asic3_read_register(dev, REG) & ~bits); \
++ asic3_write_register(dev, REG, val); \
++ spin_unlock_irqrestore(&asic3_gpio_lock, flags); \
++} \
++EXPORT_SYMBOL(set_fn_name);
++
++#define ASIC3_GPIO_REGISTER(ACTION, action, fn, FN) \
++ ASIC3_GPIO_FN(asic3_get_gpio_ ## action ## _ ## fn , \
++ asic3_set_gpio_ ## action ## _ ## fn , \
++ _IPAQ_ASIC3_GPIO_ ## FN ## _Base \
++ + _IPAQ_ASIC3_GPIO_ ## ACTION )
++
++#define ASIC3_GPIO_FUNCTIONS(fn, FN) \
++ ASIC3_GPIO_REGISTER(Direction, dir, fn, FN) \
++ ASIC3_GPIO_REGISTER(Out, out, fn, FN) \
++ ASIC3_GPIO_REGISTER(SleepMask, sleepmask, fn, FN) \
++ ASIC3_GPIO_REGISTER(SleepOut, sleepout, fn, FN) \
++ ASIC3_GPIO_REGISTER(BattFaultOut, battfaultout, fn, FN) \
++ ASIC3_GPIO_REGISTER(AltFunction, alt_fn, fn, FN) \
++ ASIC3_GPIO_REGISTER(SleepConf, sleepconf, fn, FN) \
++ ASIC3_GPIO_REGISTER(Status, status, fn, FN)
++
++#if 0
++ ASIC3_GPIO_REGISTER(Mask, mask, fn, FN)
++ ASIC3_GPIO_REGISTER(TriggerType, trigtype, fn, FN)
++ ASIC3_GPIO_REGISTER(EdgeTrigger, rising, fn, FN)
++ ASIC3_GPIO_REGISTER(LevelTrigger, triglevel, fn, FN)
++ ASIC3_GPIO_REGISTER(IntStatus, intstatus, fn, FN)
++#endif
++
++ASIC3_GPIO_FUNCTIONS(a, A)
++ASIC3_GPIO_FUNCTIONS(b, B)
++ASIC3_GPIO_FUNCTIONS(c, C)
++ASIC3_GPIO_FUNCTIONS(d, D)
++
++int asic3_gpio_get_value(struct device *dev, unsigned gpio)
++{
++ u32 mask = ASIC3_GPIO_bit(gpio);
++ printk("%s(%d)\n", __FUNCTION__, gpio);
++ switch (gpio >> 4) {
++ case _IPAQ_ASIC3_GPIO_BANK_A:
++ return asic3_get_gpio_status_a(dev) & mask;
++ case _IPAQ_ASIC3_GPIO_BANK_B:
++ return asic3_get_gpio_status_b(dev) & mask;
++ case _IPAQ_ASIC3_GPIO_BANK_C:
++ return asic3_get_gpio_status_c(dev) & mask;
++ case _IPAQ_ASIC3_GPIO_BANK_D:
++ return asic3_get_gpio_status_d(dev) & mask;
++ }
++
++ printk(KERN_ERR "%s: invalid GPIO value 0x%x", __FUNCTION__, gpio);
++ return 0;
++}
++EXPORT_SYMBOL(asic3_gpio_get_value);
++
++void asic3_gpio_set_value(struct device *dev, unsigned gpio, int val)
++{
++ u32 mask = ASIC3_GPIO_bit(gpio);
++ u32 bitval = 0;
++ if (val) bitval = mask;
++ printk("%s(%d, %d)\n", __FUNCTION__, gpio, val);
++
++ switch (gpio >> 4) {
++ case _IPAQ_ASIC3_GPIO_BANK_A:
++ asic3_set_gpio_out_a(dev, mask, bitval);
++ return;
++ case _IPAQ_ASIC3_GPIO_BANK_B:
++ asic3_set_gpio_out_b(dev, mask, bitval);
++ return;
++ case _IPAQ_ASIC3_GPIO_BANK_C:
++ asic3_set_gpio_out_c(dev, mask, bitval);
++ return;
++ case _IPAQ_ASIC3_GPIO_BANK_D:
++ asic3_set_gpio_out_d(dev, mask, bitval);
++ return;
++ }
++
++ printk(KERN_ERR "%s: invalid GPIO value 0x%x", __FUNCTION__, gpio);
++}
++EXPORT_SYMBOL(asic3_gpio_set_value);
++
++int asic3_irq_base(struct device *dev)
++{
++ struct asic3_data *asic = dev->driver_data;
++
++ return asic->irq_base;
++}
++EXPORT_SYMBOL(asic3_irq_base);
++
++static int asic3_gpio_to_irq(struct device *dev, unsigned gpio)
++{
++ struct asic3_data *asic = dev->driver_data;
++ printk("%s(%d)\n", __FUNCTION__, gpio);
++
++ return asic->irq_base + gpio;
++}
++
++void asic3_set_led(struct device *dev, int led_num, int duty_time,
++ int cycle_time, int timebase)
++{
++ struct asic3_data *asic = dev->driver_data;
++ unsigned int led_base;
++
++ /* it's a macro thing: see #define _IPAQ_ASIC_LED_0_Base for why you
++ * can't substitute led_num in the macros below...
++ */
++
++ switch (led_num) {
++ case 0:
++ led_base = _IPAQ_ASIC3_LED_0_Base;
++ break;
++ case 1:
++ led_base = _IPAQ_ASIC3_LED_1_Base;
++ break;
++ case 2:
++ led_base = _IPAQ_ASIC3_LED_2_Base;
++ break;
++ default:
++ printk(KERN_ERR "%s: invalid led number %d", __FUNCTION__,
++ led_num);
++ return;
++ }
++
++ __asic3_write_register(asic, led_base + _IPAQ_ASIC3_LED_TimeBase,
++ timebase | LED_EN);
++ __asic3_write_register(asic, led_base + _IPAQ_ASIC3_LED_PeriodTime,
++ cycle_time);
++ __asic3_write_register(asic, led_base + _IPAQ_ASIC3_LED_DutyTime,
++ 0);
++ udelay(20); /* asic voodoo - possibly need a whole duty cycle? */
++ __asic3_write_register(asic, led_base + _IPAQ_ASIC3_LED_DutyTime,
++ duty_time);
++}
++EXPORT_SYMBOL(asic3_set_led);
++
++void asic3_set_clock_sel(struct device *dev, u32 bits, u32 val)
++{
++ struct asic3_data *asic = dev->driver_data;
++ unsigned long flags;
++ u32 v;
++
++ spin_lock_irqsave(&asic3_gpio_lock, flags);
++ v = __asic3_read_register(asic, IPAQ_ASIC3_OFFSET(CLOCK, SEL));
++ v = (v & ~bits) | val;
++ __asic3_write_register(asic, IPAQ_ASIC3_OFFSET(CLOCK, SEL), v);
++ spin_unlock_irqrestore(&asic3_gpio_lock, flags);
++}
++EXPORT_SYMBOL(asic3_set_clock_sel);
++
++void asic3_set_clock_cdex(struct device *dev, u32 bits, u32 val)
++{
++ struct asic3_data *asic = dev->driver_data;
++ unsigned long flags;
++ u32 v;
++
++ spin_lock_irqsave(&asic3_gpio_lock, flags);
++ v = __asic3_read_register(asic, IPAQ_ASIC3_OFFSET(CLOCK, CDEX));
++ v = (v & ~bits) | val;
++ __asic3_write_register(asic, IPAQ_ASIC3_OFFSET(CLOCK, CDEX), v);
++ spin_unlock_irqrestore(&asic3_gpio_lock, flags);
++}
++EXPORT_SYMBOL(asic3_set_clock_cdex);
++
++static void asic3_clock_cdex_enable(struct clk *clk)
++{
++ struct asic3_data *asic = (struct asic3_data *)clk->parent->ctrlbit;
++ unsigned long flags, val;
++
++ local_irq_save(flags);
++
++ val = __asic3_read_register(asic, IPAQ_ASIC3_OFFSET(CLOCK, CDEX));
++ val |= clk->ctrlbit;
++ __asic3_write_register(asic, IPAQ_ASIC3_OFFSET(CLOCK, CDEX), val);
++
++ local_irq_restore(flags);
++}
++
++static void asic3_clock_cdex_disable(struct clk *clk)
++{
++ struct asic3_data *asic = (struct asic3_data *)clk->parent->ctrlbit;
++ unsigned long flags, val;
++
++ local_irq_save(flags);
++
++ val = __asic3_read_register(asic, IPAQ_ASIC3_OFFSET(CLOCK, CDEX));
++ val &= ~clk->ctrlbit;
++ __asic3_write_register(asic, IPAQ_ASIC3_OFFSET(CLOCK, CDEX), val);
++
++ local_irq_restore(flags);
++}
++
++/* base clocks */
++
++static struct clk clk_g = {
++ .name = "gclk",
++ .rate = 0,
++ .parent = NULL,
++};
++
++/* clock definitions */
++
++static struct clk asic3_clocks[] = {
++ {
++ .name = "spi",
++ .id = -1,
++ .parent = &clk_g,
++ .enable = asic3_clock_cdex_enable,
++ .disable = asic3_clock_cdex_disable,
++ .ctrlbit = CLOCK_CDEX_SPI,
++ },
++#ifdef CONFIG_HTC_ASIC3_DS1WM
++ {
++ .name = "ds1wm",
++ .id = -1,
++ .rate = 5000000,
++ .parent = &clk_g,
++ .enable = asic3_clock_cdex_enable,
++ .disable = asic3_clock_cdex_disable,
++ .ctrlbit = CLOCK_CDEX_OWM,
++ },
++#endif
++ {
++ .name = "pwm0",
++ .id = -1,
++ .parent = &clk_g,
++ .enable = asic3_clock_cdex_enable,
++ .disable = asic3_clock_cdex_disable,
++ .ctrlbit = CLOCK_CDEX_PWM0,
++ },
++ {
++ .name = "pwm1",
++ .id = -1,
++ .parent = &clk_g,
++ .enable = asic3_clock_cdex_enable,
++ .disable = asic3_clock_cdex_disable,
++ .ctrlbit = CLOCK_CDEX_PWM1,
++ },
++ {
++ .name = "led0",
++ .id = -1,
++ .parent = &clk_g,
++ .enable = asic3_clock_cdex_enable,
++ .disable = asic3_clock_cdex_disable,
++ .ctrlbit = CLOCK_CDEX_LED0,
++ },
++ {
++ .name = "led1",
++ .id = -1,
++ .parent = &clk_g,
++ .enable = asic3_clock_cdex_enable,
++ .disable = asic3_clock_cdex_disable,
++ .ctrlbit = CLOCK_CDEX_LED1,
++ },
++ {
++ .name = "led2",
++ .id = -1,
++ .parent = &clk_g,
++ .enable = asic3_clock_cdex_enable,
++ .disable = asic3_clock_cdex_disable,
++ .ctrlbit = CLOCK_CDEX_LED2,
++ },
++ {
++ .name = "smbus",
++ .id = -1,
++ .parent = &clk_g,
++ .enable = asic3_clock_cdex_enable,
++ .disable = asic3_clock_cdex_disable,
++ .ctrlbit = CLOCK_CDEX_SMBUS,
++ },
++ {
++ .name = "ex0",
++ .id = -1,
++ .parent = &clk_g,
++ .enable = asic3_clock_cdex_enable,
++ .disable = asic3_clock_cdex_disable,
++ .ctrlbit = CLOCK_CDEX_EX0,
++ },
++ {
++ .name = "ex1",
++ .id = -1,
++ .parent = &clk_g,
++ .enable = asic3_clock_cdex_enable,
++ .disable = asic3_clock_cdex_disable,
++ .ctrlbit = CLOCK_CDEX_EX1,
++ },
++};
++
++void asic3_set_extcf_select(struct device *dev, u32 bits, u32 val)
++{
++ struct asic3_data *asic = dev->driver_data;
++ unsigned long flags;
++ u32 v;
++
++ spin_lock_irqsave(&asic3_gpio_lock, flags);
++ v = __asic3_read_register(asic, IPAQ_ASIC3_OFFSET(EXTCF, Select));
++ v = (v & ~bits) | val;
++ __asic3_write_register(asic, IPAQ_ASIC3_OFFSET(EXTCF, Select), v);
++ spin_unlock_irqrestore(&asic3_gpio_lock, flags);
++}
++EXPORT_SYMBOL(asic3_set_extcf_select);
++
++void asic3_set_extcf_reset(struct device *dev, u32 bits, u32 val)
++{
++ struct asic3_data *asic = dev->driver_data;
++ unsigned long flags;
++ u32 v;
++
++ spin_lock_irqsave(&asic3_gpio_lock, flags);
++ v = __asic3_read_register(asic, IPAQ_ASIC3_OFFSET(EXTCF, Reset));
++ v = (v & ~bits) | val;
++ __asic3_write_register(asic, IPAQ_ASIC3_OFFSET(EXTCF, Reset), v);
++ spin_unlock_irqrestore(&asic3_gpio_lock, flags);
++}
++EXPORT_SYMBOL(asic3_set_extcf_reset);
++
++void asic3_set_sdhwctrl(struct device *dev, u32 bits, u32 val)
++{
++ struct asic3_data *asic = dev->driver_data;
++ unsigned long flags;
++ u32 v;
++
++ spin_lock_irqsave (&asic3_gpio_lock, flags);
++ v = __asic3_read_register(asic, IPAQ_ASIC3_OFFSET(SDHWCTRL, SDConf));
++ v = (v & ~bits) | val;
++ __asic3_write_register(asic, IPAQ_ASIC3_OFFSET(SDHWCTRL, SDConf), v);
++ spin_unlock_irqrestore(&asic3_gpio_lock, flags);
++}
++EXPORT_SYMBOL(asic3_set_sdhwctrl);
++
++
++#define MAX_ASIC_ISR_LOOPS 20
++#define _IPAQ_ASIC3_GPIO_Base_INCR \
++ (_IPAQ_ASIC3_GPIO_B_Base - _IPAQ_ASIC3_GPIO_A_Base)
++
++static inline void asic3_irq_flip_edge(struct asic3_data *asic,
++ u32 base, int bit)
++{
++ u16 edge = __asic3_read_register(asic,
++ base + _IPAQ_ASIC3_GPIO_EdgeTrigger);
++ edge ^= bit;
++ __asic3_write_register(asic,
++ base + _IPAQ_ASIC3_GPIO_EdgeTrigger, edge);
++}
++
++static void asic3_irq_demux(unsigned int irq, struct irq_desc *desc)
++{
++ int iter;
++ struct asic3_data *asic;
++
++ /* Acknowledge the parrent (i.e. CPU's) IRQ */
++ desc->chip->ack(irq);
++
++ asic = desc->handler_data;
++
++ /* printk( KERN_NOTICE "asic3_irq_demux: irq=%d\n", irq ); */
++ for (iter = 0 ; iter < MAX_ASIC_ISR_LOOPS; iter++) {
++ u32 status;
++ int bank;
++
++ status = __asic3_read_register(asic,
++ IPAQ_ASIC3_OFFSET(INTR, PIntStat));
++ /* Check all ten register bits */
++ if ((status & 0x3ff) == 0)
++ break;
++
++ /* Handle GPIO IRQs */
++ for (bank = 0; bank < 4; bank++) {
++ if (status & (1 << bank)) {
++ unsigned long base, i, istat;
++
++ base = _IPAQ_ASIC3_GPIO_A_Base
++ + bank * _IPAQ_ASIC3_GPIO_Base_INCR;
++ istat = __asic3_read_register(asic,
++ base + _IPAQ_ASIC3_GPIO_IntStatus);
++ /* IntStatus is write 0 to clear */
++ /* XXX could miss interrupts! */
++ __asic3_write_register(asic,
++ base + _IPAQ_ASIC3_GPIO_IntStatus, 0);
++
++ for (i = 0; i < 16; i++) {
++ int bit = (1 << i);
++ unsigned int irqnr;
++ if (!(istat & bit))
++ continue;
++
++ irqnr = asic->irq_base
++ + (16 * bank) + i;
++ desc = irq_desc + irqnr;
++ desc->handle_irq(irqnr, desc);
++ if (asic->irq_bothedge[bank] & bit) {
++ asic3_irq_flip_edge(asic, base,
++ bit);
++ }
++ }
++ }
++ }
++
++ /* Handle remaining IRQs in the status register */
++ {
++ int i;
++
++ for (i = ASIC3_LED0_IRQ; i <= ASIC3_OWM_IRQ; i++) {
++ /* They start at bit 4 and go up */
++ if (status & (1 << (i - ASIC3_LED0_IRQ + 4))) {
++ desc = irq_desc + asic->irq_base + i;
++ desc->handle_irq(asic->irq_base + i,
++ desc);
++ }
++ }
++ }
++
++ }
++
++ if (iter >= MAX_ASIC_ISR_LOOPS)
++ printk(KERN_ERR "%s: interrupt processing overrun\n",
++ __FUNCTION__);
++}
++
++static inline int asic3_irq_to_bank(struct asic3_data *asic, int irq)
++{
++ int n;
++
++ n = (irq - asic->irq_base) >> 4;
++
++ return (n * (_IPAQ_ASIC3_GPIO_B_Base - _IPAQ_ASIC3_GPIO_A_Base));
++}
++
++static inline int asic3_irq_to_index(struct asic3_data *asic, int irq)
++{
++ return (irq - asic->irq_base) & 15;
++}
++
++static void asic3_mask_gpio_irq(unsigned int irq)
++{
++ struct asic3_data *asic = get_irq_chip_data(irq);
++ u32 val, bank, index;
++ unsigned long flags;
++
++ bank = asic3_irq_to_bank(asic, irq);
++ index = asic3_irq_to_index(asic, irq);
++
++ spin_lock_irqsave(&asic3_gpio_lock, flags);
++ val = __asic3_read_register(asic, bank + _IPAQ_ASIC3_GPIO_Mask);
++ val |= 1 << index;
++ __asic3_write_register(asic, bank + _IPAQ_ASIC3_GPIO_Mask, val);
++ spin_unlock_irqrestore(&asic3_gpio_lock, flags);
++}
++
++static void asic3_mask_irq(unsigned int irq)
++{
++ struct asic3_data *asic = get_irq_chip_data(irq);
++ int regval;
++
++ if (irq < ASIC3_NR_GPIO_IRQS) {
++ printk(KERN_ERR "asic3_base: gpio mask attempt, irq %d\n",
++ irq);
++ return;
++ }
++
++ regval = __asic3_read_register(asic,
++ _IPAQ_ASIC3_INTR_Base + _IPAQ_ASIC3_INTR_IntMask);
++
++ switch (irq - asic->irq_base) {
++ case ASIC3_LED0_IRQ:
++ __asic3_write_register(asic,
++ _IPAQ_ASIC3_INTR_Base + _IPAQ_ASIC3_INTR_IntMask,
++ regval & ~ASIC3_INTMASK_MASK0);
++ break;
++ case ASIC3_LED1_IRQ:
++ __asic3_write_register(asic,
++ _IPAQ_ASIC3_INTR_Base + _IPAQ_ASIC3_INTR_IntMask,
++ regval & ~ASIC3_INTMASK_MASK1);
++ break;
++ case ASIC3_LED2_IRQ:
++ __asic3_write_register(asic,
++ _IPAQ_ASIC3_INTR_Base + _IPAQ_ASIC3_INTR_IntMask,
++ regval & ~ASIC3_INTMASK_MASK2);
++ break;
++ case ASIC3_SPI_IRQ:
++ __asic3_write_register(asic,
++ _IPAQ_ASIC3_INTR_Base + _IPAQ_ASIC3_INTR_IntMask,
++ regval & ~ASIC3_INTMASK_MASK3);
++ break;
++ case ASIC3_SMBUS_IRQ:
++ __asic3_write_register(asic,
++ _IPAQ_ASIC3_INTR_Base + _IPAQ_ASIC3_INTR_IntMask,
++ regval & ~ASIC3_INTMASK_MASK4);
++ break;
++ case ASIC3_OWM_IRQ:
++ __asic3_write_register(asic,
++ _IPAQ_ASIC3_INTR_Base + _IPAQ_ASIC3_INTR_IntMask,
++ regval & ~ASIC3_INTMASK_MASK5);
++ break;
++ default:
++ printk(KERN_ERR "asic3_base: bad non-gpio irq %d\n", irq);
++ break;
++ }
++}
++
++static void asic3_unmask_gpio_irq(unsigned int irq)
++{
++ struct asic3_data *asic = get_irq_chip_data(irq);
++ u32 val, bank, index;
++ unsigned long flags;
++
++ bank = asic3_irq_to_bank(asic, irq);
++ index = asic3_irq_to_index(asic, irq);
++
++ spin_lock_irqsave(&asic3_gpio_lock, flags);
++ val = __asic3_read_register(asic, bank + _IPAQ_ASIC3_GPIO_Mask);
++ val &= ~(1 << index);
++ __asic3_write_register(asic, bank + _IPAQ_ASIC3_GPIO_Mask, val);
++ spin_unlock_irqrestore(&asic3_gpio_lock, flags);
++}
++
++static void asic3_unmask_irq(unsigned int irq)
++{
++ struct asic3_data *asic = get_irq_chip_data(irq);
++ int regval;
++
++ if (irq < ASIC3_NR_GPIO_IRQS) {
++ printk(KERN_ERR "asic3_base: gpio unmask attempt, irq %d\n",
++ irq);
++ return;
++ }
++
++ regval = __asic3_read_register(asic,
++ _IPAQ_ASIC3_INTR_Base + _IPAQ_ASIC3_INTR_IntMask);
++
++ switch (irq - asic->irq_base) {
++ case ASIC3_LED0_IRQ:
++ __asic3_write_register(asic,
++ _IPAQ_ASIC3_INTR_Base + _IPAQ_ASIC3_INTR_IntMask,
++ regval | ASIC3_INTMASK_MASK0);
++ break;
++ case ASIC3_LED1_IRQ:
++ __asic3_write_register(asic,
++ _IPAQ_ASIC3_INTR_Base + _IPAQ_ASIC3_INTR_IntMask,
++ regval | ASIC3_INTMASK_MASK1);
++ break;
++ case ASIC3_LED2_IRQ:
++ __asic3_write_register(asic,
++ _IPAQ_ASIC3_INTR_Base + _IPAQ_ASIC3_INTR_IntMask,
++ regval | ASIC3_INTMASK_MASK2);
++ break;
++ case ASIC3_SPI_IRQ:
++ __asic3_write_register(asic,
++ _IPAQ_ASIC3_INTR_Base + _IPAQ_ASIC3_INTR_IntMask,
++ regval | ASIC3_INTMASK_MASK3);
++ break;
++ case ASIC3_SMBUS_IRQ:
++ __asic3_write_register(asic,
++ _IPAQ_ASIC3_INTR_Base + _IPAQ_ASIC3_INTR_IntMask,
++ regval | ASIC3_INTMASK_MASK4);
++ break;
++ case ASIC3_OWM_IRQ:
++ __asic3_write_register(asic,
++ _IPAQ_ASIC3_INTR_Base + _IPAQ_ASIC3_INTR_IntMask,
++ regval | ASIC3_INTMASK_MASK5);
++ break;
++ default:
++ printk(KERN_ERR "asic3_base: bad non-gpio irq %d\n", irq);
++ break;
++ }
++}
++
++static int asic3_gpio_irq_type(unsigned int irq, unsigned int type)
++{
++ struct asic3_data *asic = get_irq_chip_data(irq);
++ u32 bank, index;
++ unsigned long flags;
++ u16 trigger, level, edge, bit;
++
++ bank = asic3_irq_to_bank(asic, irq);
++ index = asic3_irq_to_index(asic, irq);
++ bit = 1<<index;
++
++ spin_lock_irqsave(&asic3_gpio_lock, flags);
++ level = __asic3_read_register(asic,
++ bank + _IPAQ_ASIC3_GPIO_LevelTrigger);
++ edge = __asic3_read_register(asic,
++ bank + _IPAQ_ASIC3_GPIO_EdgeTrigger);
++ trigger = __asic3_read_register(asic,
++ bank + _IPAQ_ASIC3_GPIO_TriggerType);
++ asic->irq_bothedge[(irq - asic->irq_base) >> 4] &= ~bit;
++
++ if (type == IRQT_RISING) {
++ trigger |= bit;
++ edge |= bit;
++ } else if (type == IRQT_FALLING) {
++ trigger |= bit;
++ edge &= ~bit;
++ } else if (type == IRQT_BOTHEDGE) {
++ trigger |= bit;
++ if (asic3_gpio_get_value(asic->dev, irq - asic->irq_base))
++ edge &= ~bit;
++ else
++ edge |= bit;
++ asic->irq_bothedge[(irq - asic->irq_base) >> 4] |= bit;
++ } else if (type == IRQT_LOW) {
++ trigger &= ~bit;
++ level &= ~bit;
++ } else if (type == IRQT_HIGH) {
++ trigger &= ~bit;
++ level |= bit;
++ } else {
++ /*
++ * if type == IRQT_NOEDGE, we should mask interrupts, but
++ * be careful to not unmask them if mask was also called.
++ * Probably need internal state for mask.
++ */
++ printk(KERN_NOTICE "asic3: irq type not changed.\n");
++ }
++ __asic3_write_register(asic, bank + _IPAQ_ASIC3_GPIO_LevelTrigger,
++ level);
++ __asic3_write_register(asic, bank + _IPAQ_ASIC3_GPIO_EdgeTrigger,
++ edge);
++ __asic3_write_register(asic, bank + _IPAQ_ASIC3_GPIO_TriggerType,
++ trigger);
++ spin_unlock_irqrestore(&asic3_gpio_lock, flags);
++ return 0;
++}
++
++static struct irq_chip asic3_gpio_irq_chip = {
++ .name = "ASIC3-GPIO",
++ .ack = asic3_mask_gpio_irq,
++ .mask = asic3_mask_gpio_irq,
++ .unmask = asic3_unmask_gpio_irq,
++ .set_type = asic3_gpio_irq_type,
++};
++
++static struct irq_chip asic3_irq_chip = {
++ .name = "ASIC3",
++ .ack = asic3_mask_irq,
++ .mask = asic3_mask_irq,
++ .unmask = asic3_unmask_irq,
++};
++
++static void asic3_release(struct device *dev)
++{
++ struct platform_device *sdev = to_platform_device(dev);
++
++ kfree(sdev->resource);
++ kfree(sdev);
++}
++
++int asic3_register_mmc(struct device *dev)
++{
++ struct platform_device *sdev = kzalloc(sizeof(*sdev), GFP_KERNEL);
++ struct tmio_mmc_hwconfig *mmc_config = kmalloc(sizeof(*mmc_config),
++ GFP_KERNEL);
++ struct platform_device *pdev = to_platform_device(dev);
++ struct asic3_data *asic = dev->driver_data;
++ struct asic3_platform_data *asic3_pdata = dev->platform_data;
++ struct resource *res;
++ int rc;
++
++ if (sdev == NULL || mmc_config == NULL)
++ return -ENOMEM;
++
++ if (asic3_pdata->tmio_mmc_hwconfig) {
++ memcpy(mmc_config, asic3_pdata->tmio_mmc_hwconfig,
++ sizeof(*mmc_config));
++ } else {
++ memset(mmc_config, 0, sizeof(*mmc_config));
++ }
++ mmc_config->address_shift = asic->bus_shift;
++
++ sdev->id = -1;
++ sdev->name = "asic3_mmc";
++ sdev->dev.parent = dev;
++ sdev->num_resources = 2;
++ sdev->dev.platform_data = mmc_config;
++ sdev->dev.release = asic3_release;
++
++ res = kzalloc(sdev->num_resources * sizeof(struct resource),
++ GFP_KERNEL);
++ if (res == NULL) {
++ kfree(sdev);
++ kfree(mmc_config);
++ return -ENOMEM;
++ }
++ sdev->resource = res;
++
++ res[0].start = pdev->resource[2].start;
++ res[0].end = pdev->resource[2].end;
++ res[0].flags = IORESOURCE_MEM;
++ res[1].start = res[1].end = pdev->resource[3].start;
++ res[1].flags = IORESOURCE_IRQ;
++
++ rc = platform_device_register(sdev);
++ if (rc) {
++ printk(KERN_ERR "asic3_base: "
++ "Could not register asic3_mmc device\n");
++ kfree(res);
++ kfree(sdev);
++ return rc;
++ }
++
++ asic->mmc_dev = sdev;
++
++ return 0;
++}
++EXPORT_SYMBOL(asic3_register_mmc);
++
++int asic3_unregister_mmc(struct device *dev)
++{
++ struct asic3_data *asic = dev->driver_data;
++ platform_device_unregister(asic->mmc_dev);
++ asic->mmc_dev = 0;
++
++ return 0;
++}
++EXPORT_SYMBOL(asic3_unregister_mmc);
++
++#ifdef CONFIG_HTC_ASIC3_DS1WM
++/*
++ * DS1WM subdevice
++ */
++
++static void asic3_ds1wm_enable(struct platform_device *ds1wm_dev)
++{
++ struct device *dev = ds1wm_dev->dev.parent;
++
++ /* Turn on external clocks and the OWM clock */
++ asic3_set_clock_cdex(dev,
++ CLOCK_CDEX_EX0 | CLOCK_CDEX_EX1 | CLOCK_CDEX_OWM,
++ CLOCK_CDEX_EX0 | CLOCK_CDEX_EX1 | CLOCK_CDEX_OWM);
++
++ mdelay(1);
++
++ asic3_set_extcf_reset(dev, ASIC3_EXTCF_OWM_RESET,
++ ASIC3_EXTCF_OWM_RESET);
++ mdelay(1);
++ asic3_set_extcf_reset(dev, ASIC3_EXTCF_OWM_RESET, 0);
++ mdelay(1);
++
++ /* Clear OWM_SMB, set OWM_EN */
++ asic3_set_extcf_select(dev,
++ ASIC3_EXTCF_OWM_SMB | ASIC3_EXTCF_OWM_EN,
++ 0 | ASIC3_EXTCF_OWM_EN);
++
++ mdelay(1);
++}
++
++static void asic3_ds1wm_disable(struct platform_device *ds1wm_dev)
++{
++ struct device *dev = ds1wm_dev->dev.parent;
++
++ asic3_set_extcf_select(dev,
++ ASIC3_EXTCF_OWM_SMB | ASIC3_EXTCF_OWM_EN,
++ 0 | 0);
++
++ asic3_set_clock_cdex(dev,
++ CLOCK_CDEX_EX0 | CLOCK_CDEX_EX1 | CLOCK_CDEX_OWM,
++ CLOCK_CDEX_EX0 | CLOCK_CDEX_EX1 | 0);
++}
++
++
++static struct resource asic3_ds1wm_resources[] = {
++ {
++ .start = _IPAQ_ASIC3_OWM_Base,
++ .end = _IPAQ_ASIC3_OWM_Base + 0x14 - 1,
++ .flags = IORESOURCE_MEM,
++ },
++ {
++ .start = ASIC3_OWM_IRQ,
++ .end = ASIC3_OWM_IRQ,
++ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE |
++ IORESOURCE_IRQ_SOC_SUBDEVICE,
++ },
++};
++
++static struct ds1wm_platform_data ds1wm_pd = {
++ .enable = asic3_ds1wm_enable,
++ .disable = asic3_ds1wm_disable,
++};
++#endif
++
++static struct soc_device_data asic3_blocks[] = {
++#ifdef CONFIG_HTC_ASIC3_DS1WM
++ {
++ .name = "ds1wm",
++ .res = asic3_ds1wm_resources,
++ .num_resources = ARRAY_SIZE(asic3_ds1wm_resources),
++ .hwconfig = &ds1wm_pd,
++ },
++#endif
++};
++
++static int asic3_probe(struct platform_device *pdev)
++{
++ struct asic3_platform_data *pdata = pdev->dev.platform_data;
++ struct asic3_data *asic;
++ struct device *dev = &pdev->dev;
++ unsigned long clksel;
++ int i, rc;
++
++ asic = kzalloc(sizeof(struct asic3_data), GFP_KERNEL);
++ if (!asic)
++ return -ENOMEM;
++
++ platform_set_drvdata(pdev, asic);
++ asic->dev = &pdev->dev;
++
++ asic->mapping = ioremap(pdev->resource[0].start, IPAQ_ASIC3_MAP_SIZE);
++ if (!asic->mapping) {
++ printk(KERN_ERR "asic3: couldn't ioremap ASIC3\n");
++ kfree (asic);
++ return -ENOMEM;
++ }
++
++ if (pdata && pdata->bus_shift)
++ asic->bus_shift = pdata->bus_shift;
++ else
++ asic->bus_shift = 2;
++
++ /* XXX: should get correct SD clock values from pdata struct */
++ clksel = 0;
++ __asic3_write_register(asic, IPAQ_ASIC3_OFFSET(CLOCK, SEL), clksel);
++
++ /* Register ASIC3's clocks. */
++ clk_g.ctrlbit = (int)asic;
++
++ if (clk_register(&clk_g) < 0)
++ printk(KERN_ERR "asic3: failed to register ASIC3 gclk\n");
++
++ for (i = 0; i < ARRAY_SIZE(asic3_clocks); i++) {
++ rc = clk_register(&asic3_clocks[i]);
++ if (rc < 0)
++ printk(KERN_ERR "asic3: "
++ "failed to register clock %s (%d)\n",
++ asic3_clocks[i].name, rc);
++ }
++
++ __asic3_write_register(asic, IPAQ_ASIC3_GPIO_OFFSET(A, Mask), 0xffff);
++ __asic3_write_register(asic, IPAQ_ASIC3_GPIO_OFFSET(B, Mask), 0xffff);
++ __asic3_write_register(asic, IPAQ_ASIC3_GPIO_OFFSET(C, Mask), 0xffff);
++ __asic3_write_register(asic, IPAQ_ASIC3_GPIO_OFFSET(D, Mask), 0xffff);
++
++ asic3_set_gpio_sleepmask_a(dev, 0xffff, 0xffff);
++ asic3_set_gpio_sleepmask_b(dev, 0xffff, 0xffff);
++ asic3_set_gpio_sleepmask_c(dev, 0xffff, 0xffff);
++ asic3_set_gpio_sleepmask_d(dev, 0xffff, 0xffff);
++
++ if (pdata) {
++ asic3_set_gpio_out_a(dev, 0xffff, pdata->gpio_a.init);
++ asic3_set_gpio_out_b(dev, 0xffff, pdata->gpio_b.init);
++ asic3_set_gpio_out_c(dev, 0xffff, pdata->gpio_c.init);
++ asic3_set_gpio_out_d(dev, 0xffff, pdata->gpio_d.init);
++
++ asic3_set_gpio_dir_a(dev, 0xffff, pdata->gpio_a.dir);
++ asic3_set_gpio_dir_b(dev, 0xffff, pdata->gpio_b.dir);
++ asic3_set_gpio_dir_c(dev, 0xffff, pdata->gpio_c.dir);
++ asic3_set_gpio_dir_d(dev, 0xffff, pdata->gpio_d.dir);
++
++ asic3_set_gpio_sleepmask_a(dev, 0xffff,
++ pdata->gpio_a.sleep_mask);
++ asic3_set_gpio_sleepmask_b(dev, 0xffff,
++ pdata->gpio_b.sleep_mask);
++ asic3_set_gpio_sleepmask_c(dev, 0xffff,
++ pdata->gpio_c.sleep_mask);
++ asic3_set_gpio_sleepmask_d(dev, 0xffff,
++ pdata->gpio_d.sleep_mask);
++
++ asic3_set_gpio_sleepout_a(dev, 0xffff,
++ pdata->gpio_a.sleep_out);
++ asic3_set_gpio_sleepout_b(dev, 0xffff,
++ pdata->gpio_b.sleep_out);
++ asic3_set_gpio_sleepout_c(dev, 0xffff,
++ pdata->gpio_c.sleep_out);
++ asic3_set_gpio_sleepout_d(dev, 0xffff,
++ pdata->gpio_d.sleep_out);
++
++ asic3_set_gpio_battfaultout_a(dev, 0xffff,
++ pdata->gpio_a.batt_fault_out);
++ asic3_set_gpio_battfaultout_b(dev, 0xffff,
++ pdata->gpio_b.batt_fault_out);
++ asic3_set_gpio_battfaultout_c(dev, 0xffff,
++ pdata->gpio_c.batt_fault_out);
++ asic3_set_gpio_battfaultout_d(dev, 0xffff,
++ pdata->gpio_d.batt_fault_out);
++
++ asic3_set_gpio_sleepconf_a(dev, 0xffff,
++ pdata->gpio_a.sleep_conf);
++ asic3_set_gpio_sleepconf_b(dev, 0xffff,
++ pdata->gpio_b.sleep_conf);
++ asic3_set_gpio_sleepconf_c(dev, 0xffff,
++ pdata->gpio_c.sleep_conf);
++ asic3_set_gpio_sleepconf_d(dev, 0xffff,
++ pdata->gpio_d.sleep_conf);
++
++ asic3_set_gpio_alt_fn_a(dev, 0xffff,
++ pdata->gpio_a.alt_function);
++ asic3_set_gpio_alt_fn_b(dev, 0xffff,
++ pdata->gpio_b.alt_function);
++ asic3_set_gpio_alt_fn_c(dev, 0xffff,
++ pdata->gpio_c.alt_function);
++ asic3_set_gpio_alt_fn_d(dev, 0xffff,
++ pdata->gpio_d.alt_function);
++ }
++
++ asic->irq_nr = -1;
++ asic->irq_base = -1;
++
++ if (pdev->num_resources > 1)
++ asic->irq_nr = pdev->resource[1].start;
++
++ if (asic->irq_nr != -1) {
++ unsigned int i;
++
++ if (!pdata->irq_base) {
++ printk(KERN_ERR "asic3: IRQ base not specified\n");
++ asic3_remove(pdev);
++ return -EINVAL;
++ }
++
++ asic->irq_base = pdata->irq_base;
++
++ /* turn on clock to IRQ controller */
++ clksel |= CLOCK_SEL_CX;
++ __asic3_write_register(asic, IPAQ_ASIC3_OFFSET(CLOCK, SEL),
++ clksel);
++
++ printk(KERN_INFO "asic3: using irq %d-%d on irq %d\n",
++ asic->irq_base, asic->irq_base + ASIC3_NR_IRQS - 1,
++ asic->irq_nr);
++
++ for (i = 0 ; i < ASIC3_NR_IRQS ; i++) {
++ int irq = i + asic->irq_base;
++ if (i < ASIC3_NR_GPIO_IRQS) {
++ set_irq_chip(irq, &asic3_gpio_irq_chip);
++ set_irq_chip_data(irq, asic);
++ set_irq_handler(irq, handle_level_irq);
++ set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
++ } else {
++ /* The remaining IRQs are not GPIO */
++ set_irq_chip(irq, &asic3_irq_chip);
++ set_irq_chip_data(irq, asic);
++ set_irq_handler(irq, handle_level_irq);
++ set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
++ }
++ }
++
++ __asic3_write_register(asic, IPAQ_ASIC3_OFFSET(INTR, IntMask),
++ ASIC3_INTMASK_GINTMASK);
++
++ set_irq_chained_handler(asic->irq_nr, asic3_irq_demux);
++ set_irq_type(asic->irq_nr, IRQT_RISING);
++ set_irq_data(asic->irq_nr, asic);
++ }
++
++#ifdef CONFIG_HTC_ASIC3_DS1WM
++ ds1wm_pd.bus_shift = asic->bus_shift;
++#endif
++
++ pdata->gpiodev_ops.get = asic3_gpio_get_value;
++ pdata->gpiodev_ops.set = asic3_gpio_set_value;
++ pdata->gpiodev_ops.to_irq = asic3_gpio_to_irq;
++
++ soc_add_devices(pdev, asic3_blocks, ARRAY_SIZE(asic3_blocks),
++ &pdev->resource[0],
++ asic->bus_shift - ASIC3_DEFAULT_ADDR_SHIFT,
++ asic->irq_base);
++
++ if (pdev->num_resources > 2) {
++ int rc;
++ rc = asic3_register_mmc(dev);
++ if (rc) {
++ asic3_remove(pdev);
++ return rc;
++ }
++ }
++
++ if (pdata && pdata->num_child_platform_devs != 0)
++ platform_add_devices(pdata->child_platform_devs,
++ pdata->num_child_platform_devs);
++
++ return 0;
++}
++
++static int asic3_remove(struct platform_device *pdev)
++{
++ struct asic3_platform_data *pdata = pdev->dev.platform_data;
++ struct asic3_data *asic = platform_get_drvdata(pdev);
++ int i;
++
++ if (pdata && pdata->num_child_platform_devs != 0) {
++ for (i = 0; i < pdata->num_child_platform_devs; i++) {
++ platform_device_unregister(
++ pdata->child_platform_devs[i]);
++ }
++ }
++
++ if (asic->irq_nr != -1) {
++ unsigned int i;
++
++ for (i = 0 ; i < ASIC3_NR_IRQS ; i++) {
++ int irq = i + asic->irq_base;
++ set_irq_flags(irq, 0);
++ set_irq_handler (irq, NULL);
++ set_irq_chip (irq, NULL);
++ set_irq_chip_data(irq, NULL);
++ }
++
++ set_irq_chained_handler(asic->irq_nr, NULL);
++ }
++
++ if (asic->mmc_dev)
++ asic3_unregister_mmc(&pdev->dev);
++
++ for (i = 0; i < ARRAY_SIZE(asic3_clocks); i++)
++ clk_unregister(&asic3_clocks[i]);
++ clk_unregister(&clk_g);
++
++ __asic3_write_register(asic, IPAQ_ASIC3_OFFSET(CLOCK, SEL), 0);
++ __asic3_write_register(asic, IPAQ_ASIC3_OFFSET(INTR, IntMask), 0);
++
++ iounmap(asic->mapping);
++
++ kfree(asic);
++
++ return 0;
++}
++
++static void asic3_shutdown(struct platform_device *pdev)
++{
++}
++
++#define ASIC3_SUSPEND_CDEX_MASK \
++ (CLOCK_CDEX_LED0 | CLOCK_CDEX_LED1 | CLOCK_CDEX_LED2)
++static unsigned short suspend_cdex;
++
++static int asic3_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ struct asic3_data *asic = platform_get_drvdata(pdev);
++ suspend_cdex = __asic3_read_register(asic,
++ _IPAQ_ASIC3_CLOCK_Base + _IPAQ_ASIC3_CLOCK_CDEX);
++ /* The LEDs are still active during suspend */
++ __asic3_write_register(asic,
++ _IPAQ_ASIC3_CLOCK_Base + _IPAQ_ASIC3_CLOCK_CDEX,
++ suspend_cdex & ASIC3_SUSPEND_CDEX_MASK);
++ return 0;
++}
++
++static int asic3_resume(struct platform_device *pdev)
++{
++ struct asic3_data *asic = platform_get_drvdata(pdev);
++ unsigned short intmask;
++
++ __asic3_write_register(asic, IPAQ_ASIC3_OFFSET(CLOCK, CDEX),
++ suspend_cdex);
++
++ if (asic->irq_nr != -1) {
++ /* Toggle the interrupt mask to try to get ASIC3 to show
++ * the CPU an interrupt edge. For more details see the
++ * kernel-discuss thread around 13 June 2005 with the
++ * subject "asic3 suspend / resume". */
++ intmask = __asic3_read_register(asic,
++ IPAQ_ASIC3_OFFSET(INTR, IntMask));
++ __asic3_write_register(asic, IPAQ_ASIC3_OFFSET(INTR, IntMask),
++ intmask & ~ASIC3_INTMASK_GINTMASK);
++ mdelay(1);
++ __asic3_write_register(asic, IPAQ_ASIC3_OFFSET(INTR, IntMask),
++ intmask | ASIC3_INTMASK_GINTMASK);
++ }
++
++ return 0;
++}
++
++static struct platform_driver asic3_device_driver = {
++ .driver = {
++ .name = "asic3",
++ },
++ .probe = asic3_probe,
++ .remove = asic3_remove,
++ .suspend = asic3_suspend,
++ .resume = asic3_resume,
++ .shutdown = asic3_shutdown,
++};
++
++static int __init asic3_base_init(void)
++{
++ int retval = 0;
++ retval = platform_driver_register(&asic3_device_driver);
++ return retval;
++}
++
++static void __exit asic3_base_exit(void)
++{
++ platform_driver_unregister(&asic3_device_driver);
++}
++
++#ifdef MODULE
++module_init(asic3_base_init);
++#else /* start early for dependencies */
++subsys_initcall(asic3_base_init);
++#endif
++module_exit(asic3_base_exit);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Phil Blundell <pb@handhelds.org>");
++MODULE_DESCRIPTION("Core driver for HTC ASIC3");
++MODULE_SUPPORTED_DEVICE("asic3");
+Index: linux-2.6.26-rc4/drivers/mfd/soc-core.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26-rc4/drivers/mfd/soc-core.c 2008-06-01 18:49:45.000000000 +0100
+@@ -0,0 +1,106 @@
++/*
++ * drivers/soc/soc-core.c
++ *
++ * core SoC support
++ * Copyright (c) 2006 Ian Molton
++ *
++ * 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 functionality used by many SoC type devices.
++ *
++ * Created: 2006-11-28
++ *
++ */
++
++#include <linux/ioport.h>
++#include <linux/slab.h>
++#include <linux/kernel.h>
++#include <linux/platform_device.h>
++#include "soc-core.h"
++
++void soc_free_devices(struct platform_device *devices, int nr_devs)
++{
++ struct platform_device *dev = devices;
++ int i;
++
++ for (i = 0; i < nr_devs; i++) {
++ struct resource *res = dev->resource;
++ platform_device_unregister(dev++);
++ kfree(res);
++ }
++ kfree(devices);
++}
++EXPORT_SYMBOL_GPL(soc_free_devices);
++
++#define SIGNED_SHIFT(val, shift) ((shift) >= 0 ? ((val) << (shift)) : ((val) >> -(shift)))
++
++struct platform_device *soc_add_devices(struct platform_device *dev,
++ struct soc_device_data *soc, int nr_devs,
++ struct resource *mem,
++ int relative_addr_shift, int irq_base)
++{
++ struct platform_device *devices;
++ int i, r, base;
++
++ devices = kzalloc(nr_devs * sizeof(struct platform_device), GFP_KERNEL);
++ if (!devices)
++ return NULL;
++
++ for (i = 0; i < nr_devs; i++) {
++ struct platform_device *sdev = &devices[i];
++ struct soc_device_data *blk = &soc[i];
++ struct resource *res;
++
++ sdev->id = -1;
++ sdev->name = blk->name;
++
++ sdev->dev.parent = &dev->dev;
++ sdev->dev.platform_data = (void *)blk->hwconfig;
++ sdev->num_resources = blk->num_resources;
++
++ /* Allocate space for the subdevice resources */
++ res = kzalloc (blk->num_resources * sizeof (struct resource), GFP_KERNEL);
++ if (!res)
++ goto fail;
++
++ for (r = 0 ; r < blk->num_resources ; r++) {
++ res[r].name = blk->res[r].name; // Fixme - should copy
++
++ /* Find out base to use */
++ base = 0;
++ if (blk->res[r].flags & IORESOURCE_MEM) {
++ base = mem->start;
++ } else if ((blk->res[r].flags & IORESOURCE_IRQ) &&
++ (blk->res[r].flags & IORESOURCE_IRQ_SOC_SUBDEVICE)) {
++ base = irq_base;
++ }
++
++ /* Adjust resource */
++ if (blk->res[r].flags & IORESOURCE_MEM) {
++ res[r].parent = mem;
++ res[r].start = base + SIGNED_SHIFT(blk->res[r].start, relative_addr_shift);
++ res[r].end = base + SIGNED_SHIFT(blk->res[r].end, relative_addr_shift);
++ } else {
++ res[r].start = base + blk->res[r].start;
++ res[r].end = base + blk->res[r].end;
++ }
++ res[r].flags = blk->res[r].flags;
++ }
++
++ sdev->resource = res;
++ if (platform_device_register(sdev)) {
++ kfree(res);
++ goto fail;
++ }
++
++ printk(KERN_INFO "SoC: registering %s\n", blk->name);
++ }
++ return devices;
++
++fail:
++ soc_free_devices(devices, i + 1);
++ return NULL;
++}
++EXPORT_SYMBOL_GPL(soc_add_devices);
+Index: linux-2.6.26-rc4/drivers/mfd/soc-core.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26-rc4/drivers/mfd/soc-core.h 2008-06-01 18:49:45.000000000 +0100
+@@ -0,0 +1,30 @@
++/*
++ * drivers/soc/soc-core.h
++ *
++ * core SoC support
++ * Copyright (c) 2006 Ian Molton
++ *
++ * 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 prototypes for the functions in soc-core.c
++ *
++ * Created: 2006-11-28
++ *
++ */
++
++struct soc_device_data {
++ char *name;
++ struct resource *res;
++ int num_resources;
++ void *hwconfig; /* platform_data to pass to the subdevice */
++};
++
++struct platform_device *soc_add_devices(struct platform_device *dev,
++ struct soc_device_data *soc, int n_devs,
++ struct resource *mem,
++ int relative_addr_shift, int irq_base);
++
++void soc_free_devices(struct platform_device *devices, int nr_devs);
++
+Index: linux-2.6.26-rc4/include/asm-arm/arch-pxa/clock.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26-rc4/include/asm-arm/arch-pxa/clock.h 2008-06-01 18:49:45.000000000 +0100
+@@ -0,0 +1,27 @@
++/*
++ * linux/include/asm-arm/arch-pxa/clock.h
++ *
++ * Copyright (C) 2006 Erik Hovland
++ *
++ * 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.
++ */
++
++struct clk {
++ struct list_head node;
++ struct module *owner;
++ struct clk *parent;
++ const char *name;
++ int id;
++ unsigned int enabled;
++ unsigned long rate;
++ unsigned long ctrlbit;
++
++ void (*enable)(struct clk *);
++ void (*disable)(struct clk *);
++};
++
++
++extern int clk_register(struct clk *clk);
++extern void clk_unregister(struct clk *clk);
+Index: linux-2.6.26-rc4/include/asm-arm/arch-pxa/htcuniversal-asic.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26-rc4/include/asm-arm/arch-pxa/htcuniversal-asic.h 2008-06-01 18:49:45.000000000 +0100
+@@ -0,0 +1,213 @@
++/*
++ * include/asm/arm/arch-pxa/htcuniversal-asic.h
++ *
++ * Authors: Giuseppe Zompatori <giuseppe_zompatori@yahoo.it>
++ *
++ * based on previews work, see below:
++ *
++ * include/asm/arm/arch-pxa/hx4700-asic.h
++ * Copyright (c) 2004 SDG Systems, LLC
++ *
++ */
++
++#ifndef _HTCUNIVERSAL_ASIC_H_
++#define _HTCUNIVERSAL_ASIC_H_
++
++#include <asm/hardware/ipaq-asic3.h>
++
++/* ASIC3 */
++
++#define HTCUNIVERSAL_ASIC3_GPIO_PHYS PXA_CS4_PHYS
++#define HTCUNIVERSAL_ASIC3_MMC_PHYS PXA_CS3_PHYS
++
++/* TODO: some information is missing here */
++
++/* ASIC3 GPIO A bank */
++
++#define GPIOA_I2C_EN 0 /* Output */
++#define GPIOA_SPK_PWR1_ON 1 /* Output */
++#define GPIOA_AUDIO_PWR_ON 2 /* Output */
++#define GPIOA_EARPHONE_PWR_ON 3 /* Output */
++
++#define GPIOA_UNKNOWN4 4 /* Output */
++#define GPIOA_BUTTON_BACKLIGHT_N 5 /* Input */
++#define GPIOA_SPK_PWR2_ON 6 /* Output */
++#define GPIOA_BUTTON_RECORD_N 7 /* Input */
++
++#define GPIOA_BUTTON_CAMERA_N 8 /* Input */
++#define GPIOA_UNKNOWN9 9 /* Output */
++#define GPIOA_FLASHLIGHT 10 /* Output */
++#define GPIOA_COVER_ROTATE_N 11 /* Input */
++
++#define GPIOA_TOUCHSCREEN_N 12 /* Input */
++#define GPIOA_VOL_UP_N 13 /* Input */
++#define GPIOA_VOL_DOWN_N 14 /* Input */
++#define GPIOA_LCD_PWR5_ON 15 /* Output */
++
++/* ASIC3 GPIO B bank */
++
++#define GPIOB_BB_READY 0 /* Input */
++#define GPIOB_CODEC_PDN 1 /* Output */
++#define GPIOB_UNKNOWN2 2 /* Input */
++#define GPIOB_BB_UNKNOWN3 3 /* Input */
++
++#define GPIOB_BT_IRQ 4 /* Input */
++#define GPIOB_CLAMSHELL_N 5 /* Input */
++#define GPIOB_LCD_PWR3_ON 6 /* Output */
++#define GPIOB_BB_ALERT 7 /* Input */
++
++#define GPIOB_BB_RESET2 8 /* Output */
++#define GPIOB_EARPHONE_N 9 /* Input */
++#define GPIOB_MICRECORD_N 10 /* Input */
++#define GPIOB_NIGHT_SENSOR 11 /* Input */
++
++#define GPIOB_UMTS_DCD 12 /* Input */
++#define GPIOB_UNKNOWN13 13 /* Input */
++#define GPIOB_CHARGE_EN 14 /* Output */
++#define GPIOB_USB_PUEN 15 /* Output */
++
++/* ASIC3 GPIO C bank */
++
++#define GPIOC_LED_BTWIFI 0 /* Output */
++#define GPIOC_LED_RED 1 /* Output */
++#define GPIOC_LED_GREEN 2 /* Output */
++#define GPIOC_BOARDID3 3 /* Input */
++
++#define GPIOC_WIFI_IRQ_N 4 /* Input */
++#define GPIOC_WIFI_RESET 5 /* Output */
++#define GPIOC_WIFI_PWR1_ON 6 /* Output */
++#define GPIOC_BT_RESET 7 /* Output */
++
++#define GPIOC_UNKNOWN8 8 /* Output */
++#define GPIOC_LCD_PWR1_ON 9 /* Output */
++#define GPIOC_LCD_PWR2_ON 10 /* Output */
++#define GPIOC_BOARDID2 11 /* Input */
++
++#define GPIOC_BOARDID1 12 /* Input */
++#define GPIOC_BOARDID0 13 /* Input */
++#define GPIOC_BT_PWR_ON 14 /* Output */
++#define GPIOC_CHARGE_ON 15 /* Output */
++
++/* ASIC3 GPIO D bank */
++
++#define GPIOD_KEY_OK_N 0 /* Input */
++#define GPIOD_KEY_RIGHT_N 1 /* Input */
++#define GPIOD_KEY_LEFT_N 2 /* Input */
++#define GPIOD_KEY_DOWN_N 3 /* Input */
++
++#define GPIOD_KEY_UP_N 4 /* Input */
++#define GPIOD_SDIO_DET 5 /* Input */
++#define GPIOD_WIFI_PWR2_ON 6 /* Output */
++#define GPIOD_HW_REBOOT 7 /* Output */
++
++#define GPIOD_BB_RESET1 8 /* Output */
++#define GPIOD_UNKNOWN9 9 /* Output */
++#define GPIOD_VIBRA_PWR_ON 10 /* Output */
++#define GPIOD_WIFI_PWR3_ON 11 /* Output */
++
++#define GPIOD_FL_PWR_ON 12 /* Output */
++#define GPIOD_LCD_PWR4_ON 13 /* Output */
++#define GPIOD_BL_KEYP_PWR_ON 14 /* Output */
++#define GPIOD_BL_KEYB_PWR_ON 15 /* Output */
++
++extern struct platform_device htcuniversal_asic3;
++
++/* ASIC3 GPIO A bank */
++
++#define GPIO_I2C_EN 0*16+GPIOA_I2C_EN
++#define GPIO_SPK_PWR1_ON 0*16+GPIOA_SPK_PWR1_ON
++#define GPIO_AUDIO_PWR_ON 0*16+GPIOA_AUDIO_PWR_ON
++#define GPIO_EARPHONE_PWR_ON 0*16+GPIOA_EARPHONE_PWR_ON
++
++#define GPIO_UNKNOWNA4 0*16+GPIOA_UNKNOWN4
++#define GPIO_BUTTON_BACKLIGHT_N 0*16+GPIOA_BUTTON_BACKLIGHT_N
++#define GPIO_SPK_PWR2_ON 0*16+GPIOA_SPK_PWR2_ON
++#define GPIO_BUTTON_RECORD_N 0*16+GPIOA_BUTTON_RECORD_N
++
++#define GPIO_BUTTON_CAMERA_N 0*16+GPIOA_BUTTON_CAMERA_N
++#define GPIO_UNKNOWNA9 0*16+GPIOA_UNKNOWN9
++#define GPIO_FLASHLIGHT 0*16+GPIOA_FLASHLIGHT
++#define GPIO_COVER_ROTATE_N 0*16+GPIOA_COVER_ROTATE_N
++
++#define GPIO_TOUCHSCREEN_N 0*16+GPIOA_TOUCHSCREEN_N
++#define GPIO_VOL_UP_N 0*16+GPIOA_VOL_UP_N
++#define GPIO_VOL_DOWN_N 0*16+GPIOA_VOL_DOWN_N
++#define GPIO_LCD_PWR5_ON 0*16+GPIOA_LCD_PWR5_ON
++
++/* ASIC3 GPIO B bank */
++
++#define GPIO_BB_READY 1*16+GPIOB_BB_READY
++#define GPIO_CODEC_PDN 1*16+GPIOB_CODEC_PDN
++#define GPIO_UNKNOWNB2 1*16+GPIOB_UNKNOWN2
++#define GPIO_BB_UNKNOWN3 1*16+GPIOB_BB_UNKNOWN3
++
++#define GPIO_BT_IRQ 1*16+GPIOB_BT_IRQ
++#define GPIO_CLAMSHELL_N 1*16+GPIOB_CLAMSHELL_N
++#define GPIO_LCD_PWR3_ON 1*16+GPIOB_LCD_PWR3_ON
++#define GPIO_BB_ALERT 1*16+GPIOB_BB_ALERT
++
++#define GPIO_BB_RESET2 1*16+GPIOB_BB_RESET2
++#define GPIO_EARPHONE_N 1*16+GPIOB_EARPHONE_N
++#define GPIO_MICRECORD_N 1*16+GPIOB_MICRECORD_N
++#define GPIO_NIGHT_SENSOR 1*16+GPIOB_NIGHT_SENSOR
++
++#define GPIO_UMTS_DCD 1*16+GPIOB_UMTS_DCD
++#define GPIO_UNKNOWNB13 1*16+GPIOB_UNKNOWN13
++#define GPIO_CHARGE_EN 1*16+GPIOB_CHARGE_EN
++#define GPIO_USB_PUEN 1*16+GPIOB_USB_PUEN
++
++/* ASIC3 GPIO C bank */
++
++#define GPIO_LED_BTWIFI 2*16+GPIOC_LED_BTWIFI
++#define GPIO_LED_RED 2*16+GPIOC_LED_RED
++#define GPIO_LED_GREEN 2*16+GPIOC_LED_GREEN
++#define GPIO_BOARDID3 2*16+GPIOC_BOARDID3
++
++#define GPIO_WIFI_IRQ_N 2*16+GPIOC_WIFI_IRQ_N
++#define GPIO_WIFI_RESET 2*16+GPIOC_WIFI_RESET
++#define GPIO_WIFI_PWR1_ON 2*16+GPIOC_WIFI_PWR1_ON
++#define GPIO_BT_RESET 2*16+GPIOC_BT_RESET
++
++#define GPIO_UNKNOWNC8 2*16+GPIOC_UNKNOWN8
++#define GPIO_LCD_PWR1_ON 2*16+GPIOC_LCD_PWR1_ON
++#define GPIO_LCD_PWR2_ON 2*16+GPIOC_LCD_PWR2_ON
++#define GPIO_BOARDID2 2*16+GPIOC_BOARDID2
++
++#define GPIO_BOARDID1 2*16+GPIOC_BOARDID1
++#define GPIO_BOARDID0 2*16+GPIOC_BOARDID0
++#define GPIO_BT_PWR_ON 2*16+GPIOC_BT_PWR_ON
++#define GPIO_CHARGE_ON 2*16+GPIOC_CHARGE_ON
++
++/* ASIC3 GPIO D bank */
++
++#define GPIO_KEY_OK_N 3*16+GPIOD_KEY_OK_N
++#define GPIO_KEY_RIGHT_N 3*16+GPIOD_KEY_RIGHT_N
++#define GPIO_KEY_LEFT_N 3*16+GPIOD_KEY_LEFT_N
++#define GPIO_KEY_DOWN_N 3*16+GPIOD_KEY_DOWN_N
++
++#define GPIO_KEY_UP_N 3*16+GPIOD_KEY_UP_N
++#define GPIO_SDIO_DET 3*16+GPIOD_SDIO_DET
++#define GPIO_WIFI_PWR2_ON 3*16+GPIOD_WIFI_PWR2_ON
++#define GPIO_HW_REBOOT 3*16+GPIOD_HW_REBOOT
++
++#define GPIO_BB_RESET1 3*16+GPIOD_BB_RESET1
++#define GPIO_UNKNOWND9 3*16+GPIOD_UNKNOWN9
++#define GPIO_VIBRA_PWR_ON 3*16+GPIOD_VIBRA_PWR_ON
++#define GPIO_WIFI_PWR3_ON 3*16+GPIOD_WIFI_PWR3_ON
++
++#define GPIO_FL_PWR_ON 3*16+GPIOD_FL_PWR_ON
++#define GPIO_LCD_PWR4_ON 3*16+GPIOD_LCD_PWR4_ON
++#define GPIO_BL_KEYP_PWR_ON 3*16+GPIOD_BL_KEYP_PWR_ON
++#define GPIO_BL_KEYB_PWR_ON 3*16+GPIOD_BL_KEYB_PWR_ON
++
++#define HTCUNIVERSAL_EGPIO_BASE PXA_CS2_PHYS+0x02000000
++
++#define EGPIO4_ON 4 /* something */
++#define EGPIO5_BT_3V3_ON 5 /* Bluetooth related */
++#define EGPIO6_WIFI_ON 6 /* WLAN related*/
++
++extern void htcuniversal_egpio_enable( u_int16_t bits );
++extern void htcuniversal_egpio_disable( u_int16_t bits );
++
++#endif /* _HTCUNIVERSAL_ASIC_H_ */
++
+Index: linux-2.6.26-rc4/include/asm-arm/arch-pxa/htcuniversal-gpio.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26-rc4/include/asm-arm/arch-pxa/htcuniversal-gpio.h 2008-06-01 18:49:45.000000000 +0100
+@@ -0,0 +1,220 @@
++/*
++ * include/asm-arm/arch-pxa/htcuniversal-gpio.h
++ * History:
++ *
++ * 2004-12-10 Michael Opdenacker. Wrote down GPIO settings as identified by Jamey Hicks.
++ * Reused the h2200-gpio.h file as a template.
++ */
++
++#ifndef _HTCUNIVERSAL_GPIO_H_
++#define _HTCUNIVERSAL_GPIO_H_
++
++#include <asm/arch/pxa-regs.h>
++
++#define GET_HTCUNIVERSAL_GPIO(gpio) \
++ (GPLR(GPIO_NR_HTCUNIVERSAL_ ## gpio) & GPIO_bit(GPIO_NR_HTCUNIVERSAL_ ## gpio))
++
++#define SET_HTCUNIVERSAL_GPIO(gpio, setp) \
++do { \
++if (setp) \
++ GPSR(GPIO_NR_HTCUNIVERSAL_ ## gpio) = GPIO_bit(GPIO_NR_HTCUNIVERSAL_ ## gpio); \
++else \
++ GPCR(GPIO_NR_HTCUNIVERSAL_ ## gpio) = GPIO_bit(GPIO_NR_HTCUNIVERSAL_ ## gpio); \
++} while (0)
++
++#define SET_HTCUNIVERSAL_GPIO_N(gpio, setp) \
++do { \
++if (setp) \
++ GPCR(GPIO_NR_HTCUNIVERSAL_ ## gpio ## _N) = GPIO_bit(GPIO_NR_HTCUNIVERSAL_ ## gpio ## _N); \
++else \
++ GPSR(GPIO_NR_HTCUNIVERSAL_ ## gpio ## _N) = GPIO_bit(GPIO_NR_HTCUNIVERSAL_ ## gpio ## _N); \
++} while (0)
++
++#define HTCUNIVERSAL_IRQ(gpio) \
++ IRQ_GPIO(GPIO_NR_HTCUNIVERSAL_ ## gpio)
++
++#define GPIO_NR_HTCUNIVERSAL_KEY_ON_N 0
++#define GPIO_NR_HTCUNIVERSAL_GP_RST_N 1
++
++#define GPIO_NR_HTCUNIVERSAL_USB_DET 9
++#define GPIO_NR_HTCUNIVERSAL_POWER_DET 10
++
++#define GPIO_NR_HTCUNIVERSAL_CIF_DD7 12
++#define GPIO_NR_HTCUNIVERSAL_ASIC3_SDIO_INT_N 13
++#define GPIO_NR_HTCUNIVERSAL_ASIC3_EXT_INT 14
++#define GPIO_NR_HTCUNIVERSAL_CS1_N 15
++
++#define GPIO_NR_HTCUNIVERSAL_CIF_DD6 17
++#define GPIO_NR_HTCUNIVERSAL_RDY 18
++
++#define GPIO_NR_HTCUNIVERSAL_PHONE_START 19
++
++#define GPIO_NR_HTCUNIVERSAL_KP_MKOUT7 22
++#define GPIO_NR_HTCUNIVERSAL_SPI_CLK 23
++#define GPIO_NR_HTCUNIVERSAL_SPI_FRM 24
++#define GPIO_NR_HTCUNIVERSAL_SPI_DO 25
++#define GPIO_NR_HTCUNIVERSAL_SPI_DI 26
++
++#define GPIO_NR_HTCUNIVERSAL_CODEC_ON 27
++#define GPIO_NR_HTCUNIVERSAL_I2S_BCK 28
++#define GPIO_NR_HTCUNIVERSAL_I2S_DIN 29
++#define GPIO_NR_HTCUNIVERSAL_I2S_DOUT 30
++#define GPIO_NR_HTCUNIVERSAL_I2S_SYNC 31
++
++#define GPIO_NR_HTCUNIVERSAL_RS232_ON 32
++#define GPIO_NR_HTCUNIVERSAL_CS5_N 33
++
++#define GPIO_NR_HTCUNIVERSAL_PHONE_RXD 34
++#define GPIO_NR_HTCUNIVERSAL_PHONE_UART_CTS 35
++#define GPIO_NR_HTCUNIVERSAL_KP_MKIN7 36
++#define GPIO_NR_HTCUNIVERSAL_KP_MKIN3 37
++#define GPIO_NR_HTCUNIVERSAL_KP_MKIN4 38
++#define GPIO_NR_HTCUNIVERSAL_PHONE_TXD 39
++#define GPIO_NR_HTCUNIVERSAL_KP_MKOUT6 40
++#define GPIO_NR_HTCUNIVERSAL_PHONE_UART_RTS 41
++#define GPIO_NR_HTCUNIVERSAL_BT_RXD 42
++#define GPIO_NR_HTCUNIVERSAL_BT_TXD 43
++#define GPIO_NR_HTCUNIVERSAL_BT_UART_CTS 44
++#define GPIO_NR_HTCUNIVERSAL_BT_UART_RTS 45
++
++#define GPIO_NR_HTCUNIVERSAL_SIR_RXD 42
++#define GPIO_NR_HTCUNIVERSAL_SIR_TXD 43
++
++#define GPIO_NR_HTCUNIVERSAL_POE_N 48
++#define GPIO_NR_HTCUNIVERSAL_PWE_N 49
++#define GPIO_NR_HTCUNIVERSAL_CIF_DD3 50
++#define GPIO_NR_HTCUNIVERSAL_CIF_DD2 51
++#define GPIO_NR_HTCUNIVERSAL_CIF_DD4 52
++
++#define GPIO_NR_HTCUNIVERSAL_CIF_MCLK 53
++#define GPIO_NR_HTCUNIVERSAL_CIF_PCLK 54
++#define GPIO_NR_HTCUNIVERSAL_CIF_DD1 55
++
++#define GPIO_NR_HTCUNIVERSAL_LDD0 58
++#define GPIO_NR_HTCUNIVERSAL_LDD1 59
++#define GPIO_NR_HTCUNIVERSAL_LDD2 60
++#define GPIO_NR_HTCUNIVERSAL_LDD3 61
++#define GPIO_NR_HTCUNIVERSAL_LDD4 62
++#define GPIO_NR_HTCUNIVERSAL_LDD5 63
++#define GPIO_NR_HTCUNIVERSAL_LDD6 64
++#define GPIO_NR_HTCUNIVERSAL_LDD7 65
++#define GPIO_NR_HTCUNIVERSAL_LDD8 66
++#define GPIO_NR_HTCUNIVERSAL_LDD9 67
++#define GPIO_NR_HTCUNIVERSAL_LDD10 68
++#define GPIO_NR_HTCUNIVERSAL_LDD11 69
++#define GPIO_NR_HTCUNIVERSAL_LDD12 70
++#define GPIO_NR_HTCUNIVERSAL_LDD13 71
++#define GPIO_NR_HTCUNIVERSAL_LDD14 72
++#define GPIO_NR_HTCUNIVERSAL_LDD15 73
++
++#define GPIO_NR_HTCUNIVERSAL_LFCLK_RD 74
++#define GPIO_NR_HTCUNIVERSAL_LFCLK_A0 75
++#define GPIO_NR_HTCUNIVERSAL_LFCLK_WR 76
++#define GPIO_NR_HTCUNIVERSAL_LBIAS 77
++
++#define GPIO_NR_HTCUNIVERSAL_CS2_N 78
++#define GPIO_NR_HTCUNIVERSAL_CS3_N 79
++#define GPIO_NR_HTCUNIVERSAL_CS4_N 80
++#define GPIO_NR_HTCUNIVERSAL_CIF_DD0 81
++#define GPIO_NR_HTCUNIVERSAL_CIF_DD5 82
++
++#define GPIO_NR_HTCUNIVERSAL_CIF_LV 84
++#define GPIO_NR_HTCUNIVERSAL_CIF_FV 85
++
++#define GPIO_NR_HTCUNIVERSAL_LCD1 86
++#define GPIO_NR_HTCUNIVERSAL_LCD2 87
++
++#define GPIO_NR_HTCUNIVERSAL_KP_MKIN5 90
++#define GPIO_NR_HTCUNIVERSAL_KP_MKIN6 91
++
++#define GPIO_NR_HTCUNIVERSAL_DREQ1 97
++
++#define GPIO_NR_HTCUNIVERSAL_PHONE_RESET 98
++
++#define GPIO_NR_HTCUNIVERSAL_KP_MKIN0 100
++#define GPIO_NR_HTCUNIVERSAL_KP_MKIN1 101
++#define GPIO_NR_HTCUNIVERSAL_KP_MKIN2 102
++#define GPIO_NR_HTCUNIVERSAL_KP_MKOUT0 103
++#define GPIO_NR_HTCUNIVERSAL_KP_MKOUT1 104
++#define GPIO_NR_HTCUNIVERSAL_KP_MKOUT2 105
++#define GPIO_NR_HTCUNIVERSAL_KP_MKOUT3 106
++#define GPIO_NR_HTCUNIVERSAL_KP_MKOUT4 107
++#define GPIO_NR_HTCUNIVERSAL_KP_MKOUT5 108
++
++#define GPIO_NR_HTCUNIVERSAL_PHONE_UNKNOWN 109
++#define GPIO_NR_HTCUNIVERSAL_PHONE_OFF 110
++
++#define GPIO_NR_HTCUNIVERSAL_USB_PUEN 112
++#define GPIO_NR_HTCUNIVERSAL_I2S_SYSCLK 113
++
++#define GPIO_NR_HTCUNIVERSAL_PWM_OUT1 115
++
++#define GPIO_NR_HTCUNIVERSAL_I2C_SCL 117
++#define GPIO_NR_HTCUNIVERSAL_I2C_SDA 118
++
++#if 0
++#define GPIO_NR_HTCUNIVERSAL_CPU_BATT_FAULT_N
++#define GPIO_NR_HTCUNIVERSAL_ASIC3_RESET_N
++#define GPIO_NR_HTCUNIVERSAL_CHARGE_EN_N
++#define GPIO_NR_HTCUNIVERSAL_FLASH_VPEN
++#define GPIO_NR_HTCUNIVERSAL_BATT_OFF
++#define GPIO_NR_HTCUNIVERSAL_USB_CHARGE_RATE
++#define GPIO_NR_HTCUNIVERSAL_BL_DETECT_N
++#define GPIO_NR_HTCUNIVERSAL_CPU_HW_RESET_N
++#endif
++
++
++#define GPIO_NR_HTCUNIVERSAL_TOUCHSCREEN_SPI_CLK_MD (23 | GPIO_ALT_FN_2_OUT)
++#define GPIO_NR_HTCUNIVERSAL_TOUCHSCREEN_SPI_FRM_MD (24 | GPIO_ALT_FN_2_OUT)
++#define GPIO_NR_HTCUNIVERSAL_TOUCHSCREEN_SPI_DO_MD (25 | GPIO_ALT_FN_2_OUT)
++#define GPIO_NR_HTCUNIVERSAL_TOUCHSCREEN_SPI_DI_MD (26 | GPIO_ALT_FN_1_IN)
++
++#define GPIO_NR_HTCUNIVERSAL_I2S_BCK_MD (28 | GPIO_ALT_FN_1_OUT)
++#define GPIO_NR_HTCUNIVERSAL_I2S_DIN_MD (29 | GPIO_ALT_FN_2_IN)
++#define GPIO_NR_HTCUNIVERSAL_I2S_DOUT_MD (30 | GPIO_ALT_FN_1_OUT)
++#define GPIO_NR_HTCUNIVERSAL_I2S_SYNC_MD (31 | GPIO_ALT_FN_1_OUT)
++
++#define GPIO_NR_HTCUNIVERSAL_PHONE_RXD_MD (34 | GPIO_ALT_FN_1_IN)
++#define GPIO_NR_HTCUNIVERSAL_PHONE_UART_CTS_MD (35 | GPIO_ALT_FN_1_IN)
++
++#define GPIO_NR_HTCUNIVERSAL_PHONE_TXD_MD (39 | GPIO_ALT_FN_2_OUT)
++#define GPIO_NR_HTCUNIVERSAL_PHONE_UART_RTS_MD (41 | GPIO_ALT_FN_2_OUT)
++
++#define GPIO_NR_HTCUNIVERSAL_BT_RXD_MD (42 | GPIO_ALT_FN_1_IN)
++#define GPIO_NR_HTCUNIVERSAL_BT_TXD_MD (43 | GPIO_ALT_FN_2_OUT)
++#define GPIO_NR_HTCUNIVERSAL_BT_UART_CTS_MD (44 | GPIO_ALT_FN_1_IN)
++#define GPIO_NR_HTCUNIVERSAL_BT_UART_RTS_MD (45 | GPIO_ALT_FN_2_OUT)
++
++#define GPIO_NR_HTCUNIVERSAL_SIR_RXD_MD (46 | GPIO_ALT_FN_2_IN)
++#define GPIO_NR_HTCUNIVERSAL_SIR_TXD_MD (47 | GPIO_ALT_FN_1_OUT)
++
++#define GPIO_NR_HTCUNIVERSAL_POE_N_MD (48 | GPIO_ALT_FN_2_OUT | GPIO_DFLT_HIGH)
++#define GPIO_NR_HTCUNIVERSAL_PWE_N_MD (49 | GPIO_ALT_FN_2_OUT | GPIO_DFLT_HIGH)
++
++#define GPIO_NR_HTCUNIVERSAL_KP_MKIN0_MD (GPIO_NR_HTCUNIVERSAL_KP_MKIN0 | GPIO_ALT_FN_1_IN)
++#define GPIO_NR_HTCUNIVERSAL_KP_MKIN1_MD (GPIO_NR_HTCUNIVERSAL_KP_MKIN1 | GPIO_ALT_FN_1_IN)
++#define GPIO_NR_HTCUNIVERSAL_KP_MKIN2_MD (GPIO_NR_HTCUNIVERSAL_KP_MKIN2 | GPIO_ALT_FN_1_IN)
++#define GPIO_NR_HTCUNIVERSAL_KP_MKIN3_MD (GPIO_NR_HTCUNIVERSAL_KP_MKIN3 | GPIO_ALT_FN_3_IN)
++#define GPIO_NR_HTCUNIVERSAL_KP_MKIN4_MD (GPIO_NR_HTCUNIVERSAL_KP_MKIN4 | GPIO_ALT_FN_2_IN)
++#define GPIO_NR_HTCUNIVERSAL_KP_MKIN5_MD (GPIO_NR_HTCUNIVERSAL_KP_MKIN5 | GPIO_ALT_FN_1_IN)
++#define GPIO_NR_HTCUNIVERSAL_KP_MKIN6_MD (GPIO_NR_HTCUNIVERSAL_KP_MKIN6 | GPIO_ALT_FN_1_IN)
++#define GPIO_NR_HTCUNIVERSAL_KP_MKIN7_MD (GPIO_NR_HTCUNIVERSAL_KP_MKIN7 | GPIO_ALT_FN_3_IN)
++
++#define GPIO_NR_HTCUNIVERSAL_KP_MKOUT0_MD (GPIO_NR_HTCUNIVERSAL_KP_MKOUT0 | GPIO_ALT_FN_2_OUT)
++#define GPIO_NR_HTCUNIVERSAL_KP_MKOUT1_MD (GPIO_NR_HTCUNIVERSAL_KP_MKOUT1 | GPIO_ALT_FN_2_OUT)
++#define GPIO_NR_HTCUNIVERSAL_KP_MKOUT2_MD (GPIO_NR_HTCUNIVERSAL_KP_MKOUT2 | GPIO_ALT_FN_2_OUT)
++#define GPIO_NR_HTCUNIVERSAL_KP_MKOUT3_MD (GPIO_NR_HTCUNIVERSAL_KP_MKOUT3 | GPIO_ALT_FN_2_OUT)
++#define GPIO_NR_HTCUNIVERSAL_KP_MKOUT4_MD (GPIO_NR_HTCUNIVERSAL_KP_MKOUT4 | GPIO_ALT_FN_2_OUT)
++#define GPIO_NR_HTCUNIVERSAL_KP_MKOUT5_MD (GPIO_NR_HTCUNIVERSAL_KP_MKOUT5 | GPIO_ALT_FN_2_OUT)
++#define GPIO_NR_HTCUNIVERSAL_KP_MKOUT6_MD (GPIO_NR_HTCUNIVERSAL_KP_MKOUT6 | GPIO_ALT_FN_1_OUT)
++#define GPIO_NR_HTCUNIVERSAL_KP_MKOUT7_MD (GPIO_NR_HTCUNIVERSAL_KP_MKOUT7 | GPIO_ALT_FN_1_OUT)
++
++
++#define GPIO_NR_HTCUNIVERSAL_I2S_SYSCLK_MD (113 | GPIO_ALT_FN_1_OUT)
++
++#define GPIO_NR_HTCUNIVERSAL_PWM1OUT_MD (115 | GPIO_ALT_FN_3_OUT)
++
++#define GPIO_NR_HTCUNIVERSAL_I2C_SCL_MD (117 | GPIO_ALT_FN_1_OUT)
++#define GPIO_NR_HTCUNIVERSAL_I2C_SDA_MD (118 | GPIO_ALT_FN_1_OUT)
++
++#endif /* _HTCUNIVERSAL_GPIO_H */
+Index: linux-2.6.26-rc4/include/asm-arm/arch-pxa/htcuniversal-init.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26-rc4/include/asm-arm/arch-pxa/htcuniversal-init.h 2008-06-01 18:49:45.000000000 +0100
+@@ -0,0 +1,14 @@
++/*
++ * include/asm/arm/arch-pxa/htcuniversal-init.h
++ * Copyright (c) 2004 SDG Systems, LLC
++ */
++
++#ifndef _HTCUNIVERSAL_INIT_H_
++#define _HTCUNIVERSAL_INIT_H_
++
++/* htcuniversal initialization data should be found here
++ * See -init.h files from other devices for details
++ */
++
++#endif /* _HTCUNIVERSAL_INIT_H_ */
++
+Index: linux-2.6.26-rc4/include/asm-arm/arch-pxa/htcuniversal.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26-rc4/include/asm-arm/arch-pxa/htcuniversal.h 2008-06-01 18:49:45.000000000 +0100
+@@ -0,0 +1,3 @@
++#include <asm/arch/irqs.h>
++
++#define HTCUNIVERSAL_ASIC3_IRQ_BASE IRQ_BOARD_START
+Index: linux-2.6.26-rc4/include/asm-arm/arch-pxa/pxa-pm_ll.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26-rc4/include/asm-arm/arch-pxa/pxa-pm_ll.h 2008-06-01 18:49:45.000000000 +0100
+@@ -0,0 +1,6 @@
++struct pxa_ll_pm_ops {
++ void (*suspend)(unsigned long);
++ void (*resume)(void);
++};
++
++extern struct pxa_ll_pm_ops *pxa_pm_set_ll_ops(struct pxa_ll_pm_ops *new_ops);
+Index: linux-2.6.26-rc4/include/asm-arm/hardware/asic3_keys.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26-rc4/include/asm-arm/hardware/asic3_keys.h 2008-06-01 18:49:45.000000000 +0100
+@@ -0,0 +1,18 @@
++#include <linux/input.h>
++
++struct asic3_keys_button {
++ /* Configuration parameters */
++ int keycode;
++ int gpio;
++ int active_low;
++ char *desc;
++ int type;
++ /* Internal state vars - add below */
++};
++
++struct asic3_keys_platform_data {
++ struct asic3_keys_button *buttons;
++ int nbuttons;
++ struct input_dev *input;
++ struct device *asic3_dev;
++};
+Index: linux-2.6.26-rc4/include/asm-arm/hardware/asic3_leds.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26-rc4/include/asm-arm/hardware/asic3_leds.h 2008-06-01 18:49:45.000000000 +0100
+@@ -0,0 +1,34 @@
++/*
++ * LEDs support for HTC ASIC3 devices.
++ *
++ * Copyright (c) 2006 Anton Vorontsov <cbou@mail.ru>
++ *
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License. See the file COPYING in the main directory of this archive for
++ * more details.
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/platform_device.h>
++#include <linux/leds.h>
++
++struct asic3_leds_machinfo;
++
++struct asic3_led {
++ struct led_classdev led_cdev;
++ int hw_num; /* Number of "hardware-accelerated" led */
++ int gpio_num; /* Number of GPIO if hw_num == -1 */
++ struct asic3_leds_machinfo *machinfo;
++};
++
++struct asic3_leds_machinfo {
++ int num_leds;
++ struct asic3_led *leds;
++ struct platform_device *asic3_pdev;
++};
++
++extern int asic3_leds_register(void);
++extern void asic3_leds_unregister(void);
++
+Index: linux-2.6.26-rc4/include/asm-arm/hardware/ipaq-asic3.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26-rc4/include/asm-arm/hardware/ipaq-asic3.h 2008-06-01 18:49:45.000000000 +0100
+@@ -0,0 +1,602 @@
++/*
++ *
++ * Definitions for the HTC ASIC3 chip found in several handheld devices
++ *
++ * Copyright 2001 Compaq Computer Corporation.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * COMPAQ COMPUTER CORPORATION MAKES NO WARRANTIES, EXPRESSED OR IMPLIED,
++ * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS
++ * FITNESS FOR ANY PARTICULAR PURPOSE.
++ *
++ * Author: Andrew Christian
++ *
++ */
++
++#ifndef IPAQ_ASIC3_H
++#define IPAQ_ASIC3_H
++
++/****************************************************/
++/* IPAQ, ASIC #3, replaces ASIC #1 */
++
++#define IPAQ_ASIC3_OFFSET(x,y) (_IPAQ_ASIC3_ ## x ## _Base + _IPAQ_ASIC3_ ## x ## _ ## y)
++#define IPAQ_ASIC3_GPIO_OFFSET(x,y) (_IPAQ_ASIC3_GPIO_ ## x ## _Base + _IPAQ_ASIC3_GPIO_ ## y)
++
++
++/* All offsets below are specified with the following address bus shift */
++#define ASIC3_DEFAULT_ADDR_SHIFT 2
++
++#define _IPAQ_ASIC3_GPIO_A_Base 0x0000
++#define _IPAQ_ASIC3_GPIO_B_Base 0x0100
++#define _IPAQ_ASIC3_GPIO_C_Base 0x0200
++#define _IPAQ_ASIC3_GPIO_D_Base 0x0300
++
++#define _IPAQ_ASIC3_GPIO_Mask 0x00 /* R/W 0:don't mask, 1:mask interrupt */
++#define _IPAQ_ASIC3_GPIO_Direction 0x04 /* R/W 0:input, 1:output */
++#define _IPAQ_ASIC3_GPIO_Out 0x08 /* R/W 0:output low, 1:output high */
++#define _IPAQ_ASIC3_GPIO_TriggerType 0x0c /* R/W 0:level, 1:edge */
++#define _IPAQ_ASIC3_GPIO_EdgeTrigger 0x10 /* R/W 0:falling, 1:rising */
++#define _IPAQ_ASIC3_GPIO_LevelTrigger 0x14 /* R/W 0:low, 1:high level detect */
++#define _IPAQ_ASIC3_GPIO_SleepMask 0x18 /* R/W 0:don't mask, 1:mask trigger in sleep mode */
++#define _IPAQ_ASIC3_GPIO_SleepOut 0x1c /* R/W level 0:low, 1:high in sleep mode */
++#define _IPAQ_ASIC3_GPIO_BattFaultOut 0x20 /* R/W level 0:low, 1:high in batt_fault */
++#define _IPAQ_ASIC3_GPIO_IntStatus 0x24 /* R/W 0:none, 1:detect */
++#define _IPAQ_ASIC3_GPIO_AltFunction 0x28 /* R/W 0:normal control 1:LED register control */
++#define _IPAQ_ASIC3_GPIO_SleepConf 0x2c /* R/W bit 1: autosleep 0: disable gposlpout in normal mode, enable gposlpout in sleep mode */
++#define _IPAQ_ASIC3_GPIO_Status 0x30 /* R Pin status */
++
++#define IPAQ_ASIC3_GPIO_A_MASK(_b) IPAQ_ASIC3_GPIO( _b, u16, A, Mask )
++#define IPAQ_ASIC3_GPIO_A_DIR(_b) IPAQ_ASIC3_GPIO( _b, u16, A, Direction )
++#define IPAQ_ASIC3_GPIO_A_OUT(_b) IPAQ_ASIC3_GPIO( _b, u16, A, Out )
++#define IPAQ_ASIC3_GPIO_A_LEVELTRI(_b) IPAQ_ASIC3_GPIO( _b, u16, A, TriggerType )
++#define IPAQ_ASIC3_GPIO_A_RISING(_b) IPAQ_ASIC3_GPIO( _b, u16, A, EdgeTrigger )
++#define IPAQ_ASIC3_GPIO_A_LEVEL(_b) IPAQ_ASIC3_GPIO( _b, u16, A, LevelTrigger )
++#define IPAQ_ASIC3_GPIO_A_SLEEP_MASK(_b) IPAQ_ASIC3_GPIO( _b, u16, A, SleepMask )
++#define IPAQ_ASIC3_GPIO_A_SLEEP_OUT(_b) IPAQ_ASIC3_GPIO( _b, u16, A, SleepOut )
++#define IPAQ_ASIC3_GPIO_A_BATT_FAULT_OUT(_b) IPAQ_ASIC3_GPIO( _b, u16, A, BattFaultOut )
++#define IPAQ_ASIC3_GPIO_A_INT_STATUS(_b) IPAQ_ASIC3_GPIO( _b, u16, A, IntStatus )
++#define IPAQ_ASIC3_GPIO_A_ALT_FUNCTION(_b) IPAQ_ASIC3_GPIO( _b, u16, A, AltFunction )
++#define IPAQ_ASIC3_GPIO_A_SLEEP_CONF(_b) IPAQ_ASIC3_GPIO( _b, u16, A, SleepConf )
++#define IPAQ_ASIC3_GPIO_A_STATUS(_b) IPAQ_ASIC3_GPIO( _b, u16, A, Status )
++
++#define IPAQ_ASIC3_GPIO_B_MASK(_b) IPAQ_ASIC3_GPIO( _b, u16, B, Mask )
++#define IPAQ_ASIC3_GPIO_B_DIR(_b) IPAQ_ASIC3_GPIO( _b, u16, B, Direction )
++#define IPAQ_ASIC3_GPIO_B_OUT(_b) IPAQ_ASIC3_GPIO( _b, u16, B, Out )
++#define IPAQ_ASIC3_GPIO_B_LEVELTRI(_b) IPAQ_ASIC3_GPIO( _b, u16, B, TriggerType )
++#define IPAQ_ASIC3_GPIO_B_RISING(_b) IPAQ_ASIC3_GPIO( _b, u16, B, EdgeTrigger )
++#define IPAQ_ASIC3_GPIO_B_LEVEL(_b) IPAQ_ASIC3_GPIO( _b, u16, B, LevelTrigger )
++#define IPAQ_ASIC3_GPIO_B_SLEEP_MASK(_b) IPAQ_ASIC3_GPIO( _b, u16, B, SleepMask )
++#define IPAQ_ASIC3_GPIO_B_SLEEP_OUT(_b) IPAQ_ASIC3_GPIO( _b, u16, B, SleepOut )
++#define IPAQ_ASIC3_GPIO_B_BATT_FAULT_OUT(_b) IPAQ_ASIC3_GPIO( _b, u16, B, BattFaultOut )
++#define IPAQ_ASIC3_GPIO_B_INT_STATUS(_b) IPAQ_ASIC3_GPIO( _b, u16, B, IntStatus )
++#define IPAQ_ASIC3_GPIO_B_ALT_FUNCTION(_b) IPAQ_ASIC3_GPIO( _b, u16, B, AltFunction )
++#define IPAQ_ASIC3_GPIO_B_SLEEP_CONF(_b) IPAQ_ASIC3_GPIO( _b, u16, B, SleepConf )
++#define IPAQ_ASIC3_GPIO_B_STATUS(_b) IPAQ_ASIC3_GPIO( _b, u16, B, Status )
++
++#define IPAQ_ASIC3_GPIO_C_MASK(_b) IPAQ_ASIC3_GPIO( _b, u16, C, Mask )
++#define IPAQ_ASIC3_GPIO_C_DIR(_b) IPAQ_ASIC3_GPIO( _b, u16, C, Direction )
++#define IPAQ_ASIC3_GPIO_C_OUT(_b) IPAQ_ASIC3_GPIO( _b, u16, C, Out )
++#define IPAQ_ASIC3_GPIO_C_LEVELTRI(_b) IPAQ_ASIC3_GPIO( _b, u16, C, TriggerType )
++#define IPAQ_ASIC3_GPIO_C_RISING(_b) IPAQ_ASIC3_GPIO( _b, u16, C, EdgeTrigger )
++#define IPAQ_ASIC3_GPIO_C_LEVEL(_b) IPAQ_ASIC3_GPIO( _b, u16, C, LevelTrigger )
++#define IPAQ_ASIC3_GPIO_C_SLEEP_MASK(_b) IPAQ_ASIC3_GPIO( _b, u16, C, SleepMask )
++#define IPAQ_ASIC3_GPIO_C_SLEEP_OUT(_b) IPAQ_ASIC3_GPIO( _b, u16, C, SleepOut )
++#define IPAQ_ASIC3_GPIO_C_BATT_FAULT_OUT(_b) IPAQ_ASIC3_GPIO( _b, u16, C, BattFaultOut )
++#define IPAQ_ASIC3_GPIO_C_INT_STATUS(_b) IPAQ_ASIC3_GPIO( _b, u16, C, IntStatus )
++#define IPAQ_ASIC3_GPIO_C_ALT_FUNCTION(_b) IPAQ_ASIC3_GPIO( _b, u16, C, AltFunction )
++#define IPAQ_ASIC3_GPIO_C_SLEEP_CONF(_b) IPAQ_ASIC3_GPIO( _b, u16, C, SleepConf )
++#define IPAQ_ASIC3_GPIO_C_STATUS(_b) IPAQ_ASIC3_GPIO( _b, u16, C, Status )
++
++#define IPAQ_ASIC3_GPIO_D_MASK(_b) IPAQ_ASIC3_GPIO( _b, u16, D, Mask )
++#define IPAQ_ASIC3_GPIO_D_DIR(_b) IPAQ_ASIC3_GPIO( _b, u16, D, Direction )
++#define IPAQ_ASIC3_GPIO_D_OUT(_b) IPAQ_ASIC3_GPIO( _b, u16, D, Out )
++#define IPAQ_ASIC3_GPIO_D_LEVELTRI(_b) IPAQ_ASIC3_GPIO( _b, u16, D, TriggerType )
++#define IPAQ_ASIC3_GPIO_D_RISING(_b) IPAQ_ASIC3_GPIO( _b, u16, D, EdgeTrigger )
++#define IPAQ_ASIC3_GPIO_D_LEVEL(_b) IPAQ_ASIC3_GPIO( _b, u16, D, LevelTrigger )
++#define IPAQ_ASIC3_GPIO_D_SLEEP_MASK(_b) IPAQ_ASIC3_GPIO( _b, u16, D, SleepMask )
++#define IPAQ_ASIC3_GPIO_D_SLEEP_OUT(_b) IPAQ_ASIC3_GPIO( _b, u16, D, SleepOut )
++#define IPAQ_ASIC3_GPIO_D_BATT_FAULT_OUT(_b) IPAQ_ASIC3_GPIO( _b, u16, D, BattFaultOut )
++#define IPAQ_ASIC3_GPIO_D_INT_STATUS(_b) IPAQ_ASIC3_GPIO( _b, u16, D, IntStatus )
++#define IPAQ_ASIC3_GPIO_D_ALT_FUNCTION(_b) IPAQ_ASIC3_GPIO( _b, u16, D, AltFunction )
++#define IPAQ_ASIC3_GPIO_D_SLEEP_CONF(_b) IPAQ_ASIC3_GPIO( _b, u16, D, SleepConf )
++#define IPAQ_ASIC3_GPIO_D_STATUS(_b) IPAQ_ASIC3_GPIO( _b, u16, D, Status )
++
++#define _IPAQ_ASIC3_SPI_Base 0x0400
++#define _IPAQ_ASIC3_SPI_Control 0x0000
++#define _IPAQ_ASIC3_SPI_TxData 0x0004
++#define _IPAQ_ASIC3_SPI_RxData 0x0008
++#define _IPAQ_ASIC3_SPI_Int 0x000c
++#define _IPAQ_ASIC3_SPI_Status 0x0010
++
++#define IPAQ_ASIC3_SPI_Control(_b) IPAQ_ASIC3( _b, u16, SPI, Control )
++#define IPAQ_ASIC3_SPI_TxData(_b) IPAQ_ASIC3( _b, u16, SPI, TxData )
++#define IPAQ_ASIC3_SPI_RxData(_b) IPAQ_ASIC3( _b, u16, SPI, RxData )
++#define IPAQ_ASIC3_SPI_Int(_b) IPAQ_ASIC3( _b, u16, SPI, Int )
++#define IPAQ_ASIC3_SPI_Status(_b) IPAQ_ASIC3( _b, u16, SPI, Status )
++
++#define SPI_CONTROL_SPR(clk) ((clk) & 0x0f) /* Clock rate */
++
++#define _IPAQ_ASIC3_PWM_0_Base 0x0500
++#define _IPAQ_ASIC3_PWM_1_Base 0x0600
++#define _IPAQ_ASIC3_PWM_TimeBase 0x0000
++#define _IPAQ_ASIC3_PWM_PeriodTime 0x0004
++#define _IPAQ_ASIC3_PWM_DutyTime 0x0008
++
++#define IPAQ_ASIC3_PWM_TimeBase(_b, x) IPAQ_ASIC3_N( _b, u16, PWM, x, TimeBase )
++#define IPAQ_ASIC3_PWM_PeriodTime(_b, x) IPAQ_ASIC3_N( _b, u16, PWM, x, PeriodTime )
++#define IPAQ_ASIC3_PWM_DutyTime(_b, x) IPAQ_ASIC3_N( _b, u16, PWM, x, DutyTime )
++
++#define PWM_TIMEBASE_VALUE(x) ((x)&0xf) /* Low 4 bits sets time base */
++#define PWM_TIMEBASE_ENABLE (1 << 4) /* Enable clock */
++
++#define _IPAQ_ASIC3_LED_0_Base 0x0700
++#define _IPAQ_ASIC3_LED_1_Base 0x0800
++#define _IPAQ_ASIC3_LED_2_Base 0x0900
++#define _IPAQ_ASIC3_LED_TimeBase 0x0000 /* R/W 7 bits */
++#define _IPAQ_ASIC3_LED_PeriodTime 0x0004 /* R/W 12 bits */
++#define _IPAQ_ASIC3_LED_DutyTime 0x0008 /* R/W 12 bits */
++#define _IPAQ_ASIC3_LED_AutoStopCount 0x000c /* R/W 16 bits */
++
++#define IPAQ_ASIC3_LED_TimeBase(_b, x) IPAQ_ASIC3_N( _b, u8, LED, x, TimeBase )
++#define IPAQ_ASIC3_LED_PeriodTime(_b, x) IPAQ_ASIC3_N( _b, u16, LED, x, PeriodTime )
++#define IPAQ_ASIC3_LED_DutyTime(_b, x) IPAQ_ASIC3_N( _b, u16, LED, x, DutyTime )
++#define IPAQ_ASIC3_LED_AutoStopCount(_b, x) IPAQ_ASIC3_N( _b, u16, LED, x, AutoStopCount )
++
++/* LED TimeBase bits - match ASIC2 */
++#define LED_TBS 0x0f /* Low 4 bits sets time base, max = 13 */
++ /* Note: max = 5 on hx4700 */
++ /* 0: maximum time base */
++ /* 1: maximum time base / 2 */
++ /* n: maximum time base / 2^n */
++
++#define LED_EN (1 << 4) /* LED ON/OFF 0:off, 1:on */
++#define LED_AUTOSTOP (1 << 5) /* LED ON/OFF auto stop set 0:disable, 1:enable */
++#define LED_ALWAYS (1 << 6) /* LED Interrupt Mask 0:No mask, 1:mask */
++
++#define _IPAQ_ASIC3_CLOCK_Base 0x0A00
++#define _IPAQ_ASIC3_CLOCK_CDEX 0x00
++#define _IPAQ_ASIC3_CLOCK_SEL 0x04
++
++#define IPAQ_ASIC3_CLOCK_CDEX(_b) IPAQ_ASIC3( _b, u16, CLOCK, CDEX )
++#define IPAQ_ASIC3_CLOCK_SEL(_b) IPAQ_ASIC3( _b, u16, CLOCK, SEL )
++
++#define CLOCK_CDEX_SOURCE (1 << 0) /* 2 bits */
++#define CLOCK_CDEX_SOURCE0 (1 << 0)
++#define CLOCK_CDEX_SOURCE1 (1 << 1)
++#define CLOCK_CDEX_SPI (1 << 2)
++#define CLOCK_CDEX_OWM (1 << 3)
++#define CLOCK_CDEX_PWM0 (1 << 4)
++#define CLOCK_CDEX_PWM1 (1 << 5)
++#define CLOCK_CDEX_LED0 (1 << 6)
++#define CLOCK_CDEX_LED1 (1 << 7)
++#define CLOCK_CDEX_LED2 (1 << 8)
++
++#define CLOCK_CDEX_SD_HOST (1 << 9) /* R/W: SD host clock source 24.576M/12.288M */
++#define CLOCK_CDEX_SD_BUS (1 << 10) /* R/W: SD bus clock source control 24.576M/12.288M */
++#define CLOCK_CDEX_SMBUS (1 << 11)
++#define CLOCK_CDEX_CONTROL_CX (1 << 12)
++
++#define CLOCK_CDEX_EX0 (1 << 13) /* R/W: 32.768 kHz crystal */
++#define CLOCK_CDEX_EX1 (1 << 14) /* R/W: 24.576 MHz crystal */
++
++#define CLOCK_SEL_SD_HCLK_SEL (1 << 0) /* R/W: SDIO host clock select - 1: 24.576 Mhz, 0: 12.288 MHz */
++#define CLOCK_SEL_SD_BCLK_SEL (1 << 1) /* R/W: SDIO bus clock select - 1: 24.576 MHz, 0: 12.288 MHz */
++#define CLOCK_SEL_CX (1 << 2) /* R/W: INT clock source control (32.768 kHz) */
++
++
++#define _IPAQ_ASIC3_INTR_Base 0x0B00
++
++#define _IPAQ_ASIC3_INTR_IntMask 0x00 /* Interrupt mask control */
++#define _IPAQ_ASIC3_INTR_PIntStat 0x04 /* Peripheral interrupt status */
++#define _IPAQ_ASIC3_INTR_IntCPS 0x08 /* Interrupt timer clock pre-scale */
++#define _IPAQ_ASIC3_INTR_IntTBS 0x0c /* Interrupt timer set */
++
++#define IPAQ_ASIC3_INTR_IntMask(_b) IPAQ_ASIC3( _b, u8, INTR, IntMask )
++#define IPAQ_ASIC3_INTR_PIntStat(_b) IPAQ_ASIC3( _b, u8, INTR, PIntStat )
++#define IPAQ_ASIC3_INTR_IntCPS(_b) IPAQ_ASIC3( _b, u8, INTR, IntCPS )
++#define IPAQ_ASIC3_INTR_IntTBS(_b) IPAQ_ASIC3( _b, u16, INTR, IntTBS )
++
++#define ASIC3_INTMASK_GINTMASK (1 << 0) /* Global interrupt mask 1:enable */
++#define ASIC3_INTMASK_GINTEL (1 << 1) /* 1: rising edge, 0: hi level */
++#define ASIC3_INTMASK_MASK0 (1 << 2)
++#define ASIC3_INTMASK_MASK1 (1 << 3)
++#define ASIC3_INTMASK_MASK2 (1 << 4)
++#define ASIC3_INTMASK_MASK3 (1 << 5)
++#define ASIC3_INTMASK_MASK4 (1 << 6)
++#define ASIC3_INTMASK_MASK5 (1 << 7)
++
++#define ASIC3_INTR_PERIPHERAL_A (1 << 0)
++#define ASIC3_INTR_PERIPHERAL_B (1 << 1)
++#define ASIC3_INTR_PERIPHERAL_C (1 << 2)
++#define ASIC3_INTR_PERIPHERAL_D (1 << 3)
++#define ASIC3_INTR_LED0 (1 << 4)
++#define ASIC3_INTR_LED1 (1 << 5)
++#define ASIC3_INTR_LED2 (1 << 6)
++#define ASIC3_INTR_SPI (1 << 7)
++#define ASIC3_INTR_SMBUS (1 << 8)
++#define ASIC3_INTR_OWM (1 << 9)
++
++#define ASIC3_INTR_CPS(x) ((x)&0x0f) /* 4 bits, max 14 */
++#define ASIC3_INTR_CPS_SET ( 1 << 4 ) /* Time base enable */
++
++
++/* Basic control of the SD ASIC */
++#define _IPAQ_ASIC3_SDHWCTRL_Base 0x0E00
++
++#define _IPAQ_ASIC3_SDHWCTRL_SDConf 0x00
++#define IPAQ_ASIC3_SDHWCTRL_SDConf(_b) IPAQ_ASIC3( _b, u8, SDHWCTRL, SDConf )
++
++#define ASIC3_SDHWCTRL_SUSPEND (1 << 0) /* 1=suspend all SD operations */
++#define ASIC3_SDHWCTRL_CLKSEL (1 << 1) /* 1=SDICK, 0=HCLK */
++#define ASIC3_SDHWCTRL_PCLR (1 << 2) /* All registers of SDIO cleared */
++#define ASIC3_SDHWCTRL_LEVCD (1 << 3) /* Level of SD card detection: 1:high, 0:low */
++#define ASIC3_SDHWCTRL_LEVWP (1 << 4) /* Level of SD card write protection: 1=low, 0=high */
++#define ASIC3_SDHWCTRL_SDLED (1 << 5) /* SD card LED signal 1=enable, 0=disable */
++#define ASIC3_SDHWCTRL_SDPWR (1 << 6) /* SD card power supply control 1=enable */
++
++
++/* This is a pointer to an array of 12 u32 values - but only the lower 2 bytes matter */
++/* Use it as "IPAQ_ASIC3_HWPROTECT_ARRAY[x]" */
++
++#define _IPAQ_ASIC3_HWPROTECT_Base 0x1000
++#define IPAQ_ASIC3_HWPROTECT_ARRAY ((volatile u32*)(_IPAQ_ASIC3_Base + _IPAQ_ASIC3_HWPROTECT_Base))
++#define HWPROTECT_ARRAY_LEN 12
++#define HWPROTECT_ARRAY_VALUES {0x4854,0x432d,0x5344,0x494f,0x2050,0x2f4e,0x3a33,0x3048,0x3830,0x3032,0x382d,0x3030}
++
++
++#define _IPAQ_ASIC3_EXTCF_Base 0x1100
++
++#define _IPAQ_ASIC3_EXTCF_Select 0x00
++#define _IPAQ_ASIC3_EXTCF_Reset 0x04
++
++#define IPAQ_ASIC3_EXTCF_Select(_b) IPAQ_ASIC3( _b, u16, EXTCF, Select )
++#define IPAQ_ASIC3_EXTCF_Reset(_b) IPAQ_ASIC3( _b, u16, EXTCF, Reset )
++
++#define ASIC3_EXTCF_SMOD0 (1 << 0) /* slot number of mode 0 */
++#define ASIC3_EXTCF_SMOD1 (1 << 1) /* slot number of mode 1 */
++#define ASIC3_EXTCF_SMOD2 (1 << 2) /* slot number of mode 2 */
++#define ASIC3_EXTCF_OWM_EN (1 << 4) /* enable onewire module */
++#define ASIC3_EXTCF_OWM_SMB (1 << 5) /* OWM bus selection */
++#define ASIC3_EXTCF_OWM_RESET (1 << 6) /* undocumented, used by OWM and CF */
++#define ASIC3_EXTCF_CF0_SLEEP_MODE (1 << 7) /* CF0 sleep state control */
++#define ASIC3_EXTCF_CF1_SLEEP_MODE (1 << 8) /* CF1 sleep state control */
++#define ASIC3_EXTCF_CF0_PWAIT_EN (1 << 10) /* CF0 PWAIT_n control */
++#define ASIC3_EXTCF_CF1_PWAIT_EN (1 << 11) /* CF1 PWAIT_n control */
++#define ASIC3_EXTCF_CF0_BUF_EN (1 << 12) /* CF0 buffer control */
++#define ASIC3_EXTCF_CF1_BUF_EN (1 << 13) /* CF1 buffer control */
++#define ASIC3_EXTCF_SD_MEM_ENABLE (1 << 14)
++#define ASIC3_EXTCF_CF_SLEEP (1 << 15) /* CF sleep mode control */
++
++/*****************************************************************************
++ * The Onewire interface registers
++ *
++ * OWM_CMD
++ * OWM_DAT
++ * OWM_INTR
++ * OWM_INTEN
++ * OWM_CLKDIV
++ *
++ *****************************************************************************/
++
++#define _IPAQ_ASIC3_OWM_Base 0xC00
++
++#define _IPAQ_ASIC3_OWM_CMD 0x00
++#define _IPAQ_ASIC3_OWM_DAT 0x04
++#define _IPAQ_ASIC3_OWM_INTR 0x08
++#define _IPAQ_ASIC3_OWM_INTEN 0x0C
++#define _IPAQ_ASIC3_OWM_CLKDIV 0x10
++
++#define ASIC3_OWM_CMD_ONEWR (1 << 0)
++#define ASIC3_OWM_CMD_SRA (1 << 1)
++#define ASIC3_OWM_CMD_DQO (1 << 2)
++#define ASIC3_OWM_CMD_DQI (1 << 3)
++
++#define ASIC3_OWM_INTR_PD (1 << 0)
++#define ASIC3_OWM_INTR_PDR (1 << 1)
++#define ASIC3_OWM_INTR_TBE (1 << 2)
++#define ASIC3_OWM_INTR_TEMP (1 << 3)
++#define ASIC3_OWM_INTR_RBF (1 << 4)
++
++#define ASIC3_OWM_INTEN_EPD (1 << 0)
++#define ASIC3_OWM_INTEN_IAS (1 << 1)
++#define ASIC3_OWM_INTEN_ETBE (1 << 2)
++#define ASIC3_OWM_INTEN_ETMT (1 << 3)
++#define ASIC3_OWM_INTEN_ERBF (1 << 4)
++
++#define ASIC3_OWM_CLKDIV_PRE (3 << 0) /* two bits wide at bit position 0 */
++#define ASIC3_OWM_CLKDIV_DIV (7 << 2) /* 3 bits wide at bit position 2 */
++
++
++/*****************************************************************************
++ * The SD configuration registers are at a completely different location
++ * in memory. They are divided into three sets of registers:
++ *
++ * SD_CONFIG Core configuration register
++ * SD_CTRL Control registers for SD operations
++ * SDIO_CTRL Control registers for SDIO operations
++ *
++ *****************************************************************************/
++
++#define IPAQ_ASIC3_SD_CONFIG(_b, s,x) \
++ (*((volatile s *) ((_b) + _IPAQ_ASIC3_SD_CONFIG_Base + (_IPAQ_ASIC3_SD_CONFIG_ ## x))))
++
++#define _IPAQ_ASIC3_SD_CONFIG_Base 0x0400 // Assumes 32 bit addressing
++
++#define _IPAQ_ASIC3_SD_CONFIG_Command 0x08 /* R/W: Command */
++#define _IPAQ_ASIC3_SD_CONFIG_Addr0 0x20 /* [9:31] SD Control Register Base Address */
++#define _IPAQ_ASIC3_SD_CONFIG_Addr1 0x24 /* [9:31] SD Control Register Base Address */
++#define _IPAQ_ASIC3_SD_CONFIG_IntPin 0x78 /* R/O: interrupt assigned to pin */
++#define _IPAQ_ASIC3_SD_CONFIG_ClkStop 0x80 /* Set to 0x1f to clock SD controller, 0 otherwise. */
++ /* at 0x82 - Gated Clock Control */
++#define _IPAQ_ASIC3_SD_CONFIG_ClockMode 0x84 /* Control clock of SD controller */
++#define _IPAQ_ASIC3_SD_CONFIG_SDHC_PinStatus 0x88 /* R/0: read status of SD pins */
++#define _IPAQ_ASIC3_SD_CONFIG_SDHC_Power1 0x90 /* Power1 - manual power control */
++ /* Power2 is at 0x92 - auto power up after card inserted */
++#define _IPAQ_ASIC3_SD_CONFIG_SDHC_Power3 0x94 /* auto power down when card removed */
++#define _IPAQ_ASIC3_SD_CONFIG_SDHC_CardDetect 0x98 /* */
++#define _IPAQ_ASIC3_SD_CONFIG_SDHC_Slot 0xA0 /* R/O: define support slot number */
++#define _IPAQ_ASIC3_SD_CONFIG_SDHC_ExtGateClk1 0x1E0 /* Could be used for gated clock (don't use) */
++#define _IPAQ_ASIC3_SD_CONFIG_SDHC_ExtGateClk2 0x1E2 /* Could be used for gated clock (don't use) */
++#define _IPAQ_ASIC3_SD_CONFIG_SDHC_GPIO_OutAndEnable 0x1E8 /* GPIO Output Reg. , at 0x1EA - GPIO Output Enable Reg. */
++#define _IPAQ_ASIC3_SD_CONFIG_SDHC_GPIO_Status 0x1EC /* GPIO Status Reg. */
++#define _IPAQ_ASIC3_SD_CONFIG_SDHC_ExtGateClk3 0x1F0 /* Bit 1: double buffer/single buffer */
++
++#define IPAQ_ASIC3_SD_CONFIG_Command(_b) IPAQ_ASIC3_SD_CONFIG(_b, u16, Command )
++#define IPAQ_ASIC3_SD_CONFIG_Addr0(_b) IPAQ_ASIC3_SD_CONFIG(_b, u16, Addr0 )
++#define IPAQ_ASIC3_SD_CONFIG_Addr1(_b) IPAQ_ASIC3_SD_CONFIG(_b, u16, Addr1 )
++#define IPAQ_ASIC3_SD_CONFIG_IntPin(_b) IPAQ_ASIC3_SD_CONFIG(_b, u8, IntPin )
++#define IPAQ_ASIC3_SD_CONFIG_ClkStop(_b) IPAQ_ASIC3_SD_CONFIG(_b, u8, ClkStop )
++#define IPAQ_ASIC3_SD_CONFIG_ClockMode(_b) IPAQ_ASIC3_SD_CONFIG(_b, u8, ClockMode )
++#define IPAQ_ASIC3_SD_CONFIG_SDHC_PinStatus(_b) IPAQ_ASIC3_SD_CONFIG(_b, u16, SDHC_PinStatus )
++#define IPAQ_ASIC3_SD_CONFIG_SDHC_Power1(_b) IPAQ_ASIC3_SD_CONFIG(_b, u16, SDHC_Power1 )
++#define IPAQ_ASIC3_SD_CONFIG_SDHC_Power3(_b) IPAQ_ASIC3_SD_CONFIG(_b, u16, SDHC_Power3 )
++#define IPAQ_ASIC3_SD_CONFIG_SDHC_CardDetect(_b) IPAQ_ASIC3_SD_CONFIG(_b, u16, SDHC_CardDetect )
++#define IPAQ_ASIC3_SD_CONFIG_SDHC_Slot(_b) IPAQ_ASIC3_SD_CONFIG(_b, u16, SDHC_Slot )
++#define IPAQ_ASIC3_SD_CONFIG_SDHC_ExtGateClk1(_b) IPAQ_ASIC3_SD_CONFIG(_b, u16, SDHC_ExtGateClk1 )
++#define IPAQ_ASIC3_SD_CONFIG_SDHC_ExtGateClk3(_b) IPAQ_ASIC3_SD_CONFIG(_b, u16, SDHC_ExtGateClk3 )
++
++#define SD_CONFIG_
++
++#define SD_CONFIG_COMMAND_MAE (1<<1) /* Memory access enable (set to 1 to access SD Controller) */
++
++#define SD_CONFIG_CLK_ENABLE_ALL 0x1f
++
++#define SD_CONFIG_POWER1_PC_33V 0x0200 /* Set for 3.3 volts */
++#define SD_CONFIG_POWER1_PC_OFF 0x0000 /* Turn off power */
++
++#define SD_CONFIG_CARDDETECTMODE_CLK ((x)&0x3) /* two bits - number of cycles for card detection */
++
++
++#define _IPAQ_ASIC3_SD_CTRL_Base 0x1000
++
++#define IPAQ_ASIC3_SD(_b, s,x) \
++ (*((volatile s *) ((_b) + _IPAQ_ASIC3_SD_CTRL_Base + (_IPAQ_ASIC3_SD_CTRL_ ## x))))
++
++#define _IPAQ_ASIC3_SD_CTRL_Cmd 0x00
++#define _IPAQ_ASIC3_SD_CTRL_Arg0 0x08
++#define _IPAQ_ASIC3_SD_CTRL_Arg1 0x0C
++#define _IPAQ_ASIC3_SD_CTRL_StopInternal 0x10
++#define _IPAQ_ASIC3_SD_CTRL_TransferSectorCount 0x14
++#define _IPAQ_ASIC3_SD_CTRL_Response0 0x18
++#define _IPAQ_ASIC3_SD_CTRL_Response1 0x1C
++#define _IPAQ_ASIC3_SD_CTRL_Response2 0x20
++#define _IPAQ_ASIC3_SD_CTRL_Response3 0x24
++#define _IPAQ_ASIC3_SD_CTRL_Response4 0x28
++#define _IPAQ_ASIC3_SD_CTRL_Response5 0x2C
++#define _IPAQ_ASIC3_SD_CTRL_Response6 0x30
++#define _IPAQ_ASIC3_SD_CTRL_Response7 0x34
++#define _IPAQ_ASIC3_SD_CTRL_CardStatus 0x38
++#define _IPAQ_ASIC3_SD_CTRL_BufferCtrl 0x3C
++#define _IPAQ_ASIC3_SD_CTRL_IntMaskCard 0x40
++#define _IPAQ_ASIC3_SD_CTRL_IntMaskBuffer 0x44
++#define _IPAQ_ASIC3_SD_CTRL_CardClockCtrl 0x48
++#define _IPAQ_ASIC3_SD_CTRL_MemCardXferDataLen 0x4C
++#define _IPAQ_ASIC3_SD_CTRL_MemCardOptionSetup 0x50
++#define _IPAQ_ASIC3_SD_CTRL_ErrorStatus0 0x58
++#define _IPAQ_ASIC3_SD_CTRL_ErrorStatus1 0x5C
++#define _IPAQ_ASIC3_SD_CTRL_DataPort 0x60
++#define _IPAQ_ASIC3_SD_CTRL_TransactionCtrl 0x68
++#define _IPAQ_ASIC3_SD_CTRL_SoftwareReset 0x1C0
++
++#define IPAQ_ASIC3_SD_CTRL_Cmd(_b) IPAQ_ASIC3_SD( _b, u16, Cmd ) /* */
++#define IPAQ_ASIC3_SD_CTRL_Arg0(_b) IPAQ_ASIC3_SD( _b, u16, Arg0 ) /* */
++#define IPAQ_ASIC3_SD_CTRL_Arg1(_b) IPAQ_ASIC3_SD( _b, u16, Arg1 ) /* */
++#define IPAQ_ASIC3_SD_CTRL_StopInternal(_b) IPAQ_ASIC3_SD( _b, u16, StopInternal ) /* */
++#define IPAQ_ASIC3_SD_CTRL_TransferSectorCount(_b) IPAQ_ASIC3_SD( _b, u16, TransferSectorCount ) /* */
++#define IPAQ_ASIC3_SD_CTRL_Response0(_b) IPAQ_ASIC3_SD( _b, u16, Response0 ) /* */
++#define IPAQ_ASIC3_SD_CTRL_Response1(_b) IPAQ_ASIC3_SD( _b, u16, Response1 ) /* */
++#define IPAQ_ASIC3_SD_CTRL_Response2(_b) IPAQ_ASIC3_SD( _b, u16, Response2 ) /* */
++#define IPAQ_ASIC3_SD_CTRL_Response3(_b) IPAQ_ASIC3_SD( _b, u16, Response3 ) /* */
++#define IPAQ_ASIC3_SD_CTRL_Response4(_b) IPAQ_ASIC3_SD( _b, u16, Response4 ) /* */
++#define IPAQ_ASIC3_SD_CTRL_Response5(_b) IPAQ_ASIC3_SD( _b, u16, Response5 ) /* */
++#define IPAQ_ASIC3_SD_CTRL_Response6(_b) IPAQ_ASIC3_SD( _b, u16, Response6 ) /* */
++#define IPAQ_ASIC3_SD_CTRL_Response7(_b) IPAQ_ASIC3_SD( _b, u16, Response7 ) /* */
++#define IPAQ_ASIC3_SD_CTRL_CardStatus(_b) IPAQ_ASIC3_SD( _b, u16, CardStatus ) /* */
++#define IPAQ_ASIC3_SD_CTRL_BufferCtrl(_b) IPAQ_ASIC3_SD( _b, u16, BufferCtrl ) /* and error status*/
++#define IPAQ_ASIC3_SD_CTRL_IntMaskCard(_b) IPAQ_ASIC3_SD( _b, u16, IntMaskCard ) /* */
++#define IPAQ_ASIC3_SD_CTRL_IntMaskBuffer(_b) IPAQ_ASIC3_SD( _b, u16, IntMaskBuffer ) /* */
++#define IPAQ_ASIC3_SD_CTRL_CardClockCtrl(_b) IPAQ_ASIC3_SD( _b, u16, CardClockCtrl ) /* */
++#define IPAQ_ASIC3_SD_CTRL_MemCardXferDataLen(_b) IPAQ_ASIC3_SD( _b, u16, MemCardXferDataLen ) /* */
++#define IPAQ_ASIC3_SD_CTRL_MemCardOptionSetup(_b) IPAQ_ASIC3_SD( _b, u16, MemCardOptionSetup ) /* */
++#define IPAQ_ASIC3_SD_CTRL_ErrorStatus0(_b) IPAQ_ASIC3_SD( _b, u16, ErrorStatus0 ) /* */
++#define IPAQ_ASIC3_SD_CTRL_ErrorStatus1(_b) IPAQ_ASIC3_SD( _b, u16, ErrorStatus1 ) /* */
++#define IPAQ_ASIC3_SD_CTRL_DataPort(_b) IPAQ_ASIC3_SD( _b, u16, DataPort ) /* */
++#define IPAQ_ASIC3_SD_CTRL_TransactionCtrl(_b) IPAQ_ASIC3_SD( _b, u16, TransactionCtrl ) /* */
++#define IPAQ_ASIC3_SD_CTRL_SoftwareReset(_b) IPAQ_ASIC3_SD( _b, u16, SoftwareReset ) /* */
++
++#define SD_CTRL_SOFTWARE_RESET_CLEAR (1<<0)
++
++#define SD_CTRL_TRANSACTIONCONTROL_SET (1<<8) // 0x0100
++
++#define SD_CTRL_CARDCLOCKCONTROL_FOR_SD_CARD (1<<15)// 0x8000
++#define SD_CTRL_CARDCLOCKCONTROL_ENABLE_CLOCK (1<<8) // 0x0100
++#define SD_CTRL_CARDCLOCKCONTROL_CLK_DIV_512 (1<<7) // 0x0080
++#define SD_CTRL_CARDCLOCKCONTROL_CLK_DIV_256 (1<<6) // 0x0040
++#define SD_CTRL_CARDCLOCKCONTROL_CLK_DIV_128 (1<<5) // 0x0020
++#define SD_CTRL_CARDCLOCKCONTROL_CLK_DIV_64 (1<<4) // 0x0010
++#define SD_CTRL_CARDCLOCKCONTROL_CLK_DIV_32 (1<<3) // 0x0008
++#define SD_CTRL_CARDCLOCKCONTROL_CLK_DIV_16 (1<<2) // 0x0004
++#define SD_CTRL_CARDCLOCKCONTROL_CLK_DIV_8 (1<<1) // 0x0002
++#define SD_CTRL_CARDCLOCKCONTROL_CLK_DIV_4 (1<<0) // 0x0001
++#define SD_CTRL_CARDCLOCKCONTROL_CLK_DIV_2 (0<<0) // 0x0000
++
++#define MEM_CARD_OPTION_REQUIRED 0x000e
++#define MEM_CARD_OPTION_DATA_RESPONSE_TIMEOUT(x) (((x)&0x0f)<<4) /* Four bits */
++#define MEM_CARD_OPTION_C2_MODULE_NOT_PRESENT (1<<14) // 0x4000
++#define MEM_CARD_OPTION_DATA_XFR_WIDTH_1 (1<<15) // 0x8000
++#define MEM_CARD_OPTION_DATA_XFR_WIDTH_4 (0<<15) //~0x8000
++
++#define SD_CTRL_COMMAND_INDEX(x) ((x)&0x3f) /* 0=CMD0, 1=CMD1, ..., 63=CMD63 */
++#define SD_CTRL_COMMAND_TYPE_CMD (0 << 6)
++#define SD_CTRL_COMMAND_TYPE_ACMD (1 << 6)
++#define SD_CTRL_COMMAND_TYPE_AUTHENTICATION (2 << 6)
++#define SD_CTRL_COMMAND_RESPONSE_TYPE_NORMAL (0 << 8)
++#define SD_CTRL_COMMAND_RESPONSE_TYPE_EXT_R1 (4 << 8)
++#define SD_CTRL_COMMAND_RESPONSE_TYPE_EXT_R1B (5 << 8)
++#define SD_CTRL_COMMAND_RESPONSE_TYPE_EXT_R2 (6 << 8)
++#define SD_CTRL_COMMAND_RESPONSE_TYPE_EXT_R3 (7 << 8)
++#define SD_CTRL_COMMAND_DATA_PRESENT (1 << 11)
++#define SD_CTRL_COMMAND_TRANSFER_READ (1 << 12)
++#define SD_CTRL_COMMAND_TRANSFER_WRITE (0 << 12)
++#define SD_CTRL_COMMAND_MULTI_BLOCK (1 << 13)
++#define SD_CTRL_COMMAND_SECURITY_CMD (1 << 14)
++
++#define SD_CTRL_STOP_INTERNAL_ISSSUE_CMD12 (1 << 0)
++#define SD_CTRL_STOP_INTERNAL_AUTO_ISSUE_CMD12 (1 << 8)
++
++#define SD_CTRL_CARDSTATUS_RESPONSE_END (1 << 0)
++#define SD_CTRL_CARDSTATUS_RW_END (1 << 2)
++#define SD_CTRL_CARDSTATUS_CARD_REMOVED_0 (1 << 3)
++#define SD_CTRL_CARDSTATUS_CARD_INSERTED_0 (1 << 4)
++#define SD_CTRL_CARDSTATUS_SIGNAL_STATE_PRESENT_0 (1 << 5)
++#define SD_CTRL_CARDSTATUS_WRITE_PROTECT (1 << 7)
++#define SD_CTRL_CARDSTATUS_CARD_REMOVED_3 (1 << 8)
++#define SD_CTRL_CARDSTATUS_CARD_INSERTED_3 (1 << 9)
++#define SD_CTRL_CARDSTATUS_SIGNAL_STATE_PRESENT_3 (1 << 10)
++
++#define SD_CTRL_BUFFERSTATUS_CMD_INDEX_ERROR (1 << 0) // 0x0001
++#define SD_CTRL_BUFFERSTATUS_CRC_ERROR (1 << 1) // 0x0002
++#define SD_CTRL_BUFFERSTATUS_STOP_BIT_END_ERROR (1 << 2) // 0x0004
++#define SD_CTRL_BUFFERSTATUS_DATA_TIMEOUT (1 << 3) // 0x0008
++#define SD_CTRL_BUFFERSTATUS_BUFFER_OVERFLOW (1 << 4) // 0x0010
++#define SD_CTRL_BUFFERSTATUS_BUFFER_UNDERFLOW (1 << 5) // 0x0020
++#define SD_CTRL_BUFFERSTATUS_CMD_TIMEOUT (1 << 6) // 0x0040
++#define SD_CTRL_BUFFERSTATUS_UNK7 (1 << 7) // 0x0080
++#define SD_CTRL_BUFFERSTATUS_BUFFER_READ_ENABLE (1 << 8) // 0x0100
++#define SD_CTRL_BUFFERSTATUS_BUFFER_WRITE_ENABLE (1 << 9) // 0x0200
++#define SD_CTRL_BUFFERSTATUS_ILLEGAL_FUNCTION (1 << 13)// 0x2000
++#define SD_CTRL_BUFFERSTATUS_CMD_BUSY (1 << 14)// 0x4000
++#define SD_CTRL_BUFFERSTATUS_ILLEGAL_ACCESS (1 << 15)// 0x8000
++
++#define SD_CTRL_INTMASKCARD_RESPONSE_END (1 << 0) // 0x0001
++#define SD_CTRL_INTMASKCARD_RW_END (1 << 2) // 0x0004
++#define SD_CTRL_INTMASKCARD_CARD_REMOVED_0 (1 << 3) // 0x0008
++#define SD_CTRL_INTMASKCARD_CARD_INSERTED_0 (1 << 4) // 0x0010
++#define SD_CTRL_INTMASKCARD_SIGNAL_STATE_PRESENT_0 (1 << 5) // 0x0020
++#define SD_CTRL_INTMASKCARD_UNK6 (1 << 6) // 0x0040
++#define SD_CTRL_INTMASKCARD_WRITE_PROTECT (1 << 7) // 0x0080
++#define SD_CTRL_INTMASKCARD_CARD_REMOVED_3 (1 << 8) // 0x0100
++#define SD_CTRL_INTMASKCARD_CARD_INSERTED_3 (1 << 9) // 0x0200
++#define SD_CTRL_INTMASKCARD_SIGNAL_STATE_PRESENT_3 (1 << 10)// 0x0400
++
++#define SD_CTRL_INTMASKBUFFER_CMD_INDEX_ERROR (1 << 0) // 0x0001
++#define SD_CTRL_INTMASKBUFFER_CRC_ERROR (1 << 1) // 0x0002
++#define SD_CTRL_INTMASKBUFFER_STOP_BIT_END_ERROR (1 << 2) // 0x0004
++#define SD_CTRL_INTMASKBUFFER_DATA_TIMEOUT (1 << 3) // 0x0008
++#define SD_CTRL_INTMASKBUFFER_BUFFER_OVERFLOW (1 << 4) // 0x0010
++#define SD_CTRL_INTMASKBUFFER_BUFFER_UNDERFLOW (1 << 5) // 0x0020
++#define SD_CTRL_INTMASKBUFFER_CMD_TIMEOUT (1 << 6) // 0x0040
++#define SD_CTRL_INTMASKBUFFER_UNK7 (1 << 7) // 0x0080
++#define SD_CTRL_INTMASKBUFFER_BUFFER_READ_ENABLE (1 << 8) // 0x0100
++#define SD_CTRL_INTMASKBUFFER_BUFFER_WRITE_ENABLE (1 << 9) // 0x0200
++#define SD_CTRL_INTMASKBUFFER_ILLEGAL_FUNCTION (1 << 13)// 0x2000
++#define SD_CTRL_INTMASKBUFFER_CMD_BUSY (1 << 14)// 0x4000
++#define SD_CTRL_INTMASKBUFFER_ILLEGAL_ACCESS (1 << 15)// 0x8000
++
++#define SD_CTRL_DETAIL0_RESPONSE_CMD_ERROR (1 << 0) // 0x0001
++#define SD_CTRL_DETAIL0_END_BIT_ERROR_FOR_RESPONSE_NON_CMD12 (1 << 2) // 0x0004
++#define SD_CTRL_DETAIL0_END_BIT_ERROR_FOR_RESPONSE_CMD12 (1 << 3) // 0x0008
++#define SD_CTRL_DETAIL0_END_BIT_ERROR_FOR_READ_DATA (1 << 4) // 0x0010
++#define SD_CTRL_DETAIL0_END_BIT_ERROR_FOR_WRITE_CRC_STATUS (1 << 5) // 0x0020
++#define SD_CTRL_DETAIL0_CRC_ERROR_FOR_RESPONSE_NON_CMD12 (1 << 8) // 0x0100
++#define SD_CTRL_DETAIL0_CRC_ERROR_FOR_RESPONSE_CMD12 (1 << 9) // 0x0200
++#define SD_CTRL_DETAIL0_CRC_ERROR_FOR_READ_DATA (1 << 10)// 0x0400
++#define SD_CTRL_DETAIL0_CRC_ERROR_FOR_WRITE_CMD (1 << 11)// 0x0800
++
++#define SD_CTRL_DETAIL1_NO_CMD_RESPONSE (1 << 0) // 0x0001
++#define SD_CTRL_DETAIL1_TIMEOUT_READ_DATA (1 << 4) // 0x0010
++#define SD_CTRL_DETAIL1_TIMEOUT_CRS_STATUS (1 << 5) // 0x0020
++#define SD_CTRL_DETAIL1_TIMEOUT_CRC_BUSY (1 << 6) // 0x0040
++
++#define _IPAQ_ASIC3_SDIO_CTRL_Base 0x1200
++
++#define IPAQ_ASIC3_SDIO(_b, s,x) \
++ (*((volatile s *) ((_b) + _IPAQ_ASIC3_SDIO_CTRL_Base + (_IPAQ_ASIC3_SDIO_CTRL_ ## x))))
++
++#define _IPAQ_ASIC3_SDIO_CTRL_Cmd 0x00
++#define _IPAQ_ASIC3_SDIO_CTRL_CardPortSel 0x04
++#define _IPAQ_ASIC3_SDIO_CTRL_Arg0 0x08
++#define _IPAQ_ASIC3_SDIO_CTRL_Arg1 0x0C
++#define _IPAQ_ASIC3_SDIO_CTRL_TransferBlockCount 0x14
++#define _IPAQ_ASIC3_SDIO_CTRL_Response0 0x18
++#define _IPAQ_ASIC3_SDIO_CTRL_Response1 0x1C
++#define _IPAQ_ASIC3_SDIO_CTRL_Response2 0x20
++#define _IPAQ_ASIC3_SDIO_CTRL_Response3 0x24
++#define _IPAQ_ASIC3_SDIO_CTRL_Response4 0x28
++#define _IPAQ_ASIC3_SDIO_CTRL_Response5 0x2C
++#define _IPAQ_ASIC3_SDIO_CTRL_Response6 0x30
++#define _IPAQ_ASIC3_SDIO_CTRL_Response7 0x34
++#define _IPAQ_ASIC3_SDIO_CTRL_CardStatus 0x38
++#define _IPAQ_ASIC3_SDIO_CTRL_BufferCtrl 0x3C
++#define _IPAQ_ASIC3_SDIO_CTRL_IntMaskCard 0x40
++#define _IPAQ_ASIC3_SDIO_CTRL_IntMaskBuffer 0x44
++#define _IPAQ_ASIC3_SDIO_CTRL_CardXferDataLen 0x4C
++#define _IPAQ_ASIC3_SDIO_CTRL_CardOptionSetup 0x50
++#define _IPAQ_ASIC3_SDIO_CTRL_ErrorStatus0 0x54
++#define _IPAQ_ASIC3_SDIO_CTRL_ErrorStatus1 0x58
++#define _IPAQ_ASIC3_SDIO_CTRL_DataPort 0x60
++#define _IPAQ_ASIC3_SDIO_CTRL_TransactionCtrl 0x68
++#define _IPAQ_ASIC3_SDIO_CTRL_CardIntCtrl 0x6C
++#define _IPAQ_ASIC3_SDIO_CTRL_ClocknWaitCtrl 0x70
++#define _IPAQ_ASIC3_SDIO_CTRL_HostInformation 0x74
++#define _IPAQ_ASIC3_SDIO_CTRL_ErrorCtrl 0x78
++#define _IPAQ_ASIC3_SDIO_CTRL_LEDCtrl 0x7C
++#define _IPAQ_ASIC3_SDIO_CTRL_SoftwareReset 0x1C0
++
++#define IPAQ_ASIC3_SDIO_CTRL_Cmd(_b) IPAQ_ASIC3_SDIO( _b, u16, Cmd ) /* */
++#define IPAQ_ASIC3_SDIO_CTRL_CardPortSel(_b) IPAQ_ASIC3_SDIO( _b, u16, CardPortSel ) /* */
++#define IPAQ_ASIC3_SDIO_CTRL_Arg0(_b) IPAQ_ASIC3_SDIO( _b, u16, Arg0 ) /* */
++#define IPAQ_ASIC3_SDIO_CTRL_Arg1(_b) IPAQ_ASIC3_SDIO( _b, u16, Arg1 ) /* */
++#define IPAQ_ASIC3_SDIO_CTRL_TransferBlockCount(_b) IPAQ_ASIC3_SDIO( _b, u16, TransferBlockCount ) /* */
++#define IPAQ_ASIC3_SDIO_CTRL_Response0(_b) IPAQ_ASIC3_SDIO( _b, u16, Response0 ) /* */
++#define IPAQ_ASIC3_SDIO_CTRL_Response1(_b) IPAQ_ASIC3_SDIO( _b, u16, Response1 ) /* */
++#define IPAQ_ASIC3_SDIO_CTRL_Response2(_b) IPAQ_ASIC3_SDIO( _b, u16, Response2 ) /* */
++#define IPAQ_ASIC3_SDIO_CTRL_Response3(_b) IPAQ_ASIC3_SDIO( _b, u16, Response3 ) /* */
++#define IPAQ_ASIC3_SDIO_CTRL_Response4(_b) IPAQ_ASIC3_SDIO( _b, u16, Response4 ) /* */
++#define IPAQ_ASIC3_SDIO_CTRL_Response5(_b) IPAQ_ASIC3_SDIO( _b, u16, Response5 ) /* */
++#define IPAQ_ASIC3_SDIO_CTRL_Response6(_b) IPAQ_ASIC3_SDIO( _b, u16, Response6 ) /* */
++#define IPAQ_ASIC3_SDIO_CTRL_Response7(_b) IPAQ_ASIC3_SDIO( _b, u16, Response7 ) /* */
++#define IPAQ_ASIC3_SDIO_CTRL_CardStatus(_b) IPAQ_ASIC3_SDIO( _b, u16, CardStatus ) /* */
++#define IPAQ_ASIC3_SDIO_CTRL_BufferCtrl(_b) IPAQ_ASIC3_SDIO( _b, u16, BufferCtrl ) /* and error status*/
++#define IPAQ_ASIC3_SDIO_CTRL_IntMaskCard(_b) IPAQ_ASIC3_SDIO( _b, u16, IntMaskCard ) /* */
++#define IPAQ_ASIC3_SDIO_CTRL_IntMaskBuffer(_b) IPAQ_ASIC3_SDIO( _b, u16, IntMaskBuffer ) /* */
++#define IPAQ_ASIC3_SDIO_CTRL_CardXferDataLen(_b) IPAQ_ASIC3_SDIO( _b, u16, CardXferDataLen ) /* */
++#define IPAQ_ASIC3_SDIO_CTRL_CardOptionSetup(_b) IPAQ_ASIC3_SDIO( _b, u16, CardOptionSetup ) /* */
++#define IPAQ_ASIC3_SDIO_CTRL_ErrorStatus0(_b) IPAQ_ASIC3_SDIO( _b, u16, ErrorStatus0 ) /* */
++#define IPAQ_ASIC3_SDIO_CTRL_ErrorStatus1(_b) IPAQ_ASIC3_SDIO( _b, u16, ErrorStatus1 ) /* */
++#define IPAQ_ASIC3_SDIO_CTRL_DataPort(_b) IPAQ_ASIC3_SDIO( _b, u16, DataPort ) /* */
++#define IPAQ_ASIC3_SDIO_CTRL_TransactionCtrl(_b) IPAQ_ASIC3_SDIO( _b, u16, TransactionCtrl ) /* */
++#define IPAQ_ASIC3_SDIO_CTRL_CardIntCtrl(_b) IPAQ_ASIC3_SDIO( _b, u16, CardIntCtrl ) /* */
++#define IPAQ_ASIC3_SDIO_CTRL_ClocknWaitCtrl(_b) IPAQ_ASIC3_SDIO( _b, u16, ClocknWaitCtrl ) /* */
++#define IPAQ_ASIC3_SDIO_CTRL_HostInformation(_b) IPAQ_ASIC3_SDIO( _b, u16, HostInformation ) /* */
++#define IPAQ_ASIC3_SDIO_CTRL_ErrorCtrl(_b) IPAQ_ASIC3_SDIO( _b, u16, ErrorCtrl ) /* */
++#define IPAQ_ASIC3_SDIO_CTRL_LEDCtrl(_b) IPAQ_ASIC3_SDIO( _b, u16, LEDCtrl ) /* */
++#define IPAQ_ASIC3_SDIO_CTRL_SoftwareReset(_b) IPAQ_ASIC3_SDIO( _b, u16, SoftwareReset ) /* */
++
++#define IPAQ_ASIC3_MAP_SIZE 0x2000
++
++#endif
+Index: linux-2.6.26-rc4/include/linux/gpiodev.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26-rc4/include/linux/gpiodev.h 2008-06-01 18:49:45.000000000 +0100
+@@ -0,0 +1,44 @@
++#ifndef __GPIODEV_H
++#define __GPIODEV_H
++
++#include <linux/device.h>
++#include <linux/platform_device.h>
++#include <asm/gpio.h>
++
++/* Interface */
++
++/* This structure must be first member of device platform_data structure
++ of a device which provides gpiodev interface. All method pointers
++ must be non-NULL, so stubs must be used for non-implemented ones. */
++struct gpiodev_ops {
++ int (*get)(struct device *this, unsigned gpio_no);
++ void (*set)(struct device *this, unsigned gpio_no, int val);
++ int (*to_irq)(struct device *this, unsigned gpio_no);
++};
++
++/* Generalized GPIO structure */
++
++struct gpio {
++ struct device *gpio_dev;
++ unsigned gpio_no;
++};
++
++/* API functions */
++
++static inline int gpiodev_get_value(struct gpio *gpio)
++{
++ struct gpiodev_ops *ops = gpio->gpio_dev->platform_data;
++ return ops->get(gpio->gpio_dev, gpio->gpio_no);
++}
++static inline void gpiodev_set_value(struct gpio *gpio, int val)
++{
++ struct gpiodev_ops *ops = gpio->gpio_dev->platform_data;
++ ops->set(gpio->gpio_dev, gpio->gpio_no, val);
++}
++static inline int gpiodev_to_irq(struct gpio *gpio)
++{
++ struct gpiodev_ops *ops = gpio->gpio_dev->platform_data;
++ return ops->to_irq(gpio->gpio_dev, gpio->gpio_no);
++}
++
++#endif /* __GPIODEV_H */
+Index: linux-2.6.26-rc4/include/linux/input_pda.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26-rc4/include/linux/input_pda.h 2008-06-01 18:49:45.000000000 +0100
+@@ -0,0 +1,47 @@
++#ifndef _INPUT_PDA_H
++#define _INPUT_PDA_H
++
++/*
++ * This is temporary virtual button key codes map
++ * for keyboardless handheld computers.
++ * Its purpose is to provide map common to all devices
++ * and known to work with current software and its bugs
++ * and misfeatures. Once issues with the software are
++ * solved, codes from input.h will be used directly
++ * (missing key definitions will be added).
++ */
++
++/* Some directly usable keycodes:
++KEY_POWER - Power/suspend button
++KEY_ENTER - Enter/Action/Central button on joypad
++KEY_UP
++KEY_DOWN
++KEY_LEFT
++KEY_RIGHT
++*/
++
++/* XXX Instead of using any values in include/linux/input.h, we have to use
++ use values < 128 due to some munging that kdrive does to get keystrokes.
++ When kdrive gets its key events from evdev instead of the console,
++ we should be able to switch to using input.h values and get rid of
++ xmodmap. */
++
++#define _KEY_APP1 KEY_F9 // xmodmap sees 67 + 8 = 75
++#define _KEY_APP2 KEY_F10 // xmodmap 76
++#define _KEY_APP3 KEY_F11 // xmodmap 95
++#define _KEY_APP4 KEY_F12 // xmodmap 96
++
++#define _KEY_RECORD KEY_RO
++
++/* It is highly recommended to use exactly 4 codes above for
++ 4 buttons the device has. This will ensure that console and
++ framebuffer applications (e.g. games) will work ok on all
++ devices. If you'd like more distinguishable names, following
++ convenience defines are provided, suiting many devices. */
++
++#define _KEY_CALENDAR _KEY_APP1
++#define _KEY_CONTACTS _KEY_APP2
++#define _KEY_MAIL _KEY_APP3
++#define _KEY_HOMEPAGE _KEY_APP4
++
++#endif
+Index: linux-2.6.26-rc4/include/linux/soc/asic3_base.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26-rc4/include/linux/soc/asic3_base.h 2008-06-01 18:49:45.000000000 +0100
+@@ -0,0 +1,104 @@
++#include <asm/types.h>
++#include <linux/gpiodev.h>
++
++/* Private API - for ASIC3 devices internal use only */
++#define HDR_IPAQ_ASIC3_ACTION(ACTION,action,fn,FN) \
++u32 asic3_get_gpio_ ## action ## _ ## fn (struct device *dev); \
++void asic3_set_gpio_ ## action ## _ ## fn (struct device *dev, u32 bits, u32 val);
++
++#define HDR_IPAQ_ASIC3_FN(fn,FN) \
++ HDR_IPAQ_ASIC3_ACTION ( MASK,mask,fn,FN) \
++ HDR_IPAQ_ASIC3_ACTION ( DIR, dir, fn, FN) \
++ HDR_IPAQ_ASIC3_ACTION ( OUT, out, fn, FN) \
++ HDR_IPAQ_ASIC3_ACTION ( LEVELTRI, trigtype, fn, FN) \
++ HDR_IPAQ_ASIC3_ACTION ( RISING, rising, fn, FN) \
++ HDR_IPAQ_ASIC3_ACTION ( LEVEL, triglevel, fn, FN) \
++ HDR_IPAQ_ASIC3_ACTION ( SLEEP_MASK, sleepmask, fn, FN) \
++ HDR_IPAQ_ASIC3_ACTION ( SLEEP_OUT, sleepout, fn, FN) \
++ HDR_IPAQ_ASIC3_ACTION ( BATT_FAULT_OUT, battfaultout, fn, FN) \
++ HDR_IPAQ_ASIC3_ACTION ( INT_STATUS, intstatus, fn, FN) \
++ HDR_IPAQ_ASIC3_ACTION ( ALT_FUNCTION, alt_fn, fn, FN) \
++ HDR_IPAQ_ASIC3_ACTION ( SLEEP_CONF, sleepconf, fn, FN) \
++ HDR_IPAQ_ASIC3_ACTION ( STATUS, status, fn, FN)
++
++/* Public API */
++
++#define ASIC3_GPIOA_IRQ_BASE 0
++#define ASIC3_GPIOB_IRQ_BASE 16
++#define ASIC3_GPIOC_IRQ_BASE 32
++#define ASIC3_GPIOD_IRQ_BASE 48
++#define ASIC3_LED0_IRQ 64
++#define ASIC3_LED1_IRQ 65
++#define ASIC3_LED2_IRQ 66
++#define ASIC3_SPI_IRQ 67
++#define ASIC3_SMBUS_IRQ 68
++#define ASIC3_OWM_IRQ 69
++
++#define ASIC3_NR_GPIO_IRQS 64 /* 16 bits each GPIO A...D banks */
++#define ASIC3_NR_IRQS (ASIC3_OWM_IRQ + 1)
++
++extern int asic3_irq_base(struct device *dev);
++
++extern void asic3_write_register(struct device *dev, unsigned int reg,
++ u32 value);
++extern u32 asic3_read_register(struct device *dev, unsigned int reg);
++
++/* old clock api */
++extern void asic3_set_clock_sel(struct device *dev, u32 bits, u32 val);
++extern u32 asic3_get_clock_cdex(struct device *dev);
++extern void asic3_set_clock_cdex(struct device *dev, u32 bits, u32 val);
++
++extern void asic3_set_extcf_select(struct device *dev, u32 bits, u32 val);
++extern void asic3_set_extcf_reset(struct device *dev, u32 bits, u32 val);
++extern void asic3_set_sdhwctrl(struct device *dev, u32 bits, u32 val);
++
++extern void asic3_set_led(struct device *dev, int led_num, int duty_time,
++ int cycle_time, int timebase);
++
++extern int asic3_register_mmc(struct device *dev);
++extern int asic3_unregister_mmc(struct device *dev);
++
++/* Accessors for GPIO banks */
++HDR_IPAQ_ASIC3_FN(a, A)
++HDR_IPAQ_ASIC3_FN(b, B)
++HDR_IPAQ_ASIC3_FN(c, C)
++HDR_IPAQ_ASIC3_FN(d, D)
++
++#define _IPAQ_ASIC3_GPIO_BANK_A 0
++#define _IPAQ_ASIC3_GPIO_BANK_B 1
++#define _IPAQ_ASIC3_GPIO_BANK_C 2
++#define _IPAQ_ASIC3_GPIO_BANK_D 3
++
++#define ASIC3_GPIO_bit(gpio) (1 << (gpio & 0xf))
++
++extern int asic3_get_gpio_bit(struct device *dev, int gpio);
++extern void asic3_set_gpio_bit(struct device *dev, int gpio, int val);
++extern int asic3_gpio_get_value(struct device *dev, unsigned gpio);
++extern void asic3_gpio_set_value(struct device *dev, unsigned gpio, int val);
++
++
++struct tmio_mmc_hwconfig;
++
++struct asic3_platform_data
++{
++ // Must be first member
++ struct gpiodev_ops gpiodev_ops;
++
++ struct {
++ u32 dir;
++ u32 init;
++ u32 sleep_mask;
++ u32 sleep_out;
++ u32 batt_fault_out;
++ u32 sleep_conf;
++ u32 alt_function;
++ } gpio_a, gpio_b, gpio_c, gpio_d;
++
++ int irq_base;
++ unsigned int bus_shift;
++
++ struct platform_device **child_platform_devs;
++ int num_child_platform_devs;
++
++ struct tmio_mmc_hwconfig *tmio_mmc_hwconfig;
++};
+Index: linux-2.6.26-rc4/include/linux/soc/tmio_mmc.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26-rc4/include/linux/soc/tmio_mmc.h 2008-06-01 18:49:45.000000000 +0100
+@@ -0,0 +1,17 @@
++#include <linux/platform_device.h>
++
++#define MMC_CLOCK_DISABLED 0
++#define MMC_CLOCK_ENABLED 1
++
++#define TMIO_WP_ALWAYS_RW ((void*)-1)
++
++struct tmio_mmc_hwconfig {
++ void (*hwinit)(struct platform_device *sdev);
++ void (*set_mmc_clock)(struct platform_device *sdev, int state);
++
++ /* NULL - use ASIC3 signal,
++ TMIO_WP_ALWAYS_RW - assume always R/W (e.g. miniSD)
++ otherwise - machine-specific handler */
++ int (*mmc_get_ro)(struct platform_device *pdev);
++ short address_shift;
++};
+Index: linux-2.6.26-rc4/include/asm-arm/arch-pxa/pxa-regs.h
+===================================================================
+--- linux-2.6.26-rc4.orig/include/asm-arm/arch-pxa/pxa-regs.h 2008-05-26 19:08:11.000000000 +0100
++++ linux-2.6.26-rc4/include/asm-arm/arch-pxa/pxa-regs.h 2008-06-01 18:49:45.000000000 +0100
+@@ -1117,6 +1117,8 @@
+ #define PWM_PWDUTY1 __REG(0x40C00004) /* PWM 1 Duty Cycle Register */
+ #define PWM_PERVAL1 __REG(0x40C00008) /* PWM 1 Period Control Register */
+
++#define LCCR4_13M_PCD_EN (1<<25) /* 13M PCD enable */
++#define LCCR4_PCDDIV (1<<31) /* PCD selection */
+
+ /*
+ * Interrupt Controller
+Index: linux-2.6.26-rc4/drivers/mmc/host/Kconfig
+===================================================================
+--- linux-2.6.26-rc4.orig/drivers/mmc/host/Kconfig 2008-05-26 19:08:11.000000000 +0100
++++ linux-2.6.26-rc4/drivers/mmc/host/Kconfig 2008-06-01 18:49:45.000000000 +0100
+@@ -130,3 +130,9 @@
+
+ If unsure, or if your system has no SPI master driver, say N.
+
++config MMC_ASIC3
++ tristate "HTC ASIC3 SD/MMC support"
++ depends on MMC && HTC_ASIC3
++ help
++ This provides support for the ASIC3 SD/MMC controller, used
++ in the iPAQ hx4700 and others.
+Index: linux-2.6.26-rc4/drivers/mmc/host/Makefile
+===================================================================
+--- linux-2.6.26-rc4.orig/drivers/mmc/host/Makefile 2008-05-26 19:08:11.000000000 +0100
++++ linux-2.6.26-rc4/drivers/mmc/host/Makefile 2008-06-01 18:49:45.000000000 +0100
+@@ -13,6 +13,7 @@
+ obj-$(CONFIG_MMC_RICOH_MMC) += ricoh_mmc.o
+ obj-$(CONFIG_MMC_WBSD) += wbsd.o
+ obj-$(CONFIG_MMC_AU1X) += au1xmmc.o
++obj-$(CONFIG_MMC_ASIC3) += asic3_mmc.o
+ obj-$(CONFIG_MMC_OMAP) += omap.o
+ obj-$(CONFIG_MMC_AT91) += at91_mci.o
+ obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o
+Index: linux-2.6.26-rc4/drivers/mmc/host/asic3_mmc.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26-rc4/drivers/mmc/host/asic3_mmc.c 2008-06-01 18:49:45.000000000 +0100
+@@ -0,0 +1,900 @@
++/* Note that this driver can likely be merged into the tmio driver, so
++ * consider this code temporary. It works, though.
++ */
++/*
++ * linux/drivers/mmc/asic3_mmc.c
++ *
++ * Copyright (c) 2005 SDG Systems, LLC
++ *
++ * based on tmio_mmc.c
++ * Copyright (C) 2004 Ian Molton
++ *
++ * Refactored to support all ASIC3 devices, 2006 Paul Sokolovsky
++ *
++ * 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.
++ *
++ * Driver for the SD / SDIO cell found in:
++ *
++ * TC6393XB
++ *
++ * This driver draws mainly on scattered spec sheets, Reverse engineering
++ * of the toshiba e800 SD driver and some parts of the 2.4 ASIC3 driver (4 bit
++ * support).
++ *
++ * Supports MMC 1 bit transfers and SD 1 and 4 bit modes.
++ *
++ * TODO:
++ * Eliminate FIXMEs
++ * SDIO support
++ * Power management
++ * Handle MMC errors (at all)
++ *
++ */
++#include <linux/module.h>
++#include <linux/moduleparam.h>
++#include <linux/init.h>
++#include <linux/ioport.h>
++#include <linux/platform_device.h>
++#include <linux/interrupt.h>
++#include <linux/blkdev.h>
++#include <linux/delay.h>
++#include <linux/err.h>
++#include <linux/mmc/mmc.h>
++#include <linux/mmc/host.h>
++#include <linux/mmc/card.h>
++//#include <linux/mmc/protocol.h>
++#include <linux/mmc/sd.h>
++#include <linux/scatterlist.h>
++//#include <linux/soc-old.h>
++#include <linux/soc/asic3_base.h>
++#include <linux/soc/tmio_mmc.h>
++
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/mach/irq.h>
++#include <linux/clk.h>
++#include <asm/mach-types.h>
++
++#include <asm/hardware/ipaq-asic3.h>
++#include "asic3_mmc.h"
++
++struct asic3_mmc_host {
++ void *ctl_base;
++ struct device *asic3_dev; /* asic3 device */
++ struct tmio_mmc_hwconfig *hwconfig; /* HW config data/handlers, guaranteed != NULL */
++ unsigned long bus_shift;
++ struct mmc_command *cmd;
++ struct mmc_request *mrq;
++ struct mmc_data *data;
++ struct mmc_host *mmc;
++ int irq;
++ unsigned short clock_for_sd;
++
++ /* I/O related stuff */
++ struct scatterlist *sg_ptr;
++ unsigned int sg_len;
++ unsigned int sg_off;
++};
++
++static void
++mmc_finish_request(struct asic3_mmc_host *host)
++{
++ struct mmc_request *mrq = host->mrq;
++
++ /* Write something to end the command */
++ host->mrq = NULL;
++ host->cmd = NULL;
++ host->data = NULL;
++
++ mmc_request_done(host->mmc, mrq);
++}
++
++
++#define ASIC3_MMC_REG(host, block, reg) (*((volatile u16 *) ((host->ctl_base) + ((_IPAQ_ASIC3_## block ## _Base + _IPAQ_ASIC3_ ## block ## _ ## reg) >> (2 - host->bus_shift))) ))
++
++static void
++mmc_start_command(struct asic3_mmc_host *host, struct mmc_command *cmd)
++{
++ struct mmc_data *data = host->data;
++ int c = cmd->opcode;
++
++ DBG("Opcode: %d, base: %p\n", cmd->opcode, host->ctl_base);
++
++ if(cmd->opcode == MMC_STOP_TRANSMISSION) {
++ ASIC3_MMC_REG(host, SD_CTRL, StopInternal) = SD_CTRL_STOP_INTERNAL_ISSSUE_CMD12;
++ cmd->resp[0] = cmd->opcode;
++ cmd->resp[1] = 0;
++ cmd->resp[2] = 0;
++ cmd->resp[3] = 0;
++ cmd->resp[4] = 0;
++ return;
++ }
++
++ switch(cmd->flags & 0x1f) {
++ case MMC_RSP_NONE: c |= SD_CTRL_COMMAND_RESPONSE_TYPE_NORMAL; break;
++ case MMC_RSP_R1: c |= SD_CTRL_COMMAND_RESPONSE_TYPE_EXT_R1; break;
++ case MMC_RSP_R1B: c |= SD_CTRL_COMMAND_RESPONSE_TYPE_EXT_R1B; break;
++ case MMC_RSP_R2: c |= SD_CTRL_COMMAND_RESPONSE_TYPE_EXT_R2; break;
++ case MMC_RSP_R3: c |= SD_CTRL_COMMAND_RESPONSE_TYPE_EXT_R3; break;
++ default:
++ DBG("Unknown response type %d\n", cmd->flags & 0x1f);
++ break;
++ }
++
++ host->cmd = cmd;
++
++ if(cmd->opcode == MMC_APP_CMD) {
++ c |= APP_CMD;
++ }
++ if (cmd->opcode == MMC_GO_IDLE_STATE) {
++ c |= (3 << 8); /* This was removed from ipaq-asic3.h for some reason */
++ }
++ if(data) {
++ c |= SD_CTRL_COMMAND_DATA_PRESENT;
++ if(data->blocks > 1) {
++ ASIC3_MMC_REG(host, SD_CTRL, StopInternal) = SD_CTRL_STOP_INTERNAL_AUTO_ISSUE_CMD12;
++ c |= SD_CTRL_COMMAND_MULTI_BLOCK;
++ }
++ if(data->flags & MMC_DATA_READ) {
++ c |= SD_CTRL_COMMAND_TRANSFER_READ;
++ }
++ /* MMC_DATA_WRITE does not require a bit to be set */
++ }
++
++ /* Enable the command and data interrupts */
++ ASIC3_MMC_REG(host, SD_CTRL, IntMaskCard) = ~(
++ SD_CTRL_INTMASKCARD_RESPONSE_END
++ | SD_CTRL_INTMASKCARD_RW_END
++ | SD_CTRL_INTMASKCARD_CARD_REMOVED_0
++ | SD_CTRL_INTMASKCARD_CARD_INSERTED_0
++#if 0
++ | SD_CTRL_INTMASKCARD_CARD_REMOVED_3
++ | SD_CTRL_INTMASKCARD_CARD_INSERTED_3
++#endif
++ );
++
++ ASIC3_MMC_REG(host, SD_CTRL, IntMaskBuffer) = ~(
++ SD_CTRL_INTMASKBUFFER_UNK7
++ | SD_CTRL_INTMASKBUFFER_CMD_BUSY
++#if 0
++ | SD_CTRL_INTMASKBUFFER_CMD_INDEX_ERROR
++ | SD_CTRL_INTMASKBUFFER_CRC_ERROR
++ | SD_CTRL_INTMASKBUFFER_STOP_BIT_END_ERROR
++ | SD_CTRL_INTMASKBUFFER_DATA_TIMEOUT
++ | SD_CTRL_INTMASKBUFFER_BUFFER_OVERFLOW
++ | SD_CTRL_INTMASKBUFFER_BUFFER_UNDERFLOW
++ | SD_CTRL_INTMASKBUFFER_CMD_TIMEOUT
++ | SD_CTRL_INTMASKBUFFER_BUFFER_READ_ENABLE
++ | SD_CTRL_INTMASKBUFFER_BUFFER_WRITE_ENABLE
++ | SD_CTRL_INTMASKBUFFER_ILLEGAL_ACCESS
++#endif
++ );
++
++ /* Send the command */
++ ASIC3_MMC_REG(host, SD_CTRL, Arg1) = cmd->arg >> 16;
++ ASIC3_MMC_REG(host, SD_CTRL, Arg0) = cmd->arg & 0xffff;
++ ASIC3_MMC_REG(host, SD_CTRL, Cmd) = c;
++}
++
++/* This chip always returns (at least?) as much data as you ask for. I'm
++ * unsure what happens if you ask for less than a block. This should be looked
++ * into to ensure that a funny length read doesnt mess up the controller data
++ * state machine.
++ *
++ * Aric: Statement above may not apply to ASIC3.
++ *
++ * FIXME - this chip cannot do 1 and 2 byte data requests in 4 bit mode
++ *
++ * Aric: Statement above may not apply to ASIC3.
++ */
++
++static struct tasklet_struct mmc_data_read_tasklet;
++
++static void
++mmc_data_transfer(unsigned long h)
++{
++ struct asic3_mmc_host *host = (struct asic3_mmc_host *)h;
++ struct mmc_data *data = host->data;
++ unsigned short *buf;
++ int count;
++ /* unsigned long flags; */
++
++ if(!data){
++ printk(KERN_WARNING DRIVER_NAME ": Spurious Data IRQ\n");
++ return;
++ }
++
++ /* local_irq_save(flags); */
++ /* buf = kmap_atomic(host->sg_ptr->page, KM_BIO_SRC_IRQ); */
++ buf = kmap(host->sg_ptr->page);
++ buf += host->sg_ptr->offset/2 + host->sg_off/2;
++
++ /*
++ * Ensure we dont read more than one block. The chip will interrupt us
++ * When the next block is available.
++ */
++ count = host->sg_ptr->length - host->sg_off;
++ if(count > data->blksz) {
++ count = data->blksz;
++ }
++
++ DBG("count: %08x, page: %p, offset: %08x flags %08x\n",
++ count, host->sg_ptr->page, host->sg_off, data->flags);
++
++ host->sg_off += count;
++
++ /* Transfer the data */
++ if(data->flags & MMC_DATA_READ) {
++ while(count > 0) {
++ /* Read two bytes from SD/MMC controller. */
++ *buf = ASIC3_MMC_REG(host, SD_CTRL, DataPort);
++ buf++;
++ count -= 2;
++ }
++ //flush_dcache_page(host->sg_ptr->page);
++ } else {
++ while(count > 0) {
++ /* Write two bytes to SD/MMC controller. */
++ ASIC3_MMC_REG(host, SD_CTRL, DataPort) = *buf;
++ buf++;
++ count -= 2;
++ }
++ }
++
++ /* kunmap_atomic(host->sg_ptr->page, KM_BIO_SRC_IRQ); */
++ kunmap(host->sg_ptr->page);
++ /* local_irq_restore(flags); */
++ if(host->sg_off == host->sg_ptr->length) {
++ host->sg_ptr++;
++ host->sg_off = 0;
++ --host->sg_len;
++ }
++
++ return;
++}
++
++static void
++mmc_data_end_irq(struct asic3_mmc_host *host)
++{
++ struct mmc_data *data = host->data;
++
++ host->data = NULL;
++
++ if(!data){
++ printk(KERN_WARNING DRIVER_NAME ": Spurious data end IRQ\n");
++ return;
++ }
++
++ if (data->error == MMC_ERR_NONE) {
++ data->bytes_xfered = data->blocks * data->blksz;
++ } else {
++ data->bytes_xfered = 0;
++ }
++
++ DBG("Completed data request\n");
++
++ ASIC3_MMC_REG(host, SD_CTRL, StopInternal) = 0;
++
++ /* Make sure read enable interrupt and write enable interrupt are disabled */
++ if(data->flags & MMC_DATA_READ) {
++ ASIC3_MMC_REG(host, SD_CTRL, IntMaskBuffer) |= SD_CTRL_INTMASKBUFFER_BUFFER_READ_ENABLE;
++ } else {
++ ASIC3_MMC_REG(host, SD_CTRL, IntMaskBuffer) |= SD_CTRL_INTMASKBUFFER_BUFFER_WRITE_ENABLE;
++ }
++
++ mmc_finish_request(host);
++}
++
++static void
++mmc_cmd_irq(struct asic3_mmc_host *host, unsigned int buffer_stat)
++{
++ struct mmc_command *cmd = host->cmd;
++ u8 *buf = (u8 *)cmd->resp;
++ u16 data;
++
++ if(!host->cmd) {
++ printk(KERN_WARNING DRIVER_NAME ": Spurious CMD irq\n");
++ return;
++ }
++
++ host->cmd = NULL;
++ if(cmd->flags & MMC_RSP_PRESENT && cmd->flags & MMC_RSP_136) {
++ /* R2 */
++ buf[12] = 0xff;
++ data = ASIC3_MMC_REG(host, SD_CTRL, Response0);
++ buf[13] = data & 0xff;
++ buf[14] = data >> 8;
++ data = ASIC3_MMC_REG(host, SD_CTRL, Response1);
++ buf[15] = data & 0xff;
++ buf[8] = data >> 8;
++ data = ASIC3_MMC_REG(host, SD_CTRL, Response2);
++ buf[9] = data & 0xff;
++ buf[10] = data >> 8;
++ data = ASIC3_MMC_REG(host, SD_CTRL, Response3);
++ buf[11] = data & 0xff;
++ buf[4] = data >> 8;
++ data = ASIC3_MMC_REG(host, SD_CTRL, Response4);
++ buf[5] = data & 0xff;
++ buf[6] = data >> 8;
++ data = ASIC3_MMC_REG(host, SD_CTRL, Response5);
++ buf[7] = data & 0xff;
++ buf[0] = data >> 8;
++ data = ASIC3_MMC_REG(host, SD_CTRL, Response6);
++ buf[1] = data & 0xff;
++ buf[2] = data >> 8;
++ data = ASIC3_MMC_REG(host, SD_CTRL, Response7);
++ buf[3] = data & 0xff;
++ } else if(cmd->flags & MMC_RSP_PRESENT) {
++ /* R1, R1B, R3 */
++ data = ASIC3_MMC_REG(host, SD_CTRL, Response0);
++ buf[0] = data & 0xff;
++ buf[1] = data >> 8;
++ data = ASIC3_MMC_REG(host, SD_CTRL, Response1);
++ buf[2] = data & 0xff;
++ buf[3] = data >> 8;
++ }
++ DBG("Response: %08x %08x %08x %08x\n", cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]);
++
++ if(buffer_stat & SD_CTRL_BUFFERSTATUS_CMD_TIMEOUT) {
++ cmd->error = MMC_ERR_TIMEOUT;
++ } else if((buffer_stat & SD_CTRL_BUFFERSTATUS_CRC_ERROR) && (cmd->flags & MMC_RSP_CRC)) {
++ cmd->error = MMC_ERR_BADCRC;
++ } else if(buffer_stat &
++ (
++ SD_CTRL_BUFFERSTATUS_ILLEGAL_ACCESS
++ | SD_CTRL_BUFFERSTATUS_CMD_INDEX_ERROR
++ | SD_CTRL_BUFFERSTATUS_STOP_BIT_END_ERROR
++ | SD_CTRL_BUFFERSTATUS_BUFFER_OVERFLOW
++ | SD_CTRL_BUFFERSTATUS_BUFFER_UNDERFLOW
++ | SD_CTRL_BUFFERSTATUS_DATA_TIMEOUT
++ )
++ ) {
++ DBG("Buffer status ERROR 0x%04x - inside check buffer\n", buffer_stat);
++ DBG("detail0 error status 0x%04x\n", ASIC3_MMC_REG(host, SD_CTRL, ErrorStatus0));
++ DBG("detail1 error status 0x%04x\n", ASIC3_MMC_REG(host, SD_CTRL, ErrorStatus1));
++ cmd->error = MMC_ERR_FAILED;
++ }
++
++ if(cmd->error == MMC_ERR_NONE) {
++ switch (cmd->opcode) {
++ case SD_APP_SET_BUS_WIDTH:
++ if(cmd->arg == SD_BUS_WIDTH_4) {
++ host->clock_for_sd = SD_CTRL_CARDCLOCKCONTROL_FOR_SD_CARD;
++ ASIC3_MMC_REG(host, SD_CTRL, MemCardOptionSetup) =
++ MEM_CARD_OPTION_REQUIRED
++ | MEM_CARD_OPTION_DATA_RESPONSE_TIMEOUT(14)
++ | MEM_CARD_OPTION_C2_MODULE_NOT_PRESENT
++ | MEM_CARD_OPTION_DATA_XFR_WIDTH_4;
++ } else {
++ host->clock_for_sd = 0;
++ ASIC3_MMC_REG(host, SD_CTRL, MemCardOptionSetup) =
++ MEM_CARD_OPTION_REQUIRED
++ | MEM_CARD_OPTION_DATA_RESPONSE_TIMEOUT(14)
++ | MEM_CARD_OPTION_C2_MODULE_NOT_PRESENT
++ | MEM_CARD_OPTION_DATA_XFR_WIDTH_1;
++ }
++ break;
++ case MMC_SELECT_CARD:
++ if((cmd->arg >> 16) == 0) {
++ /* We have been deselected. */
++ ASIC3_MMC_REG(host, SD_CTRL, MemCardOptionSetup) =
++ MEM_CARD_OPTION_REQUIRED
++ | MEM_CARD_OPTION_DATA_RESPONSE_TIMEOUT(14)
++ | MEM_CARD_OPTION_C2_MODULE_NOT_PRESENT
++ | MEM_CARD_OPTION_DATA_XFR_WIDTH_1;
++ }
++ }
++ }
++
++ /*
++ * If there is data to handle we enable data IRQs here, and we will
++ * ultimatley finish the request in the mmc_data_end_irq handler.
++ */
++ if(host->data && (cmd->error == MMC_ERR_NONE)){
++ if(host->data->flags & MMC_DATA_READ) {
++ /* Enable the read enable interrupt */
++ ASIC3_MMC_REG(host, SD_CTRL, IntMaskBuffer) &=
++ ~SD_CTRL_INTMASKBUFFER_BUFFER_READ_ENABLE;
++ } else {
++ /* Enable the write enable interrupt */
++ ASIC3_MMC_REG(host, SD_CTRL, IntMaskBuffer) &=
++ ~SD_CTRL_INTMASKBUFFER_BUFFER_WRITE_ENABLE;
++ }
++ } else {
++ /* There's no data, or we encountered an error, so finish now. */
++ mmc_finish_request(host);
++ }
++
++ return;
++}
++
++static void hwinit2_irqsafe(struct asic3_mmc_host *host);
++
++static irqreturn_t
++mmc_irq(int irq, void *irq_desc)
++{
++ struct asic3_mmc_host *host;
++ unsigned int breg, bmask, bstatus, creg, cmask, cstatus;
++
++ host = irq_desc;
++
++ /* asic3 bstatus has errors */
++ bstatus = ASIC3_MMC_REG(host, SD_CTRL, BufferCtrl);
++ bmask = ASIC3_MMC_REG(host, SD_CTRL, IntMaskBuffer);
++ cstatus = ASIC3_MMC_REG(host, SD_CTRL, CardStatus);
++ cmask = ASIC3_MMC_REG(host, SD_CTRL, IntMaskCard);
++ breg = bstatus & ~bmask & ~DONT_CARE_BUFFER_BITS;
++ creg = cstatus & ~cmask & ~DONT_CARE_CARD_BITS;
++
++ if (!breg && !creg) {
++ /* This occurs sometimes for no known reason. It doesn't hurt
++ * anything, so I don't print it. */
++ ASIC3_MMC_REG(host, SD_CTRL, IntMaskBuffer) &= ~breg;
++ ASIC3_MMC_REG(host, SD_CTRL, IntMaskCard) &= ~creg;
++ goto out;
++ }
++
++ while (breg || creg) {
++
++ /* XXX TODO: Need to handle errors in breg here. */
++
++ /*
++ * Card insert/remove. The mmc controlling code is stateless. That
++ * is, it doesn't care if it was an insert or a remove. It treats
++ * both the same.
++ */
++ /* XXX Asic3 has _3 versions of these status bits, too, for a second slot, perhaps? */
++ if (creg & (SD_CTRL_CARDSTATUS_CARD_INSERTED_0 | SD_CTRL_CARDSTATUS_CARD_REMOVED_0)) {
++ ASIC3_MMC_REG(host, SD_CTRL, CardStatus) &=
++ ~(SD_CTRL_CARDSTATUS_CARD_REMOVED_0 | SD_CTRL_CARDSTATUS_CARD_INSERTED_0);
++ if(creg & SD_CTRL_CARDSTATUS_CARD_INSERTED_0) {
++ hwinit2_irqsafe(host);
++ }
++ mmc_detect_change(host->mmc,1);
++ }
++
++ /* Command completion */
++ if (creg & SD_CTRL_CARDSTATUS_RESPONSE_END) {
++ ASIC3_MMC_REG(host, SD_CTRL, CardStatus) &=
++ ~(SD_CTRL_CARDSTATUS_RESPONSE_END);
++ mmc_cmd_irq(host, bstatus);
++ }
++
++ /* Data transfer */
++ if (breg & (SD_CTRL_BUFFERSTATUS_BUFFER_READ_ENABLE | SD_CTRL_BUFFERSTATUS_BUFFER_WRITE_ENABLE)) {
++ ASIC3_MMC_REG(host, SD_CTRL, BufferCtrl) &=
++ ~(SD_CTRL_BUFFERSTATUS_BUFFER_WRITE_ENABLE | SD_CTRL_BUFFERSTATUS_BUFFER_READ_ENABLE);
++ tasklet_schedule(&mmc_data_read_tasklet);
++ }
++
++ /* Data transfer completion */
++ if (creg & SD_CTRL_CARDSTATUS_RW_END) {
++ ASIC3_MMC_REG(host, SD_CTRL, CardStatus) &= ~(SD_CTRL_CARDSTATUS_RW_END);
++ mmc_data_end_irq(host);
++ }
++
++ /* Check status - keep going until we've handled it all */
++ bstatus = ASIC3_MMC_REG(host, SD_CTRL, BufferCtrl);
++ bmask = ASIC3_MMC_REG(host, SD_CTRL, IntMaskBuffer);
++ cstatus = ASIC3_MMC_REG(host, SD_CTRL, CardStatus);
++ cmask = ASIC3_MMC_REG(host, SD_CTRL, IntMaskCard);
++ breg = bstatus & ~bmask & ~DONT_CARE_BUFFER_BITS;
++ creg = cstatus & ~cmask & ~DONT_CARE_CARD_BITS;
++ }
++
++out:
++ /* Ensure all interrupt sources are cleared */
++ ASIC3_MMC_REG(host, SD_CTRL, BufferCtrl) = 0;
++ ASIC3_MMC_REG(host, SD_CTRL, CardStatus) = 0;
++ return IRQ_HANDLED;
++}
++
++static void
++mmc_start_data(struct asic3_mmc_host *host, struct mmc_data *data)
++{
++ DBG("setup data transfer: blocksize %08x nr_blocks %d, page: %08x, offset: %08x\n", data->blksz,
++ data->blocks, (int)data->sg->page, data->sg->offset);
++
++ host->sg_len = data->sg_len;
++ host->sg_ptr = data->sg;
++ host->sg_off = 0;
++ host->data = data;
++
++ /* Set transfer length and blocksize */
++ ASIC3_MMC_REG(host, SD_CTRL, TransferSectorCount) = data->blocks;
++ ASIC3_MMC_REG(host, SD_CTRL, MemCardXferDataLen) = data->blksz;
++}
++
++/* Process requests from the MMC layer */
++static void
++mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
++{
++ struct asic3_mmc_host *host = mmc_priv(mmc);
++
++ WARN_ON(host->mrq != NULL);
++
++ host->mrq = mrq;
++
++ /* If we're performing a data request we need to setup some
++ extra information */
++ if(mrq->data) {
++ mmc_start_data(host, mrq->data);
++ }
++
++ mmc_start_command(host, mrq->cmd);
++}
++
++/* Set MMC clock / power.
++ * Note: This controller uses a simple divider scheme therefore it cannot run
++ * a MMC card at full speed (20MHz). The max clock is 24MHz on SD, but as MMC
++ * wont run that fast, it has to be clocked at 12MHz which is the next slowest
++ * setting. This is likely not an issue because we are doing single 16-bit
++ * writes for data I/O.
++ */
++static void
++mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
++{
++ struct asic3_mmc_host *host = mmc_priv(mmc);
++ u32 clk = 0;
++
++ DBG("clock %uHz busmode %u powermode %u Vdd %u\n",
++ ios->clock, ios->bus_mode, ios->power_mode, ios->vdd);
++
++ if (ios->clock) {
++ clk = 0x80; /* slowest by default */
++ if(ios->clock >= 24000000 / 256) clk >>= 1;
++ if(ios->clock >= 24000000 / 128) clk >>= 1;
++ if(ios->clock >= 24000000 / 64) clk >>= 1;
++ if(ios->clock >= 24000000 / 32) clk >>= 1;
++ if(ios->clock >= 24000000 / 16) clk >>= 1;
++ if(ios->clock >= 24000000 / 8) clk >>= 1;
++ if(ios->clock >= 24000000 / 4) clk >>= 1;
++ if(ios->clock >= 24000000 / 2) clk >>= 1;
++ if(ios->clock >= 24000000 / 1) clk >>= 1;
++ if(clk == 0) { /* For fastest speed we disable the divider. */
++ ASIC3_MMC_REG(host, SD_CONFIG, ClockMode) = 0;
++ } else {
++ ASIC3_MMC_REG(host, SD_CONFIG, ClockMode) = 1;
++ }
++ ASIC3_MMC_REG(host, SD_CTRL, CardClockCtrl) = 0;
++ ASIC3_MMC_REG(host, SD_CTRL, CardClockCtrl) =
++ host->clock_for_sd
++ | SD_CTRL_CARDCLOCKCONTROL_ENABLE_CLOCK
++ | clk;
++ msleep(10);
++ } else {
++ ASIC3_MMC_REG(host, SD_CTRL, CardClockCtrl) = 0;
++ }
++
++ switch (ios->power_mode) {
++ case MMC_POWER_OFF:
++ ASIC3_MMC_REG(host, SD_CONFIG, SDHC_Power1) = 0;
++ msleep(1);
++ break;
++ case MMC_POWER_UP:
++ break;
++ case MMC_POWER_ON:
++ ASIC3_MMC_REG(host, SD_CONFIG, SDHC_Power1) = SD_CONFIG_POWER1_PC_33V;
++ msleep(20);
++ break;
++ }
++}
++
++static int
++mmc_get_ro(struct mmc_host *mmc)
++{
++ struct asic3_mmc_host *host = mmc_priv(mmc);
++
++ /* Call custom handler for RO status */
++ if(host->hwconfig->mmc_get_ro) {
++ /* Special case for cards w/o WP lock (like miniSD) */
++ if (host->hwconfig->mmc_get_ro == (void*)-1) {
++ return 0;
++ } else {
++ struct platform_device *pdev = to_platform_device(mmc_dev(mmc));
++ return host->hwconfig->mmc_get_ro(pdev);
++ }
++ }
++
++ /* WRITE_PROTECT is active low */
++ return (ASIC3_MMC_REG(host, SD_CTRL, CardStatus) & SD_CTRL_CARDSTATUS_WRITE_PROTECT)?0:1;
++}
++
++static struct mmc_host_ops mmc_ops = {
++ .request = mmc_request,
++ .set_ios = mmc_set_ios,
++ .get_ro = mmc_get_ro,
++};
++
++static void
++hwinit2_irqsafe(struct asic3_mmc_host *host)
++{
++ ASIC3_MMC_REG(host, SD_CONFIG, Addr1) = 0x0000;
++ ASIC3_MMC_REG(host, SD_CONFIG, Addr0) = 0x0800;
++
++ ASIC3_MMC_REG(host, SD_CONFIG, ClkStop) = SD_CONFIG_CLKSTOP_ENABLE_ALL;
++ ASIC3_MMC_REG(host, SD_CONFIG, SDHC_CardDetect) = 2;
++ ASIC3_MMC_REG(host, SD_CONFIG, Command) = SD_CONFIG_COMMAND_MAE;
++
++ ASIC3_MMC_REG(host, SD_CTRL, SoftwareReset) = 0; /* reset on */
++ mdelay(2);
++
++ ASIC3_MMC_REG(host, SD_CTRL, SoftwareReset) = 1; /* reset off */
++ mdelay(2);
++
++ ASIC3_MMC_REG(host, SD_CTRL, MemCardOptionSetup) =
++ MEM_CARD_OPTION_REQUIRED
++ | MEM_CARD_OPTION_DATA_RESPONSE_TIMEOUT(14)
++ | MEM_CARD_OPTION_C2_MODULE_NOT_PRESENT
++ | MEM_CARD_OPTION_DATA_XFR_WIDTH_1
++ ;
++ host->clock_for_sd = 0;
++
++ ASIC3_MMC_REG(host, SD_CTRL, CardClockCtrl) = 0;
++ ASIC3_MMC_REG(host, SD_CTRL, CardStatus) = 0;
++ ASIC3_MMC_REG(host, SD_CTRL, BufferCtrl) = 0;
++ ASIC3_MMC_REG(host, SD_CTRL, ErrorStatus0) = 0;
++ ASIC3_MMC_REG(host, SD_CTRL, ErrorStatus1) = 0;
++ ASIC3_MMC_REG(host, SD_CTRL, StopInternal) = 0;
++
++ ASIC3_MMC_REG(host, SDIO_CTRL, ClocknWaitCtrl) = 0x100;
++ /* *((unsigned short *)(((char *)host->ctl_base) + 0x938)) = 0x100; */
++
++ ASIC3_MMC_REG(host, SD_CONFIG, ClockMode) = 0;
++ ASIC3_MMC_REG(host, SD_CTRL, CardClockCtrl) = 0;
++
++ mdelay(1);
++
++
++ ASIC3_MMC_REG(host, SD_CTRL, IntMaskCard) = ~(
++ SD_CTRL_INTMASKCARD_RESPONSE_END
++ | SD_CTRL_INTMASKCARD_RW_END
++ | SD_CTRL_INTMASKCARD_CARD_REMOVED_0
++ | SD_CTRL_INTMASKCARD_CARD_INSERTED_0
++#if 0
++ | SD_CTRL_INTMASKCARD_CARD_REMOVED_3
++ | SD_CTRL_INTMASKCARD_CARD_INSERTED_3
++#endif
++ )
++ ; /* check */
++ ASIC3_MMC_REG(host, SD_CTRL, IntMaskBuffer) = 0xffff; /* IRQs off */
++
++ /*
++ * ASIC3_MMC_REG(host, SD_CTRL, TransactionCtrl) = SD_CTRL_TRANSACTIONCONTROL_SET;
++ * Wince has 0x1000
++ */
++ /* ASIC3_MMC_REG(host, SD_CTRL, TransactionCtrl) = 0x1000; */
++
++
++ asic3_set_sdhwctrl(host->asic3_dev, ASIC3_SDHWCTRL_SDPWR, ASIC3_SDHWCTRL_SDPWR); /* turn on power at controller(?) */
++
++}
++
++static void
++hwinit(struct asic3_mmc_host *host, struct platform_device *pdev)
++{
++ /* Call custom handler for enabling clock (if needed) */
++ if(host->hwconfig->set_mmc_clock)
++ host->hwconfig->set_mmc_clock(pdev, MMC_CLOCK_ENABLED);
++
++ /* Not sure if it must be done bit by bit, but leaving as-is */
++ asic3_set_sdhwctrl(host->asic3_dev, ASIC3_SDHWCTRL_LEVCD, ASIC3_SDHWCTRL_LEVCD);
++ asic3_set_sdhwctrl(host->asic3_dev, ASIC3_SDHWCTRL_LEVWP, ASIC3_SDHWCTRL_LEVWP);
++ asic3_set_sdhwctrl(host->asic3_dev, ASIC3_SDHWCTRL_SUSPEND, 0);
++ asic3_set_sdhwctrl(host->asic3_dev, ASIC3_SDHWCTRL_PCLR, 0);
++
++ asic3_set_clock_cdex (host->asic3_dev,
++ CLOCK_CDEX_EX1 | CLOCK_CDEX_EX0, CLOCK_CDEX_EX1 | CLOCK_CDEX_EX0);
++ msleep(1);
++
++ asic3_set_clock_sel (host->asic3_dev,
++ CLOCK_SEL_SD_HCLK_SEL | CLOCK_SEL_SD_BCLK_SEL,
++ CLOCK_SEL_SD_HCLK_SEL | 0); /* ? */
++
++ asic3_set_clock_cdex (host->asic3_dev,
++ CLOCK_CDEX_SD_HOST | CLOCK_CDEX_SD_BUS,
++ CLOCK_CDEX_SD_HOST | CLOCK_CDEX_SD_BUS);
++ msleep(1);
++
++ asic3_set_extcf_select(host->asic3_dev, ASIC3_EXTCF_SD_MEM_ENABLE, ASIC3_EXTCF_SD_MEM_ENABLE);
++
++ /* Long Delay */
++ if( !machine_is_h4700())
++ msleep(500);
++
++ hwinit2_irqsafe(host);
++}
++
++#ifdef CONFIG_PM
++static int
++mmc_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ struct mmc_host *mmc = platform_get_drvdata(pdev);
++ struct asic3_mmc_host *host = mmc_priv(mmc);
++ int ret;
++
++ ret = mmc_suspend_host(mmc, state);
++
++ if (ret) {
++ printk(KERN_ERR DRIVER_NAME ": Could not suspend MMC host, hardware not suspended");
++ return ret;
++ }
++
++ /* disable the card insert / remove interrupt while sleeping */
++ ASIC3_MMC_REG(host, SD_CTRL, IntMaskCard) = ~(
++ SD_CTRL_INTMASKCARD_RESPONSE_END
++ | SD_CTRL_INTMASKCARD_RW_END);
++
++ /* disable clock */
++ ASIC3_MMC_REG(host, SD_CTRL, CardClockCtrl) = 0;
++ ASIC3_MMC_REG(host, SD_CONFIG, ClkStop) = 0;
++
++ /* power down */
++ ASIC3_MMC_REG(host, SD_CONFIG, SDHC_Power1) = 0;
++
++ asic3_set_clock_cdex (host->asic3_dev,
++ CLOCK_CDEX_SD_HOST | CLOCK_CDEX_SD_BUS, 0);
++
++ /* disable core clock */
++ if(host->hwconfig->set_mmc_clock)
++ host->hwconfig->set_mmc_clock(pdev, MMC_CLOCK_DISABLED);
++
++ /* Put in suspend mode */
++ asic3_set_sdhwctrl(host->asic3_dev, ASIC3_SDHWCTRL_SUSPEND, ASIC3_SDHWCTRL_SUSPEND);
++ return 0;
++}
++
++static int
++mmc_resume(struct platform_device *pdev)
++{
++ struct mmc_host *mmc = platform_get_drvdata(pdev);
++ struct asic3_mmc_host *host = mmc_priv(mmc);
++
++ printk(KERN_INFO "%s: starting resume\n", DRIVER_NAME);
++
++ asic3_set_sdhwctrl(host->asic3_dev, ASIC3_SDHWCTRL_SUSPEND, 0);
++ hwinit(host, pdev);
++
++ /* re-enable card remove / insert interrupt */
++ ASIC3_MMC_REG(host, SD_CTRL, IntMaskCard) = ~(
++ SD_CTRL_INTMASKCARD_RESPONSE_END
++ | SD_CTRL_INTMASKCARD_RW_END
++ | SD_CTRL_INTMASKCARD_CARD_REMOVED_0
++ | SD_CTRL_INTMASKCARD_CARD_INSERTED_0 );
++
++ mmc_resume_host(mmc);
++
++ printk(KERN_INFO "%s: finished resume\n", DRIVER_NAME);
++ return 0;
++}
++#endif
++
++static int
++mmc_probe(struct platform_device *pdev)
++{
++ struct mmc_host *mmc;
++ struct asic3_mmc_host *host = NULL;
++ int retval = 0;
++ struct tmio_mmc_hwconfig *mmc_config = (struct tmio_mmc_hwconfig *)pdev->dev.platform_data;
++
++ /* bus_shift is mandatory */
++ if (!mmc_config) {
++ printk(KERN_ERR DRIVER_NAME ": Invalid configuration\n");
++ return -EINVAL;
++ }
++
++ mmc = mmc_alloc_host(sizeof(struct asic3_mmc_host) + 128, &pdev->dev);
++ if (!mmc) {
++ retval = -ENOMEM;
++ goto exceptional_return;
++ }
++
++ host = mmc_priv(mmc);
++ host->mmc = mmc;
++ platform_set_drvdata(pdev, mmc);
++
++ host->ctl_base = 0;
++ host->hwconfig = mmc_config;
++ host->bus_shift = mmc_config->address_shift;
++ host->asic3_dev = pdev->dev.parent;
++ host->clock_for_sd = 0;
++
++ tasklet_init(&mmc_data_read_tasklet, mmc_data_transfer, (unsigned long)host);
++
++ host->ctl_base = ioremap_nocache ((unsigned long)pdev->resource[0].start, pdev->resource[0].end - pdev->resource[0].start);
++ if(!host->ctl_base){
++ printk(KERN_ERR DRIVER_NAME ": Could not map ASIC3 SD controller\n");
++ retval = -ENODEV;
++ goto exceptional_return;
++ }
++
++ printk(DRIVER_NAME ": ASIC3 MMC/SD Driver, controller at 0x%lx\n", (unsigned long)pdev->resource[0].start);
++
++ mmc->ops = &mmc_ops;
++ mmc->caps = MMC_CAP_4_BIT_DATA;
++ mmc->f_min = 46875; /* ARIC: not sure what these should be */
++ mmc->f_max = 24000000; /* ARIC: not sure what these should be */
++ mmc->ocr_avail = MMC_VDD_32_33;
++
++ hwinit(host, pdev);
++
++
++ host->irq = pdev->resource[1].start;
++
++ retval = request_irq(host->irq, mmc_irq, 0, DRIVER_NAME, host);
++ if(retval) {
++ printk(KERN_ERR DRIVER_NAME ": Unable to get interrupt\n");
++ retval = -ENODEV;
++ goto exceptional_return;
++ }
++ set_irq_type(host->irq, IRQT_FALLING);
++
++ mmc_add_host(mmc);
++
++#ifdef CONFIG_PM
++ // resume_timer.function = resume_timer_callback;
++ // resume_timer.data = 0;
++ // init_timer(&resume_timer);
++#endif
++
++ return 0;
++
++exceptional_return:
++ if (mmc) {
++ mmc_free_host(mmc);
++ }
++ if(host && host->ctl_base) iounmap(host->ctl_base);
++ return retval;
++}
++
++static int
++mmc_remove(struct platform_device *pdev)
++{
++ struct mmc_host *mmc = platform_get_drvdata(pdev);
++
++ platform_set_drvdata(pdev, NULL);
++
++ if (mmc) {
++ struct asic3_mmc_host *host = mmc_priv(mmc);
++ mmc_remove_host(mmc);
++ free_irq(host->irq, host);
++ /* FIXME - we might want to consider stopping the chip here... */
++ iounmap(host->ctl_base);
++ mmc_free_host(mmc); /* FIXME - why does this call hang? */
++ }
++ return 0;
++}
++
++/* ------------------- device registration ----------------------- */
++
++static struct platform_driver mmc_asic3_driver = {
++ .driver = {
++ .name = DRIVER_NAME,
++ },
++ .probe = mmc_probe,
++ .remove = mmc_remove,
++#ifdef CONFIG_PM
++ .suspend = mmc_suspend,
++ .resume = mmc_resume,
++#endif
++};
++
++static int __init mmc_init(void)
++{
++ return platform_driver_register(&mmc_asic3_driver);
++}
++
++static void __exit mmc_exit(void)
++{
++ platform_driver_unregister(&mmc_asic3_driver);
++}
++
++late_initcall(mmc_init);
++module_exit(mmc_exit);
++
++MODULE_DESCRIPTION("HTC ASIC3 SD/MMC driver");
++MODULE_AUTHOR("Aric Blumer, SDG Systems, LLC");
++MODULE_LICENSE("GPL");
++
+Index: linux-2.6.26-rc4/drivers/mmc/host/asic3_mmc.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26-rc4/drivers/mmc/host/asic3_mmc.h 2008-06-01 18:49:45.000000000 +0100
+@@ -0,0 +1,25 @@
++#ifndef __ASIC3_MMC_H
++#define __ASIC3_MMC_H
++
++#define DRIVER_NAME "asic3_mmc"
++
++#ifdef CONFIG_MMC_DEBUG
++#define DBG(x...) printk(DRIVER_NAME ": " x)
++#else
++#define DBG(x...) do { } while (0)
++#endif
++
++/* Response types */
++#define APP_CMD 0x0040
++
++#define SD_CONFIG_CLKSTOP_ENABLE_ALL 0x1f
++
++#define DONT_CARE_CARD_BITS ( \
++ SD_CTRL_INTMASKCARD_SIGNAL_STATE_PRESENT_3 \
++ | SD_CTRL_INTMASKCARD_WRITE_PROTECT \
++ | SD_CTRL_INTMASKCARD_UNK6 \
++ | SD_CTRL_INTMASKCARD_SIGNAL_STATE_PRESENT_0 \
++ )
++#define DONT_CARE_BUFFER_BITS ( SD_CTRL_INTMASKBUFFER_UNK7 | SD_CTRL_INTMASKBUFFER_CMD_BUSY )
++
++#endif // __ASIC3_MMC_H
+Index: linux-2.6.26-rc4/drivers/input/keyboard/Makefile
+===================================================================
+--- linux-2.6.26-rc4.orig/drivers/input/keyboard/Makefile 2008-05-26 19:08:11.000000000 +0100
++++ linux-2.6.26-rc4/drivers/input/keyboard/Makefile 2008-06-01 18:52:37.000000000 +0100
+@@ -15,6 +15,7 @@
+ obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
+ obj-$(CONFIG_KEYBOARD_CORGI) += corgikbd.o
+ obj-$(CONFIG_KEYBOARD_SPITZ) += spitzkbd.o
++obj-$(CONFIG_KEYBOARD_ASIC3) += asic3_keys.o
+ obj-$(CONFIG_KEYBOARD_TOSA) += tosakbd.o
+ obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o
+ obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o
+Index: linux-2.6.26-rc4/drivers/input/keyboard/asic3_keys.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26-rc4/drivers/input/keyboard/asic3_keys.c 2008-06-01 18:49:45.000000000 +0100
+@@ -0,0 +1,131 @@
++/*
++ * Generic buttons driver for ASIC3 SoC.
++ *
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License. See the file COPYING in the main directory of this archive for
++ * more details.
++ *
++ * Copyright (C) 2003 Joshua Wise
++ * Copyright (C) 2005 Pawel Kolodziejski
++ * Copyright (C) 2006 Paul Sokolovsky
++ *
++ */
++
++#include <linux/input.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/interrupt.h>
++#include <linux/platform_device.h>
++#include <linux/irq.h>
++#include <linux/soc/asic3_base.h>
++#include <asm/mach/arch.h>
++#include <asm/mach/map.h>
++#include <asm/arch/irqs.h>
++#include <asm/hardware.h>
++#include <asm/hardware/ipaq-asic3.h>
++#include <asm/hardware/asic3_keys.h>
++
++static irqreturn_t asic3_keys_asic_handle(int irq, void *data)
++{
++ struct asic3_keys_platform_data *pdata = data;
++ int i, base_irq;
++
++ base_irq = asic3_irq_base(pdata->asic3_dev);
++ for (i = 0; i < pdata->nbuttons; i++) {
++ struct asic3_keys_button *b = &pdata->buttons[i];
++ if ((base_irq + b->gpio) == irq) {
++ int state = !!asic3_gpio_get_value(pdata->asic3_dev, b->gpio);
++
++ if (pdata->buttons[i].type == EV_SW)
++ input_report_switch(pdata->input, pdata->buttons[i].keycode, state ^ b->active_low);
++ else
++ input_report_key(pdata->input, b->keycode, state ^ b->active_low);
++ input_sync(pdata->input);
++ }
++ }
++
++ return IRQ_HANDLED;
++}
++
++static int __devinit asic3_keys_probe(struct platform_device *pdev)
++{
++ struct asic3_keys_platform_data *pdata = pdev->dev.platform_data;
++ int i, base_irq;
++ int j, ret;
++
++ pdata->input = input_allocate_device();
++
++ base_irq = asic3_irq_base(pdata->asic3_dev);
++
++ for (i = 0; i < pdata->nbuttons; i++) {
++ struct asic3_keys_button *b = &pdata->buttons[i];
++ set_bit(b->keycode, pdata->input->keybit);
++ ret=request_irq(base_irq + b->gpio, asic3_keys_asic_handle, SA_SAMPLE_RANDOM, b->desc, pdata);
++ if (ret)
++ {
++ printk(KERN_NOTICE "Failed to allocate asic3_keys irq=%d.\n",b->gpio);
++
++ for(j=0; j<i ; j++)
++ free_irq(base_irq + pdata->buttons[i].gpio, NULL);
++
++ input_unregister_device (pdata->input);
++
++ return -ENODEV;
++ }
++
++ set_irq_type(base_irq + b->gpio, IRQT_BOTHEDGE);
++ if (pdata->buttons[i].type == EV_SW) {
++ pdata->input->evbit[0] |= BIT(EV_SW);
++ set_bit(b->keycode, pdata->input->swbit);
++ } else {
++ pdata->input->evbit[0] |= BIT(EV_KEY);
++ set_bit(b->keycode, pdata->input->keybit);
++ }
++ }
++
++ pdata->input->name = pdev->name;
++ input_register_device(pdata->input);
++
++ return 0;
++}
++
++static int __devexit asic3_keys_remove(struct platform_device *pdev)
++{
++ struct asic3_keys_platform_data *pdata = pdev->dev.platform_data;
++ int i, base_irq;
++
++ base_irq = asic3_irq_base(pdata->asic3_dev);
++ for (i = 0; i < pdata->nbuttons; i++) {
++ free_irq(base_irq + pdata->buttons[i].gpio, NULL);
++ }
++
++ input_unregister_device(pdata->input);
++
++ return 0;
++}
++
++
++static struct platform_driver asic3_keys_driver = {
++ .probe = asic3_keys_probe,
++ .remove = __devexit_p(asic3_keys_remove),
++ .driver = {
++ .name = "asic3-keys",
++ },
++};
++
++static int __init asic3_keys_init(void)
++{
++ return platform_driver_register(&asic3_keys_driver);
++}
++
++static void __exit asic3_keys_exit(void)
++{
++ platform_driver_unregister(&asic3_keys_driver);
++}
++
++module_init(asic3_keys_init);
++module_exit(asic3_keys_exit);
++
++MODULE_AUTHOR("Joshua Wise, Pawel Kolodziejski, Paul Sokolovsky");
++MODULE_DESCRIPTION("Buttons driver for HTC ASIC3 SoC");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.26-rc4/include/asm-arm/arch-pxa/irqs.h
+===================================================================
+--- linux-2.6.26-rc4.orig/include/asm-arm/arch-pxa/irqs.h 2008-05-26 19:08:11.000000000 +0100
++++ linux-2.6.26-rc4/include/asm-arm/arch-pxa/irqs.h 2008-06-01 18:49:45.000000000 +0100
+@@ -184,6 +184,8 @@
+ defined(CONFIG_MACH_PCM027) || \
+ defined(CONFIG_MACH_MAGICIAN)
+ #define NR_IRQS (IRQ_BOARD_END)
++#elif defined(CONFIG_MACH_HTCUNIVERSAL)
++#define NR_IRQS (IRQ_BOARD_START + 96)
+ #else
+ #define NR_IRQS (IRQ_BOARD_START)
+ #endif
+Index: linux-2.6.26-rc4/include/linux/ioport.h
+===================================================================
+--- linux-2.6.26-rc4.orig/include/linux/ioport.h 2008-05-26 19:08:11.000000000 +0100
++++ linux-2.6.26-rc4/include/linux/ioport.h 2008-06-01 18:49:45.000000000 +0100
+@@ -59,6 +59,7 @@
+ #define IORESOURCE_IRQ_HIGHLEVEL (1<<2)
+ #define IORESOURCE_IRQ_LOWLEVEL (1<<3)
+ #define IORESOURCE_IRQ_SHAREABLE (1<<4)
++#define IORESOURCE_IRQ_SOC_SUBDEVICE (1<<5)
+
+ /* ISA PnP DMA specific bits (IORESOURCE_BITS) */
+ #define IORESOURCE_DMA_TYPE_MASK (3<<0)
+Index: linux-2.6.26-rc4/include/asm-arm/arch-pxa/serial.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26-rc4/include/asm-arm/arch-pxa/serial.h 2008-06-01 18:49:45.000000000 +0100
+@@ -0,0 +1,78 @@
++/*
++ * linux/include/asm-arm/arch-pxa/serial.h
++ *
++ * Author: Nicolas Pitre
++ * Copyright: (C) 2001 MontaVista Software 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.
++ */
++
++#include <asm/arch/pxa-regs.h>
++
++#define BAUD_BASE 921600
++
++/* Standard COM flags */
++#define STD_COM_FLAGS (ASYNC_SKIP_TEST)
++
++#define STD_SERIAL_PORT_DEFNS \
++ { \
++ type: PORT_PXA, \
++ xmit_fifo_size: 64, \
++ baud_base: BAUD_BASE, \
++ iomem_base: &FFUART, \
++ iomem_reg_shift: 2, \
++ io_type: SERIAL_IO_MEM, \
++ irq: IRQ_FFUART, \
++ flags: STD_COM_FLAGS, \
++ }, { \
++ type: PORT_PXA, \
++ xmit_fifo_size: 64, \
++ baud_base: BAUD_BASE, \
++ iomem_base: &STUART, \
++ iomem_reg_shift: 2, \
++ io_type: SERIAL_IO_MEM, \
++ irq: IRQ_STUART, \
++ flags: STD_COM_FLAGS, \
++ }, { \
++ type: PORT_PXA, \
++ xmit_fifo_size: 64, \
++ baud_base: BAUD_BASE, \
++ iomem_base: &BTUART, \
++ iomem_reg_shift: 2, \
++ io_type: SERIAL_IO_MEM, \
++ irq: IRQ_BTUART, \
++ flags: STD_COM_FLAGS, \
++ }
++
++#define EXTRA_SERIAL_PORT_DEFNS
++
++struct platform_pxa_serial_funcs {
++
++ /* Initialize whatever is connected to this serial port. */
++ void (*configure)(int state);
++#define PXA_UART_CFG_PRE_STARTUP 0
++#define PXA_UART_CFG_POST_STARTUP 1
++#define PXA_UART_CFG_PRE_SHUTDOWN 2
++#define PXA_UART_CFG_POST_SHUTDOWN 3
++
++ /* Enable or disable the individual transmitter/receiver submodules.
++ * On transceivers without echo cancellation (e.g. SIR)
++ * transmitter always has priority; e.g. if both bits are set,
++ * only the transmitter is enabled. */
++ void (*set_txrx)(int txrx);
++#define PXA_SERIAL_TX 1
++#define PXA_SERIAL_RX 2
++
++ /* Get the current state of tx/rx. */
++ int (*get_txrx)(void);
++
++ int (*suspend)(struct platform_device *dev, pm_message_t state);
++ int (*resume)(struct platform_device *dev);
++};
++
++void pxa_set_ffuart_info(struct platform_pxa_serial_funcs *ffuart_funcs);
++void pxa_set_btuart_info(struct platform_pxa_serial_funcs *btuart_funcs);
++void pxa_set_stuart_info(struct platform_pxa_serial_funcs *stuart_funcs);
++void pxa_set_hwuart_info(struct platform_pxa_serial_funcs *hwuart_funcs);
+Index: linux-2.6.26-rc4/drivers/serial/pxa.c
+===================================================================
+--- linux-2.6.26-rc4.orig/drivers/serial/pxa.c 2008-05-26 19:08:11.000000000 +0100
++++ linux-2.6.26-rc4/drivers/serial/pxa.c 2008-06-01 18:49:45.000000000 +0100
+@@ -47,6 +47,7 @@
+ #include <asm/io.h>
+ #include <asm/hardware.h>
+ #include <asm/irq.h>
++#include <asm/arch/serial.h>
+ #include <asm/arch/pxa-regs.h>
+
+
+@@ -60,6 +61,14 @@
+ char *name;
+ };
+
++
++#define IS_METHOD(dev, method) (dev && (dev)->platform_data && ((struct platform_pxa_serial_funcs *)(dev)->platform_data)->method)
++#define METHOD_CALL(dev, method) \
++ ((struct platform_pxa_serial_funcs *)(dev)->platform_data)->method()
++#define SAFE_METHOD_CALL(dev, method, args...) \
++ if (IS_METHOD(dev, method)) \
++ ((struct platform_pxa_serial_funcs *)(dev)->platform_data)->method(args)
++
+ static inline unsigned int serial_in(struct uart_pxa_port *up, int offset)
+ {
+ offset <<= 2;
+@@ -347,6 +356,9 @@
+ unsigned long flags;
+ int retval;
+
++ /* Perform platform-specific port initialization, if needed. */
++ SAFE_METHOD_CALL(port->dev, configure, PXA_UART_CFG_PRE_STARTUP);
++
+ if (port->line == 3) /* HWUART */
+ up->mcr |= UART_MCR_AFE;
+ else
+@@ -404,6 +416,12 @@
+ (void) serial_in(up, UART_IIR);
+ (void) serial_in(up, UART_MSR);
+
++ /*
++ * Perform platform-specific port initialization if needed
++ */
++ SAFE_METHOD_CALL(port->dev, configure, PXA_UART_CFG_POST_STARTUP);
++ SAFE_METHOD_CALL(port->dev, set_txrx, PXA_SERIAL_RX);
++
+ return 0;
+ }
+
+@@ -412,6 +430,8 @@
+ struct uart_pxa_port *up = (struct uart_pxa_port *)port;
+ unsigned long flags;
+
++ SAFE_METHOD_CALL(port->dev, configure, PXA_UART_CFG_PRE_SHUTDOWN);
++
+ free_irq(up->port.irq, up);
+
+ /*
+@@ -433,6 +453,8 @@
+ UART_FCR_CLEAR_RCVR |
+ UART_FCR_CLEAR_XMIT);
+ serial_out(up, UART_FCR, 0);
++
++ SAFE_METHOD_CALL(port->dev, configure, PXA_UART_CFG_POST_SHUTDOWN);
+ }
+
+ static void
+Index: linux-2.6.26-rc4/drivers/leds/Makefile
+===================================================================
+--- linux-2.6.26-rc4.orig/drivers/leds/Makefile 2008-05-26 19:08:11.000000000 +0100
++++ linux-2.6.26-rc4/drivers/leds/Makefile 2008-06-01 18:49:45.000000000 +0100
+@@ -14,6 +14,7 @@
+ obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o
+ obj-$(CONFIG_LEDS_WRAP) += leds-wrap.o
+ obj-$(CONFIG_LEDS_H1940) += leds-h1940.o
++obj-$(CONFIG_LEDS_ASIC3) += leds-asic3.o
+ obj-$(CONFIG_LEDS_COBALT_QUBE) += leds-cobalt-qube.o
+ obj-$(CONFIG_LEDS_COBALT_RAQ) += leds-cobalt-raq.o
+ obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o
+Index: linux-2.6.26-rc4/drivers/input/keyboard/Kconfig
+===================================================================
+--- linux-2.6.26-rc4.orig/drivers/input/keyboard/Kconfig 2008-05-26 19:08:11.000000000 +0100
++++ linux-2.6.26-rc4/drivers/input/keyboard/Kconfig 2008-06-01 18:52:13.000000000 +0100
+@@ -132,6 +132,13 @@
+ To compile this driver as a module, choose M here: the
+ module will be called stowaway.
+
++config KEYBOARD_ASIC3
++ tristate "Buttons on ASIC3 SoC GPIOs (iPaqs, etc.)"
++ depends on HTC_ASIC3
++ help
++ This enables support for the buttons attached to GPIOs of
++ HTC ASIC3 peripheral controller.
++
+ config KEYBOARD_CORGI
+ tristate "Corgi keyboard"
+ depends on PXA_SHARPSL
diff --git a/recipes/linux/linux-rp-2.6.26/pxa-serial-hack.patch b/recipes/linux/linux-rp-2.6.26/pxa-serial-hack.patch
new file mode 100644
index 0000000000..bf20f46a05
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.26/pxa-serial-hack.patch
@@ -0,0 +1,90 @@
+---
+ drivers/serial/8250.c | 5 +++++
+ drivers/serial/serial_core.c | 1 +
+ drivers/serial/serial_cs.c | 12 +++++++++---
+ include/linux/serial_core.h | 1 +
+ 4 files changed, 16 insertions(+), 3 deletions(-)
+
+Index: linux-2.6.20/drivers/serial/8250.c
+===================================================================
+--- linux-2.6.20.orig/drivers/serial/8250.c 2007-04-27 13:37:26.000000000 +0100
++++ linux-2.6.20/drivers/serial/8250.c 2007-04-27 13:38:16.000000000 +0100
+@@ -2429,7 +2429,12 @@
+ .driver_name = "serial",
+ .dev_name = "ttyS",
+ .major = TTY_MAJOR,
++#ifdef CONFIG_SERIAL_PXA
++ .minor = 64 + 4,
++ .name_base = 4,
++#else
+ .minor = 64,
++#endif
+ .nr = UART_NR,
+ .cons = SERIAL8250_CONSOLE,
+ };
+Index: linux-2.6.20/drivers/serial/serial_core.c
+===================================================================
+--- linux-2.6.20.orig/drivers/serial/serial_core.c 2007-02-04 18:44:54.000000000 +0000
++++ linux-2.6.20/drivers/serial/serial_core.c 2007-04-27 13:39:39.000000000 +0100
+@@ -2068,7 +2068,8 @@
+ printk(KERN_INFO "%s%s%s%d at %s (irq = %d) is a %s\n",
+ port->dev ? port->dev->bus_id : "",
+ port->dev ? ": " : "",
+- drv->dev_name, port->line, address, port->irq, uart_type(port));
++ drv->dev_name, port->line + drv->name_base, address, port->irq,
++ uart_type(port));
+ }
+
+ static void
+@@ -2183,6 +2184,7 @@
+ normal->owner = drv->owner;
+ normal->driver_name = drv->driver_name;
+ normal->name = drv->dev_name;
++ normal->name_base = drv->name_base;
+ normal->major = drv->major;
+ normal->minor_start = drv->minor;
+ normal->type = TTY_DRIVER_TYPE_SERIAL;
+Index: linux-2.6.20/include/linux/serial_core.h
+===================================================================
+--- linux-2.6.20.orig/include/linux/serial_core.h 2007-02-04 18:44:54.000000000 +0000
++++ linux-2.6.20/include/linux/serial_core.h 2007-04-27 13:37:27.000000000 +0100
+@@ -341,6 +341,7 @@
+ struct module *owner;
+ const char *driver_name;
+ const char *dev_name;
++ int name_base;
+ int major;
+ int minor;
+ int nr;
+Index: linux-2.6.20/drivers/serial/serial_cs.c
+===================================================================
+--- linux-2.6.20.orig/drivers/serial/serial_cs.c 2007-02-04 18:44:54.000000000 +0000
++++ linux-2.6.20/drivers/serial/serial_cs.c 2007-04-27 13:40:34.000000000 +0100
+@@ -390,7 +390,7 @@
+ kio_addr_t iobase, int irq)
+ {
+ struct uart_port port;
+- int line;
++ int line, linestart;
+
+ memset(&port, 0, sizeof (struct uart_port));
+ port.iobase = iobase;
+@@ -411,10 +411,16 @@
+ return -EINVAL;
+ }
+
++#if CONFIG_SERIAL_PXA
++ linestart = 4;
++#else
++ linestart = 0;
++#endif
++
+ info->line[info->ndev] = line;
+- sprintf(info->node[info->ndev].dev_name, "ttyS%d", line);
++ sprintf(info->node[info->ndev].dev_name, "ttyS%d", line+linestart);
+ info->node[info->ndev].major = TTY_MAJOR;
+- info->node[info->ndev].minor = 0x40 + line;
++ info->node[info->ndev].minor = 0x40 + line + linestart;
+ if (info->ndev > 0)
+ info->node[info->ndev - 1].next = &info->node[info->ndev];
+ info->ndev++;
diff --git a/recipes/linux/linux-rp-2.6.26/pxa_fb_overlay.patch b/recipes/linux/linux-rp-2.6.26/pxa_fb_overlay.patch
new file mode 100644
index 0000000000..49c59b3275
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.26/pxa_fb_overlay.patch
@@ -0,0 +1,26 @@
+---
+ drivers/video/pxafb.h | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+Index: linux-2.6.22/drivers/video/pxafb.h
+===================================================================
+--- linux-2.6.22.orig/drivers/video/pxafb.h 2007-09-25 15:44:42.000000000 +0200
++++ linux-2.6.22/drivers/video/pxafb.h 2007-09-25 15:45:07.000000000 +0200
+@@ -36,7 +36,7 @@
+ struct fb_bitfield transp;
+ };
+
+-#ifdef CONFIG_PXA27x
++#if defined(CONFIG_PXA27x) || defined(CONFIG_PXA3xx)
+ /* PXA Overlay Framebuffer Support */
+ struct overlayfb_info
+ {
+@@ -142,7 +142,7 @@
+ wait_queue_head_t ctrlr_wait;
+ struct work_struct task;
+
+-#ifdef CONFIG_PXA27x
++#if defined(CONFIG_PXA27x) || defined(CONFIG_PXA3xx)
+ /* PXA Overlay Framebuffer Support */
+ struct overlayfb_info *overlay1fb;
+ struct overlayfb_info *overlay2fb;
diff --git a/recipes/linux/linux-rp-2.6.26/serial-add-support-for-non-standard-xtals-to-16c950-driver.patch b/recipes/linux/linux-rp-2.6.26/serial-add-support-for-non-standard-xtals-to-16c950-driver.patch
new file mode 100644
index 0000000000..b513ba1466
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.26/serial-add-support-for-non-standard-xtals-to-16c950-driver.patch
@@ -0,0 +1,155 @@
+
+From: Petr Vandrovec <vandrove@vc.cvut.cz>
+
+Patch below adds support for using different prescaler than 16 for 16c950
+chips. This is needed for using Fujitsu-Siemens Connect2Air compact-flash
+card, which comes (apparently) with 806kHz clocks, and so you have to
+program prescaler for division by 7, and DLAB to 1, to get 115200Bd.
+
+To get card properly running you also have to add lines below to
+/etc/pcmcia/serial.opts so kernel knows that base speed is not 115200 but
+50400 (50400 * 16 = 806400; 806400 / 7 = 115200). As I've found no code
+specifying baud_rate in serial_cs, I assume that specifying it in
+serial.opts is right way to do this type of things.
+
+Patch also fixes problem that for UPF_MAGIC_MULTIPLIER maximum possible
+baud rate passed to uart code was uartclk / 16 while correct value for
+these devices (and for 16c950) is uartclk / 4.
+
+Patch also fixes problem that for UPF_MAGIC_MULTIPLIER devices with
+baud_rate 19200 or 9600 spd_cust did not work correctly. Not that such
+devices exist, but we should not ignore spd_cust, user probably knows why
+he asked for spd_cust.
+
+serial.opts:
+
+case "$MANFID-$FUNCID-$PRODID_1-$PRODID_2-$PRODID_3-$PRODID_4" in
+'0279,950b-2-GPRS Modem---')
+ SERIAL_OPTS="baud_base 50400"
+ ;;
+esac
+
+Cc: David Woodhouse <dwmw2@infradead.org>
+Signed-off-by: Andrew Morton <akpm@osdl.org>
+---
+
+ drivers/serial/8250.c | 82 +++++++++++++++++++++++++++++++++++++++-----------
+ 1 file changed, 64 insertions(+), 18 deletions(-)
+
+Index: linux-2.6.21/drivers/serial/8250.c
+===================================================================
+--- linux-2.6.21.orig/drivers/serial/8250.c 2007-07-01 16:59:52.000000000 +0100
++++ linux-2.6.21/drivers/serial/8250.c 2007-07-01 17:01:21.000000000 +0100
+@@ -1964,24 +1964,58 @@ static void serial8250_shutdown(struct u
+ serial_unlink_irq_chain(up);
+ }
+
+-static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int baud)
++static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int baud,
++ unsigned int *prescaler)
+ {
+- unsigned int quot;
+-
+- /*
+- * Handle magic divisors for baud rates above baud_base on
+- * SMSC SuperIO chips.
++ /*
++ * Use special handling only if user did not supply its own divider.
++ * spd_cust is defined in terms of baud_base, so always use default
++ * prescaler when spd_cust is requested.
+ */
+- if ((port->flags & UPF_MAGIC_MULTIPLIER) &&
+- baud == (port->uartclk/4))
+- quot = 0x8001;
+- else if ((port->flags & UPF_MAGIC_MULTIPLIER) &&
+- baud == (port->uartclk/8))
+- quot = 0x8002;
+- else
+- quot = uart_get_divisor(port, baud);
+
+- return quot;
++ *prescaler = 16;
++ if (baud != 38400 || (port->flags & UPF_SPD_MASK) != UPF_SPD_CUST) {
++ unsigned int quot = port->uartclk / baud;
++
++ /*
++ * Handle magic divisors for baud rates above baud_base on
++ * SMSC SuperIO chips.
++ */
++ if (port->flags & UPF_MAGIC_MULTIPLIER) {
++ if (quot == 4) {
++ return 0x8001;
++ } else if (quot == 8) {
++ return 0x8002;
++ }
++ }
++ if (port->type == PORT_16C950) {
++ /*
++ * This computes TCR value (4 to 16), not CPR value (which can
++ * be between 1.000 and 31.875) - chip I have uses XTAL of
++ * 806400Hz, and so a division by 7 is required to get 115200Bd.
++ * I'm leaving CPR disabled for now, until someone will
++ * hit even more exotic XTAL (it is needed to get 500kbps
++ * or 1000kbps from 18.432MHz XTAL, but I have no device
++ * which would benefit from doing that).
++ *
++ * If we can use divide by 16, use it. Otherwise look for
++ * better prescaler, from 15 to 4. If quotient cannot
++ * be divided by any integer value between 4 and 15, use 4.
++ */
++ if (quot & 0x0F) {
++ unsigned int div;
++
++ for (div = 15; div > 4; div--) {
++ if (quot % div == 0) {
++ break;
++ }
++ }
++ *prescaler = div;
++ return quot / div;
++ }
++ }
++ }
++ return uart_get_divisor(port, baud);
+ }
+
+ static void
+@@ -1991,7 +2025,7 @@ serial8250_set_termios(struct uart_port
+ struct uart_8250_port *up = (struct uart_8250_port *)port;
+ unsigned char cval, fcr = 0;
+ unsigned long flags;
+- unsigned int baud, quot;
++ unsigned int baud, quot, prescaler;
+
+ switch (termios->c_cflag & CSIZE) {
+ case CS5:
+@@ -2023,8 +2057,13 @@ serial8250_set_termios(struct uart_port
+ /*
+ * Ask the core to calculate the divisor for us.
+ */
+- baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
+- quot = serial8250_get_divisor(port, baud);
++ if (port->type == PORT_16C950 || (port->flags & UPF_MAGIC_MULTIPLIER)) {
++ baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/4);
++ } else {
++ baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
++ }
++ quot = serial8250_get_divisor(port, baud, &prescaler);
++
+
+ /*
+ * Oxford Semi 952 rev B workaround
+@@ -2139,6 +2178,13 @@ serial8250_set_termios(struct uart_port
+ serial_dl_write(up, quot);
+
+ /*
++ * Program prescaler for 16C950 chips.
++ */
++ if (up->port.type == PORT_16C950) {
++ serial_icr_write(up, UART_TCR, prescaler == 16 ? 0 : prescaler);
++ }
++
++ /*
+ * LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR
+ * is written without DLAB set, this mode will be disabled.
+ */
diff --git a/recipes/linux/linux-rp-2.6.26/sharpsl-rc-r1.patch b/recipes/linux/linux-rp-2.6.26/sharpsl-rc-r1.patch
new file mode 100644
index 0000000000..a9d939d076
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.26/sharpsl-rc-r1.patch
@@ -0,0 +1,555 @@
+This patch adds support for Sharp CE-RH2 on Spitz.
+
+It is not clean enough to be upstreamed:
+- It is a bit syslog-noisy.
+- Does not support other Zaurus models.
+- Maybe split to more parts:
+ * MAX1111 driver
+ * linear input device
+ * virtual keyboard on top of linear input device
+
+Index: linux-2.6.26/arch/arm/mach-pxa/spitz.c
+===================================================================
+--- linux-2.6.26.orig/arch/arm/mach-pxa/spitz.c 2008-07-13 21:51:29.000000000 +0000
++++ linux-2.6.26/arch/arm/mach-pxa/spitz.c 2008-07-15 10:11:15.000000000 +0000
+@@ -261,6 +261,13 @@
+ .id = -1,
+ };
+
++/*
++ * Spitz Remote Control Device
++ */
++static struct platform_device sharpsl_rc_device = {
++ .name = "sharpsl-remote-control",
++ .id = -1,
++};
+
+ /*
+ * Spitz LEDs
+@@ -522,6 +529,7 @@
+ &spitzscoop_device,
+ &spitzssp_device,
+ &spitzkbd_device,
++ &sharpsl_rc_device,
+ &spitzts_device,
+ &spitzbl_device,
+ &spitzled_device,
+Index: linux-2.6.26/drivers/input/keyboard/Kconfig
+===================================================================
+--- linux-2.6.26.orig/drivers/input/keyboard/Kconfig 2008-07-13 21:51:29.000000000 +0000
++++ linux-2.6.26/drivers/input/keyboard/Kconfig 2008-07-15 10:11:15.000000000 +0000
+@@ -175,6 +175,17 @@
+
+ Say Y only if you know, what you are doing!
+
++config SHARPSL_RC
++ tristate "Sharp SL-Cxx00 Remote Control"
++ depends on PXA_SHARPSL
++ default y
++ help
++ Say Y here to enable the remote on the Sharp Zaurus SL-Cxx00,
++ SL-C1000, SL-C3000 and Sl-C3100 series of PDAs.
++
++ To compile this driver as a module, choose M here: the
++ module will be called sharpsl_rc.
++
+ config KEYBOARD_AMIGA
+ tristate "Amiga keyboard"
+ depends on AMIGA
+Index: linux-2.6.26/drivers/input/keyboard/Makefile
+===================================================================
+--- linux-2.6.26.orig/drivers/input/keyboard/Makefile 2008-07-13 21:51:29.000000000 +0000
++++ linux-2.6.26/drivers/input/keyboard/Makefile 2008-07-15 10:11:15.000000000 +0000
+@@ -26,4 +26,5 @@
+ obj-$(CONFIG_KEYBOARD_HP7XX) += jornada720_kbd.o
+ obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o
+ obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o
++obj-$(CONFIG_SHARPSL_RC) += sharpsl_rc.o
+ obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o
+Index: linux-2.6.26/drivers/input/keyboard/sharpsl_rc.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.26/drivers/input/keyboard/sharpsl_rc.c 2008-07-15 10:11:15.000000000 +0000
+@@ -0,0 +1,319 @@
++/*
++ * Keyboard driver for Sharp Clamshell Models (SL-Cxx00)
++ *
++ * Copyright (c) 2004-2005 Richard Purdie
++ *
++ * Based on corgikbd.c and Sharp's RC driver
++ *
++ * 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.
++ *
++ */
++
++#define DEBUG 1
++#include <linux/delay.h>
++#include <linux/platform_device.h>
++#include <linux/init.h>
++#include <linux/input.h>
++#include <linux/interrupt.h>
++#include <linux/jiffies.h>
++#include <linux/module.h>
++#include <linux/slab.h>
++
++#include <asm/mach-types.h>
++#include <asm/arch/spitz.h>
++#include <asm/arch/akita.h>
++#include <asm/arch/corgi.h>
++
++#include <asm/arch/hardware.h>
++#include <asm/arch/pxa-regs.h>
++#include <asm/arch/pxa2xx-gpio.h>
++#include <asm/hardware/scoop.h>
++#include <asm/arch/sharpsl.h>
++#include <asm/hardware/sharpsl_pm.h>
++
++#define DPRINTK(fmt, args...) dev_dbg(data->dev, fmt "\n", ##args)
++
++struct remote_control_key {
++ unsigned char min;
++ unsigned char max;
++ unsigned char key;
++};
++
++static struct remote_control_key remote_keys_spitz[] = {
++ /* CE-RH2 values */
++ { 25, 35, KEY_STOPCD},
++ { 55, 65, KEY_PLAYPAUSE},
++ { 85, 95, KEY_NEXTSONG},
++ { 115, 125, KEY_VOLUMEUP},
++ { 145, 155, KEY_PREVIOUSSONG},
++ { 180, 190, KEY_MUTE},
++ { 215, 225, KEY_VOLUMEDOWN},
++};
++static struct remote_control_key remote_keys_corgi[] = {
++ /* CE-RH1 values */
++ { 27, 35, KEY_STOPCD},
++ { 7, 13, KEY_PLAYPAUSE},
++ { 77, 93, KEY_NEXTSONG},
++ { 115, 132, KEY_VOLUMEUP},
++ { 46, 58, KEY_PREVIOUSSONG},
++ { 170, 186, KEY_VOLUMEDOWN},
++};
++
++#define RELEASE_HI 230
++#define MAX_EARPHONE 6
++#define RC_POLL_MS 10
++#define RC_FINISH_MS 500
++#define WAIT_STATE 3
++#define NOISE_THRESHOLD 100
++
++struct sharpsl_rc {
++ struct input_dev *input;
++ struct device *dev;
++
++ spinlock_t lock;
++ struct timer_list rctimer;
++ struct timer_list rctimer_finish;
++
++ unsigned int handling_press;
++ unsigned int noise;
++ unsigned int state;
++ unsigned int last_key;
++};
++
++static int get_remocon_raw(void)
++{
++ int i, val;
++ struct remote_control_key *remote_keys;
++
++ if (machine_is_borzoi() || machine_is_spitz() || machine_is_akita())
++ remote_keys = remote_keys_spitz;
++ else
++ remote_keys = remote_keys_corgi;
++
++ val = sharpsl_pm_pxa_read_max1111(MAX1111_REMCOM);
++ for (i = 0; i < (machine_is_borzoi() || machine_is_spitz() || machine_is_akita() ?
++ ARRAY_SIZE(remote_keys_spitz) : ARRAY_SIZE(remote_keys_corgi));
++ ++i) {
++ if (val >= remote_keys[i].min
++ && val <= remote_keys[i].max) {
++ printk("get_remocon_raw: VAL=%i, KEY=%i\n", val, remote_keys[i].key);
++ return remote_keys[i].key;
++ }
++ }
++ return 0;
++}
++
++static irqreturn_t sharpsl_rc_interrupt(int irq, void *dev_id)
++{
++ struct sharpsl_rc *data = dev_id;
++ DPRINTK("sharpsl_rc_interrupt %d\n", irq);
++ if (!data->handling_press) {
++ DPRINTK("handling interrupt");
++ data->handling_press = 1;
++ data->noise = 0;
++ data->state = 0;
++ data->last_key = 0;
++
++ if (machine_is_borzoi() || machine_is_spitz())
++ reset_scoop_gpio(platform_scoop_config->devs[1].dev, SPITZ_SCP2_AKIN_PULLUP);
++ else if (machine_is_akita())
++ akita_reset_ioexp(&akitaioexp_device.dev, AKITA_IOEXP_AKIN_PULLUP);
++ else
++ reset_scoop_gpio(platform_scoop_config->devs[0].dev, CORGI_SCP_AKIN_PULLUP);
++ mod_timer(&data->rctimer, jiffies + msecs_to_jiffies(RC_POLL_MS));
++ }
++ return IRQ_HANDLED;
++}
++
++static void sharpsl_rc_timer_callback(unsigned long dataPtr)
++{
++ struct sharpsl_rc *data = (struct sharpsl_rc *) dataPtr;
++ int timer = 1;
++ int key = get_remocon_raw();
++ DPRINTK("timer callback, key: %d", key);
++
++ //wait for value to stabilize
++ if (data->state < WAIT_STATE) {
++ if (data->last_key != key) {
++ ++data->noise;
++ if (data->noise > NOISE_THRESHOLD) {
++ DPRINTK("too much noise, bailing");
++ timer = 0;
++ }
++ data->state = 0;
++ } else {
++ ++data->state;
++ }
++ data->last_key = key;
++
++ //stable value, send event
++ } else if (data->state == WAIT_STATE) {
++ data->noise = 0;
++ //non-key returned, skip the rest of the states and bail now
++ if (data->last_key == 0) {
++ DPRINTK("non-key detected %d, noise: %d", data->last_key, data->noise);
++ timer = 0;
++ //send button press
++ } else {
++ DPRINTK("key press detected %d, noise %d", data->last_key, data->noise);
++ input_report_key(data->input, data->last_key, 1);
++ }
++ ++data->state;
++
++ //wait until key is released
++ } else if (data->state < WAIT_STATE * 2) {
++ if (key == data->last_key
++ && data->noise < NOISE_THRESHOLD) {
++ data->state = WAIT_STATE + 1;
++ ++data->noise;
++ } else {
++ ++data->state;
++ }
++ //key is released, send event
++ } else {
++ //send button release
++ DPRINTK("release key %d", data->last_key);
++ input_report_key(data->input, data->last_key, 0);
++ timer = 0;
++ }
++ if (timer) {
++ mod_timer(&data->rctimer, jiffies + msecs_to_jiffies(RC_POLL_MS));
++ } else {
++ if (machine_is_borzoi() || machine_is_spitz())
++ set_scoop_gpio(platform_scoop_config->devs[1].dev, SPITZ_SCP2_AKIN_PULLUP);
++ else if (machine_is_akita())
++ akita_set_ioexp(&akitaioexp_device.dev, AKITA_IOEXP_AKIN_PULLUP);
++ else
++ set_scoop_gpio(platform_scoop_config->devs[0].dev, CORGI_SCP_AKIN_PULLUP);
++ data->handling_press = 0;
++ }
++}
++
++static int __init sharpsl_rc_probe(struct platform_device *pdev)
++{
++ struct sharpsl_rc *sharpsl_rc;
++ struct input_dev *input_dev;
++ int i, ret;
++ struct remote_control_key *remote_keys;
++
++ dev_dbg(&pdev->dev, "sharpsl_rc_probe\n");
++
++ sharpsl_rc = kzalloc(sizeof(struct sharpsl_rc), GFP_KERNEL);
++ input_dev = input_allocate_device();
++ if (!sharpsl_rc || !input_dev) {
++ kfree(sharpsl_rc);
++ input_free_device(input_dev);
++ return -ENOMEM;
++ }
++
++ platform_set_drvdata(pdev, sharpsl_rc);
++
++ sharpsl_rc->dev = &pdev->dev;
++ sharpsl_rc->input = input_dev;
++ spin_lock_init(&sharpsl_rc->lock);
++
++ /* Init Remote Control Timer */
++ init_timer(&sharpsl_rc->rctimer);
++ sharpsl_rc->rctimer.function = sharpsl_rc_timer_callback;
++ sharpsl_rc->rctimer.data = (unsigned long) sharpsl_rc;
++
++ input_dev->name = "Sharp Remote Control CE-RHX";
++ input_dev->phys = "sharpsl_rc/input0";
++ input_dev->id.bustype = BUS_HOST;
++ input_dev->id.vendor = 0x0001;
++ input_dev->id.product = 0x0001;
++ input_dev->id.version = 0x0100;
++ input_dev->dev.parent = &pdev->dev;
++
++ input_dev->evbit[0] = BIT(EV_KEY);
++
++ if (machine_is_borzoi() || machine_is_spitz() || machine_is_akita())
++ remote_keys = remote_keys_spitz;
++ else
++ remote_keys = remote_keys_corgi;
++ for (i = 0; i < (machine_is_borzoi() || machine_is_spitz() || machine_is_akita() ?
++ ARRAY_SIZE(remote_keys_spitz) : ARRAY_SIZE(remote_keys_corgi));
++ ++i)
++ set_bit(remote_keys[i].key, input_dev->keybit);
++
++ ret = input_register_device(sharpsl_rc->input);
++ if (ret) {
++ dev_dbg(&pdev->dev, "Failed to register Sharp Remote input device\n");
++ kfree(sharpsl_rc);
++ input_free_device(input_dev);
++ return ret;
++ }
++
++ if (machine_is_borzoi() || machine_is_spitz() || machine_is_akita()) {
++ pxa_gpio_mode(SPITZ_GPIO_AK_INT | GPIO_IN);
++ ret = request_irq(SPITZ_IRQ_GPIO_AK_INT,
++ sharpsl_rc_interrupt,
++ IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED,
++ "sharpsl_rc",
++ sharpsl_rc);
++ } else {
++ pxa_gpio_mode(CORGI_GPIO_AK_INT | GPIO_IN);
++ ret = request_irq(CORGI_IRQ_GPIO_AK_INT,
++ sharpsl_rc_interrupt,
++ IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED,
++ "sharpsl_rc",
++ sharpsl_rc);
++ }
++ if (ret < 0) {
++ dev_dbg(&pdev->dev, "Can't get IRQ: %d!\n", i);
++ kfree(sharpsl_rc);
++ input_free_device(input_dev);
++ return ret;
++ }
++
++ return 0;
++}
++
++static int sharpsl_rc_remove(struct platform_device *pdev)
++{
++ struct sharpsl_rc *sharpsl_rc = platform_get_drvdata(pdev);
++
++ dev_dbg(&pdev->dev, "sharpsl_rc_remove\n");
++
++ if (machine_is_borzoi() || machine_is_spitz() || machine_is_akita())
++ free_irq(SPITZ_IRQ_GPIO_AK_INT, sharpsl_rc);
++ else
++ free_irq(CORGI_IRQ_GPIO_AK_INT, sharpsl_rc);
++ del_timer_sync(&sharpsl_rc->rctimer);
++ input_unregister_device(sharpsl_rc->input);
++ kfree(sharpsl_rc);
++
++ return 0;
++}
++
++static struct platform_driver sharpsl_rc_driver = {
++ .probe = sharpsl_rc_probe,
++ .remove = sharpsl_rc_remove,
++ .suspend = NULL,
++ .resume = NULL,
++ .driver = {
++ .name = "sharpsl-remote-control",
++ },
++};
++
++static int __devinit sharpsl_rc_init(void)
++{
++ printk("sharpsl_rc_init\n");
++ return platform_driver_register(&sharpsl_rc_driver);
++}
++
++static void __exit sharpsl_rc_exit(void)
++{
++ printk("sharpsl_rc_exit\n");
++ platform_driver_unregister(&sharpsl_rc_driver);
++}
++
++module_init(sharpsl_rc_init);
++module_exit(sharpsl_rc_exit);
++
++MODULE_AUTHOR("Justin Patrin <papercrane@reversefold.com>");
++MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>");
++MODULE_DESCRIPTION("SharpSL Remote Control Driver");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.26/drivers/input/keyboard/spitzkbd.c
+===================================================================
+--- linux-2.6.26.orig/drivers/input/keyboard/spitzkbd.c 2008-07-13 21:51:29.000000000 +0000
++++ linux-2.6.26/drivers/input/keyboard/spitzkbd.c 2008-07-15 10:11:15.000000000 +0000
+@@ -19,6 +19,7 @@
+ #include <linux/jiffies.h>
+ #include <linux/module.h>
+ #include <linux/slab.h>
++#include <linux/kmod.h>
+
+ #include <asm/arch/spitz.h>
+ #include <asm/arch/hardware.h>
+@@ -280,13 +281,21 @@
+ static int sharpsl_hinge_state;
+ static int hinge_count;
+
++void spitzkbd_handle_sharpsl_rc(void *arg) {
++ request_module("sharpsl_rc");
++}
++
++DECLARE_WORK(spitzkbd_work, spitzkbd_handle_sharpsl_rc);
++
+ static void spitzkbd_hinge_timer(unsigned long data)
+ {
+ struct spitzkbd *spitzkbd_data = (struct spitzkbd *) data;
+ unsigned long state;
+ unsigned long flags;
++ unsigned int headphone, remote;
+
+ state = GPLR(SPITZ_GPIO_SWA) & (GPIO_bit(SPITZ_GPIO_SWA)|GPIO_bit(SPITZ_GPIO_SWB));
++ state |= (GPLR(SPITZ_GPIO_HP_IN) & GPIO_bit(SPITZ_GPIO_HP_IN));
+ state |= (GPLR(SPITZ_GPIO_AK_INT) & GPIO_bit(SPITZ_GPIO_AK_INT));
+ if (state != sharpsl_hinge_state) {
+ hinge_count = 0;
+@@ -300,9 +309,18 @@
+
+ input_report_switch(spitzkbd_data->input, SW_LID, ((GPLR(SPITZ_GPIO_SWA) & GPIO_bit(SPITZ_GPIO_SWA)) != 0));
+ input_report_switch(spitzkbd_data->input, SW_TABLET_MODE, ((GPLR(SPITZ_GPIO_SWB) & GPIO_bit(SPITZ_GPIO_SWB)) != 0));
+- input_report_switch(spitzkbd_data->input, SW_HEADPHONE_INSERT, ((GPLR(SPITZ_GPIO_AK_INT) & GPIO_bit(SPITZ_GPIO_AK_INT)) != 0));
++
++ headphone = ((GPLR(SPITZ_GPIO_HP_IN) & GPIO_bit(SPITZ_GPIO_HP_IN)) != 0);
++ input_report_switch(spitzkbd_data->input, SW_HEADPHONE_INSERT, headphone);
++
++ remote = headphone && ((GPLR(SPITZ_GPIO_AK_INT) & GPIO_bit(SPITZ_GPIO_AK_INT)) == 0);
++ input_report_switch(spitzkbd_data->input, SW_REMOTE_INSERT, remote);
+ input_sync(spitzkbd_data->input);
+
++ if (remote) {
++ schedule_work(&spitzkbd_work);
++ }
++
+ spin_unlock_irqrestore(&spitzkbd_data->lock, flags);
+ } else {
+ mod_timer(&spitzkbd_data->htimer, jiffies + msecs_to_jiffies(HINGE_SCAN_INTERVAL));
+@@ -396,6 +414,7 @@
+ set_bit(SW_LID, input_dev->swbit);
+ set_bit(SW_TABLET_MODE, input_dev->swbit);
+ set_bit(SW_HEADPHONE_INSERT, input_dev->swbit);
++ set_bit(SW_REMOTE_INSERT, input_dev->swbit);
+
+ err = input_register_device(input_dev);
+ if (err)
+@@ -433,9 +452,12 @@
+ request_irq(SPITZ_IRQ_GPIO_SWB, spitzkbd_hinge_isr,
+ IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "Spitzkbd SWB", spitzkbd);
+- request_irq(SPITZ_IRQ_GPIO_AK_INT, spitzkbd_hinge_isr,
++ request_irq(SPITZ_IRQ_GPIO_HP_IN, spitzkbd_hinge_isr,
+ IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "Spitzkbd HP", spitzkbd);
++ request_irq(SPITZ_IRQ_GPIO_AK_INT, spitzkbd_hinge_isr,
++ IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED,
++ "Spitzkbd HP Type", spitzkbd);
+
+ return 0;
+
+@@ -456,6 +478,7 @@
+ free_irq(SPITZ_IRQ_GPIO_ON_KEY, spitzkbd);
+ free_irq(SPITZ_IRQ_GPIO_SWA, spitzkbd);
+ free_irq(SPITZ_IRQ_GPIO_SWB, spitzkbd);
++ free_irq(SPITZ_IRQ_GPIO_HP_IN, spitzkbd);
+ free_irq(SPITZ_IRQ_GPIO_AK_INT, spitzkbd);
+
+ del_timer_sync(&spitzkbd->htimer);
+Index: linux-2.6.26/arch/arm/mach-pxa/sharpsl.h
+===================================================================
+--- linux-2.6.26.orig/arch/arm/mach-pxa/sharpsl.h 2008-07-13 21:51:29.000000000 +0000
++++ linux-2.6.26/arch/arm/mach-pxa/sharpsl.h 2008-07-15 10:11:15.000000000 +0000
+@@ -37,15 +37,10 @@
+ */
+ #define READ_GPIO_BIT(x) (GPLR(x) & GPIO_bit(x))
+
+-/* MAX1111 Channel Definitions */
+-#define MAX1111_BATT_VOLT 4u
+-#define MAX1111_BATT_TEMP 2u
+-#define MAX1111_ACIN_VOLT 6u
+-
+ extern struct battery_thresh spitz_battery_levels_acin[];
+ extern struct battery_thresh spitz_battery_levels_noac[];
+ void sharpsl_pm_pxa_init(void);
+ void sharpsl_pm_pxa_remove(void);
+-int sharpsl_pm_pxa_read_max1111(int channel);
++
+
+
+Index: linux-2.6.26/arch/arm/mach-pxa/sharpsl_pm.c
+===================================================================
+--- linux-2.6.26.orig/arch/arm/mach-pxa/sharpsl_pm.c 2008-07-13 21:51:29.000000000 +0000
++++ linux-2.6.26/arch/arm/mach-pxa/sharpsl_pm.c 2008-07-15 10:11:15.000000000 +0000
+@@ -136,6 +136,8 @@
+ | MAXCTRL_SGL | MAXCTRL_UNI | MAXCTRL_STR);
+ }
+
++EXPORT_SYMBOL(sharpsl_pm_pxa_read_max1111);
++
+ void sharpsl_pm_pxa_init(void)
+ {
+ pxa_gpio_mode(sharpsl_pm.machinfo->gpio_acin | GPIO_IN);
+Index: linux-2.6.26/include/asm-arm/hardware/sharpsl_pm.h
+===================================================================
+--- linux-2.6.26.orig/include/asm-arm/hardware/sharpsl_pm.h 2008-07-13 21:51:29.000000000 +0000
++++ linux-2.6.26/include/asm-arm/hardware/sharpsl_pm.h 2008-07-15 10:11:15.000000000 +0000
+@@ -104,3 +104,10 @@
+ irqreturn_t sharpsl_chrg_full_isr(int irq, void *dev_id);
+ irqreturn_t sharpsl_fatal_isr(int irq, void *dev_id);
+
++/* MAX1111 Channel Definitions */
++#define MAX1111_REMCOM 0u
++#define MAX1111_BATT_VOLT 4u
++#define MAX1111_BATT_TEMP 2u
++#define MAX1111_ACIN_VOLT 6u
++
++int sharpsl_pm_pxa_read_max1111(int channel);
+Index: linux-2.6.26/include/linux/input.h
+===================================================================
+--- linux-2.6.26.orig/include/linux/input.h 2008-07-13 21:51:29.000000000 +0000
++++ linux-2.6.26/include/linux/input.h 2008-07-15 10:13:04.000000000 +0000
+@@ -640,6 +640,7 @@
+ #define SW_RFKILL_ALL 0x03 /* rfkill master switch, type "any"
+ set = radio enabled */
+ #define SW_RADIO SW_RFKILL_ALL /* deprecated */
++#define SW_REMOTE_INSERT 0x04 /* set = remote */
+ #define SW_MAX 0x0f
+ #define SW_CNT (SW_MAX+1)
+
+Index: linux-2.6.26/arch/arm/mach-pxa/spitz_pm.c
+===================================================================
+--- linux-2.6.26.orig/arch/arm/mach-pxa/spitz_pm.c 2008-07-13 21:51:29.000000000 +0000
++++ linux-2.6.26/arch/arm/mach-pxa/spitz_pm.c 2008-07-15 10:11:15.000000000 +0000
+@@ -160,6 +160,13 @@
+ if (resume_on_alarm && (PEDR & PWER_RTC))
+ is_resume |= PWER_RTC;
+
++ printk("wakeup: PEDR: %x, PKSR: %x, HP_IN: %x, AK_INT: %x\n", PEDR, PKSR, GPIO_bit(SPITZ_GPIO_HP_IN), GPIO_bit(SPITZ_GPIO_AK_INT));
++
++ //remote/headphone interrupt, wakeup
++ if (PEDR == 0 && (PKSR & 0xc0d01) != 0) {
++ is_resume |= PWER_RTC;
++ }
++
+ dev_dbg(sharpsl_pm.dev, "is_resume: %x\n",is_resume);
+ return is_resume;
+ }
diff --git a/recipes/linux/linux-rp-2.6.26/spitz_h_rewrite.patch b/recipes/linux/linux-rp-2.6.26/spitz_h_rewrite.patch
new file mode 100644
index 0000000000..3dcf4ed8af
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.26/spitz_h_rewrite.patch
@@ -0,0 +1,497 @@
+http://www.uwsg.indiana.edu/hypermail/linux/kernel/0802.1/3541.html
+
+Here is a rewrite of spitz.h, which includes comments documenting
+function of particular GPIO pins.
+
+spitz_h_rewrite.patch provides:
+- no changes in compiled code
+- partial spitz.h rewrite:
+ * organized by function
+ * describes complete GPIO pinout
+ * comments added
+ * removed defines cloning pxa-regs.h
+- prefer generic pxa-regs.h GPIO if available
+- use GPIO names instead of numbers
+
+Thanks to Trisoft for providing needed information.
+
+Index: linux-2.6.26-rc4/arch/arm/mach-pxa/spitz_pm.c
+===================================================================
+--- linux-2.6.26-rc4.orig/arch/arm/mach-pxa/spitz_pm.c 2008-06-02 00:13:53.000000000 +0100
++++ linux-2.6.26-rc4/arch/arm/mach-pxa/spitz_pm.c 2008-06-02 00:13:53.000000000 +0100
+@@ -111,9 +111,9 @@
+ pxa_gpio_mode(GPIO18_RDY|GPIO_OUT | GPIO_DFLT_HIGH);
+
+ PRER = GPIO_bit(SPITZ_GPIO_KEY_INT);
+- PFER = GPIO_bit(SPITZ_GPIO_KEY_INT) | GPIO_bit(SPITZ_GPIO_RESET);
+- PWER = GPIO_bit(SPITZ_GPIO_KEY_INT) | GPIO_bit(SPITZ_GPIO_RESET) | PWER_RTC;
+- PKWR = GPIO_bit(SPITZ_GPIO_SYNC) | GPIO_bit(SPITZ_GPIO_KEY_INT) | GPIO_bit(SPITZ_GPIO_RESET);
++ PFER = GPIO_bit(SPITZ_GPIO_KEY_INT) | GPIO_bit(GPIO1_RST);
++ PWER = GPIO_bit(SPITZ_GPIO_KEY_INT) | GPIO_bit(GPIO1_RST) | PWER_RTC;
++ PKWR = GPIO_bit(SPITZ_GPIO_SYNC) | GPIO_bit(SPITZ_GPIO_KEY_INT) | GPIO_bit(GPIO1_RST);
+ PKSR = 0xffffffff; // clear
+
+ /* nRESET_OUT Disable */
+@@ -126,7 +126,7 @@
+ static void spitz_postsuspend(void)
+ {
+ pxa_gpio_mode(GPIO18_RDY_MD);
+- pxa_gpio_mode(10 | GPIO_IN);
++ pxa_gpio_mode(SPITZ_GPIO_NC_10 | GPIO_IN);
+ }
+
+ static int spitz_should_wakeup(unsigned int resume_on_alarm)
+Index: linux-2.6.26-rc4/drivers/video/pxafb.c
+===================================================================
+--- linux-2.6.26-rc4.orig/drivers/video/pxafb.c 2008-05-26 19:08:11.000000000 +0100
++++ linux-2.6.26-rc4/drivers/video/pxafb.c 2008-06-02 00:15:22.000000000 +0100
+@@ -966,7 +966,7 @@
+ return;
+ }
+
+- for (gpio = 58; ldd_bits; gpio++, ldd_bits--)
++ for (gpio = GPIO58_LDD_0; ldd_bits; gpio++, ldd_bits--)
+ pxa_gpio_mode(gpio | GPIO_ALT_FN_2_OUT);
+ pxa_gpio_mode(GPIO74_LCD_FCLK_MD);
+ pxa_gpio_mode(GPIO75_LCD_LCLK_MD);
+Index: linux-2.6.26-rc4/include/asm-arm/arch-pxa/akita.h
+===================================================================
+--- linux-2.6.26-rc4.orig/include/asm-arm/arch-pxa/akita.h 2008-05-26 19:08:11.000000000 +0100
++++ linux-2.6.26-rc4/include/asm-arm/arch-pxa/akita.h 2008-06-02 00:13:53.000000000 +0100
+@@ -12,11 +12,11 @@
+ /* Akita IO Expander GPIOs */
+
+ #define AKITA_IOEXP_RESERVED_7 (1 << 7)
+-#define AKITA_IOEXP_IR_ON (1 << 6)
+-#define AKITA_IOEXP_AKIN_PULLUP (1 << 5)
+-#define AKITA_IOEXP_BACKLIGHT_CONT (1 << 4)
+-#define AKITA_IOEXP_BACKLIGHT_ON (1 << 3)
+-#define AKITA_IOEXP_MIC_BIAS (1 << 2)
++#define AKITA_IOEXP_IR_ON (1 << 6) /* IrDA On */
++#define AKITA_IOEXP_AKIN_PULLUP (1 << 5) /* Pull-Up for Remote */
++#define AKITA_IOEXP_BACKLIGHT_CONT (1 << 4) /* Backlight Control */
++#define AKITA_IOEXP_BACKLIGHT_ON (1 << 3) /* Backlight On */
++#define AKITA_IOEXP_MIC_BIAS (1 << 2) /* Mic Bias On */
+ #define AKITA_IOEXP_RESERVED_1 (1 << 1)
+ #define AKITA_IOEXP_RESERVED_0 (1 << 0)
+
+Index: linux-2.6.26-rc4/include/asm-arm/arch-pxa/spitz.h
+===================================================================
+--- linux-2.6.26-rc4.orig/include/asm-arm/arch-pxa/spitz.h 2008-05-26 19:08:11.000000000 +0100
++++ linux-2.6.26-rc4/include/asm-arm/arch-pxa/spitz.h 2008-06-02 00:13:53.000000000 +0100
+@@ -1,8 +1,9 @@
+ /*
+- * Hardware specific definitions for SL-Cx000 series of PDAs
++ * Hardware specific definitions for SL-Cxx00 series of PDAs
+ *
+ * Copyright (c) 2005 Alexander Wykes
+ * Copyright (c) 2005 Richard Purdie
++ * Copyright (c) 2008 Stanislav Brabec
+ *
+ * Based on Sharp's 2.4 kernel patches
+ *
+@@ -13,140 +14,257 @@
+ */
+ #ifndef __ASM_ARCH_SPITZ_H
+ #define __ASM_ARCH_SPITZ_H 1
+-#endif
+
+-#include <linux/fb.h>
++#include <asm-arm/arch-pxa/irqs.h>
++#include <linux/platform_device.h>
+
+ /* Spitz/Akita GPIOs */
+
+-#define SPITZ_GPIO_KEY_INT (0) /* Key Interrupt */
+-#define SPITZ_GPIO_RESET (1)
+-#define SPITZ_GPIO_nSD_DETECT (9)
+-#define SPITZ_GPIO_TP_INT (11) /* Touch Panel interrupt */
+-#define SPITZ_GPIO_AK_INT (13) /* Remote Control */
+-#define SPITZ_GPIO_ADS7846_CS (14)
+-#define SPITZ_GPIO_SYNC (16)
+-#define SPITZ_GPIO_MAX1111_CS (20)
+-#define SPITZ_GPIO_FATAL_BAT (21)
+-#define SPITZ_GPIO_HSYNC (22)
+-#define SPITZ_GPIO_nSD_CLK (32)
+-#define SPITZ_GPIO_USB_DEVICE (35)
+-#define SPITZ_GPIO_USB_HOST (37)
+-#define SPITZ_GPIO_USB_CONNECT (41)
+-#define SPITZ_GPIO_LCDCON_CS (53)
+-#define SPITZ_GPIO_nPCE (54)
+-#define SPITZ_GPIO_nSD_WP (81)
+-#define SPITZ_GPIO_ON_RESET (89)
+-#define SPITZ_GPIO_BAT_COVER (90)
+-#define SPITZ_GPIO_CF_CD (94)
+-#define SPITZ_GPIO_ON_KEY (95)
+-#define SPITZ_GPIO_SWA (97)
+-#define SPITZ_GPIO_SWB (96)
+-#define SPITZ_GPIO_CHRG_FULL (101)
+-#define SPITZ_GPIO_CO (101)
+-#define SPITZ_GPIO_CF_IRQ (105)
+-#define SPITZ_GPIO_AC_IN (115)
+-#define SPITZ_GPIO_HP_IN (116)
++/* This list refers to all GPIO pins either in defines or in comments.
++ *
++ * GPIO pins not listed:
++ * GPIO2 SYS_EN: System Power Enable
++ * GPIO5-GPIO8 PWR_CAP0-PWR_CAP3: sleep DC-DC converter power capacitors
++ * GPIO40 not connected
++ */
+
+-/* Spitz Only GPIOs */
+
+-#define SPITZ_GPIO_CF2_IRQ (106) /* CF slot1 Ready */
+-#define SPITZ_GPIO_CF2_CD (93)
++/* Spitz/Akita System GPIO */
++
++#define SPITZ_GPIO_KEY_INT (0) /* Key Interrupt */
++#define SPITZ_GPIO_SYNC (16) /* IOPORT Wake Up (input) */
++#define SPITZ_GPIO_NAND_CS (79) /* NAND Flash Chip Select */
++#define SPITZ_GPIO_NC_10 (10) /* Not Connected (but used in kernel) */
++/* This GPIO pin is connected:
++ * GPIO1_RST
++ */
+
+
++/* Compact Flash Interface */
++
++/* Spitz/Akita Compact Flash Interface */
++#define SPITZ_GPIO_CF_CD (94) /* CF IRQ */
++#define SPITZ_GPIO_CF_IRQ (105) /* CF Ready */
++/* These GPIO pins are connected:
++ * GPIO48_nPOE
++ * GPIO49_nPWE
++ * GPIO50_nPIOR
++ * GPIO51_nPIOW
++ * GPIO54_nPCE_2
++ * GPIO55_nPREG
++ * GPIO56_nPWAIT
++ * GPIO57_nIOIS16
++ * GPIO80_nCS_4
++ * GPIO85_nPCE_1
++ * GPIO104_pSKTSEL
++ */
++
++/* Spitz only Compact Flash Interface */
++#define SPITZ_GPIO_CF2_CD (93) /* CF slot1 IRQ */
++#define SPITZ_GPIO_CF2_IRQ (106) /* CF slot1 Ready */
++/* This GPIO pin is connected:
++ * GPIO78_nCS_2
++ */
++
++
++/* Spitz/Akita Battery, Power and Service Connector */
++
++#define SPITZ_GPIO_FATAL_BAT (21) /* Fatal Battery */
++#define SPITZ_GPIO_BAT_COVER (90) /* Battery Cover switch */
++#define SPITZ_GPIO_BAT_COVER2 (15) /* Battery Cover switch, parallel pin */
++#define SPITZ_GPIO_CHRG_FULL (101) /* Battery Full */
++#define SPITZ_GPIO_AC_IN (115) /* External Power Supply is active */
++#define SPITZ_GPIO_ON_RESET (89) /* Software Reset */
++#define SPITZ_GPIO_SERVICE0 (83) /* Service Connector */
++#define SPITZ_GPIO_SERVICE1 (84) /* Service Connector */
++/* This GPIO pin is connected:
++ * GPIO18_RDY
++ */
++
++
++/* Spitz/Akita Display Controller */
++
++#define SPITZ_GPIO_HSYNC (22) /* Line Sync Feedback */
++/* These GPIO pins are connected:
++ * GPIO58_LDD_0-GPIO58_LDD_15
++ * GPIO74_LCD_FCLK
++ * GPIO75_LCD_LCLK
++ * GPIO76_LCD_PCLK
++ * GPIO77_LCD_ACBIAS
++ */
++
++
++/* Spitz/Akita SSP/SPI Bus and Devices */
++
++#define SPITZ_GPIO_SSP_CLK (19) /* SSP bus Clock */
++#define SPITZ_GPIO_SSP_RXD (86) /* SSP bus RxD */
++#define SPITZ_GPIO_SSP_TXD (87) /* SSP bus TxD */
++#define SPITZ_GPIO_TP_INT (11) /* Touch Panel IRQ */
++#define SPITZ_GPIO_ADS7846_CS (14) /* Touch Panel Controller Chip Select */
++#define SPITZ_GPIO_MAX1111_CS (20) /* Multi Channel ADC Chip Select */
++#define SPITZ_GPIO_LCDCON_CS (53) /* LCD Controller Chip Select */
++
++
++/* Spitz/Akita Supplementary USB OTG Pins */
++
++#define SPITZ_GPIO_USB_DEVICE (35) /* USB Client power is present */
++#define SPITZ_GPIO_USB_HOST (37) /* USB OTG 5V Host power supply control */
++#define SPITZ_GPIO_USB_CONNECT (41) /* USB Host Cable is connected */
++
++
++/* Spitz/Akita Audio */
++
++#define SPITZ_GPIO_HP_IN (116) /* CPU Headphone detect */
++#define SPITZ_GPIO_AK_INT (13) /* Remote Control detect */
++/* These GPIO AC97 pins are connected:
++ * GPIO28_BITCLK
++ * GPIO29_SDATA_IN
++ * GPIO30_SDATA_OUT
++ * GPIO31_SYNC
++ * GPIO113_AC97_RESET_N
++ */
++
++
++/* Spitz/Akita SD Slot */
++
++#define SPITZ_GPIO_nSD_DETECT (9) /* SD Card Presence */
++#define SPITZ_GPIO_nSD_WP (81) /* SD Write Protection */
++/* These GPIO pins are connected:
++ * GPIO32_MMCCLK
++ * GPIO92_MMCDAT0
++ * GPIO109_MMCDAT1
++ * GPIO110_MMCDAT2
++ * GPIO111_MMCDAT3
++ * GPIO112_MMCCMD
++ */
++
++/* Spitz/Akita I2C bus */
++#define SPITZ_GPIO_SCL (117) /* I2C SCL */
++#define SPITZ_GPIO_SDA (118) /* I2C SDA */
++#define SPITZ_GPIO_PWR_SCL (3) /* I2C SCL power */
++#define SPITZ_GPIO_PWR_SDA (4) /* I2C SDA power */
++
++/* audio codec pins */
++
++
++/* Spitz/Akita UART ports */
++
++/* Fully Featured UART - connected to IOPORT connector */
++#define SPITZ_GPIO_FFRXD (102) /* IOPORT has nRXD inverted levels */
++#define SPITZ_GPIO_FFTXD (99) /* IOPORT has nTXD inverted levels */
++#define SPITZ_GPIO_FFRTS (98)
++#define SPITZ_GPIO_FFCTS (100)
++#define SPITZ_GPIO_FFDTR (82)
++#define SPITZ_GPIO_FFDSR (33)
++
++/* These UART GPIO pins are connected to Bluetooth
++ * (only on Akita version with Bluetooth)
++ * GPIO42_BTRXD
++ * GPIO43_BTTXD
++ * GPIO44_BTCTS
++ * GPIO45_BTRTS
++ */
++
++/* These UART GPIO pins are connected to IrDA:
++ * GPIO46_STRXD
++ * GPIO47_STTXD
++ */
++
+ /* Spitz/Akita Keyboard Definitions */
+
+-#define SPITZ_KEY_STROBE_NUM (11)
+-#define SPITZ_KEY_SENSE_NUM (7)
+-#define SPITZ_GPIO_G0_STROBE_BIT 0x0f800000
+-#define SPITZ_GPIO_G1_STROBE_BIT 0x00100000
+-#define SPITZ_GPIO_G2_STROBE_BIT 0x01000000
+-#define SPITZ_GPIO_G3_STROBE_BIT 0x00041880
+-#define SPITZ_GPIO_G0_SENSE_BIT 0x00021000
+-#define SPITZ_GPIO_G1_SENSE_BIT 0x000000d4
+-#define SPITZ_GPIO_G2_SENSE_BIT 0x08000000
+-#define SPITZ_GPIO_G3_SENSE_BIT 0x00000000
+-
+-#define SPITZ_GPIO_KEY_STROBE0 88
+-#define SPITZ_GPIO_KEY_STROBE1 23
+-#define SPITZ_GPIO_KEY_STROBE2 24
+-#define SPITZ_GPIO_KEY_STROBE3 25
+-#define SPITZ_GPIO_KEY_STROBE4 26
+-#define SPITZ_GPIO_KEY_STROBE5 27
+-#define SPITZ_GPIO_KEY_STROBE6 52
+-#define SPITZ_GPIO_KEY_STROBE7 103
+-#define SPITZ_GPIO_KEY_STROBE8 107
+-#define SPITZ_GPIO_KEY_STROBE9 108
+-#define SPITZ_GPIO_KEY_STROBE10 114
+-
+-#define SPITZ_GPIO_KEY_SENSE0 12
+-#define SPITZ_GPIO_KEY_SENSE1 17
+-#define SPITZ_GPIO_KEY_SENSE2 91
+-#define SPITZ_GPIO_KEY_SENSE3 34
+-#define SPITZ_GPIO_KEY_SENSE4 36
+-#define SPITZ_GPIO_KEY_SENSE5 38
+-#define SPITZ_GPIO_KEY_SENSE6 39
++#define SPITZ_KEY_STROBE_NUM (11)
++#define SPITZ_KEY_SENSE_NUM (7)
++#define SPITZ_GPIO_G0_STROBE_BIT 0x0f800000
++#define SPITZ_GPIO_G1_STROBE_BIT 0x00100000
++#define SPITZ_GPIO_G2_STROBE_BIT 0x01000000
++#define SPITZ_GPIO_G3_STROBE_BIT 0x00041880
++#define SPITZ_GPIO_G0_SENSE_BIT 0x00021000
++#define SPITZ_GPIO_G1_SENSE_BIT 0x000000d4
++#define SPITZ_GPIO_G2_SENSE_BIT 0x08000000
++#define SPITZ_GPIO_G3_SENSE_BIT 0x00000000
++#define SPITZ_GPIO_KEY_STROBE0 (88)
++#define SPITZ_GPIO_KEY_STROBE1 (23)
++#define SPITZ_GPIO_KEY_STROBE2 (24)
++#define SPITZ_GPIO_KEY_STROBE3 (25)
++#define SPITZ_GPIO_KEY_STROBE4 (26)
++#define SPITZ_GPIO_KEY_STROBE5 (27)
++#define SPITZ_GPIO_KEY_STROBE6 (52)
++#define SPITZ_GPIO_KEY_STROBE7 (103)
++#define SPITZ_GPIO_KEY_STROBE8 (107)
++#define SPITZ_GPIO_KEY_STROBE9 (108)
++#define SPITZ_GPIO_KEY_STROBE10 (114)
++#define SPITZ_GPIO_KEY_SENSE0 (12)
++#define SPITZ_GPIO_KEY_SENSE1 (17)
++#define SPITZ_GPIO_KEY_SENSE2 (91)
++#define SPITZ_GPIO_KEY_SENSE3 (34)
++#define SPITZ_GPIO_KEY_SENSE4 (36)
++#define SPITZ_GPIO_KEY_SENSE5 (38)
++#define SPITZ_GPIO_KEY_SENSE6 (39)
++
++#define SPITZ_GPIO_SWA (97) /* Keyboard Interrupt A */
++#define SPITZ_GPIO_SWB (96) /* Keyboard Interrupt B */
++#define SPITZ_GPIO_ON_KEY (95) /* Power On Key */
+
+
+-/* Spitz Scoop Device (No. 1) GPIOs */
++/* Spitz/Akita Scoop Device (No. 1) GPIOs */
+ /* Suspend States in comments */
+-#define SPITZ_SCP_LED_GREEN SCOOP_GPCR_PA11 /* Keep */
+-#define SPITZ_SCP_JK_B SCOOP_GPCR_PA12 /* Keep */
+-#define SPITZ_SCP_CHRG_ON SCOOP_GPCR_PA13 /* Keep */
+-#define SPITZ_SCP_MUTE_L SCOOP_GPCR_PA14 /* Low */
+-#define SPITZ_SCP_MUTE_R SCOOP_GPCR_PA15 /* Low */
+-#define SPITZ_SCP_CF_POWER SCOOP_GPCR_PA16 /* Keep */
+-#define SPITZ_SCP_LED_ORANGE SCOOP_GPCR_PA17 /* Keep */
+-#define SPITZ_SCP_JK_A SCOOP_GPCR_PA18 /* Low */
+-#define SPITZ_SCP_ADC_TEMP_ON SCOOP_GPCR_PA19 /* Low */
++#define SPITZ_SCP_LED_GREEN SCOOP_GPCR_PA11 /* Green LED, Keep */
++#define SPITZ_SCP_JK_B SCOOP_GPCR_PA12 /* Fast Charge On, Keep */
++#define SPITZ_SCP_CHRG_ON SCOOP_GPCR_PA13 /* Charge On, Keep */
++#define SPITZ_SCP_MUTE_L SCOOP_GPCR_PA14 /* Extra Mute Left, Low */
++#define SPITZ_SCP_MUTE_R SCOOP_GPCR_PA15 /* Extra Mute Right, Low */
++#define SPITZ_SCP_CF_POWER SCOOP_GPCR_PA16 /* CF+SD Power Circuit, Keep */
++#define SPITZ_SCP_LED_ORANGE SCOOP_GPCR_PA17 /* Orange LED, Keep */
++#define SPITZ_SCP_JK_A SCOOP_GPCR_PA18 /* Dummy Load, Low */
++#define SPITZ_SCP_ADC_TEMP_ON SCOOP_GPCR_PA19 /* Battery Sensor On, Low */
+
+ #define SPITZ_SCP_IO_DIR (SPITZ_SCP_LED_GREEN | SPITZ_SCP_JK_B | SPITZ_SCP_CHRG_ON | \
+- SPITZ_SCP_MUTE_L | SPITZ_SCP_MUTE_R | SPITZ_SCP_LED_ORANGE | \
+- SPITZ_SCP_CF_POWER | SPITZ_SCP_JK_A | SPITZ_SCP_ADC_TEMP_ON)
++ SPITZ_SCP_MUTE_L | SPITZ_SCP_MUTE_R | SPITZ_SCP_LED_ORANGE | \
++ SPITZ_SCP_CF_POWER | SPITZ_SCP_JK_A | SPITZ_SCP_ADC_TEMP_ON)
+ #define SPITZ_SCP_IO_OUT (SPITZ_SCP_CHRG_ON | SPITZ_SCP_MUTE_L | SPITZ_SCP_MUTE_R)
+ #define SPITZ_SCP_SUS_CLR (SPITZ_SCP_MUTE_L | SPITZ_SCP_MUTE_R | SPITZ_SCP_JK_A | SPITZ_SCP_ADC_TEMP_ON)
+ #define SPITZ_SCP_SUS_SET 0
+
+ /* Spitz Scoop Device (No. 2) GPIOs */
+-/* Suspend States in comments */
+-#define SPITZ_SCP2_IR_ON SCOOP_GPCR_PA11 /* High */
+-#define SPITZ_SCP2_AKIN_PULLUP SCOOP_GPCR_PA12 /* Keep */
+-#define SPITZ_SCP2_RESERVED_1 SCOOP_GPCR_PA13 /* High */
+-#define SPITZ_SCP2_RESERVED_2 SCOOP_GPCR_PA14 /* Low */
+-#define SPITZ_SCP2_RESERVED_3 SCOOP_GPCR_PA15 /* Low */
+-#define SPITZ_SCP2_RESERVED_4 SCOOP_GPCR_PA16 /* Low */
+-#define SPITZ_SCP2_BACKLIGHT_CONT SCOOP_GPCR_PA17 /* Low */
+-#define SPITZ_SCP2_BACKLIGHT_ON SCOOP_GPCR_PA18 /* Low */
+-#define SPITZ_SCP2_MIC_BIAS SCOOP_GPCR_PA19 /* Low */
++/* Suspend States in comments
++ * Spitz only, Akita uses corresponding AKITA_IOEXP_ */
++#define SPITZ_SCP2_IR_ON SCOOP_GPCR_PA11 /* IrDA On, High */
++#define SPITZ_SCP2_AKIN_PULLUP SCOOP_GPCR_PA12 /* Pull-Up for Remote, Keep */
++#define SPITZ_SCP2_RESERVED_1 SCOOP_GPCR_PA13 /* High */
++#define SPITZ_SCP2_RESERVED_2 SCOOP_GPCR_PA14 /* Low */
++#define SPITZ_SCP2_RESERVED_3 SCOOP_GPCR_PA15 /* Low */
++#define SPITZ_SCP2_RESERVED_4 SCOOP_GPCR_PA16 /* Low */
++#define SPITZ_SCP2_BACKLIGHT_CONT SCOOP_GPCR_PA17 /* Backlight Control, Low */
++#define SPITZ_SCP2_BACKLIGHT_ON SCOOP_GPCR_PA18 /* Backlight On, Low */
++#define SPITZ_SCP2_MIC_BIAS SCOOP_GPCR_PA19 /* Mic Bias On, Low */
+
+ #define SPITZ_SCP2_IO_DIR (SPITZ_SCP2_IR_ON | SPITZ_SCP2_AKIN_PULLUP | SPITZ_SCP2_RESERVED_1 | \
+- SPITZ_SCP2_RESERVED_2 | SPITZ_SCP2_RESERVED_3 | SPITZ_SCP2_RESERVED_4 | \
+- SPITZ_SCP2_BACKLIGHT_CONT | SPITZ_SCP2_BACKLIGHT_ON | SPITZ_SCP2_MIC_BIAS)
++ SPITZ_SCP2_RESERVED_2 | SPITZ_SCP2_RESERVED_3 | SPITZ_SCP2_RESERVED_4 | \
++ SPITZ_SCP2_BACKLIGHT_CONT | SPITZ_SCP2_BACKLIGHT_ON | SPITZ_SCP2_MIC_BIAS)
+
+ #define SPITZ_SCP2_IO_OUT (SPITZ_SCP2_IR_ON | SPITZ_SCP2_AKIN_PULLUP | SPITZ_SCP2_RESERVED_1)
+ #define SPITZ_SCP2_SUS_CLR (SPITZ_SCP2_RESERVED_2 | SPITZ_SCP2_RESERVED_3 | SPITZ_SCP2_RESERVED_4 | \
+- SPITZ_SCP2_BACKLIGHT_CONT | SPITZ_SCP2_BACKLIGHT_ON | SPITZ_SCP2_MIC_BIAS)
++ SPITZ_SCP2_BACKLIGHT_CONT | SPITZ_SCP2_BACKLIGHT_ON | SPITZ_SCP2_MIC_BIAS)
+ #define SPITZ_SCP2_SUS_SET (SPITZ_SCP2_IR_ON | SPITZ_SCP2_RESERVED_1)
+
+
+-/* Spitz IRQ Definitions */
++/* Spitz/Akita IRQ Definitions */
+
+-#define SPITZ_IRQ_GPIO_KEY_INT IRQ_GPIO(SPITZ_GPIO_KEY_INT)
+-#define SPITZ_IRQ_GPIO_AC_IN IRQ_GPIO(SPITZ_GPIO_AC_IN)
+-#define SPITZ_IRQ_GPIO_AK_INT IRQ_GPIO(SPITZ_GPIO_AK_INT)
+-#define SPITZ_IRQ_GPIO_HP_IN IRQ_GPIO(SPITZ_GPIO_HP_IN)
+-#define SPITZ_IRQ_GPIO_TP_INT IRQ_GPIO(SPITZ_GPIO_TP_INT)
+-#define SPITZ_IRQ_GPIO_SYNC IRQ_GPIO(SPITZ_GPIO_SYNC)
+-#define SPITZ_IRQ_GPIO_ON_KEY IRQ_GPIO(SPITZ_GPIO_ON_KEY)
+-#define SPITZ_IRQ_GPIO_SWA IRQ_GPIO(SPITZ_GPIO_SWA)
+-#define SPITZ_IRQ_GPIO_SWB IRQ_GPIO(SPITZ_GPIO_SWB)
++#define SPITZ_IRQ_GPIO_KEY_INT IRQ_GPIO(SPITZ_GPIO_KEY_INT)
++#define SPITZ_IRQ_GPIO_AC_IN IRQ_GPIO(SPITZ_GPIO_AC_IN)
++#define SPITZ_IRQ_GPIO_AK_INT IRQ_GPIO(SPITZ_GPIO_AK_INT)
++#define SPITZ_IRQ_GPIO_HP_IN IRQ_GPIO(SPITZ_GPIO_HP_IN)
++#define SPITZ_IRQ_GPIO_TP_INT IRQ_GPIO(SPITZ_GPIO_TP_INT)
++#define SPITZ_IRQ_GPIO_SYNC IRQ_GPIO(SPITZ_GPIO_SYNC)
++#define SPITZ_IRQ_GPIO_ON_KEY IRQ_GPIO(SPITZ_GPIO_ON_KEY)
++#define SPITZ_IRQ_GPIO_SWA IRQ_GPIO(SPITZ_GPIO_SWA)
++#define SPITZ_IRQ_GPIO_SWB IRQ_GPIO(SPITZ_GPIO_SWB)
+ #define SPITZ_IRQ_GPIO_BAT_COVER IRQ_GPIO(SPITZ_GPIO_BAT_COVER)
+ #define SPITZ_IRQ_GPIO_FATAL_BAT IRQ_GPIO(SPITZ_GPIO_FATAL_BAT)
+-#define SPITZ_IRQ_GPIO_CO IRQ_GPIO(SPITZ_GPIO_CO)
+-#define SPITZ_IRQ_GPIO_CF_IRQ IRQ_GPIO(SPITZ_GPIO_CF_IRQ)
+-#define SPITZ_IRQ_GPIO_CF_CD IRQ_GPIO(SPITZ_GPIO_CF_CD)
+-#define SPITZ_IRQ_GPIO_CF2_IRQ IRQ_GPIO(SPITZ_GPIO_CF2_IRQ)
+-#define SPITZ_IRQ_GPIO_nSD_INT IRQ_GPIO(SPITZ_GPIO_nSD_INT)
++#define SPITZ_IRQ_GPIO_CF_IRQ IRQ_GPIO(SPITZ_GPIO_CF_IRQ)
++#define SPITZ_IRQ_GPIO_CF_CD IRQ_GPIO(SPITZ_GPIO_CF_CD)
++#define SPITZ_IRQ_GPIO_CF2_IRQ IRQ_GPIO(SPITZ_GPIO_CF2_IRQ)
++#define SPITZ_IRQ_GPIO_nSD_INT IRQ_GPIO(SPITZ_GPIO_nSD_INT)
+ #define SPITZ_IRQ_GPIO_nSD_DETECT IRQ_GPIO(SPITZ_GPIO_nSD_DETECT)
+
+ /*
+@@ -156,3 +274,5 @@
+ extern struct platform_device spitzscoop2_device;
+ extern struct platform_device spitzssp_device;
+ extern struct sharpsl_charger_machinfo spitz_pm_machinfo;
++
++#endif
+Index: linux-2.6.26-rc4/sound/arm/pxa2xx-ac97.c
+===================================================================
+--- linux-2.6.26-rc4.orig/sound/arm/pxa2xx-ac97.c 2008-05-26 19:08:11.000000000 +0100
++++ linux-2.6.26-rc4/sound/arm/pxa2xx-ac97.c 2008-06-02 00:19:38.000000000 +0100
+@@ -156,10 +156,10 @@
+ #ifdef CONFIG_PXA27x
+ /* warm reset broken on Bulverde,
+ so manually keep AC97 reset high */
+- pxa_gpio_mode(113 | GPIO_OUT | GPIO_DFLT_HIGH);
++ pxa_gpio_mode(GPIO113_AC97_RESET_N | GPIO_OUT | GPIO_DFLT_HIGH);
+ udelay(10);
+ GCR |= GCR_WARM_RST;
+- pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT);
++ pxa_gpio_mode(GPIO113_AC97_RESET_N_MD);
+ udelay(500);
+ #elif defined(CONFIG_PXA3xx)
+ timeout = 100;
+@@ -364,7 +364,7 @@
+ pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD);
+ #ifdef CONFIG_PXA27x
+ /* Use GPIO 113 as AC97 Reset on Bulverde */
+- pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT);
++ pxa_gpio_mode(GPIO113_AC97_RESET_N_MD | GPIO_ALT_FN_2_OUT);
+ ac97conf_clk = clk_get(&dev->dev, "AC97CONFCLK");
+ if (IS_ERR(ac97conf_clk)) {
+ ret = PTR_ERR(ac97conf_clk);
+Index: linux-2.6.26-rc4/include/asm-arm/arch-pxa/pxa2xx-gpio.h
+===================================================================
+--- linux-2.6.26-rc4.orig/include/asm-arm/arch-pxa/pxa2xx-gpio.h 2008-06-02 00:18:07.000000000 +0100
++++ linux-2.6.26-rc4/include/asm-arm/arch-pxa/pxa2xx-gpio.h 2008-06-02 00:18:28.000000000 +0100
+@@ -138,6 +138,7 @@
+ #define GPIO102_nPCE_1 102 /* PCMCIA (PXA27x) */
+ #define GPIO103_CIF_DD_3 103 /* Camera data pin 3 */
+ #define GPIO104_CIF_DD_2 104 /* Camera data pin 2 */
++#define GPIO104_pSKTSEL 104 /* PCMCIA Socket Select (PXA27x) */
+ #define GPIO105_CIF_DD_1 105 /* Camera data pin 1 */
+ #define GPIO106_CIF_DD_9 106 /* Camera data pin 9 */
+ #define GPIO107_CIF_DD_8 107 /* Camera data pin 8 */
diff --git a/recipes/linux/linux-rp-2.6.26/usb-gadget27bp.patch b/recipes/linux/linux-rp-2.6.26/usb-gadget27bp.patch
new file mode 100644
index 0000000000..3c86f1a474
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.26/usb-gadget27bp.patch
@@ -0,0 +1,26674 @@
+diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
+index 6e784d2..117d0c7 100644
+--- a/drivers/usb/gadget/Kconfig
++++ b/drivers/usb/gadget/Kconfig
+@@ -118,10 +118,10 @@ config USB_AMD5536UDC
+ config USB_GADGET_ATMEL_USBA
+ boolean "Atmel USBA"
+ select USB_GADGET_DUALSPEED
+- depends on AVR32 || ARCH_AT91CAP9
++ depends on AVR32 || ARCH_AT91CAP9 || ARCH_AT91SAM9RL
+ help
+ USBA is the integrated high-speed USB Device controller on
+- the AT32AP700x and AT91CAP9 processors from Atmel.
++ the AT32AP700x, some AT91SAM9 and AT91CAP9 processors from Atmel.
+
+ config USB_ATMEL_USBA
+ tristate
+@@ -172,7 +172,7 @@ config USB_NET2280
+ default USB_GADGET
+ select USB_GADGET_SELECTED
+
+-config USB_GADGET_PXA2XX
++config USB_GADGET_PXA25X
+ boolean "PXA 25x or IXP 4xx"
+ depends on (ARCH_PXA && PXA25x) || ARCH_IXP4XX
+ help
+@@ -184,19 +184,19 @@ config USB_GADGET_PXA2XX
+ zero (for control transfers).
+
+ Say "y" to link the driver statically, or "m" to build a
+- dynamically linked module called "pxa2xx_udc" and force all
++ dynamically linked module called "pxa25x_udc" and force all
+ gadget drivers to also be dynamically linked.
+
+-config USB_PXA2XX
++config USB_PXA25X
+ tristate
+- depends on USB_GADGET_PXA2XX
++ depends on USB_GADGET_PXA25X
+ default USB_GADGET
+ select USB_GADGET_SELECTED
+
+ # if there's only one gadget driver, using only two bulk endpoints,
+ # don't waste memory for the other endpoints
+-config USB_PXA2XX_SMALL
+- depends on USB_GADGET_PXA2XX
++config USB_PXA25X_SMALL
++ depends on USB_GADGET_PXA25X
+ bool
+ default n if USB_ETH_RNDIS
+ default y if USB_ZERO
+@@ -284,6 +284,16 @@ config USB_LH7A40X
+ default USB_GADGET
+ select USB_GADGET_SELECTED
+
++# built in ../musb along with host support
++config USB_GADGET_MUSB_HDRC
++ boolean "Inventra HDRC USB Peripheral (TI, ...)"
++ depends on USB_MUSB_HDRC && (USB_MUSB_PERIPHERAL || USB_MUSB_OTG)
++ select USB_GADGET_DUALSPEED
++ select USB_GADGET_SELECTED
++ help
++ This OTG-capable silicon IP is used in dual designs including
++ the TI DaVinci, OMAP 243x, OMAP 343x, and TUSB 6010.
++
+ config USB_GADGET_OMAP
+ boolean "OMAP USB Device Controller"
+ depends on ARCH_OMAP
+@@ -355,6 +365,20 @@ config USB_AT91
+ depends on USB_GADGET_AT91
+ default USB_GADGET
+
++config USB_GADGET_SA1100
++ boolean "SA1100 USB Device Port"
++ depends on ARCH_SA1100
++ help
++ Say "y" to link the driver statically, or "m" to build a
++ dynamically linked module called "sa1100_udc" and force all
++ gadget drivers to also be dynamically linked.
++
++config USB_SA1100
++ tristate
++ depends on USB_GADGET_SA1100
++ default USB_GADGET
++ select USB_GADGET_SELECTED
++
+ config USB_GADGET_DUMMY_HCD
+ boolean "Dummy HCD (DEVELOPMENT)"
+ depends on USB=y || (USB=m && USB_GADGET=m)
+@@ -504,6 +528,20 @@ config USB_ETH_RNDIS
+ XP, you'll need to download drivers from Microsoft's website; a URL
+ is given in comments found in that info file.
+
++config USB_ETH_NO_MDLM
++ bool "Disable MDLM support for CDC_SUBSET"
++ depends on USB_ETH
++ default n
++ help
++ A variation on CDC_SUBSET support is used in newer kernel
++ implementations for use with a proprietary Microsoft Windows driver
++ used for example with the Zaurus linux distribution.
++ However, the MDLM extensions break the older host side drivers making
++ g_ether incompatible with older kernels.
++
++ If you say "y" here, the Ethernet gadget driver will use the older
++ pre-MDLM extensions.
++
+ config USB_GADGETFS
+ tristate "Gadget Filesystem (EXPERIMENTAL)"
+ depends on EXPERIMENTAL
+@@ -586,6 +624,20 @@ config USB_G_PRINTER
+ For more information, see Documentation/usb/gadget_printer.txt
+ which includes sample code for accessing the device file.
+
++config USB_CDC_COMPOSITE
++ tristate "CDC Composite Device (Ethernet and ACM)"
++ depends on NET
++ help
++ This driver provides two functions in one configuration:
++ a CDC Ethernet (ECM) link, and a CDC ACM (serial port) link.
++
++ This driver requires four bulk and two interrupt endpoints,
++ plus the ability to handle altsettings. Not all peripheral
++ controllers are that capable.
++
++ Say "y" to link the driver statically, or "m" to build a
++ dynamically linked module.
++
+ # put drivers that need isochronous transfer support (for audio
+ # or video class gadget drivers), or specific hardware, here.
+
+diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
+index 1235725..79a69ae 100644
+--- a/drivers/usb/gadget/Makefile
++++ b/drivers/usb/gadget/Makefile
+@@ -8,7 +8,7 @@ endif
+ obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o
+ obj-$(CONFIG_USB_NET2280) += net2280.o
+ obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc.o
+-obj-$(CONFIG_USB_PXA2XX) += pxa2xx_udc.o
++obj-$(CONFIG_USB_PXA25X) += pxa25x_udc.o
+ obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o
+ obj-$(CONFIG_USB_GOKU) += goku_udc.o
+ obj-$(CONFIG_USB_OMAP) += omap_udc.o
+@@ -18,22 +18,27 @@ obj-$(CONFIG_USB_AT91) += at91_udc.o
+ obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o
+ obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o
+ obj-$(CONFIG_USB_M66592) += m66592-udc.o
++obj-$(CONFIG_USB_SA1100) += sa1100_udc.o
+
+ #
+ # USB gadget drivers
+ #
+-g_zero-objs := zero.o usbstring.o config.o epautoconf.o
+-g_ether-objs := ether.o usbstring.o config.o epautoconf.o
+-g_serial-objs := serial.o usbstring.o config.o epautoconf.o
++C_UTILS = composite.o usbstring.o config.o epautoconf.o
++
++g_zero-objs := zero.o f_sourcesink.o f_loopback.o $(C_UTILS)
++g_ether-objs := ether.o u_ether.o f_subset.o f_ecm.o $(C_UTILS)
++g_serial-objs := serial.o u_serial.o f_acm.o f_serial.o $(C_UTILS)
+ g_midi-objs := gmidi.o usbstring.o config.o epautoconf.o
+ gadgetfs-objs := inode.o
+ g_file_storage-objs := file_storage.o usbstring.o config.o \
+ epautoconf.o
+ g_printer-objs := printer.o usbstring.o config.o \
+ epautoconf.o
++g_cdc-objs := cdc2.o u_ether.o f_ecm.o \
++ u_serial.o f_acm.o $(C_UTILS)
+
+ ifeq ($(CONFIG_USB_ETH_RNDIS),y)
+- g_ether-objs += rndis.o
++ g_ether-objs += f_rndis.o rndis.o
+ endif
+
+ obj-$(CONFIG_USB_ZERO) += g_zero.o
+@@ -43,4 +48,5 @@ obj-$(CONFIG_USB_FILE_STORAGE) += g_file_storage.o
+ obj-$(CONFIG_USB_G_SERIAL) += g_serial.o
+ obj-$(CONFIG_USB_G_PRINTER) += g_printer.o
+ obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o
++obj-$(CONFIG_USB_CDC_COMPOSITE) += g_cdc.o
+
+diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c
+index f261d2a..abf8192 100644
+--- a/drivers/usb/gadget/amd5536udc.c
++++ b/drivers/usb/gadget/amd5536udc.c
+@@ -44,7 +44,6 @@
+ #include <linux/module.h>
+ #include <linux/pci.h>
+ #include <linux/kernel.h>
+-#include <linux/version.h>
+ #include <linux/delay.h>
+ #include <linux/ioport.h>
+ #include <linux/sched.h>
+@@ -3342,7 +3341,7 @@ static int udc_probe(struct udc *dev)
+ spin_lock_init(&dev->lock);
+ dev->gadget.ops = &udc_ops;
+
+- strcpy(dev->gadget.dev.bus_id, "gadget");
++ dev_set_name(&dev->gadget.dev, "gadget");
+ dev->gadget.dev.release = gadget_release;
+ dev->gadget.name = name;
+ dev->gadget.name = name;
+diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c
+index 274c60a..a8a1de4 100644
+--- a/drivers/usb/gadget/at91_udc.c
++++ b/drivers/usb/gadget/at91_udc.c
+@@ -40,16 +40,15 @@
+ #include <linux/usb/gadget.h>
+
+ #include <asm/byteorder.h>
+-#include <asm/hardware.h>
++#include <mach/hardware.h>
+ #include <asm/io.h>
+ #include <asm/irq.h>
+ #include <asm/system.h>
+-#include <asm/mach-types.h>
+ #include <asm/gpio.h>
+
+-#include <asm/arch/board.h>
+-#include <asm/arch/cpu.h>
+-#include <asm/arch/at91sam9261_matrix.h>
++#include <mach/board.h>
++#include <mach/cpu.h>
++#include <mach/at91sam9261_matrix.h>
+
+ #include "at91_udc.h"
+
+@@ -888,7 +887,7 @@ static void pullup(struct at91_udc *udc, int is_on)
+ at91_udp_write(udc, AT91_UDP_TXVC, 0);
+ if (cpu_is_at91rm9200())
+ gpio_set_value(udc->board.pullup_pin, active);
+- else if (cpu_is_at91sam9260() || cpu_is_at91sam9263()) {
++ else if (cpu_is_at91sam9260() || cpu_is_at91sam9263() || cpu_is_at91sam9g20()) {
+ u32 txvc = at91_udp_read(udc, AT91_UDP_TXVC);
+
+ txvc |= AT91_UDP_TXVC_PUON;
+@@ -906,7 +905,7 @@ static void pullup(struct at91_udc *udc, int is_on)
+ at91_udp_write(udc, AT91_UDP_TXVC, AT91_UDP_TXVC_TXVDIS);
+ if (cpu_is_at91rm9200())
+ gpio_set_value(udc->board.pullup_pin, !active);
+- else if (cpu_is_at91sam9260() || cpu_is_at91sam9263()) {
++ else if (cpu_is_at91sam9260() || cpu_is_at91sam9263() || cpu_is_at91sam9g20()) {
+ u32 txvc = at91_udp_read(udc, AT91_UDP_TXVC);
+
+ txvc &= ~AT91_UDP_TXVC_PUON;
+@@ -1687,6 +1686,19 @@ static int __init at91udc_probe(struct platform_device *pdev)
+ udc->board.pullup_active_low);
+ }
+
++ /* newer chips have more FIFO memory than rm9200 */
++ if (cpu_is_at91sam9260()) {
++ udc->ep[0].maxpacket = 64;
++ udc->ep[3].maxpacket = 64;
++ udc->ep[4].maxpacket = 512;
++ udc->ep[5].maxpacket = 512;
++ } else if (cpu_is_at91sam9261()) {
++ udc->ep[3].maxpacket = 64;
++ } else if (cpu_is_at91sam9263()) {
++ udc->ep[0].maxpacket = 64;
++ udc->ep[3].maxpacket = 64;
++ }
++
+ udc->udp_baseaddr = ioremap(res->start, res->end - res->start + 1);
+ if (!udc->udp_baseaddr) {
+ retval = -ENOMEM;
+diff --git a/drivers/usb/gadget/at91_udc.h b/drivers/usb/gadget/at91_udc.h
+index a973f2a..c65d622 100644
+--- a/drivers/usb/gadget/at91_udc.h
++++ b/drivers/usb/gadget/at91_udc.h
+@@ -171,7 +171,7 @@ struct at91_request {
+ #endif
+
+ #define ERR(stuff...) pr_err("udc: " stuff)
+-#define WARN(stuff...) pr_warning("udc: " stuff)
++#define WARNING(stuff...) pr_warning("udc: " stuff)
+ #define INFO(stuff...) pr_info("udc: " stuff)
+ #define DBG(stuff...) pr_debug("udc: " stuff)
+
+diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c
+index 07e5a0b..ae30ab1 100644
+--- a/drivers/usb/gadget/atmel_usba_udc.c
++++ b/drivers/usb/gadget/atmel_usba_udc.c
+@@ -22,7 +22,7 @@
+ #include <linux/delay.h>
+
+ #include <asm/gpio.h>
+-#include <asm/arch/board.h>
++#include <mach/board.h>
+
+ #include "atmel_usba_udc.h"
+
+@@ -334,7 +334,7 @@ static void toggle_bias(int is_on)
+
+ #elif defined(CONFIG_ARCH_AT91)
+
+-#include <asm/arch/at91_pmc.h>
++#include <mach/at91_pmc.h>
+
+ static void toggle_bias(int is_on)
+ {
+diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/cdc2.c
+new file mode 100644
+index 0000000..a39a4b9
+--- /dev/null
++++ b/drivers/usb/gadget/cdc2.c
+@@ -0,0 +1,246 @@
++/*
++ * cdc2.c -- CDC Composite driver, with ECM and ACM support
++ *
++ * Copyright (C) 2008 David Brownell
++ * Copyright (C) 2008 Nokia Corporation
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++#include <linux/kernel.h>
++#include <linux/utsname.h>
++
++#include "u_ether.h"
++#include "u_serial.h"
++
++
++#define DRIVER_DESC "CDC Composite Gadget"
++#define DRIVER_VERSION "King Kamehameha Day 2008"
++
++/*-------------------------------------------------------------------------*/
++
++/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
++ * Instead: allocate your own, using normal USB-IF procedures.
++ */
++
++/* Thanks to NetChip Technologies for donating this product ID.
++ * It's for devices with only this composite CDC configuration.
++ */
++#define CDC_VENDOR_NUM 0x0525 /* NetChip */
++#define CDC_PRODUCT_NUM 0xa4aa /* CDC Composite: ECM + ACM */
++
++/*-------------------------------------------------------------------------*/
++
++static struct usb_device_descriptor device_desc = {
++ .bLength = sizeof device_desc,
++ .bDescriptorType = USB_DT_DEVICE,
++
++ .bcdUSB = __constant_cpu_to_le16(0x0200),
++
++ .bDeviceClass = USB_CLASS_COMM,
++ .bDeviceSubClass = 0,
++ .bDeviceProtocol = 0,
++ /* .bMaxPacketSize0 = f(hardware) */
++
++ /* Vendor and product id can be overridden by module parameters. */
++ .idVendor = __constant_cpu_to_le16(CDC_VENDOR_NUM),
++ .idProduct = __constant_cpu_to_le16(CDC_PRODUCT_NUM),
++ /* .bcdDevice = f(hardware) */
++ /* .iManufacturer = DYNAMIC */
++ /* .iProduct = DYNAMIC */
++ /* NO SERIAL NUMBER */
++ .bNumConfigurations = 1,
++};
++
++static struct usb_otg_descriptor otg_descriptor = {
++ .bLength = sizeof otg_descriptor,
++ .bDescriptorType = USB_DT_OTG,
++
++ /* REVISIT SRP-only hardware is possible, although
++ * it would not be called "OTG" ...
++ */
++ .bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
++};
++
++static const struct usb_descriptor_header *otg_desc[] = {
++ (struct usb_descriptor_header *) &otg_descriptor,
++ NULL,
++};
++
++
++/* string IDs are assigned dynamically */
++
++#define STRING_MANUFACTURER_IDX 0
++#define STRING_PRODUCT_IDX 1
++
++static char manufacturer[50];
++
++static struct usb_string strings_dev[] = {
++ [STRING_MANUFACTURER_IDX].s = manufacturer,
++ [STRING_PRODUCT_IDX].s = DRIVER_DESC,
++ { } /* end of list */
++};
++
++static struct usb_gadget_strings stringtab_dev = {
++ .language = 0x0409, /* en-us */
++ .strings = strings_dev,
++};
++
++static struct usb_gadget_strings *dev_strings[] = {
++ &stringtab_dev,
++ NULL,
++};
++
++static u8 hostaddr[ETH_ALEN];
++
++/*-------------------------------------------------------------------------*/
++
++/*
++ * We _always_ have both CDC ECM and CDC ACM functions.
++ */
++static int __init cdc_do_config(struct usb_configuration *c)
++{
++ int status;
++
++ if (gadget_is_otg(c->cdev->gadget)) {
++ c->descriptors = otg_desc;
++ c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
++ }
++
++ status = ecm_bind_config(c, hostaddr);
++ if (status < 0)
++ return status;
++
++ status = acm_bind_config(c, 0);
++ if (status < 0)
++ return status;
++
++ return 0;
++}
++
++static struct usb_configuration cdc_config_driver = {
++ .label = "CDC Composite (ECM + ACM)",
++ .bind = cdc_do_config,
++ .bConfigurationValue = 1,
++ /* .iConfiguration = DYNAMIC */
++ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
++ .bMaxPower = 1, /* 2 mA, minimal */
++};
++
++/*-------------------------------------------------------------------------*/
++
++static int __init cdc_bind(struct usb_composite_dev *cdev)
++{
++ int gcnum;
++ struct usb_gadget *gadget = cdev->gadget;
++ int status;
++
++ if (!can_support_ecm(cdev->gadget)) {
++ ERROR(cdev, "controller '%s' not usable\n", gadget->name);
++ return -EINVAL;
++ }
++
++ /* set up network link layer */
++ status = gether_setup(cdev->gadget, hostaddr);
++ if (status < 0)
++ return status;
++
++ /* set up serial link layer */
++ status = gserial_setup(cdev->gadget, 1);
++ if (status < 0)
++ goto fail0;
++
++ gcnum = usb_gadget_controller_number(gadget);
++ if (gcnum >= 0)
++ device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
++ else {
++ /* We assume that can_support_ecm() tells the truth;
++ * but if the controller isn't recognized at all then
++ * that assumption is a bit more likely to be wrong.
++ */
++ WARNING(cdev, "controller '%s' not recognized; trying %s\n",
++ gadget->name,
++ cdc_config_driver.label);
++ device_desc.bcdDevice =
++ __constant_cpu_to_le16(0x0300 | 0x0099);
++ }
++
++
++ /* Allocate string descriptor numbers ... note that string
++ * contents can be overridden by the composite_dev glue.
++ */
++
++ /* device descriptor strings: manufacturer, product */
++ snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
++ init_utsname()->sysname, init_utsname()->release,
++ gadget->name);
++ status = usb_string_id(cdev);
++ if (status < 0)
++ goto fail1;
++ strings_dev[STRING_MANUFACTURER_IDX].id = status;
++ device_desc.iManufacturer = status;
++
++ status = usb_string_id(cdev);
++ if (status < 0)
++ goto fail1;
++ strings_dev[STRING_PRODUCT_IDX].id = status;
++ device_desc.iProduct = status;
++
++ /* register our configuration */
++ status = usb_add_config(cdev, &cdc_config_driver);
++ if (status < 0)
++ goto fail1;
++
++ INFO(cdev, "%s, version: " DRIVER_VERSION "\n", DRIVER_DESC);
++
++ return 0;
++
++fail1:
++ gserial_cleanup();
++fail0:
++ gether_cleanup();
++ return status;
++}
++
++static int __exit cdc_unbind(struct usb_composite_dev *cdev)
++{
++ gserial_cleanup();
++ gether_cleanup();
++ return 0;
++}
++
++static struct usb_composite_driver cdc_driver = {
++ .name = "g_cdc",
++ .dev = &device_desc,
++ .strings = dev_strings,
++ .bind = cdc_bind,
++ .unbind = __exit_p(cdc_unbind),
++};
++
++MODULE_DESCRIPTION(DRIVER_DESC);
++MODULE_AUTHOR("David Brownell");
++MODULE_LICENSE("GPL");
++
++static int __init init(void)
++{
++ return usb_composite_register(&cdc_driver);
++}
++module_init(init);
++
++static void __exit cleanup(void)
++{
++ usb_composite_unregister(&cdc_driver);
++}
++module_exit(cleanup);
+diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
+new file mode 100644
+index 0000000..85c876c
+--- /dev/null
++++ b/drivers/usb/gadget/composite.c
+@@ -0,0 +1,1041 @@
++/*
++ * composite.c - infrastructure for Composite USB Gadgets
++ *
++ * Copyright (C) 2006-2008 David Brownell
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++/* #define VERBOSE_DEBUG */
++
++#include <linux/kallsyms.h>
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/device.h>
++
++#include <linux/usb/composite.h>
++
++
++/*
++ * The code in this file is utility code, used to build a gadget driver
++ * from one or more "function" drivers, one or more "configuration"
++ * objects, and a "usb_composite_driver" by gluing them together along
++ * with the relevant device-wide data.
++ */
++
++/* big enough to hold our biggest descriptor */
++#define USB_BUFSIZ 512
++
++static struct usb_composite_driver *composite;
++
++/* Some systems will need runtime overrides for the product identifers
++ * published in the device descriptor, either numbers or strings or both.
++ * String parameters are in UTF-8 (superset of ASCII's 7 bit characters).
++ */
++
++static ushort idVendor;
++module_param(idVendor, ushort, 0);
++MODULE_PARM_DESC(idVendor, "USB Vendor ID");
++
++static ushort idProduct;
++module_param(idProduct, ushort, 0);
++MODULE_PARM_DESC(idProduct, "USB Product ID");
++
++static ushort bcdDevice;
++module_param(bcdDevice, ushort, 0);
++MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)");
++
++static char *iManufacturer;
++module_param(iManufacturer, charp, 0);
++MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string");
++
++static char *iProduct;
++module_param(iProduct, charp, 0);
++MODULE_PARM_DESC(iProduct, "USB Product string");
++
++static char *iSerialNumber;
++module_param(iSerialNumber, charp, 0);
++MODULE_PARM_DESC(iSerialNumber, "SerialNumber string");
++
++/*-------------------------------------------------------------------------*/
++
++/**
++ * usb_add_function() - add a function to a configuration
++ * @config: the configuration
++ * @function: the function being added
++ * Context: single threaded during gadget setup
++ *
++ * After initialization, each configuration must have one or more
++ * functions added to it. Adding a function involves calling its @bind()
++ * method to allocate resources such as interface and string identifiers
++ * and endpoints.
++ *
++ * This function returns the value of the function's bind(), which is
++ * zero for success else a negative errno value.
++ */
++int __init usb_add_function(struct usb_configuration *config,
++ struct usb_function *function)
++{
++ int value = -EINVAL;
++
++ DBG(config->cdev, "adding '%s'/%p to config '%s'/%p\n",
++ function->name, function,
++ config->label, config);
++
++ if (!function->set_alt || !function->disable)
++ goto done;
++
++ function->config = config;
++ list_add_tail(&function->list, &config->functions);
++
++ /* REVISIT *require* function->bind? */
++ if (function->bind) {
++ value = function->bind(config, function);
++ if (value < 0) {
++ list_del(&function->list);
++ function->config = NULL;
++ }
++ } else
++ value = 0;
++
++ /* We allow configurations that don't work at both speeds.
++ * If we run into a lowspeed Linux system, treat it the same
++ * as full speed ... it's the function drivers that will need
++ * to avoid bulk and ISO transfers.
++ */
++ if (!config->fullspeed && function->descriptors)
++ config->fullspeed = true;
++ if (!config->highspeed && function->hs_descriptors)
++ config->highspeed = true;
++
++done:
++ if (value)
++ DBG(config->cdev, "adding '%s'/%p --> %d\n",
++ function->name, function, value);
++ return value;
++}
++
++/**
++ * usb_interface_id() - allocate an unused interface ID
++ * @config: configuration associated with the interface
++ * @function: function handling the interface
++ * Context: single threaded during gadget setup
++ *
++ * usb_interface_id() is called from usb_function.bind() callbacks to
++ * allocate new interface IDs. The function driver will then store that
++ * ID in interface, association, CDC union, and other descriptors. It
++ * will also handle any control requests targetted at that interface,
++ * particularly changing its altsetting via set_alt(). There may
++ * also be class-specific or vendor-specific requests to handle.
++ *
++ * All interface identifier should be allocated using this routine, to
++ * ensure that for example different functions don't wrongly assign
++ * different meanings to the same identifier. Note that since interface
++ * identifers are configuration-specific, functions used in more than
++ * one configuration (or more than once in a given configuration) need
++ * multiple versions of the relevant descriptors.
++ *
++ * Returns the interface ID which was allocated; or -ENODEV if no
++ * more interface IDs can be allocated.
++ */
++int __init usb_interface_id(struct usb_configuration *config,
++ struct usb_function *function)
++{
++ unsigned id = config->next_interface_id;
++
++ if (id < MAX_CONFIG_INTERFACES) {
++ config->interface[id] = function;
++ config->next_interface_id = id + 1;
++ return id;
++ }
++ return -ENODEV;
++}
++
++static int config_buf(struct usb_configuration *config,
++ enum usb_device_speed speed, void *buf, u8 type)
++{
++ struct usb_config_descriptor *c = buf;
++ void *next = buf + USB_DT_CONFIG_SIZE;
++ int len = USB_BUFSIZ - USB_DT_CONFIG_SIZE;
++ struct usb_function *f;
++ int status;
++
++ /* write the config descriptor */
++ c = buf;
++ c->bLength = USB_DT_CONFIG_SIZE;
++ c->bDescriptorType = type;
++ /* wTotalLength is written later */
++ c->bNumInterfaces = config->next_interface_id;
++ c->bConfigurationValue = config->bConfigurationValue;
++ c->iConfiguration = config->iConfiguration;
++ c->bmAttributes = USB_CONFIG_ATT_ONE | config->bmAttributes;
++ c->bMaxPower = config->bMaxPower;
++
++ /* There may be e.g. OTG descriptors */
++ if (config->descriptors) {
++ status = usb_descriptor_fillbuf(next, len,
++ config->descriptors);
++ if (status < 0)
++ return status;
++ len -= status;
++ next += status;
++ }
++
++ /* add each function's descriptors */
++ list_for_each_entry(f, &config->functions, list) {
++ struct usb_descriptor_header **descriptors;
++
++ if (speed == USB_SPEED_HIGH)
++ descriptors = f->hs_descriptors;
++ else
++ descriptors = f->descriptors;
++ if (!descriptors)
++ continue;
++ status = usb_descriptor_fillbuf(next, len,
++ (const struct usb_descriptor_header **) descriptors);
++ if (status < 0)
++ return status;
++ len -= status;
++ next += status;
++ }
++
++ len = next - buf;
++ c->wTotalLength = cpu_to_le16(len);
++ return len;
++}
++
++static int config_desc(struct usb_composite_dev *cdev, unsigned w_value)
++{
++ struct usb_gadget *gadget = cdev->gadget;
++ struct usb_configuration *c;
++ u8 type = w_value >> 8;
++ enum usb_device_speed speed = USB_SPEED_UNKNOWN;
++
++ if (gadget_is_dualspeed(gadget)) {
++ int hs = 0;
++
++ if (gadget->speed == USB_SPEED_HIGH)
++ hs = 1;
++ if (type == USB_DT_OTHER_SPEED_CONFIG)
++ hs = !hs;
++ if (hs)
++ speed = USB_SPEED_HIGH;
++
++ }
++
++ /* This is a lookup by config *INDEX* */
++ w_value &= 0xff;
++ list_for_each_entry(c, &cdev->configs, list) {
++ /* ignore configs that won't work at this speed */
++ if (speed == USB_SPEED_HIGH) {
++ if (!c->highspeed)
++ continue;
++ } else {
++ if (!c->fullspeed)
++ continue;
++ }
++ if (w_value == 0)
++ return config_buf(c, speed, cdev->req->buf, type);
++ w_value--;
++ }
++ return -EINVAL;
++}
++
++static int count_configs(struct usb_composite_dev *cdev, unsigned type)
++{
++ struct usb_gadget *gadget = cdev->gadget;
++ struct usb_configuration *c;
++ unsigned count = 0;
++ int hs = 0;
++
++ if (gadget_is_dualspeed(gadget)) {
++ if (gadget->speed == USB_SPEED_HIGH)
++ hs = 1;
++ if (type == USB_DT_DEVICE_QUALIFIER)
++ hs = !hs;
++ }
++ list_for_each_entry(c, &cdev->configs, list) {
++ /* ignore configs that won't work at this speed */
++ if (hs) {
++ if (!c->highspeed)
++ continue;
++ } else {
++ if (!c->fullspeed)
++ continue;
++ }
++ count++;
++ }
++ return count;
++}
++
++static void device_qual(struct usb_composite_dev *cdev)
++{
++ struct usb_qualifier_descriptor *qual = cdev->req->buf;
++
++ qual->bLength = sizeof(*qual);
++ qual->bDescriptorType = USB_DT_DEVICE_QUALIFIER;
++ /* POLICY: same bcdUSB and device type info at both speeds */
++ qual->bcdUSB = cdev->desc.bcdUSB;
++ qual->bDeviceClass = cdev->desc.bDeviceClass;
++ qual->bDeviceSubClass = cdev->desc.bDeviceSubClass;
++ qual->bDeviceProtocol = cdev->desc.bDeviceProtocol;
++ /* ASSUME same EP0 fifo size at both speeds */
++ qual->bMaxPacketSize0 = cdev->desc.bMaxPacketSize0;
++ qual->bNumConfigurations = count_configs(cdev, USB_DT_DEVICE_QUALIFIER);
++ qual->bRESERVED = 0;
++}
++
++/*-------------------------------------------------------------------------*/
++
++static void reset_config(struct usb_composite_dev *cdev)
++{
++ struct usb_function *f;
++
++ DBG(cdev, "reset config\n");
++
++ list_for_each_entry(f, &cdev->config->functions, list) {
++ if (f->disable)
++ f->disable(f);
++ }
++ cdev->config = NULL;
++}
++
++static int set_config(struct usb_composite_dev *cdev,
++ const struct usb_ctrlrequest *ctrl, unsigned number)
++{
++ struct usb_gadget *gadget = cdev->gadget;
++ struct usb_configuration *c = NULL;
++ int result = -EINVAL;
++ unsigned power = gadget_is_otg(gadget) ? 8 : 100;
++ int tmp;
++
++ if (cdev->config)
++ reset_config(cdev);
++
++ if (number) {
++ list_for_each_entry(c, &cdev->configs, list) {
++ if (c->bConfigurationValue == number) {
++ result = 0;
++ break;
++ }
++ }
++ if (result < 0)
++ goto done;
++ } else
++ result = 0;
++
++ INFO(cdev, "%s speed config #%d: %s\n",
++ ({ char *speed;
++ switch (gadget->speed) {
++ case USB_SPEED_LOW: speed = "low"; break;
++ case USB_SPEED_FULL: speed = "full"; break;
++ case USB_SPEED_HIGH: speed = "high"; break;
++ default: speed = "?"; break;
++ } ; speed; }), number, c ? c->label : "unconfigured");
++
++ if (!c)
++ goto done;
++
++ cdev->config = c;
++
++ /* Initialize all interfaces by setting them to altsetting zero. */
++ for (tmp = 0; tmp < MAX_CONFIG_INTERFACES; tmp++) {
++ struct usb_function *f = c->interface[tmp];
++
++ if (!f)
++ break;
++
++ result = f->set_alt(f, tmp, 0);
++ if (result < 0) {
++ DBG(cdev, "interface %d (%s/%p) alt 0 --> %d\n",
++ tmp, f->name, f, result);
++
++ reset_config(cdev);
++ goto done;
++ }
++ }
++
++ /* when we return, be sure our power usage is valid */
++ power = 2 * c->bMaxPower;
++done:
++ usb_gadget_vbus_draw(gadget, power);
++ return result;
++}
++
++/**
++ * usb_add_config() - add a configuration to a device.
++ * @cdev: wraps the USB gadget
++ * @config: the configuration, with bConfigurationValue assigned
++ * Context: single threaded during gadget setup
++ *
++ * One of the main tasks of a composite driver's bind() routine is to
++ * add each of the configurations it supports, using this routine.
++ *
++ * This function returns the value of the configuration's bind(), which
++ * is zero for success else a negative errno value. Binding configurations
++ * assigns global resources including string IDs, and per-configuration
++ * resources such as interface IDs and endpoints.
++ */
++int __init usb_add_config(struct usb_composite_dev *cdev,
++ struct usb_configuration *config)
++{
++ int status = -EINVAL;
++ struct usb_configuration *c;
++
++ DBG(cdev, "adding config #%u '%s'/%p\n",
++ config->bConfigurationValue,
++ config->label, config);
++
++ if (!config->bConfigurationValue || !config->bind)
++ goto done;
++
++ /* Prevent duplicate configuration identifiers */
++ list_for_each_entry(c, &cdev->configs, list) {
++ if (c->bConfigurationValue == config->bConfigurationValue) {
++ status = -EBUSY;
++ goto done;
++ }
++ }
++
++ config->cdev = cdev;
++ list_add_tail(&config->list, &cdev->configs);
++
++ INIT_LIST_HEAD(&config->functions);
++ config->next_interface_id = 0;
++
++ status = config->bind(config);
++ if (status < 0) {
++ list_del(&config->list);
++ config->cdev = NULL;
++ } else {
++ unsigned i;
++
++ DBG(cdev, "cfg %d/%p speeds:%s%s\n",
++ config->bConfigurationValue, config,
++ config->highspeed ? " high" : "",
++ config->fullspeed
++ ? (gadget_is_dualspeed(cdev->gadget)
++ ? " full"
++ : " full/low")
++ : "");
++
++ for (i = 0; i < MAX_CONFIG_INTERFACES; i++) {
++ struct usb_function *f = config->interface[i];
++
++ if (!f)
++ continue;
++ DBG(cdev, " interface %d = %s/%p\n",
++ i, f->name, f);
++ }
++ }
++
++ /* set_alt(), or next config->bind(), sets up
++ * ep->driver_data as needed.
++ */
++ usb_ep_autoconfig_reset(cdev->gadget);
++
++done:
++ if (status)
++ DBG(cdev, "added config '%s'/%u --> %d\n", config->label,
++ config->bConfigurationValue, status);
++ return status;
++}
++
++/*-------------------------------------------------------------------------*/
++
++/* We support strings in multiple languages ... string descriptor zero
++ * says which languages are supported. The typical case will be that
++ * only one language (probably English) is used, with I18N handled on
++ * the host side.
++ */
++
++static void collect_langs(struct usb_gadget_strings **sp, __le16 *buf)
++{
++ const struct usb_gadget_strings *s;
++ u16 language;
++ __le16 *tmp;
++
++ while (*sp) {
++ s = *sp;
++ language = cpu_to_le16(s->language);
++ for (tmp = buf; *tmp && tmp < &buf[126]; tmp++) {
++ if (*tmp == language)
++ goto repeat;
++ }
++ *tmp++ = language;
++repeat:
++ sp++;
++ }
++}
++
++static int lookup_string(
++ struct usb_gadget_strings **sp,
++ void *buf,
++ u16 language,
++ int id
++)
++{
++ struct usb_gadget_strings *s;
++ int value;
++
++ while (*sp) {
++ s = *sp++;
++ if (s->language != language)
++ continue;
++ value = usb_gadget_get_string(s, id, buf);
++ if (value > 0)
++ return value;
++ }
++ return -EINVAL;
++}
++
++static int get_string(struct usb_composite_dev *cdev,
++ void *buf, u16 language, int id)
++{
++ struct usb_configuration *c;
++ struct usb_function *f;
++ int len;
++
++ /* Yes, not only is USB's I18N support probably more than most
++ * folk will ever care about ... also, it's all supported here.
++ * (Except for UTF8 support for Unicode's "Astral Planes".)
++ */
++
++ /* 0 == report all available language codes */
++ if (id == 0) {
++ struct usb_string_descriptor *s = buf;
++ struct usb_gadget_strings **sp;
++
++ memset(s, 0, 256);
++ s->bDescriptorType = USB_DT_STRING;
++
++ sp = composite->strings;
++ if (sp)
++ collect_langs(sp, s->wData);
++
++ list_for_each_entry(c, &cdev->configs, list) {
++ sp = c->strings;
++ if (sp)
++ collect_langs(sp, s->wData);
++
++ list_for_each_entry(f, &c->functions, list) {
++ sp = f->strings;
++ if (sp)
++ collect_langs(sp, s->wData);
++ }
++ }
++
++ for (len = 0; s->wData[len] && len <= 126; len++)
++ continue;
++ if (!len)
++ return -EINVAL;
++
++ s->bLength = 2 * (len + 1);
++ return s->bLength;
++ }
++
++ /* Otherwise, look up and return a specified string. String IDs
++ * are device-scoped, so we look up each string table we're told
++ * about. These lookups are infrequent; simpler-is-better here.
++ */
++ if (composite->strings) {
++ len = lookup_string(composite->strings, buf, language, id);
++ if (len > 0)
++ return len;
++ }
++ list_for_each_entry(c, &cdev->configs, list) {
++ if (c->strings) {
++ len = lookup_string(c->strings, buf, language, id);
++ if (len > 0)
++ return len;
++ }
++ list_for_each_entry(f, &c->functions, list) {
++ if (!f->strings)
++ continue;
++ len = lookup_string(f->strings, buf, language, id);
++ if (len > 0)
++ return len;
++ }
++ }
++ return -EINVAL;
++}
++
++/**
++ * usb_string_id() - allocate an unused string ID
++ * @cdev: the device whose string descriptor IDs are being allocated
++ * Context: single threaded during gadget setup
++ *
++ * @usb_string_id() is called from bind() callbacks to allocate
++ * string IDs. Drivers for functions, configurations, or gadgets will
++ * then store that ID in the appropriate descriptors and string table.
++ *
++ * All string identifier should be allocated using this routine, to
++ * ensure that for example different functions don't wrongly assign
++ * different meanings to the same identifier.
++ */
++int __init usb_string_id(struct usb_composite_dev *cdev)
++{
++ if (cdev->next_string_id < 254) {
++ /* string id 0 is reserved */
++ cdev->next_string_id++;
++ return cdev->next_string_id;
++ }
++ return -ENODEV;
++}
++
++/*-------------------------------------------------------------------------*/
++
++static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req)
++{
++ if (req->status || req->actual != req->length)
++ DBG((struct usb_composite_dev *) ep->driver_data,
++ "setup complete --> %d, %d/%d\n",
++ req->status, req->actual, req->length);
++}
++
++/*
++ * The setup() callback implements all the ep0 functionality that's
++ * not handled lower down, in hardware or the hardware driver(like
++ * device and endpoint feature flags, and their status). It's all
++ * housekeeping for the gadget function we're implementing. Most of
++ * the work is in config and function specific setup.
++ */
++static int
++composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
++{
++ struct usb_composite_dev *cdev = get_gadget_data(gadget);
++ struct usb_request *req = cdev->req;
++ int value = -EOPNOTSUPP;
++ u16 w_index = le16_to_cpu(ctrl->wIndex);
++ u16 w_value = le16_to_cpu(ctrl->wValue);
++ u16 w_length = le16_to_cpu(ctrl->wLength);
++ struct usb_function *f = NULL;
++
++ /* partial re-init of the response message; the function or the
++ * gadget might need to intercept e.g. a control-OUT completion
++ * when we delegate to it.
++ */
++ req->zero = 0;
++ req->complete = composite_setup_complete;
++ req->length = USB_BUFSIZ;
++ gadget->ep0->driver_data = cdev;
++
++ switch (ctrl->bRequest) {
++
++ /* we handle all standard USB descriptors */
++ case USB_REQ_GET_DESCRIPTOR:
++ if (ctrl->bRequestType != USB_DIR_IN)
++ goto unknown;
++ switch (w_value >> 8) {
++
++ case USB_DT_DEVICE:
++ cdev->desc.bNumConfigurations =
++ count_configs(cdev, USB_DT_DEVICE);
++ value = min(w_length, (u16) sizeof cdev->desc);
++ memcpy(req->buf, &cdev->desc, value);
++ break;
++ case USB_DT_DEVICE_QUALIFIER:
++ if (!gadget_is_dualspeed(gadget))
++ break;
++ device_qual(cdev);
++ value = min_t(int, w_length,
++ sizeof(struct usb_qualifier_descriptor));
++ break;
++ case USB_DT_OTHER_SPEED_CONFIG:
++ if (!gadget_is_dualspeed(gadget))
++ break;
++ /* FALLTHROUGH */
++ case USB_DT_CONFIG:
++ value = config_desc(cdev, w_value);
++ if (value >= 0)
++ value = min(w_length, (u16) value);
++ break;
++ case USB_DT_STRING:
++ value = get_string(cdev, req->buf,
++ w_index, w_value & 0xff);
++ if (value >= 0)
++ value = min(w_length, (u16) value);
++ break;
++ }
++ break;
++
++ /* any number of configs can work */
++ case USB_REQ_SET_CONFIGURATION:
++ if (ctrl->bRequestType != 0)
++ goto unknown;
++ if (gadget_is_otg(gadget)) {
++ if (gadget->a_hnp_support)
++ DBG(cdev, "HNP available\n");
++ else if (gadget->a_alt_hnp_support)
++ DBG(cdev, "HNP on another port\n");
++ else
++ VDBG(cdev, "HNP inactive\n");
++ }
++ spin_lock(&cdev->lock);
++ value = set_config(cdev, ctrl, w_value);
++ spin_unlock(&cdev->lock);
++ break;
++ case USB_REQ_GET_CONFIGURATION:
++ if (ctrl->bRequestType != USB_DIR_IN)
++ goto unknown;
++ if (cdev->config)
++ *(u8 *)req->buf = cdev->config->bConfigurationValue;
++ else
++ *(u8 *)req->buf = 0;
++ value = min(w_length, (u16) 1);
++ break;
++
++ /* function drivers must handle get/set altsetting; if there's
++ * no get() method, we know only altsetting zero works.
++ */
++ case USB_REQ_SET_INTERFACE:
++ if (ctrl->bRequestType != USB_RECIP_INTERFACE)
++ goto unknown;
++ if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES)
++ break;
++ f = cdev->config->interface[w_index];
++ if (!f)
++ break;
++ if (w_value && !f->get_alt)
++ break;
++ value = f->set_alt(f, w_index, w_value);
++ break;
++ case USB_REQ_GET_INTERFACE:
++ if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE))
++ goto unknown;
++ if (!cdev->config || w_index >= MAX_CONFIG_INTERFACES)
++ break;
++ f = cdev->config->interface[w_index];
++ if (!f)
++ break;
++ /* lots of interfaces only need altsetting zero... */
++ value = f->get_alt ? f->get_alt(f, w_index) : 0;
++ if (value < 0)
++ break;
++ *((u8 *)req->buf) = value;
++ value = min(w_length, (u16) 1);
++ break;
++ default:
++unknown:
++ VDBG(cdev,
++ "non-core control req%02x.%02x v%04x i%04x l%d\n",
++ ctrl->bRequestType, ctrl->bRequest,
++ w_value, w_index, w_length);
++
++ /* functions always handle their interfaces ... punt other
++ * recipients (endpoint, other, WUSB, ...) to the current
++ * configuration code.
++ *
++ * REVISIT it could make sense to let the composite device
++ * take such requests too, if that's ever needed: to work
++ * in config 0, etc.
++ */
++ if ((ctrl->bRequestType & USB_RECIP_MASK)
++ == USB_RECIP_INTERFACE) {
++ f = cdev->config->interface[w_index];
++ if (f && f->setup)
++ value = f->setup(f, ctrl);
++ else
++ f = NULL;
++ }
++ if (value < 0 && !f) {
++ struct usb_configuration *c;
++
++ c = cdev->config;
++ if (c && c->setup)
++ value = c->setup(c, ctrl);
++ }
++
++ goto done;
++ }
++
++ /* respond with data transfer before status phase? */
++ if (value >= 0) {
++ req->length = value;
++ req->zero = value < w_length;
++ value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
++ if (value < 0) {
++ DBG(cdev, "ep_queue --> %d\n", value);
++ req->status = 0;
++ composite_setup_complete(gadget->ep0, req);
++ }
++ }
++
++done:
++ /* device either stalls (value < 0) or reports success */
++ return value;
++}
++
++static void composite_disconnect(struct usb_gadget *gadget)
++{
++ struct usb_composite_dev *cdev = get_gadget_data(gadget);
++ unsigned long flags;
++
++ /* REVISIT: should we have config and device level
++ * disconnect callbacks?
++ */
++ spin_lock_irqsave(&cdev->lock, flags);
++ if (cdev->config)
++ reset_config(cdev);
++ spin_unlock_irqrestore(&cdev->lock, flags);
++}
++
++/*-------------------------------------------------------------------------*/
++
++static void /* __init_or_exit */
++composite_unbind(struct usb_gadget *gadget)
++{
++ struct usb_composite_dev *cdev = get_gadget_data(gadget);
++
++ /* composite_disconnect() must already have been called
++ * by the underlying peripheral controller driver!
++ * so there's no i/o concurrency that could affect the
++ * state protected by cdev->lock.
++ */
++ WARN_ON(cdev->config);
++
++ while (!list_empty(&cdev->configs)) {
++ struct usb_configuration *c;
++
++ c = list_first_entry(&cdev->configs,
++ struct usb_configuration, list);
++ while (!list_empty(&c->functions)) {
++ struct usb_function *f;
++
++ f = list_first_entry(&c->functions,
++ struct usb_function, list);
++ list_del(&f->list);
++ if (f->unbind) {
++ DBG(cdev, "unbind function '%s'/%p\n",
++ f->name, f);
++ f->unbind(c, f);
++ /* may free memory for "f" */
++ }
++ }
++ list_del(&c->list);
++ if (c->unbind) {
++ DBG(cdev, "unbind config '%s'/%p\n", c->label, c);
++ c->unbind(c);
++ /* may free memory for "c" */
++ }
++ }
++ if (composite->unbind)
++ composite->unbind(cdev);
++
++ if (cdev->req) {
++ kfree(cdev->req->buf);
++ usb_ep_free_request(gadget->ep0, cdev->req);
++ }
++ kfree(cdev);
++ set_gadget_data(gadget, NULL);
++ composite = NULL;
++}
++
++static void __init
++string_override_one(struct usb_gadget_strings *tab, u8 id, const char *s)
++{
++ struct usb_string *str = tab->strings;
++
++ for (str = tab->strings; str->s; str++) {
++ if (str->id == id) {
++ str->s = s;
++ return;
++ }
++ }
++}
++
++static void __init
++string_override(struct usb_gadget_strings **tab, u8 id, const char *s)
++{
++ while (*tab) {
++ string_override_one(*tab, id, s);
++ tab++;
++ }
++}
++
++static int __init composite_bind(struct usb_gadget *gadget)
++{
++ struct usb_composite_dev *cdev;
++ int status = -ENOMEM;
++
++ cdev = kzalloc(sizeof *cdev, GFP_KERNEL);
++ if (!cdev)
++ return status;
++
++ spin_lock_init(&cdev->lock);
++ cdev->gadget = gadget;
++ set_gadget_data(gadget, cdev);
++ INIT_LIST_HEAD(&cdev->configs);
++
++ /* preallocate control response and buffer */
++ cdev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
++ if (!cdev->req)
++ goto fail;
++ cdev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL);
++ if (!cdev->req->buf)
++ goto fail;
++ cdev->req->complete = composite_setup_complete;
++ gadget->ep0->driver_data = cdev;
++
++ cdev->bufsiz = USB_BUFSIZ;
++ cdev->driver = composite;
++
++ usb_gadget_set_selfpowered(gadget);
++
++ /* interface and string IDs start at zero via kzalloc.
++ * we force endpoints to start unassigned; few controller
++ * drivers will zero ep->driver_data.
++ */
++ usb_ep_autoconfig_reset(cdev->gadget);
++
++ /* composite gadget needs to assign strings for whole device (like
++ * serial number), register function drivers, potentially update
++ * power state and consumption, etc
++ */
++ status = composite->bind(cdev);
++ if (status < 0)
++ goto fail;
++
++ cdev->desc = *composite->dev;
++ cdev->desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
++
++ /* standardized runtime overrides for device ID data */
++ if (idVendor)
++ cdev->desc.idVendor = cpu_to_le16(idVendor);
++ if (idProduct)
++ cdev->desc.idProduct = cpu_to_le16(idProduct);
++ if (bcdDevice)
++ cdev->desc.bcdDevice = cpu_to_le16(bcdDevice);
++
++ /* strings can't be assigned before bind() allocates the
++ * releavnt identifiers
++ */
++ if (cdev->desc.iManufacturer && iManufacturer)
++ string_override(composite->strings,
++ cdev->desc.iManufacturer, iManufacturer);
++ if (cdev->desc.iProduct && iProduct)
++ string_override(composite->strings,
++ cdev->desc.iProduct, iProduct);
++ if (cdev->desc.iSerialNumber && iSerialNumber)
++ string_override(composite->strings,
++ cdev->desc.iSerialNumber, iSerialNumber);
++
++ INFO(cdev, "%s ready\n", composite->name);
++ return 0;
++
++fail:
++ composite_unbind(gadget);
++ return status;
++}
++
++/*-------------------------------------------------------------------------*/
++
++static void
++composite_suspend(struct usb_gadget *gadget)
++{
++ struct usb_composite_dev *cdev = get_gadget_data(gadget);
++ struct usb_function *f;
++
++ /* REVISIT: should we have config and device level
++ * suspend/resume callbacks?
++ */
++ DBG(cdev, "suspend\n");
++ if (cdev->config) {
++ list_for_each_entry(f, &cdev->config->functions, list) {
++ if (f->suspend)
++ f->suspend(f);
++ }
++ }
++}
++
++static void
++composite_resume(struct usb_gadget *gadget)
++{
++ struct usb_composite_dev *cdev = get_gadget_data(gadget);
++ struct usb_function *f;
++
++ /* REVISIT: should we have config and device level
++ * suspend/resume callbacks?
++ */
++ DBG(cdev, "resume\n");
++ if (cdev->config) {
++ list_for_each_entry(f, &cdev->config->functions, list) {
++ if (f->resume)
++ f->resume(f);
++ }
++ }
++}
++
++/*-------------------------------------------------------------------------*/
++
++static struct usb_gadget_driver composite_driver = {
++ .speed = USB_SPEED_HIGH,
++
++ .bind = composite_bind,
++ .unbind = __exit_p(composite_unbind),
++
++ .setup = composite_setup,
++ .disconnect = composite_disconnect,
++
++ .suspend = composite_suspend,
++ .resume = composite_resume,
++
++ .driver = {
++ .owner = THIS_MODULE,
++ },
++};
++
++/**
++ * usb_composite_register() - register a composite driver
++ * @driver: the driver to register
++ * Context: single threaded during gadget setup
++ *
++ * This function is used to register drivers using the composite driver
++ * framework. The return value is zero, or a negative errno value.
++ * Those values normally come from the driver's @bind method, which does
++ * all the work of setting up the driver to match the hardware.
++ *
++ * On successful return, the gadget is ready to respond to requests from
++ * the host, unless one of its components invokes usb_gadget_disconnect()
++ * while it was binding. That would usually be done in order to wait for
++ * some userspace participation.
++ */
++int __init usb_composite_register(struct usb_composite_driver *driver)
++{
++ if (!driver || !driver->dev || !driver->bind || composite)
++ return -EINVAL;
++
++ if (!driver->name)
++ driver->name = "composite";
++ composite_driver.function = (char *) driver->name;
++ composite_driver.driver.name = driver->name;
++ composite = driver;
++
++ return usb_gadget_register_driver(&composite_driver);
++}
++
++/**
++ * usb_composite_unregister() - unregister a composite driver
++ * @driver: the driver to unregister
++ *
++ * This function is used to unregister drivers using the composite
++ * driver framework.
++ */
++void __exit usb_composite_unregister(struct usb_composite_driver *driver)
++{
++ if (composite != driver)
++ return;
++ usb_gadget_unregister_driver(&composite_driver);
++}
+diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c
+index a4e54b2..1ca1c32 100644
+--- a/drivers/usb/gadget/config.c
++++ b/drivers/usb/gadget/config.c
+@@ -96,7 +96,7 @@ int usb_gadget_config_buf(
+ /* config descriptor first */
+ if (length < USB_DT_CONFIG_SIZE || !desc)
+ return -EINVAL;
+- *cp = *config;
++ *cp = *config;
+
+ /* then interface/endpoint/class/vendor/... */
+ len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8*)buf,
+@@ -115,3 +115,77 @@ int usb_gadget_config_buf(
+ return len;
+ }
+
++/**
++ * usb_copy_descriptors - copy a vector of USB descriptors
++ * @src: null-terminated vector to copy
++ * Context: initialization code, which may sleep
++ *
++ * This makes a copy of a vector of USB descriptors. Its primary use
++ * is to support usb_function objects which can have multiple copies,
++ * each needing different descriptors. Functions may have static
++ * tables of descriptors, which are used as templates and customized
++ * with identifiers (for interfaces, strings, endpoints, and more)
++ * as needed by a given function instance.
++ */
++struct usb_descriptor_header **__init
++usb_copy_descriptors(struct usb_descriptor_header **src)
++{
++ struct usb_descriptor_header **tmp;
++ unsigned bytes;
++ unsigned n_desc;
++ void *mem;
++ struct usb_descriptor_header **ret;
++
++ /* count descriptors and their sizes; then add vector size */
++ for (bytes = 0, n_desc = 0, tmp = src; *tmp; tmp++, n_desc++)
++ bytes += (*tmp)->bLength;
++ bytes += (n_desc + 1) * sizeof(*tmp);
++
++ mem = kmalloc(bytes, GFP_KERNEL);
++ if (!mem)
++ return NULL;
++
++ /* fill in pointers starting at "tmp",
++ * to descriptors copied starting at "mem";
++ * and return "ret"
++ */
++ tmp = mem;
++ ret = mem;
++ mem += (n_desc + 1) * sizeof(*tmp);
++ while (*src) {
++ memcpy(mem, *src, (*src)->bLength);
++ *tmp = mem;
++ tmp++;
++ mem += (*src)->bLength;
++ src++;
++ }
++ *tmp = NULL;
++
++ return ret;
++}
++
++/**
++ * usb_find_endpoint - find a copy of an endpoint descriptor
++ * @src: original vector of descriptors
++ * @copy: copy of @src
++ * @ep: endpoint descriptor found in @src
++ *
++ * This returns the copy of the @match descriptor made for @copy. Its
++ * intended use is to help remembering the endpoint descriptor to use
++ * when enabling a given endpoint.
++ */
++struct usb_endpoint_descriptor *__init
++usb_find_endpoint(
++ struct usb_descriptor_header **src,
++ struct usb_descriptor_header **copy,
++ struct usb_endpoint_descriptor *match
++)
++{
++ while (*src) {
++ if (*src == (void *) match)
++ return (void *)*copy;
++ src++;
++ copy++;
++ }
++ return NULL;
++}
+diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c
+index 4203619..7600a0c 100644
+--- a/drivers/usb/gadget/dummy_hcd.c
++++ b/drivers/usb/gadget/dummy_hcd.c
+@@ -542,13 +542,14 @@ dummy_queue (struct usb_ep *_ep, struct usb_request *_req,
+ req->req.context = dum;
+ req->req.complete = fifo_complete;
+
++ list_add_tail(&req->queue, &ep->queue);
+ spin_unlock (&dum->lock);
+ _req->actual = _req->length;
+ _req->status = 0;
+ _req->complete (_ep, _req);
+ spin_lock (&dum->lock);
+- }
+- list_add_tail (&req->queue, &ep->queue);
++ } else
++ list_add_tail(&req->queue, &ep->queue);
+ spin_unlock_irqrestore (&dum->lock, flags);
+
+ /* real hardware would likely enable transfers here, in case
+@@ -862,7 +863,7 @@ static int dummy_udc_probe (struct platform_device *pdev)
+ /* maybe claim OTG support, though we won't complete HNP */
+ dum->gadget.is_otg = (dummy_to_hcd(dum)->self.otg_port != 0);
+
+- strcpy (dum->gadget.dev.bus_id, "gadget");
++ dev_set_name(&dum->gadget.dev, "gadget");
+ dum->gadget.dev.parent = &pdev->dev;
+ dum->gadget.dev.release = dummy_gadget_release;
+ rc = device_register (&dum->gadget.dev);
+@@ -1865,7 +1866,7 @@ static int dummy_hcd_probe(struct platform_device *pdev)
+
+ dev_info(&pdev->dev, "%s, driver " DRIVER_VERSION "\n", driver_desc);
+
+- hcd = usb_create_hcd(&dummy_hcd, &pdev->dev, pdev->dev.bus_id);
++ hcd = usb_create_hcd(&dummy_hcd, &pdev->dev, dev_name(&pdev->dev));
+ if (!hcd)
+ return -ENOMEM;
+ the_controller = hcd_to_dummy (hcd);
+diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c
+index 8bdad22..9462e30 100644
+--- a/drivers/usb/gadget/epautoconf.c
++++ b/drivers/usb/gadget/epautoconf.c
+@@ -159,6 +159,7 @@ ep_matches (
+ /* MATCH!! */
+
+ /* report address */
++ desc->bEndpointAddress &= USB_DIR_IN;
+ if (isdigit (ep->name [2])) {
+ u8 num = simple_strtol (&ep->name [2], NULL, 10);
+ desc->bEndpointAddress |= num;
+diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c
+index 8d61ea6..bcac2e6 100644
+--- a/drivers/usb/gadget/ether.c
++++ b/drivers/usb/gadget/ether.c
+@@ -1,8 +1,9 @@
+ /*
+ * ether.c -- Ethernet gadget driver, with CDC and non-CDC options
+ *
+- * Copyright (C) 2003-2005 David Brownell
++ * Copyright (C) 2003-2005,2008 David Brownell
+ * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
++ * Copyright (C) 2008 Nokia Corporation
+ *
+ * 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
+@@ -23,18 +24,9 @@
+
+ #include <linux/kernel.h>
+ #include <linux/utsname.h>
+-#include <linux/device.h>
+-#include <linux/ctype.h>
+-#include <linux/etherdevice.h>
+-#include <linux/ethtool.h>
+
+-#include <linux/usb/ch9.h>
+-#include <linux/usb/cdc.h>
+-#include <linux/usb/gadget.h>
++#include "u_ether.h"
+
+-#include "gadget_chips.h"
+-
+-/*-------------------------------------------------------------------------*/
+
+ /*
+ * Ethernet gadget driver -- with CDC and non-CDC options
+@@ -46,7 +38,11 @@
+ * this USB-IF standard as its open-systems interoperability solution;
+ * most host side USB stacks (except from Microsoft) support it.
+ *
+- * There's some hardware that can't talk CDC. We make that hardware
++ * This is sometimes called "CDC ECM" (Ethernet Control Model) to support
++ * TLA-soup. "CDC ACM" (Abstract Control Model) is for modems, and a new
++ * "CDC EEM" (Ethernet Emulation Model) is starting to spread.
++ *
++ * There's some hardware that can't talk CDC ECM. We make that hardware
+ * implement a "minimalist" vendor-agnostic CDC core: same framing, but
+ * link-level setup only requires activating the configuration. Only the
+ * endpoint descriptors, and product/vendor IDs, are relevant; no control
+@@ -64,70 +60,40 @@
+ * A third option is also in use. Rather than CDC Ethernet, or something
+ * simpler, Microsoft pushes their own approach: RNDIS. The published
+ * RNDIS specs are ambiguous and appear to be incomplete, and are also
+- * needlessly complex.
++ * needlessly complex. They borrow more from CDC ACM than CDC ECM.
+ */
+
+ #define DRIVER_DESC "Ethernet Gadget"
+-#define DRIVER_VERSION "May Day 2005"
+-
+-static const char shortname [] = "ether";
+-static const char driver_desc [] = DRIVER_DESC;
+-
+-#define RX_EXTRA 20 /* guard against rx overflows */
+-
+-#include "rndis.h"
++#define DRIVER_VERSION "Memorial Day 2008"
+
+-#ifndef CONFIG_USB_ETH_RNDIS
+-#define rndis_uninit(x) do{}while(0)
+-#define rndis_deregister(c) do{}while(0)
+-#define rndis_exit() do{}while(0)
++#ifdef CONFIG_USB_ETH_RNDIS
++#define PREFIX "RNDIS/"
++#else
++#define PREFIX ""
+ #endif
+
+-/* CDC and RNDIS support the same host-chosen outgoing packet filters. */
+-#define DEFAULT_FILTER (USB_CDC_PACKET_TYPE_BROADCAST \
+- |USB_CDC_PACKET_TYPE_ALL_MULTICAST \
+- |USB_CDC_PACKET_TYPE_PROMISCUOUS \
+- |USB_CDC_PACKET_TYPE_DIRECTED)
+-
+-
+-/*-------------------------------------------------------------------------*/
+-
+-struct eth_dev {
+- spinlock_t lock;
+- struct usb_gadget *gadget;
+- struct usb_request *req; /* for control responses */
+- struct usb_request *stat_req; /* for cdc & rndis status */
+-
+- u8 config;
+- struct usb_ep *in_ep, *out_ep, *status_ep;
+- const struct usb_endpoint_descriptor
+- *in, *out, *status;
+-
+- spinlock_t req_lock;
+- struct list_head tx_reqs, rx_reqs;
+-
+- struct net_device *net;
+- struct net_device_stats stats;
+- atomic_t tx_qlen;
+-
+- struct work_struct work;
+- unsigned zlp:1;
+- unsigned cdc:1;
+- unsigned rndis:1;
+- unsigned suspended:1;
+- u16 cdc_filter;
+- unsigned long todo;
+-#define WORK_RX_MEMORY 0
+- int rndis_config;
+- u8 host_mac [ETH_ALEN];
+-};
+-
+-/* This version autoconfigures as much as possible at run-time.
++/*
++ * This driver aims for interoperability by using CDC ECM unless
++ *
++ * can_support_ecm()
++ *
++ * returns false, in which case it supports the CDC Subset. By default,
++ * that returns true; most hardware has no problems with CDC ECM, that's
++ * a good default. Previous versions of this driver had no default; this
++ * version changes that, removing overhead for new controller support.
+ *
+- * It also ASSUMES a self-powered device, without remote wakeup,
+- * although remote wakeup support would make sense.
++ * IF YOUR HARDWARE CAN'T SUPPORT CDC ECM, UPDATE THAT ROUTINE!
+ */
+
++static inline bool has_rndis(void)
++{
++#ifdef CONFIG_USB_ETH_RNDIS
++ return true;
++#else
++ return false;
++#endif
++}
++
+ /*-------------------------------------------------------------------------*/
+
+ /* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
+@@ -137,8 +103,8 @@ struct eth_dev {
+ /* Thanks to NetChip Technologies for donating this product ID.
+ * It's for devices with only CDC Ethernet configurations.
+ */
+-#define CDC_VENDOR_NUM 0x0525 /* NetChip */
+-#define CDC_PRODUCT_NUM 0xa4a1 /* Linux-USB Ethernet Gadget */
++#define CDC_VENDOR_NUM 0x0525 /* NetChip */
++#define CDC_PRODUCT_NUM 0xa4a1 /* Linux-USB Ethernet Gadget */
+
+ /* For hardware that can't talk CDC, we use the same vendor ID that
+ * ARM Linux has used for ethernet-over-usb, both with sa1100 and
+@@ -162,274 +128,9 @@ struct eth_dev {
+ #define RNDIS_VENDOR_NUM 0x0525 /* NetChip */
+ #define RNDIS_PRODUCT_NUM 0xa4a2 /* Ethernet/RNDIS Gadget */
+
+-
+-/* Some systems will want different product identifers published in the
+- * device descriptor, either numbers or strings or both. These string
+- * parameters are in UTF-8 (superset of ASCII's 7 bit characters).
+- */
+-
+-static ushort idVendor;
+-module_param(idVendor, ushort, S_IRUGO);
+-MODULE_PARM_DESC(idVendor, "USB Vendor ID");
+-
+-static ushort idProduct;
+-module_param(idProduct, ushort, S_IRUGO);
+-MODULE_PARM_DESC(idProduct, "USB Product ID");
+-
+-static ushort bcdDevice;
+-module_param(bcdDevice, ushort, S_IRUGO);
+-MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)");
+-
+-static char *iManufacturer;
+-module_param(iManufacturer, charp, S_IRUGO);
+-MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string");
+-
+-static char *iProduct;
+-module_param(iProduct, charp, S_IRUGO);
+-MODULE_PARM_DESC(iProduct, "USB Product string");
+-
+-static char *iSerialNumber;
+-module_param(iSerialNumber, charp, S_IRUGO);
+-MODULE_PARM_DESC(iSerialNumber, "SerialNumber");
+-
+-/* initial value, changed by "ifconfig usb0 hw ether xx:xx:xx:xx:xx:xx" */
+-static char *dev_addr;
+-module_param(dev_addr, charp, S_IRUGO);
+-MODULE_PARM_DESC(dev_addr, "Device Ethernet Address");
+-
+-/* this address is invisible to ifconfig */
+-static char *host_addr;
+-module_param(host_addr, charp, S_IRUGO);
+-MODULE_PARM_DESC(host_addr, "Host Ethernet Address");
+-
+-
+-/*-------------------------------------------------------------------------*/
+-
+-/* Include CDC support if we could run on CDC-capable hardware. */
+-
+-#ifdef CONFIG_USB_GADGET_NET2280
+-#define DEV_CONFIG_CDC
+-#endif
+-
+-#ifdef CONFIG_USB_GADGET_DUMMY_HCD
+-#define DEV_CONFIG_CDC
+-#endif
+-
+-#ifdef CONFIG_USB_GADGET_GOKU
+-#define DEV_CONFIG_CDC
+-#endif
+-
+-#ifdef CONFIG_USB_GADGET_LH7A40X
+-#define DEV_CONFIG_CDC
+-#endif
+-
+-#ifdef CONFIG_USB_GADGET_MQ11XX
+-#define DEV_CONFIG_CDC
+-#endif
+-
+-#ifdef CONFIG_USB_GADGET_OMAP
+-#define DEV_CONFIG_CDC
+-#endif
+-
+-#ifdef CONFIG_USB_GADGET_N9604
+-#define DEV_CONFIG_CDC
+-#endif
+-
+-#ifdef CONFIG_USB_GADGET_S3C2410
+-#define DEV_CONFIG_CDC
+-#endif
+-
+-#ifdef CONFIG_USB_GADGET_AT91
+-#define DEV_CONFIG_CDC
+-#endif
+-
+-#ifdef CONFIG_USB_GADGET_MUSBHSFC
+-#define DEV_CONFIG_CDC
+-#endif
+-
+-#ifdef CONFIG_USB_GADGET_MUSB_HDRC
+-#define DEV_CONFIG_CDC
+-#endif
+-
+-#ifdef CONFIG_USB_GADGET_ATMEL_USBA
+-#define DEV_CONFIG_CDC
+-#endif
+-
+-#ifdef CONFIG_USB_GADGET_FSL_USB2
+-#define DEV_CONFIG_CDC
+-#endif
+-
+-/* For CDC-incapable hardware, choose the simple cdc subset.
+- * Anything that talks bulk (without notable bugs) can do this.
+- */
+-#ifdef CONFIG_USB_GADGET_PXA2XX
+-#define DEV_CONFIG_SUBSET
+-#endif
+-
+-#ifdef CONFIG_USB_GADGET_PXA27X
+-#define DEV_CONFIG_SUBSET
+-#endif
+-
+-#ifdef CONFIG_USB_GADGET_SUPERH
+-#define DEV_CONFIG_SUBSET
+-#endif
+-
+-#ifdef CONFIG_USB_GADGET_SA1100
+-/* use non-CDC for backwards compatibility */
+-#define DEV_CONFIG_SUBSET
+-#endif
+-
+-#ifdef CONFIG_USB_GADGET_M66592
+-#define DEV_CONFIG_CDC
+-#endif
+-
+-#ifdef CONFIG_USB_GADGET_AMD5536UDC
+-#define DEV_CONFIG_CDC
+-#endif
+-
+-
+-/*-------------------------------------------------------------------------*/
+-
+-/* "main" config is either CDC, or its simple subset */
+-static inline int is_cdc(struct eth_dev *dev)
+-{
+-#if !defined(DEV_CONFIG_SUBSET)
+- return 1; /* only cdc possible */
+-#elif !defined (DEV_CONFIG_CDC)
+- return 0; /* only subset possible */
+-#else
+- return dev->cdc; /* depends on what hardware we found */
+-#endif
+-}
+-
+-/* "secondary" RNDIS config may sometimes be activated */
+-static inline int rndis_active(struct eth_dev *dev)
+-{
+-#ifdef CONFIG_USB_ETH_RNDIS
+- return dev->rndis;
+-#else
+- return 0;
+-#endif
+-}
+-
+-#define subset_active(dev) (!is_cdc(dev) && !rndis_active(dev))
+-#define cdc_active(dev) ( is_cdc(dev) && !rndis_active(dev))
+-
+-
+-
+-#define DEFAULT_QLEN 2 /* double buffering by default */
+-
+-/* peak bulk transfer bits-per-second */
+-#define HS_BPS (13 * 512 * 8 * 1000 * 8)
+-#define FS_BPS (19 * 64 * 1 * 1000 * 8)
+-
+-#ifdef CONFIG_USB_GADGET_DUALSPEED
+-#define DEVSPEED USB_SPEED_HIGH
+-
+-static unsigned qmult = 5;
+-module_param (qmult, uint, S_IRUGO|S_IWUSR);
+-
+-
+-/* for dual-speed hardware, use deeper queues at highspeed */
+-#define qlen(gadget) \
+- (DEFAULT_QLEN*((gadget->speed == USB_SPEED_HIGH) ? qmult : 1))
+-
+-static inline int BITRATE(struct usb_gadget *g)
+-{
+- return (g->speed == USB_SPEED_HIGH) ? HS_BPS : FS_BPS;
+-}
+-
+-#else /* full speed (low speed doesn't do bulk) */
+-
+-#define qmult 1
+-
+-#define DEVSPEED USB_SPEED_FULL
+-
+-#define qlen(gadget) DEFAULT_QLEN
+-
+-static inline int BITRATE(struct usb_gadget *g)
+-{
+- return FS_BPS;
+-}
+-#endif
+-
+-
+-/*-------------------------------------------------------------------------*/
+-
+-#define xprintk(d,level,fmt,args...) \
+- printk(level "%s: " fmt , (d)->net->name , ## args)
+-
+-#ifdef DEBUG
+-#undef DEBUG
+-#define DEBUG(dev,fmt,args...) \
+- xprintk(dev , KERN_DEBUG , fmt , ## args)
+-#else
+-#define DEBUG(dev,fmt,args...) \
+- do { } while (0)
+-#endif /* DEBUG */
+-
+-#ifdef VERBOSE_DEBUG
+-#define VDEBUG DEBUG
+-#else
+-#define VDEBUG(dev,fmt,args...) \
+- do { } while (0)
+-#endif /* DEBUG */
+-
+-#define ERROR(dev,fmt,args...) \
+- xprintk(dev , KERN_ERR , fmt , ## args)
+-#define WARN(dev,fmt,args...) \
+- xprintk(dev , KERN_WARNING , fmt , ## args)
+-#define INFO(dev,fmt,args...) \
+- xprintk(dev , KERN_INFO , fmt , ## args)
+-
+ /*-------------------------------------------------------------------------*/
+
+-/* USB DRIVER HOOKUP (to the hardware driver, below us), mostly
+- * ep0 implementation: descriptors, config management, setup().
+- * also optional class-specific notification interrupt transfer.
+- */
+-
+-/*
+- * DESCRIPTORS ... most are static, but strings and (full) configuration
+- * descriptors are built on demand. For now we do either full CDC, or
+- * our simple subset, with RNDIS as an optional second configuration.
+- *
+- * RNDIS includes some CDC ACM descriptors ... like CDC Ethernet. But
+- * the class descriptors match a modem (they're ignored; it's really just
+- * Ethernet functionality), they don't need the NOP altsetting, and the
+- * status transfer endpoint isn't optional.
+- */
+-
+-#define STRING_MANUFACTURER 1
+-#define STRING_PRODUCT 2
+-#define STRING_ETHADDR 3
+-#define STRING_DATA 4
+-#define STRING_CONTROL 5
+-#define STRING_RNDIS_CONTROL 6
+-#define STRING_CDC 7
+-#define STRING_SUBSET 8
+-#define STRING_RNDIS 9
+-#define STRING_SERIALNUMBER 10
+-
+-/* holds our biggest descriptor (or RNDIS response) */
+-#define USB_BUFSIZ 256
+-
+-/*
+- * This device advertises one configuration, eth_config, unless RNDIS
+- * is enabled (rndis_config) on hardware supporting at least two configs.
+- *
+- * NOTE: Controllers like superh_udc should probably be able to use
+- * an RNDIS-only configuration.
+- *
+- * FIXME define some higher-powered configurations to make it easier
+- * to recharge batteries ...
+- */
+-
+-#define DEV_CONFIG_VALUE 1 /* cdc or subset */
+-#define DEV_RNDIS_CONFIG_VALUE 2 /* rndis; optional */
+-
+-static struct usb_device_descriptor
+-device_desc = {
++static struct usb_device_descriptor device_desc = {
+ .bLength = sizeof device_desc,
+ .bDescriptorType = USB_DT_DEVICE,
+
+@@ -438,2220 +139,234 @@ device_desc = {
+ .bDeviceClass = USB_CLASS_COMM,
+ .bDeviceSubClass = 0,
+ .bDeviceProtocol = 0,
++ /* .bMaxPacketSize0 = f(hardware) */
+
++ /* Vendor and product id defaults change according to what configs
++ * we support. (As does bNumConfigurations.) These values can
++ * also be overridden by module parameters.
++ */
+ .idVendor = __constant_cpu_to_le16 (CDC_VENDOR_NUM),
+ .idProduct = __constant_cpu_to_le16 (CDC_PRODUCT_NUM),
+- .iManufacturer = STRING_MANUFACTURER,
+- .iProduct = STRING_PRODUCT,
++ /* .bcdDevice = f(hardware) */
++ /* .iManufacturer = DYNAMIC */
++ /* .iProduct = DYNAMIC */
++ /* NO SERIAL NUMBER */
+ .bNumConfigurations = 1,
+ };
+
+-static struct usb_otg_descriptor
+-otg_descriptor = {
++static struct usb_otg_descriptor otg_descriptor = {
+ .bLength = sizeof otg_descriptor,
+ .bDescriptorType = USB_DT_OTG,
+
+- .bmAttributes = USB_OTG_SRP,
+-};
+-
+-static struct usb_config_descriptor
+-eth_config = {
+- .bLength = sizeof eth_config,
+- .bDescriptorType = USB_DT_CONFIG,
+-
+- /* compute wTotalLength on the fly */
+- .bNumInterfaces = 2,
+- .bConfigurationValue = DEV_CONFIG_VALUE,
+- .iConfiguration = STRING_CDC,
+- .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
+- .bMaxPower = 50,
+-};
+-
+-#ifdef CONFIG_USB_ETH_RNDIS
+-static struct usb_config_descriptor
+-rndis_config = {
+- .bLength = sizeof rndis_config,
+- .bDescriptorType = USB_DT_CONFIG,
+-
+- /* compute wTotalLength on the fly */
+- .bNumInterfaces = 2,
+- .bConfigurationValue = DEV_RNDIS_CONFIG_VALUE,
+- .iConfiguration = STRING_RNDIS,
+- .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
+- .bMaxPower = 50,
+-};
+-#endif
+-
+-/*
+- * Compared to the simple CDC subset, the full CDC Ethernet model adds
+- * three class descriptors, two interface descriptors, optional status
+- * endpoint. Both have a "data" interface and two bulk endpoints.
+- * There are also differences in how control requests are handled.
+- *
+- * RNDIS shares a lot with CDC-Ethernet, since it's a variant of the
+- * CDC-ACM (modem) spec. Unfortunately MSFT's RNDIS driver is buggy; it
+- * may hang or oops. Since bugfixes (or accurate specs, letting Linux
+- * work around those bugs) are unlikely to ever come from MSFT, you may
+- * wish to avoid using RNDIS.
+- *
+- * MCCI offers an alternative to RNDIS if you need to connect to Windows
+- * but have hardware that can't support CDC Ethernet. We add descriptors
+- * to present the CDC Subset as a (nonconformant) CDC MDLM variant called
+- * "SAFE". That borrows from both CDC Ethernet and CDC MDLM. You can
+- * get those drivers from MCCI, or bundled with various products.
+- */
+-
+-#ifdef DEV_CONFIG_CDC
+-static struct usb_interface_descriptor
+-control_intf = {
+- .bLength = sizeof control_intf,
+- .bDescriptorType = USB_DT_INTERFACE,
+-
+- .bInterfaceNumber = 0,
+- /* status endpoint is optional; this may be patched later */
+- .bNumEndpoints = 1,
+- .bInterfaceClass = USB_CLASS_COMM,
+- .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET,
+- .bInterfaceProtocol = USB_CDC_PROTO_NONE,
+- .iInterface = STRING_CONTROL,
+-};
+-#endif
+-
+-#ifdef CONFIG_USB_ETH_RNDIS
+-static const struct usb_interface_descriptor
+-rndis_control_intf = {
+- .bLength = sizeof rndis_control_intf,
+- .bDescriptorType = USB_DT_INTERFACE,
+-
+- .bInterfaceNumber = 0,
+- .bNumEndpoints = 1,
+- .bInterfaceClass = USB_CLASS_COMM,
+- .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
+- .bInterfaceProtocol = USB_CDC_ACM_PROTO_VENDOR,
+- .iInterface = STRING_RNDIS_CONTROL,
+-};
+-#endif
+-
+-static const struct usb_cdc_header_desc header_desc = {
+- .bLength = sizeof header_desc,
+- .bDescriptorType = USB_DT_CS_INTERFACE,
+- .bDescriptorSubType = USB_CDC_HEADER_TYPE,
+-
+- .bcdCDC = __constant_cpu_to_le16 (0x0110),
+-};
+-
+-#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS)
+-
+-static const struct usb_cdc_union_desc union_desc = {
+- .bLength = sizeof union_desc,
+- .bDescriptorType = USB_DT_CS_INTERFACE,
+- .bDescriptorSubType = USB_CDC_UNION_TYPE,
+-
+- .bMasterInterface0 = 0, /* index of control interface */
+- .bSlaveInterface0 = 1, /* index of DATA interface */
+-};
+-
+-#endif /* CDC || RNDIS */
+-
+-#ifdef CONFIG_USB_ETH_RNDIS
+-
+-static const struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor = {
+- .bLength = sizeof call_mgmt_descriptor,
+- .bDescriptorType = USB_DT_CS_INTERFACE,
+- .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE,
+-
+- .bmCapabilities = 0x00,
+- .bDataInterface = 0x01,
+-};
+-
+-static const struct usb_cdc_acm_descriptor acm_descriptor = {
+- .bLength = sizeof acm_descriptor,
+- .bDescriptorType = USB_DT_CS_INTERFACE,
+- .bDescriptorSubType = USB_CDC_ACM_TYPE,
+-
+- .bmCapabilities = 0x00,
+-};
+-
+-#endif
+-
+-#ifndef DEV_CONFIG_CDC
+-
+-/* "SAFE" loosely follows CDC WMC MDLM, violating the spec in various
+- * ways: data endpoints live in the control interface, there's no data
+- * interface, and it's not used to talk to a cell phone radio.
+- */
+-
+-static const struct usb_cdc_mdlm_desc mdlm_desc = {
+- .bLength = sizeof mdlm_desc,
+- .bDescriptorType = USB_DT_CS_INTERFACE,
+- .bDescriptorSubType = USB_CDC_MDLM_TYPE,
+-
+- .bcdVersion = __constant_cpu_to_le16(0x0100),
+- .bGUID = {
+- 0x5d, 0x34, 0xcf, 0x66, 0x11, 0x18, 0x11, 0xd6,
+- 0xa2, 0x1a, 0x00, 0x01, 0x02, 0xca, 0x9a, 0x7f,
+- },
+-};
+-
+-/* since "usb_cdc_mdlm_detail_desc" is a variable length structure, we
+- * can't really use its struct. All we do here is say that we're using
+- * the submode of "SAFE" which directly matches the CDC Subset.
+- */
+-static const u8 mdlm_detail_desc[] = {
+- 6,
+- USB_DT_CS_INTERFACE,
+- USB_CDC_MDLM_DETAIL_TYPE,
+-
+- 0, /* "SAFE" */
+- 0, /* network control capabilities (none) */
+- 0, /* network data capabilities ("raw" encapsulation) */
+-};
+-
+-#endif
+-
+-static const struct usb_cdc_ether_desc ether_desc = {
+- .bLength = sizeof ether_desc,
+- .bDescriptorType = USB_DT_CS_INTERFACE,
+- .bDescriptorSubType = USB_CDC_ETHERNET_TYPE,
+-
+- /* this descriptor actually adds value, surprise! */
+- .iMACAddress = STRING_ETHADDR,
+- .bmEthernetStatistics = __constant_cpu_to_le32 (0), /* no statistics */
+- .wMaxSegmentSize = __constant_cpu_to_le16 (ETH_FRAME_LEN),
+- .wNumberMCFilters = __constant_cpu_to_le16 (0),
+- .bNumberPowerFilters = 0,
+-};
+-
+-
+-#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS)
+-
+-/* include the status endpoint if we can, even where it's optional.
+- * use wMaxPacketSize big enough to fit CDC_NOTIFY_SPEED_CHANGE in one
+- * packet, to simplify cancellation; and a big transfer interval, to
+- * waste less bandwidth.
+- *
+- * some drivers (like Linux 2.4 cdc-ether!) "need" it to exist even
+- * if they ignore the connect/disconnect notifications that real aether
+- * can provide. more advanced cdc configurations might want to support
+- * encapsulated commands (vendor-specific, using control-OUT).
+- *
+- * RNDIS requires the status endpoint, since it uses that encapsulation
+- * mechanism for its funky RPC scheme.
+- */
+-
+-#define LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */
+-#define STATUS_BYTECOUNT 16 /* 8 byte header + data */
+-
+-static struct usb_endpoint_descriptor
+-fs_status_desc = {
+- .bLength = USB_DT_ENDPOINT_SIZE,
+- .bDescriptorType = USB_DT_ENDPOINT,
+-
+- .bEndpointAddress = USB_DIR_IN,
+- .bmAttributes = USB_ENDPOINT_XFER_INT,
+- .wMaxPacketSize = __constant_cpu_to_le16 (STATUS_BYTECOUNT),
+- .bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC,
+-};
+-#endif
+-
+-#ifdef DEV_CONFIG_CDC
+-
+-/* the default data interface has no endpoints ... */
+-
+-static const struct usb_interface_descriptor
+-data_nop_intf = {
+- .bLength = sizeof data_nop_intf,
+- .bDescriptorType = USB_DT_INTERFACE,
+-
+- .bInterfaceNumber = 1,
+- .bAlternateSetting = 0,
+- .bNumEndpoints = 0,
+- .bInterfaceClass = USB_CLASS_CDC_DATA,
+- .bInterfaceSubClass = 0,
+- .bInterfaceProtocol = 0,
+-};
+-
+-/* ... but the "real" data interface has two bulk endpoints */
+-
+-static const struct usb_interface_descriptor
+-data_intf = {
+- .bLength = sizeof data_intf,
+- .bDescriptorType = USB_DT_INTERFACE,
+-
+- .bInterfaceNumber = 1,
+- .bAlternateSetting = 1,
+- .bNumEndpoints = 2,
+- .bInterfaceClass = USB_CLASS_CDC_DATA,
+- .bInterfaceSubClass = 0,
+- .bInterfaceProtocol = 0,
+- .iInterface = STRING_DATA,
+-};
+-
+-#endif
+-
+-#ifdef CONFIG_USB_ETH_RNDIS
+-
+-/* RNDIS doesn't activate by changing to the "real" altsetting */
+-
+-static const struct usb_interface_descriptor
+-rndis_data_intf = {
+- .bLength = sizeof rndis_data_intf,
+- .bDescriptorType = USB_DT_INTERFACE,
+-
+- .bInterfaceNumber = 1,
+- .bAlternateSetting = 0,
+- .bNumEndpoints = 2,
+- .bInterfaceClass = USB_CLASS_CDC_DATA,
+- .bInterfaceSubClass = 0,
+- .bInterfaceProtocol = 0,
+- .iInterface = STRING_DATA,
+-};
+-
+-#endif
+-
+-#ifdef DEV_CONFIG_SUBSET
+-
+-/*
+- * "Simple" CDC-subset option is a simple vendor-neutral model that most
+- * full speed controllers can handle: one interface, two bulk endpoints.
+- *
+- * To assist host side drivers, we fancy it up a bit, and add descriptors
+- * so some host side drivers will understand it as a "SAFE" variant.
+- */
+-
+-static const struct usb_interface_descriptor
+-subset_data_intf = {
+- .bLength = sizeof subset_data_intf,
+- .bDescriptorType = USB_DT_INTERFACE,
+-
+- .bInterfaceNumber = 0,
+- .bAlternateSetting = 0,
+- .bNumEndpoints = 2,
+- .bInterfaceClass = USB_CLASS_COMM,
+- .bInterfaceSubClass = USB_CDC_SUBCLASS_MDLM,
+- .bInterfaceProtocol = 0,
+- .iInterface = STRING_DATA,
+-};
+-
+-#endif /* SUBSET */
+-
+-
+-static struct usb_endpoint_descriptor
+-fs_source_desc = {
+- .bLength = USB_DT_ENDPOINT_SIZE,
+- .bDescriptorType = USB_DT_ENDPOINT,
+-
+- .bEndpointAddress = USB_DIR_IN,
+- .bmAttributes = USB_ENDPOINT_XFER_BULK,
+-};
+-
+-static struct usb_endpoint_descriptor
+-fs_sink_desc = {
+- .bLength = USB_DT_ENDPOINT_SIZE,
+- .bDescriptorType = USB_DT_ENDPOINT,
+-
+- .bEndpointAddress = USB_DIR_OUT,
+- .bmAttributes = USB_ENDPOINT_XFER_BULK,
++ /* REVISIT SRP-only hardware is possible, although
++ * it would not be called "OTG" ...
++ */
++ .bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
+ };
+
+-static const struct usb_descriptor_header *fs_eth_function [11] = {
++static const struct usb_descriptor_header *otg_desc[] = {
+ (struct usb_descriptor_header *) &otg_descriptor,
+-#ifdef DEV_CONFIG_CDC
+- /* "cdc" mode descriptors */
+- (struct usb_descriptor_header *) &control_intf,
+- (struct usb_descriptor_header *) &header_desc,
+- (struct usb_descriptor_header *) &union_desc,
+- (struct usb_descriptor_header *) &ether_desc,
+- /* NOTE: status endpoint may need to be removed */
+- (struct usb_descriptor_header *) &fs_status_desc,
+- /* data interface, with altsetting */
+- (struct usb_descriptor_header *) &data_nop_intf,
+- (struct usb_descriptor_header *) &data_intf,
+- (struct usb_descriptor_header *) &fs_source_desc,
+- (struct usb_descriptor_header *) &fs_sink_desc,
+ NULL,
+-#endif /* DEV_CONFIG_CDC */
+ };
+
+-static inline void __init fs_subset_descriptors(void)
+-{
+-#ifdef DEV_CONFIG_SUBSET
+- /* behavior is "CDC Subset"; extra descriptors say "SAFE" */
+- fs_eth_function[1] = (struct usb_descriptor_header *) &subset_data_intf;
+- fs_eth_function[2] = (struct usb_descriptor_header *) &header_desc;
+- fs_eth_function[3] = (struct usb_descriptor_header *) &mdlm_desc;
+- fs_eth_function[4] = (struct usb_descriptor_header *) &mdlm_detail_desc;
+- fs_eth_function[5] = (struct usb_descriptor_header *) &ether_desc;
+- fs_eth_function[6] = (struct usb_descriptor_header *) &fs_source_desc;
+- fs_eth_function[7] = (struct usb_descriptor_header *) &fs_sink_desc;
+- fs_eth_function[8] = NULL;
+-#else
+- fs_eth_function[1] = NULL;
+-#endif
+-}
+
+-#ifdef CONFIG_USB_ETH_RNDIS
+-static const struct usb_descriptor_header *fs_rndis_function [] = {
+- (struct usb_descriptor_header *) &otg_descriptor,
+- /* control interface matches ACM, not Ethernet */
+- (struct usb_descriptor_header *) &rndis_control_intf,
+- (struct usb_descriptor_header *) &header_desc,
+- (struct usb_descriptor_header *) &call_mgmt_descriptor,
+- (struct usb_descriptor_header *) &acm_descriptor,
+- (struct usb_descriptor_header *) &union_desc,
+- (struct usb_descriptor_header *) &fs_status_desc,
+- /* data interface has no altsetting */
+- (struct usb_descriptor_header *) &rndis_data_intf,
+- (struct usb_descriptor_header *) &fs_source_desc,
+- (struct usb_descriptor_header *) &fs_sink_desc,
+- NULL,
+-};
+-#endif
+-
+-/*
+- * usb 2.0 devices need to expose both high speed and full speed
+- * descriptors, unless they only run at full speed.
+- */
+-
+-#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS)
+-static struct usb_endpoint_descriptor
+-hs_status_desc = {
+- .bLength = USB_DT_ENDPOINT_SIZE,
+- .bDescriptorType = USB_DT_ENDPOINT,
+-
+- .bmAttributes = USB_ENDPOINT_XFER_INT,
+- .wMaxPacketSize = __constant_cpu_to_le16 (STATUS_BYTECOUNT),
+- .bInterval = LOG2_STATUS_INTERVAL_MSEC + 4,
+-};
+-#endif /* DEV_CONFIG_CDC */
+-
+-static struct usb_endpoint_descriptor
+-hs_source_desc = {
+- .bLength = USB_DT_ENDPOINT_SIZE,
+- .bDescriptorType = USB_DT_ENDPOINT,
+-
+- .bmAttributes = USB_ENDPOINT_XFER_BULK,
+- .wMaxPacketSize = __constant_cpu_to_le16 (512),
+-};
+-
+-static struct usb_endpoint_descriptor
+-hs_sink_desc = {
+- .bLength = USB_DT_ENDPOINT_SIZE,
+- .bDescriptorType = USB_DT_ENDPOINT,
+-
+- .bmAttributes = USB_ENDPOINT_XFER_BULK,
+- .wMaxPacketSize = __constant_cpu_to_le16 (512),
+-};
++/* string IDs are assigned dynamically */
+
+-static struct usb_qualifier_descriptor
+-dev_qualifier = {
+- .bLength = sizeof dev_qualifier,
+- .bDescriptorType = USB_DT_DEVICE_QUALIFIER,
++#define STRING_MANUFACTURER_IDX 0
++#define STRING_PRODUCT_IDX 1
+
+- .bcdUSB = __constant_cpu_to_le16 (0x0200),
+- .bDeviceClass = USB_CLASS_COMM,
++static char manufacturer[50];
+
+- .bNumConfigurations = 1,
++static struct usb_string strings_dev[] = {
++ [STRING_MANUFACTURER_IDX].s = manufacturer,
++ [STRING_PRODUCT_IDX].s = PREFIX DRIVER_DESC,
++ { } /* end of list */
+ };
+
+-static const struct usb_descriptor_header *hs_eth_function [11] = {
+- (struct usb_descriptor_header *) &otg_descriptor,
+-#ifdef DEV_CONFIG_CDC
+- /* "cdc" mode descriptors */
+- (struct usb_descriptor_header *) &control_intf,
+- (struct usb_descriptor_header *) &header_desc,
+- (struct usb_descriptor_header *) &union_desc,
+- (struct usb_descriptor_header *) &ether_desc,
+- /* NOTE: status endpoint may need to be removed */
+- (struct usb_descriptor_header *) &hs_status_desc,
+- /* data interface, with altsetting */
+- (struct usb_descriptor_header *) &data_nop_intf,
+- (struct usb_descriptor_header *) &data_intf,
+- (struct usb_descriptor_header *) &hs_source_desc,
+- (struct usb_descriptor_header *) &hs_sink_desc,
+- NULL,
+-#endif /* DEV_CONFIG_CDC */
++static struct usb_gadget_strings stringtab_dev = {
++ .language = 0x0409, /* en-us */
++ .strings = strings_dev,
+ };
+
+-static inline void __init hs_subset_descriptors(void)
+-{
+-#ifdef DEV_CONFIG_SUBSET
+- /* behavior is "CDC Subset"; extra descriptors say "SAFE" */
+- hs_eth_function[1] = (struct usb_descriptor_header *) &subset_data_intf;
+- hs_eth_function[2] = (struct usb_descriptor_header *) &header_desc;
+- hs_eth_function[3] = (struct usb_descriptor_header *) &mdlm_desc;
+- hs_eth_function[4] = (struct usb_descriptor_header *) &mdlm_detail_desc;
+- hs_eth_function[5] = (struct usb_descriptor_header *) &ether_desc;
+- hs_eth_function[6] = (struct usb_descriptor_header *) &hs_source_desc;
+- hs_eth_function[7] = (struct usb_descriptor_header *) &hs_sink_desc;
+- hs_eth_function[8] = NULL;
+-#else
+- hs_eth_function[1] = NULL;
+-#endif
+-}
+-
+-#ifdef CONFIG_USB_ETH_RNDIS
+-static const struct usb_descriptor_header *hs_rndis_function [] = {
+- (struct usb_descriptor_header *) &otg_descriptor,
+- /* control interface matches ACM, not Ethernet */
+- (struct usb_descriptor_header *) &rndis_control_intf,
+- (struct usb_descriptor_header *) &header_desc,
+- (struct usb_descriptor_header *) &call_mgmt_descriptor,
+- (struct usb_descriptor_header *) &acm_descriptor,
+- (struct usb_descriptor_header *) &union_desc,
+- (struct usb_descriptor_header *) &hs_status_desc,
+- /* data interface has no altsetting */
+- (struct usb_descriptor_header *) &rndis_data_intf,
+- (struct usb_descriptor_header *) &hs_source_desc,
+- (struct usb_descriptor_header *) &hs_sink_desc,
++static struct usb_gadget_strings *dev_strings[] = {
++ &stringtab_dev,
+ NULL,
+ };
+-#endif
+-
+-
+-/* maxpacket and other transfer characteristics vary by speed. */
+-static inline struct usb_endpoint_descriptor *
+-ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *hs,
+- struct usb_endpoint_descriptor *fs)
+-{
+- if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
+- return hs;
+- return fs;
+-}
+
++static u8 hostaddr[ETH_ALEN];
+
+ /*-------------------------------------------------------------------------*/
+
+-/* descriptors that are built on-demand */
+-
+-static char manufacturer [50];
+-static char product_desc [40] = DRIVER_DESC;
+-static char serial_number [20];
+-
+-/* address that the host will use ... usually assigned at random */
+-static char ethaddr [2 * ETH_ALEN + 1];
+-
+-/* static strings, in UTF-8 */
+-static struct usb_string strings [] = {
+- { STRING_MANUFACTURER, manufacturer, },
+- { STRING_PRODUCT, product_desc, },
+- { STRING_SERIALNUMBER, serial_number, },
+- { STRING_DATA, "Ethernet Data", },
+- { STRING_ETHADDR, ethaddr, },
+-#ifdef DEV_CONFIG_CDC
+- { STRING_CDC, "CDC Ethernet", },
+- { STRING_CONTROL, "CDC Communications Control", },
+-#endif
+-#ifdef DEV_CONFIG_SUBSET
+- { STRING_SUBSET, "CDC Ethernet Subset", },
+-#endif
+-#ifdef CONFIG_USB_ETH_RNDIS
+- { STRING_RNDIS, "RNDIS", },
+- { STRING_RNDIS_CONTROL, "RNDIS Communications Control", },
+-#endif
+- { } /* end of list */
+-};
+-
+-static struct usb_gadget_strings stringtab = {
+- .language = 0x0409, /* en-us */
+- .strings = strings,
+-};
+-
+ /*
+- * one config, two interfaces: control, data.
+- * complications: class descriptors, and an altsetting.
+- */
+-static int
+-config_buf(struct usb_gadget *g, u8 *buf, u8 type, unsigned index, int is_otg)
+-{
+- int len;
+- const struct usb_config_descriptor *config;
+- const struct usb_descriptor_header **function;
+- int hs = 0;
+-
+- if (gadget_is_dualspeed(g)) {
+- hs = (g->speed == USB_SPEED_HIGH);
+- if (type == USB_DT_OTHER_SPEED_CONFIG)
+- hs = !hs;
+- }
+-#define which_fn(t) (hs ? hs_ ## t ## _function : fs_ ## t ## _function)
+-
+- if (index >= device_desc.bNumConfigurations)
+- return -EINVAL;
+-
+-#ifdef CONFIG_USB_ETH_RNDIS
+- /* list the RNDIS config first, to make Microsoft's drivers
+- * happy. DOCSIS 1.0 needs this too.
+- */
+- if (device_desc.bNumConfigurations == 2 && index == 0) {
+- config = &rndis_config;
+- function = which_fn (rndis);
+- } else
+-#endif
+- {
+- config = &eth_config;
+- function = which_fn (eth);
+- }
+-
+- /* for now, don't advertise srp-only devices */
+- if (!is_otg)
+- function++;
+-
+- len = usb_gadget_config_buf (config, buf, USB_BUFSIZ, function);
+- if (len < 0)
+- return len;
+- ((struct usb_config_descriptor *) buf)->bDescriptorType = type;
+- return len;
+-}
+-
+-/*-------------------------------------------------------------------------*/
+-
+-static void eth_start (struct eth_dev *dev, gfp_t gfp_flags);
+-static int alloc_requests (struct eth_dev *dev, unsigned n, gfp_t gfp_flags);
+-
+-static int
+-set_ether_config (struct eth_dev *dev, gfp_t gfp_flags)
+-{
+- int result = 0;
+- struct usb_gadget *gadget = dev->gadget;
+-
+-#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS)
+- /* status endpoint used for RNDIS and (optionally) CDC */
+- if (!subset_active(dev) && dev->status_ep) {
+- dev->status = ep_desc (gadget, &hs_status_desc,
+- &fs_status_desc);
+- dev->status_ep->driver_data = dev;
+-
+- result = usb_ep_enable (dev->status_ep, dev->status);
+- if (result != 0) {
+- DEBUG (dev, "enable %s --> %d\n",
+- dev->status_ep->name, result);
+- goto done;
+- }
+- }
+-#endif
+-
+- dev->in = ep_desc(gadget, &hs_source_desc, &fs_source_desc);
+- dev->in_ep->driver_data = dev;
+-
+- dev->out = ep_desc(gadget, &hs_sink_desc, &fs_sink_desc);
+- dev->out_ep->driver_data = dev;
+-
+- /* With CDC, the host isn't allowed to use these two data
+- * endpoints in the default altsetting for the interface.
+- * so we don't activate them yet. Reset from SET_INTERFACE.
+- *
+- * Strictly speaking RNDIS should work the same: activation is
+- * a side effect of setting a packet filter. Deactivation is
+- * from REMOTE_NDIS_HALT_MSG, reset from REMOTE_NDIS_RESET_MSG.
+- */
+- if (!cdc_active(dev)) {
+- result = usb_ep_enable (dev->in_ep, dev->in);
+- if (result != 0) {
+- DEBUG(dev, "enable %s --> %d\n",
+- dev->in_ep->name, result);
+- goto done;
+- }
+-
+- result = usb_ep_enable (dev->out_ep, dev->out);
+- if (result != 0) {
+- DEBUG (dev, "enable %s --> %d\n",
+- dev->out_ep->name, result);
+- goto done;
+- }
+- }
+-
+-done:
+- if (result == 0)
+- result = alloc_requests (dev, qlen (gadget), gfp_flags);
+-
+- /* on error, disable any endpoints */
+- if (result < 0) {
+- if (!subset_active(dev) && dev->status_ep)
+- (void) usb_ep_disable (dev->status_ep);
+- dev->status = NULL;
+- (void) usb_ep_disable (dev->in_ep);
+- (void) usb_ep_disable (dev->out_ep);
+- dev->in = NULL;
+- dev->out = NULL;
+- }
+-
+- /* activate non-CDC configs right away
+- * this isn't strictly according to the RNDIS spec
+- */
+- else if (!cdc_active (dev)) {
+- netif_carrier_on (dev->net);
+- if (netif_running (dev->net)) {
+- spin_unlock (&dev->lock);
+- eth_start (dev, GFP_ATOMIC);
+- spin_lock (&dev->lock);
+- }
+- }
+-
+- if (result == 0)
+- DEBUG (dev, "qlen %d\n", qlen (gadget));
+-
+- /* caller is responsible for cleanup on error */
+- return result;
+-}
+-
+-static void eth_reset_config (struct eth_dev *dev)
+-{
+- struct usb_request *req;
+-
+- if (dev->config == 0)
+- return;
+-
+- DEBUG (dev, "%s\n", __func__);
+-
+- netif_stop_queue (dev->net);
+- netif_carrier_off (dev->net);
+- rndis_uninit(dev->rndis_config);
+-
+- /* disable endpoints, forcing (synchronous) completion of
+- * pending i/o. then free the requests.
+- */
+- if (dev->in) {
+- usb_ep_disable (dev->in_ep);
+- spin_lock(&dev->req_lock);
+- while (likely (!list_empty (&dev->tx_reqs))) {
+- req = container_of (dev->tx_reqs.next,
+- struct usb_request, list);
+- list_del (&req->list);
+-
+- spin_unlock(&dev->req_lock);
+- usb_ep_free_request (dev->in_ep, req);
+- spin_lock(&dev->req_lock);
+- }
+- spin_unlock(&dev->req_lock);
+- }
+- if (dev->out) {
+- usb_ep_disable (dev->out_ep);
+- spin_lock(&dev->req_lock);
+- while (likely (!list_empty (&dev->rx_reqs))) {
+- req = container_of (dev->rx_reqs.next,
+- struct usb_request, list);
+- list_del (&req->list);
+-
+- spin_unlock(&dev->req_lock);
+- usb_ep_free_request (dev->out_ep, req);
+- spin_lock(&dev->req_lock);
+- }
+- spin_unlock(&dev->req_lock);
+- }
+-
+- if (dev->status) {
+- usb_ep_disable (dev->status_ep);
+- }
+- dev->rndis = 0;
+- dev->cdc_filter = 0;
+- dev->config = 0;
+-}
+-
+-/* change our operational config. must agree with the code
+- * that returns config descriptors, and altsetting code.
++ * We may not have an RNDIS configuration, but if we do it needs to be
++ * the first one present. That's to make Microsoft's drivers happy,
++ * and to follow DOCSIS 1.0 (cable modem standard).
+ */
+-static int
+-eth_set_config (struct eth_dev *dev, unsigned number, gfp_t gfp_flags)
++static int __init rndis_do_config(struct usb_configuration *c)
+ {
+- int result = 0;
+- struct usb_gadget *gadget = dev->gadget;
+-
+- if (gadget_is_sa1100 (gadget)
+- && dev->config
+- && atomic_read (&dev->tx_qlen) != 0) {
+- /* tx fifo is full, but we can't clear it...*/
+- INFO (dev, "can't change configurations\n");
+- return -ESPIPE;
+- }
+- eth_reset_config (dev);
+-
+- switch (number) {
+- case DEV_CONFIG_VALUE:
+- result = set_ether_config (dev, gfp_flags);
+- break;
+-#ifdef CONFIG_USB_ETH_RNDIS
+- case DEV_RNDIS_CONFIG_VALUE:
+- dev->rndis = 1;
+- result = set_ether_config (dev, gfp_flags);
+- break;
+-#endif
+- default:
+- result = -EINVAL;
+- /* FALL THROUGH */
+- case 0:
+- break;
+- }
+-
+- if (result) {
+- if (number)
+- eth_reset_config (dev);
+- usb_gadget_vbus_draw(dev->gadget,
+- gadget_is_otg(dev->gadget) ? 8 : 100);
+- } else {
+- char *speed;
+- unsigned power;
+-
+- power = 2 * eth_config.bMaxPower;
+- usb_gadget_vbus_draw(dev->gadget, power);
++ /* FIXME alloc iConfiguration string, set it in c->strings */
+
+- switch (gadget->speed) {
+- case USB_SPEED_FULL: speed = "full"; break;
+-#ifdef CONFIG_USB_GADGET_DUALSPEED
+- case USB_SPEED_HIGH: speed = "high"; break;
+-#endif
+- default: speed = "?"; break;
+- }
+-
+- dev->config = number;
+- INFO (dev, "%s speed config #%d: %d mA, %s, using %s\n",
+- speed, number, power, driver_desc,
+- rndis_active(dev)
+- ? "RNDIS"
+- : (cdc_active(dev)
+- ? "CDC Ethernet"
+- : "CDC Ethernet Subset"));
++ if (gadget_is_otg(c->cdev->gadget)) {
++ c->descriptors = otg_desc;
++ c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ }
+- return result;
+-}
+-
+-/*-------------------------------------------------------------------------*/
+-
+-#ifdef DEV_CONFIG_CDC
+
+-/* The interrupt endpoint is used in CDC networking models (Ethernet, ATM)
+- * only to notify the host about link status changes (which we support) or
+- * report completion of some encapsulated command (as used in RNDIS). Since
+- * we want this CDC Ethernet code to be vendor-neutral, we don't use that
+- * command mechanism; and only one status request is ever queued.
+- */
+-
+-static void eth_status_complete (struct usb_ep *ep, struct usb_request *req)
+-{
+- struct usb_cdc_notification *event = req->buf;
+- int value = req->status;
+- struct eth_dev *dev = ep->driver_data;
+-
+- /* issue the second notification if host reads the first */
+- if (event->bNotificationType == USB_CDC_NOTIFY_NETWORK_CONNECTION
+- && value == 0) {
+- __le32 *data = req->buf + sizeof *event;
+-
+- event->bmRequestType = 0xA1;
+- event->bNotificationType = USB_CDC_NOTIFY_SPEED_CHANGE;
+- event->wValue = __constant_cpu_to_le16 (0);
+- event->wIndex = __constant_cpu_to_le16 (1);
+- event->wLength = __constant_cpu_to_le16 (8);
+-
+- /* SPEED_CHANGE data is up/down speeds in bits/sec */
+- data [0] = data [1] = cpu_to_le32 (BITRATE (dev->gadget));
+-
+- req->length = STATUS_BYTECOUNT;
+- value = usb_ep_queue (ep, req, GFP_ATOMIC);
+- DEBUG (dev, "send SPEED_CHANGE --> %d\n", value);
+- if (value == 0)
+- return;
+- } else if (value != -ECONNRESET)
+- DEBUG (dev, "event %02x --> %d\n",
+- event->bNotificationType, value);
+- req->context = NULL;
++ return rndis_bind_config(c, hostaddr);
+ }
+
+-static void issue_start_status (struct eth_dev *dev)
+-{
+- struct usb_request *req = dev->stat_req;
+- struct usb_cdc_notification *event;
+- int value;
+-
+- DEBUG (dev, "%s, flush old status first\n", __func__);
+-
+- /* flush old status
+- *
+- * FIXME ugly idiom, maybe we'd be better with just
+- * a "cancel the whole queue" primitive since any
+- * unlink-one primitive has way too many error modes.
+- * here, we "know" toggle is already clear...
+- *
+- * FIXME iff req->context != null just dequeue it
+- */
+- usb_ep_disable (dev->status_ep);
+- usb_ep_enable (dev->status_ep, dev->status);
+-
+- /* 3.8.1 says to issue first NETWORK_CONNECTION, then
+- * a SPEED_CHANGE. could be useful in some configs.
+- */
+- event = req->buf;
+- event->bmRequestType = 0xA1;
+- event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION;
+- event->wValue = __constant_cpu_to_le16 (1); /* connected */
+- event->wIndex = __constant_cpu_to_le16 (1);
+- event->wLength = 0;
+-
+- req->length = sizeof *event;
+- req->complete = eth_status_complete;
+- req->context = dev;
+-
+- value = usb_ep_queue (dev->status_ep, req, GFP_ATOMIC);
+- if (value < 0)
+- DEBUG (dev, "status buf queue --> %d\n", value);
+-}
+-
+-#endif
++static struct usb_configuration rndis_config_driver = {
++ .label = "RNDIS",
++ .bind = rndis_do_config,
++ .bConfigurationValue = 2,
++ /* .iConfiguration = DYNAMIC */
++ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
++ .bMaxPower = 1, /* 2 mA, minimal */
++};
+
+ /*-------------------------------------------------------------------------*/
+
+-static void eth_setup_complete (struct usb_ep *ep, struct usb_request *req)
+-{
+- if (req->status || req->actual != req->length)
+- DEBUG ((struct eth_dev *) ep->driver_data,
+- "setup complete --> %d, %d/%d\n",
+- req->status, req->actual, req->length);
+-}
+-
+-#ifdef CONFIG_USB_ETH_RNDIS
+-
+-static void rndis_response_complete (struct usb_ep *ep, struct usb_request *req)
+-{
+- if (req->status || req->actual != req->length)
+- DEBUG ((struct eth_dev *) ep->driver_data,
+- "rndis response complete --> %d, %d/%d\n",
+- req->status, req->actual, req->length);
+-
+- /* done sending after USB_CDC_GET_ENCAPSULATED_RESPONSE */
+-}
+-
+-static void rndis_command_complete (struct usb_ep *ep, struct usb_request *req)
+-{
+- struct eth_dev *dev = ep->driver_data;
+- int status;
+-
+- /* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */
+- spin_lock(&dev->lock);
+- status = rndis_msg_parser (dev->rndis_config, (u8 *) req->buf);
+- if (status < 0)
+- ERROR(dev, "%s: rndis parse error %d\n", __func__, status);
+- spin_unlock(&dev->lock);
+-}
+-
+-#endif /* RNDIS */
+-
+ /*
+- * The setup() callback implements all the ep0 functionality that's not
+- * handled lower down. CDC has a number of less-common features:
+- *
+- * - two interfaces: control, and ethernet data
+- * - Ethernet data interface has two altsettings: default, and active
+- * - class-specific descriptors for the control interface
+- * - class-specific control requests
++ * We _always_ have an ECM or CDC Subset configuration.
+ */
+-static int
+-eth_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
++static int __init eth_do_config(struct usb_configuration *c)
+ {
+- struct eth_dev *dev = get_gadget_data (gadget);
+- struct usb_request *req = dev->req;
+- int value = -EOPNOTSUPP;
+- u16 wIndex = le16_to_cpu(ctrl->wIndex);
+- u16 wValue = le16_to_cpu(ctrl->wValue);
+- u16 wLength = le16_to_cpu(ctrl->wLength);
+-
+- /* descriptors just go into the pre-allocated ep0 buffer,
+- * while config change events may enable network traffic.
+- */
+- req->complete = eth_setup_complete;
+- switch (ctrl->bRequest) {
+-
+- case USB_REQ_GET_DESCRIPTOR:
+- if (ctrl->bRequestType != USB_DIR_IN)
+- break;
+- switch (wValue >> 8) {
+-
+- case USB_DT_DEVICE:
+- value = min (wLength, (u16) sizeof device_desc);
+- memcpy (req->buf, &device_desc, value);
+- break;
+- case USB_DT_DEVICE_QUALIFIER:
+- if (!gadget_is_dualspeed(gadget))
+- break;
+- value = min (wLength, (u16) sizeof dev_qualifier);
+- memcpy (req->buf, &dev_qualifier, value);
+- break;
+-
+- case USB_DT_OTHER_SPEED_CONFIG:
+- if (!gadget_is_dualspeed(gadget))
+- break;
+- // FALLTHROUGH
+- case USB_DT_CONFIG:
+- value = config_buf(gadget, req->buf,
+- wValue >> 8,
+- wValue & 0xff,
+- gadget_is_otg(gadget));
+- if (value >= 0)
+- value = min (wLength, (u16) value);
+- break;
+-
+- case USB_DT_STRING:
+- value = usb_gadget_get_string (&stringtab,
+- wValue & 0xff, req->buf);
+- if (value >= 0)
+- value = min (wLength, (u16) value);
+- break;
+- }
+- break;
+-
+- case USB_REQ_SET_CONFIGURATION:
+- if (ctrl->bRequestType != 0)
+- break;
+- if (gadget->a_hnp_support)
+- DEBUG (dev, "HNP available\n");
+- else if (gadget->a_alt_hnp_support)
+- DEBUG (dev, "HNP needs a different root port\n");
+- spin_lock (&dev->lock);
+- value = eth_set_config (dev, wValue, GFP_ATOMIC);
+- spin_unlock (&dev->lock);
+- break;
+- case USB_REQ_GET_CONFIGURATION:
+- if (ctrl->bRequestType != USB_DIR_IN)
+- break;
+- *(u8 *)req->buf = dev->config;
+- value = min (wLength, (u16) 1);
+- break;
+-
+- case USB_REQ_SET_INTERFACE:
+- if (ctrl->bRequestType != USB_RECIP_INTERFACE
+- || !dev->config
+- || wIndex > 1)
+- break;
+- if (!cdc_active(dev) && wIndex != 0)
+- break;
+- spin_lock (&dev->lock);
+-
+- /* PXA hardware partially handles SET_INTERFACE;
+- * we need to kluge around that interference.
+- */
+- if (gadget_is_pxa (gadget)) {
+- value = eth_set_config (dev, DEV_CONFIG_VALUE,
+- GFP_ATOMIC);
+- goto done_set_intf;
+- }
+-
+-#ifdef DEV_CONFIG_CDC
+- switch (wIndex) {
+- case 0: /* control/master intf */
+- if (wValue != 0)
+- break;
+- if (dev->status) {
+- usb_ep_disable (dev->status_ep);
+- usb_ep_enable (dev->status_ep, dev->status);
+- }
+- value = 0;
+- break;
+- case 1: /* data intf */
+- if (wValue > 1)
+- break;
+- usb_ep_disable (dev->in_ep);
+- usb_ep_disable (dev->out_ep);
+-
+- /* CDC requires the data transfers not be done from
+- * the default interface setting ... also, setting
+- * the non-default interface resets filters etc.
+- */
+- if (wValue == 1) {
+- if (!cdc_active (dev))
+- break;
+- usb_ep_enable (dev->in_ep, dev->in);
+- usb_ep_enable (dev->out_ep, dev->out);
+- dev->cdc_filter = DEFAULT_FILTER;
+- netif_carrier_on (dev->net);
+- if (dev->status)
+- issue_start_status (dev);
+- if (netif_running (dev->net)) {
+- spin_unlock (&dev->lock);
+- eth_start (dev, GFP_ATOMIC);
+- spin_lock (&dev->lock);
+- }
+- } else {
+- netif_stop_queue (dev->net);
+- netif_carrier_off (dev->net);
+- }
+- value = 0;
+- break;
+- }
+-#else
+- /* FIXME this is wrong, as is the assumption that
+- * all non-PXA hardware talks real CDC ...
+- */
+- dev_warn (&gadget->dev, "set_interface ignored!\n");
+-#endif /* DEV_CONFIG_CDC */
+-
+-done_set_intf:
+- spin_unlock (&dev->lock);
+- break;
+- case USB_REQ_GET_INTERFACE:
+- if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)
+- || !dev->config
+- || wIndex > 1)
+- break;
+- if (!(cdc_active(dev) || rndis_active(dev)) && wIndex != 0)
+- break;
+-
+- /* for CDC, iff carrier is on, data interface is active. */
+- if (rndis_active(dev) || wIndex != 1)
+- *(u8 *)req->buf = 0;
+- else
+- *(u8 *)req->buf = netif_carrier_ok (dev->net) ? 1 : 0;
+- value = min (wLength, (u16) 1);
+- break;
+-
+-#ifdef DEV_CONFIG_CDC
+- case USB_CDC_SET_ETHERNET_PACKET_FILTER:
+- /* see 6.2.30: no data, wIndex = interface,
+- * wValue = packet filter bitmap
+- */
+- if (ctrl->bRequestType != (USB_TYPE_CLASS|USB_RECIP_INTERFACE)
+- || !cdc_active(dev)
+- || wLength != 0
+- || wIndex > 1)
+- break;
+- DEBUG (dev, "packet filter %02x\n", wValue);
+- dev->cdc_filter = wValue;
+- value = 0;
+- break;
+-
+- /* and potentially:
+- * case USB_CDC_SET_ETHERNET_MULTICAST_FILTERS:
+- * case USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER:
+- * case USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER:
+- * case USB_CDC_GET_ETHERNET_STATISTIC:
+- */
+-
+-#endif /* DEV_CONFIG_CDC */
++ /* FIXME alloc iConfiguration string, set it in c->strings */
+
+-#ifdef CONFIG_USB_ETH_RNDIS
+- /* RNDIS uses the CDC command encapsulation mechanism to implement
+- * an RPC scheme, with much getting/setting of attributes by OID.
+- */
+- case USB_CDC_SEND_ENCAPSULATED_COMMAND:
+- if (ctrl->bRequestType != (USB_TYPE_CLASS|USB_RECIP_INTERFACE)
+- || !rndis_active(dev)
+- || wLength > USB_BUFSIZ
+- || wValue
+- || rndis_control_intf.bInterfaceNumber
+- != wIndex)
+- break;
+- /* read the request, then process it */
+- value = wLength;
+- req->complete = rndis_command_complete;
+- /* later, rndis_control_ack () sends a notification */
+- break;
+-
+- case USB_CDC_GET_ENCAPSULATED_RESPONSE:
+- if ((USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE)
+- == ctrl->bRequestType
+- && rndis_active(dev)
+- // && wLength >= 0x0400
+- && !wValue
+- && rndis_control_intf.bInterfaceNumber
+- == wIndex) {
+- u8 *buf;
+- u32 n;
+-
+- /* return the result */
+- buf = rndis_get_next_response(dev->rndis_config, &n);
+- if (buf) {
+- memcpy(req->buf, buf, n);
+- req->complete = rndis_response_complete;
+- rndis_free_response(dev->rndis_config, buf);
+- value = n;
+- }
+- /* else stalls ... spec says to avoid that */
+- }
+- break;
+-#endif /* RNDIS */
+-
+- default:
+- VDEBUG (dev,
+- "unknown control req%02x.%02x v%04x i%04x l%d\n",
+- ctrl->bRequestType, ctrl->bRequest,
+- wValue, wIndex, wLength);
++ if (gadget_is_otg(c->cdev->gadget)) {
++ c->descriptors = otg_desc;
++ c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ }
+
+- /* respond with data transfer before status phase? */
+- if (value >= 0) {
+- req->length = value;
+- req->zero = value < wLength
+- && (value % gadget->ep0->maxpacket) == 0;
+- value = usb_ep_queue (gadget->ep0, req, GFP_ATOMIC);
+- if (value < 0) {
+- DEBUG (dev, "ep_queue --> %d\n", value);
+- req->status = 0;
+- eth_setup_complete (gadget->ep0, req);
+- }
+- }
+-
+- /* host either stalls (value < 0) or reports success */
+- return value;
+-}
+-
+-static void
+-eth_disconnect (struct usb_gadget *gadget)
+-{
+- struct eth_dev *dev = get_gadget_data (gadget);
+- unsigned long flags;
+-
+- spin_lock_irqsave (&dev->lock, flags);
+- netif_stop_queue (dev->net);
+- netif_carrier_off (dev->net);
+- eth_reset_config (dev);
+- spin_unlock_irqrestore (&dev->lock, flags);
+-
+- /* FIXME RNDIS should enter RNDIS_UNINITIALIZED */
+-
+- /* next we may get setup() calls to enumerate new connections;
+- * or an unbind() during shutdown (including removing module).
+- */
+-}
+-
+-/*-------------------------------------------------------------------------*/
+-
+-/* NETWORK DRIVER HOOKUP (to the layer above this driver) */
+-
+-static int eth_change_mtu (struct net_device *net, int new_mtu)
+-{
+- struct eth_dev *dev = netdev_priv(net);
+-
+- if (dev->rndis)
+- return -EBUSY;
+-
+- if (new_mtu <= ETH_HLEN || new_mtu > ETH_FRAME_LEN)
+- return -ERANGE;
+- /* no zero-length packet read wanted after mtu-sized packets */
+- if (((new_mtu + sizeof (struct ethhdr)) % dev->in_ep->maxpacket) == 0)
+- return -EDOM;
+- net->mtu = new_mtu;
+- return 0;
+-}
+-
+-static struct net_device_stats *eth_get_stats (struct net_device *net)
+-{
+- return &((struct eth_dev *)netdev_priv(net))->stats;
+-}
+-
+-static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p)
+-{
+- struct eth_dev *dev = netdev_priv(net);
+- strlcpy(p->driver, shortname, sizeof p->driver);
+- strlcpy(p->version, DRIVER_VERSION, sizeof p->version);
+- strlcpy(p->fw_version, dev->gadget->name, sizeof p->fw_version);
+- strlcpy (p->bus_info, dev->gadget->dev.bus_id, sizeof p->bus_info);
+-}
+-
+-static u32 eth_get_link(struct net_device *net)
+-{
+- struct eth_dev *dev = netdev_priv(net);
+- return dev->gadget->speed != USB_SPEED_UNKNOWN;
+-}
+-
+-static struct ethtool_ops ops = {
+- .get_drvinfo = eth_get_drvinfo,
+- .get_link = eth_get_link
+-};
+-
+-static void defer_kevent (struct eth_dev *dev, int flag)
+-{
+- if (test_and_set_bit (flag, &dev->todo))
+- return;
+- if (!schedule_work (&dev->work))
+- ERROR (dev, "kevent %d may have been dropped\n", flag);
++ if (can_support_ecm(c->cdev->gadget))
++ return ecm_bind_config(c, hostaddr);
+ else
+- DEBUG (dev, "kevent %d scheduled\n", flag);
+-}
+-
+-static void rx_complete (struct usb_ep *ep, struct usb_request *req);
+-
+-static int
+-rx_submit (struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags)
+-{
+- struct sk_buff *skb;
+- int retval = -ENOMEM;
+- size_t size;
+-
+- /* Padding up to RX_EXTRA handles minor disagreements with host.
+- * Normally we use the USB "terminate on short read" convention;
+- * so allow up to (N*maxpacket), since that memory is normally
+- * already allocated. Some hardware doesn't deal well with short
+- * reads (e.g. DMA must be N*maxpacket), so for now don't trim a
+- * byte off the end (to force hardware errors on overflow).
+- *
+- * RNDIS uses internal framing, and explicitly allows senders to
+- * pad to end-of-packet. That's potentially nice for speed,
+- * but means receivers can't recover synch on their own.
+- */
+- size = (sizeof (struct ethhdr) + dev->net->mtu + RX_EXTRA);
+- size += dev->out_ep->maxpacket - 1;
+- if (rndis_active(dev))
+- size += sizeof (struct rndis_packet_msg_type);
+- size -= size % dev->out_ep->maxpacket;
+-
+- skb = alloc_skb(size + NET_IP_ALIGN, gfp_flags);
+- if (skb == NULL) {
+- DEBUG (dev, "no rx skb\n");
+- goto enomem;
+- }
+-
+- /* Some platforms perform better when IP packets are aligned,
+- * but on at least one, checksumming fails otherwise. Note:
+- * RNDIS headers involve variable numbers of LE32 values.
+- */
+- skb_reserve(skb, NET_IP_ALIGN);
+-
+- req->buf = skb->data;
+- req->length = size;
+- req->complete = rx_complete;
+- req->context = skb;
+-
+- retval = usb_ep_queue (dev->out_ep, req, gfp_flags);
+- if (retval == -ENOMEM)
+-enomem:
+- defer_kevent (dev, WORK_RX_MEMORY);
+- if (retval) {
+- DEBUG (dev, "rx submit --> %d\n", retval);
+- if (skb)
+- dev_kfree_skb_any(skb);
+- spin_lock(&dev->req_lock);
+- list_add (&req->list, &dev->rx_reqs);
+- spin_unlock(&dev->req_lock);
+- }
+- return retval;
+-}
+-
+-static void rx_complete (struct usb_ep *ep, struct usb_request *req)
+-{
+- struct sk_buff *skb = req->context;
+- struct eth_dev *dev = ep->driver_data;
+- int status = req->status;
+-
+- switch (status) {
+-
+- /* normal completion */
+- case 0:
+- skb_put (skb, req->actual);
+- /* we know MaxPacketsPerTransfer == 1 here */
+- if (rndis_active(dev))
+- status = rndis_rm_hdr (skb);
+- if (status < 0
+- || ETH_HLEN > skb->len
+- || skb->len > ETH_FRAME_LEN) {
+- dev->stats.rx_errors++;
+- dev->stats.rx_length_errors++;
+- DEBUG (dev, "rx length %d\n", skb->len);
+- break;
+- }
+-
+- skb->protocol = eth_type_trans (skb, dev->net);
+- dev->stats.rx_packets++;
+- dev->stats.rx_bytes += skb->len;
+-
+- /* no buffer copies needed, unless hardware can't
+- * use skb buffers.
+- */
+- status = netif_rx (skb);
+- skb = NULL;
+- break;
+-
+- /* software-driven interface shutdown */
+- case -ECONNRESET: // unlink
+- case -ESHUTDOWN: // disconnect etc
+- VDEBUG (dev, "rx shutdown, code %d\n", status);
+- goto quiesce;
+-
+- /* for hardware automagic (such as pxa) */
+- case -ECONNABORTED: // endpoint reset
+- DEBUG (dev, "rx %s reset\n", ep->name);
+- defer_kevent (dev, WORK_RX_MEMORY);
+-quiesce:
+- dev_kfree_skb_any (skb);
+- goto clean;
+-
+- /* data overrun */
+- case -EOVERFLOW:
+- dev->stats.rx_over_errors++;
+- // FALLTHROUGH
+-
+- default:
+- dev->stats.rx_errors++;
+- DEBUG (dev, "rx status %d\n", status);
+- break;
+- }
+-
+- if (skb)
+- dev_kfree_skb_any (skb);
+- if (!netif_running (dev->net)) {
+-clean:
+- spin_lock(&dev->req_lock);
+- list_add (&req->list, &dev->rx_reqs);
+- spin_unlock(&dev->req_lock);
+- req = NULL;
+- }
+- if (req)
+- rx_submit (dev, req, GFP_ATOMIC);
+-}
+-
+-static int prealloc (struct list_head *list, struct usb_ep *ep,
+- unsigned n, gfp_t gfp_flags)
+-{
+- unsigned i;
+- struct usb_request *req;
+-
+- if (!n)
+- return -ENOMEM;
+-
+- /* queue/recycle up to N requests */
+- i = n;
+- list_for_each_entry (req, list, list) {
+- if (i-- == 0)
+- goto extra;
+- }
+- while (i--) {
+- req = usb_ep_alloc_request (ep, gfp_flags);
+- if (!req)
+- return list_empty (list) ? -ENOMEM : 0;
+- list_add (&req->list, list);
+- }
+- return 0;
+-
+-extra:
+- /* free extras */
+- for (;;) {
+- struct list_head *next;
+-
+- next = req->list.next;
+- list_del (&req->list);
+- usb_ep_free_request (ep, req);
+-
+- if (next == list)
+- break;
+-
+- req = container_of (next, struct usb_request, list);
+- }
+- return 0;
+-}
+-
+-static int alloc_requests (struct eth_dev *dev, unsigned n, gfp_t gfp_flags)
+-{
+- int status;
+-
+- spin_lock(&dev->req_lock);
+- status = prealloc (&dev->tx_reqs, dev->in_ep, n, gfp_flags);
+- if (status < 0)
+- goto fail;
+- status = prealloc (&dev->rx_reqs, dev->out_ep, n, gfp_flags);
+- if (status < 0)
+- goto fail;
+- goto done;
+-fail:
+- DEBUG (dev, "can't alloc requests\n");
+-done:
+- spin_unlock(&dev->req_lock);
+- return status;
+-}
+-
+-static void rx_fill (struct eth_dev *dev, gfp_t gfp_flags)
+-{
+- struct usb_request *req;
+- unsigned long flags;
+-
+- /* fill unused rxq slots with some skb */
+- spin_lock_irqsave(&dev->req_lock, flags);
+- while (!list_empty (&dev->rx_reqs)) {
+- req = container_of (dev->rx_reqs.next,
+- struct usb_request, list);
+- list_del_init (&req->list);
+- spin_unlock_irqrestore(&dev->req_lock, flags);
+-
+- if (rx_submit (dev, req, gfp_flags) < 0) {
+- defer_kevent (dev, WORK_RX_MEMORY);
+- return;
+- }
+-
+- spin_lock_irqsave(&dev->req_lock, flags);
+- }
+- spin_unlock_irqrestore(&dev->req_lock, flags);
+-}
+-
+-static void eth_work (struct work_struct *work)
+-{
+- struct eth_dev *dev = container_of(work, struct eth_dev, work);
+-
+- if (test_and_clear_bit (WORK_RX_MEMORY, &dev->todo)) {
+- if (netif_running (dev->net))
+- rx_fill (dev, GFP_KERNEL);
+- }
+-
+- if (dev->todo)
+- DEBUG (dev, "work done, flags = 0x%lx\n", dev->todo);
+-}
+-
+-static void tx_complete (struct usb_ep *ep, struct usb_request *req)
+-{
+- struct sk_buff *skb = req->context;
+- struct eth_dev *dev = ep->driver_data;
+-
+- switch (req->status) {
+- default:
+- dev->stats.tx_errors++;
+- VDEBUG (dev, "tx err %d\n", req->status);
+- /* FALLTHROUGH */
+- case -ECONNRESET: // unlink
+- case -ESHUTDOWN: // disconnect etc
+- break;
+- case 0:
+- dev->stats.tx_bytes += skb->len;
+- }
+- dev->stats.tx_packets++;
+-
+- spin_lock(&dev->req_lock);
+- list_add (&req->list, &dev->tx_reqs);
+- spin_unlock(&dev->req_lock);
+- dev_kfree_skb_any (skb);
+-
+- atomic_dec (&dev->tx_qlen);
+- if (netif_carrier_ok (dev->net))
+- netif_wake_queue (dev->net);
+-}
+-
+-static inline int eth_is_promisc (struct eth_dev *dev)
+-{
+- /* no filters for the CDC subset; always promisc */
+- if (subset_active (dev))
+- return 1;
+- return dev->cdc_filter & USB_CDC_PACKET_TYPE_PROMISCUOUS;
+-}
+-
+-static int eth_start_xmit (struct sk_buff *skb, struct net_device *net)
+-{
+- struct eth_dev *dev = netdev_priv(net);
+- int length = skb->len;
+- int retval;
+- struct usb_request *req = NULL;
+- unsigned long flags;
+-
+- /* apply outgoing CDC or RNDIS filters */
+- if (!eth_is_promisc (dev)) {
+- u8 *dest = skb->data;
+-
+- if (is_multicast_ether_addr(dest)) {
+- u16 type;
+-
+- /* ignores USB_CDC_PACKET_TYPE_MULTICAST and host
+- * SET_ETHERNET_MULTICAST_FILTERS requests
+- */
+- if (is_broadcast_ether_addr(dest))
+- type = USB_CDC_PACKET_TYPE_BROADCAST;
+- else
+- type = USB_CDC_PACKET_TYPE_ALL_MULTICAST;
+- if (!(dev->cdc_filter & type)) {
+- dev_kfree_skb_any (skb);
+- return 0;
+- }
+- }
+- /* ignores USB_CDC_PACKET_TYPE_DIRECTED */
+- }
+-
+- spin_lock_irqsave(&dev->req_lock, flags);
+- /*
+- * this freelist can be empty if an interrupt triggered disconnect()
+- * and reconfigured the gadget (shutting down this queue) after the
+- * network stack decided to xmit but before we got the spinlock.
+- */
+- if (list_empty(&dev->tx_reqs)) {
+- spin_unlock_irqrestore(&dev->req_lock, flags);
+- return 1;
+- }
+-
+- req = container_of (dev->tx_reqs.next, struct usb_request, list);
+- list_del (&req->list);
+-
+- /* temporarily stop TX queue when the freelist empties */
+- if (list_empty (&dev->tx_reqs))
+- netif_stop_queue (net);
+- spin_unlock_irqrestore(&dev->req_lock, flags);
+-
+- /* no buffer copies needed, unless the network stack did it
+- * or the hardware can't use skb buffers.
+- * or there's not enough space for any RNDIS headers we need
+- */
+- if (rndis_active(dev)) {
+- struct sk_buff *skb_rndis;
+-
+- skb_rndis = skb_realloc_headroom (skb,
+- sizeof (struct rndis_packet_msg_type));
+- if (!skb_rndis)
+- goto drop;
+-
+- dev_kfree_skb_any (skb);
+- skb = skb_rndis;
+- rndis_add_hdr (skb);
+- length = skb->len;
+- }
+- req->buf = skb->data;
+- req->context = skb;
+- req->complete = tx_complete;
+-
+- /* use zlp framing on tx for strict CDC-Ether conformance,
+- * though any robust network rx path ignores extra padding.
+- * and some hardware doesn't like to write zlps.
+- */
+- req->zero = 1;
+- if (!dev->zlp && (length % dev->in_ep->maxpacket) == 0)
+- length++;
+-
+- req->length = length;
+-
+- /* throttle highspeed IRQ rate back slightly */
+- if (gadget_is_dualspeed(dev->gadget))
+- req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH)
+- ? ((atomic_read(&dev->tx_qlen) % qmult) != 0)
+- : 0;
+-
+- retval = usb_ep_queue (dev->in_ep, req, GFP_ATOMIC);
+- switch (retval) {
+- default:
+- DEBUG (dev, "tx queue err %d\n", retval);
+- break;
+- case 0:
+- net->trans_start = jiffies;
+- atomic_inc (&dev->tx_qlen);
+- }
+-
+- if (retval) {
+-drop:
+- dev->stats.tx_dropped++;
+- dev_kfree_skb_any (skb);
+- spin_lock_irqsave(&dev->req_lock, flags);
+- if (list_empty (&dev->tx_reqs))
+- netif_start_queue (net);
+- list_add (&req->list, &dev->tx_reqs);
+- spin_unlock_irqrestore(&dev->req_lock, flags);
+- }
+- return 0;
+-}
+-
+-/*-------------------------------------------------------------------------*/
+-
+-#ifdef CONFIG_USB_ETH_RNDIS
+-
+-/* The interrupt endpoint is used in RNDIS to notify the host when messages
+- * other than data packets are available ... notably the REMOTE_NDIS_*_CMPLT
+- * messages, but also REMOTE_NDIS_INDICATE_STATUS_MSG and potentially even
+- * REMOTE_NDIS_KEEPALIVE_MSG.
+- *
+- * The RNDIS control queue is processed by GET_ENCAPSULATED_RESPONSE, and
+- * normally just one notification will be queued.
+- */
+-
+-static struct usb_request *eth_req_alloc (struct usb_ep *, unsigned, gfp_t);
+-static void eth_req_free (struct usb_ep *ep, struct usb_request *req);
+-
+-static void
+-rndis_control_ack_complete (struct usb_ep *ep, struct usb_request *req)
+-{
+- struct eth_dev *dev = ep->driver_data;
+-
+- if (req->status || req->actual != req->length)
+- DEBUG (dev,
+- "rndis control ack complete --> %d, %d/%d\n",
+- req->status, req->actual, req->length);
+- req->context = NULL;
+-
+- if (req != dev->stat_req)
+- eth_req_free(ep, req);
+-}
+-
+-static int rndis_control_ack (struct net_device *net)
+-{
+- struct eth_dev *dev = netdev_priv(net);
+- int length;
+- struct usb_request *resp = dev->stat_req;
+-
+- /* in case RNDIS calls this after disconnect */
+- if (!dev->status) {
+- DEBUG (dev, "status ENODEV\n");
+- return -ENODEV;
+- }
+-
+- /* in case queue length > 1 */
+- if (resp->context) {
+- resp = eth_req_alloc (dev->status_ep, 8, GFP_ATOMIC);
+- if (!resp)
+- return -ENOMEM;
+- }
+-
+- /* Send RNDIS RESPONSE_AVAILABLE notification;
+- * USB_CDC_NOTIFY_RESPONSE_AVAILABLE should work too
+- */
+- resp->length = 8;
+- resp->complete = rndis_control_ack_complete;
+- resp->context = dev;
+-
+- *((__le32 *) resp->buf) = __constant_cpu_to_le32 (1);
+- *((__le32 *) resp->buf + 1) = __constant_cpu_to_le32 (0);
+-
+- length = usb_ep_queue (dev->status_ep, resp, GFP_ATOMIC);
+- if (length < 0) {
+- resp->status = 0;
+- rndis_control_ack_complete (dev->status_ep, resp);
+- }
+-
+- return 0;
+-}
+-
+-#else
+-
+-#define rndis_control_ack NULL
+-
+-#endif /* RNDIS */
+-
+-static void eth_start (struct eth_dev *dev, gfp_t gfp_flags)
+-{
+- DEBUG (dev, "%s\n", __func__);
+-
+- /* fill the rx queue */
+- rx_fill (dev, gfp_flags);
+-
+- /* and open the tx floodgates */
+- atomic_set (&dev->tx_qlen, 0);
+- netif_wake_queue (dev->net);
+- if (rndis_active(dev)) {
+- rndis_set_param_medium (dev->rndis_config,
+- NDIS_MEDIUM_802_3,
+- BITRATE(dev->gadget)/100);
+- (void) rndis_signal_connect (dev->rndis_config);
+- }
+-}
+-
+-static int eth_open (struct net_device *net)
+-{
+- struct eth_dev *dev = netdev_priv(net);
+-
+- DEBUG (dev, "%s\n", __func__);
+- if (netif_carrier_ok (dev->net))
+- eth_start (dev, GFP_KERNEL);
+- return 0;
++ return geth_bind_config(c, hostaddr);
+ }
+
+-static int eth_stop (struct net_device *net)
+-{
+- struct eth_dev *dev = netdev_priv(net);
+-
+- VDEBUG (dev, "%s\n", __func__);
+- netif_stop_queue (net);
+-
+- DEBUG (dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld\n",
+- dev->stats.rx_packets, dev->stats.tx_packets,
+- dev->stats.rx_errors, dev->stats.tx_errors
+- );
+-
+- /* ensure there are no more active requests */
+- if (dev->config) {
+- usb_ep_disable (dev->in_ep);
+- usb_ep_disable (dev->out_ep);
+- if (netif_carrier_ok (dev->net)) {
+- DEBUG (dev, "host still using in/out endpoints\n");
+- // FIXME idiom may leave toggle wrong here
+- usb_ep_enable (dev->in_ep, dev->in);
+- usb_ep_enable (dev->out_ep, dev->out);
+- }
+- if (dev->status_ep) {
+- usb_ep_disable (dev->status_ep);
+- usb_ep_enable (dev->status_ep, dev->status);
+- }
+- }
+-
+- if (rndis_active(dev)) {
+- rndis_set_param_medium(dev->rndis_config, NDIS_MEDIUM_802_3, 0);
+- (void) rndis_signal_disconnect (dev->rndis_config);
+- }
+-
+- return 0;
+-}
++static struct usb_configuration eth_config_driver = {
++ /* .label = f(hardware) */
++ .bind = eth_do_config,
++ .bConfigurationValue = 1,
++ /* .iConfiguration = DYNAMIC */
++ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
++ .bMaxPower = 1, /* 2 mA, minimal */
++};
+
+ /*-------------------------------------------------------------------------*/
+
+-static struct usb_request *
+-eth_req_alloc (struct usb_ep *ep, unsigned size, gfp_t gfp_flags)
++static int __init eth_bind(struct usb_composite_dev *cdev)
+ {
+- struct usb_request *req;
+-
+- req = usb_ep_alloc_request (ep, gfp_flags);
+- if (!req)
+- return NULL;
+-
+- req->buf = kmalloc (size, gfp_flags);
+- if (!req->buf) {
+- usb_ep_free_request (ep, req);
+- req = NULL;
+- }
+- return req;
+-}
+-
+-static void
+-eth_req_free (struct usb_ep *ep, struct usb_request *req)
+-{
+- kfree (req->buf);
+- usb_ep_free_request (ep, req);
+-}
+-
+-
+-static void /* __init_or_exit */
+-eth_unbind (struct usb_gadget *gadget)
+-{
+- struct eth_dev *dev = get_gadget_data (gadget);
+-
+- DEBUG (dev, "unbind\n");
+- rndis_deregister (dev->rndis_config);
+- rndis_exit ();
+-
+- /* we've already been disconnected ... no i/o is active */
+- if (dev->req) {
+- eth_req_free (gadget->ep0, dev->req);
+- dev->req = NULL;
+- }
+- if (dev->stat_req) {
+- eth_req_free (dev->status_ep, dev->stat_req);
+- dev->stat_req = NULL;
+- }
+-
+- unregister_netdev (dev->net);
+- free_netdev(dev->net);
+-
+- /* assuming we used keventd, it must quiesce too */
+- flush_scheduled_work ();
+- set_gadget_data (gadget, NULL);
+-}
+-
+-static u8 __init nibble (unsigned char c)
+-{
+- if (likely (isdigit (c)))
+- return c - '0';
+- c = toupper (c);
+- if (likely (isxdigit (c)))
+- return 10 + c - 'A';
+- return 0;
+-}
++ int gcnum;
++ struct usb_gadget *gadget = cdev->gadget;
++ int status;
+
+-static int __init get_ether_addr(const char *str, u8 *dev_addr)
+-{
+- if (str) {
+- unsigned i;
++ /* set up network link layer */
++ status = gether_setup(cdev->gadget, hostaddr);
++ if (status < 0)
++ return status;
+
+- for (i = 0; i < 6; i++) {
+- unsigned char num;
++ /* set up main config label and device descriptor */
++ if (can_support_ecm(cdev->gadget)) {
++ /* ECM */
++ eth_config_driver.label = "CDC Ethernet (ECM)";
++ } else {
++ /* CDC Subset */
++ eth_config_driver.label = "CDC Subset/SAFE";
+
+- if((*str == '.') || (*str == ':'))
+- str++;
+- num = nibble(*str++) << 4;
+- num |= (nibble(*str++));
+- dev_addr [i] = num;
+- }
+- if (is_valid_ether_addr (dev_addr))
+- return 0;
++ device_desc.idVendor = cpu_to_le16(SIMPLE_VENDOR_NUM),
++ device_desc.idProduct = cpu_to_le16(SIMPLE_PRODUCT_NUM),
++ device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC;
+ }
+- random_ether_addr(dev_addr);
+- return 1;
+-}
+-
+-static int __init
+-eth_bind (struct usb_gadget *gadget)
+-{
+- struct eth_dev *dev;
+- struct net_device *net;
+- u8 cdc = 1, zlp = 1, rndis = 1;
+- struct usb_ep *in_ep, *out_ep, *status_ep = NULL;
+- int status = -ENOMEM;
+- int gcnum;
+-
+- /* these flags are only ever cleared; compiler take note */
+-#ifndef DEV_CONFIG_CDC
+- cdc = 0;
+-#endif
+-#ifndef CONFIG_USB_ETH_RNDIS
+- rndis = 0;
+-#endif
+
+- /* Because most host side USB stacks handle CDC Ethernet, that
+- * standard protocol is _strongly_ preferred for interop purposes.
+- * (By everyone except Microsoft.)
+- */
+- if (gadget_is_pxa (gadget)) {
+- /* pxa doesn't support altsettings */
+- cdc = 0;
+- } else if (gadget_is_musbhdrc(gadget)) {
+- /* reduce tx dma overhead by avoiding special cases */
+- zlp = 0;
+- } else if (gadget_is_sh(gadget)) {
+- /* sh doesn't support multiple interfaces or configs */
+- cdc = 0;
+- rndis = 0;
+- } else if (gadget_is_sa1100 (gadget)) {
+- /* hardware can't write zlps */
+- zlp = 0;
+- /* sa1100 CAN do CDC, without status endpoint ... we use
+- * non-CDC to be compatible with ARM Linux-2.4 "usb-eth".
+- */
+- cdc = 0;
++ if (has_rndis()) {
++ /* RNDIS plus ECM-or-Subset */
++ device_desc.idVendor = cpu_to_le16(RNDIS_VENDOR_NUM),
++ device_desc.idProduct = cpu_to_le16(RNDIS_PRODUCT_NUM),
++ device_desc.bNumConfigurations = 2;
+ }
+
+- gcnum = usb_gadget_controller_number (gadget);
++ gcnum = usb_gadget_controller_number(gadget);
+ if (gcnum >= 0)
+- device_desc.bcdDevice = cpu_to_le16 (0x0200 + gcnum);
++ device_desc.bcdDevice = cpu_to_le16(0x0300 | gcnum);
+ else {
+- /* can't assume CDC works. don't want to default to
+- * anything less functional on CDC-capable hardware,
+- * so we fail in this case.
++ /* We assume that can_support_ecm() tells the truth;
++ * but if the controller isn't recognized at all then
++ * that assumption is a bit more likely to be wrong.
+ */
+- dev_err (&gadget->dev,
+- "controller '%s' not recognized\n",
+- gadget->name);
+- return -ENODEV;
+- }
+- snprintf (manufacturer, sizeof manufacturer, "%s %s/%s",
+- init_utsname()->sysname, init_utsname()->release,
+- gadget->name);
+-
+- /* If there's an RNDIS configuration, that's what Windows wants to
+- * be using ... so use these product IDs here and in the "linux.inf"
+- * needed to install MSFT drivers. Current Linux kernels will use
+- * the second configuration if it's CDC Ethernet, and need some help
+- * to choose the right configuration otherwise.
+- */
+- if (rndis) {
+- device_desc.idVendor =
+- __constant_cpu_to_le16(RNDIS_VENDOR_NUM);
+- device_desc.idProduct =
+- __constant_cpu_to_le16(RNDIS_PRODUCT_NUM);
+- snprintf (product_desc, sizeof product_desc,
+- "RNDIS/%s", driver_desc);
+-
+- /* CDC subset ... recognized by Linux since 2.4.10, but Windows
+- * drivers aren't widely available. (That may be improved by
+- * supporting one submode of the "SAFE" variant of MDLM.)
+- */
+- } else if (!cdc) {
+- device_desc.idVendor =
+- __constant_cpu_to_le16(SIMPLE_VENDOR_NUM);
+- device_desc.idProduct =
+- __constant_cpu_to_le16(SIMPLE_PRODUCT_NUM);
+- }
+-
+- /* support optional vendor/distro customization */
+- if (idVendor) {
+- if (!idProduct) {
+- dev_err (&gadget->dev, "idVendor needs idProduct!\n");
+- return -ENODEV;
+- }
+- device_desc.idVendor = cpu_to_le16(idVendor);
+- device_desc.idProduct = cpu_to_le16(idProduct);
+- if (bcdDevice)
+- device_desc.bcdDevice = cpu_to_le16(bcdDevice);
+- }
+- if (iManufacturer)
+- strlcpy (manufacturer, iManufacturer, sizeof manufacturer);
+- if (iProduct)
+- strlcpy (product_desc, iProduct, sizeof product_desc);
+- if (iSerialNumber) {
+- device_desc.iSerialNumber = STRING_SERIALNUMBER,
+- strlcpy(serial_number, iSerialNumber, sizeof serial_number);
+- }
+-
+- /* all we really need is bulk IN/OUT */
+- usb_ep_autoconfig_reset (gadget);
+- in_ep = usb_ep_autoconfig (gadget, &fs_source_desc);
+- if (!in_ep) {
+-autoconf_fail:
+- dev_err (&gadget->dev,
+- "can't autoconfigure on %s\n",
+- gadget->name);
+- return -ENODEV;
+- }
+- in_ep->driver_data = in_ep; /* claim */
+-
+- out_ep = usb_ep_autoconfig (gadget, &fs_sink_desc);
+- if (!out_ep)
+- goto autoconf_fail;
+- out_ep->driver_data = out_ep; /* claim */
+-
+-#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS)
+- /* CDC Ethernet control interface doesn't require a status endpoint.
+- * Since some hosts expect one, try to allocate one anyway.
+- */
+- if (cdc || rndis) {
+- status_ep = usb_ep_autoconfig (gadget, &fs_status_desc);
+- if (status_ep) {
+- status_ep->driver_data = status_ep; /* claim */
+- } else if (rndis) {
+- dev_err (&gadget->dev,
+- "can't run RNDIS on %s\n",
+- gadget->name);
+- return -ENODEV;
+-#ifdef DEV_CONFIG_CDC
+- /* pxa25x only does CDC subset; often used with RNDIS */
+- } else if (cdc) {
+- control_intf.bNumEndpoints = 0;
+- /* FIXME remove endpoint from descriptor list */
+-#endif
+- }
+- }
+-#endif
+-
+- /* one config: cdc, else minimal subset */
+- if (!cdc) {
+- eth_config.bNumInterfaces = 1;
+- eth_config.iConfiguration = STRING_SUBSET;
+-
+- /* use functions to set these up, in case we're built to work
+- * with multiple controllers and must override CDC Ethernet.
+- */
+- fs_subset_descriptors();
+- hs_subset_descriptors();
+- }
+-
+- device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
+- usb_gadget_set_selfpowered (gadget);
+-
+- /* For now RNDIS is always a second config */
+- if (rndis)
+- device_desc.bNumConfigurations = 2;
+-
+- if (gadget_is_dualspeed(gadget)) {
+- if (rndis)
+- dev_qualifier.bNumConfigurations = 2;
+- else if (!cdc)
+- dev_qualifier.bDeviceClass = USB_CLASS_VENDOR_SPEC;
+-
+- /* assumes ep0 uses the same value for both speeds ... */
+- dev_qualifier.bMaxPacketSize0 = device_desc.bMaxPacketSize0;
+-
+- /* and that all endpoints are dual-speed */
+- hs_source_desc.bEndpointAddress =
+- fs_source_desc.bEndpointAddress;
+- hs_sink_desc.bEndpointAddress =
+- fs_sink_desc.bEndpointAddress;
+-#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS)
+- if (status_ep)
+- hs_status_desc.bEndpointAddress =
+- fs_status_desc.bEndpointAddress;
+-#endif
++ WARNING(cdev, "controller '%s' not recognized; trying %s\n",
++ gadget->name,
++ eth_config_driver.label);
++ device_desc.bcdDevice =
++ __constant_cpu_to_le16(0x0300 | 0x0099);
+ }
+
+- if (gadget_is_otg(gadget)) {
+- otg_descriptor.bmAttributes |= USB_OTG_HNP,
+- eth_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+- eth_config.bMaxPower = 4;
+-#ifdef CONFIG_USB_ETH_RNDIS
+- rndis_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+- rndis_config.bMaxPower = 4;
+-#endif
+- }
+-
+- net = alloc_etherdev (sizeof *dev);
+- if (!net)
+- return status;
+- dev = netdev_priv(net);
+- spin_lock_init (&dev->lock);
+- spin_lock_init (&dev->req_lock);
+- INIT_WORK (&dev->work, eth_work);
+- INIT_LIST_HEAD (&dev->tx_reqs);
+- INIT_LIST_HEAD (&dev->rx_reqs);
+-
+- /* network device setup */
+- dev->net = net;
+- strcpy (net->name, "usb%d");
+- dev->cdc = cdc;
+- dev->zlp = zlp;
+
+- dev->in_ep = in_ep;
+- dev->out_ep = out_ep;
+- dev->status_ep = status_ep;
+-
+- /* Module params for these addresses should come from ID proms.
+- * The host side address is used with CDC and RNDIS, and commonly
+- * ends up in a persistent config database. It's not clear if
+- * host side code for the SAFE thing cares -- its original BLAN
+- * thing didn't, Sharp never assigned those addresses on Zaurii.
++ /* Allocate string descriptor numbers ... note that string
++ * contents can be overridden by the composite_dev glue.
+ */
+- if (get_ether_addr(dev_addr, net->dev_addr))
+- dev_warn(&gadget->dev,
+- "using random %s ethernet address\n", "self");
+- if (get_ether_addr(host_addr, dev->host_mac))
+- dev_warn(&gadget->dev,
+- "using random %s ethernet address\n", "host");
+- snprintf (ethaddr, sizeof ethaddr, "%02X%02X%02X%02X%02X%02X",
+- dev->host_mac [0], dev->host_mac [1],
+- dev->host_mac [2], dev->host_mac [3],
+- dev->host_mac [4], dev->host_mac [5]);
+-
+- if (rndis) {
+- status = rndis_init();
+- if (status < 0) {
+- dev_err (&gadget->dev, "can't init RNDIS, %d\n",
+- status);
+- goto fail;
+- }
+- }
+
+- net->change_mtu = eth_change_mtu;
+- net->get_stats = eth_get_stats;
+- net->hard_start_xmit = eth_start_xmit;
+- net->open = eth_open;
+- net->stop = eth_stop;
+- // watchdog_timeo, tx_timeout ...
+- // set_multicast_list
+- SET_ETHTOOL_OPS(net, &ops);
++ /* device descriptor strings: manufacturer, product */
++ snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
++ init_utsname()->sysname, init_utsname()->release,
++ gadget->name);
++ status = usb_string_id(cdev);
++ if (status < 0)
++ goto fail;
++ strings_dev[STRING_MANUFACTURER_IDX].id = status;
++ device_desc.iManufacturer = status;
+
+- /* preallocate control message data and buffer */
+- dev->req = eth_req_alloc (gadget->ep0, USB_BUFSIZ, GFP_KERNEL);
+- if (!dev->req)
++ status = usb_string_id(cdev);
++ if (status < 0)
+ goto fail;
+- dev->req->complete = eth_setup_complete;
++ strings_dev[STRING_PRODUCT_IDX].id = status;
++ device_desc.iProduct = status;
+
+- /* ... and maybe likewise for status transfer */
+-#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS)
+- if (dev->status_ep) {
+- dev->stat_req = eth_req_alloc (dev->status_ep,
+- STATUS_BYTECOUNT, GFP_KERNEL);
+- if (!dev->stat_req) {
+- eth_req_free (gadget->ep0, dev->req);
++ /* register our configuration(s); RNDIS first, if it's used */
++ if (has_rndis()) {
++ status = usb_add_config(cdev, &rndis_config_driver);
++ if (status < 0)
+ goto fail;
+- }
+- dev->stat_req->context = NULL;
+ }
+-#endif
+-
+- /* finish hookup to lower layer ... */
+- dev->gadget = gadget;
+- set_gadget_data (gadget, dev);
+- gadget->ep0->driver_data = dev;
+
+- /* two kinds of host-initiated state changes:
+- * - iff DATA transfer is active, carrier is "on"
+- * - tx queueing enabled if open *and* carrier is "on"
+- */
+- netif_stop_queue (dev->net);
+- netif_carrier_off (dev->net);
+-
+- SET_NETDEV_DEV (dev->net, &gadget->dev);
+- status = register_netdev (dev->net);
++ status = usb_add_config(cdev, &eth_config_driver);
+ if (status < 0)
+- goto fail1;
+-
+- INFO (dev, "%s, version: " DRIVER_VERSION "\n", driver_desc);
+- INFO (dev, "using %s, OUT %s IN %s%s%s\n", gadget->name,
+- out_ep->name, in_ep->name,
+- status_ep ? " STATUS " : "",
+- status_ep ? status_ep->name : ""
+- );
+- INFO (dev, "MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
+- net->dev_addr [0], net->dev_addr [1],
+- net->dev_addr [2], net->dev_addr [3],
+- net->dev_addr [4], net->dev_addr [5]);
+-
+- if (cdc || rndis)
+- INFO (dev, "HOST MAC %02x:%02x:%02x:%02x:%02x:%02x\n",
+- dev->host_mac [0], dev->host_mac [1],
+- dev->host_mac [2], dev->host_mac [3],
+- dev->host_mac [4], dev->host_mac [5]);
+-
+- if (rndis) {
+- u32 vendorID = 0;
+-
+- /* FIXME RNDIS vendor id == "vendor NIC code" == ? */
+-
+- dev->rndis_config = rndis_register (rndis_control_ack);
+- if (dev->rndis_config < 0) {
+-fail0:
+- unregister_netdev (dev->net);
+- status = -ENODEV;
+- goto fail;
+- }
++ goto fail;
+
+- /* these set up a lot of the OIDs that RNDIS needs */
+- rndis_set_host_mac (dev->rndis_config, dev->host_mac);
+- if (rndis_set_param_dev (dev->rndis_config, dev->net,
+- &dev->stats, &dev->cdc_filter))
+- goto fail0;
+- if (rndis_set_param_vendor(dev->rndis_config, vendorID,
+- manufacturer))
+- goto fail0;
+- if (rndis_set_param_medium(dev->rndis_config,
+- NDIS_MEDIUM_802_3, 0))
+- goto fail0;
+- INFO (dev, "RNDIS ready\n");
+- }
++ INFO(cdev, "%s, version: " DRIVER_VERSION "\n", DRIVER_DESC);
+
+- return status;
++ return 0;
+
+-fail1:
+- dev_dbg(&gadget->dev, "register_netdev failed, %d\n", status);
+ fail:
+- eth_unbind (gadget);
++ gether_cleanup();
+ return status;
+ }
+
+-/*-------------------------------------------------------------------------*/
+-
+-static void
+-eth_suspend (struct usb_gadget *gadget)
+-{
+- struct eth_dev *dev = get_gadget_data (gadget);
+-
+- DEBUG (dev, "suspend\n");
+- dev->suspended = 1;
+-}
+-
+-static void
+-eth_resume (struct usb_gadget *gadget)
++static int __exit eth_unbind(struct usb_composite_dev *cdev)
+ {
+- struct eth_dev *dev = get_gadget_data (gadget);
+-
+- DEBUG (dev, "resume\n");
+- dev->suspended = 0;
++ gether_cleanup();
++ return 0;
+ }
+
+-/*-------------------------------------------------------------------------*/
+-
+-static struct usb_gadget_driver eth_driver = {
+- .speed = DEVSPEED,
+-
+- .function = (char *) driver_desc,
++static struct usb_composite_driver eth_driver = {
++ .name = "g_ether",
++ .dev = &device_desc,
++ .strings = dev_strings,
+ .bind = eth_bind,
+- .unbind = eth_unbind,
+-
+- .setup = eth_setup,
+- .disconnect = eth_disconnect,
+-
+- .suspend = eth_suspend,
+- .resume = eth_resume,
+-
+- .driver = {
+- .name = (char *) shortname,
+- .owner = THIS_MODULE,
+- },
++ .unbind = __exit_p(eth_unbind),
+ };
+
+-MODULE_DESCRIPTION (DRIVER_DESC);
+-MODULE_AUTHOR ("David Brownell, Benedikt Spanger");
+-MODULE_LICENSE ("GPL");
++MODULE_DESCRIPTION(PREFIX DRIVER_DESC);
++MODULE_AUTHOR("David Brownell, Benedikt Spanger");
++MODULE_LICENSE("GPL");
+
+-
+-static int __init init (void)
++static int __init init(void)
+ {
+- return usb_gadget_register_driver (&eth_driver);
++ return usb_composite_register(&eth_driver);
+ }
+-module_init (init);
++module_init(init);
+
+-static void __exit cleanup (void)
++static void __exit cleanup(void)
+ {
+- usb_gadget_unregister_driver (&eth_driver);
++ usb_composite_unregister(&eth_driver);
+ }
+-module_exit (cleanup);
+-
++module_exit(cleanup);
+diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c
+new file mode 100644
+index 0000000..5ee1590
+--- /dev/null
++++ b/drivers/usb/gadget/f_acm.c
+@@ -0,0 +1,759 @@
++/*
++ * f_acm.c -- USB CDC serial (ACM) function driver
++ *
++ * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
++ * Copyright (C) 2008 by David Brownell
++ * Copyright (C) 2008 by Nokia Corporation
++ *
++ * This software is distributed under the terms of the GNU General
++ * Public License ("GPL") as published by the Free Software Foundation,
++ * either version 2 of that License or (at your option) any later version.
++ */
++
++/* #define VERBOSE_DEBUG */
++
++#include <linux/kernel.h>
++#include <linux/device.h>
++
++#include "u_serial.h"
++#include "gadget_chips.h"
++
++
++/*
++ * This CDC ACM function support just wraps control functions and
++ * notifications around the generic serial-over-usb code.
++ *
++ * Because CDC ACM is standardized by the USB-IF, many host operating
++ * systems have drivers for it. Accordingly, ACM is the preferred
++ * interop solution for serial-port type connections. The control
++ * models are often not necessary, and in any case don't do much in
++ * this bare-bones implementation.
++ *
++ * Note that even MS-Windows has some support for ACM. However, that
++ * support is somewhat broken because when you use ACM in a composite
++ * device, having multiple interfaces confuses the poor OS. It doesn't
++ * seem to understand CDC Union descriptors. The new "association"
++ * descriptors (roughly equivalent to CDC Unions) may sometimes help.
++ */
++
++struct acm_ep_descs {
++ struct usb_endpoint_descriptor *in;
++ struct usb_endpoint_descriptor *out;
++ struct usb_endpoint_descriptor *notify;
++};
++
++struct f_acm {
++ struct gserial port;
++ u8 ctrl_id, data_id;
++ u8 port_num;
++
++ u8 pending;
++
++ /* lock is mostly for pending and notify_req ... they get accessed
++ * by callbacks both from tty (open/close/break) under its spinlock,
++ * and notify_req.complete() which can't use that lock.
++ */
++ spinlock_t lock;
++
++ struct acm_ep_descs fs;
++ struct acm_ep_descs hs;
++
++ struct usb_ep *notify;
++ struct usb_endpoint_descriptor *notify_desc;
++ struct usb_request *notify_req;
++
++ struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */
++
++ /* SetControlLineState request -- CDC 1.1 section 6.2.14 (INPUT) */
++ u16 port_handshake_bits;
++#define ACM_CTRL_RTS (1 << 1) /* unused with full duplex */
++#define ACM_CTRL_DTR (1 << 0) /* host is ready for data r/w */
++
++ /* SerialState notification -- CDC 1.1 section 6.3.5 (OUTPUT) */
++ u16 serial_state;
++#define ACM_CTRL_OVERRUN (1 << 6)
++#define ACM_CTRL_PARITY (1 << 5)
++#define ACM_CTRL_FRAMING (1 << 4)
++#define ACM_CTRL_RI (1 << 3)
++#define ACM_CTRL_BRK (1 << 2)
++#define ACM_CTRL_DSR (1 << 1)
++#define ACM_CTRL_DCD (1 << 0)
++};
++
++static inline struct f_acm *func_to_acm(struct usb_function *f)
++{
++ return container_of(f, struct f_acm, port.func);
++}
++
++static inline struct f_acm *port_to_acm(struct gserial *p)
++{
++ return container_of(p, struct f_acm, port);
++}
++
++/*-------------------------------------------------------------------------*/
++
++/* notification endpoint uses smallish and infrequent fixed-size messages */
++
++#define GS_LOG2_NOTIFY_INTERVAL 5 /* 1 << 5 == 32 msec */
++#define GS_NOTIFY_MAXPACKET 10 /* notification + 2 bytes */
++
++/* interface and class descriptors: */
++
++static struct usb_interface_descriptor acm_control_interface_desc __initdata = {
++ .bLength = USB_DT_INTERFACE_SIZE,
++ .bDescriptorType = USB_DT_INTERFACE,
++ /* .bInterfaceNumber = DYNAMIC */
++ .bNumEndpoints = 1,
++ .bInterfaceClass = USB_CLASS_COMM,
++ .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
++ .bInterfaceProtocol = USB_CDC_ACM_PROTO_AT_V25TER,
++ /* .iInterface = DYNAMIC */
++};
++
++static struct usb_interface_descriptor acm_data_interface_desc __initdata = {
++ .bLength = USB_DT_INTERFACE_SIZE,
++ .bDescriptorType = USB_DT_INTERFACE,
++ /* .bInterfaceNumber = DYNAMIC */
++ .bNumEndpoints = 2,
++ .bInterfaceClass = USB_CLASS_CDC_DATA,
++ .bInterfaceSubClass = 0,
++ .bInterfaceProtocol = 0,
++ /* .iInterface = DYNAMIC */
++};
++
++static struct usb_cdc_header_desc acm_header_desc __initdata = {
++ .bLength = sizeof(acm_header_desc),
++ .bDescriptorType = USB_DT_CS_INTERFACE,
++ .bDescriptorSubType = USB_CDC_HEADER_TYPE,
++ .bcdCDC = __constant_cpu_to_le16(0x0110),
++};
++
++static struct usb_cdc_call_mgmt_descriptor
++acm_call_mgmt_descriptor __initdata = {
++ .bLength = sizeof(acm_call_mgmt_descriptor),
++ .bDescriptorType = USB_DT_CS_INTERFACE,
++ .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE,
++ .bmCapabilities = 0,
++ /* .bDataInterface = DYNAMIC */
++};
++
++static struct usb_cdc_acm_descriptor acm_descriptor __initdata = {
++ .bLength = sizeof(acm_descriptor),
++ .bDescriptorType = USB_DT_CS_INTERFACE,
++ .bDescriptorSubType = USB_CDC_ACM_TYPE,
++ .bmCapabilities = USB_CDC_CAP_LINE,
++};
++
++static struct usb_cdc_union_desc acm_union_desc __initdata = {
++ .bLength = sizeof(acm_union_desc),
++ .bDescriptorType = USB_DT_CS_INTERFACE,
++ .bDescriptorSubType = USB_CDC_UNION_TYPE,
++ /* .bMasterInterface0 = DYNAMIC */
++ /* .bSlaveInterface0 = DYNAMIC */
++};
++
++/* full speed support: */
++
++static struct usb_endpoint_descriptor acm_fs_notify_desc __initdata = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++ .bEndpointAddress = USB_DIR_IN,
++ .bmAttributes = USB_ENDPOINT_XFER_INT,
++ .wMaxPacketSize = __constant_cpu_to_le16(GS_NOTIFY_MAXPACKET),
++ .bInterval = 1 << GS_LOG2_NOTIFY_INTERVAL,
++};
++
++static struct usb_endpoint_descriptor acm_fs_in_desc __initdata = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++ .bEndpointAddress = USB_DIR_IN,
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++};
++
++static struct usb_endpoint_descriptor acm_fs_out_desc __initdata = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++ .bEndpointAddress = USB_DIR_OUT,
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++};
++
++static struct usb_descriptor_header *acm_fs_function[] __initdata = {
++ (struct usb_descriptor_header *) &acm_control_interface_desc,
++ (struct usb_descriptor_header *) &acm_header_desc,
++ (struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
++ (struct usb_descriptor_header *) &acm_descriptor,
++ (struct usb_descriptor_header *) &acm_union_desc,
++ (struct usb_descriptor_header *) &acm_fs_notify_desc,
++ (struct usb_descriptor_header *) &acm_data_interface_desc,
++ (struct usb_descriptor_header *) &acm_fs_in_desc,
++ (struct usb_descriptor_header *) &acm_fs_out_desc,
++ NULL,
++};
++
++/* high speed support: */
++
++static struct usb_endpoint_descriptor acm_hs_notify_desc __initdata = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++ .bEndpointAddress = USB_DIR_IN,
++ .bmAttributes = USB_ENDPOINT_XFER_INT,
++ .wMaxPacketSize = __constant_cpu_to_le16(GS_NOTIFY_MAXPACKET),
++ .bInterval = GS_LOG2_NOTIFY_INTERVAL+4,
++};
++
++static struct usb_endpoint_descriptor acm_hs_in_desc __initdata = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++ .wMaxPacketSize = __constant_cpu_to_le16(512),
++};
++
++static struct usb_endpoint_descriptor acm_hs_out_desc __initdata = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++ .wMaxPacketSize = __constant_cpu_to_le16(512),
++};
++
++static struct usb_descriptor_header *acm_hs_function[] __initdata = {
++ (struct usb_descriptor_header *) &acm_control_interface_desc,
++ (struct usb_descriptor_header *) &acm_header_desc,
++ (struct usb_descriptor_header *) &acm_call_mgmt_descriptor,
++ (struct usb_descriptor_header *) &acm_descriptor,
++ (struct usb_descriptor_header *) &acm_union_desc,
++ (struct usb_descriptor_header *) &acm_hs_notify_desc,
++ (struct usb_descriptor_header *) &acm_data_interface_desc,
++ (struct usb_descriptor_header *) &acm_hs_in_desc,
++ (struct usb_descriptor_header *) &acm_hs_out_desc,
++ NULL,
++};
++
++/* string descriptors: */
++
++#define ACM_CTRL_IDX 0
++#define ACM_DATA_IDX 1
++
++/* static strings, in UTF-8 */
++static struct usb_string acm_string_defs[] = {
++ [ACM_CTRL_IDX].s = "CDC Abstract Control Model (ACM)",
++ [ACM_DATA_IDX].s = "CDC ACM Data",
++ { /* ZEROES END LIST */ },
++};
++
++static struct usb_gadget_strings acm_string_table = {
++ .language = 0x0409, /* en-us */
++ .strings = acm_string_defs,
++};
++
++static struct usb_gadget_strings *acm_strings[] = {
++ &acm_string_table,
++ NULL,
++};
++
++/*-------------------------------------------------------------------------*/
++
++/* ACM control ... data handling is delegated to tty library code.
++ * The main task of this function is to activate and deactivate
++ * that code based on device state; track parameters like line
++ * speed, handshake state, and so on; and issue notifications.
++ */
++
++static void acm_complete_set_line_coding(struct usb_ep *ep,
++ struct usb_request *req)
++{
++ struct f_acm *acm = ep->driver_data;
++ struct usb_composite_dev *cdev = acm->port.func.config->cdev;
++
++ if (req->status != 0) {
++ DBG(cdev, "acm ttyGS%d completion, err %d\n",
++ acm->port_num, req->status);
++ return;
++ }
++
++ /* normal completion */
++ if (req->actual != sizeof(acm->port_line_coding)) {
++ DBG(cdev, "acm ttyGS%d short resp, len %d\n",
++ acm->port_num, req->actual);
++ usb_ep_set_halt(ep);
++ } else {
++ struct usb_cdc_line_coding *value = req->buf;
++
++ /* REVISIT: we currently just remember this data.
++ * If we change that, (a) validate it first, then
++ * (b) update whatever hardware needs updating,
++ * (c) worry about locking. This is information on
++ * the order of 9600-8-N-1 ... most of which means
++ * nothing unless we control a real RS232 line.
++ */
++ acm->port_line_coding = *value;
++ }
++}
++
++static int acm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
++{
++ struct f_acm *acm = func_to_acm(f);
++ struct usb_composite_dev *cdev = f->config->cdev;
++ struct usb_request *req = cdev->req;
++ int value = -EOPNOTSUPP;
++ u16 w_index = le16_to_cpu(ctrl->wIndex);
++ u16 w_value = le16_to_cpu(ctrl->wValue);
++ u16 w_length = le16_to_cpu(ctrl->wLength);
++
++ /* composite driver infrastructure handles everything except
++ * CDC class messages; interface activation uses set_alt().
++ *
++ * Note CDC spec table 4 lists the ACM request profile. It requires
++ * encapsulated command support ... we don't handle any, and respond
++ * to them by stalling. Options include get/set/clear comm features
++ * (not that useful) and SEND_BREAK.
++ */
++ switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
++
++ /* SET_LINE_CODING ... just read and save what the host sends */
++ case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
++ | USB_CDC_REQ_SET_LINE_CODING:
++ if (w_length != sizeof(struct usb_cdc_line_coding)
++ || w_index != acm->ctrl_id)
++ goto invalid;
++
++ value = w_length;
++ cdev->gadget->ep0->driver_data = acm;
++ req->complete = acm_complete_set_line_coding;
++ break;
++
++ /* GET_LINE_CODING ... return what host sent, or initial value */
++ case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
++ | USB_CDC_REQ_GET_LINE_CODING:
++ if (w_index != acm->ctrl_id)
++ goto invalid;
++
++ value = min_t(unsigned, w_length,
++ sizeof(struct usb_cdc_line_coding));
++ memcpy(req->buf, &acm->port_line_coding, value);
++ break;
++
++ /* SET_CONTROL_LINE_STATE ... save what the host sent */
++ case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
++ | USB_CDC_REQ_SET_CONTROL_LINE_STATE:
++ if (w_index != acm->ctrl_id)
++ goto invalid;
++
++ value = 0;
++
++ /* FIXME we should not allow data to flow until the
++ * host sets the ACM_CTRL_DTR bit; and when it clears
++ * that bit, we should return to that no-flow state.
++ */
++ acm->port_handshake_bits = w_value;
++ break;
++
++ default:
++invalid:
++ VDBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
++ ctrl->bRequestType, ctrl->bRequest,
++ w_value, w_index, w_length);
++ }
++
++ /* respond with data transfer or status phase? */
++ if (value >= 0) {
++ DBG(cdev, "acm ttyGS%d req%02x.%02x v%04x i%04x l%d\n",
++ acm->port_num, ctrl->bRequestType, ctrl->bRequest,
++ w_value, w_index, w_length);
++ req->zero = 0;
++ req->length = value;
++ value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
++ if (value < 0)
++ ERROR(cdev, "acm response on ttyGS%d, err %d\n",
++ acm->port_num, value);
++ }
++
++ /* device either stalls (value < 0) or reports success */
++ return value;
++}
++
++static int acm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
++{
++ struct f_acm *acm = func_to_acm(f);
++ struct usb_composite_dev *cdev = f->config->cdev;
++
++ /* we know alt == 0, so this is an activation or a reset */
++
++ if (intf == acm->ctrl_id) {
++ if (acm->notify->driver_data) {
++ VDBG(cdev, "reset acm control interface %d\n", intf);
++ usb_ep_disable(acm->notify);
++ } else {
++ VDBG(cdev, "init acm ctrl interface %d\n", intf);
++ acm->notify_desc = ep_choose(cdev->gadget,
++ acm->hs.notify,
++ acm->fs.notify);
++ }
++ usb_ep_enable(acm->notify, acm->notify_desc);
++ acm->notify->driver_data = acm;
++
++ } else if (intf == acm->data_id) {
++ if (acm->port.in->driver_data) {
++ DBG(cdev, "reset acm ttyGS%d\n", acm->port_num);
++ gserial_disconnect(&acm->port);
++ } else {
++ DBG(cdev, "activate acm ttyGS%d\n", acm->port_num);
++ acm->port.in_desc = ep_choose(cdev->gadget,
++ acm->hs.in, acm->fs.in);
++ acm->port.out_desc = ep_choose(cdev->gadget,
++ acm->hs.out, acm->fs.out);
++ }
++ gserial_connect(&acm->port, acm->port_num);
++
++ } else
++ return -EINVAL;
++
++ return 0;
++}
++
++static void acm_disable(struct usb_function *f)
++{
++ struct f_acm *acm = func_to_acm(f);
++ struct usb_composite_dev *cdev = f->config->cdev;
++
++ DBG(cdev, "acm ttyGS%d deactivated\n", acm->port_num);
++ gserial_disconnect(&acm->port);
++ usb_ep_disable(acm->notify);
++ acm->notify->driver_data = NULL;
++}
++
++/*-------------------------------------------------------------------------*/
++
++/**
++ * acm_cdc_notify - issue CDC notification to host
++ * @acm: wraps host to be notified
++ * @type: notification type
++ * @value: Refer to cdc specs, wValue field.
++ * @data: data to be sent
++ * @length: size of data
++ * Context: irqs blocked, acm->lock held, acm_notify_req non-null
++ *
++ * Returns zero on sucess or a negative errno.
++ *
++ * See section 6.3.5 of the CDC 1.1 specification for information
++ * about the only notification we issue: SerialState change.
++ */
++static int acm_cdc_notify(struct f_acm *acm, u8 type, u16 value,
++ void *data, unsigned length)
++{
++ struct usb_ep *ep = acm->notify;
++ struct usb_request *req;
++ struct usb_cdc_notification *notify;
++ const unsigned len = sizeof(*notify) + length;
++ void *buf;
++ int status;
++
++ req = acm->notify_req;
++ acm->notify_req = NULL;
++ acm->pending = false;
++
++ req->length = len;
++ notify = req->buf;
++ buf = notify + 1;
++
++ notify->bmRequestType = USB_DIR_IN | USB_TYPE_CLASS
++ | USB_RECIP_INTERFACE;
++ notify->bNotificationType = type;
++ notify->wValue = cpu_to_le16(value);
++ notify->wIndex = cpu_to_le16(acm->ctrl_id);
++ notify->wLength = cpu_to_le16(length);
++ memcpy(buf, data, length);
++
++ status = usb_ep_queue(ep, req, GFP_ATOMIC);
++ if (status < 0) {
++ ERROR(acm->port.func.config->cdev,
++ "acm ttyGS%d can't notify serial state, %d\n",
++ acm->port_num, status);
++ acm->notify_req = req;
++ }
++
++ return status;
++}
++
++static int acm_notify_serial_state(struct f_acm *acm)
++{
++ struct usb_composite_dev *cdev = acm->port.func.config->cdev;
++ int status;
++
++ spin_lock(&acm->lock);
++ if (acm->notify_req) {
++ DBG(cdev, "acm ttyGS%d serial state %04x\n",
++ acm->port_num, acm->serial_state);
++ status = acm_cdc_notify(acm, USB_CDC_NOTIFY_SERIAL_STATE,
++ 0, &acm->serial_state, sizeof(acm->serial_state));
++ } else {
++ acm->pending = true;
++ status = 0;
++ }
++ spin_unlock(&acm->lock);
++ return status;
++}
++
++static void acm_cdc_notify_complete(struct usb_ep *ep, struct usb_request *req)
++{
++ struct f_acm *acm = req->context;
++ u8 doit = false;
++
++ /* on this call path we do NOT hold the port spinlock,
++ * which is why ACM needs its own spinlock
++ */
++ spin_lock(&acm->lock);
++ if (req->status != -ESHUTDOWN)
++ doit = acm->pending;
++ acm->notify_req = req;
++ spin_unlock(&acm->lock);
++
++ if (doit)
++ acm_notify_serial_state(acm);
++}
++
++/* connect == the TTY link is open */
++
++static void acm_connect(struct gserial *port)
++{
++ struct f_acm *acm = port_to_acm(port);
++
++ acm->serial_state |= ACM_CTRL_DSR | ACM_CTRL_DCD;
++ acm_notify_serial_state(acm);
++}
++
++static void acm_disconnect(struct gserial *port)
++{
++ struct f_acm *acm = port_to_acm(port);
++
++ acm->serial_state &= ~(ACM_CTRL_DSR | ACM_CTRL_DCD);
++ acm_notify_serial_state(acm);
++}
++
++static int acm_send_break(struct gserial *port, int duration)
++{
++ struct f_acm *acm = port_to_acm(port);
++ u16 state;
++
++ state = acm->serial_state;
++ state &= ~ACM_CTRL_BRK;
++ if (duration)
++ state |= ACM_CTRL_BRK;
++
++ acm->serial_state = state;
++ return acm_notify_serial_state(acm);
++}
++
++/*-------------------------------------------------------------------------*/
++
++/* ACM function driver setup/binding */
++static int __init
++acm_bind(struct usb_configuration *c, struct usb_function *f)
++{
++ struct usb_composite_dev *cdev = c->cdev;
++ struct f_acm *acm = func_to_acm(f);
++ int status;
++ struct usb_ep *ep;
++
++ /* allocate instance-specific interface IDs, and patch descriptors */
++ status = usb_interface_id(c, f);
++ if (status < 0)
++ goto fail;
++ acm->ctrl_id = status;
++
++ acm_control_interface_desc.bInterfaceNumber = status;
++ acm_union_desc .bMasterInterface0 = status;
++
++ status = usb_interface_id(c, f);
++ if (status < 0)
++ goto fail;
++ acm->data_id = status;
++
++ acm_data_interface_desc.bInterfaceNumber = status;
++ acm_union_desc.bSlaveInterface0 = status;
++ acm_call_mgmt_descriptor.bDataInterface = status;
++
++ status = -ENODEV;
++
++ /* allocate instance-specific endpoints */
++ ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_in_desc);
++ if (!ep)
++ goto fail;
++ acm->port.in = ep;
++ ep->driver_data = cdev; /* claim */
++
++ ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_out_desc);
++ if (!ep)
++ goto fail;
++ acm->port.out = ep;
++ ep->driver_data = cdev; /* claim */
++
++ ep = usb_ep_autoconfig(cdev->gadget, &acm_fs_notify_desc);
++ if (!ep)
++ goto fail;
++ acm->notify = ep;
++ ep->driver_data = cdev; /* claim */
++
++ /* allocate notification */
++ acm->notify_req = gs_alloc_req(ep,
++ sizeof(struct usb_cdc_notification) + 2,
++ GFP_KERNEL);
++ if (!acm->notify_req)
++ goto fail;
++
++ acm->notify_req->complete = acm_cdc_notify_complete;
++ acm->notify_req->context = acm;
++
++ /* copy descriptors, and track endpoint copies */
++ f->descriptors = usb_copy_descriptors(acm_fs_function);
++ if (!f->descriptors)
++ goto fail;
++
++ acm->fs.in = usb_find_endpoint(acm_fs_function,
++ f->descriptors, &acm_fs_in_desc);
++ acm->fs.out = usb_find_endpoint(acm_fs_function,
++ f->descriptors, &acm_fs_out_desc);
++ acm->fs.notify = usb_find_endpoint(acm_fs_function,
++ f->descriptors, &acm_fs_notify_desc);
++
++ /* support all relevant hardware speeds... we expect that when
++ * hardware is dual speed, all bulk-capable endpoints work at
++ * both speeds
++ */
++ if (gadget_is_dualspeed(c->cdev->gadget)) {
++ acm_hs_in_desc.bEndpointAddress =
++ acm_fs_in_desc.bEndpointAddress;
++ acm_hs_out_desc.bEndpointAddress =
++ acm_fs_out_desc.bEndpointAddress;
++ acm_hs_notify_desc.bEndpointAddress =
++ acm_fs_notify_desc.bEndpointAddress;
++
++ /* copy descriptors, and track endpoint copies */
++ f->hs_descriptors = usb_copy_descriptors(acm_hs_function);
++
++ acm->hs.in = usb_find_endpoint(acm_hs_function,
++ f->hs_descriptors, &acm_hs_in_desc);
++ acm->hs.out = usb_find_endpoint(acm_hs_function,
++ f->hs_descriptors, &acm_hs_out_desc);
++ acm->hs.notify = usb_find_endpoint(acm_hs_function,
++ f->hs_descriptors, &acm_hs_notify_desc);
++ }
++
++ DBG(cdev, "acm ttyGS%d: %s speed IN/%s OUT/%s NOTIFY/%s\n",
++ acm->port_num,
++ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
++ acm->port.in->name, acm->port.out->name,
++ acm->notify->name);
++ return 0;
++
++fail:
++ if (acm->notify_req)
++ gs_free_req(acm->notify, acm->notify_req);
++
++ /* we might as well release our claims on endpoints */
++ if (acm->notify)
++ acm->notify->driver_data = NULL;
++ if (acm->port.out)
++ acm->port.out->driver_data = NULL;
++ if (acm->port.in)
++ acm->port.in->driver_data = NULL;
++
++ ERROR(cdev, "%s/%p: can't bind, err %d\n", f->name, f, status);
++
++ return status;
++}
++
++static void
++acm_unbind(struct usb_configuration *c, struct usb_function *f)
++{
++ struct f_acm *acm = func_to_acm(f);
++
++ if (gadget_is_dualspeed(c->cdev->gadget))
++ usb_free_descriptors(f->hs_descriptors);
++ usb_free_descriptors(f->descriptors);
++ gs_free_req(acm->notify, acm->notify_req);
++ kfree(acm);
++}
++
++/* Some controllers can't support CDC ACM ... */
++static inline bool can_support_cdc(struct usb_configuration *c)
++{
++ /* SH3 doesn't support multiple interfaces */
++ if (gadget_is_sh(c->cdev->gadget))
++ return false;
++
++ /* sa1100 doesn't have a third interrupt endpoint */
++ if (gadget_is_sa1100(c->cdev->gadget))
++ return false;
++
++ /* everything else is *probably* fine ... */
++ return true;
++}
++
++/**
++ * acm_bind_config - add a CDC ACM function to a configuration
++ * @c: the configuration to support the CDC ACM instance
++ * @port_num: /dev/ttyGS* port this interface will use
++ * Context: single threaded during gadget setup
++ *
++ * Returns zero on success, else negative errno.
++ *
++ * Caller must have called @gserial_setup() with enough ports to
++ * handle all the ones it binds. Caller is also responsible
++ * for calling @gserial_cleanup() before module unload.
++ */
++int __init acm_bind_config(struct usb_configuration *c, u8 port_num)
++{
++ struct f_acm *acm;
++ int status;
++
++ if (!can_support_cdc(c))
++ return -EINVAL;
++
++ /* REVISIT might want instance-specific strings to help
++ * distinguish instances ...
++ */
++
++ /* maybe allocate device-global string IDs, and patch descriptors */
++ if (acm_string_defs[ACM_CTRL_IDX].id == 0) {
++ status = usb_string_id(c->cdev);
++ if (status < 0)
++ return status;
++ acm_string_defs[ACM_CTRL_IDX].id = status;
++
++ acm_control_interface_desc.iInterface = status;
++
++ status = usb_string_id(c->cdev);
++ if (status < 0)
++ return status;
++ acm_string_defs[ACM_DATA_IDX].id = status;
++
++ acm_data_interface_desc.iInterface = status;
++ }
++
++ /* allocate and initialize one new instance */
++ acm = kzalloc(sizeof *acm, GFP_KERNEL);
++ if (!acm)
++ return -ENOMEM;
++
++ spin_lock_init(&acm->lock);
++
++ acm->port_num = port_num;
++
++ acm->port.connect = acm_connect;
++ acm->port.disconnect = acm_disconnect;
++ acm->port.send_break = acm_send_break;
++
++ acm->port.func.name = "acm";
++ acm->port.func.strings = acm_strings;
++ /* descriptors are per-instance copies */
++ acm->port.func.bind = acm_bind;
++ acm->port.func.unbind = acm_unbind;
++ acm->port.func.set_alt = acm_set_alt;
++ acm->port.func.setup = acm_setup;
++ acm->port.func.disable = acm_disable;
++
++ status = usb_add_function(c, &acm->port.func);
++ if (status)
++ kfree(acm);
++ return status;
++}
+diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/f_ecm.c
+new file mode 100644
+index 0000000..a2b5c09
+--- /dev/null
++++ b/drivers/usb/gadget/f_ecm.c
+@@ -0,0 +1,831 @@
++/*
++ * f_ecm.c -- USB CDC Ethernet (ECM) link function driver
++ *
++ * Copyright (C) 2003-2005,2008 David Brownell
++ * Copyright (C) 2008 Nokia Corporation
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++/* #define VERBOSE_DEBUG */
++
++#include <linux/kernel.h>
++#include <linux/device.h>
++#include <linux/etherdevice.h>
++
++#include "u_ether.h"
++
++
++/*
++ * This function is a "CDC Ethernet Networking Control Model" (CDC ECM)
++ * Ethernet link. The data transfer model is simple (packets sent and
++ * received over bulk endpoints using normal short packet termination),
++ * and the control model exposes various data and optional notifications.
++ *
++ * ECM is well standardized and (except for Microsoft) supported by most
++ * operating systems with USB host support. It's the preferred interop
++ * solution for Ethernet over USB, at least for firmware based solutions.
++ * (Hardware solutions tend to be more minimalist.) A newer and simpler
++ * "Ethernet Emulation Model" (CDC EEM) hasn't yet caught on.
++ *
++ * Note that ECM requires the use of "alternate settings" for its data
++ * interface. This means that the set_alt() method has real work to do,
++ * and also means that a get_alt() method is required.
++ */
++
++struct ecm_ep_descs {
++ struct usb_endpoint_descriptor *in;
++ struct usb_endpoint_descriptor *out;
++ struct usb_endpoint_descriptor *notify;
++};
++
++enum ecm_notify_state {
++ ECM_NOTIFY_NONE, /* don't notify */
++ ECM_NOTIFY_CONNECT, /* issue CONNECT next */
++ ECM_NOTIFY_SPEED, /* issue SPEED_CHANGE next */
++};
++
++struct f_ecm {
++ struct gether port;
++ u8 ctrl_id, data_id;
++
++ char ethaddr[14];
++
++ struct ecm_ep_descs fs;
++ struct ecm_ep_descs hs;
++
++ struct usb_ep *notify;
++ struct usb_endpoint_descriptor *notify_desc;
++ struct usb_request *notify_req;
++ u8 notify_state;
++ bool is_open;
++
++ /* FIXME is_open needs some irq-ish locking
++ * ... possibly the same as port.ioport
++ */
++};
++
++static inline struct f_ecm *func_to_ecm(struct usb_function *f)
++{
++ return container_of(f, struct f_ecm, port.func);
++}
++
++/* peak (theoretical) bulk transfer rate in bits-per-second */
++static inline unsigned bitrate(struct usb_gadget *g)
++{
++ if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
++ return 13 * 512 * 8 * 1000 * 8;
++ else
++ return 19 * 64 * 1 * 1000 * 8;
++}
++
++/*-------------------------------------------------------------------------*/
++
++/*
++ * Include the status endpoint if we can, even though it's optional.
++ *
++ * Use wMaxPacketSize big enough to fit CDC_NOTIFY_SPEED_CHANGE in one
++ * packet, to simplify cancellation; and a big transfer interval, to
++ * waste less bandwidth.
++ *
++ * Some drivers (like Linux 2.4 cdc-ether!) "need" it to exist even
++ * if they ignore the connect/disconnect notifications that real aether
++ * can provide. More advanced cdc configurations might want to support
++ * encapsulated commands (vendor-specific, using control-OUT).
++ */
++
++#define LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */
++#define STATUS_BYTECOUNT 16 /* 8 byte header + data */
++
++
++/* interface descriptor: */
++
++static struct usb_interface_descriptor ecm_control_intf __initdata = {
++ .bLength = sizeof ecm_control_intf,
++ .bDescriptorType = USB_DT_INTERFACE,
++
++ /* .bInterfaceNumber = DYNAMIC */
++ /* status endpoint is optional; this could be patched later */
++ .bNumEndpoints = 1,
++ .bInterfaceClass = USB_CLASS_COMM,
++ .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET,
++ .bInterfaceProtocol = USB_CDC_PROTO_NONE,
++ /* .iInterface = DYNAMIC */
++};
++
++static struct usb_cdc_header_desc header_desc __initdata = {
++ .bLength = sizeof header_desc,
++ .bDescriptorType = USB_DT_CS_INTERFACE,
++ .bDescriptorSubType = USB_CDC_HEADER_TYPE,
++
++ .bcdCDC = __constant_cpu_to_le16(0x0110),
++};
++
++static struct usb_cdc_union_desc ecm_union_desc __initdata = {
++ .bLength = sizeof(ecm_union_desc),
++ .bDescriptorType = USB_DT_CS_INTERFACE,
++ .bDescriptorSubType = USB_CDC_UNION_TYPE,
++ /* .bMasterInterface0 = DYNAMIC */
++ /* .bSlaveInterface0 = DYNAMIC */
++};
++
++static struct usb_cdc_ether_desc ether_desc __initdata = {
++ .bLength = sizeof ether_desc,
++ .bDescriptorType = USB_DT_CS_INTERFACE,
++ .bDescriptorSubType = USB_CDC_ETHERNET_TYPE,
++
++ /* this descriptor actually adds value, surprise! */
++ /* .iMACAddress = DYNAMIC */
++ .bmEthernetStatistics = __constant_cpu_to_le32(0), /* no statistics */
++ .wMaxSegmentSize = __constant_cpu_to_le16(ETH_FRAME_LEN),
++ .wNumberMCFilters = __constant_cpu_to_le16(0),
++ .bNumberPowerFilters = 0,
++};
++
++/* the default data interface has no endpoints ... */
++
++static struct usb_interface_descriptor ecm_data_nop_intf __initdata = {
++ .bLength = sizeof ecm_data_nop_intf,
++ .bDescriptorType = USB_DT_INTERFACE,
++
++ .bInterfaceNumber = 1,
++ .bAlternateSetting = 0,
++ .bNumEndpoints = 0,
++ .bInterfaceClass = USB_CLASS_CDC_DATA,
++ .bInterfaceSubClass = 0,
++ .bInterfaceProtocol = 0,
++ /* .iInterface = DYNAMIC */
++};
++
++/* ... but the "real" data interface has two bulk endpoints */
++
++static struct usb_interface_descriptor ecm_data_intf __initdata = {
++ .bLength = sizeof ecm_data_intf,
++ .bDescriptorType = USB_DT_INTERFACE,
++
++ .bInterfaceNumber = 1,
++ .bAlternateSetting = 1,
++ .bNumEndpoints = 2,
++ .bInterfaceClass = USB_CLASS_CDC_DATA,
++ .bInterfaceSubClass = 0,
++ .bInterfaceProtocol = 0,
++ /* .iInterface = DYNAMIC */
++};
++
++/* full speed support: */
++
++static struct usb_endpoint_descriptor fs_notify_desc __initdata = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++
++ .bEndpointAddress = USB_DIR_IN,
++ .bmAttributes = USB_ENDPOINT_XFER_INT,
++ .wMaxPacketSize = __constant_cpu_to_le16(STATUS_BYTECOUNT),
++ .bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC,
++};
++
++static struct usb_endpoint_descriptor fs_in_desc __initdata = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++
++ .bEndpointAddress = USB_DIR_IN,
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++};
++
++static struct usb_endpoint_descriptor fs_out_desc __initdata = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++
++ .bEndpointAddress = USB_DIR_OUT,
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++};
++
++static struct usb_descriptor_header *eth_fs_function[] __initdata = {
++ /* CDC ECM control descriptors */
++ (struct usb_descriptor_header *) &ecm_control_intf,
++ (struct usb_descriptor_header *) &header_desc,
++ (struct usb_descriptor_header *) &ecm_union_desc,
++ (struct usb_descriptor_header *) &ether_desc,
++ /* NOTE: status endpoint might need to be removed */
++ (struct usb_descriptor_header *) &fs_notify_desc,
++ /* data interface, altsettings 0 and 1 */
++ (struct usb_descriptor_header *) &ecm_data_nop_intf,
++ (struct usb_descriptor_header *) &ecm_data_intf,
++ (struct usb_descriptor_header *) &fs_in_desc,
++ (struct usb_descriptor_header *) &fs_out_desc,
++ NULL,
++};
++
++/* high speed support: */
++
++static struct usb_endpoint_descriptor hs_notify_desc __initdata = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++
++ .bEndpointAddress = USB_DIR_IN,
++ .bmAttributes = USB_ENDPOINT_XFER_INT,
++ .wMaxPacketSize = __constant_cpu_to_le16(STATUS_BYTECOUNT),
++ .bInterval = LOG2_STATUS_INTERVAL_MSEC + 4,
++};
++static struct usb_endpoint_descriptor hs_in_desc __initdata = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++
++ .bEndpointAddress = USB_DIR_IN,
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++ .wMaxPacketSize = __constant_cpu_to_le16(512),
++};
++
++static struct usb_endpoint_descriptor hs_out_desc __initdata = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++
++ .bEndpointAddress = USB_DIR_OUT,
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++ .wMaxPacketSize = __constant_cpu_to_le16(512),
++};
++
++static struct usb_descriptor_header *eth_hs_function[] __initdata = {
++ /* CDC ECM control descriptors */
++ (struct usb_descriptor_header *) &ecm_control_intf,
++ (struct usb_descriptor_header *) &header_desc,
++ (struct usb_descriptor_header *) &ecm_union_desc,
++ (struct usb_descriptor_header *) &ether_desc,
++ /* NOTE: status endpoint might need to be removed */
++ (struct usb_descriptor_header *) &hs_notify_desc,
++ /* data interface, altsettings 0 and 1 */
++ (struct usb_descriptor_header *) &ecm_data_nop_intf,
++ (struct usb_descriptor_header *) &ecm_data_intf,
++ (struct usb_descriptor_header *) &hs_in_desc,
++ (struct usb_descriptor_header *) &hs_out_desc,
++ NULL,
++};
++
++/* string descriptors: */
++
++static struct usb_string ecm_string_defs[] = {
++ [0].s = "CDC Ethernet Control Model (ECM)",
++ [1].s = NULL /* DYNAMIC */,
++ [2].s = "CDC Ethernet Data",
++ { } /* end of list */
++};
++
++static struct usb_gadget_strings ecm_string_table = {
++ .language = 0x0409, /* en-us */
++ .strings = ecm_string_defs,
++};
++
++static struct usb_gadget_strings *ecm_strings[] = {
++ &ecm_string_table,
++ NULL,
++};
++
++/*-------------------------------------------------------------------------*/
++
++static void ecm_do_notify(struct f_ecm *ecm)
++{
++ struct usb_request *req = ecm->notify_req;
++ struct usb_cdc_notification *event;
++ struct usb_composite_dev *cdev = ecm->port.func.config->cdev;
++ __le32 *data;
++ int status;
++
++ /* notification already in flight? */
++ if (!req)
++ return;
++
++ event = req->buf;
++ switch (ecm->notify_state) {
++ case ECM_NOTIFY_NONE:
++ return;
++
++ case ECM_NOTIFY_CONNECT:
++ event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION;
++ if (ecm->is_open)
++ event->wValue = cpu_to_le16(1);
++ else
++ event->wValue = cpu_to_le16(0);
++ event->wLength = 0;
++ req->length = sizeof *event;
++
++ DBG(cdev, "notify connect %s\n",
++ ecm->is_open ? "true" : "false");
++ ecm->notify_state = ECM_NOTIFY_SPEED;
++ break;
++
++ case ECM_NOTIFY_SPEED:
++ event->bNotificationType = USB_CDC_NOTIFY_SPEED_CHANGE;
++ event->wValue = cpu_to_le16(0);
++ event->wLength = cpu_to_le16(8);
++ req->length = STATUS_BYTECOUNT;
++
++ /* SPEED_CHANGE data is up/down speeds in bits/sec */
++ data = req->buf + sizeof *event;
++ data[0] = cpu_to_le32(bitrate(cdev->gadget));
++ data[1] = data[0];
++
++ DBG(cdev, "notify speed %d\n", bitrate(cdev->gadget));
++ ecm->notify_state = ECM_NOTIFY_NONE;
++ break;
++ }
++ event->bmRequestType = 0xA1;
++ event->wIndex = cpu_to_le16(ecm->ctrl_id);
++
++ ecm->notify_req = NULL;
++ status = usb_ep_queue(ecm->notify, req, GFP_ATOMIC);
++ if (status < 0) {
++ ecm->notify_req = req;
++ DBG(cdev, "notify --> %d\n", status);
++ }
++}
++
++static void ecm_notify(struct f_ecm *ecm)
++{
++ /* NOTE on most versions of Linux, host side cdc-ethernet
++ * won't listen for notifications until its netdevice opens.
++ * The first notification then sits in the FIFO for a long
++ * time, and the second one is queued.
++ */
++ ecm->notify_state = ECM_NOTIFY_CONNECT;
++ ecm_do_notify(ecm);
++}
++
++static void ecm_notify_complete(struct usb_ep *ep, struct usb_request *req)
++{
++ struct f_ecm *ecm = req->context;
++ struct usb_composite_dev *cdev = ecm->port.func.config->cdev;
++ struct usb_cdc_notification *event = req->buf;
++
++ switch (req->status) {
++ case 0:
++ /* no fault */
++ break;
++ case -ECONNRESET:
++ case -ESHUTDOWN:
++ ecm->notify_state = ECM_NOTIFY_NONE;
++ break;
++ default:
++ DBG(cdev, "event %02x --> %d\n",
++ event->bNotificationType, req->status);
++ break;
++ }
++ ecm->notify_req = req;
++ ecm_do_notify(ecm);
++}
++
++static int ecm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
++{
++ struct f_ecm *ecm = func_to_ecm(f);
++ struct usb_composite_dev *cdev = f->config->cdev;
++ struct usb_request *req = cdev->req;
++ int value = -EOPNOTSUPP;
++ u16 w_index = le16_to_cpu(ctrl->wIndex);
++ u16 w_value = le16_to_cpu(ctrl->wValue);
++ u16 w_length = le16_to_cpu(ctrl->wLength);
++
++ /* composite driver infrastructure handles everything except
++ * CDC class messages; interface activation uses set_alt().
++ */
++ switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
++ case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
++ | USB_CDC_SET_ETHERNET_PACKET_FILTER:
++ /* see 6.2.30: no data, wIndex = interface,
++ * wValue = packet filter bitmap
++ */
++ if (w_length != 0 || w_index != ecm->ctrl_id)
++ goto invalid;
++ DBG(cdev, "packet filter %02x\n", w_value);
++ /* REVISIT locking of cdc_filter. This assumes the UDC
++ * driver won't have a concurrent packet TX irq running on
++ * another CPU; or that if it does, this write is atomic...
++ */
++ ecm->port.cdc_filter = w_value;
++ value = 0;
++ break;
++
++ /* and optionally:
++ * case USB_CDC_SEND_ENCAPSULATED_COMMAND:
++ * case USB_CDC_GET_ENCAPSULATED_RESPONSE:
++ * case USB_CDC_SET_ETHERNET_MULTICAST_FILTERS:
++ * case USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER:
++ * case USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER:
++ * case USB_CDC_GET_ETHERNET_STATISTIC:
++ */
++
++ default:
++invalid:
++ DBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
++ ctrl->bRequestType, ctrl->bRequest,
++ w_value, w_index, w_length);
++ }
++
++ /* respond with data transfer or status phase? */
++ if (value >= 0) {
++ DBG(cdev, "ecm req%02x.%02x v%04x i%04x l%d\n",
++ ctrl->bRequestType, ctrl->bRequest,
++ w_value, w_index, w_length);
++ req->zero = 0;
++ req->length = value;
++ value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
++ if (value < 0)
++ ERROR(cdev, "ecm req %02x.%02x response err %d\n",
++ ctrl->bRequestType, ctrl->bRequest,
++ value);
++ }
++
++ /* device either stalls (value < 0) or reports success */
++ return value;
++}
++
++
++static int ecm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
++{
++ struct f_ecm *ecm = func_to_ecm(f);
++ struct usb_composite_dev *cdev = f->config->cdev;
++
++ /* Control interface has only altsetting 0 */
++ if (intf == ecm->ctrl_id) {
++ if (alt != 0)
++ goto fail;
++
++ if (ecm->notify->driver_data) {
++ VDBG(cdev, "reset ecm control %d\n", intf);
++ usb_ep_disable(ecm->notify);
++ } else {
++ VDBG(cdev, "init ecm ctrl %d\n", intf);
++ ecm->notify_desc = ep_choose(cdev->gadget,
++ ecm->hs.notify,
++ ecm->fs.notify);
++ }
++ usb_ep_enable(ecm->notify, ecm->notify_desc);
++ ecm->notify->driver_data = ecm;
++
++ /* Data interface has two altsettings, 0 and 1 */
++ } else if (intf == ecm->data_id) {
++ if (alt > 1)
++ goto fail;
++
++ if (ecm->port.in_ep->driver_data) {
++ DBG(cdev, "reset ecm\n");
++ gether_disconnect(&ecm->port);
++ }
++
++ if (!ecm->port.in) {
++ DBG(cdev, "init ecm\n");
++ ecm->port.in = ep_choose(cdev->gadget,
++ ecm->hs.in, ecm->fs.in);
++ ecm->port.out = ep_choose(cdev->gadget,
++ ecm->hs.out, ecm->fs.out);
++ }
++
++ /* CDC Ethernet only sends data in non-default altsettings.
++ * Changing altsettings resets filters, statistics, etc.
++ */
++ if (alt == 1) {
++ struct net_device *net;
++
++ /* Enable zlps by default for ECM conformance;
++ * override for musb_hdrc (avoids txdma ovhead)
++ * and sa1100 (can't).
++ */
++ ecm->port.is_zlp_ok = !(
++ gadget_is_sa1100(cdev->gadget)
++ || gadget_is_musbhdrc(cdev->gadget)
++ );
++ ecm->port.cdc_filter = DEFAULT_FILTER;
++ DBG(cdev, "activate ecm\n");
++ net = gether_connect(&ecm->port);
++ if (IS_ERR(net))
++ return PTR_ERR(net);
++ }
++
++ /* NOTE this can be a minor disagreement with the ECM spec,
++ * which says speed notifications will "always" follow
++ * connection notifications. But we allow one connect to
++ * follow another (if the first is in flight), and instead
++ * just guarantee that a speed notification is always sent.
++ */
++ ecm_notify(ecm);
++ } else
++ goto fail;
++
++ return 0;
++fail:
++ return -EINVAL;
++}
++
++/* Because the data interface supports multiple altsettings,
++ * this ECM function *MUST* implement a get_alt() method.
++ */
++static int ecm_get_alt(struct usb_function *f, unsigned intf)
++{
++ struct f_ecm *ecm = func_to_ecm(f);
++
++ if (intf == ecm->ctrl_id)
++ return 0;
++ return ecm->port.in_ep->driver_data ? 1 : 0;
++}
++
++static void ecm_disable(struct usb_function *f)
++{
++ struct f_ecm *ecm = func_to_ecm(f);
++ struct usb_composite_dev *cdev = f->config->cdev;
++
++ DBG(cdev, "ecm deactivated\n");
++
++ if (ecm->port.in_ep->driver_data)
++ gether_disconnect(&ecm->port);
++
++ if (ecm->notify->driver_data) {
++ usb_ep_disable(ecm->notify);
++ ecm->notify->driver_data = NULL;
++ ecm->notify_desc = NULL;
++ }
++}
++
++/*-------------------------------------------------------------------------*/
++
++/*
++ * Callbacks let us notify the host about connect/disconnect when the
++ * net device is opened or closed.
++ *
++ * For testing, note that link states on this side include both opened
++ * and closed variants of:
++ *
++ * - disconnected/unconfigured
++ * - configured but inactive (data alt 0)
++ * - configured and active (data alt 1)
++ *
++ * Each needs to be tested with unplug, rmmod, SET_CONFIGURATION, and
++ * SET_INTERFACE (altsetting). Remember also that "configured" doesn't
++ * imply the host is actually polling the notification endpoint, and
++ * likewise that "active" doesn't imply it's actually using the data
++ * endpoints for traffic.
++ */
++
++static void ecm_open(struct gether *geth)
++{
++ struct f_ecm *ecm = func_to_ecm(&geth->func);
++
++ DBG(ecm->port.func.config->cdev, "%s\n", __func__);
++
++ ecm->is_open = true;
++ ecm_notify(ecm);
++}
++
++static void ecm_close(struct gether *geth)
++{
++ struct f_ecm *ecm = func_to_ecm(&geth->func);
++
++ DBG(ecm->port.func.config->cdev, "%s\n", __func__);
++
++ ecm->is_open = false;
++ ecm_notify(ecm);
++}
++
++/*-------------------------------------------------------------------------*/
++
++/* ethernet function driver setup/binding */
++
++static int __init
++ecm_bind(struct usb_configuration *c, struct usb_function *f)
++{
++ struct usb_composite_dev *cdev = c->cdev;
++ struct f_ecm *ecm = func_to_ecm(f);
++ int status;
++ struct usb_ep *ep;
++
++ /* allocate instance-specific interface IDs */
++ status = usb_interface_id(c, f);
++ if (status < 0)
++ goto fail;
++ ecm->ctrl_id = status;
++
++ ecm_control_intf.bInterfaceNumber = status;
++ ecm_union_desc.bMasterInterface0 = status;
++
++ status = usb_interface_id(c, f);
++ if (status < 0)
++ goto fail;
++ ecm->data_id = status;
++
++ ecm_data_nop_intf.bInterfaceNumber = status;
++ ecm_data_intf.bInterfaceNumber = status;
++ ecm_union_desc.bSlaveInterface0 = status;
++
++ status = -ENODEV;
++
++ /* allocate instance-specific endpoints */
++ ep = usb_ep_autoconfig(cdev->gadget, &fs_in_desc);
++ if (!ep)
++ goto fail;
++ ecm->port.in_ep = ep;
++ ep->driver_data = cdev; /* claim */
++
++ ep = usb_ep_autoconfig(cdev->gadget, &fs_out_desc);
++ if (!ep)
++ goto fail;
++ ecm->port.out_ep = ep;
++ ep->driver_data = cdev; /* claim */
++
++ /* NOTE: a status/notification endpoint is *OPTIONAL* but we
++ * don't treat it that way. It's simpler, and some newer CDC
++ * profiles (wireless handsets) no longer treat it as optional.
++ */
++ ep = usb_ep_autoconfig(cdev->gadget, &fs_notify_desc);
++ if (!ep)
++ goto fail;
++ ecm->notify = ep;
++ ep->driver_data = cdev; /* claim */
++
++ status = -ENOMEM;
++
++ /* allocate notification request and buffer */
++ ecm->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL);
++ if (!ecm->notify_req)
++ goto fail;
++ ecm->notify_req->buf = kmalloc(STATUS_BYTECOUNT, GFP_KERNEL);
++ if (!ecm->notify_req->buf)
++ goto fail;
++ ecm->notify_req->context = ecm;
++ ecm->notify_req->complete = ecm_notify_complete;
++
++ /* copy descriptors, and track endpoint copies */
++ f->descriptors = usb_copy_descriptors(eth_fs_function);
++ if (!f->descriptors)
++ goto fail;
++
++ ecm->fs.in = usb_find_endpoint(eth_fs_function,
++ f->descriptors, &fs_in_desc);
++ ecm->fs.out = usb_find_endpoint(eth_fs_function,
++ f->descriptors, &fs_out_desc);
++ ecm->fs.notify = usb_find_endpoint(eth_fs_function,
++ f->descriptors, &fs_notify_desc);
++
++ /* support all relevant hardware speeds... we expect that when
++ * hardware is dual speed, all bulk-capable endpoints work at
++ * both speeds
++ */
++ if (gadget_is_dualspeed(c->cdev->gadget)) {
++ hs_in_desc.bEndpointAddress =
++ fs_in_desc.bEndpointAddress;
++ hs_out_desc.bEndpointAddress =
++ fs_out_desc.bEndpointAddress;
++ hs_notify_desc.bEndpointAddress =
++ fs_notify_desc.bEndpointAddress;
++
++ /* copy descriptors, and track endpoint copies */
++ f->hs_descriptors = usb_copy_descriptors(eth_hs_function);
++ if (!f->hs_descriptors)
++ goto fail;
++
++ ecm->hs.in = usb_find_endpoint(eth_hs_function,
++ f->hs_descriptors, &hs_in_desc);
++ ecm->hs.out = usb_find_endpoint(eth_hs_function,
++ f->hs_descriptors, &hs_out_desc);
++ ecm->hs.notify = usb_find_endpoint(eth_hs_function,
++ f->hs_descriptors, &hs_notify_desc);
++ }
++
++ /* NOTE: all that is done without knowing or caring about
++ * the network link ... which is unavailable to this code
++ * until we're activated via set_alt().
++ */
++
++ ecm->port.open = ecm_open;
++ ecm->port.close = ecm_close;
++
++ DBG(cdev, "CDC Ethernet: %s speed IN/%s OUT/%s NOTIFY/%s\n",
++ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
++ ecm->port.in_ep->name, ecm->port.out_ep->name,
++ ecm->notify->name);
++ return 0;
++
++fail:
++ if (f->descriptors)
++ usb_free_descriptors(f->descriptors);
++
++ if (ecm->notify_req) {
++ kfree(ecm->notify_req->buf);
++ usb_ep_free_request(ecm->notify, ecm->notify_req);
++ }
++
++ /* we might as well release our claims on endpoints */
++ if (ecm->notify)
++ ecm->notify->driver_data = NULL;
++ if (ecm->port.out)
++ ecm->port.out_ep->driver_data = NULL;
++ if (ecm->port.in)
++ ecm->port.in_ep->driver_data = NULL;
++
++ ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
++
++ return status;
++}
++
++static void
++ecm_unbind(struct usb_configuration *c, struct usb_function *f)
++{
++ struct f_ecm *ecm = func_to_ecm(f);
++
++ DBG(c->cdev, "ecm unbind\n");
++
++ if (gadget_is_dualspeed(c->cdev->gadget))
++ usb_free_descriptors(f->hs_descriptors);
++ usb_free_descriptors(f->descriptors);
++
++ kfree(ecm->notify_req->buf);
++ usb_ep_free_request(ecm->notify, ecm->notify_req);
++
++ ecm_string_defs[1].s = NULL;
++ kfree(ecm);
++}
++
++/**
++ * ecm_bind_config - add CDC Ethernet network link to a configuration
++ * @c: the configuration to support the network link
++ * @ethaddr: a buffer in which the ethernet address of the host side
++ * side of the link was recorded
++ * Context: single threaded during gadget setup
++ *
++ * Returns zero on success, else negative errno.
++ *
++ * Caller must have called @gether_setup(). Caller is also responsible
++ * for calling @gether_cleanup() before module unload.
++ */
++int __init ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
++{
++ struct f_ecm *ecm;
++ int status;
++
++ if (!can_support_ecm(c->cdev->gadget) || !ethaddr)
++ return -EINVAL;
++
++ /* maybe allocate device-global string IDs */
++ if (ecm_string_defs[0].id == 0) {
++
++ /* control interface label */
++ status = usb_string_id(c->cdev);
++ if (status < 0)
++ return status;
++ ecm_string_defs[0].id = status;
++ ecm_control_intf.iInterface = status;
++
++ /* data interface label */
++ status = usb_string_id(c->cdev);
++ if (status < 0)
++ return status;
++ ecm_string_defs[2].id = status;
++ ecm_data_intf.iInterface = status;
++
++ /* MAC address */
++ status = usb_string_id(c->cdev);
++ if (status < 0)
++ return status;
++ ecm_string_defs[1].id = status;
++ ether_desc.iMACAddress = status;
++ }
++
++ /* allocate and initialize one new instance */
++ ecm = kzalloc(sizeof *ecm, GFP_KERNEL);
++ if (!ecm)
++ return -ENOMEM;
++
++ /* export host's Ethernet address in CDC format */
++ snprintf(ecm->ethaddr, sizeof ecm->ethaddr,
++ "%02X%02X%02X%02X%02X%02X",
++ ethaddr[0], ethaddr[1], ethaddr[2],
++ ethaddr[3], ethaddr[4], ethaddr[5]);
++ ecm_string_defs[1].s = ecm->ethaddr;
++
++ ecm->port.cdc_filter = DEFAULT_FILTER;
++
++ ecm->port.func.name = "cdc_ethernet";
++ ecm->port.func.strings = ecm_strings;
++ /* descriptors are per-instance copies */
++ ecm->port.func.bind = ecm_bind;
++ ecm->port.func.unbind = ecm_unbind;
++ ecm->port.func.set_alt = ecm_set_alt;
++ ecm->port.func.get_alt = ecm_get_alt;
++ ecm->port.func.setup = ecm_setup;
++ ecm->port.func.disable = ecm_disable;
++
++ status = usb_add_function(c, &ecm->port.func);
++ if (status) {
++ ecm_string_defs[1].s = NULL;
++ kfree(ecm);
++ }
++ return status;
++}
+diff --git a/drivers/usb/gadget/f_loopback.c b/drivers/usb/gadget/f_loopback.c
+new file mode 100644
+index 0000000..eda4cde
+--- /dev/null
++++ b/drivers/usb/gadget/f_loopback.c
+@@ -0,0 +1,381 @@
++/*
++ * f_loopback.c - USB peripheral loopback configuration driver
++ *
++ * Copyright (C) 2003-2008 David Brownell
++ * Copyright (C) 2008 by Nokia Corporation
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++/* #define VERBOSE_DEBUG */
++
++#include <linux/kernel.h>
++#include <linux/utsname.h>
++#include <linux/device.h>
++
++#include "g_zero.h"
++#include "gadget_chips.h"
++
++
++/*
++ * LOOPBACK FUNCTION ... a testing vehicle for USB peripherals,
++ *
++ * This takes messages of various sizes written OUT to a device, and loops
++ * them back so they can be read IN from it. It has been used by certain
++ * test applications. It supports limited testing of data queueing logic.
++ *
++ *
++ * This is currently packaged as a configuration driver, which can't be
++ * combined with other functions to make composite devices. However, it
++ * can be combined with other independent configurations.
++ */
++struct f_loopback {
++ struct usb_function function;
++
++ struct usb_ep *in_ep;
++ struct usb_ep *out_ep;
++};
++
++static inline struct f_loopback *func_to_loop(struct usb_function *f)
++{
++ return container_of(f, struct f_loopback, function);
++}
++
++static unsigned qlen = 32;
++module_param(qlen, uint, 0);
++MODULE_PARM_DESC(qlenn, "depth of loopback queue");
++
++/*-------------------------------------------------------------------------*/
++
++static struct usb_interface_descriptor loopback_intf = {
++ .bLength = sizeof loopback_intf,
++ .bDescriptorType = USB_DT_INTERFACE,
++
++ .bNumEndpoints = 2,
++ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
++ /* .iInterface = DYNAMIC */
++};
++
++/* full speed support: */
++
++static struct usb_endpoint_descriptor fs_source_desc = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++
++ .bEndpointAddress = USB_DIR_IN,
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++};
++
++static struct usb_endpoint_descriptor fs_sink_desc = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++
++ .bEndpointAddress = USB_DIR_OUT,
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++};
++
++static struct usb_descriptor_header *fs_loopback_descs[] = {
++ (struct usb_descriptor_header *) &loopback_intf,
++ (struct usb_descriptor_header *) &fs_sink_desc,
++ (struct usb_descriptor_header *) &fs_source_desc,
++ NULL,
++};
++
++/* high speed support: */
++
++static struct usb_endpoint_descriptor hs_source_desc = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++ .wMaxPacketSize = __constant_cpu_to_le16(512),
++};
++
++static struct usb_endpoint_descriptor hs_sink_desc = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++ .wMaxPacketSize = __constant_cpu_to_le16(512),
++};
++
++static struct usb_descriptor_header *hs_loopback_descs[] = {
++ (struct usb_descriptor_header *) &loopback_intf,
++ (struct usb_descriptor_header *) &hs_source_desc,
++ (struct usb_descriptor_header *) &hs_sink_desc,
++ NULL,
++};
++
++/* function-specific strings: */
++
++static struct usb_string strings_loopback[] = {
++ [0].s = "loop input to output",
++ { } /* end of list */
++};
++
++static struct usb_gadget_strings stringtab_loop = {
++ .language = 0x0409, /* en-us */
++ .strings = strings_loopback,
++};
++
++static struct usb_gadget_strings *loopback_strings[] = {
++ &stringtab_loop,
++ NULL,
++};
++
++/*-------------------------------------------------------------------------*/
++
++static int __init
++loopback_bind(struct usb_configuration *c, struct usb_function *f)
++{
++ struct usb_composite_dev *cdev = c->cdev;
++ struct f_loopback *loop = func_to_loop(f);
++ int id;
++
++ /* allocate interface ID(s) */
++ id = usb_interface_id(c, f);
++ if (id < 0)
++ return id;
++ loopback_intf.bInterfaceNumber = id;
++
++ /* allocate endpoints */
++
++ loop->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc);
++ if (!loop->in_ep) {
++autoconf_fail:
++ ERROR(cdev, "%s: can't autoconfigure on %s\n",
++ f->name, cdev->gadget->name);
++ return -ENODEV;
++ }
++ loop->in_ep->driver_data = cdev; /* claim */
++
++ loop->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_sink_desc);
++ if (!loop->out_ep)
++ goto autoconf_fail;
++ loop->out_ep->driver_data = cdev; /* claim */
++
++ /* support high speed hardware */
++ if (gadget_is_dualspeed(c->cdev->gadget)) {
++ hs_source_desc.bEndpointAddress =
++ fs_source_desc.bEndpointAddress;
++ hs_sink_desc.bEndpointAddress =
++ fs_sink_desc.bEndpointAddress;
++ f->hs_descriptors = hs_loopback_descs;
++ }
++
++ DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
++ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
++ f->name, loop->in_ep->name, loop->out_ep->name);
++ return 0;
++}
++
++static void
++loopback_unbind(struct usb_configuration *c, struct usb_function *f)
++{
++ kfree(func_to_loop(f));
++}
++
++static void loopback_complete(struct usb_ep *ep, struct usb_request *req)
++{
++ struct f_loopback *loop = ep->driver_data;
++ struct usb_composite_dev *cdev = loop->function.config->cdev;
++ int status = req->status;
++
++ switch (status) {
++
++ case 0: /* normal completion? */
++ if (ep == loop->out_ep) {
++ /* loop this OUT packet back IN to the host */
++ req->zero = (req->actual < req->length);
++ req->length = req->actual;
++ status = usb_ep_queue(loop->in_ep, req, GFP_ATOMIC);
++ if (status == 0)
++ return;
++
++ /* "should never get here" */
++ ERROR(cdev, "can't loop %s to %s: %d\n",
++ ep->name, loop->in_ep->name,
++ status);
++ }
++
++ /* queue the buffer for some later OUT packet */
++ req->length = buflen;
++ status = usb_ep_queue(loop->out_ep, req, GFP_ATOMIC);
++ if (status == 0)
++ return;
++
++ /* "should never get here" */
++ /* FALLTHROUGH */
++
++ default:
++ ERROR(cdev, "%s loop complete --> %d, %d/%d\n", ep->name,
++ status, req->actual, req->length);
++ /* FALLTHROUGH */
++
++ /* NOTE: since this driver doesn't maintain an explicit record
++ * of requests it submitted (just maintains qlen count), we
++ * rely on the hardware driver to clean up on disconnect or
++ * endpoint disable.
++ */
++ case -ECONNABORTED: /* hardware forced ep reset */
++ case -ECONNRESET: /* request dequeued */
++ case -ESHUTDOWN: /* disconnect from host */
++ free_ep_req(ep, req);
++ return;
++ }
++}
++
++static void disable_loopback(struct f_loopback *loop)
++{
++ struct usb_composite_dev *cdev;
++
++ cdev = loop->function.config->cdev;
++ disable_endpoints(cdev, loop->in_ep, loop->out_ep);
++ VDBG(cdev, "%s disabled\n", loop->function.name);
++}
++
++static int
++enable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop)
++{
++ int result = 0;
++ const struct usb_endpoint_descriptor *src, *sink;
++ struct usb_ep *ep;
++ struct usb_request *req;
++ unsigned i;
++
++ src = ep_choose(cdev->gadget, &hs_source_desc, &fs_source_desc);
++ sink = ep_choose(cdev->gadget, &hs_sink_desc, &fs_sink_desc);
++
++ /* one endpoint writes data back IN to the host */
++ ep = loop->in_ep;
++ result = usb_ep_enable(ep, src);
++ if (result < 0)
++ return result;
++ ep->driver_data = loop;
++
++ /* one endpoint just reads OUT packets */
++ ep = loop->out_ep;
++ result = usb_ep_enable(ep, sink);
++ if (result < 0) {
++fail0:
++ ep = loop->in_ep;
++ usb_ep_disable(ep);
++ ep->driver_data = NULL;
++ return result;
++ }
++ ep->driver_data = loop;
++
++ /* allocate a bunch of read buffers and queue them all at once.
++ * we buffer at most 'qlen' transfers; fewer if any need more
++ * than 'buflen' bytes each.
++ */
++ for (i = 0; i < qlen && result == 0; i++) {
++ req = alloc_ep_req(ep);
++ if (req) {
++ req->complete = loopback_complete;
++ result = usb_ep_queue(ep, req, GFP_ATOMIC);
++ if (result)
++ ERROR(cdev, "%s queue req --> %d\n",
++ ep->name, result);
++ } else {
++ usb_ep_disable(ep);
++ ep->driver_data = NULL;
++ result = -ENOMEM;
++ goto fail0;
++ }
++ }
++
++ DBG(cdev, "%s enabled\n", loop->function.name);
++ return result;
++}
++
++static int loopback_set_alt(struct usb_function *f,
++ unsigned intf, unsigned alt)
++{
++ struct f_loopback *loop = func_to_loop(f);
++ struct usb_composite_dev *cdev = f->config->cdev;
++
++ /* we know alt is zero */
++ if (loop->in_ep->driver_data)
++ disable_loopback(loop);
++ return enable_loopback(cdev, loop);
++}
++
++static void loopback_disable(struct usb_function *f)
++{
++ struct f_loopback *loop = func_to_loop(f);
++
++ disable_loopback(loop);
++}
++
++/*-------------------------------------------------------------------------*/
++
++static int __init loopback_bind_config(struct usb_configuration *c)
++{
++ struct f_loopback *loop;
++ int status;
++
++ loop = kzalloc(sizeof *loop, GFP_KERNEL);
++ if (!loop)
++ return -ENOMEM;
++
++ loop->function.name = "loopback";
++ loop->function.descriptors = fs_loopback_descs;
++ loop->function.bind = loopback_bind;
++ loop->function.unbind = loopback_unbind;
++ loop->function.set_alt = loopback_set_alt;
++ loop->function.disable = loopback_disable;
++
++ status = usb_add_function(c, &loop->function);
++ if (status)
++ kfree(loop);
++ return status;
++}
++
++static struct usb_configuration loopback_driver = {
++ .label = "loopback",
++ .strings = loopback_strings,
++ .bind = loopback_bind_config,
++ .bConfigurationValue = 2,
++ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
++ .bMaxPower = 1, /* 2 mA, minimal */
++ /* .iConfiguration = DYNAMIC */
++};
++
++/**
++ * loopback_add - add a loopback testing configuration to a device
++ * @cdev: the device to support the loopback configuration
++ */
++int __init loopback_add(struct usb_composite_dev *cdev)
++{
++ int id;
++
++ /* allocate string ID(s) */
++ id = usb_string_id(cdev);
++ if (id < 0)
++ return id;
++ strings_loopback[0].id = id;
++
++ loopback_intf.iInterface = id;
++ loopback_driver.iConfiguration = id;
++
++ /* support OTG systems */
++ if (gadget_is_otg(cdev->gadget)) {
++ loopback_driver.descriptors = otg_desc;
++ loopback_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
++ }
++
++ return usb_add_config(cdev, &loopback_driver);
++}
+diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c
+new file mode 100644
+index 0000000..659b3d9
+--- /dev/null
++++ b/drivers/usb/gadget/f_rndis.c
+@@ -0,0 +1,825 @@
++/*
++ * f_rndis.c -- RNDIS link function driver
++ *
++ * Copyright (C) 2003-2005,2008 David Brownell
++ * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
++ * Copyright (C) 2008 Nokia Corporation
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++/* #define VERBOSE_DEBUG */
++
++#include <linux/kernel.h>
++#include <linux/device.h>
++#include <linux/etherdevice.h>
++
++#include <asm/atomic.h>
++
++#include "u_ether.h"
++#include "rndis.h"
++
++
++/*
++ * This function is an RNDIS Ethernet port -- a Microsoft protocol that's
++ * been promoted instead of the standard CDC Ethernet. The published RNDIS
++ * spec is ambiguous, incomplete, and needlessly complex. Variants such as
++ * ActiveSync have even worse status in terms of specification.
++ *
++ * In short: it's a protocol controlled by (and for) Microsoft, not for an
++ * Open ecosystem or markets. Linux supports it *only* because Microsoft
++ * doesn't support the CDC Ethernet standard.
++ *
++ * The RNDIS data transfer model is complex, with multiple Ethernet packets
++ * per USB message, and out of band data. The control model is built around
++ * what's essentially an "RNDIS RPC" protocol. It's all wrapped in a CDC ACM
++ * (modem, not Ethernet) veneer, with those ACM descriptors being entirely
++ * useless (they're ignored). RNDIS expects to be the only function in its
++ * configuration, so it's no real help if you need composite devices; and
++ * it expects to be the first configuration too.
++ *
++ * There is a single technical advantage of RNDIS over CDC Ethernet, if you
++ * discount the fluff that its RPC can be made to deliver: it doesn't need
++ * a NOP altsetting for the data interface. That lets it work on some of the
++ * "so smart it's stupid" hardware which takes over configuration changes
++ * from the software, and adds restrictions like "no altsettings".
++ *
++ * Unfortunately MSFT's RNDIS drivers are buggy. They hang or oops, and
++ * have all sorts of contrary-to-specification oddities that can prevent
++ * them from working sanely. Since bugfixes (or accurate specs, letting
++ * Linux work around those bugs) are unlikely to ever come from MSFT, you
++ * may want to avoid using RNDIS on purely operational grounds.
++ *
++ * Omissions from the RNDIS 1.0 specification include:
++ *
++ * - Power management ... references data that's scattered around lots
++ * of other documentation, which is incorrect/incomplete there too.
++ *
++ * - There are various undocumented protocol requirements, like the need
++ * to send garbage in some control-OUT messages.
++ *
++ * - MS-Windows drivers sometimes emit undocumented requests.
++ */
++
++struct rndis_ep_descs {
++ struct usb_endpoint_descriptor *in;
++ struct usb_endpoint_descriptor *out;
++ struct usb_endpoint_descriptor *notify;
++};
++
++struct f_rndis {
++ struct gether port;
++ u8 ctrl_id, data_id;
++ u8 ethaddr[ETH_ALEN];
++ int config;
++
++ struct rndis_ep_descs fs;
++ struct rndis_ep_descs hs;
++
++ struct usb_ep *notify;
++ struct usb_endpoint_descriptor *notify_desc;
++ struct usb_request *notify_req;
++ atomic_t notify_count;
++};
++
++static inline struct f_rndis *func_to_rndis(struct usb_function *f)
++{
++ return container_of(f, struct f_rndis, port.func);
++}
++
++/* peak (theoretical) bulk transfer rate in bits-per-second */
++static unsigned int bitrate(struct usb_gadget *g)
++{
++ if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
++ return 13 * 512 * 8 * 1000 * 8;
++ else
++ return 19 * 64 * 1 * 1000 * 8;
++}
++
++/*-------------------------------------------------------------------------*/
++
++/*
++ */
++
++#define LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */
++#define STATUS_BYTECOUNT 8 /* 8 bytes data */
++
++
++/* interface descriptor: */
++
++static struct usb_interface_descriptor rndis_control_intf __initdata = {
++ .bLength = sizeof rndis_control_intf,
++ .bDescriptorType = USB_DT_INTERFACE,
++
++ /* .bInterfaceNumber = DYNAMIC */
++ /* status endpoint is optional; this could be patched later */
++ .bNumEndpoints = 1,
++ .bInterfaceClass = USB_CLASS_COMM,
++ .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
++ .bInterfaceProtocol = USB_CDC_ACM_PROTO_VENDOR,
++ /* .iInterface = DYNAMIC */
++};
++
++static struct usb_cdc_header_desc header_desc __initdata = {
++ .bLength = sizeof header_desc,
++ .bDescriptorType = USB_DT_CS_INTERFACE,
++ .bDescriptorSubType = USB_CDC_HEADER_TYPE,
++
++ .bcdCDC = __constant_cpu_to_le16(0x0110),
++};
++
++static struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor __initdata = {
++ .bLength = sizeof call_mgmt_descriptor,
++ .bDescriptorType = USB_DT_CS_INTERFACE,
++ .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE,
++
++ .bmCapabilities = 0x00,
++ .bDataInterface = 0x01,
++};
++
++static struct usb_cdc_acm_descriptor acm_descriptor __initdata = {
++ .bLength = sizeof acm_descriptor,
++ .bDescriptorType = USB_DT_CS_INTERFACE,
++ .bDescriptorSubType = USB_CDC_ACM_TYPE,
++
++ .bmCapabilities = 0x00,
++};
++
++static struct usb_cdc_union_desc rndis_union_desc __initdata = {
++ .bLength = sizeof(rndis_union_desc),
++ .bDescriptorType = USB_DT_CS_INTERFACE,
++ .bDescriptorSubType = USB_CDC_UNION_TYPE,
++ /* .bMasterInterface0 = DYNAMIC */
++ /* .bSlaveInterface0 = DYNAMIC */
++};
++
++/* the data interface has two bulk endpoints */
++
++static struct usb_interface_descriptor rndis_data_intf __initdata = {
++ .bLength = sizeof rndis_data_intf,
++ .bDescriptorType = USB_DT_INTERFACE,
++
++ /* .bInterfaceNumber = DYNAMIC */
++ .bAlternateSetting = 1,
++ .bNumEndpoints = 2,
++ .bInterfaceClass = USB_CLASS_CDC_DATA,
++ .bInterfaceSubClass = 0,
++ .bInterfaceProtocol = 0,
++ /* .iInterface = DYNAMIC */
++};
++
++/* full speed support: */
++
++static struct usb_endpoint_descriptor fs_notify_desc __initdata = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++
++ .bEndpointAddress = USB_DIR_IN,
++ .bmAttributes = USB_ENDPOINT_XFER_INT,
++ .wMaxPacketSize = __constant_cpu_to_le16(STATUS_BYTECOUNT),
++ .bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC,
++};
++
++static struct usb_endpoint_descriptor fs_in_desc __initdata = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++
++ .bEndpointAddress = USB_DIR_IN,
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++};
++
++static struct usb_endpoint_descriptor fs_out_desc __initdata = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++
++ .bEndpointAddress = USB_DIR_OUT,
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++};
++
++static struct usb_descriptor_header *eth_fs_function[] __initdata = {
++ /* control interface matches ACM, not Ethernet */
++ (struct usb_descriptor_header *) &rndis_control_intf,
++ (struct usb_descriptor_header *) &header_desc,
++ (struct usb_descriptor_header *) &call_mgmt_descriptor,
++ (struct usb_descriptor_header *) &acm_descriptor,
++ (struct usb_descriptor_header *) &rndis_union_desc,
++ (struct usb_descriptor_header *) &fs_notify_desc,
++ /* data interface has no altsetting */
++ (struct usb_descriptor_header *) &rndis_data_intf,
++ (struct usb_descriptor_header *) &fs_in_desc,
++ (struct usb_descriptor_header *) &fs_out_desc,
++ NULL,
++};
++
++/* high speed support: */
++
++static struct usb_endpoint_descriptor hs_notify_desc __initdata = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++
++ .bEndpointAddress = USB_DIR_IN,
++ .bmAttributes = USB_ENDPOINT_XFER_INT,
++ .wMaxPacketSize = __constant_cpu_to_le16(STATUS_BYTECOUNT),
++ .bInterval = LOG2_STATUS_INTERVAL_MSEC + 4,
++};
++static struct usb_endpoint_descriptor hs_in_desc __initdata = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++
++ .bEndpointAddress = USB_DIR_IN,
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++ .wMaxPacketSize = __constant_cpu_to_le16(512),
++};
++
++static struct usb_endpoint_descriptor hs_out_desc __initdata = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++
++ .bEndpointAddress = USB_DIR_OUT,
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++ .wMaxPacketSize = __constant_cpu_to_le16(512),
++};
++
++static struct usb_descriptor_header *eth_hs_function[] __initdata = {
++ /* control interface matches ACM, not Ethernet */
++ (struct usb_descriptor_header *) &rndis_control_intf,
++ (struct usb_descriptor_header *) &header_desc,
++ (struct usb_descriptor_header *) &call_mgmt_descriptor,
++ (struct usb_descriptor_header *) &acm_descriptor,
++ (struct usb_descriptor_header *) &rndis_union_desc,
++ (struct usb_descriptor_header *) &hs_notify_desc,
++ /* data interface has no altsetting */
++ (struct usb_descriptor_header *) &rndis_data_intf,
++ (struct usb_descriptor_header *) &hs_in_desc,
++ (struct usb_descriptor_header *) &hs_out_desc,
++ NULL,
++};
++
++/* string descriptors: */
++
++static struct usb_string rndis_string_defs[] = {
++ [0].s = "RNDIS Communications Control",
++ [1].s = "RNDIS Ethernet Data",
++ { } /* end of list */
++};
++
++static struct usb_gadget_strings rndis_string_table = {
++ .language = 0x0409, /* en-us */
++ .strings = rndis_string_defs,
++};
++
++static struct usb_gadget_strings *rndis_strings[] = {
++ &rndis_string_table,
++ NULL,
++};
++
++/*-------------------------------------------------------------------------*/
++
++static struct sk_buff *rndis_add_header(struct sk_buff *skb)
++{
++ skb = skb_realloc_headroom(skb, sizeof(struct rndis_packet_msg_type));
++ if (skb)
++ rndis_add_hdr(skb);
++ return skb;
++}
++
++static void rndis_response_available(void *_rndis)
++{
++ struct f_rndis *rndis = _rndis;
++ struct usb_request *req = rndis->notify_req;
++ struct usb_composite_dev *cdev = rndis->port.func.config->cdev;
++ __le32 *data = req->buf;
++ int status;
++
++ if (atomic_inc_return(&rndis->notify_count))
++ return;
++
++ /* Send RNDIS RESPONSE_AVAILABLE notification; a
++ * USB_CDC_NOTIFY_RESPONSE_AVAILABLE "should" work too
++ *
++ * This is the only notification defined by RNDIS.
++ */
++ data[0] = cpu_to_le32(1);
++ data[1] = cpu_to_le32(0);
++
++ status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC);
++ if (status) {
++ atomic_dec(&rndis->notify_count);
++ DBG(cdev, "notify/0 --> %d\n", status);
++ }
++}
++
++static void rndis_response_complete(struct usb_ep *ep, struct usb_request *req)
++{
++ struct f_rndis *rndis = req->context;
++ struct usb_composite_dev *cdev = rndis->port.func.config->cdev;
++ int status = req->status;
++
++ /* after TX:
++ * - USB_CDC_GET_ENCAPSULATED_RESPONSE (ep0/control)
++ * - RNDIS_RESPONSE_AVAILABLE (status/irq)
++ */
++ switch (status) {
++ case -ECONNRESET:
++ case -ESHUTDOWN:
++ /* connection gone */
++ atomic_set(&rndis->notify_count, 0);
++ break;
++ default:
++ DBG(cdev, "RNDIS %s response error %d, %d/%d\n",
++ ep->name, status,
++ req->actual, req->length);
++ /* FALLTHROUGH */
++ case 0:
++ if (ep != rndis->notify)
++ break;
++
++ /* handle multiple pending RNDIS_RESPONSE_AVAILABLE
++ * notifications by resending until we're done
++ */
++ if (atomic_dec_and_test(&rndis->notify_count))
++ break;
++ status = usb_ep_queue(rndis->notify, req, GFP_ATOMIC);
++ if (status) {
++ atomic_dec(&rndis->notify_count);
++ DBG(cdev, "notify/1 --> %d\n", status);
++ }
++ break;
++ }
++}
++
++static void rndis_command_complete(struct usb_ep *ep, struct usb_request *req)
++{
++ struct f_rndis *rndis = req->context;
++ struct usb_composite_dev *cdev = rndis->port.func.config->cdev;
++ int status;
++
++ /* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */
++// spin_lock(&dev->lock);
++ status = rndis_msg_parser(rndis->config, (u8 *) req->buf);
++ if (status < 0)
++ ERROR(cdev, "RNDIS command error %d, %d/%d\n",
++ status, req->actual, req->length);
++// spin_unlock(&dev->lock);
++}
++
++static int
++rndis_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
++{
++ struct f_rndis *rndis = func_to_rndis(f);
++ struct usb_composite_dev *cdev = f->config->cdev;
++ struct usb_request *req = cdev->req;
++ int value = -EOPNOTSUPP;
++ u16 w_index = le16_to_cpu(ctrl->wIndex);
++ u16 w_value = le16_to_cpu(ctrl->wValue);
++ u16 w_length = le16_to_cpu(ctrl->wLength);
++
++ /* composite driver infrastructure handles everything except
++ * CDC class messages; interface activation uses set_alt().
++ */
++ switch ((ctrl->bRequestType << 8) | ctrl->bRequest) {
++
++ /* RNDIS uses the CDC command encapsulation mechanism to implement
++ * an RPC scheme, with much getting/setting of attributes by OID.
++ */
++ case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
++ | USB_CDC_SEND_ENCAPSULATED_COMMAND:
++ if (w_length > req->length || w_value
++ || w_index != rndis->ctrl_id)
++ goto invalid;
++ /* read the request; process it later */
++ value = w_length;
++ req->complete = rndis_command_complete;
++ req->context = rndis;
++ /* later, rndis_response_available() sends a notification */
++ break;
++
++ case ((USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
++ | USB_CDC_GET_ENCAPSULATED_RESPONSE:
++ if (w_value || w_index != rndis->ctrl_id)
++ goto invalid;
++ else {
++ u8 *buf;
++ u32 n;
++
++ /* return the result */
++ buf = rndis_get_next_response(rndis->config, &n);
++ if (buf) {
++ memcpy(req->buf, buf, n);
++ req->complete = rndis_response_complete;
++ rndis_free_response(rndis->config, buf);
++ value = n;
++ }
++ /* else stalls ... spec says to avoid that */
++ }
++ break;
++
++ default:
++invalid:
++ VDBG(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n",
++ ctrl->bRequestType, ctrl->bRequest,
++ w_value, w_index, w_length);
++ }
++
++ /* respond with data transfer or status phase? */
++ if (value >= 0) {
++ DBG(cdev, "rndis req%02x.%02x v%04x i%04x l%d\n",
++ ctrl->bRequestType, ctrl->bRequest,
++ w_value, w_index, w_length);
++ req->zero = 0;
++ req->length = value;
++ value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
++ if (value < 0)
++ ERROR(cdev, "rndis response on err %d\n", value);
++ }
++
++ /* device either stalls (value < 0) or reports success */
++ return value;
++}
++
++
++static int rndis_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
++{
++ struct f_rndis *rndis = func_to_rndis(f);
++ struct usb_composite_dev *cdev = f->config->cdev;
++
++ /* we know alt == 0 */
++
++ if (intf == rndis->ctrl_id) {
++ if (rndis->notify->driver_data) {
++ VDBG(cdev, "reset rndis control %d\n", intf);
++ usb_ep_disable(rndis->notify);
++ } else {
++ VDBG(cdev, "init rndis ctrl %d\n", intf);
++ rndis->notify_desc = ep_choose(cdev->gadget,
++ rndis->hs.notify,
++ rndis->fs.notify);
++ }
++ usb_ep_enable(rndis->notify, rndis->notify_desc);
++ rndis->notify->driver_data = rndis;
++
++ } else if (intf == rndis->data_id) {
++ struct net_device *net;
++
++ if (rndis->port.in_ep->driver_data) {
++ DBG(cdev, "reset rndis\n");
++ gether_disconnect(&rndis->port);
++ } else {
++ DBG(cdev, "init rndis\n");
++ rndis->port.in = ep_choose(cdev->gadget,
++ rndis->hs.in, rndis->fs.in);
++ rndis->port.out = ep_choose(cdev->gadget,
++ rndis->hs.out, rndis->fs.out);
++ }
++
++ /* Avoid ZLPs; they can be troublesome. */
++ rndis->port.is_zlp_ok = false;
++
++ /* RNDIS should be in the "RNDIS uninitialized" state,
++ * either never activated or after rndis_uninit().
++ *
++ * We don't want data to flow here until a nonzero packet
++ * filter is set, at which point it enters "RNDIS data
++ * initialized" state ... but we do want the endpoints
++ * to be activated. It's a strange little state.
++ *
++ * REVISIT the RNDIS gadget code has done this wrong for a
++ * very long time. We need another call to the link layer
++ * code -- gether_updown(...bool) maybe -- to do it right.
++ */
++ rndis->port.cdc_filter = 0;
++
++ DBG(cdev, "RNDIS RX/TX early activation ... \n");
++ net = gether_connect(&rndis->port);
++ if (IS_ERR(net))
++ return PTR_ERR(net);
++
++ rndis_set_param_dev(rndis->config, net,
++ &rndis->port.cdc_filter);
++ } else
++ goto fail;
++
++ return 0;
++fail:
++ return -EINVAL;
++}
++
++static void rndis_disable(struct usb_function *f)
++{
++ struct f_rndis *rndis = func_to_rndis(f);
++ struct usb_composite_dev *cdev = f->config->cdev;
++
++ if (!rndis->notify->driver_data)
++ return;
++
++ DBG(cdev, "rndis deactivated\n");
++
++ rndis_uninit(rndis->config);
++ gether_disconnect(&rndis->port);
++
++ usb_ep_disable(rndis->notify);
++ rndis->notify->driver_data = NULL;
++}
++
++/*-------------------------------------------------------------------------*/
++
++/*
++ * This isn't quite the same mechanism as CDC Ethernet, since the
++ * notification scheme passes less data, but the same set of link
++ * states must be tested. A key difference is that altsettings are
++ * not used to tell whether the link should send packets or not.
++ */
++
++static void rndis_open(struct gether *geth)
++{
++ struct f_rndis *rndis = func_to_rndis(&geth->func);
++ struct usb_composite_dev *cdev = geth->func.config->cdev;
++
++ DBG(cdev, "%s\n", __func__);
++
++ rndis_set_param_medium(rndis->config, NDIS_MEDIUM_802_3,
++ bitrate(cdev->gadget) / 100);
++ rndis_signal_connect(rndis->config);
++}
++
++static void rndis_close(struct gether *geth)
++{
++ struct f_rndis *rndis = func_to_rndis(&geth->func);
++
++ DBG(geth->func.config->cdev, "%s\n", __func__);
++
++ rndis_set_param_medium(rndis->config, NDIS_MEDIUM_802_3, 0);
++ rndis_signal_disconnect(rndis->config);
++}
++
++/*-------------------------------------------------------------------------*/
++
++/* ethernet function driver setup/binding */
++
++static int __init
++rndis_bind(struct usb_configuration *c, struct usb_function *f)
++{
++ struct usb_composite_dev *cdev = c->cdev;
++ struct f_rndis *rndis = func_to_rndis(f);
++ int status;
++ struct usb_ep *ep;
++
++ /* allocate instance-specific interface IDs */
++ status = usb_interface_id(c, f);
++ if (status < 0)
++ goto fail;
++ rndis->ctrl_id = status;
++
++ rndis_control_intf.bInterfaceNumber = status;
++ rndis_union_desc.bMasterInterface0 = status;
++
++ status = usb_interface_id(c, f);
++ if (status < 0)
++ goto fail;
++ rndis->data_id = status;
++
++ rndis_data_intf.bInterfaceNumber = status;
++ rndis_union_desc.bSlaveInterface0 = status;
++
++ status = -ENODEV;
++
++ /* allocate instance-specific endpoints */
++ ep = usb_ep_autoconfig(cdev->gadget, &fs_in_desc);
++ if (!ep)
++ goto fail;
++ rndis->port.in_ep = ep;
++ ep->driver_data = cdev; /* claim */
++
++ ep = usb_ep_autoconfig(cdev->gadget, &fs_out_desc);
++ if (!ep)
++ goto fail;
++ rndis->port.out_ep = ep;
++ ep->driver_data = cdev; /* claim */
++
++ /* NOTE: a status/notification endpoint is, strictly speaking,
++ * optional. We don't treat it that way though! It's simpler,
++ * and some newer profiles don't treat it as optional.
++ */
++ ep = usb_ep_autoconfig(cdev->gadget, &fs_notify_desc);
++ if (!ep)
++ goto fail;
++ rndis->notify = ep;
++ ep->driver_data = cdev; /* claim */
++
++ status = -ENOMEM;
++
++ /* allocate notification request and buffer */
++ rndis->notify_req = usb_ep_alloc_request(ep, GFP_KERNEL);
++ if (!rndis->notify_req)
++ goto fail;
++ rndis->notify_req->buf = kmalloc(STATUS_BYTECOUNT, GFP_KERNEL);
++ if (!rndis->notify_req->buf)
++ goto fail;
++ rndis->notify_req->length = STATUS_BYTECOUNT;
++ rndis->notify_req->context = rndis;
++ rndis->notify_req->complete = rndis_response_complete;
++
++ /* copy descriptors, and track endpoint copies */
++ f->descriptors = usb_copy_descriptors(eth_fs_function);
++ if (!f->descriptors)
++ goto fail;
++
++ rndis->fs.in = usb_find_endpoint(eth_fs_function,
++ f->descriptors, &fs_in_desc);
++ rndis->fs.out = usb_find_endpoint(eth_fs_function,
++ f->descriptors, &fs_out_desc);
++ rndis->fs.notify = usb_find_endpoint(eth_fs_function,
++ f->descriptors, &fs_notify_desc);
++
++ /* support all relevant hardware speeds... we expect that when
++ * hardware is dual speed, all bulk-capable endpoints work at
++ * both speeds
++ */
++ if (gadget_is_dualspeed(c->cdev->gadget)) {
++ hs_in_desc.bEndpointAddress =
++ fs_in_desc.bEndpointAddress;
++ hs_out_desc.bEndpointAddress =
++ fs_out_desc.bEndpointAddress;
++
++ /* copy descriptors, and track endpoint copies */
++ f->hs_descriptors = usb_copy_descriptors(eth_hs_function);
++
++ if (!f->hs_descriptors)
++ goto fail;
++
++ rndis->hs.in = usb_find_endpoint(eth_hs_function,
++ f->hs_descriptors, &hs_in_desc);
++ rndis->hs.out = usb_find_endpoint(eth_hs_function,
++ f->hs_descriptors, &hs_out_desc);
++ }
++
++ rndis->port.open = rndis_open;
++ rndis->port.close = rndis_close;
++
++ status = rndis_register(rndis_response_available, rndis);
++ if (status < 0)
++ goto fail;
++ rndis->config = status;
++
++ rndis_set_param_medium(rndis->config, NDIS_MEDIUM_802_3, 0);
++ rndis_set_host_mac(rndis->config, rndis->ethaddr);
++
++#if 0
++// FIXME
++ if (rndis_set_param_vendor(rndis->config, vendorID,
++ manufacturer))
++ goto fail0;
++#endif
++
++ /* NOTE: all that is done without knowing or caring about
++ * the network link ... which is unavailable to this code
++ * until we're activated via set_alt().
++ */
++
++ DBG(cdev, "RNDIS: %s speed IN/%s OUT/%s NOTIFY/%s\n",
++ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
++ rndis->port.in_ep->name, rndis->port.out_ep->name,
++ rndis->notify->name);
++ return 0;
++
++fail:
++ if (gadget_is_dualspeed(c->cdev->gadget) && f->hs_descriptors)
++ usb_free_descriptors(f->hs_descriptors);
++ if (f->descriptors)
++ usb_free_descriptors(f->descriptors);
++
++ if (rndis->notify_req) {
++ kfree(rndis->notify_req->buf);
++ usb_ep_free_request(rndis->notify, rndis->notify_req);
++ }
++
++ /* we might as well release our claims on endpoints */
++ if (rndis->notify)
++ rndis->notify->driver_data = NULL;
++ if (rndis->port.out)
++ rndis->port.out_ep->driver_data = NULL;
++ if (rndis->port.in)
++ rndis->port.in_ep->driver_data = NULL;
++
++ ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
++
++ return status;
++}
++
++static void
++rndis_unbind(struct usb_configuration *c, struct usb_function *f)
++{
++ struct f_rndis *rndis = func_to_rndis(f);
++
++ rndis_deregister(rndis->config);
++ rndis_exit();
++
++ if (gadget_is_dualspeed(c->cdev->gadget))
++ usb_free_descriptors(f->hs_descriptors);
++ usb_free_descriptors(f->descriptors);
++
++ kfree(rndis->notify_req->buf);
++ usb_ep_free_request(rndis->notify, rndis->notify_req);
++
++ kfree(rndis);
++}
++
++/* Some controllers can't support RNDIS ... */
++static inline bool can_support_rndis(struct usb_configuration *c)
++{
++ /* only two endpoints on sa1100 */
++ if (gadget_is_sa1100(c->cdev->gadget))
++ return false;
++
++ /* everything else is *presumably* fine */
++ return true;
++}
++
++/**
++ * rndis_bind_config - add RNDIS network link to a configuration
++ * @c: the configuration to support the network link
++ * @ethaddr: a buffer in which the ethernet address of the host side
++ * side of the link was recorded
++ * Context: single threaded during gadget setup
++ *
++ * Returns zero on success, else negative errno.
++ *
++ * Caller must have called @gether_setup(). Caller is also responsible
++ * for calling @gether_cleanup() before module unload.
++ */
++int __init rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
++{
++ struct f_rndis *rndis;
++ int status;
++
++ if (!can_support_rndis(c) || !ethaddr)
++ return -EINVAL;
++
++ /* maybe allocate device-global string IDs */
++ if (rndis_string_defs[0].id == 0) {
++
++ /* ... and setup RNDIS itself */
++ status = rndis_init();
++ if (status < 0)
++ return status;
++
++ /* control interface label */
++ status = usb_string_id(c->cdev);
++ if (status < 0)
++ return status;
++ rndis_string_defs[0].id = status;
++ rndis_control_intf.iInterface = status;
++
++ /* data interface label */
++ status = usb_string_id(c->cdev);
++ if (status < 0)
++ return status;
++ rndis_string_defs[1].id = status;
++ rndis_data_intf.iInterface = status;
++ }
++
++ /* allocate and initialize one new instance */
++ status = -ENOMEM;
++ rndis = kzalloc(sizeof *rndis, GFP_KERNEL);
++ if (!rndis)
++ goto fail;
++
++ memcpy(rndis->ethaddr, ethaddr, ETH_ALEN);
++
++ /* RNDIS activates when the host changes this filter */
++ rndis->port.cdc_filter = 0;
++
++ /* RNDIS has special (and complex) framing */
++ rndis->port.header_len = sizeof(struct rndis_packet_msg_type);
++ rndis->port.wrap = rndis_add_header;
++ rndis->port.unwrap = rndis_rm_hdr;
++
++ rndis->port.func.name = "rndis";
++ rndis->port.func.strings = rndis_strings;
++ /* descriptors are per-instance copies */
++ rndis->port.func.bind = rndis_bind;
++ rndis->port.func.unbind = rndis_unbind;
++ rndis->port.func.set_alt = rndis_set_alt;
++ rndis->port.func.setup = rndis_setup;
++ rndis->port.func.disable = rndis_disable;
++
++ status = usb_add_function(c, &rndis->port.func);
++ if (status) {
++ kfree(rndis);
++fail:
++ rndis_exit();
++ }
++ return status;
++}
+diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/f_serial.c
+new file mode 100644
+index 0000000..fe5674d
+--- /dev/null
++++ b/drivers/usb/gadget/f_serial.c
+@@ -0,0 +1,294 @@
++/*
++ * f_serial.c - generic USB serial function driver
++ *
++ * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
++ * Copyright (C) 2008 by David Brownell
++ * Copyright (C) 2008 by Nokia Corporation
++ *
++ * This software is distributed under the terms of the GNU General
++ * Public License ("GPL") as published by the Free Software Foundation,
++ * either version 2 of that License or (at your option) any later version.
++ */
++
++#include <linux/kernel.h>
++#include <linux/device.h>
++
++#include "u_serial.h"
++#include "gadget_chips.h"
++
++
++/*
++ * This function packages a simple "generic serial" port with no real
++ * control mechanisms, just raw data transfer over two bulk endpoints.
++ *
++ * Because it's not standardized, this isn't as interoperable as the
++ * CDC ACM driver. However, for many purposes it's just as functional
++ * if you can arrange appropriate host side drivers.
++ */
++
++struct gser_descs {
++ struct usb_endpoint_descriptor *in;
++ struct usb_endpoint_descriptor *out;
++};
++
++struct f_gser {
++ struct gserial port;
++ u8 data_id;
++ u8 port_num;
++
++ struct gser_descs fs;
++ struct gser_descs hs;
++};
++
++static inline struct f_gser *func_to_gser(struct usb_function *f)
++{
++ return container_of(f, struct f_gser, port.func);
++}
++
++/*-------------------------------------------------------------------------*/
++
++/* interface descriptor: */
++
++static struct usb_interface_descriptor gser_interface_desc __initdata = {
++ .bLength = USB_DT_INTERFACE_SIZE,
++ .bDescriptorType = USB_DT_INTERFACE,
++ /* .bInterfaceNumber = DYNAMIC */
++ .bNumEndpoints = 2,
++ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
++ .bInterfaceSubClass = 0,
++ .bInterfaceProtocol = 0,
++ /* .iInterface = DYNAMIC */
++};
++
++/* full speed support: */
++
++static struct usb_endpoint_descriptor gser_fs_in_desc __initdata = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++ .bEndpointAddress = USB_DIR_IN,
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++};
++
++static struct usb_endpoint_descriptor gser_fs_out_desc __initdata = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++ .bEndpointAddress = USB_DIR_OUT,
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++};
++
++static struct usb_descriptor_header *gser_fs_function[] __initdata = {
++ (struct usb_descriptor_header *) &gser_interface_desc,
++ (struct usb_descriptor_header *) &gser_fs_in_desc,
++ (struct usb_descriptor_header *) &gser_fs_out_desc,
++ NULL,
++};
++
++/* high speed support: */
++
++static struct usb_endpoint_descriptor gser_hs_in_desc __initdata = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++ .wMaxPacketSize = __constant_cpu_to_le16(512),
++};
++
++static struct usb_endpoint_descriptor gser_hs_out_desc __initdata = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++ .wMaxPacketSize = __constant_cpu_to_le16(512),
++};
++
++static struct usb_descriptor_header *gser_hs_function[] __initdata = {
++ (struct usb_descriptor_header *) &gser_interface_desc,
++ (struct usb_descriptor_header *) &gser_hs_in_desc,
++ (struct usb_descriptor_header *) &gser_hs_out_desc,
++ NULL,
++};
++
++/* string descriptors: */
++
++static struct usb_string gser_string_defs[] = {
++ [0].s = "Generic Serial",
++ { } /* end of list */
++};
++
++static struct usb_gadget_strings gser_string_table = {
++ .language = 0x0409, /* en-us */
++ .strings = gser_string_defs,
++};
++
++static struct usb_gadget_strings *gser_strings[] = {
++ &gser_string_table,
++ NULL,
++};
++
++/*-------------------------------------------------------------------------*/
++
++static int gser_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
++{
++ struct f_gser *gser = func_to_gser(f);
++ struct usb_composite_dev *cdev = f->config->cdev;
++
++ /* we know alt == 0, so this is an activation or a reset */
++
++ if (gser->port.in->driver_data) {
++ DBG(cdev, "reset generic ttyGS%d\n", gser->port_num);
++ gserial_disconnect(&gser->port);
++ } else {
++ DBG(cdev, "activate generic ttyGS%d\n", gser->port_num);
++ gser->port.in_desc = ep_choose(cdev->gadget,
++ gser->hs.in, gser->fs.in);
++ gser->port.out_desc = ep_choose(cdev->gadget,
++ gser->hs.out, gser->fs.out);
++ }
++ gserial_connect(&gser->port, gser->port_num);
++ return 0;
++}
++
++static void gser_disable(struct usb_function *f)
++{
++ struct f_gser *gser = func_to_gser(f);
++ struct usb_composite_dev *cdev = f->config->cdev;
++
++ DBG(cdev, "generic ttyGS%d deactivated\n", gser->port_num);
++ gserial_disconnect(&gser->port);
++}
++
++/*-------------------------------------------------------------------------*/
++
++/* serial function driver setup/binding */
++
++static int __init
++gser_bind(struct usb_configuration *c, struct usb_function *f)
++{
++ struct usb_composite_dev *cdev = c->cdev;
++ struct f_gser *gser = func_to_gser(f);
++ int status;
++ struct usb_ep *ep;
++
++ /* allocate instance-specific interface IDs */
++ status = usb_interface_id(c, f);
++ if (status < 0)
++ goto fail;
++ gser->data_id = status;
++ gser_interface_desc.bInterfaceNumber = status;
++
++ status = -ENODEV;
++
++ /* allocate instance-specific endpoints */
++ ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_in_desc);
++ if (!ep)
++ goto fail;
++ gser->port.in = ep;
++ ep->driver_data = cdev; /* claim */
++
++ ep = usb_ep_autoconfig(cdev->gadget, &gser_fs_out_desc);
++ if (!ep)
++ goto fail;
++ gser->port.out = ep;
++ ep->driver_data = cdev; /* claim */
++
++ /* copy descriptors, and track endpoint copies */
++ f->descriptors = usb_copy_descriptors(gser_fs_function);
++
++ gser->fs.in = usb_find_endpoint(gser_fs_function,
++ f->descriptors, &gser_fs_in_desc);
++ gser->fs.out = usb_find_endpoint(gser_fs_function,
++ f->descriptors, &gser_fs_out_desc);
++
++
++ /* support all relevant hardware speeds... we expect that when
++ * hardware is dual speed, all bulk-capable endpoints work at
++ * both speeds
++ */
++ if (gadget_is_dualspeed(c->cdev->gadget)) {
++ gser_hs_in_desc.bEndpointAddress =
++ gser_fs_in_desc.bEndpointAddress;
++ gser_hs_out_desc.bEndpointAddress =
++ gser_fs_out_desc.bEndpointAddress;
++
++ /* copy descriptors, and track endpoint copies */
++ f->hs_descriptors = usb_copy_descriptors(gser_hs_function);
++
++ gser->hs.in = usb_find_endpoint(gser_hs_function,
++ f->hs_descriptors, &gser_hs_in_desc);
++ gser->hs.out = usb_find_endpoint(gser_hs_function,
++ f->hs_descriptors, &gser_hs_out_desc);
++ }
++
++ DBG(cdev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n",
++ gser->port_num,
++ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
++ gser->port.in->name, gser->port.out->name);
++ return 0;
++
++fail:
++ /* we might as well release our claims on endpoints */
++ if (gser->port.out)
++ gser->port.out->driver_data = NULL;
++ if (gser->port.in)
++ gser->port.in->driver_data = NULL;
++
++ ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
++
++ return status;
++}
++
++static void
++gser_unbind(struct usb_configuration *c, struct usb_function *f)
++{
++ if (gadget_is_dualspeed(c->cdev->gadget))
++ usb_free_descriptors(f->hs_descriptors);
++ usb_free_descriptors(f->descriptors);
++ kfree(func_to_gser(f));
++}
++
++/**
++ * gser_bind_config - add a generic serial function to a configuration
++ * @c: the configuration to support the serial instance
++ * @port_num: /dev/ttyGS* port this interface will use
++ * Context: single threaded during gadget setup
++ *
++ * Returns zero on success, else negative errno.
++ *
++ * Caller must have called @gserial_setup() with enough ports to
++ * handle all the ones it binds. Caller is also responsible
++ * for calling @gserial_cleanup() before module unload.
++ */
++int __init gser_bind_config(struct usb_configuration *c, u8 port_num)
++{
++ struct f_gser *gser;
++ int status;
++
++ /* REVISIT might want instance-specific strings to help
++ * distinguish instances ...
++ */
++
++ /* maybe allocate device-global string ID */
++ if (gser_string_defs[0].id == 0) {
++ status = usb_string_id(c->cdev);
++ if (status < 0)
++ return status;
++ gser_string_defs[0].id = status;
++ }
++
++ /* allocate and initialize one new instance */
++ gser = kzalloc(sizeof *gser, GFP_KERNEL);
++ if (!gser)
++ return -ENOMEM;
++
++ gser->port_num = port_num;
++
++ gser->port.func.name = "gser";
++ gser->port.func.strings = gser_strings;
++ gser->port.func.bind = gser_bind;
++ gser->port.func.unbind = gser_unbind;
++ gser->port.func.set_alt = gser_set_alt;
++ gser->port.func.disable = gser_disable;
++
++ status = usb_add_function(c, &gser->port.func);
++ if (status)
++ kfree(gser);
++ return status;
++}
+diff --git a/drivers/usb/gadget/f_sourcesink.c b/drivers/usb/gadget/f_sourcesink.c
+new file mode 100644
+index 0000000..f18c3a1
+--- /dev/null
++++ b/drivers/usb/gadget/f_sourcesink.c
+@@ -0,0 +1,587 @@
++/*
++ * f_sourcesink.c - USB peripheral source/sink configuration driver
++ *
++ * Copyright (C) 2003-2008 David Brownell
++ * Copyright (C) 2008 by Nokia Corporation
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++/* #define VERBOSE_DEBUG */
++
++#include <linux/kernel.h>
++#include <linux/utsname.h>
++#include <linux/device.h>
++
++#include "g_zero.h"
++#include "gadget_chips.h"
++
++
++/*
++ * SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripheral
++ * controller drivers.
++ *
++ * This just sinks bulk packets OUT to the peripheral and sources them IN
++ * to the host, optionally with specific data patterns for integrity tests.
++ * As such it supports basic functionality and load tests.
++ *
++ * In terms of control messaging, this supports all the standard requests
++ * plus two that support control-OUT tests. If the optional "autoresume"
++ * mode is enabled, it provides good functional coverage for the "USBCV"
++ * test harness from USB-IF.
++ *
++ * Note that because this doesn't queue more than one request at a time,
++ * some other function must be used to test queueing logic. The network
++ * link (g_ether) is the best overall option for that, since its TX and RX
++ * queues are relatively independent, will receive a range of packet sizes,
++ * and can often be made to run out completely. Those issues are important
++ * when stress testing peripheral controller drivers.
++ *
++ *
++ * This is currently packaged as a configuration driver, which can't be
++ * combined with other functions to make composite devices. However, it
++ * can be combined with other independent configurations.
++ */
++struct f_sourcesink {
++ struct usb_function function;
++
++ struct usb_ep *in_ep;
++ struct usb_ep *out_ep;
++ struct timer_list resume;
++};
++
++static inline struct f_sourcesink *func_to_ss(struct usb_function *f)
++{
++ return container_of(f, struct f_sourcesink, function);
++}
++
++static unsigned autoresume;
++module_param(autoresume, uint, 0);
++MODULE_PARM_DESC(autoresume, "zero, or seconds before remote wakeup");
++
++static unsigned pattern;
++module_param(pattern, uint, 0);
++MODULE_PARM_DESC(pattern, "0 = all zeroes, 1 = mod63 ");
++
++/*-------------------------------------------------------------------------*/
++
++static struct usb_interface_descriptor source_sink_intf = {
++ .bLength = sizeof source_sink_intf,
++ .bDescriptorType = USB_DT_INTERFACE,
++
++ .bNumEndpoints = 2,
++ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
++ /* .iInterface = DYNAMIC */
++};
++
++/* full speed support: */
++
++static struct usb_endpoint_descriptor fs_source_desc = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++
++ .bEndpointAddress = USB_DIR_IN,
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++};
++
++static struct usb_endpoint_descriptor fs_sink_desc = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++
++ .bEndpointAddress = USB_DIR_OUT,
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++};
++
++static struct usb_descriptor_header *fs_source_sink_descs[] = {
++ (struct usb_descriptor_header *) &source_sink_intf,
++ (struct usb_descriptor_header *) &fs_sink_desc,
++ (struct usb_descriptor_header *) &fs_source_desc,
++ NULL,
++};
++
++/* high speed support: */
++
++static struct usb_endpoint_descriptor hs_source_desc = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++ .wMaxPacketSize = __constant_cpu_to_le16(512),
++};
++
++static struct usb_endpoint_descriptor hs_sink_desc = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++ .wMaxPacketSize = __constant_cpu_to_le16(512),
++};
++
++static struct usb_descriptor_header *hs_source_sink_descs[] = {
++ (struct usb_descriptor_header *) &source_sink_intf,
++ (struct usb_descriptor_header *) &hs_source_desc,
++ (struct usb_descriptor_header *) &hs_sink_desc,
++ NULL,
++};
++
++/* function-specific strings: */
++
++static struct usb_string strings_sourcesink[] = {
++ [0].s = "source and sink data",
++ { } /* end of list */
++};
++
++static struct usb_gadget_strings stringtab_sourcesink = {
++ .language = 0x0409, /* en-us */
++ .strings = strings_sourcesink,
++};
++
++static struct usb_gadget_strings *sourcesink_strings[] = {
++ &stringtab_sourcesink,
++ NULL,
++};
++
++/*-------------------------------------------------------------------------*/
++
++static void sourcesink_autoresume(unsigned long _c)
++{
++ struct usb_composite_dev *cdev = (void *)_c;
++ struct usb_gadget *g = cdev->gadget;
++
++ /* Normally the host would be woken up for something
++ * more significant than just a timer firing; likely
++ * because of some direct user request.
++ */
++ if (g->speed != USB_SPEED_UNKNOWN) {
++ int status = usb_gadget_wakeup(g);
++ DBG(cdev, "%s --> %d\n", __func__, status);
++ }
++}
++
++static int __init
++sourcesink_bind(struct usb_configuration *c, struct usb_function *f)
++{
++ struct usb_composite_dev *cdev = c->cdev;
++ struct f_sourcesink *ss = func_to_ss(f);
++ int id;
++
++ /* allocate interface ID(s) */
++ id = usb_interface_id(c, f);
++ if (id < 0)
++ return id;
++ source_sink_intf.bInterfaceNumber = id;
++
++ /* allocate endpoints */
++ ss->in_ep = usb_ep_autoconfig(cdev->gadget, &fs_source_desc);
++ if (!ss->in_ep) {
++autoconf_fail:
++ ERROR(cdev, "%s: can't autoconfigure on %s\n",
++ f->name, cdev->gadget->name);
++ return -ENODEV;
++ }
++ ss->in_ep->driver_data = cdev; /* claim */
++
++ ss->out_ep = usb_ep_autoconfig(cdev->gadget, &fs_sink_desc);
++ if (!ss->out_ep)
++ goto autoconf_fail;
++ ss->out_ep->driver_data = cdev; /* claim */
++
++ setup_timer(&ss->resume, sourcesink_autoresume,
++ (unsigned long) c->cdev);
++
++ /* support high speed hardware */
++ if (gadget_is_dualspeed(c->cdev->gadget)) {
++ hs_source_desc.bEndpointAddress =
++ fs_source_desc.bEndpointAddress;
++ hs_sink_desc.bEndpointAddress =
++ fs_sink_desc.bEndpointAddress;
++ f->hs_descriptors = hs_source_sink_descs;
++ }
++
++ DBG(cdev, "%s speed %s: IN/%s, OUT/%s\n",
++ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
++ f->name, ss->in_ep->name, ss->out_ep->name);
++ return 0;
++}
++
++static void
++sourcesink_unbind(struct usb_configuration *c, struct usb_function *f)
++{
++ kfree(func_to_ss(f));
++}
++
++/* optionally require specific source/sink data patterns */
++static int check_read_data(struct f_sourcesink *ss, struct usb_request *req)
++{
++ unsigned i;
++ u8 *buf = req->buf;
++ struct usb_composite_dev *cdev = ss->function.config->cdev;
++
++ for (i = 0; i < req->actual; i++, buf++) {
++ switch (pattern) {
++
++ /* all-zeroes has no synchronization issues */
++ case 0:
++ if (*buf == 0)
++ continue;
++ break;
++
++ /* "mod63" stays in sync with short-terminated transfers,
++ * OR otherwise when host and gadget agree on how large
++ * each usb transfer request should be. Resync is done
++ * with set_interface or set_config. (We *WANT* it to
++ * get quickly out of sync if controllers or their drivers
++ * stutter for any reason, including buffer duplcation...)
++ */
++ case 1:
++ if (*buf == (u8)(i % 63))
++ continue;
++ break;
++ }
++ ERROR(cdev, "bad OUT byte, buf[%d] = %d\n", i, *buf);
++ usb_ep_set_halt(ss->out_ep);
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static void reinit_write_data(struct usb_ep *ep, struct usb_request *req)
++{
++ unsigned i;
++ u8 *buf = req->buf;
++
++ switch (pattern) {
++ case 0:
++ memset(req->buf, 0, req->length);
++ break;
++ case 1:
++ for (i = 0; i < req->length; i++)
++ *buf++ = (u8) (i % 63);
++ break;
++ }
++}
++
++static void source_sink_complete(struct usb_ep *ep, struct usb_request *req)
++{
++ struct f_sourcesink *ss = ep->driver_data;
++ struct usb_composite_dev *cdev = ss->function.config->cdev;
++ int status = req->status;
++
++ switch (status) {
++
++ case 0: /* normal completion? */
++ if (ep == ss->out_ep) {
++ check_read_data(ss, req);
++ memset(req->buf, 0x55, req->length);
++ } else
++ reinit_write_data(ep, req);
++ break;
++
++ /* this endpoint is normally active while we're configured */
++ case -ECONNABORTED: /* hardware forced ep reset */
++ case -ECONNRESET: /* request dequeued */
++ case -ESHUTDOWN: /* disconnect from host */
++ VDBG(cdev, "%s gone (%d), %d/%d\n", ep->name, status,
++ req->actual, req->length);
++ if (ep == ss->out_ep)
++ check_read_data(ss, req);
++ free_ep_req(ep, req);
++ return;
++
++ case -EOVERFLOW: /* buffer overrun on read means that
++ * we didn't provide a big enough
++ * buffer.
++ */
++ default:
++#if 1
++ DBG(cdev, "%s complete --> %d, %d/%d\n", ep->name,
++ status, req->actual, req->length);
++#endif
++ case -EREMOTEIO: /* short read */
++ break;
++ }
++
++ status = usb_ep_queue(ep, req, GFP_ATOMIC);
++ if (status) {
++ ERROR(cdev, "kill %s: resubmit %d bytes --> %d\n",
++ ep->name, req->length, status);
++ usb_ep_set_halt(ep);
++ /* FIXME recover later ... somehow */
++ }
++}
++
++static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in)
++{
++ struct usb_ep *ep;
++ struct usb_request *req;
++ int status;
++
++ ep = is_in ? ss->in_ep : ss->out_ep;
++ req = alloc_ep_req(ep);
++ if (!req)
++ return -ENOMEM;
++
++ req->complete = source_sink_complete;
++ if (is_in)
++ reinit_write_data(ep, req);
++ else
++ memset(req->buf, 0x55, req->length);
++
++ status = usb_ep_queue(ep, req, GFP_ATOMIC);
++ if (status) {
++ struct usb_composite_dev *cdev;
++
++ cdev = ss->function.config->cdev;
++ ERROR(cdev, "start %s %s --> %d\n",
++ is_in ? "IN" : "OUT",
++ ep->name, status);
++ free_ep_req(ep, req);
++ }
++
++ return status;
++}
++
++static void disable_source_sink(struct f_sourcesink *ss)
++{
++ struct usb_composite_dev *cdev;
++
++ cdev = ss->function.config->cdev;
++ disable_endpoints(cdev, ss->in_ep, ss->out_ep);
++ del_timer(&ss->resume);
++ VDBG(cdev, "%s disabled\n", ss->function.name);
++}
++
++static int
++enable_source_sink(struct usb_composite_dev *cdev, struct f_sourcesink *ss)
++{
++ int result = 0;
++ const struct usb_endpoint_descriptor *src, *sink;
++ struct usb_ep *ep;
++
++ src = ep_choose(cdev->gadget, &hs_source_desc, &fs_source_desc);
++ sink = ep_choose(cdev->gadget, &hs_sink_desc, &fs_sink_desc);
++
++ /* one endpoint writes (sources) zeroes IN (to the host) */
++ ep = ss->in_ep;
++ result = usb_ep_enable(ep, src);
++ if (result < 0)
++ return result;
++ ep->driver_data = ss;
++
++ result = source_sink_start_ep(ss, true);
++ if (result < 0) {
++fail:
++ ep = ss->in_ep;
++ usb_ep_disable(ep);
++ ep->driver_data = NULL;
++ return result;
++ }
++
++ /* one endpoint reads (sinks) anything OUT (from the host) */
++ ep = ss->out_ep;
++ result = usb_ep_enable(ep, sink);
++ if (result < 0)
++ goto fail;
++ ep->driver_data = ss;
++
++ result = source_sink_start_ep(ss, false);
++ if (result < 0) {
++ usb_ep_disable(ep);
++ ep->driver_data = NULL;
++ goto fail;
++ }
++
++ DBG(cdev, "%s enabled\n", ss->function.name);
++ return result;
++}
++
++static int sourcesink_set_alt(struct usb_function *f,
++ unsigned intf, unsigned alt)
++{
++ struct f_sourcesink *ss = func_to_ss(f);
++ struct usb_composite_dev *cdev = f->config->cdev;
++
++ /* we know alt is zero */
++ if (ss->in_ep->driver_data)
++ disable_source_sink(ss);
++ return enable_source_sink(cdev, ss);
++}
++
++static void sourcesink_disable(struct usb_function *f)
++{
++ struct f_sourcesink *ss = func_to_ss(f);
++
++ disable_source_sink(ss);
++}
++
++static void sourcesink_suspend(struct usb_function *f)
++{
++ struct f_sourcesink *ss = func_to_ss(f);
++ struct usb_composite_dev *cdev = f->config->cdev;
++
++ if (cdev->gadget->speed == USB_SPEED_UNKNOWN)
++ return;
++
++ if (autoresume) {
++ mod_timer(&ss->resume, jiffies + (HZ * autoresume));
++ DBG(cdev, "suspend, wakeup in %d seconds\n", autoresume);
++ } else
++ DBG(cdev, "%s\n", __func__);
++}
++
++static void sourcesink_resume(struct usb_function *f)
++{
++ struct f_sourcesink *ss = func_to_ss(f);
++ struct usb_composite_dev *cdev = f->config->cdev;
++
++ DBG(cdev, "%s\n", __func__);
++ del_timer(&ss->resume);
++}
++
++/*-------------------------------------------------------------------------*/
++
++static int __init sourcesink_bind_config(struct usb_configuration *c)
++{
++ struct f_sourcesink *ss;
++ int status;
++
++ ss = kzalloc(sizeof *ss, GFP_KERNEL);
++ if (!ss)
++ return -ENOMEM;
++
++ ss->function.name = "source/sink";
++ ss->function.descriptors = fs_source_sink_descs;
++ ss->function.bind = sourcesink_bind;
++ ss->function.unbind = sourcesink_unbind;
++ ss->function.set_alt = sourcesink_set_alt;
++ ss->function.disable = sourcesink_disable;
++ ss->function.suspend = sourcesink_suspend;
++ ss->function.resume = sourcesink_resume;
++
++ status = usb_add_function(c, &ss->function);
++ if (status)
++ kfree(ss);
++ return status;
++}
++
++static int sourcesink_setup(struct usb_configuration *c,
++ const struct usb_ctrlrequest *ctrl)
++{
++ struct usb_request *req = c->cdev->req;
++ int value = -EOPNOTSUPP;
++ u16 w_index = le16_to_cpu(ctrl->wIndex);
++ u16 w_value = le16_to_cpu(ctrl->wValue);
++ u16 w_length = le16_to_cpu(ctrl->wLength);
++
++ /* composite driver infrastructure handles everything except
++ * the two control test requests.
++ */
++ switch (ctrl->bRequest) {
++
++ /*
++ * These are the same vendor-specific requests supported by
++ * Intel's USB 2.0 compliance test devices. We exceed that
++ * device spec by allowing multiple-packet requests.
++ *
++ * NOTE: the Control-OUT data stays in req->buf ... better
++ * would be copying it into a scratch buffer, so that other
++ * requests may safely intervene.
++ */
++ case 0x5b: /* control WRITE test -- fill the buffer */
++ if (ctrl->bRequestType != (USB_DIR_OUT|USB_TYPE_VENDOR))
++ goto unknown;
++ if (w_value || w_index)
++ break;
++ /* just read that many bytes into the buffer */
++ if (w_length > req->length)
++ break;
++ value = w_length;
++ break;
++ case 0x5c: /* control READ test -- return the buffer */
++ if (ctrl->bRequestType != (USB_DIR_IN|USB_TYPE_VENDOR))
++ goto unknown;
++ if (w_value || w_index)
++ break;
++ /* expect those bytes are still in the buffer; send back */
++ if (w_length > req->length)
++ break;
++ value = w_length;
++ break;
++
++ default:
++unknown:
++ VDBG(c->cdev,
++ "unknown control req%02x.%02x v%04x i%04x l%d\n",
++ ctrl->bRequestType, ctrl->bRequest,
++ w_value, w_index, w_length);
++ }
++
++ /* respond with data transfer or status phase? */
++ if (value >= 0) {
++ VDBG(c->cdev, "source/sink req%02x.%02x v%04x i%04x l%d\n",
++ ctrl->bRequestType, ctrl->bRequest,
++ w_value, w_index, w_length);
++ req->zero = 0;
++ req->length = value;
++ value = usb_ep_queue(c->cdev->gadget->ep0, req, GFP_ATOMIC);
++ if (value < 0)
++ ERROR(c->cdev, "source/sinkc response, err %d\n",
++ value);
++ }
++
++ /* device either stalls (value < 0) or reports success */
++ return value;
++}
++
++static struct usb_configuration sourcesink_driver = {
++ .label = "source/sink",
++ .strings = sourcesink_strings,
++ .bind = sourcesink_bind_config,
++ .setup = sourcesink_setup,
++ .bConfigurationValue = 3,
++ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
++ .bMaxPower = 1, /* 2 mA, minimal */
++ /* .iConfiguration = DYNAMIC */
++};
++
++/**
++ * sourcesink_add - add a source/sink testing configuration to a device
++ * @cdev: the device to support the configuration
++ */
++int __init sourcesink_add(struct usb_composite_dev *cdev)
++{
++ int id;
++
++ /* allocate string ID(s) */
++ id = usb_string_id(cdev);
++ if (id < 0)
++ return id;
++ strings_sourcesink[0].id = id;
++
++ source_sink_intf.iInterface = id;
++ sourcesink_driver.iConfiguration = id;
++
++ /* support autoresume for remote wakeup testing */
++ if (autoresume)
++ sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
++
++ /* support OTG systems */
++ if (gadget_is_otg(cdev->gadget)) {
++ sourcesink_driver.descriptors = otg_desc;
++ sourcesink_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
++ }
++
++ return usb_add_config(cdev, &sourcesink_driver);
++}
+diff --git a/drivers/usb/gadget/f_subset.c b/drivers/usb/gadget/f_subset.c
+new file mode 100644
+index 0000000..acb8d23
+--- /dev/null
++++ b/drivers/usb/gadget/f_subset.c
+@@ -0,0 +1,421 @@
++/*
++ * f_subset.c -- "CDC Subset" Ethernet link function driver
++ *
++ * Copyright (C) 2003-2005,2008 David Brownell
++ * Copyright (C) 2008 Nokia Corporation
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++#include <linux/kernel.h>
++#include <linux/device.h>
++#include <linux/etherdevice.h>
++
++#include "u_ether.h"
++
++
++/*
++ * This function packages a simple "CDC Subset" Ethernet port with no real
++ * control mechanisms; just raw data transfer over two bulk endpoints.
++ * The data transfer model is exactly that of CDC Ethernet, which is
++ * why we call it the "CDC Subset".
++ *
++ * Because it's not standardized, this has some interoperability issues.
++ * They mostly relate to driver binding, since the data transfer model is
++ * so simple (CDC Ethernet). The original versions of this protocol used
++ * specific product/vendor IDs: byteswapped IDs for Digital Equipment's
++ * SA-1100 "Itsy" board, which could run Linux 2.4 kernels and supported
++ * daughtercards with USB peripheral connectors. (It was used more often
++ * with other boards, using the Itsy identifiers.) Linux hosts recognized
++ * this with CONFIG_USB_ARMLINUX; these devices have only one configuration
++ * and one interface.
++ *
++ * At some point, MCCI defined a (nonconformant) CDC MDLM variant called
++ * "SAFE", which happens to have a mode which is identical to the "CDC
++ * Subset" in terms of data transfer and lack of control model. This was
++ * adopted by later Sharp Zaurus models, and by some other software which
++ * Linux hosts recognize with CONFIG_USB_NET_ZAURUS.
++ *
++ * Because Microsoft's RNDIS drivers are far from robust, we added a few
++ * descriptors to the CDC Subset code, making this code look like a SAFE
++ * implementation. This lets you use MCCI's host side MS-Windows drivers
++ * if you get fed up with RNDIS. It also makes it easier for composite
++ * drivers to work, since they can use class based binding instead of
++ * caring about specific product and vendor IDs.
++ */
++
++struct geth_descs {
++ struct usb_endpoint_descriptor *in;
++ struct usb_endpoint_descriptor *out;
++};
++
++struct f_gether {
++ struct gether port;
++
++ char ethaddr[14];
++
++ struct geth_descs fs;
++ struct geth_descs hs;
++};
++
++static inline struct f_gether *func_to_geth(struct usb_function *f)
++{
++ return container_of(f, struct f_gether, port.func);
++}
++
++/*-------------------------------------------------------------------------*/
++
++/*
++ * "Simple" CDC-subset option is a simple vendor-neutral model that most
++ * full speed controllers can handle: one interface, two bulk endpoints.
++ * To assist host side drivers, we fancy it up a bit, and add descriptors so
++ * some host side drivers will understand it as a "SAFE" variant.
++ *
++ * "SAFE" loosely follows CDC WMC MDLM, violating the spec in various ways.
++ * Data endpoints live in the control interface, there's no data interface.
++ * And it's not used to talk to a cell phone radio.
++ */
++
++/* interface descriptor: */
++
++static struct usb_interface_descriptor subset_data_intf __initdata = {
++ .bLength = sizeof subset_data_intf,
++ .bDescriptorType = USB_DT_INTERFACE,
++
++ /* .bInterfaceNumber = DYNAMIC */
++ .bAlternateSetting = 0,
++ .bNumEndpoints = 2,
++ .bInterfaceClass = USB_CLASS_COMM,
++ .bInterfaceSubClass = USB_CDC_SUBCLASS_MDLM,
++ .bInterfaceProtocol = 0,
++ /* .iInterface = DYNAMIC */
++};
++
++static struct usb_cdc_header_desc header_desc __initdata = {
++ .bLength = sizeof header_desc,
++ .bDescriptorType = USB_DT_CS_INTERFACE,
++ .bDescriptorSubType = USB_CDC_HEADER_TYPE,
++
++ .bcdCDC = __constant_cpu_to_le16(0x0110),
++};
++
++static struct usb_cdc_mdlm_desc mdlm_desc __initdata = {
++ .bLength = sizeof mdlm_desc,
++ .bDescriptorType = USB_DT_CS_INTERFACE,
++ .bDescriptorSubType = USB_CDC_MDLM_TYPE,
++
++ .bcdVersion = __constant_cpu_to_le16(0x0100),
++ .bGUID = {
++ 0x5d, 0x34, 0xcf, 0x66, 0x11, 0x18, 0x11, 0xd6,
++ 0xa2, 0x1a, 0x00, 0x01, 0x02, 0xca, 0x9a, 0x7f,
++ },
++};
++
++/* since "usb_cdc_mdlm_detail_desc" is a variable length structure, we
++ * can't really use its struct. All we do here is say that we're using
++ * the submode of "SAFE" which directly matches the CDC Subset.
++ */
++static u8 mdlm_detail_desc[] __initdata = {
++ 6,
++ USB_DT_CS_INTERFACE,
++ USB_CDC_MDLM_DETAIL_TYPE,
++
++ 0, /* "SAFE" */
++ 0, /* network control capabilities (none) */
++ 0, /* network data capabilities ("raw" encapsulation) */
++};
++
++static struct usb_cdc_ether_desc ether_desc __initdata = {
++ .bLength = sizeof ether_desc,
++ .bDescriptorType = USB_DT_CS_INTERFACE,
++ .bDescriptorSubType = USB_CDC_ETHERNET_TYPE,
++
++ /* this descriptor actually adds value, surprise! */
++ /* .iMACAddress = DYNAMIC */
++ .bmEthernetStatistics = __constant_cpu_to_le32(0), /* no statistics */
++ .wMaxSegmentSize = __constant_cpu_to_le16(ETH_FRAME_LEN),
++ .wNumberMCFilters = __constant_cpu_to_le16(0),
++ .bNumberPowerFilters = 0,
++};
++
++/* full speed support: */
++
++static struct usb_endpoint_descriptor fs_in_desc __initdata = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++
++ .bEndpointAddress = USB_DIR_IN,
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++};
++
++static struct usb_endpoint_descriptor fs_out_desc __initdata = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++
++ .bEndpointAddress = USB_DIR_OUT,
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++};
++
++static struct usb_descriptor_header *fs_eth_function[] __initdata = {
++ (struct usb_descriptor_header *) &subset_data_intf,
++ (struct usb_descriptor_header *) &header_desc,
++ (struct usb_descriptor_header *) &mdlm_desc,
++ (struct usb_descriptor_header *) &mdlm_detail_desc,
++ (struct usb_descriptor_header *) &ether_desc,
++ (struct usb_descriptor_header *) &fs_in_desc,
++ (struct usb_descriptor_header *) &fs_out_desc,
++ NULL,
++};
++
++/* high speed support: */
++
++static struct usb_endpoint_descriptor hs_in_desc __initdata = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++ .wMaxPacketSize = __constant_cpu_to_le16(512),
++};
++
++static struct usb_endpoint_descriptor hs_out_desc __initdata = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++ .wMaxPacketSize = __constant_cpu_to_le16(512),
++};
++
++static struct usb_descriptor_header *hs_eth_function[] __initdata = {
++ (struct usb_descriptor_header *) &subset_data_intf,
++ (struct usb_descriptor_header *) &header_desc,
++ (struct usb_descriptor_header *) &mdlm_desc,
++ (struct usb_descriptor_header *) &mdlm_detail_desc,
++ (struct usb_descriptor_header *) &ether_desc,
++ (struct usb_descriptor_header *) &hs_in_desc,
++ (struct usb_descriptor_header *) &hs_out_desc,
++ NULL,
++};
++
++/* string descriptors: */
++
++static struct usb_string geth_string_defs[] = {
++ [0].s = "CDC Ethernet Subset/SAFE",
++ [1].s = NULL /* DYNAMIC */,
++ { } /* end of list */
++};
++
++static struct usb_gadget_strings geth_string_table = {
++ .language = 0x0409, /* en-us */
++ .strings = geth_string_defs,
++};
++
++static struct usb_gadget_strings *geth_strings[] = {
++ &geth_string_table,
++ NULL,
++};
++
++/*-------------------------------------------------------------------------*/
++
++static int geth_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
++{
++ struct f_gether *geth = func_to_geth(f);
++ struct usb_composite_dev *cdev = f->config->cdev;
++ struct net_device *net;
++
++ /* we know alt == 0, so this is an activation or a reset */
++
++ if (geth->port.in_ep->driver_data) {
++ DBG(cdev, "reset cdc subset\n");
++ gether_disconnect(&geth->port);
++ }
++
++ DBG(cdev, "init + activate cdc subset\n");
++ geth->port.in = ep_choose(cdev->gadget,
++ geth->hs.in, geth->fs.in);
++ geth->port.out = ep_choose(cdev->gadget,
++ geth->hs.out, geth->fs.out);
++
++ net = gether_connect(&geth->port);
++ return IS_ERR(net) ? PTR_ERR(net) : 0;
++}
++
++static void geth_disable(struct usb_function *f)
++{
++ struct f_gether *geth = func_to_geth(f);
++ struct usb_composite_dev *cdev = f->config->cdev;
++
++ DBG(cdev, "net deactivated\n");
++ gether_disconnect(&geth->port);
++}
++
++/*-------------------------------------------------------------------------*/
++
++/* serial function driver setup/binding */
++
++static int __init
++geth_bind(struct usb_configuration *c, struct usb_function *f)
++{
++ struct usb_composite_dev *cdev = c->cdev;
++ struct f_gether *geth = func_to_geth(f);
++ int status;
++ struct usb_ep *ep;
++
++ /* allocate instance-specific interface IDs */
++ status = usb_interface_id(c, f);
++ if (status < 0)
++ goto fail;
++ subset_data_intf.bInterfaceNumber = status;
++
++ status = -ENODEV;
++
++ /* allocate instance-specific endpoints */
++ ep = usb_ep_autoconfig(cdev->gadget, &fs_in_desc);
++ if (!ep)
++ goto fail;
++ geth->port.in_ep = ep;
++ ep->driver_data = cdev; /* claim */
++
++ ep = usb_ep_autoconfig(cdev->gadget, &fs_out_desc);
++ if (!ep)
++ goto fail;
++ geth->port.out_ep = ep;
++ ep->driver_data = cdev; /* claim */
++
++ /* copy descriptors, and track endpoint copies */
++ f->descriptors = usb_copy_descriptors(fs_eth_function);
++
++ geth->fs.in = usb_find_endpoint(fs_eth_function,
++ f->descriptors, &fs_in_desc);
++ geth->fs.out = usb_find_endpoint(fs_eth_function,
++ f->descriptors, &fs_out_desc);
++
++
++ /* support all relevant hardware speeds... we expect that when
++ * hardware is dual speed, all bulk-capable endpoints work at
++ * both speeds
++ */
++ if (gadget_is_dualspeed(c->cdev->gadget)) {
++ hs_in_desc.bEndpointAddress =
++ fs_in_desc.bEndpointAddress;
++ hs_out_desc.bEndpointAddress =
++ fs_out_desc.bEndpointAddress;
++
++ /* copy descriptors, and track endpoint copies */
++ f->hs_descriptors = usb_copy_descriptors(hs_eth_function);
++
++ geth->hs.in = usb_find_endpoint(hs_eth_function,
++ f->hs_descriptors, &hs_in_desc);
++ geth->hs.out = usb_find_endpoint(hs_eth_function,
++ f->hs_descriptors, &hs_out_desc);
++ }
++
++ /* NOTE: all that is done without knowing or caring about
++ * the network link ... which is unavailable to this code
++ * until we're activated via set_alt().
++ */
++
++ DBG(cdev, "CDC Subset: %s speed IN/%s OUT/%s\n",
++ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
++ geth->port.in_ep->name, geth->port.out_ep->name);
++ return 0;
++
++fail:
++ /* we might as well release our claims on endpoints */
++ if (geth->port.out)
++ geth->port.out_ep->driver_data = NULL;
++ if (geth->port.in)
++ geth->port.in_ep->driver_data = NULL;
++
++ ERROR(cdev, "%s: can't bind, err %d\n", f->name, status);
++
++ return status;
++}
++
++static void
++geth_unbind(struct usb_configuration *c, struct usb_function *f)
++{
++ if (gadget_is_dualspeed(c->cdev->gadget))
++ usb_free_descriptors(f->hs_descriptors);
++ usb_free_descriptors(f->descriptors);
++ geth_string_defs[1].s = NULL;
++ kfree(func_to_geth(f));
++}
++
++/**
++ * geth_bind_config - add CDC Subset network link to a configuration
++ * @c: the configuration to support the network link
++ * @ethaddr: a buffer in which the ethernet address of the host side
++ * side of the link was recorded
++ * Context: single threaded during gadget setup
++ *
++ * Returns zero on success, else negative errno.
++ *
++ * Caller must have called @gether_setup(). Caller is also responsible
++ * for calling @gether_cleanup() before module unload.
++ */
++int __init geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
++{
++ struct f_gether *geth;
++ int status;
++
++ if (!ethaddr)
++ return -EINVAL;
++
++ /* maybe allocate device-global string IDs */
++ if (geth_string_defs[0].id == 0) {
++
++ /* interface label */
++ status = usb_string_id(c->cdev);
++ if (status < 0)
++ return status;
++ geth_string_defs[0].id = status;
++ subset_data_intf.iInterface = status;
++
++ /* MAC address */
++ status = usb_string_id(c->cdev);
++ if (status < 0)
++ return status;
++ geth_string_defs[1].id = status;
++ ether_desc.iMACAddress = status;
++ }
++
++ /* allocate and initialize one new instance */
++ geth = kzalloc(sizeof *geth, GFP_KERNEL);
++ if (!geth)
++ return -ENOMEM;
++
++ /* export host's Ethernet address in CDC format */
++ snprintf(geth->ethaddr, sizeof geth->ethaddr,
++ "%02X%02X%02X%02X%02X%02X",
++ ethaddr[0], ethaddr[1], ethaddr[2],
++ ethaddr[3], ethaddr[4], ethaddr[5]);
++ geth_string_defs[1].s = geth->ethaddr;
++
++ geth->port.cdc_filter = DEFAULT_FILTER;
++
++ geth->port.func.name = "cdc_subset";
++ geth->port.func.strings = geth_strings;
++ geth->port.func.bind = geth_bind;
++ geth->port.func.unbind = geth_unbind;
++ geth->port.func.set_alt = geth_set_alt;
++ geth->port.func.disable = geth_disable;
++
++ status = usb_add_function(c, &geth->port.func);
++ if (status) {
++ geth_string_defs[1].s = NULL;
++ kfree(geth);
++ }
++ return status;
++}
+diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c
+index 47bb9f0..ea2c31d 100644
+--- a/drivers/usb/gadget/file_storage.c
++++ b/drivers/usb/gadget/file_storage.c
+@@ -308,7 +308,7 @@ MODULE_LICENSE("Dual BSD/GPL");
+ dev_vdbg(&(d)->gadget->dev , fmt , ## args)
+ #define ERROR(d, fmt, args...) \
+ dev_err(&(d)->gadget->dev , fmt , ## args)
+-#define WARN(d, fmt, args...) \
++#define WARNING(d, fmt, args...) \
+ dev_warn(&(d)->gadget->dev , fmt , ## args)
+ #define INFO(d, fmt, args...) \
+ dev_info(&(d)->gadget->dev , fmt , ## args)
+@@ -1091,7 +1091,7 @@ static int ep0_queue(struct fsg_dev *fsg)
+ if (rc != 0 && rc != -ESHUTDOWN) {
+
+ /* We can't do much more than wait for a reset */
+- WARN(fsg, "error in submission: %s --> %d\n",
++ WARNING(fsg, "error in submission: %s --> %d\n",
+ fsg->ep0->name, rc);
+ }
+ return rc;
+@@ -1227,7 +1227,7 @@ static void received_cbi_adsc(struct fsg_dev *fsg, struct fsg_buffhd *bh)
+
+ /* Save the command for later */
+ if (fsg->cbbuf_cmnd_size)
+- WARN(fsg, "CB[I] overwriting previous command\n");
++ WARNING(fsg, "CB[I] overwriting previous command\n");
+ fsg->cbbuf_cmnd_size = req->actual;
+ memcpy(fsg->cbbuf_cmnd, req->buf, fsg->cbbuf_cmnd_size);
+
+@@ -1506,7 +1506,7 @@ static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep,
+ * submissions if DMA is enabled. */
+ if (rc != -ESHUTDOWN && !(rc == -EOPNOTSUPP &&
+ req->length == 0))
+- WARN(fsg, "error in submission: %s --> %d\n",
++ WARNING(fsg, "error in submission: %s --> %d\n",
+ ep->name, rc);
+ }
+ }
+@@ -2294,7 +2294,7 @@ static int halt_bulk_in_endpoint(struct fsg_dev *fsg)
+ VDBG(fsg, "delayed bulk-in endpoint halt\n");
+ while (rc != 0) {
+ if (rc != -EAGAIN) {
+- WARN(fsg, "usb_ep_set_halt -> %d\n", rc);
++ WARNING(fsg, "usb_ep_set_halt -> %d\n", rc);
+ rc = 0;
+ break;
+ }
+@@ -2317,7 +2317,7 @@ static int wedge_bulk_in_endpoint(struct fsg_dev *fsg)
+ VDBG(fsg, "delayed bulk-in endpoint wedge\n");
+ while (rc != 0) {
+ if (rc != -EAGAIN) {
+- WARN(fsg, "usb_ep_set_wedge -> %d\n", rc);
++ WARNING(fsg, "usb_ep_set_wedge -> %d\n", rc);
+ rc = 0;
+ break;
+ }
+@@ -3755,7 +3755,7 @@ static int __init check_parameters(struct fsg_dev *fsg)
+ if (gcnum >= 0)
+ mod_data.release = 0x0300 + gcnum;
+ else {
+- WARN(fsg, "controller '%s' not recognized\n",
++ WARNING(fsg, "controller '%s' not recognized\n",
+ fsg->gadget->name);
+ mod_data.release = 0x0399;
+ }
+@@ -3867,8 +3867,8 @@ static int __init fsg_bind(struct usb_gadget *gadget)
+ curlun->dev.parent = &gadget->dev;
+ curlun->dev.driver = &fsg_driver.driver;
+ dev_set_drvdata(&curlun->dev, fsg);
+- snprintf(curlun->dev.bus_id, BUS_ID_SIZE,
+- "%s-lun%d", gadget->dev.bus_id, i);
++ dev_set_name(&curlun->dev,"%s-lun%d",
++ dev_name(&gadget->dev), i);
+
+ if ((rc = device_register(&curlun->dev)) != 0) {
+ INFO(fsg, "failed to register LUN%d: %d\n", i, rc);
+diff --git a/drivers/usb/gadget/fsl_usb2_udc.c b/drivers/usb/gadget/fsl_usb2_udc.c
+index 1868754..45ad556 100644
+--- a/drivers/usb/gadget/fsl_usb2_udc.c
++++ b/drivers/usb/gadget/fsl_usb2_udc.c
+@@ -223,7 +223,7 @@ static int dr_controller_setup(struct fsl_udc *udc)
+ fsl_writel(tmp, &dr_regs->endpointlistaddr);
+
+ VDBG("vir[qh_base] is %p phy[qh_base] is 0x%8x reg is 0x%8x",
+- (int)udc->ep_qh, (int)tmp,
++ udc->ep_qh, (int)tmp,
+ fsl_readl(&dr_regs->endpointlistaddr));
+
+ /* Config PHY interface */
+@@ -1538,7 +1538,7 @@ static void dtd_complete_irq(struct fsl_udc *udc)
+
+ /* If the ep is configured */
+ if (curr_ep->name == NULL) {
+- WARN("Invalid EP?");
++ WARNING("Invalid EP?");
+ continue;
+ }
+
+@@ -2331,7 +2331,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
+ udc_controller->gadget.name = driver_name;
+
+ /* Setup gadget.dev and register with kernel */
+- strcpy(udc_controller->gadget.dev.bus_id, "gadget");
++ dev_set_name(&udc_controller->gadget.dev, "gadget");
+ udc_controller->gadget.dev.release = fsl_udc_release;
+ udc_controller->gadget.dev.parent = &pdev->dev;
+ ret = device_register(&udc_controller->gadget.dev);
+diff --git a/drivers/usb/gadget/fsl_usb2_udc.h b/drivers/usb/gadget/fsl_usb2_udc.h
+index 98b1483..6131752 100644
+--- a/drivers/usb/gadget/fsl_usb2_udc.h
++++ b/drivers/usb/gadget/fsl_usb2_udc.h
+@@ -552,7 +552,7 @@ static void dump_msg(const char *label, const u8 * buf, unsigned int length)
+ #endif
+
+ #define ERR(stuff...) pr_err("udc: " stuff)
+-#define WARN(stuff...) pr_warning("udc: " stuff)
++#define WARNING(stuff...) pr_warning("udc: " stuff)
+ #define INFO(stuff...) pr_info("udc: " stuff)
+
+ /*-------------------------------------------------------------------------*/
+diff --git a/drivers/usb/gadget/g_zero.h b/drivers/usb/gadget/g_zero.h
+new file mode 100644
+index 0000000..dd2f16a
+--- /dev/null
++++ b/drivers/usb/gadget/g_zero.h
+@@ -0,0 +1,25 @@
++/*
++ * This header declares the utility functions used by "Gadget Zero", plus
++ * interfaces to its two single-configuration function drivers.
++ */
++
++#ifndef __G_ZERO_H
++#define __G_ZERO_H
++
++#include <linux/usb/composite.h>
++
++/* global state */
++extern unsigned buflen;
++extern const struct usb_descriptor_header *otg_desc[];
++
++/* common utilities */
++struct usb_request *alloc_ep_req(struct usb_ep *ep);
++void free_ep_req(struct usb_ep *ep, struct usb_request *req);
++void disable_endpoints(struct usb_composite_dev *cdev,
++ struct usb_ep *in, struct usb_ep *out);
++
++/* configuration-specific linkup */
++int sourcesink_add(struct usb_composite_dev *cdev);
++int loopback_add(struct usb_composite_dev *cdev);
++
++#endif /* __G_ZERO_H */
+diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
+index f7f159c..17d9905 100644
+--- a/drivers/usb/gadget/gadget_chips.h
++++ b/drivers/usb/gadget/gadget_chips.h
+@@ -11,6 +11,10 @@
+ * Some are available on 2.4 kernels; several are available, but not
+ * yet pushed in the 2.6 mainline tree.
+ */
++
++#ifndef __GADGET_CHIPS_H
++#define __GADGET_CHIPS_H
++
+ #ifdef CONFIG_USB_GADGET_NET2280
+ #define gadget_is_net2280(g) !strcmp("net2280", (g)->name)
+ #else
+@@ -29,8 +33,8 @@
+ #define gadget_is_dummy(g) 0
+ #endif
+
+-#ifdef CONFIG_USB_GADGET_PXA2XX
+-#define gadget_is_pxa(g) !strcmp("pxa2xx_udc", (g)->name)
++#ifdef CONFIG_USB_GADGET_PXA25X
++#define gadget_is_pxa(g) !strcmp("pxa25x_udc", (g)->name)
+ #else
+ #define gadget_is_pxa(g) 0
+ #endif
+@@ -214,3 +218,28 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
+ return 0x21;
+ return -ENOENT;
+ }
++
++
++/**
++ * gadget_supports_altsettings - return true if altsettings work
++ * @gadget: the gadget in question
++ */
++static inline bool gadget_supports_altsettings(struct usb_gadget *gadget)
++{
++ /* PXA 21x/25x/26x has no altsettings at all */
++ if (gadget_is_pxa(gadget))
++ return false;
++
++ /* PXA 27x and 3xx have *broken* altsetting support */
++ if (gadget_is_pxa27x(gadget))
++ return false;
++
++ /* SH3 hardware just doesn't do altsettings */
++ if (gadget_is_sh(gadget))
++ return false;
++
++ /* Everything else is *presumably* fine ... */
++ return true;
++}
++
++#endif /* __GADGET_CHIPS_H */
+diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c
+index 7f4d482..ea8651e 100644
+--- a/drivers/usb/gadget/gmidi.c
++++ b/drivers/usb/gadget/gmidi.c
+@@ -138,8 +138,6 @@ static void gmidi_transmit(struct gmidi_device* dev, struct usb_request* req);
+ dev_vdbg(&(d)->gadget->dev , fmt , ## args)
+ #define ERROR(d, fmt, args...) \
+ dev_err(&(d)->gadget->dev , fmt , ## args)
+-#define WARN(d, fmt, args...) \
+- dev_warn(&(d)->gadget->dev , fmt , ## args)
+ #define INFO(d, fmt, args...) \
+ dev_info(&(d)->gadget->dev , fmt , ## args)
+
+diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c
+index be6613a..60aa048 100644
+--- a/drivers/usb/gadget/goku_udc.c
++++ b/drivers/usb/gadget/goku_udc.c
+@@ -1768,7 +1768,7 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+ * usb_gadget_driver_{register,unregister}() must change.
+ */
+ if (the_controller) {
+- WARN(dev, "ignoring %s\n", pci_name(pdev));
++ WARNING(dev, "ignoring %s\n", pci_name(pdev));
+ return -EBUSY;
+ }
+ if (!pdev->irq) {
+@@ -1790,7 +1790,7 @@ static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+ dev->gadget.ops = &goku_ops;
+
+ /* the "gadget" abstracts/virtualizes the controller */
+- strcpy(dev->gadget.dev.bus_id, "gadget");
++ dev_set_name(&dev->gadget.dev, "gadget");
+ dev->gadget.dev.parent = &pdev->dev;
+ dev->gadget.dev.dma_mask = pdev->dev.dma_mask;
+ dev->gadget.dev.release = gadget_release;
+diff --git a/drivers/usb/gadget/goku_udc.h b/drivers/usb/gadget/goku_udc.h
+index bc4eb1e..566cb23 100644
+--- a/drivers/usb/gadget/goku_udc.h
++++ b/drivers/usb/gadget/goku_udc.h
+@@ -285,7 +285,7 @@ struct goku_udc {
+
+ #define ERROR(dev,fmt,args...) \
+ xprintk(dev , KERN_ERR , fmt , ## args)
+-#define WARN(dev,fmt,args...) \
++#define WARNING(dev,fmt,args...) \
+ xprintk(dev , KERN_WARNING , fmt , ## args)
+ #define INFO(dev,fmt,args...) \
+ xprintk(dev , KERN_INFO , fmt , ## args)
+diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c
+index 69b0a27..f4585d3 100644
+--- a/drivers/usb/gadget/inode.c
++++ b/drivers/usb/gadget/inode.c
+@@ -32,6 +32,7 @@
+ #include <asm/uaccess.h>
+ #include <linux/slab.h>
+ #include <linux/poll.h>
++#include <linux/smp_lock.h>
+
+ #include <linux/device.h>
+ #include <linux/moduleparam.h>
+@@ -261,8 +262,6 @@ static const char *CHIP;
+
+ #define ERROR(dev,fmt,args...) \
+ xprintk(dev , KERN_ERR , fmt , ## args)
+-#define WARN(dev,fmt,args...) \
+- xprintk(dev , KERN_WARNING , fmt , ## args)
+ #define INFO(dev,fmt,args...) \
+ xprintk(dev , KERN_INFO , fmt , ## args)
+
+@@ -483,8 +482,7 @@ ep_release (struct inode *inode, struct file *fd)
+ return 0;
+ }
+
+-static int ep_ioctl (struct inode *inode, struct file *fd,
+- unsigned code, unsigned long value)
++static long ep_ioctl(struct file *fd, unsigned code, unsigned long value)
+ {
+ struct ep_data *data = fd->private_data;
+ int status;
+@@ -740,7 +738,7 @@ static const struct file_operations ep_io_operations = {
+
+ .read = ep_read,
+ .write = ep_write,
+- .ioctl = ep_ioctl,
++ .unlocked_ioctl = ep_ioctl,
+ .release = ep_release,
+
+ .aio_read = ep_aio_read,
+@@ -1294,15 +1292,18 @@ out:
+ return mask;
+ }
+
+-static int dev_ioctl (struct inode *inode, struct file *fd,
+- unsigned code, unsigned long value)
++static long dev_ioctl (struct file *fd, unsigned code, unsigned long value)
+ {
+ struct dev_data *dev = fd->private_data;
+ struct usb_gadget *gadget = dev->gadget;
++ long ret = -ENOTTY;
+
+- if (gadget->ops->ioctl)
+- return gadget->ops->ioctl (gadget, code, value);
+- return -ENOTTY;
++ if (gadget->ops->ioctl) {
++ lock_kernel();
++ ret = gadget->ops->ioctl (gadget, code, value);
++ unlock_kernel();
++ }
++ return ret;
+ }
+
+ /* used after device configuration */
+@@ -1314,7 +1315,7 @@ static const struct file_operations ep0_io_operations = {
+ .write = ep0_write,
+ .fasync = ep0_fasync,
+ .poll = ep0_poll,
+- .ioctl = dev_ioctl,
++ .unlocked_ioctl = dev_ioctl,
+ .release = dev_release,
+ };
+
+@@ -1501,7 +1502,7 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
+ }
+ break;
+
+-#ifndef CONFIG_USB_GADGET_PXA2XX
++#ifndef CONFIG_USB_GADGET_PXA25X
+ /* PXA automagically handles this request too */
+ case USB_REQ_GET_CONFIGURATION:
+ if (ctrl->bRequestType != 0x80)
+@@ -1964,7 +1965,7 @@ static const struct file_operations dev_init_operations = {
+ .open = dev_open,
+ .write = dev_config,
+ .fasync = ep0_fasync,
+- .ioctl = dev_ioctl,
++ .unlocked_ioctl = dev_ioctl,
+ .release = dev_release,
+ };
+
+diff --git a/drivers/usb/gadget/lh7a40x_udc.c b/drivers/usb/gadget/lh7a40x_udc.c
+index 825abd2..c6e7df0 100644
+--- a/drivers/usb/gadget/lh7a40x_udc.c
++++ b/drivers/usb/gadget/lh7a40x_udc.c
+@@ -1970,7 +1970,7 @@ static const struct usb_gadget_ops lh7a40x_udc_ops = {
+
+ static void nop_release(struct device *dev)
+ {
+- DEBUG("%s %s\n", __func__, dev->bus_id);
++ DEBUG("%s %s\n", __func__, dev_name(dev));
+ }
+
+ static struct lh7a40x_udc memory = {
+diff --git a/drivers/usb/gadget/lh7a40x_udc.h b/drivers/usb/gadget/lh7a40x_udc.h
+index 1ecfd63..ca86120 100644
+--- a/drivers/usb/gadget/lh7a40x_udc.h
++++ b/drivers/usb/gadget/lh7a40x_udc.h
+@@ -47,7 +47,7 @@
+ #include <asm/irq.h>
+ #include <asm/system.h>
+ #include <asm/unaligned.h>
+-#include <asm/hardware.h>
++#include <mach/hardware.h>
+
+ #include <linux/usb/ch9.h>
+ #include <linux/usb/gadget.h>
+diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c
+index ee6b35f..77b44fb 100644
+--- a/drivers/usb/gadget/m66592-udc.c
++++ b/drivers/usb/gadget/m66592-udc.c
+@@ -1593,7 +1593,7 @@ static int __init m66592_probe(struct platform_device *pdev)
+
+ m66592->gadget.ops = &m66592_gadget_ops;
+ device_initialize(&m66592->gadget.dev);
+- strcpy(m66592->gadget.dev.bus_id, "gadget");
++ dev_set_name(&m66592->gadget.dev, "gadget");
+ m66592->gadget.is_dualspeed = 1;
+ m66592->gadget.dev.parent = &pdev->dev;
+ m66592->gadget.dev.dma_mask = pdev->dev.dma_mask;
+diff --git a/drivers/usb/gadget/ndis.h b/drivers/usb/gadget/ndis.h
+index 09e3ee4..df886ce 100644
+--- a/drivers/usb/gadget/ndis.h
++++ b/drivers/usb/gadget/ndis.h
+@@ -1,11 +1,11 @@
+ /*
+- * ndis.h
+- *
++ * ndis.h
++ *
+ * ntddndis.h modified by Benedikt Spranger <b.spranger@pengutronix.de>
+- *
+- * Thanks to the cygwin development team,
++ *
++ * Thanks to the cygwin development team,
+ * espacially to Casper S. Hornstrup <chorns@users.sourceforge.net>
+- *
++ *
+ * THIS SOFTWARE IS NOT COPYRIGHTED
+ *
+ * This source code is offered for use in the public domain. You may
+diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c
+index e018623..5cfb5eb 100644
+--- a/drivers/usb/gadget/net2280.c
++++ b/drivers/usb/gadget/net2280.c
+@@ -1007,7 +1007,7 @@ static void scan_dma_completions (struct net2280_ep *ep)
+ * 0122, and 0124; not all cases trigger the warning.
+ */
+ if ((tmp & (1 << NAK_OUT_PACKETS)) == 0) {
+- WARN (ep->dev, "%s lost packet sync!\n",
++ WARNING (ep->dev, "%s lost packet sync!\n",
+ ep->ep.name);
+ req->req.status = -EOVERFLOW;
+ } else if ((tmp = readl (&ep->regs->ep_avail)) != 0) {
+@@ -2768,7 +2768,7 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id)
+ dev->gadget.is_dualspeed = 1;
+
+ /* the "gadget" abstracts/virtualizes the controller */
+- strcpy (dev->gadget.dev.bus_id, "gadget");
++ dev_set_name(&dev->gadget.dev, "gadget");
+ dev->gadget.dev.parent = &pdev->dev;
+ dev->gadget.dev.dma_mask = pdev->dev.dma_mask;
+ dev->gadget.dev.release = gadget_release;
+diff --git a/drivers/usb/gadget/net2280.h b/drivers/usb/gadget/net2280.h
+index 1f2af39..81a71db 100644
+--- a/drivers/usb/gadget/net2280.h
++++ b/drivers/usb/gadget/net2280.h
+@@ -272,7 +272,7 @@ static inline void net2280_led_shutdown (struct net2280 *dev)
+
+ #define ERROR(dev,fmt,args...) \
+ xprintk(dev , KERN_ERR , fmt , ## args)
+-#define WARN(dev,fmt,args...) \
++#define WARNING(dev,fmt,args...) \
+ xprintk(dev , KERN_WARNING , fmt , ## args)
+ #define INFO(dev,fmt,args...) \
+ xprintk(dev , KERN_INFO , fmt , ## args)
+diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c
+index 881d74c..bb54cca 100644
+--- a/drivers/usb/gadget/omap_udc.c
++++ b/drivers/usb/gadget/omap_udc.c
+@@ -52,8 +52,9 @@
+ #include <asm/unaligned.h>
+ #include <asm/mach-types.h>
+
+-#include <asm/arch/dma.h>
+-#include <asm/arch/usb.h>
++#include <mach/dma.h>
++#include <mach/usb.h>
++#include <mach/control.h>
+
+ #include "omap_udc.h"
+
+@@ -135,13 +136,17 @@ static void use_ep(struct omap_ep *ep, u16 select)
+
+ if (ep->bEndpointAddress & USB_DIR_IN)
+ num |= UDC_EP_DIR;
+- UDC_EP_NUM_REG = num | select;
++ omap_writew(num | select, UDC_EP_NUM);
+ /* when select, MUST deselect later !! */
+ }
+
+ static inline void deselect_ep(void)
+ {
+- UDC_EP_NUM_REG &= ~UDC_EP_SEL;
++ u16 w;
++
++ w = omap_readw(UDC_EP_NUM);
++ w &= ~UDC_EP_SEL;
++ omap_writew(w, UDC_EP_NUM);
+ /* 6 wait states before TX will happen */
+ }
+
+@@ -216,7 +221,7 @@ static int omap_ep_enable(struct usb_ep *_ep,
+ ep->has_dma = 0;
+ ep->lch = -1;
+ use_ep(ep, UDC_EP_SEL);
+- UDC_CTRL_REG = udc->clr_halt;
++ omap_writew(udc->clr_halt, UDC_CTRL);
+ ep->ackwait = 0;
+ deselect_ep();
+
+@@ -232,7 +237,7 @@ static int omap_ep_enable(struct usb_ep *_ep,
+ if (desc->bmAttributes != USB_ENDPOINT_XFER_ISOC
+ && !ep->has_dma
+ && !(ep->bEndpointAddress & USB_DIR_IN)) {
+- UDC_CTRL_REG = UDC_SET_FIFO_EN;
++ omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
+ ep->ackwait = 1 + ep->double_buf;
+ }
+
+@@ -259,7 +264,7 @@ static int omap_ep_disable(struct usb_ep *_ep)
+ nuke (ep, -ESHUTDOWN);
+ ep->ep.maxpacket = ep->maxpacket;
+ ep->has_dma = 0;
+- UDC_CTRL_REG = UDC_SET_HALT;
++ omap_writew(UDC_SET_HALT, UDC_CTRL);
+ list_del_init(&ep->iso);
+ del_timer(&ep->timer);
+
+@@ -360,13 +365,13 @@ write_packet(u8 *buf, struct omap_req *req, unsigned max)
+ if (likely((((int)buf) & 1) == 0)) {
+ wp = (u16 *)buf;
+ while (max >= 2) {
+- UDC_DATA_REG = *wp++;
++ omap_writew(*wp++, UDC_DATA);
+ max -= 2;
+ }
+ buf = (u8 *)wp;
+ }
+ while (max--)
+- *(volatile u8 *)&UDC_DATA_REG = *buf++;
++ omap_writeb(*buf++, UDC_DATA);
+ return len;
+ }
+
+@@ -385,13 +390,13 @@ static int write_fifo(struct omap_ep *ep, struct omap_req *req)
+ prefetch(buf);
+
+ /* PIO-IN isn't double buffered except for iso */
+- ep_stat = UDC_STAT_FLG_REG;
++ ep_stat = omap_readw(UDC_STAT_FLG);
+ if (ep_stat & UDC_FIFO_UNWRITABLE)
+ return 0;
+
+ count = ep->ep.maxpacket;
+ count = write_packet(buf, req, count);
+- UDC_CTRL_REG = UDC_SET_FIFO_EN;
++ omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
+ ep->ackwait = 1;
+
+ /* last packet is often short (sometimes a zlp) */
+@@ -425,13 +430,13 @@ read_packet(u8 *buf, struct omap_req *req, unsigned avail)
+ if (likely((((int)buf) & 1) == 0)) {
+ wp = (u16 *)buf;
+ while (avail >= 2) {
+- *wp++ = UDC_DATA_REG;
++ *wp++ = omap_readw(UDC_DATA);
+ avail -= 2;
+ }
+ buf = (u8 *)wp;
+ }
+ while (avail--)
+- *buf++ = *(volatile u8 *)&UDC_DATA_REG;
++ *buf++ = omap_readb(UDC_DATA);
+ return len;
+ }
+
+@@ -446,7 +451,7 @@ static int read_fifo(struct omap_ep *ep, struct omap_req *req)
+ prefetchw(buf);
+
+ for (;;) {
+- u16 ep_stat = UDC_STAT_FLG_REG;
++ u16 ep_stat = omap_readw(UDC_STAT_FLG);
+
+ is_last = 0;
+ if (ep_stat & FIFO_EMPTY) {
+@@ -460,7 +465,7 @@ static int read_fifo(struct omap_ep *ep, struct omap_req *req)
+ if (ep_stat & UDC_FIFO_FULL)
+ avail = ep->ep.maxpacket;
+ else {
+- avail = UDC_RXFSTAT_REG;
++ avail = omap_readw(UDC_RXFSTAT);
+ ep->fnf = ep->double_buf;
+ }
+ count = read_packet(buf, req, avail);
+@@ -473,7 +478,7 @@ static int read_fifo(struct omap_ep *ep, struct omap_req *req)
+ req->req.status = -EOVERFLOW;
+ avail -= count;
+ while (avail--)
+- (void) *(volatile u8 *)&UDC_DATA_REG;
++ omap_readw(UDC_DATA);
+ }
+ } else if (req->req.length == req->req.actual)
+ is_last = 1;
+@@ -491,32 +496,6 @@ static int read_fifo(struct omap_ep *ep, struct omap_req *req)
+
+ /*-------------------------------------------------------------------------*/
+
+-static inline dma_addr_t dma_csac(unsigned lch)
+-{
+- dma_addr_t csac;
+-
+- /* omap 3.2/3.3 erratum: sometimes 0 is returned if CSAC/CDAC is
+- * read before the DMA controller finished disabling the channel.
+- */
+- csac = OMAP_DMA_CSAC_REG(lch);
+- if (csac == 0)
+- csac = OMAP_DMA_CSAC_REG(lch);
+- return csac;
+-}
+-
+-static inline dma_addr_t dma_cdac(unsigned lch)
+-{
+- dma_addr_t cdac;
+-
+- /* omap 3.2/3.3 erratum: sometimes 0 is returned if CSAC/CDAC is
+- * read before the DMA controller finished disabling the channel.
+- */
+- cdac = OMAP_DMA_CDAC_REG(lch);
+- if (cdac == 0)
+- cdac = OMAP_DMA_CDAC_REG(lch);
+- return cdac;
+-}
+-
+ static u16 dma_src_len(struct omap_ep *ep, dma_addr_t start)
+ {
+ dma_addr_t end;
+@@ -527,7 +506,7 @@ static u16 dma_src_len(struct omap_ep *ep, dma_addr_t start)
+ if (cpu_is_omap15xx())
+ return 0;
+
+- end = dma_csac(ep->lch);
++ end = omap_get_dma_src_pos(ep->lch);
+ if (end == ep->dma_counter)
+ return 0;
+
+@@ -537,15 +516,11 @@ static u16 dma_src_len(struct omap_ep *ep, dma_addr_t start)
+ return end - start;
+ }
+
+-#define DMA_DEST_LAST(x) (cpu_is_omap15xx() \
+- ? OMAP_DMA_CSAC_REG(x) /* really: CPC */ \
+- : dma_cdac(x))
+-
+ static u16 dma_dest_len(struct omap_ep *ep, dma_addr_t start)
+ {
+ dma_addr_t end;
+
+- end = DMA_DEST_LAST(ep->lch);
++ end = omap_get_dma_dst_pos(ep->lch);
+ if (end == ep->dma_counter)
+ return 0;
+
+@@ -565,7 +540,7 @@ static u16 dma_dest_len(struct omap_ep *ep, dma_addr_t start)
+
+ static void next_in_dma(struct omap_ep *ep, struct omap_req *req)
+ {
+- u16 txdma_ctrl;
++ u16 txdma_ctrl, w;
+ unsigned length = req->req.length - req->req.actual;
+ const int sync_mode = cpu_is_omap15xx()
+ ? OMAP_DMA_SYNC_FRAME
+@@ -596,14 +571,18 @@ static void next_in_dma(struct omap_ep *ep, struct omap_req *req)
+ 0, 0);
+
+ omap_start_dma(ep->lch);
+- ep->dma_counter = dma_csac(ep->lch);
+- UDC_DMA_IRQ_EN_REG |= UDC_TX_DONE_IE(ep->dma_channel);
+- UDC_TXDMA_REG(ep->dma_channel) = UDC_TXN_START | txdma_ctrl;
++ ep->dma_counter = omap_get_dma_src_pos(ep->lch);
++ w = omap_readw(UDC_DMA_IRQ_EN);
++ w |= UDC_TX_DONE_IE(ep->dma_channel);
++ omap_writew(w, UDC_DMA_IRQ_EN);
++ omap_writew(UDC_TXN_START | txdma_ctrl, UDC_TXDMA(ep->dma_channel));
+ req->dma_bytes = length;
+ }
+
+ static void finish_in_dma(struct omap_ep *ep, struct omap_req *req, int status)
+ {
++ u16 w;
++
+ if (status == 0) {
+ req->req.actual += req->dma_bytes;
+
+@@ -620,7 +599,9 @@ static void finish_in_dma(struct omap_ep *ep, struct omap_req *req, int status)
+
+ /* tx completion */
+ omap_stop_dma(ep->lch);
+- UDC_DMA_IRQ_EN_REG &= ~UDC_TX_DONE_IE(ep->dma_channel);
++ w = omap_readw(UDC_DMA_IRQ_EN);
++ w &= ~UDC_TX_DONE_IE(ep->dma_channel);
++ omap_writew(w, UDC_DMA_IRQ_EN);
+ done(ep, req, status);
+ }
+
+@@ -628,6 +609,7 @@ static void next_out_dma(struct omap_ep *ep, struct omap_req *req)
+ {
+ unsigned packets = req->req.length - req->req.actual;
+ int dma_trigger = 0;
++ u16 w;
+
+ if (cpu_is_omap24xx())
+ dma_trigger = OMAP24XX_DMA(USB_W2FC_RX0, ep->dma_channel);
+@@ -654,12 +636,14 @@ static void next_out_dma(struct omap_ep *ep, struct omap_req *req)
+ omap_set_dma_dest_params(ep->lch, OMAP_DMA_PORT_EMIFF,
+ OMAP_DMA_AMODE_POST_INC, req->req.dma + req->req.actual,
+ 0, 0);
+- ep->dma_counter = DMA_DEST_LAST(ep->lch);
++ ep->dma_counter = omap_get_dma_dst_pos(ep->lch);
+
+- UDC_RXDMA_REG(ep->dma_channel) = UDC_RXN_STOP | (packets - 1);
+- UDC_DMA_IRQ_EN_REG |= UDC_RX_EOT_IE(ep->dma_channel);
+- UDC_EP_NUM_REG = (ep->bEndpointAddress & 0xf);
+- UDC_CTRL_REG = UDC_SET_FIFO_EN;
++ omap_writew(UDC_RXN_STOP | (packets - 1), UDC_RXDMA(ep->dma_channel));
++ w = omap_readw(UDC_DMA_IRQ_EN);
++ w |= UDC_RX_EOT_IE(ep->dma_channel);
++ omap_writew(w, UDC_DMA_IRQ_EN);
++ omap_writew(ep->bEndpointAddress & 0xf, UDC_EP_NUM);
++ omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
+
+ omap_start_dma(ep->lch);
+ }
+@@ -667,7 +651,7 @@ static void next_out_dma(struct omap_ep *ep, struct omap_req *req)
+ static void
+ finish_out_dma(struct omap_ep *ep, struct omap_req *req, int status, int one)
+ {
+- u16 count;
++ u16 count, w;
+
+ if (status == 0)
+ ep->dma_counter = (u16) (req->req.dma + req->req.actual);
+@@ -686,13 +670,15 @@ finish_out_dma(struct omap_ep *ep, struct omap_req *req, int status, int one)
+ return;
+
+ /* rx completion */
+- UDC_DMA_IRQ_EN_REG &= ~UDC_RX_EOT_IE(ep->dma_channel);
++ w = omap_readw(UDC_DMA_IRQ_EN);
++ w &= ~UDC_RX_EOT_IE(ep->dma_channel);
++ omap_writew(w, UDC_DMA_IRQ_EN);
+ done(ep, req, status);
+ }
+
+ static void dma_irq(struct omap_udc *udc, u16 irq_src)
+ {
+- u16 dman_stat = UDC_DMAN_STAT_REG;
++ u16 dman_stat = omap_readw(UDC_DMAN_STAT);
+ struct omap_ep *ep;
+ struct omap_req *req;
+
+@@ -706,7 +692,7 @@ static void dma_irq(struct omap_udc *udc, u16 irq_src)
+ struct omap_req, queue);
+ finish_in_dma(ep, req, 0);
+ }
+- UDC_IRQ_SRC_REG = UDC_TXN_DONE;
++ omap_writew(UDC_TXN_DONE, UDC_IRQ_SRC);
+
+ if (!list_empty (&ep->queue)) {
+ req = container_of(ep->queue.next,
+@@ -725,7 +711,7 @@ static void dma_irq(struct omap_udc *udc, u16 irq_src)
+ struct omap_req, queue);
+ finish_out_dma(ep, req, 0, dman_stat & UDC_DMA_RX_SB);
+ }
+- UDC_IRQ_SRC_REG = UDC_RXN_EOT;
++ omap_writew(UDC_RXN_EOT, UDC_IRQ_SRC);
+
+ if (!list_empty (&ep->queue)) {
+ req = container_of(ep->queue.next,
+@@ -739,7 +725,7 @@ static void dma_irq(struct omap_udc *udc, u16 irq_src)
+ ep->irqs++;
+ /* omap15xx does this unasked... */
+ VDBG("%s, RX_CNT irq?\n", ep->ep.name);
+- UDC_IRQ_SRC_REG = UDC_RXN_CNT;
++ omap_writew(UDC_RXN_CNT, UDC_IRQ_SRC);
+ }
+ }
+
+@@ -762,9 +748,9 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel)
+
+ is_in = ep->bEndpointAddress & USB_DIR_IN;
+ if (is_in)
+- reg = UDC_TXDMA_CFG_REG;
++ reg = omap_readw(UDC_TXDMA_CFG);
+ else
+- reg = UDC_RXDMA_CFG_REG;
++ reg = omap_readw(UDC_RXDMA_CFG);
+ reg |= UDC_DMA_REQ; /* "pulse" activated */
+
+ ep->dma_channel = 0;
+@@ -792,7 +778,7 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel)
+ status = omap_request_dma(dma_channel,
+ ep->ep.name, dma_error, ep, &ep->lch);
+ if (status == 0) {
+- UDC_TXDMA_CFG_REG = reg;
++ omap_writew(reg, UDC_TXDMA_CFG);
+ /* EMIFF or SDRC */
+ omap_set_dma_src_burst_mode(ep->lch,
+ OMAP_DMA_DATA_BURST_4);
+@@ -801,7 +787,7 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel)
+ omap_set_dma_dest_params(ep->lch,
+ OMAP_DMA_PORT_TIPB,
+ OMAP_DMA_AMODE_CONSTANT,
+- (unsigned long) io_v2p((u32)&UDC_DATA_DMA_REG),
++ UDC_DATA_DMA,
+ 0, 0);
+ }
+ } else {
+@@ -813,12 +799,12 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel)
+ status = omap_request_dma(dma_channel,
+ ep->ep.name, dma_error, ep, &ep->lch);
+ if (status == 0) {
+- UDC_RXDMA_CFG_REG = reg;
++ omap_writew(reg, UDC_RXDMA_CFG);
+ /* TIPB */
+ omap_set_dma_src_params(ep->lch,
+ OMAP_DMA_PORT_TIPB,
+ OMAP_DMA_AMODE_CONSTANT,
+- (unsigned long) io_v2p((u32)&UDC_DATA_DMA_REG),
++ UDC_DATA_DMA,
+ 0, 0);
+ /* EMIFF or SDRC */
+ omap_set_dma_dest_burst_mode(ep->lch,
+@@ -834,7 +820,7 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel)
+
+ /* channel type P: hw synch (fifo) */
+ if (cpu_class_is_omap1() && !cpu_is_omap15xx())
+- OMAP1_DMA_LCH_CTRL_REG(ep->lch) = 2;
++ omap_set_dma_channel_mode(ep->lch, OMAP_DMA_LCH_P);
+ }
+
+ just_restart:
+@@ -860,7 +846,7 @@ just_restart:
+ (is_in ? write_fifo : read_fifo)(ep, req);
+ deselect_ep();
+ if (!is_in) {
+- UDC_CTRL_REG = UDC_SET_FIFO_EN;
++ omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
+ ep->ackwait = 1 + ep->double_buf;
+ }
+ /* IN: 6 wait states before it'll tx */
+@@ -881,7 +867,7 @@ static void dma_channel_release(struct omap_ep *ep)
+ else
+ req = NULL;
+
+- active = ((1 << 7) & OMAP_DMA_CCR_REG(ep->lch)) != 0;
++ active = omap_get_dma_active_status(ep->lch);
+
+ DBG("%s release %s %cxdma%d %p\n", ep->ep.name,
+ active ? "active" : "idle",
+@@ -894,23 +880,25 @@ static void dma_channel_release(struct omap_ep *ep)
+
+ /* wait till current packet DMA finishes, and fifo empties */
+ if (ep->bEndpointAddress & USB_DIR_IN) {
+- UDC_TXDMA_CFG_REG = (UDC_TXDMA_CFG_REG & ~mask) | UDC_DMA_REQ;
++ omap_writew((omap_readw(UDC_TXDMA_CFG) & ~mask) | UDC_DMA_REQ,
++ UDC_TXDMA_CFG);
+
+ if (req) {
+ finish_in_dma(ep, req, -ECONNRESET);
+
+ /* clear FIFO; hosts probably won't empty it */
+ use_ep(ep, UDC_EP_SEL);
+- UDC_CTRL_REG = UDC_CLR_EP;
++ omap_writew(UDC_CLR_EP, UDC_CTRL);
+ deselect_ep();
+ }
+- while (UDC_TXDMA_CFG_REG & mask)
++ while (omap_readw(UDC_TXDMA_CFG) & mask)
+ udelay(10);
+ } else {
+- UDC_RXDMA_CFG_REG = (UDC_RXDMA_CFG_REG & ~mask) | UDC_DMA_REQ;
++ omap_writew((omap_readw(UDC_RXDMA_CFG) & ~mask) | UDC_DMA_REQ,
++ UDC_RXDMA_CFG);
+
+ /* dma empties the fifo */
+- while (UDC_RXDMA_CFG_REG & mask)
++ while (omap_readw(UDC_RXDMA_CFG) & mask)
+ udelay(10);
+ if (req)
+ finish_out_dma(ep, req, -ECONNRESET, 0);
+@@ -997,9 +985,13 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
+ req->req.actual = 0;
+
+ /* maybe kickstart non-iso i/o queues */
+- if (is_iso)
+- UDC_IRQ_EN_REG |= UDC_SOF_IE;
+- else if (list_empty(&ep->queue) && !ep->stopped && !ep->ackwait) {
++ if (is_iso) {
++ u16 w;
++
++ w = omap_readw(UDC_IRQ_EN);
++ w |= UDC_SOF_IE;
++ omap_writew(w, UDC_IRQ_EN);
++ } else if (list_empty(&ep->queue) && !ep->stopped && !ep->ackwait) {
+ int is_in;
+
+ if (ep->bEndpointAddress == 0) {
+@@ -1017,23 +1009,23 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
+ * requests to non-control endpoints
+ */
+ if (udc->ep0_set_config) {
+- u16 irq_en = UDC_IRQ_EN_REG;
++ u16 irq_en = omap_readw(UDC_IRQ_EN);
+
+ irq_en |= UDC_DS_CHG_IE | UDC_EP0_IE;
+ if (!udc->ep0_reset_config)
+ irq_en |= UDC_EPN_RX_IE
+ | UDC_EPN_TX_IE;
+- UDC_IRQ_EN_REG = irq_en;
++ omap_writew(irq_en, UDC_IRQ_EN);
+ }
+
+ /* STATUS for zero length DATA stages is
+ * always an IN ... even for IN transfers,
+ * a weird case which seem to stall OMAP.
+ */
+- UDC_EP_NUM_REG = (UDC_EP_SEL|UDC_EP_DIR);
+- UDC_CTRL_REG = UDC_CLR_EP;
+- UDC_CTRL_REG = UDC_SET_FIFO_EN;
+- UDC_EP_NUM_REG = UDC_EP_DIR;
++ omap_writew(UDC_EP_SEL | UDC_EP_DIR, UDC_EP_NUM);
++ omap_writew(UDC_CLR_EP, UDC_CTRL);
++ omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
++ omap_writew(UDC_EP_DIR, UDC_EP_NUM);
+
+ /* cleanup */
+ udc->ep0_pending = 0;
+@@ -1042,11 +1034,11 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
+
+ /* non-empty DATA stage */
+ } else if (is_in) {
+- UDC_EP_NUM_REG = UDC_EP_SEL|UDC_EP_DIR;
++ omap_writew(UDC_EP_SEL | UDC_EP_DIR, UDC_EP_NUM);
+ } else {
+ if (udc->ep0_setup)
+ goto irq_wait;
+- UDC_EP_NUM_REG = UDC_EP_SEL;
++ omap_writew(UDC_EP_SEL, UDC_EP_NUM);
+ }
+ } else {
+ is_in = ep->bEndpointAddress & USB_DIR_IN;
+@@ -1062,7 +1054,7 @@ omap_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
+ req = NULL;
+ deselect_ep();
+ if (!is_in) {
+- UDC_CTRL_REG = UDC_SET_FIFO_EN;
++ omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
+ ep->ackwait = 1 + ep->double_buf;
+ }
+ /* IN: 6 wait states before it'll tx */
+@@ -1129,10 +1121,10 @@ static int omap_ep_set_halt(struct usb_ep *_ep, int value)
+ status = -EINVAL;
+ else if (value) {
+ if (ep->udc->ep0_set_config) {
+- WARN("error changing config?\n");
+- UDC_SYSCON2_REG = UDC_CLR_CFG;
++ WARNING("error changing config?\n");
++ omap_writew(UDC_CLR_CFG, UDC_SYSCON2);
+ }
+- UDC_SYSCON2_REG = UDC_STALL_CMD;
++ omap_writew(UDC_STALL_CMD, UDC_SYSCON2);
+ ep->udc->ep0_pending = 0;
+ status = 0;
+ } else /* NOP */
+@@ -1159,8 +1151,8 @@ static int omap_ep_set_halt(struct usb_ep *_ep, int value)
+ channel = 0;
+
+ use_ep(ep, UDC_EP_SEL);
+- if (UDC_STAT_FLG_REG & UDC_NON_ISO_FIFO_EMPTY) {
+- UDC_CTRL_REG = UDC_SET_HALT;
++ if (omap_readw(UDC_STAT_FLG) & UDC_NON_ISO_FIFO_EMPTY) {
++ omap_writew(UDC_SET_HALT, UDC_CTRL);
+ status = 0;
+ } else
+ status = -EAGAIN;
+@@ -1170,10 +1162,10 @@ static int omap_ep_set_halt(struct usb_ep *_ep, int value)
+ dma_channel_claim(ep, channel);
+ } else {
+ use_ep(ep, 0);
+- UDC_CTRL_REG = ep->udc->clr_halt;
++ omap_writew(ep->udc->clr_halt, UDC_CTRL);
+ ep->ackwait = 0;
+ if (!(ep->bEndpointAddress & USB_DIR_IN)) {
+- UDC_CTRL_REG = UDC_SET_FIFO_EN;
++ omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
+ ep->ackwait = 1 + ep->double_buf;
+ }
+ }
+@@ -1205,7 +1197,7 @@ static struct usb_ep_ops omap_ep_ops = {
+
+ static int omap_get_frame(struct usb_gadget *gadget)
+ {
+- u16 sof = UDC_SOF_REG;
++ u16 sof = omap_readw(UDC_SOF);
+ return (sof & UDC_TS_OK) ? (sof & UDC_TS) : -EL2NSYNC;
+ }
+
+@@ -1224,7 +1216,7 @@ static int omap_wakeup(struct usb_gadget *gadget)
+ */
+ if (udc->devstat & (UDC_B_HNP_ENABLE|UDC_R_WK_OK)) {
+ DBG("remote wakeup...\n");
+- UDC_SYSCON2_REG = UDC_RMT_WKP;
++ omap_writew(UDC_RMT_WKP, UDC_SYSCON2);
+ retval = 0;
+ }
+
+@@ -1247,12 +1239,12 @@ omap_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered)
+
+ udc = container_of(gadget, struct omap_udc, gadget);
+ spin_lock_irqsave(&udc->lock, flags);
+- syscon1 = UDC_SYSCON1_REG;
++ syscon1 = omap_readw(UDC_SYSCON1);
+ if (is_selfpowered)
+ syscon1 |= UDC_SELF_PWR;
+ else
+ syscon1 &= ~UDC_SELF_PWR;
+- UDC_SYSCON1_REG = syscon1;
++ omap_writew(syscon1, UDC_SYSCON1);
+ spin_unlock_irqrestore(&udc->lock, flags);
+
+ return 0;
+@@ -1265,18 +1257,36 @@ static int can_pullup(struct omap_udc *udc)
+
+ static void pullup_enable(struct omap_udc *udc)
+ {
+- UDC_SYSCON1_REG |= UDC_PULLUP_EN;
+- if (!gadget_is_otg(&udc->gadget) && !cpu_is_omap15xx())
+- OTG_CTRL_REG |= OTG_BSESSVLD;
+- UDC_IRQ_EN_REG = UDC_DS_CHG_IE;
++ u16 w;
++
++ w = omap_readw(UDC_SYSCON1);
++ w |= UDC_PULLUP_EN;
++ omap_writew(w, UDC_SYSCON1);
++ if (!gadget_is_otg(&udc->gadget) && !cpu_is_omap15xx()) {
++ u32 l;
++
++ l = omap_readl(OTG_CTRL);
++ l |= OTG_BSESSVLD;
++ omap_writel(l, OTG_CTRL);
++ }
++ omap_writew(UDC_DS_CHG_IE, UDC_IRQ_EN);
+ }
+
+ static void pullup_disable(struct omap_udc *udc)
+ {
+- if (!gadget_is_otg(&udc->gadget) && !cpu_is_omap15xx())
+- OTG_CTRL_REG &= ~OTG_BSESSVLD;
+- UDC_IRQ_EN_REG = UDC_DS_CHG_IE;
+- UDC_SYSCON1_REG &= ~UDC_PULLUP_EN;
++ u16 w;
++
++ if (!gadget_is_otg(&udc->gadget) && !cpu_is_omap15xx()) {
++ u32 l;
++
++ l = omap_readl(OTG_CTRL);
++ l &= ~OTG_BSESSVLD;
++ omap_writel(l, OTG_CTRL);
++ }
++ omap_writew(UDC_DS_CHG_IE, UDC_IRQ_EN);
++ w = omap_readw(UDC_SYSCON1);
++ w &= ~UDC_PULLUP_EN;
++ omap_writew(w, UDC_SYSCON1);
+ }
+
+ static struct omap_udc *udc;
+@@ -1304,6 +1314,7 @@ static int omap_vbus_session(struct usb_gadget *gadget, int is_active)
+ {
+ struct omap_udc *udc;
+ unsigned long flags;
++ u32 l;
+
+ udc = container_of(gadget, struct omap_udc, gadget);
+ spin_lock_irqsave(&udc->lock, flags);
+@@ -1311,10 +1322,12 @@ static int omap_vbus_session(struct usb_gadget *gadget, int is_active)
+ udc->vbus_active = (is_active != 0);
+ if (cpu_is_omap15xx()) {
+ /* "software" detect, ignored if !VBUS_MODE_1510 */
++ l = omap_readl(FUNC_MUX_CTRL_0);
+ if (is_active)
+- FUNC_MUX_CTRL_0_REG |= VBUS_CTRL_1510;
++ l |= VBUS_CTRL_1510;
+ else
+- FUNC_MUX_CTRL_0_REG &= ~VBUS_CTRL_1510;
++ l &= ~VBUS_CTRL_1510;
++ omap_writel(l, FUNC_MUX_CTRL_0);
+ }
+ if (udc->dc_clk != NULL && is_active) {
+ if (!udc->clk_requested) {
+@@ -1384,9 +1397,9 @@ static void nuke(struct omap_ep *ep, int status)
+ dma_channel_release(ep);
+
+ use_ep(ep, 0);
+- UDC_CTRL_REG = UDC_CLR_EP;
++ omap_writew(UDC_CLR_EP, UDC_CTRL);
+ if (ep->bEndpointAddress && ep->bmAttributes != USB_ENDPOINT_XFER_ISOC)
+- UDC_CTRL_REG = UDC_SET_HALT;
++ omap_writew(UDC_SET_HALT, UDC_CTRL);
+
+ while (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next, struct omap_req, queue);
+@@ -1414,8 +1427,8 @@ static void update_otg(struct omap_udc *udc)
+ if (!gadget_is_otg(&udc->gadget))
+ return;
+
+- if (OTG_CTRL_REG & OTG_ID)
+- devstat = UDC_DEVSTAT_REG;
++ if (omap_readl(OTG_CTRL) & OTG_ID)
++ devstat = omap_readw(UDC_DEVSTAT);
+ else
+ devstat = 0;
+
+@@ -1426,9 +1439,14 @@ static void update_otg(struct omap_udc *udc)
+ /* Enable HNP early, avoiding races on suspend irq path.
+ * ASSUMES OTG state machine B_BUS_REQ input is true.
+ */
+- if (udc->gadget.b_hnp_enable)
+- OTG_CTRL_REG = (OTG_CTRL_REG | OTG_B_HNPEN | OTG_B_BUSREQ)
+- & ~OTG_PULLUP;
++ if (udc->gadget.b_hnp_enable) {
++ u32 l;
++
++ l = omap_readl(OTG_CTRL);
++ l |= OTG_B_HNPEN | OTG_B_BUSREQ;
++ l &= ~OTG_PULLUP;
++ omap_writel(l, OTG_CTRL);
++ }
+ }
+
+ static void ep0_irq(struct omap_udc *udc, u16 irq_src)
+@@ -1446,7 +1464,7 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src)
+
+ nuke(ep0, 0);
+ if (ack) {
+- UDC_IRQ_SRC_REG = ack;
++ omap_writew(ack, UDC_IRQ_SRC);
+ irq_src = UDC_SETUP;
+ }
+ }
+@@ -1466,9 +1484,9 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src)
+ if (irq_src & UDC_EP0_TX) {
+ int stat;
+
+- UDC_IRQ_SRC_REG = UDC_EP0_TX;
+- UDC_EP_NUM_REG = UDC_EP_SEL|UDC_EP_DIR;
+- stat = UDC_STAT_FLG_REG;
++ omap_writew(UDC_EP0_TX, UDC_IRQ_SRC);
++ omap_writew(UDC_EP_SEL|UDC_EP_DIR, UDC_EP_NUM);
++ stat = omap_readw(UDC_STAT_FLG);
+ if (stat & UDC_ACK) {
+ if (udc->ep0_in) {
+ /* write next IN packet from response,
+@@ -1476,26 +1494,26 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src)
+ */
+ if (req)
+ stat = write_fifo(ep0, req);
+- UDC_EP_NUM_REG = UDC_EP_DIR;
++ omap_writew(UDC_EP_DIR, UDC_EP_NUM);
+ if (!req && udc->ep0_pending) {
+- UDC_EP_NUM_REG = UDC_EP_SEL;
+- UDC_CTRL_REG = UDC_CLR_EP;
+- UDC_CTRL_REG = UDC_SET_FIFO_EN;
+- UDC_EP_NUM_REG = 0;
++ omap_writew(UDC_EP_SEL, UDC_EP_NUM);
++ omap_writew(UDC_CLR_EP, UDC_CTRL);
++ omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
++ omap_writew(0, UDC_EP_NUM);
+ udc->ep0_pending = 0;
+ } /* else: 6 wait states before it'll tx */
+ } else {
+ /* ack status stage of OUT transfer */
+- UDC_EP_NUM_REG = UDC_EP_DIR;
++ omap_writew(UDC_EP_DIR, UDC_EP_NUM);
+ if (req)
+ done(ep0, req, 0);
+ }
+ req = NULL;
+ } else if (stat & UDC_STALL) {
+- UDC_CTRL_REG = UDC_CLR_HALT;
+- UDC_EP_NUM_REG = UDC_EP_DIR;
++ omap_writew(UDC_CLR_HALT, UDC_CTRL);
++ omap_writew(UDC_EP_DIR, UDC_EP_NUM);
+ } else {
+- UDC_EP_NUM_REG = UDC_EP_DIR;
++ omap_writew(UDC_EP_DIR, UDC_EP_NUM);
+ }
+ }
+
+@@ -1503,9 +1521,9 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src)
+ if (irq_src & UDC_EP0_RX) {
+ int stat;
+
+- UDC_IRQ_SRC_REG = UDC_EP0_RX;
+- UDC_EP_NUM_REG = UDC_EP_SEL;
+- stat = UDC_STAT_FLG_REG;
++ omap_writew(UDC_EP0_RX, UDC_IRQ_SRC);
++ omap_writew(UDC_EP_SEL, UDC_EP_NUM);
++ stat = omap_readw(UDC_STAT_FLG);
+ if (stat & UDC_ACK) {
+ if (!udc->ep0_in) {
+ stat = 0;
+@@ -1513,34 +1531,35 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src)
+ * reactiviting the fifo; stall on errors.
+ */
+ if (!req || (stat = read_fifo(ep0, req)) < 0) {
+- UDC_SYSCON2_REG = UDC_STALL_CMD;
++ omap_writew(UDC_STALL_CMD, UDC_SYSCON2);
+ udc->ep0_pending = 0;
+ stat = 0;
+ } else if (stat == 0)
+- UDC_CTRL_REG = UDC_SET_FIFO_EN;
+- UDC_EP_NUM_REG = 0;
++ omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
++ omap_writew(0, UDC_EP_NUM);
+
+ /* activate status stage */
+ if (stat == 1) {
+ done(ep0, req, 0);
+ /* that may have STALLed ep0... */
+- UDC_EP_NUM_REG = UDC_EP_SEL|UDC_EP_DIR;
+- UDC_CTRL_REG = UDC_CLR_EP;
+- UDC_CTRL_REG = UDC_SET_FIFO_EN;
+- UDC_EP_NUM_REG = UDC_EP_DIR;
++ omap_writew(UDC_EP_SEL | UDC_EP_DIR,
++ UDC_EP_NUM);
++ omap_writew(UDC_CLR_EP, UDC_CTRL);
++ omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
++ omap_writew(UDC_EP_DIR, UDC_EP_NUM);
+ udc->ep0_pending = 0;
+ }
+ } else {
+ /* ack status stage of IN transfer */
+- UDC_EP_NUM_REG = 0;
++ omap_writew(0, UDC_EP_NUM);
+ if (req)
+ done(ep0, req, 0);
+ }
+ } else if (stat & UDC_STALL) {
+- UDC_CTRL_REG = UDC_CLR_HALT;
+- UDC_EP_NUM_REG = 0;
++ omap_writew(UDC_CLR_HALT, UDC_CTRL);
++ omap_writew(0, UDC_EP_NUM);
+ } else {
+- UDC_EP_NUM_REG = 0;
++ omap_writew(0, UDC_EP_NUM);
+ }
+ }
+
+@@ -1555,14 +1574,14 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src)
+
+ /* read the (latest) SETUP message */
+ do {
+- UDC_EP_NUM_REG = UDC_SETUP_SEL;
++ omap_writew(UDC_SETUP_SEL, UDC_EP_NUM);
+ /* two bytes at a time */
+- u.word[0] = UDC_DATA_REG;
+- u.word[1] = UDC_DATA_REG;
+- u.word[2] = UDC_DATA_REG;
+- u.word[3] = UDC_DATA_REG;
+- UDC_EP_NUM_REG = 0;
+- } while (UDC_IRQ_SRC_REG & UDC_SETUP);
++ u.word[0] = omap_readw(UDC_DATA);
++ u.word[1] = omap_readw(UDC_DATA);
++ u.word[2] = omap_readw(UDC_DATA);
++ u.word[3] = omap_readw(UDC_DATA);
++ omap_writew(0, UDC_EP_NUM);
++ } while (omap_readw(UDC_IRQ_SRC) & UDC_SETUP);
+
+ #define w_value le16_to_cpu(u.r.wValue)
+ #define w_index le16_to_cpu(u.r.wIndex)
+@@ -1593,9 +1612,9 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src)
+ * later if it fails the request.
+ */
+ if (udc->ep0_reset_config)
+- UDC_SYSCON2_REG = UDC_CLR_CFG;
++ omap_writew(UDC_CLR_CFG, UDC_SYSCON2);
+ else
+- UDC_SYSCON2_REG = UDC_DEV_CFG;
++ omap_writew(UDC_DEV_CFG, UDC_SYSCON2);
+ update_otg(udc);
+ goto delegate;
+ case USB_REQ_CLEAR_FEATURE:
+@@ -1613,10 +1632,10 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src)
+ || !ep->desc)
+ goto do_stall;
+ use_ep(ep, 0);
+- UDC_CTRL_REG = udc->clr_halt;
++ omap_writew(udc->clr_halt, UDC_CTRL);
+ ep->ackwait = 0;
+ if (!(ep->bEndpointAddress & USB_DIR_IN)) {
+- UDC_CTRL_REG = UDC_SET_FIFO_EN;
++ omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
+ ep->ackwait = 1 + ep->double_buf;
+ }
+ /* NOTE: assumes the host behaves sanely,
+@@ -1649,15 +1668,15 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src)
+ }
+ use_ep(ep, 0);
+ /* can't halt if fifo isn't empty... */
+- UDC_CTRL_REG = UDC_CLR_EP;
+- UDC_CTRL_REG = UDC_SET_HALT;
++ omap_writew(UDC_CLR_EP, UDC_CTRL);
++ omap_writew(UDC_SET_HALT, UDC_CTRL);
+ VDBG("%s halted by host\n", ep->name);
+ ep0out_status_stage:
+ status = 0;
+- UDC_EP_NUM_REG = UDC_EP_SEL|UDC_EP_DIR;
+- UDC_CTRL_REG = UDC_CLR_EP;
+- UDC_CTRL_REG = UDC_SET_FIFO_EN;
+- UDC_EP_NUM_REG = UDC_EP_DIR;
++ omap_writew(UDC_EP_SEL|UDC_EP_DIR, UDC_EP_NUM);
++ omap_writew(UDC_CLR_EP, UDC_CTRL);
++ omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
++ omap_writew(UDC_EP_DIR, UDC_EP_NUM);
+ udc->ep0_pending = 0;
+ break;
+ case USB_REQ_GET_STATUS:
+@@ -1694,10 +1713,10 @@ intf_status:
+
+ zero_status:
+ /* return two zero bytes */
+- UDC_EP_NUM_REG = UDC_EP_SEL|UDC_EP_DIR;
+- UDC_DATA_REG = 0;
+- UDC_CTRL_REG = UDC_SET_FIFO_EN;
+- UDC_EP_NUM_REG = UDC_EP_DIR;
++ omap_writew(UDC_EP_SEL|UDC_EP_DIR, UDC_EP_NUM);
++ omap_writew(0, UDC_DATA);
++ omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
++ omap_writew(UDC_EP_DIR, UDC_EP_NUM);
+ status = 0;
+ VDBG("GET_STATUS, interface %d\n", w_index);
+ /* next, status stage */
+@@ -1706,8 +1725,8 @@ zero_status:
+ delegate:
+ /* activate the ep0out fifo right away */
+ if (!udc->ep0_in && w_length) {
+- UDC_EP_NUM_REG = 0;
+- UDC_CTRL_REG = UDC_SET_FIFO_EN;
++ omap_writew(0, UDC_EP_NUM);
++ omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
+ }
+
+ /* gadget drivers see class/vendor specific requests,
+@@ -1746,11 +1765,11 @@ do_stall:
+ u.r.bRequestType, u.r.bRequest, status);
+ if (udc->ep0_set_config) {
+ if (udc->ep0_reset_config)
+- WARN("error resetting config?\n");
++ WARNING("error resetting config?\n");
+ else
+- UDC_SYSCON2_REG = UDC_CLR_CFG;
++ omap_writew(UDC_CLR_CFG, UDC_SYSCON2);
+ }
+- UDC_SYSCON2_REG = UDC_STALL_CMD;
++ omap_writew(UDC_STALL_CMD, UDC_SYSCON2);
+ udc->ep0_pending = 0;
+ }
+ }
+@@ -1764,7 +1783,7 @@ static void devstate_irq(struct omap_udc *udc, u16 irq_src)
+ {
+ u16 devstat, change;
+
+- devstat = UDC_DEVSTAT_REG;
++ devstat = omap_readw(UDC_DEVSTAT);
+ change = devstat ^ udc->devstat;
+ udc->devstat = devstat;
+
+@@ -1804,7 +1823,8 @@ static void devstate_irq(struct omap_udc *udc, u16 irq_src)
+ INFO("USB reset done, gadget %s\n",
+ udc->driver->driver.name);
+ /* ep0 traffic is legal from now on */
+- UDC_IRQ_EN_REG = UDC_DS_CHG_IE | UDC_EP0_IE;
++ omap_writew(UDC_DS_CHG_IE | UDC_EP0_IE,
++ UDC_IRQ_EN);
+ }
+ change &= ~UDC_USB_RESET;
+ }
+@@ -1848,7 +1868,7 @@ static void devstate_irq(struct omap_udc *udc, u16 irq_src)
+ VDBG("devstat %03x, ignore change %03x\n",
+ devstat, change);
+
+- UDC_IRQ_SRC_REG = UDC_DS_CHG;
++ omap_writew(UDC_DS_CHG, UDC_IRQ_SRC);
+ }
+
+ static irqreturn_t omap_udc_irq(int irq, void *_udc)
+@@ -1859,7 +1879,7 @@ static irqreturn_t omap_udc_irq(int irq, void *_udc)
+ unsigned long flags;
+
+ spin_lock_irqsave(&udc->lock, flags);
+- irq_src = UDC_IRQ_SRC_REG;
++ irq_src = omap_readw(UDC_IRQ_SRC);
+
+ /* Device state change (usb ch9 stuff) */
+ if (irq_src & UDC_DS_CHG) {
+@@ -1882,7 +1902,7 @@ static irqreturn_t omap_udc_irq(int irq, void *_udc)
+ irq_src &= ~(UDC_TXN_DONE|UDC_RXN_CNT|UDC_RXN_EOT);
+ }
+
+- irq_src &= ~(UDC_SOF|UDC_EPN_TX|UDC_EPN_RX);
++ irq_src &= ~(UDC_IRQ_SOF | UDC_EPN_TX|UDC_EPN_RX);
+ if (irq_src)
+ DBG("udc_irq, unhandled %03x\n", irq_src);
+ spin_unlock_irqrestore(&udc->lock, flags);
+@@ -1903,7 +1923,7 @@ static void pio_out_timer(unsigned long _ep)
+ spin_lock_irqsave(&ep->udc->lock, flags);
+ if (!list_empty(&ep->queue) && ep->ackwait) {
+ use_ep(ep, UDC_EP_SEL);
+- stat_flg = UDC_STAT_FLG_REG;
++ stat_flg = omap_readw(UDC_STAT_FLG);
+
+ if ((stat_flg & UDC_ACK) && (!(stat_flg & UDC_FIFO_EN)
+ || (ep->double_buf && HALF_FULL(stat_flg)))) {
+@@ -1913,8 +1933,8 @@ static void pio_out_timer(unsigned long _ep)
+ req = container_of(ep->queue.next,
+ struct omap_req, queue);
+ (void) read_fifo(ep, req);
+- UDC_EP_NUM_REG = ep->bEndpointAddress;
+- UDC_CTRL_REG = UDC_SET_FIFO_EN;
++ omap_writew(ep->bEndpointAddress, UDC_EP_NUM);
++ omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
+ ep->ackwait = 1 + ep->double_buf;
+ } else
+ deselect_ep();
+@@ -1934,20 +1954,20 @@ static irqreturn_t omap_udc_pio_irq(int irq, void *_dev)
+ unsigned long flags;
+
+ spin_lock_irqsave(&udc->lock, flags);
+- epn_stat = UDC_EPN_STAT_REG;
+- irq_src = UDC_IRQ_SRC_REG;
++ epn_stat = omap_readw(UDC_EPN_STAT);
++ irq_src = omap_readw(UDC_IRQ_SRC);
+
+ /* handle OUT first, to avoid some wasteful NAKs */
+ if (irq_src & UDC_EPN_RX) {
+ epnum = (epn_stat >> 8) & 0x0f;
+- UDC_IRQ_SRC_REG = UDC_EPN_RX;
++ omap_writew(UDC_EPN_RX, UDC_IRQ_SRC);
+ status = IRQ_HANDLED;
+ ep = &udc->ep[epnum];
+ ep->irqs++;
+
+- UDC_EP_NUM_REG = epnum | UDC_EP_SEL;
++ omap_writew(epnum | UDC_EP_SEL, UDC_EP_NUM);
+ ep->fnf = 0;
+- if ((UDC_STAT_FLG_REG & UDC_ACK)) {
++ if (omap_readw(UDC_STAT_FLG) & UDC_ACK) {
+ ep->ackwait--;
+ if (!list_empty(&ep->queue)) {
+ int stat;
+@@ -1959,15 +1979,15 @@ static irqreturn_t omap_udc_pio_irq(int irq, void *_dev)
+ }
+ }
+ /* min 6 clock delay before clearing EP_SEL ... */
+- epn_stat = UDC_EPN_STAT_REG;
+- epn_stat = UDC_EPN_STAT_REG;
+- UDC_EP_NUM_REG = epnum;
++ epn_stat = omap_readw(UDC_EPN_STAT);
++ epn_stat = omap_readw(UDC_EPN_STAT);
++ omap_writew(epnum, UDC_EP_NUM);
+
+ /* enabling fifo _after_ clearing ACK, contrary to docs,
+ * reduces lossage; timer still needed though (sigh).
+ */
+ if (ep->fnf) {
+- UDC_CTRL_REG = UDC_SET_FIFO_EN;
++ omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
+ ep->ackwait = 1 + ep->double_buf;
+ }
+ mod_timer(&ep->timer, PIO_OUT_TIMEOUT);
+@@ -1976,13 +1996,13 @@ static irqreturn_t omap_udc_pio_irq(int irq, void *_dev)
+ /* then IN transfers */
+ else if (irq_src & UDC_EPN_TX) {
+ epnum = epn_stat & 0x0f;
+- UDC_IRQ_SRC_REG = UDC_EPN_TX;
++ omap_writew(UDC_EPN_TX, UDC_IRQ_SRC);
+ status = IRQ_HANDLED;
+ ep = &udc->ep[16 + epnum];
+ ep->irqs++;
+
+- UDC_EP_NUM_REG = epnum | UDC_EP_DIR | UDC_EP_SEL;
+- if ((UDC_STAT_FLG_REG & UDC_ACK)) {
++ omap_writew(epnum | UDC_EP_DIR | UDC_EP_SEL, UDC_EP_NUM);
++ if (omap_readw(UDC_STAT_FLG) & UDC_ACK) {
+ ep->ackwait = 0;
+ if (!list_empty(&ep->queue)) {
+ req = container_of(ep->queue.next,
+@@ -1991,9 +2011,9 @@ static irqreturn_t omap_udc_pio_irq(int irq, void *_dev)
+ }
+ }
+ /* min 6 clock delay before clearing EP_SEL ... */
+- epn_stat = UDC_EPN_STAT_REG;
+- epn_stat = UDC_EPN_STAT_REG;
+- UDC_EP_NUM_REG = epnum | UDC_EP_DIR;
++ epn_stat = omap_readw(UDC_EPN_STAT);
++ epn_stat = omap_readw(UDC_EPN_STAT);
++ omap_writew(epnum | UDC_EP_DIR, UDC_EP_NUM);
+ /* then 6 clocks before it'd tx */
+ }
+
+@@ -2021,7 +2041,7 @@ static irqreturn_t omap_udc_iso_irq(int irq, void *_dev)
+ req = list_entry(ep->queue.next, struct omap_req, queue);
+
+ use_ep(ep, UDC_EP_SEL);
+- stat = UDC_STAT_FLG_REG;
++ stat = omap_readw(UDC_STAT_FLG);
+
+ /* NOTE: like the other controller drivers, this isn't
+ * currently reporting lost or damaged frames.
+@@ -2053,9 +2073,14 @@ static irqreturn_t omap_udc_iso_irq(int irq, void *_dev)
+ if (!list_empty(&ep->queue))
+ pending = 1;
+ }
+- if (!pending)
+- UDC_IRQ_EN_REG &= ~UDC_SOF_IE;
+- UDC_IRQ_SRC_REG = UDC_SOF;
++ if (!pending) {
++ u16 w;
++
++ w = omap_readw(UDC_IRQ_EN);
++ w &= ~UDC_SOF_IE;
++ omap_writew(w, UDC_IRQ_EN);
++ }
++ omap_writew(UDC_IRQ_SOF, UDC_IRQ_SRC);
+
+ spin_unlock_irqrestore(&udc->lock, flags);
+ return IRQ_HANDLED;
+@@ -2104,7 +2129,7 @@ int usb_gadget_register_driver (struct usb_gadget_driver *driver)
+ if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC)
+ continue;
+ use_ep(ep, 0);
+- UDC_CTRL_REG = UDC_SET_HALT;
++ omap_writew(UDC_SET_HALT, UDC_CTRL);
+ }
+ udc->ep0_pending = 0;
+ udc->ep[0].irqs = 0;
+@@ -2128,7 +2153,7 @@ int usb_gadget_register_driver (struct usb_gadget_driver *driver)
+ }
+ DBG("bound to driver %s\n", driver->driver.name);
+
+- UDC_IRQ_SRC_REG = UDC_IRQ_SRC_MASK;
++ omap_writew(UDC_IRQ_SRC_MASK, UDC_IRQ_SRC);
+
+ /* connect to bus through transceiver */
+ if (udc->transceiver) {
+@@ -2225,7 +2250,7 @@ static void proc_ep_show(struct seq_file *s, struct omap_ep *ep)
+ else
+ buf[0] = 0;
+
+- stat_flg = UDC_STAT_FLG_REG;
++ stat_flg = omap_readw(UDC_STAT_FLG);
+ seq_printf(s,
+ "\n%s %s%s%sirqs %ld stat %04x " EIGHTBITS FOURBITS "%s\n",
+ ep->name, buf,
+@@ -2286,17 +2311,17 @@ static int proc_otg_show(struct seq_file *s)
+ u32 trans;
+ char *ctrl_name;
+
+- tmp = OTG_REV_REG;
++ tmp = omap_readl(OTG_REV);
+ if (cpu_is_omap24xx()) {
+ ctrl_name = "control_devconf";
+- trans = CONTROL_DEVCONF_REG;
++ trans = omap_ctrl_readl(OMAP2_CONTROL_DEVCONF0);
+ } else {
+ ctrl_name = "tranceiver_ctrl";
+- trans = USB_TRANSCEIVER_CTRL_REG;
++ trans = omap_readw(USB_TRANSCEIVER_CTRL);
+ }
+ seq_printf(s, "\nOTG rev %d.%d, %s %05x\n",
+ tmp >> 4, tmp & 0xf, ctrl_name, trans);
+- tmp = OTG_SYSCON_1_REG;
++ tmp = omap_readw(OTG_SYSCON_1);
+ seq_printf(s, "otg_syscon1 %08x usb2 %s, usb1 %s, usb0 %s,"
+ FOURBITS "\n", tmp,
+ trx_mode(USB2_TRX_MODE(tmp), trans & CONF_USB2_UNI_R),
+@@ -2308,7 +2333,7 @@ static int proc_otg_show(struct seq_file *s)
+ (tmp & HST_IDLE_EN) ? " !host" : "",
+ (tmp & DEV_IDLE_EN) ? " !dev" : "",
+ (tmp & OTG_RESET_DONE) ? " reset_done" : " reset_active");
+- tmp = OTG_SYSCON_2_REG;
++ tmp = omap_readl(OTG_SYSCON_2);
+ seq_printf(s, "otg_syscon2 %08x%s" EIGHTBITS
+ " b_ase_brst=%d hmc=%d\n", tmp,
+ (tmp & OTG_EN) ? " otg_en" : "",
+@@ -2323,7 +2348,7 @@ static int proc_otg_show(struct seq_file *s)
+ (tmp & HMC_TLLATTACH) ? " tllattach" : "",
+ B_ASE_BRST(tmp),
+ OTG_HMC(tmp));
+- tmp = OTG_CTRL_REG;
++ tmp = omap_readl(OTG_CTRL);
+ seq_printf(s, "otg_ctrl %06x" EIGHTBITS EIGHTBITS "%s\n", tmp,
+ (tmp & OTG_ASESSVLD) ? " asess" : "",
+ (tmp & OTG_BSESSEND) ? " bsess_end" : "",
+@@ -2343,13 +2368,13 @@ static int proc_otg_show(struct seq_file *s)
+ (tmp & OTG_PU_VBUS) ? " pu_vb" : "",
+ (tmp & OTG_PU_ID) ? " pu_id" : ""
+ );
+- tmp = OTG_IRQ_EN_REG;
++ tmp = omap_readw(OTG_IRQ_EN);
+ seq_printf(s, "otg_irq_en %04x" "\n", tmp);
+- tmp = OTG_IRQ_SRC_REG;
++ tmp = omap_readw(OTG_IRQ_SRC);
+ seq_printf(s, "otg_irq_src %04x" "\n", tmp);
+- tmp = OTG_OUTCTRL_REG;
++ tmp = omap_readw(OTG_OUTCTRL);
+ seq_printf(s, "otg_outctrl %04x" "\n", tmp);
+- tmp = OTG_TEST_REG;
++ tmp = omap_readw(OTG_TEST);
+ seq_printf(s, "otg_test %04x" "\n", tmp);
+ return 0;
+ }
+@@ -2370,7 +2395,7 @@ static int proc_udc_show(struct seq_file *s, void *_)
+ driver_desc,
+ use_dma ? " (dma)" : "");
+
+- tmp = UDC_REV_REG & 0xff;
++ tmp = omap_readw(UDC_REV) & 0xff;
+ seq_printf(s,
+ "UDC rev %d.%d, fifo mode %d, gadget %s\n"
+ "hmc %d, transceiver %s\n",
+@@ -2384,16 +2409,16 @@ static int proc_udc_show(struct seq_file *s, void *_)
+ ? "external" : "(none)"));
+ if (cpu_class_is_omap1()) {
+ seq_printf(s, "ULPD control %04x req %04x status %04x\n",
+- __REG16(ULPD_CLOCK_CTRL),
+- __REG16(ULPD_SOFT_REQ),
+- __REG16(ULPD_STATUS_REQ));
++ omap_readw(ULPD_CLOCK_CTRL),
++ omap_readw(ULPD_SOFT_REQ),
++ omap_readw(ULPD_STATUS_REQ));
+ }
+
+ /* OTG controller registers */
+ if (!cpu_is_omap15xx())
+ proc_otg_show(s);
+
+- tmp = UDC_SYSCON1_REG;
++ tmp = omap_readw(UDC_SYSCON1);
+ seq_printf(s, "\nsyscon1 %04x" EIGHTBITS "\n", tmp,
+ (tmp & UDC_CFG_LOCK) ? " cfg_lock" : "",
+ (tmp & UDC_DATA_ENDIAN) ? " data_endian" : "",
+@@ -2412,7 +2437,7 @@ static int proc_udc_show(struct seq_file *s, void *_)
+ return 0;
+ }
+
+- tmp = UDC_DEVSTAT_REG;
++ tmp = omap_readw(UDC_DEVSTAT);
+ seq_printf(s, "devstat %04x" EIGHTBITS "%s%s\n", tmp,
+ (tmp & UDC_B_HNP_ENABLE) ? " b_hnp" : "",
+ (tmp & UDC_A_HNP_SUPPORT) ? " a_hnp" : "",
+@@ -2424,20 +2449,20 @@ static int proc_udc_show(struct seq_file *s, void *_)
+ (tmp & UDC_ADD) ? " ADD" : "",
+ (tmp & UDC_DEF) ? " DEF" : "",
+ (tmp & UDC_ATT) ? " ATT" : "");
+- seq_printf(s, "sof %04x\n", UDC_SOF_REG);
+- tmp = UDC_IRQ_EN_REG;
++ seq_printf(s, "sof %04x\n", omap_readw(UDC_SOF));
++ tmp = omap_readw(UDC_IRQ_EN);
+ seq_printf(s, "irq_en %04x" FOURBITS "%s\n", tmp,
+ (tmp & UDC_SOF_IE) ? " sof" : "",
+ (tmp & UDC_EPN_RX_IE) ? " epn_rx" : "",
+ (tmp & UDC_EPN_TX_IE) ? " epn_tx" : "",
+ (tmp & UDC_DS_CHG_IE) ? " ds_chg" : "",
+ (tmp & UDC_EP0_IE) ? " ep0" : "");
+- tmp = UDC_IRQ_SRC_REG;
++ tmp = omap_readw(UDC_IRQ_SRC);
+ seq_printf(s, "irq_src %04x" EIGHTBITS "%s%s\n", tmp,
+ (tmp & UDC_TXN_DONE) ? " txn_done" : "",
+ (tmp & UDC_RXN_CNT) ? " rxn_cnt" : "",
+ (tmp & UDC_RXN_EOT) ? " rxn_eot" : "",
+- (tmp & UDC_SOF) ? " sof" : "",
++ (tmp & UDC_IRQ_SOF) ? " sof" : "",
+ (tmp & UDC_EPN_RX) ? " epn_rx" : "",
+ (tmp & UDC_EPN_TX) ? " epn_tx" : "",
+ (tmp & UDC_DS_CHG) ? " ds_chg" : "",
+@@ -2447,7 +2472,7 @@ static int proc_udc_show(struct seq_file *s, void *_)
+ if (use_dma) {
+ unsigned i;
+
+- tmp = UDC_DMA_IRQ_EN_REG;
++ tmp = omap_readw(UDC_DMA_IRQ_EN);
+ seq_printf(s, "dma_irq_en %04x%s" EIGHTBITS "\n", tmp,
+ (tmp & UDC_TX_DONE_IE(3)) ? " tx2_done" : "",
+ (tmp & UDC_RX_CNT_IE(3)) ? " rx2_cnt" : "",
+@@ -2461,29 +2486,29 @@ static int proc_udc_show(struct seq_file *s, void *_)
+ (tmp & UDC_RX_CNT_IE(1)) ? " rx0_cnt" : "",
+ (tmp & UDC_RX_EOT_IE(1)) ? " rx0_eot" : "");
+
+- tmp = UDC_RXDMA_CFG_REG;
++ tmp = omap_readw(UDC_RXDMA_CFG);
+ seq_printf(s, "rxdma_cfg %04x\n", tmp);
+ if (tmp) {
+ for (i = 0; i < 3; i++) {
+ if ((tmp & (0x0f << (i * 4))) == 0)
+ continue;
+ seq_printf(s, "rxdma[%d] %04x\n", i,
+- UDC_RXDMA_REG(i + 1));
++ omap_readw(UDC_RXDMA(i + 1)));
+ }
+ }
+- tmp = UDC_TXDMA_CFG_REG;
++ tmp = omap_readw(UDC_TXDMA_CFG);
+ seq_printf(s, "txdma_cfg %04x\n", tmp);
+ if (tmp) {
+ for (i = 0; i < 3; i++) {
+ if (!(tmp & (0x0f << (i * 4))))
+ continue;
+ seq_printf(s, "txdma[%d] %04x\n", i,
+- UDC_TXDMA_REG(i + 1));
++ omap_readw(UDC_TXDMA(i + 1)));
+ }
+ }
+ }
+
+- tmp = UDC_DEVSTAT_REG;
++ tmp = omap_readw(UDC_DEVSTAT);
+ if (tmp & UDC_ATT) {
+ proc_ep_show(s, &udc->ep[0]);
+ if (tmp & UDC_ADD) {
+@@ -2535,7 +2560,7 @@ static inline void remove_proc_file(void) {}
+ * buffer space among the endpoints we'll be operating.
+ *
+ * NOTE: as of OMAP 1710 ES2.0, writing a new endpoint config when
+- * UDC_SYSCON_1_REG.CFG_LOCK is set can now work. We won't use that
++ * UDC_SYSCON_1.CFG_LOCK is set can now work. We won't use that
+ * capability yet though.
+ */
+ static unsigned __init
+@@ -2597,9 +2622,9 @@ omap_ep_setup(char *name, u8 addr, u8 type,
+ name, addr, epn_rxtx, maxp, dbuf ? "x2" : "", buf);
+
+ if (addr & USB_DIR_IN)
+- UDC_EP_TX_REG(addr & 0xf) = epn_rxtx;
++ omap_writew(epn_rxtx, UDC_EP_TX(addr & 0xf));
+ else
+- UDC_EP_RX_REG(addr) = epn_rxtx;
++ omap_writew(epn_rxtx, UDC_EP_RX(addr));
+
+ /* next endpoint's buffer starts after this one's */
+ buf += maxp;
+@@ -2638,15 +2663,15 @@ omap_udc_setup(struct platform_device *odev, struct otg_transceiver *xceiv)
+ unsigned tmp, buf;
+
+ /* abolish any previous hardware state */
+- UDC_SYSCON1_REG = 0;
+- UDC_IRQ_EN_REG = 0;
+- UDC_IRQ_SRC_REG = UDC_IRQ_SRC_MASK;
+- UDC_DMA_IRQ_EN_REG = 0;
+- UDC_RXDMA_CFG_REG = 0;
+- UDC_TXDMA_CFG_REG = 0;
++ omap_writew(0, UDC_SYSCON1);
++ omap_writew(0, UDC_IRQ_EN);
++ omap_writew(UDC_IRQ_SRC_MASK, UDC_IRQ_SRC);
++ omap_writew(0, UDC_DMA_IRQ_EN);
++ omap_writew(0, UDC_RXDMA_CFG);
++ omap_writew(0, UDC_TXDMA_CFG);
+
+ /* UDC_PULLUP_EN gates the chip clock */
+- // OTG_SYSCON_1_REG |= DEV_IDLE_EN;
++ // OTG_SYSCON_1 |= DEV_IDLE_EN;
+
+ udc = kzalloc(sizeof(*udc), GFP_KERNEL);
+ if (!udc)
+@@ -2662,7 +2687,7 @@ omap_udc_setup(struct platform_device *odev, struct otg_transceiver *xceiv)
+ udc->gadget.name = driver_name;
+
+ device_initialize(&udc->gadget.dev);
+- strcpy (udc->gadget.dev.bus_id, "gadget");
++ dev_set_name(&udc->gadget.dev, "gadget");
+ udc->gadget.dev.release = omap_udc_release;
+ udc->gadget.dev.parent = &odev->dev;
+ if (use_dma)
+@@ -2677,8 +2702,8 @@ omap_udc_setup(struct platform_device *odev, struct otg_transceiver *xceiv)
+
+ /* initially disable all non-ep0 endpoints */
+ for (tmp = 1; tmp < 15; tmp++) {
+- UDC_EP_RX_REG(tmp) = 0;
+- UDC_EP_TX_REG(tmp) = 0;
++ omap_writew(0, UDC_EP_RX(tmp));
++ omap_writew(0, UDC_EP_TX(tmp));
+ }
+
+ #define OMAP_BULK_EP(name,addr) \
+@@ -2763,7 +2788,7 @@ omap_udc_setup(struct platform_device *odev, struct otg_transceiver *xceiv)
+ ERR("unsupported fifo_mode #%d\n", fifo_mode);
+ return -ENODEV;
+ }
+- UDC_SYSCON1_REG = UDC_CFG_LOCK|UDC_SELF_PWR;
++ omap_writew(UDC_CFG_LOCK|UDC_SELF_PWR, UDC_SYSCON1);
+ INFO("fifo mode %d, %d bytes not used\n", fifo_mode, 2048 - buf);
+ return 0;
+ }
+@@ -2807,7 +2832,7 @@ static int __init omap_udc_probe(struct platform_device *pdev)
+ }
+
+ INFO("OMAP UDC rev %d.%d%s\n",
+- UDC_REV_REG >> 4, UDC_REV_REG & 0xf,
++ omap_readw(UDC_REV) >> 4, omap_readw(UDC_REV) & 0xf,
+ config->otg ? ", Mini-AB" : "");
+
+ /* use the mode given to us by board init code */
+@@ -2822,12 +2847,12 @@ static int __init omap_udc_probe(struct platform_device *pdev)
+ * know when to turn PULLUP_EN on/off; and that
+ * means we always "need" the 48MHz clock.
+ */
+- u32 tmp = FUNC_MUX_CTRL_0_REG;
+-
+- FUNC_MUX_CTRL_0_REG &= ~VBUS_CTRL_1510;
++ u32 tmp = omap_readl(FUNC_MUX_CTRL_0);
++ tmp &= ~VBUS_CTRL_1510;
++ omap_writel(tmp, FUNC_MUX_CTRL_0);
+ tmp |= VBUS_MODE_1510;
+ tmp &= ~VBUS_CTRL_1510;
+- FUNC_MUX_CTRL_0_REG = tmp;
++ omap_writel(tmp, FUNC_MUX_CTRL_0);
+ }
+ } else {
+ /* The transceiver may package some GPIO logic or handle
+@@ -2907,7 +2932,7 @@ known:
+ #endif
+
+ /* starting with omap1710 es2.0, clear toggle is a separate bit */
+- if (UDC_REV_REG >= 0x61)
++ if (omap_readw(UDC_REV) >= 0x61)
+ udc->clr_halt = UDC_RESET_EP | UDC_CLRDATA_TOGGLE;
+ else
+ udc->clr_halt = UDC_RESET_EP;
+@@ -3005,7 +3030,7 @@ static int __exit omap_udc_remove(struct platform_device *pdev)
+ put_device(udc->transceiver->dev);
+ udc->transceiver = NULL;
+ }
+- UDC_SYSCON1_REG = 0;
++ omap_writew(0, UDC_SYSCON1);
+
+ remove_proc_file();
+
+@@ -3036,7 +3061,7 @@ static int __exit omap_udc_remove(struct platform_device *pdev)
+ *
+ * REVISIT we should probably reject suspend requests when there's a host
+ * session active, rather than disconnecting, at least on boards that can
+- * report VBUS irqs (UDC_DEVSTAT_REG.UDC_ATT). And in any case, we need to
++ * report VBUS irqs (UDC_DEVSTAT.UDC_ATT). And in any case, we need to
+ * make host resumes and VBUS detection trigger OMAP wakeup events; that
+ * may involve talking to an external transceiver (e.g. isp1301).
+ */
+@@ -3045,14 +3070,14 @@ static int omap_udc_suspend(struct platform_device *dev, pm_message_t message)
+ {
+ u32 devstat;
+
+- devstat = UDC_DEVSTAT_REG;
++ devstat = omap_readw(UDC_DEVSTAT);
+
+ /* we're requesting 48 MHz clock if the pullup is enabled
+ * (== we're attached to the host) and we're not suspended,
+ * which would prevent entry to deep sleep...
+ */
+ if ((devstat & UDC_ATT) != 0 && (devstat & UDC_SUS) == 0) {
+- WARN("session active; suspend requires disconnect\n");
++ WARNING("session active; suspend requires disconnect\n");
+ omap_pullup(&udc->gadget, 0);
+ }
+
+diff --git a/drivers/usb/gadget/omap_udc.h b/drivers/usb/gadget/omap_udc.h
+index c6b9cbc..29edc51 100644
+--- a/drivers/usb/gadget/omap_udc.h
++++ b/drivers/usb/gadget/omap_udc.h
+@@ -8,23 +8,22 @@
+ /*
+ * USB device/endpoint management registers
+ */
+-#define UDC_REG(offset) __REG16(UDC_BASE + (offset))
+
+-#define UDC_REV_REG UDC_REG(0x0) /* Revision */
+-#define UDC_EP_NUM_REG UDC_REG(0x4) /* Which endpoint */
++#define UDC_REV (UDC_BASE + 0x0) /* Revision */
++#define UDC_EP_NUM (UDC_BASE + 0x4) /* Which endpoint */
+ # define UDC_SETUP_SEL (1 << 6)
+ # define UDC_EP_SEL (1 << 5)
+ # define UDC_EP_DIR (1 << 4)
+ /* low 4 bits for endpoint number */
+-#define UDC_DATA_REG UDC_REG(0x08) /* Endpoint FIFO */
+-#define UDC_CTRL_REG UDC_REG(0x0C) /* Endpoint control */
++#define UDC_DATA (UDC_BASE + 0x08) /* Endpoint FIFO */
++#define UDC_CTRL (UDC_BASE + 0x0C) /* Endpoint control */
+ # define UDC_CLR_HALT (1 << 7)
+ # define UDC_SET_HALT (1 << 6)
+ # define UDC_CLRDATA_TOGGLE (1 << 3)
+ # define UDC_SET_FIFO_EN (1 << 2)
+ # define UDC_CLR_EP (1 << 1)
+ # define UDC_RESET_EP (1 << 0)
+-#define UDC_STAT_FLG_REG UDC_REG(0x10) /* Endpoint status */
++#define UDC_STAT_FLG (UDC_BASE + 0x10) /* Endpoint status */
+ # define UDC_NO_RXPACKET (1 << 15)
+ # define UDC_MISS_IN (1 << 14)
+ # define UDC_DATA_FLUSH (1 << 13)
+@@ -38,8 +37,8 @@
+ # define UDC_FIFO_EN (1 << 2)
+ # define UDC_NON_ISO_FIFO_EMPTY (1 << 1)
+ # define UDC_NON_ISO_FIFO_FULL (1 << 0)
+-#define UDC_RXFSTAT_REG UDC_REG(0x14) /* OUT bytecount */
+-#define UDC_SYSCON1_REG UDC_REG(0x18) /* System config 1 */
++#define UDC_RXFSTAT (UDC_BASE + 0x14) /* OUT bytecount */
++#define UDC_SYSCON1 (UDC_BASE + 0x18) /* System config 1 */
+ # define UDC_CFG_LOCK (1 << 8)
+ # define UDC_DATA_ENDIAN (1 << 7)
+ # define UDC_DMA_ENDIAN (1 << 6)
+@@ -48,12 +47,12 @@
+ # define UDC_SELF_PWR (1 << 2)
+ # define UDC_SOFF_DIS (1 << 1)
+ # define UDC_PULLUP_EN (1 << 0)
+-#define UDC_SYSCON2_REG UDC_REG(0x1C) /* System config 2 */
++#define UDC_SYSCON2 (UDC_BASE + 0x1C) /* System config 2 */
+ # define UDC_RMT_WKP (1 << 6)
+ # define UDC_STALL_CMD (1 << 5)
+ # define UDC_DEV_CFG (1 << 3)
+ # define UDC_CLR_CFG (1 << 2)
+-#define UDC_DEVSTAT_REG UDC_REG(0x20) /* Device status */
++#define UDC_DEVSTAT (UDC_BASE + 0x20) /* Device status */
+ # define UDC_B_HNP_ENABLE (1 << 9)
+ # define UDC_A_HNP_SUPPORT (1 << 8)
+ # define UDC_A_ALT_HNP_SUPPORT (1 << 7)
+@@ -64,26 +63,26 @@
+ # define UDC_ADD (1 << 2)
+ # define UDC_DEF (1 << 1)
+ # define UDC_ATT (1 << 0)
+-#define UDC_SOF_REG UDC_REG(0x24) /* Start of frame */
++#define UDC_SOF (UDC_BASE + 0x24) /* Start of frame */
+ # define UDC_FT_LOCK (1 << 12)
+ # define UDC_TS_OK (1 << 11)
+ # define UDC_TS 0x03ff
+-#define UDC_IRQ_EN_REG UDC_REG(0x28) /* Interrupt enable */
++#define UDC_IRQ_EN (UDC_BASE + 0x28) /* Interrupt enable */
+ # define UDC_SOF_IE (1 << 7)
+ # define UDC_EPN_RX_IE (1 << 5)
+ # define UDC_EPN_TX_IE (1 << 4)
+ # define UDC_DS_CHG_IE (1 << 3)
+ # define UDC_EP0_IE (1 << 0)
+-#define UDC_DMA_IRQ_EN_REG UDC_REG(0x2C) /* DMA irq enable */
++#define UDC_DMA_IRQ_EN (UDC_BASE + 0x2C) /* DMA irq enable */
+ /* rx/tx dma channels numbered 1-3 not 0-2 */
+ # define UDC_TX_DONE_IE(n) (1 << (4 * (n) - 2))
+ # define UDC_RX_CNT_IE(n) (1 << (4 * (n) - 3))
+ # define UDC_RX_EOT_IE(n) (1 << (4 * (n) - 4))
+-#define UDC_IRQ_SRC_REG UDC_REG(0x30) /* Interrupt source */
++#define UDC_IRQ_SRC (UDC_BASE + 0x30) /* Interrupt source */
+ # define UDC_TXN_DONE (1 << 10)
+ # define UDC_RXN_CNT (1 << 9)
+ # define UDC_RXN_EOT (1 << 8)
+-# define UDC_SOF (1 << 7)
++# define UDC_IRQ_SOF (1 << 7)
+ # define UDC_EPN_RX (1 << 5)
+ # define UDC_EPN_TX (1 << 4)
+ # define UDC_DS_CHG (1 << 3)
+@@ -91,41 +90,41 @@
+ # define UDC_EP0_RX (1 << 1)
+ # define UDC_EP0_TX (1 << 0)
+ # define UDC_IRQ_SRC_MASK 0x7bf
+-#define UDC_EPN_STAT_REG UDC_REG(0x34) /* EP irq status */
+-#define UDC_DMAN_STAT_REG UDC_REG(0x38) /* DMA irq status */
++#define UDC_EPN_STAT (UDC_BASE + 0x34) /* EP irq status */
++#define UDC_DMAN_STAT (UDC_BASE + 0x38) /* DMA irq status */
+ # define UDC_DMA_RX_SB (1 << 12)
+ # define UDC_DMA_RX_SRC(x) (((x)>>8) & 0xf)
+ # define UDC_DMA_TX_SRC(x) (((x)>>0) & 0xf)
+
+
+ /* DMA configuration registers: up to three channels in each direction. */
+-#define UDC_RXDMA_CFG_REG UDC_REG(0x40) /* 3 eps for RX DMA */
++#define UDC_RXDMA_CFG (UDC_BASE + 0x40) /* 3 eps for RX DMA */
+ # define UDC_DMA_REQ (1 << 12)
+-#define UDC_TXDMA_CFG_REG UDC_REG(0x44) /* 3 eps for TX DMA */
+-#define UDC_DATA_DMA_REG UDC_REG(0x48) /* rx/tx fifo addr */
++#define UDC_TXDMA_CFG (UDC_BASE + 0x44) /* 3 eps for TX DMA */
++#define UDC_DATA_DMA (UDC_BASE + 0x48) /* rx/tx fifo addr */
+
+ /* rx/tx dma control, numbering channels 1-3 not 0-2 */
+-#define UDC_TXDMA_REG(chan) UDC_REG(0x50 - 4 + 4 * (chan))
++#define UDC_TXDMA(chan) (UDC_BASE + 0x50 - 4 + 4 * (chan))
+ # define UDC_TXN_EOT (1 << 15) /* bytes vs packets */
+ # define UDC_TXN_START (1 << 14) /* start transfer */
+ # define UDC_TXN_TSC 0x03ff /* units in xfer */
+-#define UDC_RXDMA_REG(chan) UDC_REG(0x60 - 4 + 4 * (chan))
++#define UDC_RXDMA(chan) (UDC_BASE + 0x60 - 4 + 4 * (chan))
+ # define UDC_RXN_STOP (1 << 15) /* enable EOT irq */
+ # define UDC_RXN_TC 0x00ff /* packets in xfer */
+
+
+ /*
+ * Endpoint configuration registers (used before CFG_LOCK is set)
+- * UDC_EP_TX_REG(0) is unused
++ * UDC_EP_TX(0) is unused
+ */
+-#define UDC_EP_RX_REG(endpoint) UDC_REG(0x80 + (endpoint)*4)
++#define UDC_EP_RX(endpoint) (UDC_BASE + 0x80 + (endpoint)*4)
+ # define UDC_EPN_RX_VALID (1 << 15)
+ # define UDC_EPN_RX_DB (1 << 14)
+ /* buffer size in bits 13, 12 */
+ # define UDC_EPN_RX_ISO (1 << 11)
+ /* buffer pointer in low 11 bits */
+-#define UDC_EP_TX_REG(endpoint) UDC_REG(0xc0 + (endpoint)*4)
+- /* same bitfields as in RX_REG */
++#define UDC_EP_TX(endpoint) (UDC_BASE + 0xc0 + (endpoint)*4)
++ /* same bitfields as in RX */
+
+ /*-------------------------------------------------------------------------*/
+
+@@ -189,20 +188,20 @@ struct omap_udc {
+ #endif
+
+ #define ERR(stuff...) pr_err("udc: " stuff)
+-#define WARN(stuff...) pr_warning("udc: " stuff)
++#define WARNING(stuff...) pr_warning("udc: " stuff)
+ #define INFO(stuff...) pr_info("udc: " stuff)
+ #define DBG(stuff...) pr_debug("udc: " stuff)
+
+ /*-------------------------------------------------------------------------*/
+
+-#define MOD_CONF_CTRL_0_REG __REG32(MOD_CONF_CTRL_0)
+-#define VBUS_W2FC_1510 (1 << 17) /* 0 gpio0, 1 dvdd2 pin */
++/* MOD_CONF_CTRL_0 */
++#define VBUS_W2FC_1510 (1 << 17) /* 0 gpio0, 1 dvdd2 pin */
+
+-#define FUNC_MUX_CTRL_0_REG __REG32(FUNC_MUX_CTRL_0)
++/* FUNC_MUX_CTRL_0 */
+ #define VBUS_CTRL_1510 (1 << 19) /* 1 connected (software) */
+ #define VBUS_MODE_1510 (1 << 18) /* 0 hardware, 1 software */
+
+-#define HMC_1510 ((MOD_CONF_CTRL_0_REG >> 1) & 0x3f)
+-#define HMC_1610 (OTG_SYSCON_2_REG & 0x3f)
++#define HMC_1510 ((omap_readl(MOD_CONF_CTRL_0) >> 1) & 0x3f)
++#define HMC_1610 (omap_readl(OTG_SYSCON_2) & 0x3f)
+ #define HMC (cpu_is_omap15xx() ? HMC_1510 : HMC_1610)
+
+diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/printer.c
+index 76be75e..e009008 100644
+--- a/drivers/usb/gadget/printer.c
++++ b/drivers/usb/gadget/printer.c
+@@ -179,7 +179,7 @@ module_param(qlen, uint, S_IRUGO|S_IWUSR);
+
+ #define ERROR(dev, fmt, args...) \
+ xprintk(dev, KERN_ERR, fmt, ## args)
+-#define WARN(dev, fmt, args...) \
++#define WARNING(dev, fmt, args...) \
+ xprintk(dev, KERN_WARNING, fmt, ## args)
+ #define INFO(dev, fmt, args...) \
+ xprintk(dev, KERN_INFO, fmt, ## args)
+@@ -462,6 +462,7 @@ printer_open(struct inode *inode, struct file *fd)
+ unsigned long flags;
+ int ret = -EBUSY;
+
++ lock_kernel();
+ dev = container_of(inode->i_cdev, struct printer_dev, printer_cdev);
+
+ spin_lock_irqsave(&dev->lock, flags);
+@@ -477,7 +478,7 @@ printer_open(struct inode *inode, struct file *fd)
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ DBG(dev, "printer_open returned %x\n", ret);
+-
++ unlock_kernel();
+ return ret;
+ }
+
+@@ -827,9 +828,8 @@ printer_poll(struct file *fd, poll_table *wait)
+ return status;
+ }
+
+-static int
+-printer_ioctl(struct inode *inode, struct file *fd, unsigned int code,
+- unsigned long arg)
++static long
++printer_ioctl(struct file *fd, unsigned int code, unsigned long arg)
+ {
+ struct printer_dev *dev = fd->private_data;
+ unsigned long flags;
+@@ -868,7 +868,7 @@ static struct file_operations printer_io_operations = {
+ .write = printer_write,
+ .fsync = printer_fsync,
+ .poll = printer_poll,
+- .ioctl = printer_ioctl,
++ .unlocked_ioctl = printer_ioctl,
+ .release = printer_close
+ };
+
+@@ -1360,8 +1360,8 @@ printer_bind(struct usb_gadget *gadget)
+
+
+ /* Setup the sysfs files for the printer gadget. */
+- dev->pdev = device_create(usb_gadget_class, NULL, g_printer_devno,
+- "g_printer");
++ dev->pdev = device_create_drvdata(usb_gadget_class, NULL,
++ g_printer_devno, NULL, "g_printer");
+ if (IS_ERR(dev->pdev)) {
+ ERROR(dev, "Failed to create device: g_printer\n");
+ goto fail;
+diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c
+new file mode 100644
+index 0000000..da6e93c
+--- /dev/null
++++ b/drivers/usb/gadget/pxa25x_udc.c
+@@ -0,0 +1,2390 @@
++/*
++ * Intel PXA25x and IXP4xx on-chip full speed USB device controllers
++ *
++ * Copyright (C) 2002 Intrinsyc, Inc. (Frank Becker)
++ * Copyright (C) 2003 Robert Schwebel, Pengutronix
++ * Copyright (C) 2003 Benedikt Spranger, Pengutronix
++ * Copyright (C) 2003 David Brownell
++ * Copyright (C) 2003 Joshua Wise
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++/* #define VERBOSE_DEBUG */
++
++#include <linux/device.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/ioport.h>
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/delay.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/timer.h>
++#include <linux/list.h>
++#include <linux/interrupt.h>
++#include <linux/mm.h>
++#include <linux/platform_device.h>
++#include <linux/dma-mapping.h>
++#include <linux/irq.h>
++#include <linux/clk.h>
++#include <linux/err.h>
++#include <linux/seq_file.h>
++#include <linux/debugfs.h>
++#include <linux/io.h>
++
++#include <asm/byteorder.h>
++#include <asm/dma.h>
++#include <asm/gpio.h>
++#include <asm/system.h>
++#include <asm/mach-types.h>
++#include <asm/unaligned.h>
++
++#include <linux/usb/ch9.h>
++#include <linux/usb/gadget.h>
++
++/*
++ * This driver is PXA25x only. Grab the right register definitions.
++ */
++#ifdef CONFIG_ARCH_PXA
++#include <mach/pxa25x-udc.h>
++#endif
++
++#include <asm/mach/udc_pxa2xx.h>
++
++
++/*
++ * This driver handles the USB Device Controller (UDC) in Intel's PXA 25x
++ * series processors. The UDC for the IXP 4xx series is very similar.
++ * There are fifteen endpoints, in addition to ep0.
++ *
++ * Such controller drivers work with a gadget driver. The gadget driver
++ * returns descriptors, implements configuration and data protocols used
++ * by the host to interact with this device, and allocates endpoints to
++ * the different protocol interfaces. The controller driver virtualizes
++ * usb hardware so that the gadget drivers will be more portable.
++ *
++ * This UDC hardware wants to implement a bit too much USB protocol, so
++ * it constrains the sorts of USB configuration change events that work.
++ * The errata for these chips are misleading; some "fixed" bugs from
++ * pxa250 a0/a1 b0/b1/b2 sure act like they're still there.
++ *
++ * Note that the UDC hardware supports DMA (except on IXP) but that's
++ * not used here. IN-DMA (to host) is simple enough, when the data is
++ * suitably aligned (16 bytes) ... the network stack doesn't do that,
++ * other software can. OUT-DMA is buggy in most chip versions, as well
++ * as poorly designed (data toggle not automatic). So this driver won't
++ * bother using DMA. (Mostly-working IN-DMA support was available in
++ * kernels before 2.6.23, but was never enabled or well tested.)
++ */
++
++#define DRIVER_VERSION "30-June-2007"
++#define DRIVER_DESC "PXA 25x USB Device Controller driver"
++
++
++static const char driver_name [] = "pxa25x_udc";
++
++static const char ep0name [] = "ep0";
++
++
++#ifdef CONFIG_ARCH_IXP4XX
++
++/* cpu-specific register addresses are compiled in to this code */
++#ifdef CONFIG_ARCH_PXA
++#error "Can't configure both IXP and PXA"
++#endif
++
++/* IXP doesn't yet support <linux/clk.h> */
++#define clk_get(dev,name) NULL
++#define clk_enable(clk) do { } while (0)
++#define clk_disable(clk) do { } while (0)
++#define clk_put(clk) do { } while (0)
++
++#endif
++
++#include "pxa25x_udc.h"
++
++
++#ifdef CONFIG_USB_PXA25X_SMALL
++#define SIZE_STR " (small)"
++#else
++#define SIZE_STR ""
++#endif
++
++/* ---------------------------------------------------------------------------
++ * endpoint related parts of the api to the usb controller hardware,
++ * used by gadget driver; and the inner talker-to-hardware core.
++ * ---------------------------------------------------------------------------
++ */
++
++static void pxa25x_ep_fifo_flush (struct usb_ep *ep);
++static void nuke (struct pxa25x_ep *, int status);
++
++/* one GPIO should be used to detect VBUS from the host */
++static int is_vbus_present(void)
++{
++ struct pxa2xx_udc_mach_info *mach = the_controller->mach;
++
++ if (mach->gpio_vbus) {
++ int value = gpio_get_value(mach->gpio_vbus);
++ return mach->gpio_vbus_inverted ? !value : value;
++ }
++ if (mach->udc_is_connected)
++ return mach->udc_is_connected();
++ return 1;
++}
++
++/* one GPIO should control a D+ pullup, so host sees this device (or not) */
++static void pullup_off(void)
++{
++ struct pxa2xx_udc_mach_info *mach = the_controller->mach;
++ int off_level = mach->gpio_pullup_inverted;
++
++ if (mach->gpio_pullup)
++ gpio_set_value(mach->gpio_pullup, off_level);
++ else if (mach->udc_command)
++ mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT);
++}
++
++static void pullup_on(void)
++{
++ struct pxa2xx_udc_mach_info *mach = the_controller->mach;
++ int on_level = !mach->gpio_pullup_inverted;
++
++ if (mach->gpio_pullup)
++ gpio_set_value(mach->gpio_pullup, on_level);
++ else if (mach->udc_command)
++ mach->udc_command(PXA2XX_UDC_CMD_CONNECT);
++}
++
++static void pio_irq_enable(int bEndpointAddress)
++{
++ bEndpointAddress &= 0xf;
++ if (bEndpointAddress < 8)
++ UICR0 &= ~(1 << bEndpointAddress);
++ else {
++ bEndpointAddress -= 8;
++ UICR1 &= ~(1 << bEndpointAddress);
++ }
++}
++
++static void pio_irq_disable(int bEndpointAddress)
++{
++ bEndpointAddress &= 0xf;
++ if (bEndpointAddress < 8)
++ UICR0 |= 1 << bEndpointAddress;
++ else {
++ bEndpointAddress -= 8;
++ UICR1 |= 1 << bEndpointAddress;
++ }
++}
++
++/* The UDCCR reg contains mask and interrupt status bits,
++ * so using '|=' isn't safe as it may ack an interrupt.
++ */
++#define UDCCR_MASK_BITS (UDCCR_REM | UDCCR_SRM | UDCCR_UDE)
++
++static inline void udc_set_mask_UDCCR(int mask)
++{
++ UDCCR = (UDCCR & UDCCR_MASK_BITS) | (mask & UDCCR_MASK_BITS);
++}
++
++static inline void udc_clear_mask_UDCCR(int mask)
++{
++ UDCCR = (UDCCR & UDCCR_MASK_BITS) & ~(mask & UDCCR_MASK_BITS);
++}
++
++static inline void udc_ack_int_UDCCR(int mask)
++{
++ /* udccr contains the bits we dont want to change */
++ __u32 udccr = UDCCR & UDCCR_MASK_BITS;
++
++ UDCCR = udccr | (mask & ~UDCCR_MASK_BITS);
++}
++
++/*
++ * endpoint enable/disable
++ *
++ * we need to verify the descriptors used to enable endpoints. since pxa25x
++ * endpoint configurations are fixed, and are pretty much always enabled,
++ * there's not a lot to manage here.
++ *
++ * because pxa25x can't selectively initialize bulk (or interrupt) endpoints,
++ * (resetting endpoint halt and toggle), SET_INTERFACE is unusable except
++ * for a single interface (with only the default altsetting) and for gadget
++ * drivers that don't halt endpoints (not reset by set_interface). that also
++ * means that if you use ISO, you must violate the USB spec rule that all
++ * iso endpoints must be in non-default altsettings.
++ */
++static int pxa25x_ep_enable (struct usb_ep *_ep,
++ const struct usb_endpoint_descriptor *desc)
++{
++ struct pxa25x_ep *ep;
++ struct pxa25x_udc *dev;
++
++ ep = container_of (_ep, struct pxa25x_ep, ep);
++ if (!_ep || !desc || ep->desc || _ep->name == ep0name
++ || desc->bDescriptorType != USB_DT_ENDPOINT
++ || ep->bEndpointAddress != desc->bEndpointAddress
++ || ep->fifo_size < le16_to_cpu
++ (desc->wMaxPacketSize)) {
++ DMSG("%s, bad ep or descriptor\n", __func__);
++ return -EINVAL;
++ }
++
++ /* xfer types must match, except that interrupt ~= bulk */
++ if (ep->bmAttributes != desc->bmAttributes
++ && ep->bmAttributes != USB_ENDPOINT_XFER_BULK
++ && desc->bmAttributes != USB_ENDPOINT_XFER_INT) {
++ DMSG("%s, %s type mismatch\n", __func__, _ep->name);
++ return -EINVAL;
++ }
++
++ /* hardware _could_ do smaller, but driver doesn't */
++ if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK
++ && le16_to_cpu (desc->wMaxPacketSize)
++ != BULK_FIFO_SIZE)
++ || !desc->wMaxPacketSize) {
++ DMSG("%s, bad %s maxpacket\n", __func__, _ep->name);
++ return -ERANGE;
++ }
++
++ dev = ep->dev;
++ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {
++ DMSG("%s, bogus device state\n", __func__);
++ return -ESHUTDOWN;
++ }
++
++ ep->desc = desc;
++ ep->stopped = 0;
++ ep->pio_irqs = 0;
++ ep->ep.maxpacket = le16_to_cpu (desc->wMaxPacketSize);
++
++ /* flush fifo (mostly for OUT buffers) */
++ pxa25x_ep_fifo_flush (_ep);
++
++ /* ... reset halt state too, if we could ... */
++
++ DBG(DBG_VERBOSE, "enabled %s\n", _ep->name);
++ return 0;
++}
++
++static int pxa25x_ep_disable (struct usb_ep *_ep)
++{
++ struct pxa25x_ep *ep;
++ unsigned long flags;
++
++ ep = container_of (_ep, struct pxa25x_ep, ep);
++ if (!_ep || !ep->desc) {
++ DMSG("%s, %s not enabled\n", __func__,
++ _ep ? ep->ep.name : NULL);
++ return -EINVAL;
++ }
++ local_irq_save(flags);
++
++ nuke (ep, -ESHUTDOWN);
++
++ /* flush fifo (mostly for IN buffers) */
++ pxa25x_ep_fifo_flush (_ep);
++
++ ep->desc = NULL;
++ ep->stopped = 1;
++
++ local_irq_restore(flags);
++ DBG(DBG_VERBOSE, "%s disabled\n", _ep->name);
++ return 0;
++}
++
++/*-------------------------------------------------------------------------*/
++
++/* for the pxa25x, these can just wrap kmalloc/kfree. gadget drivers
++ * must still pass correctly initialized endpoints, since other controller
++ * drivers may care about how it's currently set up (dma issues etc).
++ */
++
++/*
++ * pxa25x_ep_alloc_request - allocate a request data structure
++ */
++static struct usb_request *
++pxa25x_ep_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags)
++{
++ struct pxa25x_request *req;
++
++ req = kzalloc(sizeof(*req), gfp_flags);
++ if (!req)
++ return NULL;
++
++ INIT_LIST_HEAD (&req->queue);
++ return &req->req;
++}
++
++
++/*
++ * pxa25x_ep_free_request - deallocate a request data structure
++ */
++static void
++pxa25x_ep_free_request (struct usb_ep *_ep, struct usb_request *_req)
++{
++ struct pxa25x_request *req;
++
++ req = container_of (_req, struct pxa25x_request, req);
++ WARN_ON(!list_empty (&req->queue));
++ kfree(req);
++}
++
++/*-------------------------------------------------------------------------*/
++
++/*
++ * done - retire a request; caller blocked irqs
++ */
++static void done(struct pxa25x_ep *ep, struct pxa25x_request *req, int status)
++{
++ unsigned stopped = ep->stopped;
++
++ list_del_init(&req->queue);
++
++ if (likely (req->req.status == -EINPROGRESS))
++ req->req.status = status;
++ else
++ status = req->req.status;
++
++ if (status && status != -ESHUTDOWN)
++ DBG(DBG_VERBOSE, "complete %s req %p stat %d len %u/%u\n",
++ ep->ep.name, &req->req, status,
++ req->req.actual, req->req.length);
++
++ /* don't modify queue heads during completion callback */
++ ep->stopped = 1;
++ req->req.complete(&ep->ep, &req->req);
++ ep->stopped = stopped;
++}
++
++
++static inline void ep0_idle (struct pxa25x_udc *dev)
++{
++ dev->ep0state = EP0_IDLE;
++}
++
++static int
++write_packet(volatile u32 *uddr, struct pxa25x_request *req, unsigned max)
++{
++ u8 *buf;
++ unsigned length, count;
++
++ buf = req->req.buf + req->req.actual;
++ prefetch(buf);
++
++ /* how big will this packet be? */
++ length = min(req->req.length - req->req.actual, max);
++ req->req.actual += length;
++
++ count = length;
++ while (likely(count--))
++ *uddr = *buf++;
++
++ return length;
++}
++
++/*
++ * write to an IN endpoint fifo, as many packets as possible.
++ * irqs will use this to write the rest later.
++ * caller guarantees at least one packet buffer is ready (or a zlp).
++ */
++static int
++write_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req)
++{
++ unsigned max;
++
++ max = le16_to_cpu(ep->desc->wMaxPacketSize);
++ do {
++ unsigned count;
++ int is_last, is_short;
++
++ count = write_packet(ep->reg_uddr, req, max);
++
++ /* last packet is usually short (or a zlp) */
++ if (unlikely (count != max))
++ is_last = is_short = 1;
++ else {
++ if (likely(req->req.length != req->req.actual)
++ || req->req.zero)
++ is_last = 0;
++ else
++ is_last = 1;
++ /* interrupt/iso maxpacket may not fill the fifo */
++ is_short = unlikely (max < ep->fifo_size);
++ }
++
++ DBG(DBG_VERY_NOISY, "wrote %s %d bytes%s%s %d left %p\n",
++ ep->ep.name, count,
++ is_last ? "/L" : "", is_short ? "/S" : "",
++ req->req.length - req->req.actual, req);
++
++ /* let loose that packet. maybe try writing another one,
++ * double buffering might work. TSP, TPC, and TFS
++ * bit values are the same for all normal IN endpoints.
++ */
++ *ep->reg_udccs = UDCCS_BI_TPC;
++ if (is_short)
++ *ep->reg_udccs = UDCCS_BI_TSP;
++
++ /* requests complete when all IN data is in the FIFO */
++ if (is_last) {
++ done (ep, req, 0);
++ if (list_empty(&ep->queue))
++ pio_irq_disable (ep->bEndpointAddress);
++ return 1;
++ }
++
++ // TODO experiment: how robust can fifo mode tweaking be?
++ // double buffering is off in the default fifo mode, which
++ // prevents TFS from being set here.
++
++ } while (*ep->reg_udccs & UDCCS_BI_TFS);
++ return 0;
++}
++
++/* caller asserts req->pending (ep0 irq status nyet cleared); starts
++ * ep0 data stage. these chips want very simple state transitions.
++ */
++static inline
++void ep0start(struct pxa25x_udc *dev, u32 flags, const char *tag)
++{
++ UDCCS0 = flags|UDCCS0_SA|UDCCS0_OPR;
++ USIR0 = USIR0_IR0;
++ dev->req_pending = 0;
++ DBG(DBG_VERY_NOISY, "%s %s, %02x/%02x\n",
++ __func__, tag, UDCCS0, flags);
++}
++
++static int
++write_ep0_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req)
++{
++ unsigned count;
++ int is_short;
++
++ count = write_packet(&UDDR0, req, EP0_FIFO_SIZE);
++ ep->dev->stats.write.bytes += count;
++
++ /* last packet "must be" short (or a zlp) */
++ is_short = (count != EP0_FIFO_SIZE);
++
++ DBG(DBG_VERY_NOISY, "ep0in %d bytes %d left %p\n", count,
++ req->req.length - req->req.actual, req);
++
++ if (unlikely (is_short)) {
++ if (ep->dev->req_pending)
++ ep0start(ep->dev, UDCCS0_IPR, "short IN");
++ else
++ UDCCS0 = UDCCS0_IPR;
++
++ count = req->req.length;
++ done (ep, req, 0);
++ ep0_idle(ep->dev);
++#ifndef CONFIG_ARCH_IXP4XX
++#if 1
++ /* This seems to get rid of lost status irqs in some cases:
++ * host responds quickly, or next request involves config
++ * change automagic, or should have been hidden, or ...
++ *
++ * FIXME get rid of all udelays possible...
++ */
++ if (count >= EP0_FIFO_SIZE) {
++ count = 100;
++ do {
++ if ((UDCCS0 & UDCCS0_OPR) != 0) {
++ /* clear OPR, generate ack */
++ UDCCS0 = UDCCS0_OPR;
++ break;
++ }
++ count--;
++ udelay(1);
++ } while (count);
++ }
++#endif
++#endif
++ } else if (ep->dev->req_pending)
++ ep0start(ep->dev, 0, "IN");
++ return is_short;
++}
++
++
++/*
++ * read_fifo - unload packet(s) from the fifo we use for usb OUT
++ * transfers and put them into the request. caller should have made
++ * sure there's at least one packet ready.
++ *
++ * returns true if the request completed because of short packet or the
++ * request buffer having filled (and maybe overran till end-of-packet).
++ */
++static int
++read_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req)
++{
++ for (;;) {
++ u32 udccs;
++ u8 *buf;
++ unsigned bufferspace, count, is_short;
++
++ /* make sure there's a packet in the FIFO.
++ * UDCCS_{BO,IO}_RPC are all the same bit value.
++ * UDCCS_{BO,IO}_RNE are all the same bit value.
++ */
++ udccs = *ep->reg_udccs;
++ if (unlikely ((udccs & UDCCS_BO_RPC) == 0))
++ break;
++ buf = req->req.buf + req->req.actual;
++ prefetchw(buf);
++ bufferspace = req->req.length - req->req.actual;
++
++ /* read all bytes from this packet */
++ if (likely (udccs & UDCCS_BO_RNE)) {
++ count = 1 + (0x0ff & *ep->reg_ubcr);
++ req->req.actual += min (count, bufferspace);
++ } else /* zlp */
++ count = 0;
++ is_short = (count < ep->ep.maxpacket);
++ DBG(DBG_VERY_NOISY, "read %s %02x, %d bytes%s req %p %d/%d\n",
++ ep->ep.name, udccs, count,
++ is_short ? "/S" : "",
++ req, req->req.actual, req->req.length);
++ while (likely (count-- != 0)) {
++ u8 byte = (u8) *ep->reg_uddr;
++
++ if (unlikely (bufferspace == 0)) {
++ /* this happens when the driver's buffer
++ * is smaller than what the host sent.
++ * discard the extra data.
++ */
++ if (req->req.status != -EOVERFLOW)
++ DMSG("%s overflow %d\n",
++ ep->ep.name, count);
++ req->req.status = -EOVERFLOW;
++ } else {
++ *buf++ = byte;
++ bufferspace--;
++ }
++ }
++ *ep->reg_udccs = UDCCS_BO_RPC;
++ /* RPC/RSP/RNE could now reflect the other packet buffer */
++
++ /* iso is one request per packet */
++ if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
++ if (udccs & UDCCS_IO_ROF)
++ req->req.status = -EHOSTUNREACH;
++ /* more like "is_done" */
++ is_short = 1;
++ }
++
++ /* completion */
++ if (is_short || req->req.actual == req->req.length) {
++ done (ep, req, 0);
++ if (list_empty(&ep->queue))
++ pio_irq_disable (ep->bEndpointAddress);
++ return 1;
++ }
++
++ /* finished that packet. the next one may be waiting... */
++ }
++ return 0;
++}
++
++/*
++ * special ep0 version of the above. no UBCR0 or double buffering; status
++ * handshaking is magic. most device protocols don't need control-OUT.
++ * CDC vendor commands (and RNDIS), mass storage CB/CBI, and some other
++ * protocols do use them.
++ */
++static int
++read_ep0_fifo (struct pxa25x_ep *ep, struct pxa25x_request *req)
++{
++ u8 *buf, byte;
++ unsigned bufferspace;
++
++ buf = req->req.buf + req->req.actual;
++ bufferspace = req->req.length - req->req.actual;
++
++ while (UDCCS0 & UDCCS0_RNE) {
++ byte = (u8) UDDR0;
++
++ if (unlikely (bufferspace == 0)) {
++ /* this happens when the driver's buffer
++ * is smaller than what the host sent.
++ * discard the extra data.
++ */
++ if (req->req.status != -EOVERFLOW)
++ DMSG("%s overflow\n", ep->ep.name);
++ req->req.status = -EOVERFLOW;
++ } else {
++ *buf++ = byte;
++ req->req.actual++;
++ bufferspace--;
++ }
++ }
++
++ UDCCS0 = UDCCS0_OPR | UDCCS0_IPR;
++
++ /* completion */
++ if (req->req.actual >= req->req.length)
++ return 1;
++
++ /* finished that packet. the next one may be waiting... */
++ return 0;
++}
++
++/*-------------------------------------------------------------------------*/
++
++static int
++pxa25x_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
++{
++ struct pxa25x_request *req;
++ struct pxa25x_ep *ep;
++ struct pxa25x_udc *dev;
++ unsigned long flags;
++
++ req = container_of(_req, struct pxa25x_request, req);
++ if (unlikely (!_req || !_req->complete || !_req->buf
++ || !list_empty(&req->queue))) {
++ DMSG("%s, bad params\n", __func__);
++ return -EINVAL;
++ }
++
++ ep = container_of(_ep, struct pxa25x_ep, ep);
++ if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) {
++ DMSG("%s, bad ep\n", __func__);
++ return -EINVAL;
++ }
++
++ dev = ep->dev;
++ if (unlikely (!dev->driver
++ || dev->gadget.speed == USB_SPEED_UNKNOWN)) {
++ DMSG("%s, bogus device state\n", __func__);
++ return -ESHUTDOWN;
++ }
++
++ /* iso is always one packet per request, that's the only way
++ * we can report per-packet status. that also helps with dma.
++ */
++ if (unlikely (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC
++ && req->req.length > le16_to_cpu
++ (ep->desc->wMaxPacketSize)))
++ return -EMSGSIZE;
++
++ DBG(DBG_NOISY, "%s queue req %p, len %d buf %p\n",
++ _ep->name, _req, _req->length, _req->buf);
++
++ local_irq_save(flags);
++
++ _req->status = -EINPROGRESS;
++ _req->actual = 0;
++
++ /* kickstart this i/o queue? */
++ if (list_empty(&ep->queue) && !ep->stopped) {
++ if (ep->desc == NULL/* ep0 */) {
++ unsigned length = _req->length;
++
++ switch (dev->ep0state) {
++ case EP0_IN_DATA_PHASE:
++ dev->stats.write.ops++;
++ if (write_ep0_fifo(ep, req))
++ req = NULL;
++ break;
++
++ case EP0_OUT_DATA_PHASE:
++ dev->stats.read.ops++;
++ /* messy ... */
++ if (dev->req_config) {
++ DBG(DBG_VERBOSE, "ep0 config ack%s\n",
++ dev->has_cfr ? "" : " raced");
++ if (dev->has_cfr)
++ UDCCFR = UDCCFR_AREN|UDCCFR_ACM
++ |UDCCFR_MB1;
++ done(ep, req, 0);
++ dev->ep0state = EP0_END_XFER;
++ local_irq_restore (flags);
++ return 0;
++ }
++ if (dev->req_pending)
++ ep0start(dev, UDCCS0_IPR, "OUT");
++ if (length == 0 || ((UDCCS0 & UDCCS0_RNE) != 0
++ && read_ep0_fifo(ep, req))) {
++ ep0_idle(dev);
++ done(ep, req, 0);
++ req = NULL;
++ }
++ break;
++
++ default:
++ DMSG("ep0 i/o, odd state %d\n", dev->ep0state);
++ local_irq_restore (flags);
++ return -EL2HLT;
++ }
++ /* can the FIFO can satisfy the request immediately? */
++ } else if ((ep->bEndpointAddress & USB_DIR_IN) != 0) {
++ if ((*ep->reg_udccs & UDCCS_BI_TFS) != 0
++ && write_fifo(ep, req))
++ req = NULL;
++ } else if ((*ep->reg_udccs & UDCCS_BO_RFS) != 0
++ && read_fifo(ep, req)) {
++ req = NULL;
++ }
++
++ if (likely (req && ep->desc))
++ pio_irq_enable(ep->bEndpointAddress);
++ }
++
++ /* pio or dma irq handler advances the queue. */
++ if (likely(req != NULL))
++ list_add_tail(&req->queue, &ep->queue);
++ local_irq_restore(flags);
++
++ return 0;
++}
++
++
++/*
++ * nuke - dequeue ALL requests
++ */
++static void nuke(struct pxa25x_ep *ep, int status)
++{
++ struct pxa25x_request *req;
++
++ /* called with irqs blocked */
++ while (!list_empty(&ep->queue)) {
++ req = list_entry(ep->queue.next,
++ struct pxa25x_request,
++ queue);
++ done(ep, req, status);
++ }
++ if (ep->desc)
++ pio_irq_disable (ep->bEndpointAddress);
++}
++
++
++/* dequeue JUST ONE request */
++static int pxa25x_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
++{
++ struct pxa25x_ep *ep;
++ struct pxa25x_request *req;
++ unsigned long flags;
++
++ ep = container_of(_ep, struct pxa25x_ep, ep);
++ if (!_ep || ep->ep.name == ep0name)
++ return -EINVAL;
++
++ local_irq_save(flags);
++
++ /* make sure it's actually queued on this endpoint */
++ list_for_each_entry (req, &ep->queue, queue) {
++ if (&req->req == _req)
++ break;
++ }
++ if (&req->req != _req) {
++ local_irq_restore(flags);
++ return -EINVAL;
++ }
++
++ done(ep, req, -ECONNRESET);
++
++ local_irq_restore(flags);
++ return 0;
++}
++
++/*-------------------------------------------------------------------------*/
++
++static int pxa25x_ep_set_halt(struct usb_ep *_ep, int value)
++{
++ struct pxa25x_ep *ep;
++ unsigned long flags;
++
++ ep = container_of(_ep, struct pxa25x_ep, ep);
++ if (unlikely (!_ep
++ || (!ep->desc && ep->ep.name != ep0name))
++ || ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
++ DMSG("%s, bad ep\n", __func__);
++ return -EINVAL;
++ }
++ if (value == 0) {
++ /* this path (reset toggle+halt) is needed to implement
++ * SET_INTERFACE on normal hardware. but it can't be
++ * done from software on the PXA UDC, and the hardware
++ * forgets to do it as part of SET_INTERFACE automagic.
++ */
++ DMSG("only host can clear %s halt\n", _ep->name);
++ return -EROFS;
++ }
++
++ local_irq_save(flags);
++
++ if ((ep->bEndpointAddress & USB_DIR_IN) != 0
++ && ((*ep->reg_udccs & UDCCS_BI_TFS) == 0
++ || !list_empty(&ep->queue))) {
++ local_irq_restore(flags);
++ return -EAGAIN;
++ }
++
++ /* FST bit is the same for control, bulk in, bulk out, interrupt in */
++ *ep->reg_udccs = UDCCS_BI_FST|UDCCS_BI_FTF;
++
++ /* ep0 needs special care */
++ if (!ep->desc) {
++ start_watchdog(ep->dev);
++ ep->dev->req_pending = 0;
++ ep->dev->ep0state = EP0_STALL;
++
++ /* and bulk/intr endpoints like dropping stalls too */
++ } else {
++ unsigned i;
++ for (i = 0; i < 1000; i += 20) {
++ if (*ep->reg_udccs & UDCCS_BI_SST)
++ break;
++ udelay(20);
++ }
++ }
++ local_irq_restore(flags);
++
++ DBG(DBG_VERBOSE, "%s halt\n", _ep->name);
++ return 0;
++}
++
++static int pxa25x_ep_fifo_status(struct usb_ep *_ep)
++{
++ struct pxa25x_ep *ep;
++
++ ep = container_of(_ep, struct pxa25x_ep, ep);
++ if (!_ep) {
++ DMSG("%s, bad ep\n", __func__);
++ return -ENODEV;
++ }
++ /* pxa can't report unclaimed bytes from IN fifos */
++ if ((ep->bEndpointAddress & USB_DIR_IN) != 0)
++ return -EOPNOTSUPP;
++ if (ep->dev->gadget.speed == USB_SPEED_UNKNOWN
++ || (*ep->reg_udccs & UDCCS_BO_RFS) == 0)
++ return 0;
++ else
++ return (*ep->reg_ubcr & 0xfff) + 1;
++}
++
++static void pxa25x_ep_fifo_flush(struct usb_ep *_ep)
++{
++ struct pxa25x_ep *ep;
++
++ ep = container_of(_ep, struct pxa25x_ep, ep);
++ if (!_ep || ep->ep.name == ep0name || !list_empty(&ep->queue)) {
++ DMSG("%s, bad ep\n", __func__);
++ return;
++ }
++
++ /* toggle and halt bits stay unchanged */
++
++ /* for OUT, just read and discard the FIFO contents. */
++ if ((ep->bEndpointAddress & USB_DIR_IN) == 0) {
++ while (((*ep->reg_udccs) & UDCCS_BO_RNE) != 0)
++ (void) *ep->reg_uddr;
++ return;
++ }
++
++ /* most IN status is the same, but ISO can't stall */
++ *ep->reg_udccs = UDCCS_BI_TPC|UDCCS_BI_FTF|UDCCS_BI_TUR
++ | (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC)
++ ? 0 : UDCCS_BI_SST;
++}
++
++
++static struct usb_ep_ops pxa25x_ep_ops = {
++ .enable = pxa25x_ep_enable,
++ .disable = pxa25x_ep_disable,
++
++ .alloc_request = pxa25x_ep_alloc_request,
++ .free_request = pxa25x_ep_free_request,
++
++ .queue = pxa25x_ep_queue,
++ .dequeue = pxa25x_ep_dequeue,
++
++ .set_halt = pxa25x_ep_set_halt,
++ .fifo_status = pxa25x_ep_fifo_status,
++ .fifo_flush = pxa25x_ep_fifo_flush,
++};
++
++
++/* ---------------------------------------------------------------------------
++ * device-scoped parts of the api to the usb controller hardware
++ * ---------------------------------------------------------------------------
++ */
++
++static int pxa25x_udc_get_frame(struct usb_gadget *_gadget)
++{
++ return ((UFNRH & 0x07) << 8) | (UFNRL & 0xff);
++}
++
++static int pxa25x_udc_wakeup(struct usb_gadget *_gadget)
++{
++ /* host may not have enabled remote wakeup */
++ if ((UDCCS0 & UDCCS0_DRWF) == 0)
++ return -EHOSTUNREACH;
++ udc_set_mask_UDCCR(UDCCR_RSM);
++ return 0;
++}
++
++static void stop_activity(struct pxa25x_udc *, struct usb_gadget_driver *);
++static void udc_enable (struct pxa25x_udc *);
++static void udc_disable(struct pxa25x_udc *);
++
++/* We disable the UDC -- and its 48 MHz clock -- whenever it's not
++ * in active use.
++ */
++static int pullup(struct pxa25x_udc *udc)
++{
++ int is_active = udc->vbus && udc->pullup && !udc->suspended;
++ DMSG("%s\n", is_active ? "active" : "inactive");
++ if (is_active) {
++ if (!udc->active) {
++ udc->active = 1;
++ /* Enable clock for USB device */
++ clk_enable(udc->clk);
++ udc_enable(udc);
++ }
++ } else {
++ if (udc->active) {
++ if (udc->gadget.speed != USB_SPEED_UNKNOWN) {
++ DMSG("disconnect %s\n", udc->driver
++ ? udc->driver->driver.name
++ : "(no driver)");
++ stop_activity(udc, udc->driver);
++ }
++ udc_disable(udc);
++ /* Disable clock for USB device */
++ clk_disable(udc->clk);
++ udc->active = 0;
++ }
++
++ }
++ return 0;
++}
++
++/* VBUS reporting logically comes from a transceiver */
++static int pxa25x_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
++{
++ struct pxa25x_udc *udc;
++
++ udc = container_of(_gadget, struct pxa25x_udc, gadget);
++ udc->vbus = (is_active != 0);
++ DMSG("vbus %s\n", is_active ? "supplied" : "inactive");
++ pullup(udc);
++ return 0;
++}
++
++/* drivers may have software control over D+ pullup */
++static int pxa25x_udc_pullup(struct usb_gadget *_gadget, int is_active)
++{
++ struct pxa25x_udc *udc;
++
++ udc = container_of(_gadget, struct pxa25x_udc, gadget);
++
++ /* not all boards support pullup control */
++ if (!udc->mach->gpio_pullup && !udc->mach->udc_command)
++ return -EOPNOTSUPP;
++
++ udc->pullup = (is_active != 0);
++ pullup(udc);
++ return 0;
++}
++
++static const struct usb_gadget_ops pxa25x_udc_ops = {
++ .get_frame = pxa25x_udc_get_frame,
++ .wakeup = pxa25x_udc_wakeup,
++ .vbus_session = pxa25x_udc_vbus_session,
++ .pullup = pxa25x_udc_pullup,
++
++ // .vbus_draw ... boards may consume current from VBUS, up to
++ // 100-500mA based on config. the 500uA suspend ceiling means
++ // that exclusively vbus-powered PXA designs violate USB specs.
++};
++
++/*-------------------------------------------------------------------------*/
++
++#ifdef CONFIG_USB_GADGET_DEBUG_FS
++
++static int
++udc_seq_show(struct seq_file *m, void *_d)
++{
++ struct pxa25x_udc *dev = m->private;
++ unsigned long flags;
++ int i;
++ u32 tmp;
++
++ local_irq_save(flags);
++
++ /* basic device status */
++ seq_printf(m, DRIVER_DESC "\n"
++ "%s version: %s\nGadget driver: %s\nHost %s\n\n",
++ driver_name, DRIVER_VERSION SIZE_STR "(pio)",
++ dev->driver ? dev->driver->driver.name : "(none)",
++ is_vbus_present() ? "full speed" : "disconnected");
++
++ /* registers for device and ep0 */
++ seq_printf(m,
++ "uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n",
++ UICR1, UICR0, USIR1, USIR0, UFNRH, UFNRL);
++
++ tmp = UDCCR;
++ seq_printf(m,
++ "udccr %02X =%s%s%s%s%s%s%s%s\n", tmp,
++ (tmp & UDCCR_REM) ? " rem" : "",
++ (tmp & UDCCR_RSTIR) ? " rstir" : "",
++ (tmp & UDCCR_SRM) ? " srm" : "",
++ (tmp & UDCCR_SUSIR) ? " susir" : "",
++ (tmp & UDCCR_RESIR) ? " resir" : "",
++ (tmp & UDCCR_RSM) ? " rsm" : "",
++ (tmp & UDCCR_UDA) ? " uda" : "",
++ (tmp & UDCCR_UDE) ? " ude" : "");
++
++ tmp = UDCCS0;
++ seq_printf(m,
++ "udccs0 %02X =%s%s%s%s%s%s%s%s\n", tmp,
++ (tmp & UDCCS0_SA) ? " sa" : "",
++ (tmp & UDCCS0_RNE) ? " rne" : "",
++ (tmp & UDCCS0_FST) ? " fst" : "",
++ (tmp & UDCCS0_SST) ? " sst" : "",
++ (tmp & UDCCS0_DRWF) ? " dwrf" : "",
++ (tmp & UDCCS0_FTF) ? " ftf" : "",
++ (tmp & UDCCS0_IPR) ? " ipr" : "",
++ (tmp & UDCCS0_OPR) ? " opr" : "");
++
++ if (dev->has_cfr) {
++ tmp = UDCCFR;
++ seq_printf(m,
++ "udccfr %02X =%s%s\n", tmp,
++ (tmp & UDCCFR_AREN) ? " aren" : "",
++ (tmp & UDCCFR_ACM) ? " acm" : "");
++ }
++
++ if (!is_vbus_present() || !dev->driver)
++ goto done;
++
++ seq_printf(m, "ep0 IN %lu/%lu, OUT %lu/%lu\nirqs %lu\n\n",
++ dev->stats.write.bytes, dev->stats.write.ops,
++ dev->stats.read.bytes, dev->stats.read.ops,
++ dev->stats.irqs);
++
++ /* dump endpoint queues */
++ for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) {
++ struct pxa25x_ep *ep = &dev->ep [i];
++ struct pxa25x_request *req;
++
++ if (i != 0) {
++ const struct usb_endpoint_descriptor *desc;
++
++ desc = ep->desc;
++ if (!desc)
++ continue;
++ tmp = *dev->ep [i].reg_udccs;
++ seq_printf(m,
++ "%s max %d %s udccs %02x irqs %lu\n",
++ ep->ep.name, le16_to_cpu(desc->wMaxPacketSize),
++ "pio", tmp, ep->pio_irqs);
++ /* TODO translate all five groups of udccs bits! */
++
++ } else /* ep0 should only have one transfer queued */
++ seq_printf(m, "ep0 max 16 pio irqs %lu\n",
++ ep->pio_irqs);
++
++ if (list_empty(&ep->queue)) {
++ seq_printf(m, "\t(nothing queued)\n");
++ continue;
++ }
++ list_for_each_entry(req, &ep->queue, queue) {
++ seq_printf(m,
++ "\treq %p len %d/%d buf %p\n",
++ &req->req, req->req.actual,
++ req->req.length, req->req.buf);
++ }
++ }
++
++done:
++ local_irq_restore(flags);
++ return 0;
++}
++
++static int
++udc_debugfs_open(struct inode *inode, struct file *file)
++{
++ return single_open(file, udc_seq_show, inode->i_private);
++}
++
++static const struct file_operations debug_fops = {
++ .open = udc_debugfs_open,
++ .read = seq_read,
++ .llseek = seq_lseek,
++ .release = single_release,
++ .owner = THIS_MODULE,
++};
++
++#define create_debug_files(dev) \
++ do { \
++ dev->debugfs_udc = debugfs_create_file(dev->gadget.name, \
++ S_IRUGO, NULL, dev, &debug_fops); \
++ } while (0)
++#define remove_debug_files(dev) \
++ do { \
++ if (dev->debugfs_udc) \
++ debugfs_remove(dev->debugfs_udc); \
++ } while (0)
++
++#else /* !CONFIG_USB_GADGET_DEBUG_FILES */
++
++#define create_debug_files(dev) do {} while (0)
++#define remove_debug_files(dev) do {} while (0)
++
++#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
++
++/*-------------------------------------------------------------------------*/
++
++/*
++ * udc_disable - disable USB device controller
++ */
++static void udc_disable(struct pxa25x_udc *dev)
++{
++ /* block all irqs */
++ udc_set_mask_UDCCR(UDCCR_SRM|UDCCR_REM);
++ UICR0 = UICR1 = 0xff;
++ UFNRH = UFNRH_SIM;
++
++ /* if hardware supports it, disconnect from usb */
++ pullup_off();
++
++ udc_clear_mask_UDCCR(UDCCR_UDE);
++
++ ep0_idle (dev);
++ dev->gadget.speed = USB_SPEED_UNKNOWN;
++}
++
++
++/*
++ * udc_reinit - initialize software state
++ */
++static void udc_reinit(struct pxa25x_udc *dev)
++{
++ u32 i;
++
++ /* device/ep0 records init */
++ INIT_LIST_HEAD (&dev->gadget.ep_list);
++ INIT_LIST_HEAD (&dev->gadget.ep0->ep_list);
++ dev->ep0state = EP0_IDLE;
++
++ /* basic endpoint records init */
++ for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) {
++ struct pxa25x_ep *ep = &dev->ep[i];
++
++ if (i != 0)
++ list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list);
++
++ ep->desc = NULL;
++ ep->stopped = 0;
++ INIT_LIST_HEAD (&ep->queue);
++ ep->pio_irqs = 0;
++ }
++
++ /* the rest was statically initialized, and is read-only */
++}
++
++/* until it's enabled, this UDC should be completely invisible
++ * to any USB host.
++ */
++static void udc_enable (struct pxa25x_udc *dev)
++{
++ udc_clear_mask_UDCCR(UDCCR_UDE);
++
++ /* try to clear these bits before we enable the udc */
++ udc_ack_int_UDCCR(UDCCR_SUSIR|/*UDCCR_RSTIR|*/UDCCR_RESIR);
++
++ ep0_idle(dev);
++ dev->gadget.speed = USB_SPEED_UNKNOWN;
++ dev->stats.irqs = 0;
++
++ /*
++ * sequence taken from chapter 12.5.10, PXA250 AppProcDevManual:
++ * - enable UDC
++ * - if RESET is already in progress, ack interrupt
++ * - unmask reset interrupt
++ */
++ udc_set_mask_UDCCR(UDCCR_UDE);
++ if (!(UDCCR & UDCCR_UDA))
++ udc_ack_int_UDCCR(UDCCR_RSTIR);
++
++ if (dev->has_cfr /* UDC_RES2 is defined */) {
++ /* pxa255 (a0+) can avoid a set_config race that could
++ * prevent gadget drivers from configuring correctly
++ */
++ UDCCFR = UDCCFR_ACM | UDCCFR_MB1;
++ } else {
++ /* "USB test mode" for pxa250 errata 40-42 (stepping a0, a1)
++ * which could result in missing packets and interrupts.
++ * supposedly one bit per endpoint, controlling whether it
++ * double buffers or not; ACM/AREN bits fit into the holes.
++ * zero bits (like USIR0_IRx) disable double buffering.
++ */
++ UDC_RES1 = 0x00;
++ UDC_RES2 = 0x00;
++ }
++
++ /* enable suspend/resume and reset irqs */
++ udc_clear_mask_UDCCR(UDCCR_SRM | UDCCR_REM);
++
++ /* enable ep0 irqs */
++ UICR0 &= ~UICR0_IM0;
++
++ /* if hardware supports it, pullup D+ and wait for reset */
++ pullup_on();
++}
++
++
++/* when a driver is successfully registered, it will receive
++ * control requests including set_configuration(), which enables
++ * non-control requests. then usb traffic follows until a
++ * disconnect is reported. then a host may connect again, or
++ * the driver might get unbound.
++ */
++int usb_gadget_register_driver(struct usb_gadget_driver *driver)
++{
++ struct pxa25x_udc *dev = the_controller;
++ int retval;
++
++ if (!driver
++ || driver->speed < USB_SPEED_FULL
++ || !driver->bind
++ || !driver->disconnect
++ || !driver->setup)
++ return -EINVAL;
++ if (!dev)
++ return -ENODEV;
++ if (dev->driver)
++ return -EBUSY;
++
++ /* first hook up the driver ... */
++ dev->driver = driver;
++ dev->gadget.dev.driver = &driver->driver;
++ dev->pullup = 1;
++
++ retval = device_add (&dev->gadget.dev);
++ if (retval) {
++fail:
++ dev->driver = NULL;
++ dev->gadget.dev.driver = NULL;
++ return retval;
++ }
++ retval = driver->bind(&dev->gadget);
++ if (retval) {
++ DMSG("bind to driver %s --> error %d\n",
++ driver->driver.name, retval);
++ device_del (&dev->gadget.dev);
++ goto fail;
++ }
++
++ /* ... then enable host detection and ep0; and we're ready
++ * for set_configuration as well as eventual disconnect.
++ */
++ DMSG("registered gadget driver '%s'\n", driver->driver.name);
++ pullup(dev);
++ dump_state(dev);
++ return 0;
++}
++EXPORT_SYMBOL(usb_gadget_register_driver);
++
++static void
++stop_activity(struct pxa25x_udc *dev, struct usb_gadget_driver *driver)
++{
++ int i;
++
++ /* don't disconnect drivers more than once */
++ if (dev->gadget.speed == USB_SPEED_UNKNOWN)
++ driver = NULL;
++ dev->gadget.speed = USB_SPEED_UNKNOWN;
++
++ /* prevent new request submissions, kill any outstanding requests */
++ for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) {
++ struct pxa25x_ep *ep = &dev->ep[i];
++
++ ep->stopped = 1;
++ nuke(ep, -ESHUTDOWN);
++ }
++ del_timer_sync(&dev->timer);
++
++ /* report disconnect; the driver is already quiesced */
++ if (driver)
++ driver->disconnect(&dev->gadget);
++
++ /* re-init driver-visible data structures */
++ udc_reinit(dev);
++}
++
++int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
++{
++ struct pxa25x_udc *dev = the_controller;
++
++ if (!dev)
++ return -ENODEV;
++ if (!driver || driver != dev->driver || !driver->unbind)
++ return -EINVAL;
++
++ local_irq_disable();
++ dev->pullup = 0;
++ pullup(dev);
++ stop_activity(dev, driver);
++ local_irq_enable();
++
++ driver->unbind(&dev->gadget);
++ dev->gadget.dev.driver = NULL;
++ dev->driver = NULL;
++
++ device_del (&dev->gadget.dev);
++
++ DMSG("unregistered gadget driver '%s'\n", driver->driver.name);
++ dump_state(dev);
++ return 0;
++}
++EXPORT_SYMBOL(usb_gadget_unregister_driver);
++
++
++/*-------------------------------------------------------------------------*/
++
++#ifdef CONFIG_ARCH_LUBBOCK
++
++/* Lubbock has separate connect and disconnect irqs. More typical designs
++ * use one GPIO as the VBUS IRQ, and another to control the D+ pullup.
++ */
++
++static irqreturn_t
++lubbock_vbus_irq(int irq, void *_dev)
++{
++ struct pxa25x_udc *dev = _dev;
++ int vbus;
++
++ dev->stats.irqs++;
++ switch (irq) {
++ case LUBBOCK_USB_IRQ:
++ vbus = 1;
++ disable_irq(LUBBOCK_USB_IRQ);
++ enable_irq(LUBBOCK_USB_DISC_IRQ);
++ break;
++ case LUBBOCK_USB_DISC_IRQ:
++ vbus = 0;
++ disable_irq(LUBBOCK_USB_DISC_IRQ);
++ enable_irq(LUBBOCK_USB_IRQ);
++ break;
++ default:
++ return IRQ_NONE;
++ }
++
++ pxa25x_udc_vbus_session(&dev->gadget, vbus);
++ return IRQ_HANDLED;
++}
++
++#endif
++
++static irqreturn_t udc_vbus_irq(int irq, void *_dev)
++{
++ struct pxa25x_udc *dev = _dev;
++ int vbus = gpio_get_value(dev->mach->gpio_vbus);
++
++ if (dev->mach->gpio_vbus_inverted)
++ vbus = !vbus;
++
++ pxa25x_udc_vbus_session(&dev->gadget, vbus);
++ return IRQ_HANDLED;
++}
++
++
++/*-------------------------------------------------------------------------*/
++
++static inline void clear_ep_state (struct pxa25x_udc *dev)
++{
++ unsigned i;
++
++ /* hardware SET_{CONFIGURATION,INTERFACE} automagic resets endpoint
++ * fifos, and pending transactions mustn't be continued in any case.
++ */
++ for (i = 1; i < PXA_UDC_NUM_ENDPOINTS; i++)
++ nuke(&dev->ep[i], -ECONNABORTED);
++}
++
++static void udc_watchdog(unsigned long _dev)
++{
++ struct pxa25x_udc *dev = (void *)_dev;
++
++ local_irq_disable();
++ if (dev->ep0state == EP0_STALL
++ && (UDCCS0 & UDCCS0_FST) == 0
++ && (UDCCS0 & UDCCS0_SST) == 0) {
++ UDCCS0 = UDCCS0_FST|UDCCS0_FTF;
++ DBG(DBG_VERBOSE, "ep0 re-stall\n");
++ start_watchdog(dev);
++ }
++ local_irq_enable();
++}
++
++static void handle_ep0 (struct pxa25x_udc *dev)
++{
++ u32 udccs0 = UDCCS0;
++ struct pxa25x_ep *ep = &dev->ep [0];
++ struct pxa25x_request *req;
++ union {
++ struct usb_ctrlrequest r;
++ u8 raw [8];
++ u32 word [2];
++ } u;
++
++ if (list_empty(&ep->queue))
++ req = NULL;
++ else
++ req = list_entry(ep->queue.next, struct pxa25x_request, queue);
++
++ /* clear stall status */
++ if (udccs0 & UDCCS0_SST) {
++ nuke(ep, -EPIPE);
++ UDCCS0 = UDCCS0_SST;
++ del_timer(&dev->timer);
++ ep0_idle(dev);
++ }
++
++ /* previous request unfinished? non-error iff back-to-back ... */
++ if ((udccs0 & UDCCS0_SA) != 0 && dev->ep0state != EP0_IDLE) {
++ nuke(ep, 0);
++ del_timer(&dev->timer);
++ ep0_idle(dev);
++ }
++
++ switch (dev->ep0state) {
++ case EP0_IDLE:
++ /* late-breaking status? */
++ udccs0 = UDCCS0;
++
++ /* start control request? */
++ if (likely((udccs0 & (UDCCS0_OPR|UDCCS0_SA|UDCCS0_RNE))
++ == (UDCCS0_OPR|UDCCS0_SA|UDCCS0_RNE))) {
++ int i;
++
++ nuke (ep, -EPROTO);
++
++ /* read SETUP packet */
++ for (i = 0; i < 8; i++) {
++ if (unlikely(!(UDCCS0 & UDCCS0_RNE))) {
++bad_setup:
++ DMSG("SETUP %d!\n", i);
++ goto stall;
++ }
++ u.raw [i] = (u8) UDDR0;
++ }
++ if (unlikely((UDCCS0 & UDCCS0_RNE) != 0))
++ goto bad_setup;
++
++got_setup:
++ DBG(DBG_VERBOSE, "SETUP %02x.%02x v%04x i%04x l%04x\n",
++ u.r.bRequestType, u.r.bRequest,
++ le16_to_cpu(u.r.wValue),
++ le16_to_cpu(u.r.wIndex),
++ le16_to_cpu(u.r.wLength));
++
++ /* cope with automagic for some standard requests. */
++ dev->req_std = (u.r.bRequestType & USB_TYPE_MASK)
++ == USB_TYPE_STANDARD;
++ dev->req_config = 0;
++ dev->req_pending = 1;
++ switch (u.r.bRequest) {
++ /* hardware restricts gadget drivers here! */
++ case USB_REQ_SET_CONFIGURATION:
++ if (u.r.bRequestType == USB_RECIP_DEVICE) {
++ /* reflect hardware's automagic
++ * up to the gadget driver.
++ */
++config_change:
++ dev->req_config = 1;
++ clear_ep_state(dev);
++ /* if !has_cfr, there's no synch
++ * else use AREN (later) not SA|OPR
++ * USIR0_IR0 acts edge sensitive
++ */
++ }
++ break;
++ /* ... and here, even more ... */
++ case USB_REQ_SET_INTERFACE:
++ if (u.r.bRequestType == USB_RECIP_INTERFACE) {
++ /* udc hardware is broken by design:
++ * - altsetting may only be zero;
++ * - hw resets all interfaces' eps;
++ * - ep reset doesn't include halt(?).
++ */
++ DMSG("broken set_interface (%d/%d)\n",
++ le16_to_cpu(u.r.wIndex),
++ le16_to_cpu(u.r.wValue));
++ goto config_change;
++ }
++ break;
++ /* hardware was supposed to hide this */
++ case USB_REQ_SET_ADDRESS:
++ if (u.r.bRequestType == USB_RECIP_DEVICE) {
++ ep0start(dev, 0, "address");
++ return;
++ }
++ break;
++ }
++
++ if (u.r.bRequestType & USB_DIR_IN)
++ dev->ep0state = EP0_IN_DATA_PHASE;
++ else
++ dev->ep0state = EP0_OUT_DATA_PHASE;
++
++ i = dev->driver->setup(&dev->gadget, &u.r);
++ if (i < 0) {
++ /* hardware automagic preventing STALL... */
++ if (dev->req_config) {
++ /* hardware sometimes neglects to tell
++ * tell us about config change events,
++ * so later ones may fail...
++ */
++ WARNING("config change %02x fail %d?\n",
++ u.r.bRequest, i);
++ return;
++ /* TODO experiment: if has_cfr,
++ * hardware didn't ACK; maybe we
++ * could actually STALL!
++ */
++ }
++ DBG(DBG_VERBOSE, "protocol STALL, "
++ "%02x err %d\n", UDCCS0, i);
++stall:
++ /* the watchdog timer helps deal with cases
++ * where udc seems to clear FST wrongly, and
++ * then NAKs instead of STALLing.
++ */
++ ep0start(dev, UDCCS0_FST|UDCCS0_FTF, "stall");
++ start_watchdog(dev);
++ dev->ep0state = EP0_STALL;
++
++ /* deferred i/o == no response yet */
++ } else if (dev->req_pending) {
++ if (likely(dev->ep0state == EP0_IN_DATA_PHASE
++ || dev->req_std || u.r.wLength))
++ ep0start(dev, 0, "defer");
++ else
++ ep0start(dev, UDCCS0_IPR, "defer/IPR");
++ }
++
++ /* expect at least one data or status stage irq */
++ return;
++
++ } else if (likely((udccs0 & (UDCCS0_OPR|UDCCS0_SA))
++ == (UDCCS0_OPR|UDCCS0_SA))) {
++ unsigned i;
++
++ /* pxa210/250 erratum 131 for B0/B1 says RNE lies.
++ * still observed on a pxa255 a0.
++ */
++ DBG(DBG_VERBOSE, "e131\n");
++ nuke(ep, -EPROTO);
++
++ /* read SETUP data, but don't trust it too much */
++ for (i = 0; i < 8; i++)
++ u.raw [i] = (u8) UDDR0;
++ if ((u.r.bRequestType & USB_RECIP_MASK)
++ > USB_RECIP_OTHER)
++ goto stall;
++ if (u.word [0] == 0 && u.word [1] == 0)
++ goto stall;
++ goto got_setup;
++ } else {
++ /* some random early IRQ:
++ * - we acked FST
++ * - IPR cleared
++ * - OPR got set, without SA (likely status stage)
++ */
++ UDCCS0 = udccs0 & (UDCCS0_SA|UDCCS0_OPR);
++ }
++ break;
++ case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR etc */
++ if (udccs0 & UDCCS0_OPR) {
++ UDCCS0 = UDCCS0_OPR|UDCCS0_FTF;
++ DBG(DBG_VERBOSE, "ep0in premature status\n");
++ if (req)
++ done(ep, req, 0);
++ ep0_idle(dev);
++ } else /* irq was IPR clearing */ {
++ if (req) {
++ /* this IN packet might finish the request */
++ (void) write_ep0_fifo(ep, req);
++ } /* else IN token before response was written */
++ }
++ break;
++ case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR etc */
++ if (udccs0 & UDCCS0_OPR) {
++ if (req) {
++ /* this OUT packet might finish the request */
++ if (read_ep0_fifo(ep, req))
++ done(ep, req, 0);
++ /* else more OUT packets expected */
++ } /* else OUT token before read was issued */
++ } else /* irq was IPR clearing */ {
++ DBG(DBG_VERBOSE, "ep0out premature status\n");
++ if (req)
++ done(ep, req, 0);
++ ep0_idle(dev);
++ }
++ break;
++ case EP0_END_XFER:
++ if (req)
++ done(ep, req, 0);
++ /* ack control-IN status (maybe in-zlp was skipped)
++ * also appears after some config change events.
++ */
++ if (udccs0 & UDCCS0_OPR)
++ UDCCS0 = UDCCS0_OPR;
++ ep0_idle(dev);
++ break;
++ case EP0_STALL:
++ UDCCS0 = UDCCS0_FST;
++ break;
++ }
++ USIR0 = USIR0_IR0;
++}
++
++static void handle_ep(struct pxa25x_ep *ep)
++{
++ struct pxa25x_request *req;
++ int is_in = ep->bEndpointAddress & USB_DIR_IN;
++ int completed;
++ u32 udccs, tmp;
++
++ do {
++ completed = 0;
++ if (likely (!list_empty(&ep->queue)))
++ req = list_entry(ep->queue.next,
++ struct pxa25x_request, queue);
++ else
++ req = NULL;
++
++ // TODO check FST handling
++
++ udccs = *ep->reg_udccs;
++ if (unlikely(is_in)) { /* irq from TPC, SST, or (ISO) TUR */
++ tmp = UDCCS_BI_TUR;
++ if (likely(ep->bmAttributes == USB_ENDPOINT_XFER_BULK))
++ tmp |= UDCCS_BI_SST;
++ tmp &= udccs;
++ if (likely (tmp))
++ *ep->reg_udccs = tmp;
++ if (req && likely ((udccs & UDCCS_BI_TFS) != 0))
++ completed = write_fifo(ep, req);
++
++ } else { /* irq from RPC (or for ISO, ROF) */
++ if (likely(ep->bmAttributes == USB_ENDPOINT_XFER_BULK))
++ tmp = UDCCS_BO_SST | UDCCS_BO_DME;
++ else
++ tmp = UDCCS_IO_ROF | UDCCS_IO_DME;
++ tmp &= udccs;
++ if (likely(tmp))
++ *ep->reg_udccs = tmp;
++
++ /* fifos can hold packets, ready for reading... */
++ if (likely(req)) {
++ completed = read_fifo(ep, req);
++ } else
++ pio_irq_disable (ep->bEndpointAddress);
++ }
++ ep->pio_irqs++;
++ } while (completed);
++}
++
++/*
++ * pxa25x_udc_irq - interrupt handler
++ *
++ * avoid delays in ep0 processing. the control handshaking isn't always
++ * under software control (pxa250c0 and the pxa255 are better), and delays
++ * could cause usb protocol errors.
++ */
++static irqreturn_t
++pxa25x_udc_irq(int irq, void *_dev)
++{
++ struct pxa25x_udc *dev = _dev;
++ int handled;
++
++ dev->stats.irqs++;
++ do {
++ u32 udccr = UDCCR;
++
++ handled = 0;
++
++ /* SUSpend Interrupt Request */
++ if (unlikely(udccr & UDCCR_SUSIR)) {
++ udc_ack_int_UDCCR(UDCCR_SUSIR);
++ handled = 1;
++ DBG(DBG_VERBOSE, "USB suspend%s\n", is_vbus_present()
++ ? "" : "+disconnect");
++
++ if (!is_vbus_present())
++ stop_activity(dev, dev->driver);
++ else if (dev->gadget.speed != USB_SPEED_UNKNOWN
++ && dev->driver
++ && dev->driver->suspend)
++ dev->driver->suspend(&dev->gadget);
++ ep0_idle (dev);
++ }
++
++ /* RESume Interrupt Request */
++ if (unlikely(udccr & UDCCR_RESIR)) {
++ udc_ack_int_UDCCR(UDCCR_RESIR);
++ handled = 1;
++ DBG(DBG_VERBOSE, "USB resume\n");
++
++ if (dev->gadget.speed != USB_SPEED_UNKNOWN
++ && dev->driver
++ && dev->driver->resume
++ && is_vbus_present())
++ dev->driver->resume(&dev->gadget);
++ }
++
++ /* ReSeT Interrupt Request - USB reset */
++ if (unlikely(udccr & UDCCR_RSTIR)) {
++ udc_ack_int_UDCCR(UDCCR_RSTIR);
++ handled = 1;
++
++ if ((UDCCR & UDCCR_UDA) == 0) {
++ DBG(DBG_VERBOSE, "USB reset start\n");
++
++ /* reset driver and endpoints,
++ * in case that's not yet done
++ */
++ stop_activity (dev, dev->driver);
++
++ } else {
++ DBG(DBG_VERBOSE, "USB reset end\n");
++ dev->gadget.speed = USB_SPEED_FULL;
++ memset(&dev->stats, 0, sizeof dev->stats);
++ /* driver and endpoints are still reset */
++ }
++
++ } else {
++ u32 usir0 = USIR0 & ~UICR0;
++ u32 usir1 = USIR1 & ~UICR1;
++ int i;
++
++ if (unlikely (!usir0 && !usir1))
++ continue;
++
++ DBG(DBG_VERY_NOISY, "irq %02x.%02x\n", usir1, usir0);
++
++ /* control traffic */
++ if (usir0 & USIR0_IR0) {
++ dev->ep[0].pio_irqs++;
++ handle_ep0(dev);
++ handled = 1;
++ }
++
++ /* endpoint data transfers */
++ for (i = 0; i < 8; i++) {
++ u32 tmp = 1 << i;
++
++ if (i && (usir0 & tmp)) {
++ handle_ep(&dev->ep[i]);
++ USIR0 |= tmp;
++ handled = 1;
++ }
++ if (usir1 & tmp) {
++ handle_ep(&dev->ep[i+8]);
++ USIR1 |= tmp;
++ handled = 1;
++ }
++ }
++ }
++
++ /* we could also ask for 1 msec SOF (SIR) interrupts */
++
++ } while (handled);
++ return IRQ_HANDLED;
++}
++
++/*-------------------------------------------------------------------------*/
++
++static void nop_release (struct device *dev)
++{
++ DMSG("%s %s\n", __func__, dev_name(dev));
++}
++
++/* this uses load-time allocation and initialization (instead of
++ * doing it at run-time) to save code, eliminate fault paths, and
++ * be more obviously correct.
++ */
++static struct pxa25x_udc memory = {
++ .gadget = {
++ .ops = &pxa25x_udc_ops,
++ .ep0 = &memory.ep[0].ep,
++ .name = driver_name,
++ .dev = {
++ .bus_id = "gadget",
++ .release = nop_release,
++ },
++ },
++
++ /* control endpoint */
++ .ep[0] = {
++ .ep = {
++ .name = ep0name,
++ .ops = &pxa25x_ep_ops,
++ .maxpacket = EP0_FIFO_SIZE,
++ },
++ .dev = &memory,
++ .reg_udccs = &UDCCS0,
++ .reg_uddr = &UDDR0,
++ },
++
++ /* first group of endpoints */
++ .ep[1] = {
++ .ep = {
++ .name = "ep1in-bulk",
++ .ops = &pxa25x_ep_ops,
++ .maxpacket = BULK_FIFO_SIZE,
++ },
++ .dev = &memory,
++ .fifo_size = BULK_FIFO_SIZE,
++ .bEndpointAddress = USB_DIR_IN | 1,
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++ .reg_udccs = &UDCCS1,
++ .reg_uddr = &UDDR1,
++ },
++ .ep[2] = {
++ .ep = {
++ .name = "ep2out-bulk",
++ .ops = &pxa25x_ep_ops,
++ .maxpacket = BULK_FIFO_SIZE,
++ },
++ .dev = &memory,
++ .fifo_size = BULK_FIFO_SIZE,
++ .bEndpointAddress = 2,
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++ .reg_udccs = &UDCCS2,
++ .reg_ubcr = &UBCR2,
++ .reg_uddr = &UDDR2,
++ },
++#ifndef CONFIG_USB_PXA25X_SMALL
++ .ep[3] = {
++ .ep = {
++ .name = "ep3in-iso",
++ .ops = &pxa25x_ep_ops,
++ .maxpacket = ISO_FIFO_SIZE,
++ },
++ .dev = &memory,
++ .fifo_size = ISO_FIFO_SIZE,
++ .bEndpointAddress = USB_DIR_IN | 3,
++ .bmAttributes = USB_ENDPOINT_XFER_ISOC,
++ .reg_udccs = &UDCCS3,
++ .reg_uddr = &UDDR3,
++ },
++ .ep[4] = {
++ .ep = {
++ .name = "ep4out-iso",
++ .ops = &pxa25x_ep_ops,
++ .maxpacket = ISO_FIFO_SIZE,
++ },
++ .dev = &memory,
++ .fifo_size = ISO_FIFO_SIZE,
++ .bEndpointAddress = 4,
++ .bmAttributes = USB_ENDPOINT_XFER_ISOC,
++ .reg_udccs = &UDCCS4,
++ .reg_ubcr = &UBCR4,
++ .reg_uddr = &UDDR4,
++ },
++ .ep[5] = {
++ .ep = {
++ .name = "ep5in-int",
++ .ops = &pxa25x_ep_ops,
++ .maxpacket = INT_FIFO_SIZE,
++ },
++ .dev = &memory,
++ .fifo_size = INT_FIFO_SIZE,
++ .bEndpointAddress = USB_DIR_IN | 5,
++ .bmAttributes = USB_ENDPOINT_XFER_INT,
++ .reg_udccs = &UDCCS5,
++ .reg_uddr = &UDDR5,
++ },
++
++ /* second group of endpoints */
++ .ep[6] = {
++ .ep = {
++ .name = "ep6in-bulk",
++ .ops = &pxa25x_ep_ops,
++ .maxpacket = BULK_FIFO_SIZE,
++ },
++ .dev = &memory,
++ .fifo_size = BULK_FIFO_SIZE,
++ .bEndpointAddress = USB_DIR_IN | 6,
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++ .reg_udccs = &UDCCS6,
++ .reg_uddr = &UDDR6,
++ },
++ .ep[7] = {
++ .ep = {
++ .name = "ep7out-bulk",
++ .ops = &pxa25x_ep_ops,
++ .maxpacket = BULK_FIFO_SIZE,
++ },
++ .dev = &memory,
++ .fifo_size = BULK_FIFO_SIZE,
++ .bEndpointAddress = 7,
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++ .reg_udccs = &UDCCS7,
++ .reg_ubcr = &UBCR7,
++ .reg_uddr = &UDDR7,
++ },
++ .ep[8] = {
++ .ep = {
++ .name = "ep8in-iso",
++ .ops = &pxa25x_ep_ops,
++ .maxpacket = ISO_FIFO_SIZE,
++ },
++ .dev = &memory,
++ .fifo_size = ISO_FIFO_SIZE,
++ .bEndpointAddress = USB_DIR_IN | 8,
++ .bmAttributes = USB_ENDPOINT_XFER_ISOC,
++ .reg_udccs = &UDCCS8,
++ .reg_uddr = &UDDR8,
++ },
++ .ep[9] = {
++ .ep = {
++ .name = "ep9out-iso",
++ .ops = &pxa25x_ep_ops,
++ .maxpacket = ISO_FIFO_SIZE,
++ },
++ .dev = &memory,
++ .fifo_size = ISO_FIFO_SIZE,
++ .bEndpointAddress = 9,
++ .bmAttributes = USB_ENDPOINT_XFER_ISOC,
++ .reg_udccs = &UDCCS9,
++ .reg_ubcr = &UBCR9,
++ .reg_uddr = &UDDR9,
++ },
++ .ep[10] = {
++ .ep = {
++ .name = "ep10in-int",
++ .ops = &pxa25x_ep_ops,
++ .maxpacket = INT_FIFO_SIZE,
++ },
++ .dev = &memory,
++ .fifo_size = INT_FIFO_SIZE,
++ .bEndpointAddress = USB_DIR_IN | 10,
++ .bmAttributes = USB_ENDPOINT_XFER_INT,
++ .reg_udccs = &UDCCS10,
++ .reg_uddr = &UDDR10,
++ },
++
++ /* third group of endpoints */
++ .ep[11] = {
++ .ep = {
++ .name = "ep11in-bulk",
++ .ops = &pxa25x_ep_ops,
++ .maxpacket = BULK_FIFO_SIZE,
++ },
++ .dev = &memory,
++ .fifo_size = BULK_FIFO_SIZE,
++ .bEndpointAddress = USB_DIR_IN | 11,
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++ .reg_udccs = &UDCCS11,
++ .reg_uddr = &UDDR11,
++ },
++ .ep[12] = {
++ .ep = {
++ .name = "ep12out-bulk",
++ .ops = &pxa25x_ep_ops,
++ .maxpacket = BULK_FIFO_SIZE,
++ },
++ .dev = &memory,
++ .fifo_size = BULK_FIFO_SIZE,
++ .bEndpointAddress = 12,
++ .bmAttributes = USB_ENDPOINT_XFER_BULK,
++ .reg_udccs = &UDCCS12,
++ .reg_ubcr = &UBCR12,
++ .reg_uddr = &UDDR12,
++ },
++ .ep[13] = {
++ .ep = {
++ .name = "ep13in-iso",
++ .ops = &pxa25x_ep_ops,
++ .maxpacket = ISO_FIFO_SIZE,
++ },
++ .dev = &memory,
++ .fifo_size = ISO_FIFO_SIZE,
++ .bEndpointAddress = USB_DIR_IN | 13,
++ .bmAttributes = USB_ENDPOINT_XFER_ISOC,
++ .reg_udccs = &UDCCS13,
++ .reg_uddr = &UDDR13,
++ },
++ .ep[14] = {
++ .ep = {
++ .name = "ep14out-iso",
++ .ops = &pxa25x_ep_ops,
++ .maxpacket = ISO_FIFO_SIZE,
++ },
++ .dev = &memory,
++ .fifo_size = ISO_FIFO_SIZE,
++ .bEndpointAddress = 14,
++ .bmAttributes = USB_ENDPOINT_XFER_ISOC,
++ .reg_udccs = &UDCCS14,
++ .reg_ubcr = &UBCR14,
++ .reg_uddr = &UDDR14,
++ },
++ .ep[15] = {
++ .ep = {
++ .name = "ep15in-int",
++ .ops = &pxa25x_ep_ops,
++ .maxpacket = INT_FIFO_SIZE,
++ },
++ .dev = &memory,
++ .fifo_size = INT_FIFO_SIZE,
++ .bEndpointAddress = USB_DIR_IN | 15,
++ .bmAttributes = USB_ENDPOINT_XFER_INT,
++ .reg_udccs = &UDCCS15,
++ .reg_uddr = &UDDR15,
++ },
++#endif /* !CONFIG_USB_PXA25X_SMALL */
++};
++
++#define CP15R0_VENDOR_MASK 0xffffe000
++
++#if defined(CONFIG_ARCH_PXA)
++#define CP15R0_XSCALE_VALUE 0x69052000 /* intel/arm/xscale */
++
++#elif defined(CONFIG_ARCH_IXP4XX)
++#define CP15R0_XSCALE_VALUE 0x69054000 /* intel/arm/ixp4xx */
++
++#endif
++
++#define CP15R0_PROD_MASK 0x000003f0
++#define PXA25x 0x00000100 /* and PXA26x */
++#define PXA210 0x00000120
++
++#define CP15R0_REV_MASK 0x0000000f
++
++#define CP15R0_PRODREV_MASK (CP15R0_PROD_MASK | CP15R0_REV_MASK)
++
++#define PXA255_A0 0x00000106 /* or PXA260_B1 */
++#define PXA250_C0 0x00000105 /* or PXA26x_B0 */
++#define PXA250_B2 0x00000104
++#define PXA250_B1 0x00000103 /* or PXA260_A0 */
++#define PXA250_B0 0x00000102
++#define PXA250_A1 0x00000101
++#define PXA250_A0 0x00000100
++
++#define PXA210_C0 0x00000125
++#define PXA210_B2 0x00000124
++#define PXA210_B1 0x00000123
++#define PXA210_B0 0x00000122
++#define IXP425_A0 0x000001c1
++#define IXP425_B0 0x000001f1
++#define IXP465_AD 0x00000200
++
++/*
++ * probe - binds to the platform device
++ */
++static int __init pxa25x_udc_probe(struct platform_device *pdev)
++{
++ struct pxa25x_udc *dev = &memory;
++ int retval, vbus_irq, irq;
++ u32 chiprev;
++
++ /* insist on Intel/ARM/XScale */
++ asm("mrc%? p15, 0, %0, c0, c0" : "=r" (chiprev));
++ if ((chiprev & CP15R0_VENDOR_MASK) != CP15R0_XSCALE_VALUE) {
++ pr_err("%s: not XScale!\n", driver_name);
++ return -ENODEV;
++ }
++
++ /* trigger chiprev-specific logic */
++ switch (chiprev & CP15R0_PRODREV_MASK) {
++#if defined(CONFIG_ARCH_PXA)
++ case PXA255_A0:
++ dev->has_cfr = 1;
++ break;
++ case PXA250_A0:
++ case PXA250_A1:
++ /* A0/A1 "not released"; ep 13, 15 unusable */
++ /* fall through */
++ case PXA250_B2: case PXA210_B2:
++ case PXA250_B1: case PXA210_B1:
++ case PXA250_B0: case PXA210_B0:
++ /* OUT-DMA is broken ... */
++ /* fall through */
++ case PXA250_C0: case PXA210_C0:
++ break;
++#elif defined(CONFIG_ARCH_IXP4XX)
++ case IXP425_A0:
++ case IXP425_B0:
++ case IXP465_AD:
++ dev->has_cfr = 1;
++ break;
++#endif
++ default:
++ pr_err("%s: unrecognized processor: %08x\n",
++ driver_name, chiprev);
++ /* iop3xx, ixp4xx, ... */
++ return -ENODEV;
++ }
++
++ irq = platform_get_irq(pdev, 0);
++ if (irq < 0)
++ return -ENODEV;
++
++ dev->clk = clk_get(&pdev->dev, "UDCCLK");
++ if (IS_ERR(dev->clk)) {
++ retval = PTR_ERR(dev->clk);
++ goto err_clk;
++ }
++
++ pr_debug("%s: IRQ %d%s%s\n", driver_name, irq,
++ dev->has_cfr ? "" : " (!cfr)",
++ SIZE_STR "(pio)"
++ );
++
++ /* other non-static parts of init */
++ dev->dev = &pdev->dev;
++ dev->mach = pdev->dev.platform_data;
++
++ if (dev->mach->gpio_vbus) {
++ if ((retval = gpio_request(dev->mach->gpio_vbus,
++ "pxa25x_udc GPIO VBUS"))) {
++ dev_dbg(&pdev->dev,
++ "can't get vbus gpio %d, err: %d\n",
++ dev->mach->gpio_vbus, retval);
++ goto err_gpio_vbus;
++ }
++ gpio_direction_input(dev->mach->gpio_vbus);
++ vbus_irq = gpio_to_irq(dev->mach->gpio_vbus);
++ } else
++ vbus_irq = 0;
++
++ if (dev->mach->gpio_pullup) {
++ if ((retval = gpio_request(dev->mach->gpio_pullup,
++ "pca25x_udc GPIO PULLUP"))) {
++ dev_dbg(&pdev->dev,
++ "can't get pullup gpio %d, err: %d\n",
++ dev->mach->gpio_pullup, retval);
++ goto err_gpio_pullup;
++ }
++ gpio_direction_output(dev->mach->gpio_pullup, 0);
++ }
++
++ init_timer(&dev->timer);
++ dev->timer.function = udc_watchdog;
++ dev->timer.data = (unsigned long) dev;
++
++ device_initialize(&dev->gadget.dev);
++ dev->gadget.dev.parent = &pdev->dev;
++ dev->gadget.dev.dma_mask = pdev->dev.dma_mask;
++
++ the_controller = dev;
++ platform_set_drvdata(pdev, dev);
++
++ udc_disable(dev);
++ udc_reinit(dev);
++
++ dev->vbus = is_vbus_present();
++
++ /* irq setup after old hardware state is cleaned up */
++ retval = request_irq(irq, pxa25x_udc_irq,
++ IRQF_DISABLED, driver_name, dev);
++ if (retval != 0) {
++ pr_err("%s: can't get irq %d, err %d\n",
++ driver_name, irq, retval);
++ goto err_irq1;
++ }
++ dev->got_irq = 1;
++
++#ifdef CONFIG_ARCH_LUBBOCK
++ if (machine_is_lubbock()) {
++ retval = request_irq(LUBBOCK_USB_DISC_IRQ,
++ lubbock_vbus_irq,
++ IRQF_DISABLED | IRQF_SAMPLE_RANDOM,
++ driver_name, dev);
++ if (retval != 0) {
++ pr_err("%s: can't get irq %i, err %d\n",
++ driver_name, LUBBOCK_USB_DISC_IRQ, retval);
++lubbock_fail0:
++ goto err_irq_lub;
++ }
++ retval = request_irq(LUBBOCK_USB_IRQ,
++ lubbock_vbus_irq,
++ IRQF_DISABLED | IRQF_SAMPLE_RANDOM,
++ driver_name, dev);
++ if (retval != 0) {
++ pr_err("%s: can't get irq %i, err %d\n",
++ driver_name, LUBBOCK_USB_IRQ, retval);
++ free_irq(LUBBOCK_USB_DISC_IRQ, dev);
++ goto lubbock_fail0;
++ }
++ } else
++#endif
++ if (vbus_irq) {
++ retval = request_irq(vbus_irq, udc_vbus_irq,
++ IRQF_DISABLED | IRQF_SAMPLE_RANDOM |
++ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
++ driver_name, dev);
++ if (retval != 0) {
++ pr_err("%s: can't get irq %i, err %d\n",
++ driver_name, vbus_irq, retval);
++ goto err_vbus_irq;
++ }
++ }
++ create_debug_files(dev);
++
++ return 0;
++
++ err_vbus_irq:
++#ifdef CONFIG_ARCH_LUBBOCK
++ free_irq(LUBBOCK_USB_DISC_IRQ, dev);
++ err_irq_lub:
++#endif
++ free_irq(irq, dev);
++ err_irq1:
++ if (dev->mach->gpio_pullup)
++ gpio_free(dev->mach->gpio_pullup);
++ err_gpio_pullup:
++ if (dev->mach->gpio_vbus)
++ gpio_free(dev->mach->gpio_vbus);
++ err_gpio_vbus:
++ clk_put(dev->clk);
++ err_clk:
++ return retval;
++}
++
++static void pxa25x_udc_shutdown(struct platform_device *_dev)
++{
++ pullup_off();
++}
++
++static int __exit pxa25x_udc_remove(struct platform_device *pdev)
++{
++ struct pxa25x_udc *dev = platform_get_drvdata(pdev);
++
++ if (dev->driver)
++ return -EBUSY;
++
++ dev->pullup = 0;
++ pullup(dev);
++
++ remove_debug_files(dev);
++
++ if (dev->got_irq) {
++ free_irq(platform_get_irq(pdev, 0), dev);
++ dev->got_irq = 0;
++ }
++#ifdef CONFIG_ARCH_LUBBOCK
++ if (machine_is_lubbock()) {
++ free_irq(LUBBOCK_USB_DISC_IRQ, dev);
++ free_irq(LUBBOCK_USB_IRQ, dev);
++ }
++#endif
++ if (dev->mach->gpio_vbus) {
++ free_irq(gpio_to_irq(dev->mach->gpio_vbus), dev);
++ gpio_free(dev->mach->gpio_vbus);
++ }
++ if (dev->mach->gpio_pullup)
++ gpio_free(dev->mach->gpio_pullup);
++
++ clk_put(dev->clk);
++
++ platform_set_drvdata(pdev, NULL);
++ the_controller = NULL;
++ return 0;
++}
++
++/*-------------------------------------------------------------------------*/
++
++#ifdef CONFIG_PM
++
++/* USB suspend (controlled by the host) and system suspend (controlled
++ * by the PXA) don't necessarily work well together. If USB is active,
++ * the 48 MHz clock is required; so the system can't enter 33 MHz idle
++ * mode, or any deeper PM saving state.
++ *
++ * For now, we punt and forcibly disconnect from the USB host when PXA
++ * enters any suspend state. While we're disconnected, we always disable
++ * the 48MHz USB clock ... allowing PXA sleep and/or 33 MHz idle states.
++ * Boards without software pullup control shouldn't use those states.
++ * VBUS IRQs should probably be ignored so that the PXA device just acts
++ * "dead" to USB hosts until system resume.
++ */
++static int pxa25x_udc_suspend(struct platform_device *dev, pm_message_t state)
++{
++ struct pxa25x_udc *udc = platform_get_drvdata(dev);
++ unsigned long flags;
++
++ if (!udc->mach->gpio_pullup && !udc->mach->udc_command)
++ WARNING("USB host won't detect disconnect!\n");
++ udc->suspended = 1;
++
++ local_irq_save(flags);
++ pullup(udc);
++ local_irq_restore(flags);
++
++ return 0;
++}
++
++static int pxa25x_udc_resume(struct platform_device *dev)
++{
++ struct pxa25x_udc *udc = platform_get_drvdata(dev);
++ unsigned long flags;
++
++ udc->suspended = 0;
++ local_irq_save(flags);
++ pullup(udc);
++ local_irq_restore(flags);
++
++ return 0;
++}
++
++#else
++#define pxa25x_udc_suspend NULL
++#define pxa25x_udc_resume NULL
++#endif
++
++/*-------------------------------------------------------------------------*/
++
++static struct platform_driver udc_driver = {
++ .shutdown = pxa25x_udc_shutdown,
++ .remove = __exit_p(pxa25x_udc_remove),
++ .suspend = pxa25x_udc_suspend,
++ .resume = pxa25x_udc_resume,
++ .driver = {
++ .owner = THIS_MODULE,
++ .name = "pxa25x-udc",
++ },
++};
++
++static int __init udc_init(void)
++{
++ pr_info("%s: version %s\n", driver_name, DRIVER_VERSION);
++ return platform_driver_probe(&udc_driver, pxa25x_udc_probe);
++}
++module_init(udc_init);
++
++static void __exit udc_exit(void)
++{
++ platform_driver_unregister(&udc_driver);
++}
++module_exit(udc_exit);
++
++MODULE_DESCRIPTION(DRIVER_DESC);
++MODULE_AUTHOR("Frank Becker, Robert Schwebel, David Brownell");
++MODULE_LICENSE("GPL");
++MODULE_ALIAS("platform:pxa25x-udc");
+diff --git a/drivers/usb/gadget/pxa25x_udc.h b/drivers/usb/gadget/pxa25x_udc.h
+new file mode 100644
+index 0000000..1d51aa2
+--- /dev/null
++++ b/drivers/usb/gadget/pxa25x_udc.h
+@@ -0,0 +1,266 @@
++/*
++ * Intel PXA25x on-chip full speed USB device controller
++ *
++ * Copyright (C) 2003 Robert Schwebel <r.schwebel@pengutronix.de>, Pengutronix
++ * Copyright (C) 2003 David Brownell
++ *
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++#ifndef __LINUX_USB_GADGET_PXA25X_H
++#define __LINUX_USB_GADGET_PXA25X_H
++
++#include <linux/types.h>
++
++/*-------------------------------------------------------------------------*/
++
++/* pxa25x has this (move to include/asm-arm/arch-pxa/pxa-regs.h) */
++#define UFNRH_SIR (1 << 7) /* SOF interrupt request */
++#define UFNRH_SIM (1 << 6) /* SOF interrupt mask */
++#define UFNRH_IPE14 (1 << 5) /* ISO packet error, ep14 */
++#define UFNRH_IPE9 (1 << 4) /* ISO packet error, ep9 */
++#define UFNRH_IPE4 (1 << 3) /* ISO packet error, ep4 */
++
++/* pxa255 has this (move to include/asm-arm/arch-pxa/pxa-regs.h) */
++#define UDCCFR UDC_RES2 /* UDC Control Function Register */
++#define UDCCFR_AREN (1 << 7) /* ACK response enable (now) */
++#define UDCCFR_ACM (1 << 2) /* ACK control mode (wait for AREN) */
++
++/* latest pxa255 errata define new "must be one" bits in UDCCFR */
++#define UDCCFR_MB1 (0xff & ~(UDCCFR_AREN|UDCCFR_ACM))
++
++/*-------------------------------------------------------------------------*/
++
++struct pxa25x_udc;
++
++struct pxa25x_ep {
++ struct usb_ep ep;
++ struct pxa25x_udc *dev;
++
++ const struct usb_endpoint_descriptor *desc;
++ struct list_head queue;
++ unsigned long pio_irqs;
++
++ unsigned short fifo_size;
++ u8 bEndpointAddress;
++ u8 bmAttributes;
++
++ unsigned stopped : 1;
++ unsigned dma_fixup : 1;
++
++ /* UDCCS = UDC Control/Status for this EP
++ * UBCR = UDC Byte Count Remaining (contents of OUT fifo)
++ * UDDR = UDC Endpoint Data Register (the fifo)
++ * DRCM = DMA Request Channel Map
++ */
++ volatile u32 *reg_udccs;
++ volatile u32 *reg_ubcr;
++ volatile u32 *reg_uddr;
++};
++
++struct pxa25x_request {
++ struct usb_request req;
++ struct list_head queue;
++};
++
++enum ep0_state {
++ EP0_IDLE,
++ EP0_IN_DATA_PHASE,
++ EP0_OUT_DATA_PHASE,
++ EP0_END_XFER,
++ EP0_STALL,
++};
++
++#define EP0_FIFO_SIZE ((unsigned)16)
++#define BULK_FIFO_SIZE ((unsigned)64)
++#define ISO_FIFO_SIZE ((unsigned)256)
++#define INT_FIFO_SIZE ((unsigned)8)
++
++struct udc_stats {
++ struct ep0stats {
++ unsigned long ops;
++ unsigned long bytes;
++ } read, write;
++ unsigned long irqs;
++};
++
++#ifdef CONFIG_USB_PXA25X_SMALL
++/* when memory's tight, SMALL config saves code+data. */
++#define PXA_UDC_NUM_ENDPOINTS 3
++#endif
++
++#ifndef PXA_UDC_NUM_ENDPOINTS
++#define PXA_UDC_NUM_ENDPOINTS 16
++#endif
++
++struct pxa25x_udc {
++ struct usb_gadget gadget;
++ struct usb_gadget_driver *driver;
++
++ enum ep0_state ep0state;
++ struct udc_stats stats;
++ unsigned got_irq : 1,
++ vbus : 1,
++ pullup : 1,
++ has_cfr : 1,
++ req_pending : 1,
++ req_std : 1,
++ req_config : 1,
++ suspended : 1,
++ active : 1;
++
++#define start_watchdog(dev) mod_timer(&dev->timer, jiffies + (HZ/200))
++ struct timer_list timer;
++
++ struct device *dev;
++ struct clk *clk;
++ struct pxa2xx_udc_mach_info *mach;
++ u64 dma_mask;
++ struct pxa25x_ep ep [PXA_UDC_NUM_ENDPOINTS];
++
++#ifdef CONFIG_USB_GADGET_DEBUG_FS
++ struct dentry *debugfs_udc;
++#endif
++};
++
++/*-------------------------------------------------------------------------*/
++
++#ifdef CONFIG_ARCH_LUBBOCK
++#include <mach/lubbock.h>
++/* lubbock can also report usb connect/disconnect irqs */
++#endif
++
++static struct pxa25x_udc *the_controller;
++
++/*-------------------------------------------------------------------------*/
++
++/*
++ * Debugging support vanishes in non-debug builds. DBG_NORMAL should be
++ * mostly silent during normal use/testing, with no timing side-effects.
++ */
++#define DBG_NORMAL 1 /* error paths, device state transitions */
++#define DBG_VERBOSE 2 /* add some success path trace info */
++#define DBG_NOISY 3 /* ... even more: request level */
++#define DBG_VERY_NOISY 4 /* ... even more: packet level */
++
++#define DMSG(stuff...) pr_debug("udc: " stuff)
++
++#ifdef DEBUG
++
++static int is_vbus_present(void);
++
++static const char *state_name[] = {
++ "EP0_IDLE",
++ "EP0_IN_DATA_PHASE", "EP0_OUT_DATA_PHASE",
++ "EP0_END_XFER", "EP0_STALL"
++};
++
++#ifdef VERBOSE_DEBUG
++# define UDC_DEBUG DBG_VERBOSE
++#else
++# define UDC_DEBUG DBG_NORMAL
++#endif
++
++static void __maybe_unused
++dump_udccr(const char *label)
++{
++ u32 udccr = UDCCR;
++ DMSG("%s %02X =%s%s%s%s%s%s%s%s\n",
++ label, udccr,
++ (udccr & UDCCR_REM) ? " rem" : "",
++ (udccr & UDCCR_RSTIR) ? " rstir" : "",
++ (udccr & UDCCR_SRM) ? " srm" : "",
++ (udccr & UDCCR_SUSIR) ? " susir" : "",
++ (udccr & UDCCR_RESIR) ? " resir" : "",
++ (udccr & UDCCR_RSM) ? " rsm" : "",
++ (udccr & UDCCR_UDA) ? " uda" : "",
++ (udccr & UDCCR_UDE) ? " ude" : "");
++}
++
++static void __maybe_unused
++dump_udccs0(const char *label)
++{
++ u32 udccs0 = UDCCS0;
++
++ DMSG("%s %s %02X =%s%s%s%s%s%s%s%s\n",
++ label, state_name[the_controller->ep0state], udccs0,
++ (udccs0 & UDCCS0_SA) ? " sa" : "",
++ (udccs0 & UDCCS0_RNE) ? " rne" : "",
++ (udccs0 & UDCCS0_FST) ? " fst" : "",
++ (udccs0 & UDCCS0_SST) ? " sst" : "",
++ (udccs0 & UDCCS0_DRWF) ? " dwrf" : "",
++ (udccs0 & UDCCS0_FTF) ? " ftf" : "",
++ (udccs0 & UDCCS0_IPR) ? " ipr" : "",
++ (udccs0 & UDCCS0_OPR) ? " opr" : "");
++}
++
++static void __maybe_unused
++dump_state(struct pxa25x_udc *dev)
++{
++ u32 tmp;
++ unsigned i;
++
++ DMSG("%s %s, uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n",
++ is_vbus_present() ? "host " : "disconnected",
++ state_name[dev->ep0state],
++ UICR1, UICR0, USIR1, USIR0, UFNRH, UFNRL);
++ dump_udccr("udccr");
++ if (dev->has_cfr) {
++ tmp = UDCCFR;
++ DMSG("udccfr %02X =%s%s\n", tmp,
++ (tmp & UDCCFR_AREN) ? " aren" : "",
++ (tmp & UDCCFR_ACM) ? " acm" : "");
++ }
++
++ if (!dev->driver) {
++ DMSG("no gadget driver bound\n");
++ return;
++ } else
++ DMSG("ep0 driver '%s'\n", dev->driver->driver.name);
++
++ if (!is_vbus_present())
++ return;
++
++ dump_udccs0 ("udccs0");
++ DMSG("ep0 IN %lu/%lu, OUT %lu/%lu\n",
++ dev->stats.write.bytes, dev->stats.write.ops,
++ dev->stats.read.bytes, dev->stats.read.ops);
++
++ for (i = 1; i < PXA_UDC_NUM_ENDPOINTS; i++) {
++ if (dev->ep [i].desc == NULL)
++ continue;
++ DMSG ("udccs%d = %02x\n", i, *dev->ep->reg_udccs);
++ }
++}
++
++#else
++
++#define dump_udccr(x) do{}while(0)
++#define dump_udccs0(x) do{}while(0)
++#define dump_state(x) do{}while(0)
++
++#define UDC_DEBUG ((unsigned)0)
++
++#endif
++
++#define DBG(lvl, stuff...) do{if ((lvl) <= UDC_DEBUG) DMSG(stuff);}while(0)
++
++#define ERR(stuff...) pr_err("udc: " stuff)
++#define WARNING(stuff...) pr_warning("udc: " stuff)
++#define INFO(stuff...) pr_info("udc: " stuff)
++
++
++#endif /* __LINUX_USB_GADGET_PXA25X_H */
+diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c
+index e02bfd4..7cbc78a 100644
+--- a/drivers/usb/gadget/pxa27x_udc.c
++++ b/drivers/usb/gadget/pxa27x_udc.c
+@@ -33,13 +33,13 @@
+ #include <linux/irq.h>
+
+ #include <asm/byteorder.h>
+-#include <asm/hardware.h>
++#include <mach/hardware.h>
+
+ #include <linux/usb.h>
+ #include <linux/usb/ch9.h>
+ #include <linux/usb/gadget.h>
+-
+-#include <asm/arch/udc.h>
++#include <mach/pxa2xx-regs.h> /* FIXME: for PSSR */
++#include <mach/udc.h>
+
+ #include "pxa27x_udc.h"
+
+@@ -1575,7 +1575,6 @@ static void udc_enable(struct pxa_udc *udc)
+ {
+ udc_writel(udc, UDCICR0, 0);
+ udc_writel(udc, UDCICR1, 0);
+- udc_writel(udc, UP2OCR, UP2OCR_HXOE);
+ udc_clear_mask_UDCCR(udc, UDCCR_UDE);
+
+ clk_enable(udc->clk);
+@@ -1623,7 +1622,7 @@ int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+ struct pxa_udc *udc = the_controller;
+ int retval;
+
+- if (!driver || driver->speed != USB_SPEED_FULL || !driver->bind
++ if (!driver || driver->speed < USB_SPEED_FULL || !driver->bind
+ || !driver->disconnect || !driver->setup)
+ return -EINVAL;
+ if (!udc)
+@@ -2360,18 +2359,19 @@ static int pxa_udc_resume(struct platform_device *_dev)
+ * Software must configure the USB OTG pad, UDC, and UHC
+ * to the state they were in before entering sleep mode.
+ */
+- PSSR |= PSSR_OTGPH;
++ if (cpu_is_pxa27x())
++ PSSR |= PSSR_OTGPH;
+
+ return 0;
+ }
+ #endif
+
+ /* work with hotplug and coldplug */
+-MODULE_ALIAS("platform:pxa2xx-udc");
++MODULE_ALIAS("platform:pxa27x-udc");
+
+ static struct platform_driver udc_driver = {
+ .driver = {
+- .name = "pxa2xx-udc",
++ .name = "pxa27x-udc",
+ .owner = THIS_MODULE,
+ },
+ .remove = __exit_p(pxa_udc_remove),
+diff --git a/drivers/usb/gadget/pxa27x_udc.h b/drivers/usb/gadget/pxa27x_udc.h
+index 97453db..1d1b793 100644
+--- a/drivers/usb/gadget/pxa27x_udc.h
++++ b/drivers/usb/gadget/pxa27x_udc.h
+@@ -484,12 +484,4 @@ static inline struct pxa_udc *to_gadget_udc(struct usb_gadget *gadget)
+ #define ep_warn(ep, fmt, arg...) \
+ dev_warn(ep->dev->dev, "%s:%s:" fmt, EPNAME(ep), __func__, ## arg)
+
+-/*
+- * Cannot include pxa-regs.h, as register names are similar.
+- * So PSSR is redefined here. This should be removed once UDC registers will
+- * be gone from pxa-regs.h.
+- */
+-#define PSSR __REG(0x40F00004) /* Power Manager Sleep Status */
+-#define PSSR_OTGPH (1 << 6) /* OTG Peripheral Hold */
+-
+ #endif /* __LINUX_USB_GADGET_PXA27X_H */
+diff --git a/drivers/usb/gadget/pxa2xx_udc.c b/drivers/usb/gadget/pxa2xx_udc.c
+deleted file mode 100644
+index 08f699b..0000000
+--- a/drivers/usb/gadget/pxa2xx_udc.c
++++ /dev/null
+@@ -1,2383 +0,0 @@
+-/*
+- * linux/drivers/usb/gadget/pxa2xx_udc.c
+- * Intel PXA25x and IXP4xx on-chip full speed USB device controllers
+- *
+- * Copyright (C) 2002 Intrinsyc, Inc. (Frank Becker)
+- * Copyright (C) 2003 Robert Schwebel, Pengutronix
+- * Copyright (C) 2003 Benedikt Spranger, Pengutronix
+- * Copyright (C) 2003 David Brownell
+- * Copyright (C) 2003 Joshua Wise
+- *
+- * This program is free software; you can redistribute it and/or modify
+- * it under the terms of the GNU General Public License as published by
+- * the Free Software Foundation; either version 2 of the License, or
+- * (at your option) any later version.
+- *
+- * This program is distributed in the hope that it will be useful,
+- * but WITHOUT ANY WARRANTY; without even the implied warranty of
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+- * GNU General Public License for more details.
+- *
+- * You should have received a copy of the GNU General Public License
+- * along with this program; if not, write to the Free Software
+- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+- *
+- */
+-
+-/* #define VERBOSE_DEBUG */
+-
+-#include <linux/device.h>
+-#include <linux/module.h>
+-#include <linux/kernel.h>
+-#include <linux/ioport.h>
+-#include <linux/types.h>
+-#include <linux/errno.h>
+-#include <linux/delay.h>
+-#include <linux/slab.h>
+-#include <linux/init.h>
+-#include <linux/timer.h>
+-#include <linux/list.h>
+-#include <linux/interrupt.h>
+-#include <linux/mm.h>
+-#include <linux/platform_device.h>
+-#include <linux/dma-mapping.h>
+-#include <linux/irq.h>
+-#include <linux/clk.h>
+-#include <linux/err.h>
+-#include <linux/seq_file.h>
+-#include <linux/debugfs.h>
+-
+-#include <asm/byteorder.h>
+-#include <asm/dma.h>
+-#include <asm/gpio.h>
+-#include <asm/io.h>
+-#include <asm/system.h>
+-#include <asm/mach-types.h>
+-#include <asm/unaligned.h>
+-#include <asm/hardware.h>
+-
+-#include <linux/usb/ch9.h>
+-#include <linux/usb/gadget.h>
+-
+-#include <asm/mach/udc_pxa2xx.h>
+-
+-
+-/*
+- * This driver handles the USB Device Controller (UDC) in Intel's PXA 25x
+- * series processors. The UDC for the IXP 4xx series is very similar.
+- * There are fifteen endpoints, in addition to ep0.
+- *
+- * Such controller drivers work with a gadget driver. The gadget driver
+- * returns descriptors, implements configuration and data protocols used
+- * by the host to interact with this device, and allocates endpoints to
+- * the different protocol interfaces. The controller driver virtualizes
+- * usb hardware so that the gadget drivers will be more portable.
+- *
+- * This UDC hardware wants to implement a bit too much USB protocol, so
+- * it constrains the sorts of USB configuration change events that work.
+- * The errata for these chips are misleading; some "fixed" bugs from
+- * pxa250 a0/a1 b0/b1/b2 sure act like they're still there.
+- *
+- * Note that the UDC hardware supports DMA (except on IXP) but that's
+- * not used here. IN-DMA (to host) is simple enough, when the data is
+- * suitably aligned (16 bytes) ... the network stack doesn't do that,
+- * other software can. OUT-DMA is buggy in most chip versions, as well
+- * as poorly designed (data toggle not automatic). So this driver won't
+- * bother using DMA. (Mostly-working IN-DMA support was available in
+- * kernels before 2.6.23, but was never enabled or well tested.)
+- */
+-
+-#define DRIVER_VERSION "30-June-2007"
+-#define DRIVER_DESC "PXA 25x USB Device Controller driver"
+-
+-
+-static const char driver_name [] = "pxa2xx_udc";
+-
+-static const char ep0name [] = "ep0";
+-
+-
+-#ifdef CONFIG_ARCH_IXP4XX
+-
+-/* cpu-specific register addresses are compiled in to this code */
+-#ifdef CONFIG_ARCH_PXA
+-#error "Can't configure both IXP and PXA"
+-#endif
+-
+-/* IXP doesn't yet support <linux/clk.h> */
+-#define clk_get(dev,name) NULL
+-#define clk_enable(clk) do { } while (0)
+-#define clk_disable(clk) do { } while (0)
+-#define clk_put(clk) do { } while (0)
+-
+-#endif
+-
+-#include "pxa2xx_udc.h"
+-
+-
+-#ifdef CONFIG_USB_PXA2XX_SMALL
+-#define SIZE_STR " (small)"
+-#else
+-#define SIZE_STR ""
+-#endif
+-
+-/* ---------------------------------------------------------------------------
+- * endpoint related parts of the api to the usb controller hardware,
+- * used by gadget driver; and the inner talker-to-hardware core.
+- * ---------------------------------------------------------------------------
+- */
+-
+-static void pxa2xx_ep_fifo_flush (struct usb_ep *ep);
+-static void nuke (struct pxa2xx_ep *, int status);
+-
+-/* one GPIO should be used to detect VBUS from the host */
+-static int is_vbus_present(void)
+-{
+- struct pxa2xx_udc_mach_info *mach = the_controller->mach;
+-
+- if (mach->gpio_vbus) {
+- int value = gpio_get_value(mach->gpio_vbus);
+- return mach->gpio_vbus_inverted ? !value : value;
+- }
+- if (mach->udc_is_connected)
+- return mach->udc_is_connected();
+- return 1;
+-}
+-
+-/* one GPIO should control a D+ pullup, so host sees this device (or not) */
+-static void pullup_off(void)
+-{
+- struct pxa2xx_udc_mach_info *mach = the_controller->mach;
+-
+- if (mach->gpio_pullup)
+- gpio_set_value(mach->gpio_pullup, 0);
+- else if (mach->udc_command)
+- mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT);
+-}
+-
+-static void pullup_on(void)
+-{
+- struct pxa2xx_udc_mach_info *mach = the_controller->mach;
+-
+- if (mach->gpio_pullup)
+- gpio_set_value(mach->gpio_pullup, 1);
+- else if (mach->udc_command)
+- mach->udc_command(PXA2XX_UDC_CMD_CONNECT);
+-}
+-
+-static void pio_irq_enable(int bEndpointAddress)
+-{
+- bEndpointAddress &= 0xf;
+- if (bEndpointAddress < 8)
+- UICR0 &= ~(1 << bEndpointAddress);
+- else {
+- bEndpointAddress -= 8;
+- UICR1 &= ~(1 << bEndpointAddress);
+- }
+-}
+-
+-static void pio_irq_disable(int bEndpointAddress)
+-{
+- bEndpointAddress &= 0xf;
+- if (bEndpointAddress < 8)
+- UICR0 |= 1 << bEndpointAddress;
+- else {
+- bEndpointAddress -= 8;
+- UICR1 |= 1 << bEndpointAddress;
+- }
+-}
+-
+-/* The UDCCR reg contains mask and interrupt status bits,
+- * so using '|=' isn't safe as it may ack an interrupt.
+- */
+-#define UDCCR_MASK_BITS (UDCCR_REM | UDCCR_SRM | UDCCR_UDE)
+-
+-static inline void udc_set_mask_UDCCR(int mask)
+-{
+- UDCCR = (UDCCR & UDCCR_MASK_BITS) | (mask & UDCCR_MASK_BITS);
+-}
+-
+-static inline void udc_clear_mask_UDCCR(int mask)
+-{
+- UDCCR = (UDCCR & UDCCR_MASK_BITS) & ~(mask & UDCCR_MASK_BITS);
+-}
+-
+-static inline void udc_ack_int_UDCCR(int mask)
+-{
+- /* udccr contains the bits we dont want to change */
+- __u32 udccr = UDCCR & UDCCR_MASK_BITS;
+-
+- UDCCR = udccr | (mask & ~UDCCR_MASK_BITS);
+-}
+-
+-/*
+- * endpoint enable/disable
+- *
+- * we need to verify the descriptors used to enable endpoints. since pxa2xx
+- * endpoint configurations are fixed, and are pretty much always enabled,
+- * there's not a lot to manage here.
+- *
+- * because pxa2xx can't selectively initialize bulk (or interrupt) endpoints,
+- * (resetting endpoint halt and toggle), SET_INTERFACE is unusable except
+- * for a single interface (with only the default altsetting) and for gadget
+- * drivers that don't halt endpoints (not reset by set_interface). that also
+- * means that if you use ISO, you must violate the USB spec rule that all
+- * iso endpoints must be in non-default altsettings.
+- */
+-static int pxa2xx_ep_enable (struct usb_ep *_ep,
+- const struct usb_endpoint_descriptor *desc)
+-{
+- struct pxa2xx_ep *ep;
+- struct pxa2xx_udc *dev;
+-
+- ep = container_of (_ep, struct pxa2xx_ep, ep);
+- if (!_ep || !desc || ep->desc || _ep->name == ep0name
+- || desc->bDescriptorType != USB_DT_ENDPOINT
+- || ep->bEndpointAddress != desc->bEndpointAddress
+- || ep->fifo_size < le16_to_cpu
+- (desc->wMaxPacketSize)) {
+- DMSG("%s, bad ep or descriptor\n", __func__);
+- return -EINVAL;
+- }
+-
+- /* xfer types must match, except that interrupt ~= bulk */
+- if (ep->bmAttributes != desc->bmAttributes
+- && ep->bmAttributes != USB_ENDPOINT_XFER_BULK
+- && desc->bmAttributes != USB_ENDPOINT_XFER_INT) {
+- DMSG("%s, %s type mismatch\n", __func__, _ep->name);
+- return -EINVAL;
+- }
+-
+- /* hardware _could_ do smaller, but driver doesn't */
+- if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK
+- && le16_to_cpu (desc->wMaxPacketSize)
+- != BULK_FIFO_SIZE)
+- || !desc->wMaxPacketSize) {
+- DMSG("%s, bad %s maxpacket\n", __func__, _ep->name);
+- return -ERANGE;
+- }
+-
+- dev = ep->dev;
+- if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {
+- DMSG("%s, bogus device state\n", __func__);
+- return -ESHUTDOWN;
+- }
+-
+- ep->desc = desc;
+- ep->stopped = 0;
+- ep->pio_irqs = 0;
+- ep->ep.maxpacket = le16_to_cpu (desc->wMaxPacketSize);
+-
+- /* flush fifo (mostly for OUT buffers) */
+- pxa2xx_ep_fifo_flush (_ep);
+-
+- /* ... reset halt state too, if we could ... */
+-
+- DBG(DBG_VERBOSE, "enabled %s\n", _ep->name);
+- return 0;
+-}
+-
+-static int pxa2xx_ep_disable (struct usb_ep *_ep)
+-{
+- struct pxa2xx_ep *ep;
+- unsigned long flags;
+-
+- ep = container_of (_ep, struct pxa2xx_ep, ep);
+- if (!_ep || !ep->desc) {
+- DMSG("%s, %s not enabled\n", __func__,
+- _ep ? ep->ep.name : NULL);
+- return -EINVAL;
+- }
+- local_irq_save(flags);
+-
+- nuke (ep, -ESHUTDOWN);
+-
+- /* flush fifo (mostly for IN buffers) */
+- pxa2xx_ep_fifo_flush (_ep);
+-
+- ep->desc = NULL;
+- ep->stopped = 1;
+-
+- local_irq_restore(flags);
+- DBG(DBG_VERBOSE, "%s disabled\n", _ep->name);
+- return 0;
+-}
+-
+-/*-------------------------------------------------------------------------*/
+-
+-/* for the pxa2xx, these can just wrap kmalloc/kfree. gadget drivers
+- * must still pass correctly initialized endpoints, since other controller
+- * drivers may care about how it's currently set up (dma issues etc).
+- */
+-
+-/*
+- * pxa2xx_ep_alloc_request - allocate a request data structure
+- */
+-static struct usb_request *
+-pxa2xx_ep_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags)
+-{
+- struct pxa2xx_request *req;
+-
+- req = kzalloc(sizeof(*req), gfp_flags);
+- if (!req)
+- return NULL;
+-
+- INIT_LIST_HEAD (&req->queue);
+- return &req->req;
+-}
+-
+-
+-/*
+- * pxa2xx_ep_free_request - deallocate a request data structure
+- */
+-static void
+-pxa2xx_ep_free_request (struct usb_ep *_ep, struct usb_request *_req)
+-{
+- struct pxa2xx_request *req;
+-
+- req = container_of (_req, struct pxa2xx_request, req);
+- WARN_ON (!list_empty (&req->queue));
+- kfree(req);
+-}
+-
+-/*-------------------------------------------------------------------------*/
+-
+-/*
+- * done - retire a request; caller blocked irqs
+- */
+-static void done(struct pxa2xx_ep *ep, struct pxa2xx_request *req, int status)
+-{
+- unsigned stopped = ep->stopped;
+-
+- list_del_init(&req->queue);
+-
+- if (likely (req->req.status == -EINPROGRESS))
+- req->req.status = status;
+- else
+- status = req->req.status;
+-
+- if (status && status != -ESHUTDOWN)
+- DBG(DBG_VERBOSE, "complete %s req %p stat %d len %u/%u\n",
+- ep->ep.name, &req->req, status,
+- req->req.actual, req->req.length);
+-
+- /* don't modify queue heads during completion callback */
+- ep->stopped = 1;
+- req->req.complete(&ep->ep, &req->req);
+- ep->stopped = stopped;
+-}
+-
+-
+-static inline void ep0_idle (struct pxa2xx_udc *dev)
+-{
+- dev->ep0state = EP0_IDLE;
+-}
+-
+-static int
+-write_packet(volatile u32 *uddr, struct pxa2xx_request *req, unsigned max)
+-{
+- u8 *buf;
+- unsigned length, count;
+-
+- buf = req->req.buf + req->req.actual;
+- prefetch(buf);
+-
+- /* how big will this packet be? */
+- length = min(req->req.length - req->req.actual, max);
+- req->req.actual += length;
+-
+- count = length;
+- while (likely(count--))
+- *uddr = *buf++;
+-
+- return length;
+-}
+-
+-/*
+- * write to an IN endpoint fifo, as many packets as possible.
+- * irqs will use this to write the rest later.
+- * caller guarantees at least one packet buffer is ready (or a zlp).
+- */
+-static int
+-write_fifo (struct pxa2xx_ep *ep, struct pxa2xx_request *req)
+-{
+- unsigned max;
+-
+- max = le16_to_cpu(ep->desc->wMaxPacketSize);
+- do {
+- unsigned count;
+- int is_last, is_short;
+-
+- count = write_packet(ep->reg_uddr, req, max);
+-
+- /* last packet is usually short (or a zlp) */
+- if (unlikely (count != max))
+- is_last = is_short = 1;
+- else {
+- if (likely(req->req.length != req->req.actual)
+- || req->req.zero)
+- is_last = 0;
+- else
+- is_last = 1;
+- /* interrupt/iso maxpacket may not fill the fifo */
+- is_short = unlikely (max < ep->fifo_size);
+- }
+-
+- DBG(DBG_VERY_NOISY, "wrote %s %d bytes%s%s %d left %p\n",
+- ep->ep.name, count,
+- is_last ? "/L" : "", is_short ? "/S" : "",
+- req->req.length - req->req.actual, req);
+-
+- /* let loose that packet. maybe try writing another one,
+- * double buffering might work. TSP, TPC, and TFS
+- * bit values are the same for all normal IN endpoints.
+- */
+- *ep->reg_udccs = UDCCS_BI_TPC;
+- if (is_short)
+- *ep->reg_udccs = UDCCS_BI_TSP;
+-
+- /* requests complete when all IN data is in the FIFO */
+- if (is_last) {
+- done (ep, req, 0);
+- if (list_empty(&ep->queue))
+- pio_irq_disable (ep->bEndpointAddress);
+- return 1;
+- }
+-
+- // TODO experiment: how robust can fifo mode tweaking be?
+- // double buffering is off in the default fifo mode, which
+- // prevents TFS from being set here.
+-
+- } while (*ep->reg_udccs & UDCCS_BI_TFS);
+- return 0;
+-}
+-
+-/* caller asserts req->pending (ep0 irq status nyet cleared); starts
+- * ep0 data stage. these chips want very simple state transitions.
+- */
+-static inline
+-void ep0start(struct pxa2xx_udc *dev, u32 flags, const char *tag)
+-{
+- UDCCS0 = flags|UDCCS0_SA|UDCCS0_OPR;
+- USIR0 = USIR0_IR0;
+- dev->req_pending = 0;
+- DBG(DBG_VERY_NOISY, "%s %s, %02x/%02x\n",
+- __func__, tag, UDCCS0, flags);
+-}
+-
+-static int
+-write_ep0_fifo (struct pxa2xx_ep *ep, struct pxa2xx_request *req)
+-{
+- unsigned count;
+- int is_short;
+-
+- count = write_packet(&UDDR0, req, EP0_FIFO_SIZE);
+- ep->dev->stats.write.bytes += count;
+-
+- /* last packet "must be" short (or a zlp) */
+- is_short = (count != EP0_FIFO_SIZE);
+-
+- DBG(DBG_VERY_NOISY, "ep0in %d bytes %d left %p\n", count,
+- req->req.length - req->req.actual, req);
+-
+- if (unlikely (is_short)) {
+- if (ep->dev->req_pending)
+- ep0start(ep->dev, UDCCS0_IPR, "short IN");
+- else
+- UDCCS0 = UDCCS0_IPR;
+-
+- count = req->req.length;
+- done (ep, req, 0);
+- ep0_idle(ep->dev);
+-#ifndef CONFIG_ARCH_IXP4XX
+-#if 1
+- /* This seems to get rid of lost status irqs in some cases:
+- * host responds quickly, or next request involves config
+- * change automagic, or should have been hidden, or ...
+- *
+- * FIXME get rid of all udelays possible...
+- */
+- if (count >= EP0_FIFO_SIZE) {
+- count = 100;
+- do {
+- if ((UDCCS0 & UDCCS0_OPR) != 0) {
+- /* clear OPR, generate ack */
+- UDCCS0 = UDCCS0_OPR;
+- break;
+- }
+- count--;
+- udelay(1);
+- } while (count);
+- }
+-#endif
+-#endif
+- } else if (ep->dev->req_pending)
+- ep0start(ep->dev, 0, "IN");
+- return is_short;
+-}
+-
+-
+-/*
+- * read_fifo - unload packet(s) from the fifo we use for usb OUT
+- * transfers and put them into the request. caller should have made
+- * sure there's at least one packet ready.
+- *
+- * returns true if the request completed because of short packet or the
+- * request buffer having filled (and maybe overran till end-of-packet).
+- */
+-static int
+-read_fifo (struct pxa2xx_ep *ep, struct pxa2xx_request *req)
+-{
+- for (;;) {
+- u32 udccs;
+- u8 *buf;
+- unsigned bufferspace, count, is_short;
+-
+- /* make sure there's a packet in the FIFO.
+- * UDCCS_{BO,IO}_RPC are all the same bit value.
+- * UDCCS_{BO,IO}_RNE are all the same bit value.
+- */
+- udccs = *ep->reg_udccs;
+- if (unlikely ((udccs & UDCCS_BO_RPC) == 0))
+- break;
+- buf = req->req.buf + req->req.actual;
+- prefetchw(buf);
+- bufferspace = req->req.length - req->req.actual;
+-
+- /* read all bytes from this packet */
+- if (likely (udccs & UDCCS_BO_RNE)) {
+- count = 1 + (0x0ff & *ep->reg_ubcr);
+- req->req.actual += min (count, bufferspace);
+- } else /* zlp */
+- count = 0;
+- is_short = (count < ep->ep.maxpacket);
+- DBG(DBG_VERY_NOISY, "read %s %02x, %d bytes%s req %p %d/%d\n",
+- ep->ep.name, udccs, count,
+- is_short ? "/S" : "",
+- req, req->req.actual, req->req.length);
+- while (likely (count-- != 0)) {
+- u8 byte = (u8) *ep->reg_uddr;
+-
+- if (unlikely (bufferspace == 0)) {
+- /* this happens when the driver's buffer
+- * is smaller than what the host sent.
+- * discard the extra data.
+- */
+- if (req->req.status != -EOVERFLOW)
+- DMSG("%s overflow %d\n",
+- ep->ep.name, count);
+- req->req.status = -EOVERFLOW;
+- } else {
+- *buf++ = byte;
+- bufferspace--;
+- }
+- }
+- *ep->reg_udccs = UDCCS_BO_RPC;
+- /* RPC/RSP/RNE could now reflect the other packet buffer */
+-
+- /* iso is one request per packet */
+- if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
+- if (udccs & UDCCS_IO_ROF)
+- req->req.status = -EHOSTUNREACH;
+- /* more like "is_done" */
+- is_short = 1;
+- }
+-
+- /* completion */
+- if (is_short || req->req.actual == req->req.length) {
+- done (ep, req, 0);
+- if (list_empty(&ep->queue))
+- pio_irq_disable (ep->bEndpointAddress);
+- return 1;
+- }
+-
+- /* finished that packet. the next one may be waiting... */
+- }
+- return 0;
+-}
+-
+-/*
+- * special ep0 version of the above. no UBCR0 or double buffering; status
+- * handshaking is magic. most device protocols don't need control-OUT.
+- * CDC vendor commands (and RNDIS), mass storage CB/CBI, and some other
+- * protocols do use them.
+- */
+-static int
+-read_ep0_fifo (struct pxa2xx_ep *ep, struct pxa2xx_request *req)
+-{
+- u8 *buf, byte;
+- unsigned bufferspace;
+-
+- buf = req->req.buf + req->req.actual;
+- bufferspace = req->req.length - req->req.actual;
+-
+- while (UDCCS0 & UDCCS0_RNE) {
+- byte = (u8) UDDR0;
+-
+- if (unlikely (bufferspace == 0)) {
+- /* this happens when the driver's buffer
+- * is smaller than what the host sent.
+- * discard the extra data.
+- */
+- if (req->req.status != -EOVERFLOW)
+- DMSG("%s overflow\n", ep->ep.name);
+- req->req.status = -EOVERFLOW;
+- } else {
+- *buf++ = byte;
+- req->req.actual++;
+- bufferspace--;
+- }
+- }
+-
+- UDCCS0 = UDCCS0_OPR | UDCCS0_IPR;
+-
+- /* completion */
+- if (req->req.actual >= req->req.length)
+- return 1;
+-
+- /* finished that packet. the next one may be waiting... */
+- return 0;
+-}
+-
+-/*-------------------------------------------------------------------------*/
+-
+-static int
+-pxa2xx_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
+-{
+- struct pxa2xx_request *req;
+- struct pxa2xx_ep *ep;
+- struct pxa2xx_udc *dev;
+- unsigned long flags;
+-
+- req = container_of(_req, struct pxa2xx_request, req);
+- if (unlikely (!_req || !_req->complete || !_req->buf
+- || !list_empty(&req->queue))) {
+- DMSG("%s, bad params\n", __func__);
+- return -EINVAL;
+- }
+-
+- ep = container_of(_ep, struct pxa2xx_ep, ep);
+- if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) {
+- DMSG("%s, bad ep\n", __func__);
+- return -EINVAL;
+- }
+-
+- dev = ep->dev;
+- if (unlikely (!dev->driver
+- || dev->gadget.speed == USB_SPEED_UNKNOWN)) {
+- DMSG("%s, bogus device state\n", __func__);
+- return -ESHUTDOWN;
+- }
+-
+- /* iso is always one packet per request, that's the only way
+- * we can report per-packet status. that also helps with dma.
+- */
+- if (unlikely (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC
+- && req->req.length > le16_to_cpu
+- (ep->desc->wMaxPacketSize)))
+- return -EMSGSIZE;
+-
+- DBG(DBG_NOISY, "%s queue req %p, len %d buf %p\n",
+- _ep->name, _req, _req->length, _req->buf);
+-
+- local_irq_save(flags);
+-
+- _req->status = -EINPROGRESS;
+- _req->actual = 0;
+-
+- /* kickstart this i/o queue? */
+- if (list_empty(&ep->queue) && !ep->stopped) {
+- if (ep->desc == NULL/* ep0 */) {
+- unsigned length = _req->length;
+-
+- switch (dev->ep0state) {
+- case EP0_IN_DATA_PHASE:
+- dev->stats.write.ops++;
+- if (write_ep0_fifo(ep, req))
+- req = NULL;
+- break;
+-
+- case EP0_OUT_DATA_PHASE:
+- dev->stats.read.ops++;
+- /* messy ... */
+- if (dev->req_config) {
+- DBG(DBG_VERBOSE, "ep0 config ack%s\n",
+- dev->has_cfr ? "" : " raced");
+- if (dev->has_cfr)
+- UDCCFR = UDCCFR_AREN|UDCCFR_ACM
+- |UDCCFR_MB1;
+- done(ep, req, 0);
+- dev->ep0state = EP0_END_XFER;
+- local_irq_restore (flags);
+- return 0;
+- }
+- if (dev->req_pending)
+- ep0start(dev, UDCCS0_IPR, "OUT");
+- if (length == 0 || ((UDCCS0 & UDCCS0_RNE) != 0
+- && read_ep0_fifo(ep, req))) {
+- ep0_idle(dev);
+- done(ep, req, 0);
+- req = NULL;
+- }
+- break;
+-
+- default:
+- DMSG("ep0 i/o, odd state %d\n", dev->ep0state);
+- local_irq_restore (flags);
+- return -EL2HLT;
+- }
+- /* can the FIFO can satisfy the request immediately? */
+- } else if ((ep->bEndpointAddress & USB_DIR_IN) != 0) {
+- if ((*ep->reg_udccs & UDCCS_BI_TFS) != 0
+- && write_fifo(ep, req))
+- req = NULL;
+- } else if ((*ep->reg_udccs & UDCCS_BO_RFS) != 0
+- && read_fifo(ep, req)) {
+- req = NULL;
+- }
+-
+- if (likely (req && ep->desc))
+- pio_irq_enable(ep->bEndpointAddress);
+- }
+-
+- /* pio or dma irq handler advances the queue. */
+- if (likely(req != NULL))
+- list_add_tail(&req->queue, &ep->queue);
+- local_irq_restore(flags);
+-
+- return 0;
+-}
+-
+-
+-/*
+- * nuke - dequeue ALL requests
+- */
+-static void nuke(struct pxa2xx_ep *ep, int status)
+-{
+- struct pxa2xx_request *req;
+-
+- /* called with irqs blocked */
+- while (!list_empty(&ep->queue)) {
+- req = list_entry(ep->queue.next,
+- struct pxa2xx_request,
+- queue);
+- done(ep, req, status);
+- }
+- if (ep->desc)
+- pio_irq_disable (ep->bEndpointAddress);
+-}
+-
+-
+-/* dequeue JUST ONE request */
+-static int pxa2xx_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+-{
+- struct pxa2xx_ep *ep;
+- struct pxa2xx_request *req;
+- unsigned long flags;
+-
+- ep = container_of(_ep, struct pxa2xx_ep, ep);
+- if (!_ep || ep->ep.name == ep0name)
+- return -EINVAL;
+-
+- local_irq_save(flags);
+-
+- /* make sure it's actually queued on this endpoint */
+- list_for_each_entry (req, &ep->queue, queue) {
+- if (&req->req == _req)
+- break;
+- }
+- if (&req->req != _req) {
+- local_irq_restore(flags);
+- return -EINVAL;
+- }
+-
+- done(ep, req, -ECONNRESET);
+-
+- local_irq_restore(flags);
+- return 0;
+-}
+-
+-/*-------------------------------------------------------------------------*/
+-
+-static int pxa2xx_ep_set_halt(struct usb_ep *_ep, int value)
+-{
+- struct pxa2xx_ep *ep;
+- unsigned long flags;
+-
+- ep = container_of(_ep, struct pxa2xx_ep, ep);
+- if (unlikely (!_ep
+- || (!ep->desc && ep->ep.name != ep0name))
+- || ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
+- DMSG("%s, bad ep\n", __func__);
+- return -EINVAL;
+- }
+- if (value == 0) {
+- /* this path (reset toggle+halt) is needed to implement
+- * SET_INTERFACE on normal hardware. but it can't be
+- * done from software on the PXA UDC, and the hardware
+- * forgets to do it as part of SET_INTERFACE automagic.
+- */
+- DMSG("only host can clear %s halt\n", _ep->name);
+- return -EROFS;
+- }
+-
+- local_irq_save(flags);
+-
+- if ((ep->bEndpointAddress & USB_DIR_IN) != 0
+- && ((*ep->reg_udccs & UDCCS_BI_TFS) == 0
+- || !list_empty(&ep->queue))) {
+- local_irq_restore(flags);
+- return -EAGAIN;
+- }
+-
+- /* FST bit is the same for control, bulk in, bulk out, interrupt in */
+- *ep->reg_udccs = UDCCS_BI_FST|UDCCS_BI_FTF;
+-
+- /* ep0 needs special care */
+- if (!ep->desc) {
+- start_watchdog(ep->dev);
+- ep->dev->req_pending = 0;
+- ep->dev->ep0state = EP0_STALL;
+-
+- /* and bulk/intr endpoints like dropping stalls too */
+- } else {
+- unsigned i;
+- for (i = 0; i < 1000; i += 20) {
+- if (*ep->reg_udccs & UDCCS_BI_SST)
+- break;
+- udelay(20);
+- }
+- }
+- local_irq_restore(flags);
+-
+- DBG(DBG_VERBOSE, "%s halt\n", _ep->name);
+- return 0;
+-}
+-
+-static int pxa2xx_ep_fifo_status(struct usb_ep *_ep)
+-{
+- struct pxa2xx_ep *ep;
+-
+- ep = container_of(_ep, struct pxa2xx_ep, ep);
+- if (!_ep) {
+- DMSG("%s, bad ep\n", __func__);
+- return -ENODEV;
+- }
+- /* pxa can't report unclaimed bytes from IN fifos */
+- if ((ep->bEndpointAddress & USB_DIR_IN) != 0)
+- return -EOPNOTSUPP;
+- if (ep->dev->gadget.speed == USB_SPEED_UNKNOWN
+- || (*ep->reg_udccs & UDCCS_BO_RFS) == 0)
+- return 0;
+- else
+- return (*ep->reg_ubcr & 0xfff) + 1;
+-}
+-
+-static void pxa2xx_ep_fifo_flush(struct usb_ep *_ep)
+-{
+- struct pxa2xx_ep *ep;
+-
+- ep = container_of(_ep, struct pxa2xx_ep, ep);
+- if (!_ep || ep->ep.name == ep0name || !list_empty(&ep->queue)) {
+- DMSG("%s, bad ep\n", __func__);
+- return;
+- }
+-
+- /* toggle and halt bits stay unchanged */
+-
+- /* for OUT, just read and discard the FIFO contents. */
+- if ((ep->bEndpointAddress & USB_DIR_IN) == 0) {
+- while (((*ep->reg_udccs) & UDCCS_BO_RNE) != 0)
+- (void) *ep->reg_uddr;
+- return;
+- }
+-
+- /* most IN status is the same, but ISO can't stall */
+- *ep->reg_udccs = UDCCS_BI_TPC|UDCCS_BI_FTF|UDCCS_BI_TUR
+- | (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC)
+- ? 0 : UDCCS_BI_SST;
+-}
+-
+-
+-static struct usb_ep_ops pxa2xx_ep_ops = {
+- .enable = pxa2xx_ep_enable,
+- .disable = pxa2xx_ep_disable,
+-
+- .alloc_request = pxa2xx_ep_alloc_request,
+- .free_request = pxa2xx_ep_free_request,
+-
+- .queue = pxa2xx_ep_queue,
+- .dequeue = pxa2xx_ep_dequeue,
+-
+- .set_halt = pxa2xx_ep_set_halt,
+- .fifo_status = pxa2xx_ep_fifo_status,
+- .fifo_flush = pxa2xx_ep_fifo_flush,
+-};
+-
+-
+-/* ---------------------------------------------------------------------------
+- * device-scoped parts of the api to the usb controller hardware
+- * ---------------------------------------------------------------------------
+- */
+-
+-static int pxa2xx_udc_get_frame(struct usb_gadget *_gadget)
+-{
+- return ((UFNRH & 0x07) << 8) | (UFNRL & 0xff);
+-}
+-
+-static int pxa2xx_udc_wakeup(struct usb_gadget *_gadget)
+-{
+- /* host may not have enabled remote wakeup */
+- if ((UDCCS0 & UDCCS0_DRWF) == 0)
+- return -EHOSTUNREACH;
+- udc_set_mask_UDCCR(UDCCR_RSM);
+- return 0;
+-}
+-
+-static void stop_activity(struct pxa2xx_udc *, struct usb_gadget_driver *);
+-static void udc_enable (struct pxa2xx_udc *);
+-static void udc_disable(struct pxa2xx_udc *);
+-
+-/* We disable the UDC -- and its 48 MHz clock -- whenever it's not
+- * in active use.
+- */
+-static int pullup(struct pxa2xx_udc *udc)
+-{
+- int is_active = udc->vbus && udc->pullup && !udc->suspended;
+- DMSG("%s\n", is_active ? "active" : "inactive");
+- if (is_active) {
+- if (!udc->active) {
+- udc->active = 1;
+- /* Enable clock for USB device */
+- clk_enable(udc->clk);
+- udc_enable(udc);
+- }
+- } else {
+- if (udc->active) {
+- if (udc->gadget.speed != USB_SPEED_UNKNOWN) {
+- DMSG("disconnect %s\n", udc->driver
+- ? udc->driver->driver.name
+- : "(no driver)");
+- stop_activity(udc, udc->driver);
+- }
+- udc_disable(udc);
+- /* Disable clock for USB device */
+- clk_disable(udc->clk);
+- udc->active = 0;
+- }
+-
+- }
+- return 0;
+-}
+-
+-/* VBUS reporting logically comes from a transceiver */
+-static int pxa2xx_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
+-{
+- struct pxa2xx_udc *udc;
+-
+- udc = container_of(_gadget, struct pxa2xx_udc, gadget);
+- udc->vbus = (is_active != 0);
+- DMSG("vbus %s\n", is_active ? "supplied" : "inactive");
+- pullup(udc);
+- return 0;
+-}
+-
+-/* drivers may have software control over D+ pullup */
+-static int pxa2xx_udc_pullup(struct usb_gadget *_gadget, int is_active)
+-{
+- struct pxa2xx_udc *udc;
+-
+- udc = container_of(_gadget, struct pxa2xx_udc, gadget);
+-
+- /* not all boards support pullup control */
+- if (!udc->mach->gpio_pullup && !udc->mach->udc_command)
+- return -EOPNOTSUPP;
+-
+- udc->pullup = (is_active != 0);
+- pullup(udc);
+- return 0;
+-}
+-
+-static const struct usb_gadget_ops pxa2xx_udc_ops = {
+- .get_frame = pxa2xx_udc_get_frame,
+- .wakeup = pxa2xx_udc_wakeup,
+- .vbus_session = pxa2xx_udc_vbus_session,
+- .pullup = pxa2xx_udc_pullup,
+-
+- // .vbus_draw ... boards may consume current from VBUS, up to
+- // 100-500mA based on config. the 500uA suspend ceiling means
+- // that exclusively vbus-powered PXA designs violate USB specs.
+-};
+-
+-/*-------------------------------------------------------------------------*/
+-
+-#ifdef CONFIG_USB_GADGET_DEBUG_FS
+-
+-static int
+-udc_seq_show(struct seq_file *m, void *_d)
+-{
+- struct pxa2xx_udc *dev = m->private;
+- unsigned long flags;
+- int i;
+- u32 tmp;
+-
+- local_irq_save(flags);
+-
+- /* basic device status */
+- seq_printf(m, DRIVER_DESC "\n"
+- "%s version: %s\nGadget driver: %s\nHost %s\n\n",
+- driver_name, DRIVER_VERSION SIZE_STR "(pio)",
+- dev->driver ? dev->driver->driver.name : "(none)",
+- is_vbus_present() ? "full speed" : "disconnected");
+-
+- /* registers for device and ep0 */
+- seq_printf(m,
+- "uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n",
+- UICR1, UICR0, USIR1, USIR0, UFNRH, UFNRL);
+-
+- tmp = UDCCR;
+- seq_printf(m,
+- "udccr %02X =%s%s%s%s%s%s%s%s\n", tmp,
+- (tmp & UDCCR_REM) ? " rem" : "",
+- (tmp & UDCCR_RSTIR) ? " rstir" : "",
+- (tmp & UDCCR_SRM) ? " srm" : "",
+- (tmp & UDCCR_SUSIR) ? " susir" : "",
+- (tmp & UDCCR_RESIR) ? " resir" : "",
+- (tmp & UDCCR_RSM) ? " rsm" : "",
+- (tmp & UDCCR_UDA) ? " uda" : "",
+- (tmp & UDCCR_UDE) ? " ude" : "");
+-
+- tmp = UDCCS0;
+- seq_printf(m,
+- "udccs0 %02X =%s%s%s%s%s%s%s%s\n", tmp,
+- (tmp & UDCCS0_SA) ? " sa" : "",
+- (tmp & UDCCS0_RNE) ? " rne" : "",
+- (tmp & UDCCS0_FST) ? " fst" : "",
+- (tmp & UDCCS0_SST) ? " sst" : "",
+- (tmp & UDCCS0_DRWF) ? " dwrf" : "",
+- (tmp & UDCCS0_FTF) ? " ftf" : "",
+- (tmp & UDCCS0_IPR) ? " ipr" : "",
+- (tmp & UDCCS0_OPR) ? " opr" : "");
+-
+- if (dev->has_cfr) {
+- tmp = UDCCFR;
+- seq_printf(m,
+- "udccfr %02X =%s%s\n", tmp,
+- (tmp & UDCCFR_AREN) ? " aren" : "",
+- (tmp & UDCCFR_ACM) ? " acm" : "");
+- }
+-
+- if (!is_vbus_present() || !dev->driver)
+- goto done;
+-
+- seq_printf(m, "ep0 IN %lu/%lu, OUT %lu/%lu\nirqs %lu\n\n",
+- dev->stats.write.bytes, dev->stats.write.ops,
+- dev->stats.read.bytes, dev->stats.read.ops,
+- dev->stats.irqs);
+-
+- /* dump endpoint queues */
+- for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) {
+- struct pxa2xx_ep *ep = &dev->ep [i];
+- struct pxa2xx_request *req;
+-
+- if (i != 0) {
+- const struct usb_endpoint_descriptor *desc;
+-
+- desc = ep->desc;
+- if (!desc)
+- continue;
+- tmp = *dev->ep [i].reg_udccs;
+- seq_printf(m,
+- "%s max %d %s udccs %02x irqs %lu\n",
+- ep->ep.name, le16_to_cpu(desc->wMaxPacketSize),
+- "pio", tmp, ep->pio_irqs);
+- /* TODO translate all five groups of udccs bits! */
+-
+- } else /* ep0 should only have one transfer queued */
+- seq_printf(m, "ep0 max 16 pio irqs %lu\n",
+- ep->pio_irqs);
+-
+- if (list_empty(&ep->queue)) {
+- seq_printf(m, "\t(nothing queued)\n");
+- continue;
+- }
+- list_for_each_entry(req, &ep->queue, queue) {
+- seq_printf(m,
+- "\treq %p len %d/%d buf %p\n",
+- &req->req, req->req.actual,
+- req->req.length, req->req.buf);
+- }
+- }
+-
+-done:
+- local_irq_restore(flags);
+- return 0;
+-}
+-
+-static int
+-udc_debugfs_open(struct inode *inode, struct file *file)
+-{
+- return single_open(file, udc_seq_show, inode->i_private);
+-}
+-
+-static const struct file_operations debug_fops = {
+- .open = udc_debugfs_open,
+- .read = seq_read,
+- .llseek = seq_lseek,
+- .release = single_release,
+- .owner = THIS_MODULE,
+-};
+-
+-#define create_debug_files(dev) \
+- do { \
+- dev->debugfs_udc = debugfs_create_file(dev->gadget.name, \
+- S_IRUGO, NULL, dev, &debug_fops); \
+- } while (0)
+-#define remove_debug_files(dev) \
+- do { \
+- if (dev->debugfs_udc) \
+- debugfs_remove(dev->debugfs_udc); \
+- } while (0)
+-
+-#else /* !CONFIG_USB_GADGET_DEBUG_FILES */
+-
+-#define create_debug_files(dev) do {} while (0)
+-#define remove_debug_files(dev) do {} while (0)
+-
+-#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
+-
+-/*-------------------------------------------------------------------------*/
+-
+-/*
+- * udc_disable - disable USB device controller
+- */
+-static void udc_disable(struct pxa2xx_udc *dev)
+-{
+- /* block all irqs */
+- udc_set_mask_UDCCR(UDCCR_SRM|UDCCR_REM);
+- UICR0 = UICR1 = 0xff;
+- UFNRH = UFNRH_SIM;
+-
+- /* if hardware supports it, disconnect from usb */
+- pullup_off();
+-
+- udc_clear_mask_UDCCR(UDCCR_UDE);
+-
+- ep0_idle (dev);
+- dev->gadget.speed = USB_SPEED_UNKNOWN;
+-}
+-
+-
+-/*
+- * udc_reinit - initialize software state
+- */
+-static void udc_reinit(struct pxa2xx_udc *dev)
+-{
+- u32 i;
+-
+- /* device/ep0 records init */
+- INIT_LIST_HEAD (&dev->gadget.ep_list);
+- INIT_LIST_HEAD (&dev->gadget.ep0->ep_list);
+- dev->ep0state = EP0_IDLE;
+-
+- /* basic endpoint records init */
+- for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) {
+- struct pxa2xx_ep *ep = &dev->ep[i];
+-
+- if (i != 0)
+- list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list);
+-
+- ep->desc = NULL;
+- ep->stopped = 0;
+- INIT_LIST_HEAD (&ep->queue);
+- ep->pio_irqs = 0;
+- }
+-
+- /* the rest was statically initialized, and is read-only */
+-}
+-
+-/* until it's enabled, this UDC should be completely invisible
+- * to any USB host.
+- */
+-static void udc_enable (struct pxa2xx_udc *dev)
+-{
+- udc_clear_mask_UDCCR(UDCCR_UDE);
+-
+- /* try to clear these bits before we enable the udc */
+- udc_ack_int_UDCCR(UDCCR_SUSIR|/*UDCCR_RSTIR|*/UDCCR_RESIR);
+-
+- ep0_idle(dev);
+- dev->gadget.speed = USB_SPEED_UNKNOWN;
+- dev->stats.irqs = 0;
+-
+- /*
+- * sequence taken from chapter 12.5.10, PXA250 AppProcDevManual:
+- * - enable UDC
+- * - if RESET is already in progress, ack interrupt
+- * - unmask reset interrupt
+- */
+- udc_set_mask_UDCCR(UDCCR_UDE);
+- if (!(UDCCR & UDCCR_UDA))
+- udc_ack_int_UDCCR(UDCCR_RSTIR);
+-
+- if (dev->has_cfr /* UDC_RES2 is defined */) {
+- /* pxa255 (a0+) can avoid a set_config race that could
+- * prevent gadget drivers from configuring correctly
+- */
+- UDCCFR = UDCCFR_ACM | UDCCFR_MB1;
+- } else {
+- /* "USB test mode" for pxa250 errata 40-42 (stepping a0, a1)
+- * which could result in missing packets and interrupts.
+- * supposedly one bit per endpoint, controlling whether it
+- * double buffers or not; ACM/AREN bits fit into the holes.
+- * zero bits (like USIR0_IRx) disable double buffering.
+- */
+- UDC_RES1 = 0x00;
+- UDC_RES2 = 0x00;
+- }
+-
+- /* enable suspend/resume and reset irqs */
+- udc_clear_mask_UDCCR(UDCCR_SRM | UDCCR_REM);
+-
+- /* enable ep0 irqs */
+- UICR0 &= ~UICR0_IM0;
+-
+- /* if hardware supports it, pullup D+ and wait for reset */
+- pullup_on();
+-}
+-
+-
+-/* when a driver is successfully registered, it will receive
+- * control requests including set_configuration(), which enables
+- * non-control requests. then usb traffic follows until a
+- * disconnect is reported. then a host may connect again, or
+- * the driver might get unbound.
+- */
+-int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+-{
+- struct pxa2xx_udc *dev = the_controller;
+- int retval;
+-
+- if (!driver
+- || driver->speed < USB_SPEED_FULL
+- || !driver->bind
+- || !driver->disconnect
+- || !driver->setup)
+- return -EINVAL;
+- if (!dev)
+- return -ENODEV;
+- if (dev->driver)
+- return -EBUSY;
+-
+- /* first hook up the driver ... */
+- dev->driver = driver;
+- dev->gadget.dev.driver = &driver->driver;
+- dev->pullup = 1;
+-
+- retval = device_add (&dev->gadget.dev);
+- if (retval) {
+-fail:
+- dev->driver = NULL;
+- dev->gadget.dev.driver = NULL;
+- return retval;
+- }
+- retval = driver->bind(&dev->gadget);
+- if (retval) {
+- DMSG("bind to driver %s --> error %d\n",
+- driver->driver.name, retval);
+- device_del (&dev->gadget.dev);
+- goto fail;
+- }
+-
+- /* ... then enable host detection and ep0; and we're ready
+- * for set_configuration as well as eventual disconnect.
+- */
+- DMSG("registered gadget driver '%s'\n", driver->driver.name);
+- pullup(dev);
+- dump_state(dev);
+- return 0;
+-}
+-EXPORT_SYMBOL(usb_gadget_register_driver);
+-
+-static void
+-stop_activity(struct pxa2xx_udc *dev, struct usb_gadget_driver *driver)
+-{
+- int i;
+-
+- /* don't disconnect drivers more than once */
+- if (dev->gadget.speed == USB_SPEED_UNKNOWN)
+- driver = NULL;
+- dev->gadget.speed = USB_SPEED_UNKNOWN;
+-
+- /* prevent new request submissions, kill any outstanding requests */
+- for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) {
+- struct pxa2xx_ep *ep = &dev->ep[i];
+-
+- ep->stopped = 1;
+- nuke(ep, -ESHUTDOWN);
+- }
+- del_timer_sync(&dev->timer);
+-
+- /* report disconnect; the driver is already quiesced */
+- if (driver)
+- driver->disconnect(&dev->gadget);
+-
+- /* re-init driver-visible data structures */
+- udc_reinit(dev);
+-}
+-
+-int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+-{
+- struct pxa2xx_udc *dev = the_controller;
+-
+- if (!dev)
+- return -ENODEV;
+- if (!driver || driver != dev->driver || !driver->unbind)
+- return -EINVAL;
+-
+- local_irq_disable();
+- dev->pullup = 0;
+- pullup(dev);
+- stop_activity(dev, driver);
+- local_irq_enable();
+-
+- driver->unbind(&dev->gadget);
+- dev->gadget.dev.driver = NULL;
+- dev->driver = NULL;
+-
+- device_del (&dev->gadget.dev);
+-
+- DMSG("unregistered gadget driver '%s'\n", driver->driver.name);
+- dump_state(dev);
+- return 0;
+-}
+-EXPORT_SYMBOL(usb_gadget_unregister_driver);
+-
+-
+-/*-------------------------------------------------------------------------*/
+-
+-#ifdef CONFIG_ARCH_LUBBOCK
+-
+-/* Lubbock has separate connect and disconnect irqs. More typical designs
+- * use one GPIO as the VBUS IRQ, and another to control the D+ pullup.
+- */
+-
+-static irqreturn_t
+-lubbock_vbus_irq(int irq, void *_dev)
+-{
+- struct pxa2xx_udc *dev = _dev;
+- int vbus;
+-
+- dev->stats.irqs++;
+- switch (irq) {
+- case LUBBOCK_USB_IRQ:
+- vbus = 1;
+- disable_irq(LUBBOCK_USB_IRQ);
+- enable_irq(LUBBOCK_USB_DISC_IRQ);
+- break;
+- case LUBBOCK_USB_DISC_IRQ:
+- vbus = 0;
+- disable_irq(LUBBOCK_USB_DISC_IRQ);
+- enable_irq(LUBBOCK_USB_IRQ);
+- break;
+- default:
+- return IRQ_NONE;
+- }
+-
+- pxa2xx_udc_vbus_session(&dev->gadget, vbus);
+- return IRQ_HANDLED;
+-}
+-
+-#endif
+-
+-static irqreturn_t udc_vbus_irq(int irq, void *_dev)
+-{
+- struct pxa2xx_udc *dev = _dev;
+- int vbus = gpio_get_value(dev->mach->gpio_vbus);
+-
+- if (dev->mach->gpio_vbus_inverted)
+- vbus = !vbus;
+-
+- pxa2xx_udc_vbus_session(&dev->gadget, vbus);
+- return IRQ_HANDLED;
+-}
+-
+-
+-/*-------------------------------------------------------------------------*/
+-
+-static inline void clear_ep_state (struct pxa2xx_udc *dev)
+-{
+- unsigned i;
+-
+- /* hardware SET_{CONFIGURATION,INTERFACE} automagic resets endpoint
+- * fifos, and pending transactions mustn't be continued in any case.
+- */
+- for (i = 1; i < PXA_UDC_NUM_ENDPOINTS; i++)
+- nuke(&dev->ep[i], -ECONNABORTED);
+-}
+-
+-static void udc_watchdog(unsigned long _dev)
+-{
+- struct pxa2xx_udc *dev = (void *)_dev;
+-
+- local_irq_disable();
+- if (dev->ep0state == EP0_STALL
+- && (UDCCS0 & UDCCS0_FST) == 0
+- && (UDCCS0 & UDCCS0_SST) == 0) {
+- UDCCS0 = UDCCS0_FST|UDCCS0_FTF;
+- DBG(DBG_VERBOSE, "ep0 re-stall\n");
+- start_watchdog(dev);
+- }
+- local_irq_enable();
+-}
+-
+-static void handle_ep0 (struct pxa2xx_udc *dev)
+-{
+- u32 udccs0 = UDCCS0;
+- struct pxa2xx_ep *ep = &dev->ep [0];
+- struct pxa2xx_request *req;
+- union {
+- struct usb_ctrlrequest r;
+- u8 raw [8];
+- u32 word [2];
+- } u;
+-
+- if (list_empty(&ep->queue))
+- req = NULL;
+- else
+- req = list_entry(ep->queue.next, struct pxa2xx_request, queue);
+-
+- /* clear stall status */
+- if (udccs0 & UDCCS0_SST) {
+- nuke(ep, -EPIPE);
+- UDCCS0 = UDCCS0_SST;
+- del_timer(&dev->timer);
+- ep0_idle(dev);
+- }
+-
+- /* previous request unfinished? non-error iff back-to-back ... */
+- if ((udccs0 & UDCCS0_SA) != 0 && dev->ep0state != EP0_IDLE) {
+- nuke(ep, 0);
+- del_timer(&dev->timer);
+- ep0_idle(dev);
+- }
+-
+- switch (dev->ep0state) {
+- case EP0_IDLE:
+- /* late-breaking status? */
+- udccs0 = UDCCS0;
+-
+- /* start control request? */
+- if (likely((udccs0 & (UDCCS0_OPR|UDCCS0_SA|UDCCS0_RNE))
+- == (UDCCS0_OPR|UDCCS0_SA|UDCCS0_RNE))) {
+- int i;
+-
+- nuke (ep, -EPROTO);
+-
+- /* read SETUP packet */
+- for (i = 0; i < 8; i++) {
+- if (unlikely(!(UDCCS0 & UDCCS0_RNE))) {
+-bad_setup:
+- DMSG("SETUP %d!\n", i);
+- goto stall;
+- }
+- u.raw [i] = (u8) UDDR0;
+- }
+- if (unlikely((UDCCS0 & UDCCS0_RNE) != 0))
+- goto bad_setup;
+-
+-got_setup:
+- DBG(DBG_VERBOSE, "SETUP %02x.%02x v%04x i%04x l%04x\n",
+- u.r.bRequestType, u.r.bRequest,
+- le16_to_cpu(u.r.wValue),
+- le16_to_cpu(u.r.wIndex),
+- le16_to_cpu(u.r.wLength));
+-
+- /* cope with automagic for some standard requests. */
+- dev->req_std = (u.r.bRequestType & USB_TYPE_MASK)
+- == USB_TYPE_STANDARD;
+- dev->req_config = 0;
+- dev->req_pending = 1;
+- switch (u.r.bRequest) {
+- /* hardware restricts gadget drivers here! */
+- case USB_REQ_SET_CONFIGURATION:
+- if (u.r.bRequestType == USB_RECIP_DEVICE) {
+- /* reflect hardware's automagic
+- * up to the gadget driver.
+- */
+-config_change:
+- dev->req_config = 1;
+- clear_ep_state(dev);
+- /* if !has_cfr, there's no synch
+- * else use AREN (later) not SA|OPR
+- * USIR0_IR0 acts edge sensitive
+- */
+- }
+- break;
+- /* ... and here, even more ... */
+- case USB_REQ_SET_INTERFACE:
+- if (u.r.bRequestType == USB_RECIP_INTERFACE) {
+- /* udc hardware is broken by design:
+- * - altsetting may only be zero;
+- * - hw resets all interfaces' eps;
+- * - ep reset doesn't include halt(?).
+- */
+- DMSG("broken set_interface (%d/%d)\n",
+- le16_to_cpu(u.r.wIndex),
+- le16_to_cpu(u.r.wValue));
+- goto config_change;
+- }
+- break;
+- /* hardware was supposed to hide this */
+- case USB_REQ_SET_ADDRESS:
+- if (u.r.bRequestType == USB_RECIP_DEVICE) {
+- ep0start(dev, 0, "address");
+- return;
+- }
+- break;
+- }
+-
+- if (u.r.bRequestType & USB_DIR_IN)
+- dev->ep0state = EP0_IN_DATA_PHASE;
+- else
+- dev->ep0state = EP0_OUT_DATA_PHASE;
+-
+- i = dev->driver->setup(&dev->gadget, &u.r);
+- if (i < 0) {
+- /* hardware automagic preventing STALL... */
+- if (dev->req_config) {
+- /* hardware sometimes neglects to tell
+- * tell us about config change events,
+- * so later ones may fail...
+- */
+- WARN("config change %02x fail %d?\n",
+- u.r.bRequest, i);
+- return;
+- /* TODO experiment: if has_cfr,
+- * hardware didn't ACK; maybe we
+- * could actually STALL!
+- */
+- }
+- DBG(DBG_VERBOSE, "protocol STALL, "
+- "%02x err %d\n", UDCCS0, i);
+-stall:
+- /* the watchdog timer helps deal with cases
+- * where udc seems to clear FST wrongly, and
+- * then NAKs instead of STALLing.
+- */
+- ep0start(dev, UDCCS0_FST|UDCCS0_FTF, "stall");
+- start_watchdog(dev);
+- dev->ep0state = EP0_STALL;
+-
+- /* deferred i/o == no response yet */
+- } else if (dev->req_pending) {
+- if (likely(dev->ep0state == EP0_IN_DATA_PHASE
+- || dev->req_std || u.r.wLength))
+- ep0start(dev, 0, "defer");
+- else
+- ep0start(dev, UDCCS0_IPR, "defer/IPR");
+- }
+-
+- /* expect at least one data or status stage irq */
+- return;
+-
+- } else if (likely((udccs0 & (UDCCS0_OPR|UDCCS0_SA))
+- == (UDCCS0_OPR|UDCCS0_SA))) {
+- unsigned i;
+-
+- /* pxa210/250 erratum 131 for B0/B1 says RNE lies.
+- * still observed on a pxa255 a0.
+- */
+- DBG(DBG_VERBOSE, "e131\n");
+- nuke(ep, -EPROTO);
+-
+- /* read SETUP data, but don't trust it too much */
+- for (i = 0; i < 8; i++)
+- u.raw [i] = (u8) UDDR0;
+- if ((u.r.bRequestType & USB_RECIP_MASK)
+- > USB_RECIP_OTHER)
+- goto stall;
+- if (u.word [0] == 0 && u.word [1] == 0)
+- goto stall;
+- goto got_setup;
+- } else {
+- /* some random early IRQ:
+- * - we acked FST
+- * - IPR cleared
+- * - OPR got set, without SA (likely status stage)
+- */
+- UDCCS0 = udccs0 & (UDCCS0_SA|UDCCS0_OPR);
+- }
+- break;
+- case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR etc */
+- if (udccs0 & UDCCS0_OPR) {
+- UDCCS0 = UDCCS0_OPR|UDCCS0_FTF;
+- DBG(DBG_VERBOSE, "ep0in premature status\n");
+- if (req)
+- done(ep, req, 0);
+- ep0_idle(dev);
+- } else /* irq was IPR clearing */ {
+- if (req) {
+- /* this IN packet might finish the request */
+- (void) write_ep0_fifo(ep, req);
+- } /* else IN token before response was written */
+- }
+- break;
+- case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR etc */
+- if (udccs0 & UDCCS0_OPR) {
+- if (req) {
+- /* this OUT packet might finish the request */
+- if (read_ep0_fifo(ep, req))
+- done(ep, req, 0);
+- /* else more OUT packets expected */
+- } /* else OUT token before read was issued */
+- } else /* irq was IPR clearing */ {
+- DBG(DBG_VERBOSE, "ep0out premature status\n");
+- if (req)
+- done(ep, req, 0);
+- ep0_idle(dev);
+- }
+- break;
+- case EP0_END_XFER:
+- if (req)
+- done(ep, req, 0);
+- /* ack control-IN status (maybe in-zlp was skipped)
+- * also appears after some config change events.
+- */
+- if (udccs0 & UDCCS0_OPR)
+- UDCCS0 = UDCCS0_OPR;
+- ep0_idle(dev);
+- break;
+- case EP0_STALL:
+- UDCCS0 = UDCCS0_FST;
+- break;
+- }
+- USIR0 = USIR0_IR0;
+-}
+-
+-static void handle_ep(struct pxa2xx_ep *ep)
+-{
+- struct pxa2xx_request *req;
+- int is_in = ep->bEndpointAddress & USB_DIR_IN;
+- int completed;
+- u32 udccs, tmp;
+-
+- do {
+- completed = 0;
+- if (likely (!list_empty(&ep->queue)))
+- req = list_entry(ep->queue.next,
+- struct pxa2xx_request, queue);
+- else
+- req = NULL;
+-
+- // TODO check FST handling
+-
+- udccs = *ep->reg_udccs;
+- if (unlikely(is_in)) { /* irq from TPC, SST, or (ISO) TUR */
+- tmp = UDCCS_BI_TUR;
+- if (likely(ep->bmAttributes == USB_ENDPOINT_XFER_BULK))
+- tmp |= UDCCS_BI_SST;
+- tmp &= udccs;
+- if (likely (tmp))
+- *ep->reg_udccs = tmp;
+- if (req && likely ((udccs & UDCCS_BI_TFS) != 0))
+- completed = write_fifo(ep, req);
+-
+- } else { /* irq from RPC (or for ISO, ROF) */
+- if (likely(ep->bmAttributes == USB_ENDPOINT_XFER_BULK))
+- tmp = UDCCS_BO_SST | UDCCS_BO_DME;
+- else
+- tmp = UDCCS_IO_ROF | UDCCS_IO_DME;
+- tmp &= udccs;
+- if (likely(tmp))
+- *ep->reg_udccs = tmp;
+-
+- /* fifos can hold packets, ready for reading... */
+- if (likely(req)) {
+- completed = read_fifo(ep, req);
+- } else
+- pio_irq_disable (ep->bEndpointAddress);
+- }
+- ep->pio_irqs++;
+- } while (completed);
+-}
+-
+-/*
+- * pxa2xx_udc_irq - interrupt handler
+- *
+- * avoid delays in ep0 processing. the control handshaking isn't always
+- * under software control (pxa250c0 and the pxa255 are better), and delays
+- * could cause usb protocol errors.
+- */
+-static irqreturn_t
+-pxa2xx_udc_irq(int irq, void *_dev)
+-{
+- struct pxa2xx_udc *dev = _dev;
+- int handled;
+-
+- dev->stats.irqs++;
+- do {
+- u32 udccr = UDCCR;
+-
+- handled = 0;
+-
+- /* SUSpend Interrupt Request */
+- if (unlikely(udccr & UDCCR_SUSIR)) {
+- udc_ack_int_UDCCR(UDCCR_SUSIR);
+- handled = 1;
+- DBG(DBG_VERBOSE, "USB suspend%s\n", is_vbus_present()
+- ? "" : "+disconnect");
+-
+- if (!is_vbus_present())
+- stop_activity(dev, dev->driver);
+- else if (dev->gadget.speed != USB_SPEED_UNKNOWN
+- && dev->driver
+- && dev->driver->suspend)
+- dev->driver->suspend(&dev->gadget);
+- ep0_idle (dev);
+- }
+-
+- /* RESume Interrupt Request */
+- if (unlikely(udccr & UDCCR_RESIR)) {
+- udc_ack_int_UDCCR(UDCCR_RESIR);
+- handled = 1;
+- DBG(DBG_VERBOSE, "USB resume\n");
+-
+- if (dev->gadget.speed != USB_SPEED_UNKNOWN
+- && dev->driver
+- && dev->driver->resume
+- && is_vbus_present())
+- dev->driver->resume(&dev->gadget);
+- }
+-
+- /* ReSeT Interrupt Request - USB reset */
+- if (unlikely(udccr & UDCCR_RSTIR)) {
+- udc_ack_int_UDCCR(UDCCR_RSTIR);
+- handled = 1;
+-
+- if ((UDCCR & UDCCR_UDA) == 0) {
+- DBG(DBG_VERBOSE, "USB reset start\n");
+-
+- /* reset driver and endpoints,
+- * in case that's not yet done
+- */
+- stop_activity (dev, dev->driver);
+-
+- } else {
+- DBG(DBG_VERBOSE, "USB reset end\n");
+- dev->gadget.speed = USB_SPEED_FULL;
+- memset(&dev->stats, 0, sizeof dev->stats);
+- /* driver and endpoints are still reset */
+- }
+-
+- } else {
+- u32 usir0 = USIR0 & ~UICR0;
+- u32 usir1 = USIR1 & ~UICR1;
+- int i;
+-
+- if (unlikely (!usir0 && !usir1))
+- continue;
+-
+- DBG(DBG_VERY_NOISY, "irq %02x.%02x\n", usir1, usir0);
+-
+- /* control traffic */
+- if (usir0 & USIR0_IR0) {
+- dev->ep[0].pio_irqs++;
+- handle_ep0(dev);
+- handled = 1;
+- }
+-
+- /* endpoint data transfers */
+- for (i = 0; i < 8; i++) {
+- u32 tmp = 1 << i;
+-
+- if (i && (usir0 & tmp)) {
+- handle_ep(&dev->ep[i]);
+- USIR0 |= tmp;
+- handled = 1;
+- }
+- if (usir1 & tmp) {
+- handle_ep(&dev->ep[i+8]);
+- USIR1 |= tmp;
+- handled = 1;
+- }
+- }
+- }
+-
+- /* we could also ask for 1 msec SOF (SIR) interrupts */
+-
+- } while (handled);
+- return IRQ_HANDLED;
+-}
+-
+-/*-------------------------------------------------------------------------*/
+-
+-static void nop_release (struct device *dev)
+-{
+- DMSG("%s %s\n", __func__, dev->bus_id);
+-}
+-
+-/* this uses load-time allocation and initialization (instead of
+- * doing it at run-time) to save code, eliminate fault paths, and
+- * be more obviously correct.
+- */
+-static struct pxa2xx_udc memory = {
+- .gadget = {
+- .ops = &pxa2xx_udc_ops,
+- .ep0 = &memory.ep[0].ep,
+- .name = driver_name,
+- .dev = {
+- .bus_id = "gadget",
+- .release = nop_release,
+- },
+- },
+-
+- /* control endpoint */
+- .ep[0] = {
+- .ep = {
+- .name = ep0name,
+- .ops = &pxa2xx_ep_ops,
+- .maxpacket = EP0_FIFO_SIZE,
+- },
+- .dev = &memory,
+- .reg_udccs = &UDCCS0,
+- .reg_uddr = &UDDR0,
+- },
+-
+- /* first group of endpoints */
+- .ep[1] = {
+- .ep = {
+- .name = "ep1in-bulk",
+- .ops = &pxa2xx_ep_ops,
+- .maxpacket = BULK_FIFO_SIZE,
+- },
+- .dev = &memory,
+- .fifo_size = BULK_FIFO_SIZE,
+- .bEndpointAddress = USB_DIR_IN | 1,
+- .bmAttributes = USB_ENDPOINT_XFER_BULK,
+- .reg_udccs = &UDCCS1,
+- .reg_uddr = &UDDR1,
+- },
+- .ep[2] = {
+- .ep = {
+- .name = "ep2out-bulk",
+- .ops = &pxa2xx_ep_ops,
+- .maxpacket = BULK_FIFO_SIZE,
+- },
+- .dev = &memory,
+- .fifo_size = BULK_FIFO_SIZE,
+- .bEndpointAddress = 2,
+- .bmAttributes = USB_ENDPOINT_XFER_BULK,
+- .reg_udccs = &UDCCS2,
+- .reg_ubcr = &UBCR2,
+- .reg_uddr = &UDDR2,
+- },
+-#ifndef CONFIG_USB_PXA2XX_SMALL
+- .ep[3] = {
+- .ep = {
+- .name = "ep3in-iso",
+- .ops = &pxa2xx_ep_ops,
+- .maxpacket = ISO_FIFO_SIZE,
+- },
+- .dev = &memory,
+- .fifo_size = ISO_FIFO_SIZE,
+- .bEndpointAddress = USB_DIR_IN | 3,
+- .bmAttributes = USB_ENDPOINT_XFER_ISOC,
+- .reg_udccs = &UDCCS3,
+- .reg_uddr = &UDDR3,
+- },
+- .ep[4] = {
+- .ep = {
+- .name = "ep4out-iso",
+- .ops = &pxa2xx_ep_ops,
+- .maxpacket = ISO_FIFO_SIZE,
+- },
+- .dev = &memory,
+- .fifo_size = ISO_FIFO_SIZE,
+- .bEndpointAddress = 4,
+- .bmAttributes = USB_ENDPOINT_XFER_ISOC,
+- .reg_udccs = &UDCCS4,
+- .reg_ubcr = &UBCR4,
+- .reg_uddr = &UDDR4,
+- },
+- .ep[5] = {
+- .ep = {
+- .name = "ep5in-int",
+- .ops = &pxa2xx_ep_ops,
+- .maxpacket = INT_FIFO_SIZE,
+- },
+- .dev = &memory,
+- .fifo_size = INT_FIFO_SIZE,
+- .bEndpointAddress = USB_DIR_IN | 5,
+- .bmAttributes = USB_ENDPOINT_XFER_INT,
+- .reg_udccs = &UDCCS5,
+- .reg_uddr = &UDDR5,
+- },
+-
+- /* second group of endpoints */
+- .ep[6] = {
+- .ep = {
+- .name = "ep6in-bulk",
+- .ops = &pxa2xx_ep_ops,
+- .maxpacket = BULK_FIFO_SIZE,
+- },
+- .dev = &memory,
+- .fifo_size = BULK_FIFO_SIZE,
+- .bEndpointAddress = USB_DIR_IN | 6,
+- .bmAttributes = USB_ENDPOINT_XFER_BULK,
+- .reg_udccs = &UDCCS6,
+- .reg_uddr = &UDDR6,
+- },
+- .ep[7] = {
+- .ep = {
+- .name = "ep7out-bulk",
+- .ops = &pxa2xx_ep_ops,
+- .maxpacket = BULK_FIFO_SIZE,
+- },
+- .dev = &memory,
+- .fifo_size = BULK_FIFO_SIZE,
+- .bEndpointAddress = 7,
+- .bmAttributes = USB_ENDPOINT_XFER_BULK,
+- .reg_udccs = &UDCCS7,
+- .reg_ubcr = &UBCR7,
+- .reg_uddr = &UDDR7,
+- },
+- .ep[8] = {
+- .ep = {
+- .name = "ep8in-iso",
+- .ops = &pxa2xx_ep_ops,
+- .maxpacket = ISO_FIFO_SIZE,
+- },
+- .dev = &memory,
+- .fifo_size = ISO_FIFO_SIZE,
+- .bEndpointAddress = USB_DIR_IN | 8,
+- .bmAttributes = USB_ENDPOINT_XFER_ISOC,
+- .reg_udccs = &UDCCS8,
+- .reg_uddr = &UDDR8,
+- },
+- .ep[9] = {
+- .ep = {
+- .name = "ep9out-iso",
+- .ops = &pxa2xx_ep_ops,
+- .maxpacket = ISO_FIFO_SIZE,
+- },
+- .dev = &memory,
+- .fifo_size = ISO_FIFO_SIZE,
+- .bEndpointAddress = 9,
+- .bmAttributes = USB_ENDPOINT_XFER_ISOC,
+- .reg_udccs = &UDCCS9,
+- .reg_ubcr = &UBCR9,
+- .reg_uddr = &UDDR9,
+- },
+- .ep[10] = {
+- .ep = {
+- .name = "ep10in-int",
+- .ops = &pxa2xx_ep_ops,
+- .maxpacket = INT_FIFO_SIZE,
+- },
+- .dev = &memory,
+- .fifo_size = INT_FIFO_SIZE,
+- .bEndpointAddress = USB_DIR_IN | 10,
+- .bmAttributes = USB_ENDPOINT_XFER_INT,
+- .reg_udccs = &UDCCS10,
+- .reg_uddr = &UDDR10,
+- },
+-
+- /* third group of endpoints */
+- .ep[11] = {
+- .ep = {
+- .name = "ep11in-bulk",
+- .ops = &pxa2xx_ep_ops,
+- .maxpacket = BULK_FIFO_SIZE,
+- },
+- .dev = &memory,
+- .fifo_size = BULK_FIFO_SIZE,
+- .bEndpointAddress = USB_DIR_IN | 11,
+- .bmAttributes = USB_ENDPOINT_XFER_BULK,
+- .reg_udccs = &UDCCS11,
+- .reg_uddr = &UDDR11,
+- },
+- .ep[12] = {
+- .ep = {
+- .name = "ep12out-bulk",
+- .ops = &pxa2xx_ep_ops,
+- .maxpacket = BULK_FIFO_SIZE,
+- },
+- .dev = &memory,
+- .fifo_size = BULK_FIFO_SIZE,
+- .bEndpointAddress = 12,
+- .bmAttributes = USB_ENDPOINT_XFER_BULK,
+- .reg_udccs = &UDCCS12,
+- .reg_ubcr = &UBCR12,
+- .reg_uddr = &UDDR12,
+- },
+- .ep[13] = {
+- .ep = {
+- .name = "ep13in-iso",
+- .ops = &pxa2xx_ep_ops,
+- .maxpacket = ISO_FIFO_SIZE,
+- },
+- .dev = &memory,
+- .fifo_size = ISO_FIFO_SIZE,
+- .bEndpointAddress = USB_DIR_IN | 13,
+- .bmAttributes = USB_ENDPOINT_XFER_ISOC,
+- .reg_udccs = &UDCCS13,
+- .reg_uddr = &UDDR13,
+- },
+- .ep[14] = {
+- .ep = {
+- .name = "ep14out-iso",
+- .ops = &pxa2xx_ep_ops,
+- .maxpacket = ISO_FIFO_SIZE,
+- },
+- .dev = &memory,
+- .fifo_size = ISO_FIFO_SIZE,
+- .bEndpointAddress = 14,
+- .bmAttributes = USB_ENDPOINT_XFER_ISOC,
+- .reg_udccs = &UDCCS14,
+- .reg_ubcr = &UBCR14,
+- .reg_uddr = &UDDR14,
+- },
+- .ep[15] = {
+- .ep = {
+- .name = "ep15in-int",
+- .ops = &pxa2xx_ep_ops,
+- .maxpacket = INT_FIFO_SIZE,
+- },
+- .dev = &memory,
+- .fifo_size = INT_FIFO_SIZE,
+- .bEndpointAddress = USB_DIR_IN | 15,
+- .bmAttributes = USB_ENDPOINT_XFER_INT,
+- .reg_udccs = &UDCCS15,
+- .reg_uddr = &UDDR15,
+- },
+-#endif /* !CONFIG_USB_PXA2XX_SMALL */
+-};
+-
+-#define CP15R0_VENDOR_MASK 0xffffe000
+-
+-#if defined(CONFIG_ARCH_PXA)
+-#define CP15R0_XSCALE_VALUE 0x69052000 /* intel/arm/xscale */
+-
+-#elif defined(CONFIG_ARCH_IXP4XX)
+-#define CP15R0_XSCALE_VALUE 0x69054000 /* intel/arm/ixp4xx */
+-
+-#endif
+-
+-#define CP15R0_PROD_MASK 0x000003f0
+-#define PXA25x 0x00000100 /* and PXA26x */
+-#define PXA210 0x00000120
+-
+-#define CP15R0_REV_MASK 0x0000000f
+-
+-#define CP15R0_PRODREV_MASK (CP15R0_PROD_MASK | CP15R0_REV_MASK)
+-
+-#define PXA255_A0 0x00000106 /* or PXA260_B1 */
+-#define PXA250_C0 0x00000105 /* or PXA26x_B0 */
+-#define PXA250_B2 0x00000104
+-#define PXA250_B1 0x00000103 /* or PXA260_A0 */
+-#define PXA250_B0 0x00000102
+-#define PXA250_A1 0x00000101
+-#define PXA250_A0 0x00000100
+-
+-#define PXA210_C0 0x00000125
+-#define PXA210_B2 0x00000124
+-#define PXA210_B1 0x00000123
+-#define PXA210_B0 0x00000122
+-#define IXP425_A0 0x000001c1
+-#define IXP425_B0 0x000001f1
+-#define IXP465_AD 0x00000200
+-
+-/*
+- * probe - binds to the platform device
+- */
+-static int __init pxa2xx_udc_probe(struct platform_device *pdev)
+-{
+- struct pxa2xx_udc *dev = &memory;
+- int retval, vbus_irq, irq;
+- u32 chiprev;
+-
+- /* insist on Intel/ARM/XScale */
+- asm("mrc%? p15, 0, %0, c0, c0" : "=r" (chiprev));
+- if ((chiprev & CP15R0_VENDOR_MASK) != CP15R0_XSCALE_VALUE) {
+- pr_err("%s: not XScale!\n", driver_name);
+- return -ENODEV;
+- }
+-
+- /* trigger chiprev-specific logic */
+- switch (chiprev & CP15R0_PRODREV_MASK) {
+-#if defined(CONFIG_ARCH_PXA)
+- case PXA255_A0:
+- dev->has_cfr = 1;
+- break;
+- case PXA250_A0:
+- case PXA250_A1:
+- /* A0/A1 "not released"; ep 13, 15 unusable */
+- /* fall through */
+- case PXA250_B2: case PXA210_B2:
+- case PXA250_B1: case PXA210_B1:
+- case PXA250_B0: case PXA210_B0:
+- /* OUT-DMA is broken ... */
+- /* fall through */
+- case PXA250_C0: case PXA210_C0:
+- break;
+-#elif defined(CONFIG_ARCH_IXP4XX)
+- case IXP425_A0:
+- case IXP425_B0:
+- case IXP465_AD:
+- dev->has_cfr = 1;
+- break;
+-#endif
+- default:
+- pr_err("%s: unrecognized processor: %08x\n",
+- driver_name, chiprev);
+- /* iop3xx, ixp4xx, ... */
+- return -ENODEV;
+- }
+-
+- irq = platform_get_irq(pdev, 0);
+- if (irq < 0)
+- return -ENODEV;
+-
+- dev->clk = clk_get(&pdev->dev, "UDCCLK");
+- if (IS_ERR(dev->clk)) {
+- retval = PTR_ERR(dev->clk);
+- goto err_clk;
+- }
+-
+- pr_debug("%s: IRQ %d%s%s\n", driver_name, irq,
+- dev->has_cfr ? "" : " (!cfr)",
+- SIZE_STR "(pio)"
+- );
+-
+- /* other non-static parts of init */
+- dev->dev = &pdev->dev;
+- dev->mach = pdev->dev.platform_data;
+-
+- if (dev->mach->gpio_vbus) {
+- if ((retval = gpio_request(dev->mach->gpio_vbus,
+- "pxa2xx_udc GPIO VBUS"))) {
+- dev_dbg(&pdev->dev,
+- "can't get vbus gpio %d, err: %d\n",
+- dev->mach->gpio_vbus, retval);
+- goto err_gpio_vbus;
+- }
+- gpio_direction_input(dev->mach->gpio_vbus);
+- vbus_irq = gpio_to_irq(dev->mach->gpio_vbus);
+- } else
+- vbus_irq = 0;
+-
+- if (dev->mach->gpio_pullup) {
+- if ((retval = gpio_request(dev->mach->gpio_pullup,
+- "pca2xx_udc GPIO PULLUP"))) {
+- dev_dbg(&pdev->dev,
+- "can't get pullup gpio %d, err: %d\n",
+- dev->mach->gpio_pullup, retval);
+- goto err_gpio_pullup;
+- }
+- gpio_direction_output(dev->mach->gpio_pullup, 0);
+- }
+-
+- init_timer(&dev->timer);
+- dev->timer.function = udc_watchdog;
+- dev->timer.data = (unsigned long) dev;
+-
+- device_initialize(&dev->gadget.dev);
+- dev->gadget.dev.parent = &pdev->dev;
+- dev->gadget.dev.dma_mask = pdev->dev.dma_mask;
+-
+- the_controller = dev;
+- platform_set_drvdata(pdev, dev);
+-
+- udc_disable(dev);
+- udc_reinit(dev);
+-
+- dev->vbus = is_vbus_present();
+-
+- /* irq setup after old hardware state is cleaned up */
+- retval = request_irq(irq, pxa2xx_udc_irq,
+- IRQF_DISABLED, driver_name, dev);
+- if (retval != 0) {
+- pr_err("%s: can't get irq %d, err %d\n",
+- driver_name, irq, retval);
+- goto err_irq1;
+- }
+- dev->got_irq = 1;
+-
+-#ifdef CONFIG_ARCH_LUBBOCK
+- if (machine_is_lubbock()) {
+- retval = request_irq(LUBBOCK_USB_DISC_IRQ,
+- lubbock_vbus_irq,
+- IRQF_DISABLED | IRQF_SAMPLE_RANDOM,
+- driver_name, dev);
+- if (retval != 0) {
+- pr_err("%s: can't get irq %i, err %d\n",
+- driver_name, LUBBOCK_USB_DISC_IRQ, retval);
+-lubbock_fail0:
+- goto err_irq_lub;
+- }
+- retval = request_irq(LUBBOCK_USB_IRQ,
+- lubbock_vbus_irq,
+- IRQF_DISABLED | IRQF_SAMPLE_RANDOM,
+- driver_name, dev);
+- if (retval != 0) {
+- pr_err("%s: can't get irq %i, err %d\n",
+- driver_name, LUBBOCK_USB_IRQ, retval);
+- free_irq(LUBBOCK_USB_DISC_IRQ, dev);
+- goto lubbock_fail0;
+- }
+- } else
+-#endif
+- if (vbus_irq) {
+- retval = request_irq(vbus_irq, udc_vbus_irq,
+- IRQF_DISABLED | IRQF_SAMPLE_RANDOM |
+- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+- driver_name, dev);
+- if (retval != 0) {
+- pr_err("%s: can't get irq %i, err %d\n",
+- driver_name, vbus_irq, retval);
+- goto err_vbus_irq;
+- }
+- }
+- create_debug_files(dev);
+-
+- return 0;
+-
+- err_vbus_irq:
+-#ifdef CONFIG_ARCH_LUBBOCK
+- free_irq(LUBBOCK_USB_DISC_IRQ, dev);
+- err_irq_lub:
+-#endif
+- free_irq(irq, dev);
+- err_irq1:
+- if (dev->mach->gpio_pullup)
+- gpio_free(dev->mach->gpio_pullup);
+- err_gpio_pullup:
+- if (dev->mach->gpio_vbus)
+- gpio_free(dev->mach->gpio_vbus);
+- err_gpio_vbus:
+- clk_put(dev->clk);
+- err_clk:
+- return retval;
+-}
+-
+-static void pxa2xx_udc_shutdown(struct platform_device *_dev)
+-{
+- pullup_off();
+-}
+-
+-static int __exit pxa2xx_udc_remove(struct platform_device *pdev)
+-{
+- struct pxa2xx_udc *dev = platform_get_drvdata(pdev);
+-
+- if (dev->driver)
+- return -EBUSY;
+-
+- dev->pullup = 0;
+- pullup(dev);
+-
+- remove_debug_files(dev);
+-
+- if (dev->got_irq) {
+- free_irq(platform_get_irq(pdev, 0), dev);
+- dev->got_irq = 0;
+- }
+-#ifdef CONFIG_ARCH_LUBBOCK
+- if (machine_is_lubbock()) {
+- free_irq(LUBBOCK_USB_DISC_IRQ, dev);
+- free_irq(LUBBOCK_USB_IRQ, dev);
+- }
+-#endif
+- if (dev->mach->gpio_vbus) {
+- free_irq(gpio_to_irq(dev->mach->gpio_vbus), dev);
+- gpio_free(dev->mach->gpio_vbus);
+- }
+- if (dev->mach->gpio_pullup)
+- gpio_free(dev->mach->gpio_pullup);
+-
+- clk_put(dev->clk);
+-
+- platform_set_drvdata(pdev, NULL);
+- the_controller = NULL;
+- return 0;
+-}
+-
+-/*-------------------------------------------------------------------------*/
+-
+-#ifdef CONFIG_PM
+-
+-/* USB suspend (controlled by the host) and system suspend (controlled
+- * by the PXA) don't necessarily work well together. If USB is active,
+- * the 48 MHz clock is required; so the system can't enter 33 MHz idle
+- * mode, or any deeper PM saving state.
+- *
+- * For now, we punt and forcibly disconnect from the USB host when PXA
+- * enters any suspend state. While we're disconnected, we always disable
+- * the 48MHz USB clock ... allowing PXA sleep and/or 33 MHz idle states.
+- * Boards without software pullup control shouldn't use those states.
+- * VBUS IRQs should probably be ignored so that the PXA device just acts
+- * "dead" to USB hosts until system resume.
+- */
+-static int pxa2xx_udc_suspend(struct platform_device *dev, pm_message_t state)
+-{
+- struct pxa2xx_udc *udc = platform_get_drvdata(dev);
+- unsigned long flags;
+-
+- if (!udc->mach->gpio_pullup && !udc->mach->udc_command)
+- WARN("USB host won't detect disconnect!\n");
+- udc->suspended = 1;
+-
+- local_irq_save(flags);
+- pullup(udc);
+- local_irq_restore(flags);
+-
+- return 0;
+-}
+-
+-static int pxa2xx_udc_resume(struct platform_device *dev)
+-{
+- struct pxa2xx_udc *udc = platform_get_drvdata(dev);
+- unsigned long flags;
+-
+- udc->suspended = 0;
+- local_irq_save(flags);
+- pullup(udc);
+- local_irq_restore(flags);
+-
+- return 0;
+-}
+-
+-#else
+-#define pxa2xx_udc_suspend NULL
+-#define pxa2xx_udc_resume NULL
+-#endif
+-
+-/*-------------------------------------------------------------------------*/
+-
+-static struct platform_driver udc_driver = {
+- .shutdown = pxa2xx_udc_shutdown,
+- .remove = __exit_p(pxa2xx_udc_remove),
+- .suspend = pxa2xx_udc_suspend,
+- .resume = pxa2xx_udc_resume,
+- .driver = {
+- .owner = THIS_MODULE,
+- .name = "pxa2xx-udc",
+- },
+-};
+-
+-static int __init udc_init(void)
+-{
+- pr_info("%s: version %s\n", driver_name, DRIVER_VERSION);
+- return platform_driver_probe(&udc_driver, pxa2xx_udc_probe);
+-}
+-module_init(udc_init);
+-
+-static void __exit udc_exit(void)
+-{
+- platform_driver_unregister(&udc_driver);
+-}
+-module_exit(udc_exit);
+-
+-MODULE_DESCRIPTION(DRIVER_DESC);
+-MODULE_AUTHOR("Frank Becker, Robert Schwebel, David Brownell");
+-MODULE_LICENSE("GPL");
+-MODULE_ALIAS("platform:pxa2xx-udc");
+diff --git a/drivers/usb/gadget/pxa2xx_udc.h b/drivers/usb/gadget/pxa2xx_udc.h
+deleted file mode 100644
+index e2c19e8..0000000
+--- a/drivers/usb/gadget/pxa2xx_udc.h
++++ /dev/null
+@@ -1,267 +0,0 @@
+-/*
+- * linux/drivers/usb/gadget/pxa2xx_udc.h
+- * Intel PXA2xx on-chip full speed USB device controller
+- *
+- * Copyright (C) 2003 Robert Schwebel <r.schwebel@pengutronix.de>, Pengutronix
+- * Copyright (C) 2003 David Brownell
+- *
+- *
+- * This program is free software; you can redistribute it and/or modify
+- * it under the terms of the GNU General Public License as published by
+- * the Free Software Foundation; either version 2 of the License, or
+- * (at your option) any later version.
+- *
+- * This program is distributed in the hope that it will be useful,
+- * but WITHOUT ANY WARRANTY; without even the implied warranty of
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+- * GNU General Public License for more details.
+- *
+- * You should have received a copy of the GNU General Public License
+- * along with this program; if not, write to the Free Software
+- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+- */
+-
+-#ifndef __LINUX_USB_GADGET_PXA2XX_H
+-#define __LINUX_USB_GADGET_PXA2XX_H
+-
+-#include <linux/types.h>
+-
+-/*-------------------------------------------------------------------------*/
+-
+-/* pxa2xx has this (move to include/asm-arm/arch-pxa/pxa-regs.h) */
+-#define UFNRH_SIR (1 << 7) /* SOF interrupt request */
+-#define UFNRH_SIM (1 << 6) /* SOF interrupt mask */
+-#define UFNRH_IPE14 (1 << 5) /* ISO packet error, ep14 */
+-#define UFNRH_IPE9 (1 << 4) /* ISO packet error, ep9 */
+-#define UFNRH_IPE4 (1 << 3) /* ISO packet error, ep4 */
+-
+-/* pxa255 has this (move to include/asm-arm/arch-pxa/pxa-regs.h) */
+-#define UDCCFR UDC_RES2 /* UDC Control Function Register */
+-#define UDCCFR_AREN (1 << 7) /* ACK response enable (now) */
+-#define UDCCFR_ACM (1 << 2) /* ACK control mode (wait for AREN) */
+-
+-/* latest pxa255 errata define new "must be one" bits in UDCCFR */
+-#define UDCCFR_MB1 (0xff & ~(UDCCFR_AREN|UDCCFR_ACM))
+-
+-/*-------------------------------------------------------------------------*/
+-
+-struct pxa2xx_udc;
+-
+-struct pxa2xx_ep {
+- struct usb_ep ep;
+- struct pxa2xx_udc *dev;
+-
+- const struct usb_endpoint_descriptor *desc;
+- struct list_head queue;
+- unsigned long pio_irqs;
+-
+- unsigned short fifo_size;
+- u8 bEndpointAddress;
+- u8 bmAttributes;
+-
+- unsigned stopped : 1;
+- unsigned dma_fixup : 1;
+-
+- /* UDCCS = UDC Control/Status for this EP
+- * UBCR = UDC Byte Count Remaining (contents of OUT fifo)
+- * UDDR = UDC Endpoint Data Register (the fifo)
+- * DRCM = DMA Request Channel Map
+- */
+- volatile u32 *reg_udccs;
+- volatile u32 *reg_ubcr;
+- volatile u32 *reg_uddr;
+-};
+-
+-struct pxa2xx_request {
+- struct usb_request req;
+- struct list_head queue;
+-};
+-
+-enum ep0_state {
+- EP0_IDLE,
+- EP0_IN_DATA_PHASE,
+- EP0_OUT_DATA_PHASE,
+- EP0_END_XFER,
+- EP0_STALL,
+-};
+-
+-#define EP0_FIFO_SIZE ((unsigned)16)
+-#define BULK_FIFO_SIZE ((unsigned)64)
+-#define ISO_FIFO_SIZE ((unsigned)256)
+-#define INT_FIFO_SIZE ((unsigned)8)
+-
+-struct udc_stats {
+- struct ep0stats {
+- unsigned long ops;
+- unsigned long bytes;
+- } read, write;
+- unsigned long irqs;
+-};
+-
+-#ifdef CONFIG_USB_PXA2XX_SMALL
+-/* when memory's tight, SMALL config saves code+data. */
+-#define PXA_UDC_NUM_ENDPOINTS 3
+-#endif
+-
+-#ifndef PXA_UDC_NUM_ENDPOINTS
+-#define PXA_UDC_NUM_ENDPOINTS 16
+-#endif
+-
+-struct pxa2xx_udc {
+- struct usb_gadget gadget;
+- struct usb_gadget_driver *driver;
+-
+- enum ep0_state ep0state;
+- struct udc_stats stats;
+- unsigned got_irq : 1,
+- vbus : 1,
+- pullup : 1,
+- has_cfr : 1,
+- req_pending : 1,
+- req_std : 1,
+- req_config : 1,
+- suspended : 1,
+- active : 1;
+-
+-#define start_watchdog(dev) mod_timer(&dev->timer, jiffies + (HZ/200))
+- struct timer_list timer;
+-
+- struct device *dev;
+- struct clk *clk;
+- struct pxa2xx_udc_mach_info *mach;
+- u64 dma_mask;
+- struct pxa2xx_ep ep [PXA_UDC_NUM_ENDPOINTS];
+-
+-#ifdef CONFIG_USB_GADGET_DEBUG_FS
+- struct dentry *debugfs_udc;
+-#endif
+-};
+-
+-/*-------------------------------------------------------------------------*/
+-
+-#ifdef CONFIG_ARCH_LUBBOCK
+-#include <asm/arch/lubbock.h>
+-/* lubbock can also report usb connect/disconnect irqs */
+-#endif
+-
+-static struct pxa2xx_udc *the_controller;
+-
+-/*-------------------------------------------------------------------------*/
+-
+-/*
+- * Debugging support vanishes in non-debug builds. DBG_NORMAL should be
+- * mostly silent during normal use/testing, with no timing side-effects.
+- */
+-#define DBG_NORMAL 1 /* error paths, device state transitions */
+-#define DBG_VERBOSE 2 /* add some success path trace info */
+-#define DBG_NOISY 3 /* ... even more: request level */
+-#define DBG_VERY_NOISY 4 /* ... even more: packet level */
+-
+-#define DMSG(stuff...) pr_debug("udc: " stuff)
+-
+-#ifdef DEBUG
+-
+-static int is_vbus_present(void);
+-
+-static const char *state_name[] = {
+- "EP0_IDLE",
+- "EP0_IN_DATA_PHASE", "EP0_OUT_DATA_PHASE",
+- "EP0_END_XFER", "EP0_STALL"
+-};
+-
+-#ifdef VERBOSE_DEBUG
+-# define UDC_DEBUG DBG_VERBOSE
+-#else
+-# define UDC_DEBUG DBG_NORMAL
+-#endif
+-
+-static void __maybe_unused
+-dump_udccr(const char *label)
+-{
+- u32 udccr = UDCCR;
+- DMSG("%s %02X =%s%s%s%s%s%s%s%s\n",
+- label, udccr,
+- (udccr & UDCCR_REM) ? " rem" : "",
+- (udccr & UDCCR_RSTIR) ? " rstir" : "",
+- (udccr & UDCCR_SRM) ? " srm" : "",
+- (udccr & UDCCR_SUSIR) ? " susir" : "",
+- (udccr & UDCCR_RESIR) ? " resir" : "",
+- (udccr & UDCCR_RSM) ? " rsm" : "",
+- (udccr & UDCCR_UDA) ? " uda" : "",
+- (udccr & UDCCR_UDE) ? " ude" : "");
+-}
+-
+-static void __maybe_unused
+-dump_udccs0(const char *label)
+-{
+- u32 udccs0 = UDCCS0;
+-
+- DMSG("%s %s %02X =%s%s%s%s%s%s%s%s\n",
+- label, state_name[the_controller->ep0state], udccs0,
+- (udccs0 & UDCCS0_SA) ? " sa" : "",
+- (udccs0 & UDCCS0_RNE) ? " rne" : "",
+- (udccs0 & UDCCS0_FST) ? " fst" : "",
+- (udccs0 & UDCCS0_SST) ? " sst" : "",
+- (udccs0 & UDCCS0_DRWF) ? " dwrf" : "",
+- (udccs0 & UDCCS0_FTF) ? " ftf" : "",
+- (udccs0 & UDCCS0_IPR) ? " ipr" : "",
+- (udccs0 & UDCCS0_OPR) ? " opr" : "");
+-}
+-
+-static void __maybe_unused
+-dump_state(struct pxa2xx_udc *dev)
+-{
+- u32 tmp;
+- unsigned i;
+-
+- DMSG("%s %s, uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n",
+- is_vbus_present() ? "host " : "disconnected",
+- state_name[dev->ep0state],
+- UICR1, UICR0, USIR1, USIR0, UFNRH, UFNRL);
+- dump_udccr("udccr");
+- if (dev->has_cfr) {
+- tmp = UDCCFR;
+- DMSG("udccfr %02X =%s%s\n", tmp,
+- (tmp & UDCCFR_AREN) ? " aren" : "",
+- (tmp & UDCCFR_ACM) ? " acm" : "");
+- }
+-
+- if (!dev->driver) {
+- DMSG("no gadget driver bound\n");
+- return;
+- } else
+- DMSG("ep0 driver '%s'\n", dev->driver->driver.name);
+-
+- if (!is_vbus_present())
+- return;
+-
+- dump_udccs0 ("udccs0");
+- DMSG("ep0 IN %lu/%lu, OUT %lu/%lu\n",
+- dev->stats.write.bytes, dev->stats.write.ops,
+- dev->stats.read.bytes, dev->stats.read.ops);
+-
+- for (i = 1; i < PXA_UDC_NUM_ENDPOINTS; i++) {
+- if (dev->ep [i].desc == NULL)
+- continue;
+- DMSG ("udccs%d = %02x\n", i, *dev->ep->reg_udccs);
+- }
+-}
+-
+-#else
+-
+-#define dump_udccr(x) do{}while(0)
+-#define dump_udccs0(x) do{}while(0)
+-#define dump_state(x) do{}while(0)
+-
+-#define UDC_DEBUG ((unsigned)0)
+-
+-#endif
+-
+-#define DBG(lvl, stuff...) do{if ((lvl) <= UDC_DEBUG) DMSG(stuff);}while(0)
+-
+-#define ERR(stuff...) pr_err("udc: " stuff)
+-#define WARN(stuff...) pr_warning("udc: " stuff)
+-#define INFO(stuff...) pr_info("udc: " stuff)
+-
+-
+-#endif /* __LINUX_USB_GADGET_PXA2XX_H */
+diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c
+index d0677f5..7228e85 100644
+--- a/drivers/usb/gadget/rndis.c
++++ b/drivers/usb/gadget/rndis.c
+@@ -1,8 +1,6 @@
+ /*
+ * RNDIS MSG parser
+ *
+- * Version: $Id: rndis.c,v 1.19 2004/03/25 21:33:46 robert Exp $
+- *
+ * Authors: Benedikt Spranger, Pengutronix
+ * Robert Schwebel, Pengutronix
+ *
+@@ -30,6 +28,7 @@
+ #include <linux/init.h>
+ #include <linux/list.h>
+ #include <linux/proc_fs.h>
++#include <linux/seq_file.h>
+ #include <linux/netdevice.h>
+
+ #include <asm/io.h>
+@@ -38,9 +37,7 @@
+ #include <asm/unaligned.h>
+
+
+-#undef RNDIS_PM
+-#undef RNDIS_WAKEUP
+-#undef VERBOSE
++#undef VERBOSE_DEBUG
+
+ #include "rndis.h"
+
+@@ -96,9 +93,6 @@ static const u32 oid_supported_list [] =
+ OID_GEN_MAXIMUM_TOTAL_SIZE,
+ OID_GEN_MEDIA_CONNECT_STATUS,
+ OID_GEN_PHYSICAL_MEDIUM,
+-#if 0
+- OID_GEN_RNDIS_CONFIG_PARAMETER,
+-#endif
+
+ /* the statistical stuff */
+ OID_GEN_XMIT_OK,
+@@ -146,7 +140,14 @@ static const u32 oid_supported_list [] =
+ #endif /* RNDIS_OPTIONAL_STATS */
+
+ #ifdef RNDIS_PM
+- /* PM and wakeup are mandatory for USB: */
++ /* PM and wakeup are "mandatory" for USB, but the RNDIS specs
++ * don't say what they mean ... and the NDIS specs are often
++ * confusing and/or ambiguous in this context. (That is, more
++ * so than their specs for the other OIDs.)
++ *
++ * FIXME someone who knows what these should do, please
++ * implement them!
++ */
+
+ /* power management */
+ OID_PNP_CAPABILITIES,
+@@ -173,6 +174,8 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
+ __le32 *outbuf;
+ int i, count;
+ rndis_query_cmplt_type *resp;
++ struct net_device *net;
++ struct net_device_stats *stats;
+
+ if (!r) return -ENOMEM;
+ resp = (rndis_query_cmplt_type *) r->buf;
+@@ -194,6 +197,12 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
+ outbuf = (__le32 *) &resp[1];
+ resp->InformationBufferOffset = __constant_cpu_to_le32 (16);
+
++ net = rndis_per_dev_params[configNr].dev;
++ if (net->get_stats)
++ stats = net->get_stats(net);
++ else
++ stats = NULL;
++
+ switch (OID) {
+
+ /* general oids (table 4-1) */
+@@ -350,11 +359,9 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
+ case OID_GEN_XMIT_OK:
+ if (rndis_debug > 1)
+ DBG("%s: OID_GEN_XMIT_OK\n", __func__);
+- if (rndis_per_dev_params [configNr].stats) {
+- *outbuf = cpu_to_le32 (
+- rndis_per_dev_params [configNr].stats->tx_packets -
+- rndis_per_dev_params [configNr].stats->tx_errors -
+- rndis_per_dev_params [configNr].stats->tx_dropped);
++ if (stats) {
++ *outbuf = cpu_to_le32(stats->tx_packets
++ - stats->tx_errors - stats->tx_dropped);
+ retval = 0;
+ }
+ break;
+@@ -363,11 +370,9 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
+ case OID_GEN_RCV_OK:
+ if (rndis_debug > 1)
+ DBG("%s: OID_GEN_RCV_OK\n", __func__);
+- if (rndis_per_dev_params [configNr].stats) {
+- *outbuf = cpu_to_le32 (
+- rndis_per_dev_params [configNr].stats->rx_packets -
+- rndis_per_dev_params [configNr].stats->rx_errors -
+- rndis_per_dev_params [configNr].stats->rx_dropped);
++ if (stats) {
++ *outbuf = cpu_to_le32(stats->rx_packets
++ - stats->rx_errors - stats->rx_dropped);
+ retval = 0;
+ }
+ break;
+@@ -376,9 +381,8 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
+ case OID_GEN_XMIT_ERROR:
+ if (rndis_debug > 1)
+ DBG("%s: OID_GEN_XMIT_ERROR\n", __func__);
+- if (rndis_per_dev_params [configNr].stats) {
+- *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
+- .stats->tx_errors);
++ if (stats) {
++ *outbuf = cpu_to_le32(stats->tx_errors);
+ retval = 0;
+ }
+ break;
+@@ -387,9 +391,8 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
+ case OID_GEN_RCV_ERROR:
+ if (rndis_debug > 1)
+ DBG("%s: OID_GEN_RCV_ERROR\n", __func__);
+- if (rndis_per_dev_params [configNr].stats) {
+- *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
+- .stats->rx_errors);
++ if (stats) {
++ *outbuf = cpu_to_le32(stats->rx_errors);
+ retval = 0;
+ }
+ break;
+@@ -397,150 +400,12 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
+ /* mandatory */
+ case OID_GEN_RCV_NO_BUFFER:
+ DBG("%s: OID_GEN_RCV_NO_BUFFER\n", __func__);
+- if (rndis_per_dev_params [configNr].stats) {
+- *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
+- .stats->rx_dropped);
+- retval = 0;
+- }
+- break;
+-
+-#ifdef RNDIS_OPTIONAL_STATS
+- case OID_GEN_DIRECTED_BYTES_XMIT:
+- DBG("%s: OID_GEN_DIRECTED_BYTES_XMIT\n", __func__);
+- /*
+- * Aunt Tilly's size of shoes
+- * minus antarctica count of penguins
+- * divided by weight of Alpha Centauri
+- */
+- if (rndis_per_dev_params [configNr].stats) {
+- *outbuf = cpu_to_le32 (
+- (rndis_per_dev_params [configNr]
+- .stats->tx_packets -
+- rndis_per_dev_params [configNr]
+- .stats->tx_errors -
+- rndis_per_dev_params [configNr]
+- .stats->tx_dropped)
+- * 123);
+- retval = 0;
+- }
+- break;
+-
+- case OID_GEN_DIRECTED_FRAMES_XMIT:
+- DBG("%s: OID_GEN_DIRECTED_FRAMES_XMIT\n", __func__);
+- /* dito */
+- if (rndis_per_dev_params [configNr].stats) {
+- *outbuf = cpu_to_le32 (
+- (rndis_per_dev_params [configNr]
+- .stats->tx_packets -
+- rndis_per_dev_params [configNr]
+- .stats->tx_errors -
+- rndis_per_dev_params [configNr]
+- .stats->tx_dropped)
+- / 123);
+- retval = 0;
+- }
+- break;
+-
+- case OID_GEN_MULTICAST_BYTES_XMIT:
+- DBG("%s: OID_GEN_MULTICAST_BYTES_XMIT\n", __func__);
+- if (rndis_per_dev_params [configNr].stats) {
+- *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
+- .stats->multicast*1234);
+- retval = 0;
+- }
+- break;
+-
+- case OID_GEN_MULTICAST_FRAMES_XMIT:
+- DBG("%s: OID_GEN_MULTICAST_FRAMES_XMIT\n", __func__);
+- if (rndis_per_dev_params [configNr].stats) {
+- *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
+- .stats->multicast);
+- retval = 0;
+- }
+- break;
+-
+- case OID_GEN_BROADCAST_BYTES_XMIT:
+- DBG("%s: OID_GEN_BROADCAST_BYTES_XMIT\n", __func__);
+- if (rndis_per_dev_params [configNr].stats) {
+- *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
+- .stats->tx_packets/42*255);
+- retval = 0;
+- }
+- break;
+-
+- case OID_GEN_BROADCAST_FRAMES_XMIT:
+- DBG("%s: OID_GEN_BROADCAST_FRAMES_XMIT\n", __func__);
+- if (rndis_per_dev_params [configNr].stats) {
+- *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
+- .stats->tx_packets/42);
+- retval = 0;
+- }
+- break;
+-
+- case OID_GEN_DIRECTED_BYTES_RCV:
+- DBG("%s: OID_GEN_DIRECTED_BYTES_RCV\n", __func__);
+- *outbuf = __constant_cpu_to_le32 (0);
+- retval = 0;
+- break;
+-
+- case OID_GEN_DIRECTED_FRAMES_RCV:
+- DBG("%s: OID_GEN_DIRECTED_FRAMES_RCV\n", __func__);
+- *outbuf = __constant_cpu_to_le32 (0);
+- retval = 0;
+- break;
+-
+- case OID_GEN_MULTICAST_BYTES_RCV:
+- DBG("%s: OID_GEN_MULTICAST_BYTES_RCV\n", __func__);
+- if (rndis_per_dev_params [configNr].stats) {
+- *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
+- .stats->multicast * 1111);
+- retval = 0;
+- }
+- break;
+-
+- case OID_GEN_MULTICAST_FRAMES_RCV:
+- DBG("%s: OID_GEN_MULTICAST_FRAMES_RCV\n", __func__);
+- if (rndis_per_dev_params [configNr].stats) {
+- *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
+- .stats->multicast);
+- retval = 0;
+- }
+- break;
+-
+- case OID_GEN_BROADCAST_BYTES_RCV:
+- DBG("%s: OID_GEN_BROADCAST_BYTES_RCV\n", __func__);
+- if (rndis_per_dev_params [configNr].stats) {
+- *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
+- .stats->rx_packets/42*255);
+- retval = 0;
+- }
+- break;
+-
+- case OID_GEN_BROADCAST_FRAMES_RCV:
+- DBG("%s: OID_GEN_BROADCAST_FRAMES_RCV\n", __func__);
+- if (rndis_per_dev_params [configNr].stats) {
+- *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
+- .stats->rx_packets/42);
++ if (stats) {
++ *outbuf = cpu_to_le32(stats->rx_dropped);
+ retval = 0;
+ }
+ break;
+
+- case OID_GEN_RCV_CRC_ERROR:
+- DBG("%s: OID_GEN_RCV_CRC_ERROR\n", __func__);
+- if (rndis_per_dev_params [configNr].stats) {
+- *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
+- .stats->rx_crc_errors);
+- retval = 0;
+- }
+- break;
+-
+- case OID_GEN_TRANSMIT_QUEUE_LENGTH:
+- DBG("%s: OID_GEN_TRANSMIT_QUEUE_LENGTH\n", __func__);
+- *outbuf = __constant_cpu_to_le32 (0);
+- retval = 0;
+- break;
+-#endif /* RNDIS_OPTIONAL_STATS */
+-
+ /* ieee802.3 OIDs (table 4-3) */
+
+ /* mandatory */
+@@ -592,9 +457,8 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
+ /* mandatory */
+ case OID_802_3_RCV_ERROR_ALIGNMENT:
+ DBG("%s: OID_802_3_RCV_ERROR_ALIGNMENT\n", __func__);
+- if (rndis_per_dev_params [configNr].stats) {
+- *outbuf = cpu_to_le32 (rndis_per_dev_params [configNr]
+- .stats->rx_frame_errors);
++ if (stats) {
++ *outbuf = cpu_to_le32(stats->rx_frame_errors);
+ retval = 0;
+ }
+ break;
+@@ -613,64 +477,6 @@ gen_ndis_query_resp (int configNr, u32 OID, u8 *buf, unsigned buf_len,
+ retval = 0;
+ break;
+
+-#ifdef RNDIS_OPTIONAL_STATS
+- case OID_802_3_XMIT_DEFERRED:
+- DBG("%s: OID_802_3_XMIT_DEFERRED\n", __func__);
+- /* TODO */
+- break;
+-
+- case OID_802_3_XMIT_MAX_COLLISIONS:
+- DBG("%s: OID_802_3_XMIT_MAX_COLLISIONS\n", __func__);
+- /* TODO */
+- break;
+-
+- case OID_802_3_RCV_OVERRUN:
+- DBG("%s: OID_802_3_RCV_OVERRUN\n", __func__);
+- /* TODO */
+- break;
+-
+- case OID_802_3_XMIT_UNDERRUN:
+- DBG("%s: OID_802_3_XMIT_UNDERRUN\n", __func__);
+- /* TODO */
+- break;
+-
+- case OID_802_3_XMIT_HEARTBEAT_FAILURE:
+- DBG("%s: OID_802_3_XMIT_HEARTBEAT_FAILURE\n", __func__);
+- /* TODO */
+- break;
+-
+- case OID_802_3_XMIT_TIMES_CRS_LOST:
+- DBG("%s: OID_802_3_XMIT_TIMES_CRS_LOST\n", __func__);
+- /* TODO */
+- break;
+-
+- case OID_802_3_XMIT_LATE_COLLISIONS:
+- DBG("%s: OID_802_3_XMIT_LATE_COLLISIONS\n", __func__);
+- /* TODO */
+- break;
+-#endif /* RNDIS_OPTIONAL_STATS */
+-
+-#ifdef RNDIS_PM
+- /* power management OIDs (table 4-5) */
+- case OID_PNP_CAPABILITIES:
+- DBG("%s: OID_PNP_CAPABILITIES\n", __func__);
+-
+- /* for now, no wakeup capabilities */
+- length = sizeof (struct NDIS_PNP_CAPABILITIES);
+- memset(outbuf, 0, length);
+- retval = 0;
+- break;
+- case OID_PNP_QUERY_POWER:
+- DBG("%s: OID_PNP_QUERY_POWER D%d\n", __func__,
+- get_unaligned_le32(buf) - 1);
+- /* only suspend is a real power state, and
+- * it can't be entered by OID_PNP_SET_POWER...
+- */
+- length = 0;
+- retval = 0;
+- break;
+-#endif
+-
+ default:
+ pr_warning("%s: query unknown OID 0x%08X\n",
+ __func__, OID);
+@@ -726,9 +532,6 @@ static int gen_ndis_set_resp (u8 configNr, u32 OID, u8 *buf, u32 buf_len,
+ * what makes the packet flow start and stop, like
+ * activating the CDC Ethernet altsetting.
+ */
+-#ifdef RNDIS_PM
+-update_linkstate:
+-#endif
+ retval = 0;
+ if (*params->filter) {
+ params->state = RNDIS_DATA_INITIALIZED;
+@@ -747,49 +550,6 @@ update_linkstate:
+ DBG("%s: OID_802_3_MULTICAST_LIST\n", __func__);
+ retval = 0;
+ break;
+-#if 0
+- case OID_GEN_RNDIS_CONFIG_PARAMETER:
+- {
+- struct rndis_config_parameter *param;
+- param = (struct rndis_config_parameter *) buf;
+- DBG("%s: OID_GEN_RNDIS_CONFIG_PARAMETER '%*s'\n",
+- __func__,
+- min(cpu_to_le32(param->ParameterNameLength),80),
+- buf + param->ParameterNameOffset);
+- retval = 0;
+- }
+- break;
+-#endif
+-
+-#ifdef RNDIS_PM
+- case OID_PNP_SET_POWER:
+- /* The only real power state is USB suspend, and RNDIS requests
+- * can't enter it; this one isn't really about power. After
+- * resuming, Windows forces a reset, and then SET_POWER D0.
+- * FIXME ... then things go batty; Windows wedges itself.
+- */
+- i = get_unaligned_le32(buf);
+- DBG("%s: OID_PNP_SET_POWER D%d\n", __func__, i - 1);
+- switch (i) {
+- case NdisDeviceStateD0:
+- *params->filter = params->saved_filter;
+- goto update_linkstate;
+- case NdisDeviceStateD3:
+- case NdisDeviceStateD2:
+- case NdisDeviceStateD1:
+- params->saved_filter = *params->filter;
+- retval = 0;
+- break;
+- }
+- break;
+-
+-#ifdef RNDIS_WAKEUP
+- // no wakeup support advertised, so wakeup OIDs always fail:
+- // - OID_PNP_ENABLE_WAKE_UP
+- // - OID_PNP_{ADD,REMOVE}_WAKE_UP_PATTERN
+-#endif
+-
+-#endif /* RNDIS_PM */
+
+ default:
+ pr_warning("%s: set unknown OID 0x%08X, size %d\n",
+@@ -807,8 +567,10 @@ static int rndis_init_response (int configNr, rndis_init_msg_type *buf)
+ {
+ rndis_init_cmplt_type *resp;
+ rndis_resp_t *r;
++ struct rndis_params *params = rndis_per_dev_params + configNr;
+
+- if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP;
++ if (!params->dev)
++ return -ENOTSUPP;
+
+ r = rndis_add_response (configNr, sizeof (rndis_init_cmplt_type));
+ if (!r)
+@@ -826,7 +588,7 @@ static int rndis_init_response (int configNr, rndis_init_msg_type *buf)
+ resp->Medium = __constant_cpu_to_le32 (RNDIS_MEDIUM_802_3);
+ resp->MaxPacketsPerTransfer = __constant_cpu_to_le32 (1);
+ resp->MaxTransferSize = cpu_to_le32 (
+- rndis_per_dev_params [configNr].dev->mtu
++ params->dev->mtu
+ + sizeof (struct ethhdr)
+ + sizeof (struct rndis_packet_msg_type)
+ + 22);
+@@ -834,10 +596,7 @@ static int rndis_init_response (int configNr, rndis_init_msg_type *buf)
+ resp->AFListOffset = __constant_cpu_to_le32 (0);
+ resp->AFListSize = __constant_cpu_to_le32 (0);
+
+- if (rndis_per_dev_params [configNr].ack)
+- rndis_per_dev_params [configNr].ack (
+- rndis_per_dev_params [configNr].dev);
+-
++ params->resp_avail(params->v);
+ return 0;
+ }
+
+@@ -845,9 +604,11 @@ static int rndis_query_response (int configNr, rndis_query_msg_type *buf)
+ {
+ rndis_query_cmplt_type *resp;
+ rndis_resp_t *r;
++ struct rndis_params *params = rndis_per_dev_params + configNr;
+
+ // DBG("%s: OID = %08X\n", __func__, cpu_to_le32(buf->OID));
+- if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP;
++ if (!params->dev)
++ return -ENOTSUPP;
+
+ /*
+ * we need more memory:
+@@ -878,9 +639,7 @@ static int rndis_query_response (int configNr, rndis_query_msg_type *buf)
+ } else
+ resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS);
+
+- if (rndis_per_dev_params [configNr].ack)
+- rndis_per_dev_params [configNr].ack (
+- rndis_per_dev_params [configNr].dev);
++ params->resp_avail(params->v);
+ return 0;
+ }
+
+@@ -889,6 +648,7 @@ static int rndis_set_response (int configNr, rndis_set_msg_type *buf)
+ u32 BufLength, BufOffset;
+ rndis_set_cmplt_type *resp;
+ rndis_resp_t *r;
++ struct rndis_params *params = rndis_per_dev_params + configNr;
+
+ r = rndis_add_response (configNr, sizeof (rndis_set_cmplt_type));
+ if (!r)
+@@ -898,7 +658,7 @@ static int rndis_set_response (int configNr, rndis_set_msg_type *buf)
+ BufLength = le32_to_cpu (buf->InformationBufferLength);
+ BufOffset = le32_to_cpu (buf->InformationBufferOffset);
+
+-#ifdef VERBOSE
++#ifdef VERBOSE_DEBUG
+ DBG("%s: Length: %d\n", __func__, BufLength);
+ DBG("%s: Offset: %d\n", __func__, BufOffset);
+ DBG("%s: InfoBuffer: ", __func__);
+@@ -919,10 +679,7 @@ static int rndis_set_response (int configNr, rndis_set_msg_type *buf)
+ else
+ resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS);
+
+- if (rndis_per_dev_params [configNr].ack)
+- rndis_per_dev_params [configNr].ack (
+- rndis_per_dev_params [configNr].dev);
+-
++ params->resp_avail(params->v);
+ return 0;
+ }
+
+@@ -930,6 +687,7 @@ static int rndis_reset_response (int configNr, rndis_reset_msg_type *buf)
+ {
+ rndis_reset_cmplt_type *resp;
+ rndis_resp_t *r;
++ struct rndis_params *params = rndis_per_dev_params + configNr;
+
+ r = rndis_add_response (configNr, sizeof (rndis_reset_cmplt_type));
+ if (!r)
+@@ -942,10 +700,7 @@ static int rndis_reset_response (int configNr, rndis_reset_msg_type *buf)
+ /* resent information */
+ resp->AddressingReset = __constant_cpu_to_le32 (1);
+
+- if (rndis_per_dev_params [configNr].ack)
+- rndis_per_dev_params [configNr].ack (
+- rndis_per_dev_params [configNr].dev);
+-
++ params->resp_avail(params->v);
+ return 0;
+ }
+
+@@ -954,6 +709,7 @@ static int rndis_keepalive_response (int configNr,
+ {
+ rndis_keepalive_cmplt_type *resp;
+ rndis_resp_t *r;
++ struct rndis_params *params = rndis_per_dev_params + configNr;
+
+ /* host "should" check only in RNDIS_DATA_INITIALIZED state */
+
+@@ -968,10 +724,7 @@ static int rndis_keepalive_response (int configNr,
+ resp->RequestID = buf->RequestID; /* Still LE in msg buffer */
+ resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS);
+
+- if (rndis_per_dev_params [configNr].ack)
+- rndis_per_dev_params [configNr].ack (
+- rndis_per_dev_params [configNr].dev);
+-
++ params->resp_avail(params->v);
+ return 0;
+ }
+
+@@ -983,8 +736,9 @@ static int rndis_indicate_status_msg (int configNr, u32 status)
+ {
+ rndis_indicate_status_msg_type *resp;
+ rndis_resp_t *r;
++ struct rndis_params *params = rndis_per_dev_params + configNr;
+
+- if (rndis_per_dev_params [configNr].state == RNDIS_UNINITIALIZED)
++ if (params->state == RNDIS_UNINITIALIZED)
+ return -ENOTSUPP;
+
+ r = rndis_add_response (configNr,
+@@ -1000,9 +754,7 @@ static int rndis_indicate_status_msg (int configNr, u32 status)
+ resp->StatusBufferLength = __constant_cpu_to_le32 (0);
+ resp->StatusBufferOffset = __constant_cpu_to_le32 (0);
+
+- if (rndis_per_dev_params [configNr].ack)
+- rndis_per_dev_params [configNr].ack (
+- rndis_per_dev_params [configNr].dev);
++ params->resp_avail(params->v);
+ return 0;
+ }
+
+@@ -1029,7 +781,6 @@ void rndis_uninit (int configNr)
+
+ if (configNr >= RNDIS_MAX_CONFIGS)
+ return;
+- rndis_per_dev_params [configNr].used = 0;
+ rndis_per_dev_params [configNr].state = RNDIS_UNINITIALIZED;
+
+ /* drain the response queue */
+@@ -1142,21 +893,25 @@ int rndis_msg_parser (u8 configNr, u8 *buf)
+ return -ENOTSUPP;
+ }
+
+-int rndis_register (int (* rndis_control_ack) (struct net_device *))
++int rndis_register(void (*resp_avail)(void *v), void *v)
+ {
+ u8 i;
+
++ if (!resp_avail)
++ return -EINVAL;
++
+ for (i = 0; i < RNDIS_MAX_CONFIGS; i++) {
+ if (!rndis_per_dev_params [i].used) {
+ rndis_per_dev_params [i].used = 1;
+- rndis_per_dev_params [i].ack = rndis_control_ack;
++ rndis_per_dev_params [i].resp_avail = resp_avail;
++ rndis_per_dev_params [i].v = v;
+ DBG("%s: configNr = %d\n", __func__, i);
+ return i;
+ }
+ }
+ DBG("failed\n");
+
+- return -1;
++ return -ENODEV;
+ }
+
+ void rndis_deregister (int configNr)
+@@ -1169,16 +924,14 @@ void rndis_deregister (int configNr)
+ return;
+ }
+
+-int rndis_set_param_dev (u8 configNr, struct net_device *dev,
+- struct net_device_stats *stats,
+- u16 *cdc_filter)
++int rndis_set_param_dev(u8 configNr, struct net_device *dev, u16 *cdc_filter)
+ {
+ DBG("%s:\n", __func__ );
+- if (!dev || !stats) return -1;
++ if (!dev)
++ return -EINVAL;
+ if (configNr >= RNDIS_MAX_CONFIGS) return -1;
+
+ rndis_per_dev_params [configNr].dev = dev;
+- rndis_per_dev_params [configNr].stats = stats;
+ rndis_per_dev_params [configNr].filter = cdc_filter;
+
+ return 0;
+@@ -1296,14 +1049,11 @@ int rndis_rm_hdr(struct sk_buff *skb)
+
+ #ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+-static int rndis_proc_read (char *page, char **start, off_t off, int count, int *eof,
+- void *data)
++static int rndis_proc_show(struct seq_file *m, void *v)
+ {
+- char *out = page;
+- int len;
+- rndis_params *param = (rndis_params *) data;
++ rndis_params *param = m->private;
+
+- out += snprintf (out, count,
++ seq_printf(m,
+ "Config Nr. %d\n"
+ "used : %s\n"
+ "state : %s\n"
+@@ -1326,25 +1076,13 @@ static int rndis_proc_read (char *page, char **start, off_t off, int count, int
+ (param->media_state) ? 0 : param->speed*100,
+ (param->media_state) ? "disconnected" : "connected",
+ param->vendorID, param->vendorDescr);
+-
+- len = out - page;
+- len -= off;
+-
+- if (len < count) {
+- *eof = 1;
+- if (len <= 0)
+- return 0;
+- } else
+- len = count;
+-
+- *start = page + off;
+- return len;
++ return 0;
+ }
+
+-static int rndis_proc_write (struct file *file, const char __user *buffer,
+- unsigned long count, void *data)
++static ssize_t rndis_proc_write(struct file *file, const char __user *buffer,
++ size_t count, loff_t *ppos)
+ {
+- rndis_params *p = data;
++ rndis_params *p = PDE(file->f_path.dentry->d_inode)->data;
+ u32 speed = 0;
+ int i, fl_speed = 0;
+
+@@ -1386,6 +1124,20 @@ static int rndis_proc_write (struct file *file, const char __user *buffer,
+ return count;
+ }
+
++static int rndis_proc_open(struct inode *inode, struct file *file)
++{
++ return single_open(file, rndis_proc_show, PDE(inode)->data);
++}
++
++static const struct file_operations rndis_proc_fops = {
++ .owner = THIS_MODULE,
++ .open = rndis_proc_open,
++ .read = seq_read,
++ .llseek = seq_lseek,
++ .release = single_release,
++ .write = rndis_proc_write,
++};
++
+ #define NAME_TEMPLATE "driver/rndis-%03d"
+
+ static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS];
+@@ -1403,7 +1155,9 @@ int __init rndis_init (void)
+
+ sprintf (name, NAME_TEMPLATE, i);
+ if (!(rndis_connect_state [i]
+- = create_proc_entry (name, 0660, NULL)))
++ = proc_create_data(name, 0660, NULL,
++ &rndis_proc_fops,
++ (void *)(rndis_per_dev_params + i))))
+ {
+ DBG("%s :remove entries", __func__);
+ while (i) {
+@@ -1413,11 +1167,6 @@ int __init rndis_init (void)
+ DBG("\n");
+ return -EIO;
+ }
+-
+- rndis_connect_state [i]->write_proc = rndis_proc_write;
+- rndis_connect_state [i]->read_proc = rndis_proc_read;
+- rndis_connect_state [i]->data = (void *)
+- (rndis_per_dev_params + i);
+ #endif
+ rndis_per_dev_params [i].confignr = i;
+ rndis_per_dev_params [i].used = 0;
+diff --git a/drivers/usb/gadget/rndis.h b/drivers/usb/gadget/rndis.h
+index 397b149..aac61df 100644
+--- a/drivers/usb/gadget/rndis.h
++++ b/drivers/usb/gadget/rndis.h
+@@ -1,8 +1,6 @@
+ /*
+ * RNDIS Definitions for Remote NDIS
+ *
+- * Version: $Id: rndis.h,v 1.15 2004/03/25 21:33:46 robert Exp $
+- *
+ * Authors: Benedikt Spranger, Pengutronix
+ * Robert Schwebel, Pengutronix
+ *
+@@ -235,20 +233,19 @@ typedef struct rndis_params
+ const u8 *host_mac;
+ u16 *filter;
+ struct net_device *dev;
+- struct net_device_stats *stats;
+
+ u32 vendorID;
+ const char *vendorDescr;
+- int (*ack) (struct net_device *);
++ void (*resp_avail)(void *v);
++ void *v;
+ struct list_head resp_queue;
+ } rndis_params;
+
+ /* RNDIS Message parser and other useless functions */
+ int rndis_msg_parser (u8 configNr, u8 *buf);
+-int rndis_register (int (*rndis_control_ack) (struct net_device *));
++int rndis_register(void (*resp_avail)(void *v), void *v);
+ void rndis_deregister (int configNr);
+ int rndis_set_param_dev (u8 configNr, struct net_device *dev,
+- struct net_device_stats *stats,
+ u16 *cdc_filter);
+ int rndis_set_param_vendor (u8 configNr, u32 vendorID,
+ const char *vendorDescr);
+diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c
+index 6b1ef48..29d13eb 100644
+--- a/drivers/usb/gadget/s3c2410_udc.c
++++ b/drivers/usb/gadget/s3c2410_udc.c
+@@ -35,7 +35,6 @@
+ #include <linux/list.h>
+ #include <linux/interrupt.h>
+ #include <linux/platform_device.h>
+-#include <linux/version.h>
+ #include <linux/clk.h>
+
+ #include <linux/debugfs.h>
+@@ -49,15 +48,14 @@
+ #include <asm/irq.h>
+ #include <asm/system.h>
+ #include <asm/unaligned.h>
+-#include <asm/arch/irqs.h>
++#include <mach/irqs.h>
+
+-#include <asm/arch/hardware.h>
+-#include <asm/arch/regs-gpio.h>
++#include <mach/hardware.h>
++#include <mach/regs-gpio.h>
+
+ #include <asm/plat-s3c24xx/regs-udc.h>
+ #include <asm/plat-s3c24xx/udc.h>
+
+-#include <asm/mach-types.h>
+
+ #include "s3c2410_udc.h"
+
+@@ -888,7 +886,7 @@ static void s3c2410_udc_handle_ep(struct s3c2410_ep *ep)
+ }
+ }
+
+-#include <asm/arch/regs-irq.h>
++#include <mach/regs-irq.h>
+
+ /*
+ * s3c2410_udc_irq - interrupt handler
+diff --git a/drivers/usb/gadget/sa1100_udc.c b/drivers/usb/gadget/sa1100_udc.c
+new file mode 100644
+index 0000000..3a9f24f
+--- /dev/null
++++ b/drivers/usb/gadget/sa1100_udc.c
+@@ -0,0 +1,2421 @@
++/*
++ * SA1100 USB Device Controller (UDC) driver.
++ *
++ * Copyright (C) Compaq Computer Corporation, 1998, 1999
++ * Copyright (C) Extenex Corporation, 2001
++ * Copyright (C) David Brownell, 2003
++ * Copyright (C) Nick Bane, 2005, 2006, 2007
++ * Many fragments from pxa2xx_udc.c and mach-sa1100 driver with various
++ * GPL Copyright authors incl Russel king and Nicolas Pitre
++ * Working port to 2.6.32-1 by N C Bane
++ *
++ * This file provides interrupt routing and overall coordination for the
++ * sa1100 USB endpoints: ep0, ep1out-bulk, ep2in-bulk, as well as device
++ * initialization and some parts of USB "Chapter 9" device behavior.
++ *
++ * It implements the "USB gadget controller" API, abstracting most hardware
++ * details so that drivers running on top of this API are mostly independent
++ * of hardware. A key exception is that ep0 logic needs to understand which
++ * endpoints a given controller has, and their capabilities. Also, hardware
++ * that doesn't fully support USB (like sa1100) may need workarounds in the
++ * protocols implemented by device functions.
++ *
++ * See linux/Documentation/arm/SA1100/SA1100_USB for more info, or the
++ * kerneldoc for the API exposed to gadget drivers.
++ *
++ */
++//#define DEBUG 1
++//#define VERBOSE 1
++
++//#define SA1100_USB_DEBUG
++#ifdef SA1100_USB_DEBUG
++static int sa1100_usb_debug=0;
++#endif
++
++#define NCB_DMA_FIX
++#ifdef NCB_DMA_FIX
++// This is a clunky fix for dma alignemnt issues
++// It should probably be done better by someone more
++// steeped in DMA lore
++#include <linux/slab.h>
++#define SEND_BUFFER_SIZE 4096 /* this is probably a bit big */
++#define RECEIVE_BUFFER_SIZE 256 /* 64 may be all that is necessary */
++static char *send_buffer=NULL;
++static char *receive_buffer=NULL;
++#endif
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/delay.h>
++#include <linux/ioport.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/smp_lock.h>
++#include <linux/errno.h>
++#include <linux/init.h>
++#include <linux/timer.h>
++#include <linux/list.h>
++#include <linux/interrupt.h>
++#include <linux/version.h>
++#include <linux/device.h>
++#include <linux/platform_device.h>
++
++#include <asm/byteorder.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/dma.h>
++#include <asm/system.h>
++#include <asm/mach-types.h>
++#include <asm/unaligned.h>
++
++#include <linux/usb.h>
++#include <linux/usb/ch9.h>
++#include <linux/usb/gadget.h>
++
++#if CONFIG_PROC_FS
++#include <linux/proc_fs.h>
++#endif
++
++#if defined(CONFIG_SA1100_BALLOON)
++#include <asm/arch/balloon2.h>
++#endif
++
++#if defined(CONFIG_SA1100_COLLIE)
++#include <asm/hardware/scoop.h>
++#include <asm/arch/collie.h>
++#endif
++
++#define DRIVER_VERSION __DATE__
++
++#define DMA_ADDR_INVALID (~(dma_addr_t)0)
++
++
++static const char driver_name [] = "sa1100_udc";
++static const char driver_desc [] = "SA-1110 USB Device Controller";
++
++static const char ep0name [] = "ep0";
++
++#ifdef DEBUG
++static char *type_string (u8 bmAttributes)
++{
++ switch ( (bmAttributes) & USB_ENDPOINT_XFERTYPE_MASK) {
++ case USB_ENDPOINT_XFER_BULK: return "bulk";
++ //case USB_ENDPOINT_XFER_ISOC: return "iso";
++ case USB_ENDPOINT_XFER_INT: return "intr";
++ };
++ return "control";
++}
++#endif
++
++#include <linux/dma-mapping.h>
++struct usb_stats_t {
++ unsigned long ep0_fifo_write_failures;
++ unsigned long ep0_bytes_written;
++ unsigned long ep0_fifo_read_failures;
++ unsigned long ep0_bytes_read;
++};
++
++struct usb_info_t {
++ dma_regs_t *dmaregs_tx, *dmaregs_rx;
++ int state;
++ unsigned char address;
++ struct usb_stats_t stats;
++};
++
++enum { kError=-1, kEvSuspend=0, kEvReset=1,
++ kEvResume=2, kEvAddress=3, kEvConfig=4, kEvDeConfig=5 };
++int usbctl_next_state_on_event( int event ) {
++ return 0;
++}
++static struct usb_info_t usbd_info;
++
++/* receiver */
++void ep1_reset(void);
++void ep1_stall(void);
++int sa1100_usb_recv (struct usb_request *req, void (*callback) (int,int));
++
++/* xmitter */
++void ep2_reset(void);
++void ep2_stall(void);
++int sa1100_usb_send (struct usb_request *req, void (*callback) (int,int));
++
++/* UDC register utility functions */
++#define UDC_write(reg, val) { \
++ int i = 10000; \
++ do { \
++ (reg) = (val); \
++ if (i-- <= 0) { \
++ printk( "%s [%d]: write %#x to %p (%#lx) failed\n", \
++ __FUNCTION__, __LINE__, (val), &(reg), (reg)); \
++ break; \
++ } \
++ } while((reg) != (val)); \
++}
++
++#define UDC_set(reg, val) { \
++ int i = 10000; \
++ do { \
++ (reg) |= (val); \
++ if (i-- <= 0) { \
++ printk( "%s [%d]: set %#x of %p (%#lx) failed\n", \
++ __FUNCTION__, __LINE__, (val), &(reg), (reg)); \
++ break; \
++ } \
++ } while(!((reg) & (val))); \
++}
++
++#define UDC_clear(reg, val) { \
++ int i = 10000; \
++ do { \
++ (reg) &= ~(val); \
++ if (i-- <= 0) { \
++ printk( "%s [%d]: clear %#x of %p (%#lx) failed\n", \
++ __FUNCTION__, __LINE__, (val), &(reg), (reg)); \
++ break; \
++ } \
++ } while((reg) & (val)); \
++}
++
++#define UDC_flip(reg, val) { \
++ int i = 10000; \
++ (reg) = (val); \
++ do { \
++ (reg) = (val); \
++ if (i-- <= 0) { \
++ printk( "%s [%d]: flip %#x of %p (%#lx) failed\n", \
++ __FUNCTION__, __LINE__, (val), &(reg), (reg)); \
++ break; \
++ } \
++ } while(((reg) & (val))); \
++}
++
++#include "sa1100_udc.h"
++
++static struct sa1100_udc *the_controller;
++static void nuke (struct sa1100_ep *, int status);
++static void done (struct sa1100_ep *ep, struct sa1100_request *req, int status);
++static inline void ep0_idle (struct sa1100_udc *dev)
++{
++ dev->ep0state = EP0_IDLE;
++}
++
++// ep0 handlers
++
++// 1 == lots of trace noise, 0 = only "important' stuff
++#define VERBOSITY 0
++
++#if 1 && !defined( ASSERT )
++# define ASSERT(expr) \
++ if(!(expr)) { \
++ printk( "Assertion failed! %s,%s,%s,line=%d\n",\
++ #expr,__FILE__,__FUNCTION__,__LINE__); \
++ }
++#else
++# define ASSERT(expr)
++#endif
++
++#if VERBOSITY
++#define PRINTKD(fmt, args...) printk( fmt , ## args)
++#else
++#define PRINTKD(fmt, args...)
++#endif
++
++/* other subroutines */
++unsigned int (*wrint)(void);
++void ep0_int_hndlr( void );
++static void ep0_queue(void *buf, unsigned int req, unsigned int act);
++static void write_fifo( void );
++static int read_fifo( struct usb_ctrlrequest * p );
++
++/* some voodo helpers 01Mar01ww */
++static void set_cs_bits( __u32 set_bits );
++static void set_de( void );
++static void set_ipr( void );
++static void set_ipr_and_de( void );
++static bool clear_opr( void );
++
++/***************************************************************************
++Inline Helpers
++***************************************************************************/
++
++/* Data extraction from usb_request_t fields */
++enum { kTargetDevice=0, kTargetInterface=1, kTargetEndpoint=2 };
++static inline int request_target( __u8 b ) { return (int) ( b & 0x0F); }
++
++static inline int windex_to_ep_num( __u16 w ) { return (int) ( w & 0x000F); }
++inline int type_code_from_request( __u8 by ) { return (( by >> 4 ) & 3); }
++
++/* following is hook for self-powered flag in GET_STATUS. Some devices
++ .. might like to override and return real info */
++static inline bool self_powered_hook( void ) { return true; }
++
++#if VERBOSITY
++/* "pcs" == "print control status" */
++static inline void pcs( void )
++{
++ __u32 foo = Ser0UDCCS0;
++ printk( "%8.8X: %s %s %s %s\n",
++ foo,
++ foo & UDCCS0_SE ? "SE" : "",
++ foo & UDCCS0_OPR ? "OPR" : "",
++ foo & UDCCS0_IPR ? "IPR" : "",
++ foo & UDCCS0_SST ? "SST" : ""
++ );
++}
++static inline void preq( struct usb_ctrlrequest * pReq )
++{
++ static char * tnames[] = { "dev", "intf", "ep", "oth" };
++ static char * rnames[] = { "std", "class", "vendor", "???" };
++ char * psz;
++ switch( pReq->bRequest ) {
++ case USB_REQ_GET_STATUS: psz = "get stat"; break;
++ case USB_REQ_CLEAR_FEATURE: psz = "clr feat"; break;
++ case USB_REQ_SET_FEATURE: psz = "set feat"; break;
++ case USB_REQ_SET_ADDRESS: psz = "set addr"; break;
++ case USB_REQ_GET_DESCRIPTOR: psz = "get desc"; break;
++ case USB_REQ_SET_DESCRIPTOR: psz = "set desc"; break;
++ case USB_REQ_GET_CONFIGURATION: psz = "get cfg"; break;
++ case USB_REQ_SET_CONFIGURATION: psz = "set cfg"; break;
++ case USB_REQ_GET_INTERFACE: psz = "get intf"; break;
++ case USB_REQ_SET_INTERFACE: psz = "set intf"; break;
++ default: psz = "unknown"; break;
++ }
++ printk( "- [%s: %s req to %s. dir=%s]\n", psz,
++ rnames[ (pReq->bRequestType >> 5) & 3 ],
++ tnames[ pReq->bRequestType & 3 ],
++ ( pReq->bRequestType & 0x80 ) ? "in" : "out" );
++}
++
++static inline void usbctl_dump_request(const char *prefix, const struct usb_ctrlrequest *req)
++{
++ printk("%s: bRequestType=0x%02x bRequest=0x%02x "
++ "wValue=0x%04x wIndex=0x%04x wLength=0x%04x\n",
++ prefix, req->bRequestType, req->bRequest,
++ le16_to_cpu(req->wValue), le16_to_cpu(req->wIndex),
++ le16_to_cpu(req->wLength));
++}
++#else
++static inline void pcs( void ){}
++//static inline void preq( void ){}
++static inline void preq( void *x ){}
++static inline void usbctl_dump_request(const char *prefix, const struct usb_ctrlrequest *req) {}
++#endif
++
++/***************************************************************************
++Globals
++***************************************************************************/
++static const char pszMe[] = "usbep0: ";
++
++
++/* global write struct to keep write
++ ..state around across interrupts */
++static struct {
++ unsigned char *p;
++ int bytes_left;
++} wr;
++
++/***************************************************************************
++Public Interface
++***************************************************************************/
++
++/* reset received from HUB (or controller just went nuts and reset by itself!)
++ so udc core has been reset, track this state here */
++void ep0_reset(void)
++{
++ /* reset state machine */
++ wr.p = NULL;
++ wr.bytes_left = 0;
++ usbd_info.address=0;
++// needed?
++ Ser0UDCAR = 0;
++}
++
++
++/* handle interrupt for endpoint zero */
++
++inline void ep0_clear_write(void) {
++ wr.p = NULL;
++ wr.bytes_left = 0;
++}
++
++/* this is a config packet parser based on that from the updated HH 2.6 udc */
++static void ep0_read_packet(void)
++{
++ unsigned char status_buf[2]; /* returned in GET_STATUS */
++ struct usb_ctrlrequest req;
++ int request_type;
++ int n;
++ __u32 address;
++ __u32 in, out;
++
++ /* reset previous count */
++ the_controller->ep0_req_len=-1;
++
++ /* read the setup request */
++ n = read_fifo( &req );
++ usbctl_dump_request("ep0_read_packet",&req);
++
++ if ( n != sizeof( req ) ) {
++ printk( "%ssetup begin: fifo READ ERROR wanted %d bytes got %d. "
++ " Stalling out...\n",
++ pszMe, sizeof( req ), n );
++ /* force stall, serviced out */
++ set_cs_bits( UDCCS0_FST | UDCCS0_SO );
++ goto sh_sb_end;
++ }
++
++ /* Is it a standard request? (not vendor or class request) */
++ request_type = type_code_from_request( req.bRequestType );
++ if ( request_type != 0 ) {
++ printk( "%ssetup begin: unsupported bRequestType: %d ignored\n",
++ pszMe, request_type );
++ set_cs_bits( UDCCS0_DE | UDCCS0_SO );
++ goto sh_sb_end;
++ }
++
++ /* save requested reply size */
++ the_controller->ep0_req_len=le16_to_cpu(req.wLength);
++ PRINTKD("%s: request length is %d\n",__FUNCTION__,the_controller->ep0_req_len);
++
++#if VERBOSITY
++ {
++ unsigned char * pdb = (unsigned char *) &req;
++ PRINTKD( "%2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X ",
++ pdb[0], pdb[1], pdb[2], pdb[3], pdb[4], pdb[5], pdb[6], pdb[7]
++ );
++ preq( &req );
++ }
++#endif
++
++ /* Handle it */
++ switch( req.bRequest ) {
++
++ /* This first bunch have no data phase */
++
++ case USB_REQ_SET_ADDRESS:
++ address = (__u32) (req.wValue & 0x7F);
++ /* when SO and DE sent, UDC will enter status phase and ack,
++ ..propagating new address to udc core. Next control transfer
++ ..will be on the new address. You can't see the change in a
++ ..read back of CAR until then. (about 250us later, on my box).
++ ..The original Intel driver sets S0 and DE and code to check
++ ..that address has propagated here. I tried this, but it
++ ..would only work sometimes! The rest of the time it would
++ ..never propagate and we'd spin forever. So now I just set
++ ..it and pray...
++ */
++ Ser0UDCAR = address;
++ usbd_info.address = address;
++ usbctl_next_state_on_event( kEvAddress );
++ set_cs_bits( UDCCS0_SO | UDCCS0_DE ); /* no data phase */
++ printk( "%sI have been assigned address: %d\n", pszMe, address );
++ break;
++
++
++ case USB_REQ_SET_CONFIGURATION:
++ if ( req.wValue == 1 ) {
++ /* configured */
++ if (usbctl_next_state_on_event( kEvConfig ) != kError) {
++ /* (re)set the out and in max packet sizes */
++ PRINTKD( "%s: calling the_controller.driver->setup with SET_CONFIGURATION\n", __FUNCTION__ );
++ the_controller->driver->setup(&the_controller->gadget, &req);
++ in = __le16_to_cpu( the_controller->ep[1].ep.maxpacket );
++ out = __le16_to_cpu( the_controller->ep[2].ep.maxpacket );
++ Ser0UDCOMP = ( out - 1 );
++ Ser0UDCIMP = ( in - 1 );
++ // we are configured
++ usbd_info.state = USB_STATE_CONFIGURED;
++ // enable rx and tx interrupts
++ Ser0UDCCR &= ~(UDCCR_RIM | UDCCR_TIM);
++
++ printk( "%sConfigured (OMP=%8.8X IMP=%8.8X)\n", pszMe, out, in );
++ break;
++ }
++ } else if ( req.wValue == 0 ) {
++ /* de-configured */
++ if (usbctl_next_state_on_event( kEvDeConfig ) != kError )
++ printk( "%sDe-Configured\n", pszMe );
++ usbd_info.state = 0;
++ Ser0UDCCR |= UDCCR_RIM | UDCCR_TIM;
++ ep1_reset ();
++ ep2_reset ();
++ printk("%s: de-configured. Tx and Rx interrupts disabled. ep1 and ep2 reset\n",__FUNCTION__);
++ } else {
++ printk( "%ssetup phase: Unknown "
++ "\"set configuration\" data %d\n",
++ pszMe, req.wValue );
++ }
++ set_cs_bits( UDCCS0_SO | UDCCS0_DE ); /* no data phase */
++ break;
++
++ case USB_REQ_CLEAR_FEATURE:
++ /* could check data length, direction...26Jan01ww */
++ if ( req.wValue == 0 ) { /* clearing ENDPOINT_HALT/STALL */
++ int ep = windex_to_ep_num( req.wIndex );
++ if ( ep == 1 ) {
++ printk( "%sclear feature \"endpoint halt\" "
++ " on receiver\n", pszMe );
++ ep1_reset();
++ }
++ else if ( ep == 2 ) {
++ printk( "%sclear feature \"endpoint halt\" "
++ "on xmitter\n", pszMe );
++ ep2_reset();
++ } else {
++ printk( "%sclear feature \"endpoint halt\" "
++ "on unsupported ep # %d\n",
++ pszMe, ep );
++ }
++ } else {
++ printk( "%sUnsupported feature selector (%d) "
++ "in clear feature. Ignored.\n" ,
++ pszMe, req.wValue );
++ }
++ set_cs_bits( UDCCS0_SO | UDCCS0_DE ); /* no data phase */
++ break;
++
++ case USB_REQ_SET_FEATURE:
++ if ( req.wValue == 0 ) { /* setting ENDPOINT_HALT/STALL */
++ int ep = windex_to_ep_num( req.wValue );
++ if ( ep == 1 ) {
++ printk( "%set feature \"endpoint halt\" "
++ "on receiver\n", pszMe );
++ ep1_stall();
++ }
++ else if ( ep == 2 ) {
++ printk( "%sset feature \"endpoint halt\" "
++ " on xmitter\n", pszMe );
++ ep2_stall();
++ } else {
++ printk( "%sset feature \"endpoint halt\" "
++ "on unsupported ep # %d\n",
++ pszMe, ep );
++ }
++ }
++ else {
++ printk( "%sUnsupported feature selector "
++ "(%d) in set feature\n",
++ pszMe, req.wValue );
++ }
++ set_cs_bits( UDCCS0_SO | UDCCS0_DE ); /* no data phase */
++ break;
++
++ /* The rest have a data phase that writes back to the host */
++ case USB_REQ_GET_STATUS:
++ /* return status bit flags */
++ status_buf[0] = status_buf[1] = 0;
++ n = request_target(req.bRequestType);
++ switch( n ) {
++ case kTargetDevice:
++ if ( self_powered_hook() )
++ status_buf[0] |= 1;
++ break;
++ case kTargetInterface:
++ break;
++ case kTargetEndpoint:
++ /* return stalled bit */
++ n = windex_to_ep_num( req.wIndex );
++ if ( n == 1 )
++ status_buf[0] |= (Ser0UDCCS1 & UDCCS1_FST) >> 4;
++ else if ( n == 2 )
++ status_buf[0] |= (Ser0UDCCS2 & UDCCS2_FST) >> 5;
++ else {
++ printk( "%sUnknown endpoint (%d) "
++ "in GET_STATUS\n", pszMe, n );
++ }
++ break;
++ default:
++ printk( "%sUnknown target (%d) in GET_STATUS\n",
++ pszMe, n );
++ /* fall thru */
++ break;
++ }
++ PRINTKD("%s: GET_STATUS writing %d\n",__FUNCTION__,req.wLength);
++ ep0_queue( status_buf, req.wLength, sizeof( status_buf ));
++ break;
++ case USB_REQ_GET_DESCRIPTOR:
++ PRINTKD( "%s: calling the_controller.driver->setup with GET_DESCRIPTOR\n", __FUNCTION__ );
++ the_controller->driver->setup(&the_controller->gadget, &req);
++ break;
++ case USB_REQ_GET_CONFIGURATION:
++ PRINTKD( "%s: calling the_controller.driver->setup with GET_CONFIGURATION\n", __FUNCTION__ );
++ the_controller->driver->setup(&the_controller->gadget, &req);
++ break;
++ case USB_REQ_GET_INTERFACE:
++ PRINTKD( "%s: calling the_controller->driver->setup with GET_INTERFACE\n", __FUNCTION__ );
++ the_controller->driver->setup(&the_controller->gadget, &req);
++ break;
++ case USB_REQ_SET_INTERFACE:
++ PRINTKD( "%s: calling the_controller->driver->setup with SET_INTERFACE\n", __FUNCTION__ );
++ the_controller->driver->setup(&the_controller->gadget, &req);
++ break;
++ default :
++ printk("%sunknown request 0x%x\n", pszMe, req.bRequest);
++ break;
++ } /* switch( bRequest ) */
++
++sh_sb_end:
++ return;
++
++}
++
++void ep0_int_hndlr(void)
++{
++ u32 cs_reg_in;
++
++ pcs();
++
++ cs_reg_in = Ser0UDCCS0;
++
++ /*
++ * If "setup end" has been set, the usb controller has terminated
++ * a setup transaction before we set DE. This happens during
++ * enumeration with some hosts. For example, the host will ask for
++ * our device descriptor and specify a return of 64 bytes. When we
++ * hand back the first 8, the host will know our max packet size
++ * and turn around and issue a new setup immediately. This causes
++ * the UDC to auto-ack the new setup and set SE. We must then
++ * "unload" (process) the new setup, which is what will happen
++ * after this preamble is finished executing.
++ */
++ if (cs_reg_in & UDCCS0_SE) {
++ PRINTKD("UDC: early termination of setup\n");
++
++ /*
++ * Clear setup end
++ */
++ set_cs_bits(UDCCS0_SSE);
++
++ /*
++ * Clear any pending write.
++ */
++ ep0_clear_write();
++ }
++
++ /*
++ * UDC sent a stall due to a protocol violation.
++ */
++ if (cs_reg_in & UDCCS0_SST) {
++ PRINTKD("UDC: write_preamble: UDC sent stall\n");
++
++ /*
++ * Clear sent stall
++ */
++ set_cs_bits(UDCCS0_SST);
++
++ /*
++ * Clear any pending write.
++ */
++ ep0_clear_write();
++ }
++
++ switch (cs_reg_in & (UDCCS0_OPR | UDCCS0_IPR)) {
++ case UDCCS0_OPR | UDCCS0_IPR:
++ PRINTKD("UDC: write_preamble: see OPR. Stopping write to "
++ "handle new SETUP\n");
++
++ /*
++ * very rarely, you can get OPR and
++ * leftover IPR. Try to clear
++ */
++ UDC_clear(Ser0UDCCS0, UDCCS0_IPR);
++
++ /*
++ * Clear any pending write.
++ */
++ ep0_clear_write();
++
++ /*FALLTHROUGH*/
++ case UDCCS0_OPR:
++ /*
++ * A new setup request is pending. Handle
++ * it. Note that we don't try to read a
++ * packet if SE was set and OPR is clear.
++ */
++ ep0_read_packet();
++ break;
++
++ case 0:
++ // if data pending ...
++ if (wr.p) {
++ unsigned int cs_bits = 0;
++ if (wr.bytes_left != 0) {
++ /*
++ * More data to go
++ */
++ write_fifo();
++ // packet ready
++ cs_bits |= UDCCS0_IPR;
++ }
++
++ if (wr.bytes_left == 0) {
++ /*
++ * All data sent.
++ */
++ cs_bits |= wrint();
++ // a null packet may be following
++ if (!wrint)
++ ep0_clear_write();
++ }
++ set_cs_bits(cs_bits);
++ }
++ else
++ PRINTKD("%s: No data - probably an ACK\n",__FUNCTION__);
++ break;
++
++ case UDCCS0_IPR:
++ PRINTKD("UDC: IPR set, not writing\n");
++ break;
++ }
++
++ pcs();
++ PRINTKD( "-end-\n" );
++}
++
++static unsigned int ep0_sh_write_data(void)
++{
++ /*
++ * If bytes left is zero, we are coming in on the
++ * interrupt after the last packet went out. And
++ * we know we don't have to empty packet this
++ * transfer so just set DE and we are done
++ */
++ PRINTKD("UDC: normal packet ended\n");
++ wrint=NULL;
++ return UDCCS0_DE;
++}
++
++static unsigned int ep0_sh_write_with_empty_packet(void)
++{
++ /*
++ * If bytes left is zero, we are coming in on the
++ * interrupt after the last packet went out.
++ * We must do short packet suff, so set DE and IPR
++ */
++ PRINTKD("UDC: short packet sent\n");
++ wrint=NULL;
++ return UDCCS0_IPR | UDCCS0_DE;
++}
++
++static unsigned int ep0_sh_write_data_then_empty_packet(void)
++{
++ PRINTKD("UDC: last packet full. Send empty packet next\n");
++ wrint=ep0_sh_write_with_empty_packet;
++ return 0;
++}
++
++static void ep0_queue(void *buf, unsigned int len, unsigned int req_len)
++{
++ __u32 cs_reg_bits = UDCCS0_IPR;
++
++ PRINTKD("a=%d r=%d\n", len, req_len);
++
++ if (len == 0) {
++ // no output packet to wait for
++ PRINTKD("%s: zero byte packet being queued. Setting DE and OPR end exiting\n",__FUNCTION__);
++ set_cs_bits(UDCCS0_DE | UDCCS0_SO);
++ return;
++ }
++
++ /*
++ * thou shalt not enter data phase until
++ * Out Packet Ready is clear
++ */
++ if (!clear_opr()) {
++ printk("UDC: SO did not clear OPR\n");
++ set_cs_bits(UDCCS0_DE | UDCCS0_SO);
++ return;
++ }
++
++ // note data to xmit stored
++ wr.p=buf;
++ wr.bytes_left=min(len, req_len);
++
++ // write the first block
++ write_fifo();
++
++ // done already?
++ if (wr.bytes_left == 0) {
++ /*
++ * out in one, so data end
++ */
++ cs_reg_bits |= UDCCS0_DE;
++ ep0_clear_write();
++ // rest is a shorter than expected reply?
++ } else if (len < req_len) {
++ /*
++ * we are going to short-change host
++ * so need nul to not stall
++ */
++ if (len % 8) {
++ PRINTKD("%s: %d more to go ending in a short packet.\n",__FUNCTION__,wr.bytes_left);
++ wrint=ep0_sh_write_with_empty_packet;
++ }
++ // unless we are on a packet boundary. Then send full packet plus null packet.
++ else {
++ PRINTKD("%s: %d more to go then add empty packet.\n",__FUNCTION__,wr.bytes_left);
++ wrint=ep0_sh_write_data_then_empty_packet;
++ }
++ } else {
++ /*
++ * we have as much or more than requested
++ */
++ PRINTKD("%s: %d more to go.\n",__FUNCTION__,wr.bytes_left);
++ wrint=ep0_sh_write_data;
++ }
++
++ /*
++ * note: IPR was set uncondtionally at start of routine
++ */
++ set_cs_bits(cs_reg_bits);
++}
++
++/*
++ * write_fifo()
++ * Stick bytes in the 8 bytes endpoint zero FIFO.
++ * This version uses a variety of tricks to make sure the bytes
++ * are written correctly. 1. The count register is checked to
++ * see if the byte went in, and the write is attempted again
++ * if not. 2. An overall counter is used to break out so we
++ * don't hang in those (rare) cases where the UDC reverses
++ * direction of the FIFO underneath us without notification
++ * (in response to host aborting a setup transaction early).
++ *
++ */
++static void write_fifo( void )
++{
++ int bytes_this_time = min(wr.bytes_left, 8);
++ int bytes_written = 0;
++
++ PRINTKD( "WF=%d: ", bytes_this_time );
++
++ while( bytes_this_time-- ) {
++ unsigned int cwc;
++ int i;
++ PRINTKD( "%2.2X ", *wr.p );
++ cwc = Ser0UDCWC & 15;
++ i = 10;
++ do {
++ Ser0UDCD0 = *wr.p;
++ udelay( 20 ); /* voodo 28Feb01ww */
++ } while( (Ser0UDCWC &15) == cwc && --i );
++
++ if ( i == 0 ) {
++ printk( "%swrite_fifo: write failure\n", pszMe );
++ usbd_info.stats.ep0_fifo_write_failures++;
++ }
++
++ wr.p++;
++ bytes_written++;
++ }
++ wr.bytes_left -= bytes_written;
++
++ /* following propagation voodo so maybe caller writing IPR in
++ ..a moment might actually get it to stick 28Feb01ww */
++ udelay( 300 );
++
++ usbd_info.stats.ep0_bytes_written += bytes_written;
++ PRINTKD( "L=%d WCR=%8.8lX\n", wr.bytes_left, Ser0UDCWC );
++}
++/*
++ * read_fifo()
++ * Read 1-8 bytes out of FIFO and put in request.
++ * Called to do the initial read of setup requests
++ * from the host. Return number of bytes read.
++ *
++ * Like write fifo above, this driver uses multiple
++ * reads checked agains the count register with an
++ * overall timeout.
++ *
++ */
++static int
++read_fifo( struct usb_ctrlrequest * request )
++{
++ int bytes_read = 0;
++ int fifo_count;
++
++ unsigned char * pOut = (unsigned char*) request;
++
++ fifo_count = ( Ser0UDCWC & 0xFF );
++
++ ASSERT( fifo_count <= 8 );
++ PRINTKD( "RF=%d ", fifo_count );
++
++ while( fifo_count-- ) {
++ unsigned int cwc;
++ int i;
++
++ cwc = Ser0UDCWC & 15;
++
++ i = 10;
++ do {
++ *pOut = (unsigned char) Ser0UDCD0;
++ udelay( 20 );
++ } while( ( Ser0UDCWC & 15 ) == cwc && --i );
++
++ if ( i == 0 ) {
++ printk( "%sread_fifo(): read failure\n", pszMe );
++ usbd_info.stats.ep0_fifo_read_failures++;
++ }
++ pOut++;
++ bytes_read++;
++ }
++
++ PRINTKD( "fc=%d\n", bytes_read );
++ usbd_info.stats.ep0_bytes_read++;
++ return bytes_read;
++}
++
++/* some voodo I am adding, since the vanilla macros just aren't doing it 1Mar01ww */
++
++#define ABORT_BITS ( UDCCS0_SST | UDCCS0_SE )
++#define OK_TO_WRITE (!( Ser0UDCCS0 & ABORT_BITS ))
++#define BOTH_BITS (UDCCS0_IPR | UDCCS0_DE)
++
++static void set_cs_bits( __u32 bits )
++{
++ if ( bits & ( UDCCS0_SO | UDCCS0_SSE | UDCCS0_FST | UDCCS0_SST) )
++ Ser0UDCCS0 = bits;
++ else if ( (bits & BOTH_BITS) == BOTH_BITS )
++ set_ipr_and_de();
++ else if ( bits & UDCCS0_IPR )
++ set_ipr();
++ else if ( bits & UDCCS0_DE )
++ set_de();
++}
++
++static void set_de( void )
++{
++ int i = 1;
++ while( 1 ) {
++ if ( OK_TO_WRITE ) {
++ Ser0UDCCS0 |= UDCCS0_DE;
++ } else {
++ PRINTKD( "%sQuitting set DE because SST or SE set\n", pszMe );
++ break;
++ }
++ if ( Ser0UDCCS0 & UDCCS0_DE )
++ break;
++ udelay( i );
++ if ( ++i == 50 ) {
++ printk( "%sDangnabbbit! Cannot set DE! (DE=%8.8X CCS0=%8.8lX)\n",
++ pszMe, UDCCS0_DE, Ser0UDCCS0 );
++ break;
++ }
++ }
++}
++
++static void set_ipr( void )
++{
++ int i = 1;
++ while( 1 ) {
++ if ( OK_TO_WRITE ) {
++ Ser0UDCCS0 |= UDCCS0_IPR;
++ } else {
++ PRINTKD( "%sQuitting set IPR because SST or SE set\n", pszMe );
++ break;
++ }
++ if ( Ser0UDCCS0 & UDCCS0_IPR )
++ break;
++ udelay( i );
++ if ( ++i == 50 ) {
++ printk( "%sDangnabbbit! Cannot set IPR! (IPR=%8.8X CCS0=%8.8lX)\n",
++ pszMe, UDCCS0_IPR, Ser0UDCCS0 );
++ break;
++ }
++ }
++}
++
++static void set_ipr_and_de( void )
++{
++ int i = 1;
++ while( 1 ) {
++ if ( OK_TO_WRITE ) {
++ Ser0UDCCS0 |= BOTH_BITS;
++ } else {
++ PRINTKD( "%sQuitting set IPR/DE because SST or SE set\n", pszMe );
++ break;
++ }
++ if ( (Ser0UDCCS0 & BOTH_BITS) == BOTH_BITS)
++ break;
++ udelay( i );
++ if ( ++i == 50 ) {
++ printk( "%sDangnabbbit! Cannot set DE/IPR! (DE=%8.8X IPR=%8.8X CCS0=%8.8lX)\n",
++ pszMe, UDCCS0_DE, UDCCS0_IPR, Ser0UDCCS0 );
++ break;
++ }
++ }
++}
++
++static bool clear_opr( void )
++{
++ int i = 10000;
++ bool is_clear;
++ do {
++ Ser0UDCCS0 = UDCCS0_SO;
++ is_clear = ! ( Ser0UDCCS0 & UDCCS0_OPR );
++ if ( i-- <= 0 ) {
++ printk( "%sclear_opr(): failed\n", pszMe );
++ break;
++ }
++ } while( ! is_clear );
++ return is_clear;
++}
++
++
++
++// ep1 handlers
++
++static char *ep1_buf;
++static int ep1_len;
++static void (*ep1_callback)(int flag, int size);
++static char *ep1_curdmabuf;
++static dma_addr_t ep1_curdmapos;
++static int ep1_curdmalen;
++static int ep1_remain;
++static int ep1_used;
++
++static dma_regs_t *dmaregs_rx = NULL;
++static int rx_pktsize;
++
++static int naking;
++
++static void
++ep1_start(void)
++{
++ sa1100_reset_dma(dmaregs_rx);
++ if (!ep1_curdmalen) {
++ ep1_curdmalen = rx_pktsize;
++ if (ep1_curdmalen > ep1_remain)
++ ep1_curdmalen = ep1_remain;
++ ep1_curdmapos = dma_map_single(NULL, ep1_curdmabuf, ep1_curdmalen,
++ DMA_FROM_DEVICE);
++ }
++
++ UDC_write( Ser0UDCOMP, ep1_curdmalen-1 );
++
++ sa1100_start_dma(dmaregs_rx, ep1_curdmapos, ep1_curdmalen);
++
++ if ( naking ) {
++ /* turn off NAK of OUT packets, if set */
++ UDC_flip( Ser0UDCCS1, UDCCS1_RPC );
++ naking = 0;
++ }
++}
++
++static void
++ep1_done(int flag)
++{
++ int size = ep1_len - ep1_remain;
++
++ if (!ep1_len)
++ return;
++ if (ep1_curdmalen)
++ dma_unmap_single(NULL, ep1_curdmapos, ep1_curdmalen,
++ DMA_FROM_DEVICE);
++ ep1_len = ep1_curdmalen = 0;
++ if (ep1_callback)
++ ep1_callback(flag, size);
++}
++
++void
++ep1_state_change_notify( int new_state )
++{
++
++}
++
++void
++ep1_stall( void )
++{
++ /* SET_FEATURE force stall at UDC */
++ UDC_set( Ser0UDCCS1, UDCCS1_FST );
++}
++
++int
++ep1_init(dma_regs_t *dmaregs)
++{
++ dmaregs_rx = dmaregs;
++ sa1100_reset_dma(dmaregs_rx);
++ ep1_done(-EAGAIN);
++ return 0;
++}
++
++void
++ep1_reset(void)
++{
++ if (dmaregs_rx)
++ sa1100_reset_dma(dmaregs_rx);
++ UDC_clear(Ser0UDCCS1, UDCCS1_FST);
++ ep1_done(-EINTR);
++}
++
++void ep1_int_hndlr(int udcsr)
++{
++ dma_addr_t dma_addr;
++ unsigned int len;
++ int status = Ser0UDCCS1;
++
++ if ( naking ) printk( "%sEh? in ISR but naking = %d\n", "usbrx: ", naking );
++
++ if (status & UDCCS1_RPC) {
++
++ if (!ep1_curdmalen) {
++ printk("usb_recv: RPC for non-existent buffer\n");
++ naking=1;
++ return;
++ }
++
++ sa1100_stop_dma(dmaregs_rx);
++
++ if (status & UDCCS1_SST) {
++ printk("usb_recv: stall sent OMP=%ld\n", Ser0UDCOMP);
++ UDC_flip(Ser0UDCCS1, UDCCS1_SST);
++ ep1_done(-EIO); // UDC aborted current transfer, so we do
++ return;
++ }
++
++ if (status & UDCCS1_RPE) {
++ printk("usb_recv: RPError %x\n", status);
++ UDC_flip(Ser0UDCCS1, UDCCS1_RPC);
++ ep1_done(-EIO);
++ return;
++ }
++
++ dma_addr=sa1100_get_dma_pos(dmaregs_rx);
++ dma_unmap_single(NULL, ep1_curdmapos, ep1_curdmalen,
++ DMA_FROM_DEVICE);
++ len = dma_addr - ep1_curdmapos;
++#ifdef SA1100_USB_DEBUG
++ if (sa1100_usb_debug) {
++ int i;
++ printk("usb rx %d :\n ",len);
++ if (sa1100_usb_debug>1) {
++ for (i=0; i<len; i++) {
++ if ((i % 32)==31)
++ printk("\n ");
++ printk("%2.2x ",((char *)ep1_curdmapos)[i]);
++ }
++ }
++ printk("\n");
++ }
++#endif
++ if (len < ep1_curdmalen) {
++ char *buf = ep1_curdmabuf + len;
++ while (Ser0UDCCS1 & UDCCS1_RNE) {
++ if (len >= ep1_curdmalen) {
++ printk("usb_recv: too much data in fifo\n");
++ break;
++ }
++ *buf++ = Ser0UDCDR;
++ len++;
++ }
++ } else if (Ser0UDCCS1 & UDCCS1_RNE) {
++ printk("usb_recv: fifo screwed, shouldn't contain data\n");
++ len = 0;
++ }
++
++#if defined(NCB_DMA_FIX)
++// if (len && (ep1_buf != ep1_curdmabuf))
++// memcpy(ep1_buf,ep1_curdmabuf,len);
++ if (len)
++ memcpy(&(((unsigned char *)ep1_buf)[ep1_used]),ep1_curdmabuf,len);
++#endif
++
++ ep1_curdmalen = 0; /* dma unmap already done */
++ ep1_remain -= len;
++ ep1_used += len;
++// ep1_curdmabuf += len; // use same buffer again
++ naking = 1;
++//printk("%s: received %d, %d remaining\n",__FUNCTION__,len,ep1_remain);
++ if (len && (len == rx_pktsize))
++ ep1_start();
++ else
++ ep1_done((len) ? 0 : -EPIPE);
++ }
++ /* else, you can get here if we are holding NAK */
++}
++
++int
++sa1100_usb_recv(struct usb_request *req, void (*callback)(int flag, int size))
++{
++ unsigned long flags;
++ char *buf=req->buf;
++ int len=req->length;
++
++ if (ep1_len)
++ return -EBUSY;
++
++ local_irq_save(flags);
++ ep1_buf = buf;
++ ep1_len = len;
++ ep1_callback = callback;
++ ep1_remain = len;
++ ep1_used = 0;
++#ifdef NCB_DMA_FIX
++// if (((size_t)buf)&3)
++ if (1)
++ ep1_curdmabuf = receive_buffer;
++ else
++#else
++ ep1_curdmabuf = buf;
++#endif
++ ep1_curdmalen = 0;
++ ep1_start();
++ local_irq_restore(flags);
++
++ return 0;
++}
++
++// ep2 handlers
++
++static char *ep2_buf;
++static int ep2_len;
++static void (*ep2_callback)(int status, int size);
++static dma_addr_t ep2_dma;
++static dma_addr_t ep2_curdmapos;
++static int ep2_curdmalen;
++static int ep2_remain;
++static dma_regs_t *dmaregs_tx = NULL;
++static int tx_pktsize;
++
++/* device state is changing, async */
++void
++ep2_state_change_notify( int new_state )
++{
++}
++
++/* set feature stall executing, async */
++void
++ep2_stall( void )
++{
++ UDC_set( Ser0UDCCS2, UDCCS2_FST ); /* force stall at UDC */
++}
++
++static void
++ep2_start(void)
++{
++ if (!ep2_len)
++ return;
++
++ ep2_curdmalen = tx_pktsize;
++ if (ep2_curdmalen > ep2_remain)
++ ep2_curdmalen = ep2_remain;
++
++ /* must do this _before_ queue buffer.. */
++ UDC_flip( Ser0UDCCS2,UDCCS2_TPC ); /* stop NAKing IN tokens */
++ UDC_write( Ser0UDCIMP, ep2_curdmalen-1 );
++
++ Ser0UDCAR = usbd_info.address; // fighting stupid silicon bug
++ sa1100_start_dma(dmaregs_tx, ep2_curdmapos, ep2_curdmalen);
++}
++
++static void
++ep2_done(int flag)
++{
++ int size = ep2_len - ep2_remain;
++ if (ep2_len) {
++ dma_unmap_single(NULL, ep2_dma, ep2_len, DMA_TO_DEVICE);
++ ep2_len = 0;
++ if (ep2_callback)
++ ep2_callback(flag, size);
++ }
++}
++
++int ep2_init(dma_regs_t *dmaregs)
++{
++ dmaregs_tx = dmaregs;
++ sa1100_reset_dma(dmaregs_tx);
++ ep2_done(-EAGAIN);
++ return 0;
++}
++
++void ep2_reset(void)
++{
++ UDC_clear(Ser0UDCCS2, UDCCS2_FST);
++ if (dmaregs_tx)
++ sa1100_reset_dma(dmaregs_tx);
++ ep2_done(-EINTR);
++}
++
++void ep2_int_hndlr(int udcsr)
++{
++ int status = Ser0UDCCS2;
++
++ if (Ser0UDCAR != usbd_info.address) // check for stupid silicon bug.
++ Ser0UDCAR = usbd_info.address;
++
++ if (status & UDCCS2_TPC) {
++
++ UDC_flip(Ser0UDCCS2, UDCCS2_SST);
++
++ sa1100_reset_dma(dmaregs_tx);
++
++ if (status & (UDCCS2_TPE | UDCCS2_TUR)) {
++ printk("usb_send: transmit error %x\n", status);
++ ep2_done(-EIO);
++ } else {
++ ep2_curdmapos += ep2_curdmalen;
++ ep2_remain -= ep2_curdmalen;
++
++ if (ep2_remain != 0)
++ ep2_start();
++ else
++ ep2_done(0);
++ }
++ } else {
++ printk("usb_send: Not TPC: UDCCS2 = %x\n", status);
++ }
++}
++
++int
++sa1100_usb_send(struct usb_request *req, void (*callback)(int status, int size))
++{
++ char *buf=req->buf;
++ int len=req->length;
++ unsigned long flags;
++
++ if (usbd_info.state != USB_STATE_CONFIGURED) {
++ PRINTKD("%s: return -ENODEV\n",__FUNCTION__);
++ return -ENODEV;
++ }
++
++ if (ep2_len) {
++ PRINTKD("%s: return -EBUSY\n",__FUNCTION__);
++ return -EBUSY;
++ }
++
++ local_irq_save(flags);
++#ifdef NCB_DMA_FIX
++ // if misaligned, copy to aligned buffer
++// if (((size_t)buf)&3) {
++ if (1) {
++ PRINTKD("%s: copying %d bytes to send_buffer\n",__FUNCTION__,len);
++ memcpy(send_buffer,buf,len);
++ ep2_buf = send_buffer;
++ }
++ else
++#endif
++ ep2_buf = buf;
++
++ ep2_len = len;
++ ep2_dma = dma_map_single(NULL, ep2_buf, len,DMA_TO_DEVICE);
++ PRINTKD("%s: mapped dma to buffer(%p0\n",__FUNCTION__,buf);
++
++ ep2_callback = callback;
++ ep2_remain = len;
++ ep2_curdmapos = ep2_dma;
++
++ PRINTKD("%s: calling ep2_start\n",__FUNCTION__);
++ ep2_start();
++ local_irq_restore(flags);
++
++ return 0;
++}
++/*-------------------------------------------------------------------------*/
++
++static int
++sa1100_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
++{
++ struct sa1100_udc *dev;
++ struct sa1100_ep *ep;
++ u32 max;
++ int type;
++
++ ep = container_of (_ep, struct sa1100_ep, ep);
++ if (!_ep || !desc || ep->desc || _ep->name == ep0name
++ || desc->bDescriptorType != USB_DT_ENDPOINT) {
++ PRINTKD("%s: _ep = %p, desc = %p\n",__FUNCTION__,_ep,desc);
++ if (_ep && desc)
++ PRINTKD("%s: ep->desc = %p, _ep->name = %s desc->bDescriptorType = %s\n",__FUNCTION__,ep->desc,_ep->name,
++ (desc->bDescriptorType == USB_DT_ENDPOINT) ? "USB_DT_ENDPOINT":"bad!!");
++ return -EINVAL;
++ }
++
++ dev = ep->dev;
++ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
++ return -ESHUTDOWN;
++
++ type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
++ max = le16_to_cpu (desc->wMaxPacketSize);
++ switch (max) {
++ case 64: case 32:
++ /* note: maxpacket > 16 means DMA might overrun/underrun */
++ case 16: case 8:
++ break;
++ default:
++ if (type == USB_ENDPOINT_XFER_INT && max < 64)
++ break;
++ return -EDOM;
++ }
++
++ switch (type) {
++ case USB_ENDPOINT_XFER_BULK:
++ case USB_ENDPOINT_XFER_INT:
++ if (ep == &dev->ep[2]) {
++ if (desc->bEndpointAddress != (USB_DIR_IN|2)) {
++ PRINTKD("%s: ep[2] has invalid endpoint\n",__FUNCTION__);
++ return -EINVAL;
++ }
++ tx_pktsize = max;
++ Ser0UDCOMP = max - 1;
++ PRINTKD("%s: ep2 max packet size is %d\n",__FUNCTION__,max);
++ break;
++ } else if (ep == &dev->ep[1]) {
++ if (desc->bEndpointAddress != (USB_DIR_OUT|1)) {
++ PRINTKD("%s: ep[1] has invalid endpoint\n",__FUNCTION__);
++ return -EINVAL;
++ }
++ rx_pktsize = max;
++ Ser0UDCIMP = max - 1;
++ PRINTKD("%s: ep1 max packet size is %d\n",__FUNCTION__,max);
++ break;
++ }
++ // FALLTHROUGH
++ default:
++ PRINTKD("%s: Invalid endpoint\n",__FUNCTION__);
++ return -EINVAL;
++ }
++
++ _ep->maxpacket = max;
++ ep->desc = desc;
++ ep->stopped = 0;
++
++ DEBUG (dev, "enabled %s %s max %04x\n", _ep->name,
++ type_string (desc->bmAttributes), max);
++
++ return 0;
++}
++
++static int sa1100_disable (struct usb_ep *_ep)
++{
++ struct sa1100_ep *ep;
++
++ ep = container_of (_ep, struct sa1100_ep, ep);
++ if (!_ep || !ep->desc || _ep->name == ep0name)
++ return -EINVAL;
++
++ nuke (ep, -ESHUTDOWN);
++
++ DEBUG (ep->dev, "disabled %s\n", _ep->name);
++
++ ep->desc = NULL;
++ ep->stopped = 1;
++ return 0;
++}
++
++/*-------------------------------------------------------------------------*/
++
++static struct usb_request *
++sa1100_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags)
++{
++ struct sa1100_request *req;
++
++ if (!_ep)
++ return 0;
++
++ req = kzalloc(sizeof *req, gfp_flags);
++ if (!req)
++ return 0;
++
++ memset (req, 0, sizeof *req);
++ req->req.dma = DMA_ADDR_INVALID;
++ INIT_LIST_HEAD (&req->queue);
++ return &req->req;
++}
++
++static void sa1100_free_request(struct usb_ep *_ep, struct usb_request *_req)
++{
++ struct sa1100_request *req;
++
++ req = container_of (_req, struct sa1100_request, req);
++ WARN_ON (!list_empty (&req->queue));
++ kfree(req); //NCB - see pxa2xx_udc
++}
++
++/*-------------------------------------------------------------------------*/
++
++static void done(struct sa1100_ep *ep, struct sa1100_request *req, int status)
++{
++ unsigned stopped = ep->stopped;
++
++ list_del_init (&req->queue);
++
++ if (likely(req->req.status == -EINPROGRESS))
++ req->req.status = status;
++ else
++ status = req->req.status;
++
++ if (status && status != -ESHUTDOWN)
++ VDEBUG (ep->dev, "complete %s req %p stat %d len %u/%u\n",
++ ep->ep.name, &req->req, status,
++ req->req.actual, req->req.length);
++
++ /* don't modify queue heads during completion callback */
++ ep->stopped = 1;
++ req->req.complete (&ep->ep, &req->req);
++ ep->stopped = stopped;
++}
++
++/*-------------------------------------------------------------------------*/
++
++/* FIXME move away from the old non-queued api.
++ * - forces extra work on us
++ * - stores request state twice
++ * - doesn't let gadget driver handle dma mapping
++ * - status codes need mapping
++ */
++
++static int map_status(int status)
++{
++ switch (status) {
++ case 0:
++ case -EIO: /* ep[12]_int_handler */
++ return status;
++ case -EPIPE: /* ep1_int_handler */
++ return 0;
++ // case -EAGAIN: /* ep[12]_init */
++ // case -EINTR: /* ep[12]_reset */
++ default:
++ return -ESHUTDOWN;
++ }
++}
++
++static void tx_callback(int status, int size)
++{
++ struct sa1100_ep *ep = &the_controller->ep[2];
++ struct sa1100_request *req;
++
++ if (list_empty (&ep->queue)) {
++ if (status != -EAGAIN)
++ DEBUG (ep->dev, "%s, bogus tx callback %d/%d\n",
++ ep->ep.name, status, size);
++ return;
++ }
++ req = list_entry (ep->queue.next, struct sa1100_request, queue);
++ req->req.actual = size;
++ done (ep, req, map_status (status));
++
++ if (ep->stopped || list_empty (&ep->queue))
++ return;
++ req = list_entry (ep->queue.next, struct sa1100_request, queue);
++ sa1100_usb_send (&req->req, tx_callback);
++}
++
++static void rx_callback (int status, int size)
++{
++ struct sa1100_ep *ep = &the_controller->ep[1];
++ struct sa1100_request *req;
++
++ if (list_empty (&ep->queue)) {
++ if (status != -EAGAIN)
++ DEBUG (ep->dev, "%s, bogus tx callback %d/%d\n",
++ ep->ep.name, status, size);
++ return;
++ }
++ req = list_entry (ep->queue.next, struct sa1100_request, queue);
++ req->req.actual = size;
++ done (ep, req, map_status (status));
++
++ if (ep->stopped || list_empty (&ep->queue))
++ return;
++ req = list_entry (ep->queue.next, struct sa1100_request, queue);
++ sa1100_usb_recv (&req->req, rx_callback);
++}
++
++
++static int
++sa1100_queue (struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
++{
++ struct sa1100_request *req;
++ struct sa1100_ep *ep;
++ struct sa1100_udc *dev;
++ unsigned long flags;
++
++ req = container_of (_req, struct sa1100_request, req);
++ if (!_req || !_req->complete || !_req->buf
++ || !list_empty (&req->queue))
++ return -EINVAL;
++
++ ep = container_of (_ep, struct sa1100_ep, ep);
++ if (unlikely(!_ep || (!ep->desc && _ep->name != ep0name)))
++ return -EINVAL;
++
++ dev = ep->dev;
++ if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN))
++ return -ESHUTDOWN;
++
++ // handle ep0
++ if (_ep->name == ep0name) {
++ ep0_queue( _req->buf, _req->length, dev->ep0_req_len >=0 ? dev->ep0_req_len: _req->length );
++ return 0;
++ }
++
++ /* sa1100 udc can't write zlps */
++ if (ep == &dev->ep[2] && _req->length == 0)
++ return -ERANGE;
++
++ /* the old sa1100 api doesn't use 'unsigned' for lengths */
++ if (_req->length > INT_MAX)
++ return -ERANGE;
++
++ VDEBUG (dev, "%s queue req %p, len %d buf %p\n",
++ _ep->name, _req, _req->length, _req->buf);
++
++ local_irq_save (flags);
++
++ _req->status = -EINPROGRESS;
++ _req->actual = 0;
++
++ if (list_empty (&ep->queue) && !ep->stopped) {
++ /* FIXME this does DMA mapping wrong. caller is allowed
++ * to provide buffers that don't need mapping, but this
++ * doesn't use them.
++ */
++ if (ep == &ep->dev->ep[2]) {
++ PRINTKD("%s: sa1100_usb_send buf %p length %d\n",__FUNCTION__,_req->buf,_req->length);
++ sa1100_usb_send (_req, tx_callback);
++ }
++ else if (ep == &ep->dev->ep[1]) {
++ PRINTKD("%s: sa1100_usb_recv buf %p length %d\n",__FUNCTION__,_req->buf,_req->length);
++ sa1100_usb_recv (_req, rx_callback);
++ }
++ /* ep0 rx/tx is handled separately */
++ }
++ list_add_tail (&req->queue, &ep->queue);
++
++ local_irq_restore (flags);
++
++ return 0;
++}
++
++/* dequeue ALL requests */
++static void nuke (struct sa1100_ep *ep, int status)
++{
++ struct sa1100_request *req;
++
++ /* called with irqs blocked */
++ while (!list_empty (&ep->queue)) {
++ req = list_entry (ep->queue.next,
++ struct sa1100_request,
++ queue);
++ done (ep, req, status);
++ }
++ if (ep == &ep->dev->ep[1])
++ ep1_reset ();
++ else if (ep == &ep->dev->ep[2])
++ ep2_reset ();
++}
++
++/* dequeue JUST ONE request */
++static int sa1100_dequeue (struct usb_ep *_ep, struct usb_request *_req)
++{
++ struct sa1100_ep *ep;
++ struct sa1100_request *req;
++ unsigned long flags;
++
++ ep = container_of (_ep, struct sa1100_ep, ep);
++ if (!_ep || (!ep->desc && _ep->name != ep0name) || !_req)
++ return -EINVAL;
++
++ local_irq_save (flags);
++
++ /* make sure it's actually queued on this endpoint */
++ list_for_each_entry (req, &ep->queue, queue) {
++ if (&req->req == _req)
++ break;
++ }
++ if (&req->req != _req) {
++ local_irq_restore(flags);
++ return -EINVAL;
++ }
++
++ done(ep, req, -ECONNRESET);
++
++ local_irq_restore(flags);
++
++ return 0;
++}
++
++/*-------------------------------------------------------------------------*/
++
++static int
++sa1100_set_halt (struct usb_ep *_ep, int value)
++{
++ struct sa1100_ep *ep;
++
++ ep = container_of (_ep, struct sa1100_ep, ep);
++ if (unlikely(!_ep
++ || (!ep->desc && _ep->name != ep0name))
++ || (ep->desc->bmAttributes & 0x03) == USB_ENDPOINT_XFER_ISOC)
++ return -EINVAL;
++ if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
++ return -ESHUTDOWN;
++
++ VDEBUG (ep->dev, "%s %s halt\n", _ep->name, value ? "set" : "clear");
++
++ /* set/clear, then synch memory views with the device */
++ if (value) {
++ if (ep == &ep->dev->ep[1])
++ ep1_stall ();
++ else
++ ep2_stall ();
++ } else {
++ if (ep == &ep->dev->ep[1])
++ ep1_reset ();
++ else
++ ep2_reset ();
++ }
++
++ return 0;
++}
++
++static struct usb_ep_ops sa1100_ep_ops = {
++ .enable = sa1100_enable,
++ .disable = sa1100_disable,
++
++ .alloc_request = sa1100_alloc_request,
++ .free_request = sa1100_free_request,
++
++ .queue = sa1100_queue,
++ .dequeue = sa1100_dequeue,
++
++ .set_halt = sa1100_set_halt,
++ // .fifo_status = sa1100_fifo_status,
++ // .fifo_flush = sa1100_fifo_flush,
++};
++
++/*-------------------------------------------------------------------------*/
++
++static int sa1100_get_frame (struct usb_gadget *_gadget)
++{
++ return -EOPNOTSUPP;
++}
++
++static int sa1100_wakeup (struct usb_gadget *_gadget)
++{
++ struct sa1100_udc *dev;
++
++ if (!_gadget)
++ return 0;
++ dev = container_of (_gadget, struct sa1100_udc, gadget);
++
++ // FIXME
++
++ return 0;
++}
++
++static const struct usb_gadget_ops sa1100_ops = {
++ .get_frame = sa1100_get_frame,
++ .wakeup = sa1100_wakeup,
++
++ // .set_selfpowered = sa1100_set_selfpowered,
++};
++
++/*-------------------------------------------------------------------------*/
++
++static inline void enable_resume_mask_suspend (void)
++{
++ int i = 0;
++
++ while (1) {
++ Ser0UDCCR |= UDCCR_SUSIM; // mask future suspend events
++ udelay (i);
++ if ( (Ser0UDCCR & UDCCR_SUSIM) || (Ser0UDCSR & UDCSR_RSTIR))
++ break;
++ if (++i == 50) {
++ WARN (&the_controller, "%s Could not set SUSIM %8.8lX\n",
++ __FUNCTION__, Ser0UDCCR);
++ break;
++ }
++ }
++
++ i = 0;
++ while (1) {
++ Ser0UDCCR &= ~UDCCR_RESIM;
++ udelay (i);
++ if ( (Ser0UDCCR & UDCCR_RESIM) == 0
++ || (Ser0UDCSR & UDCSR_RSTIR))
++ break;
++ if (++i == 50) {
++ WARN (&the_controller, "%s Could not clear RESIM %8.8lX\n",
++ __FUNCTION__, Ser0UDCCR);
++ break;
++ }
++ }
++}
++
++static inline void enable_suspend_mask_resume (void)
++{
++ int i = 0;
++ while (1) {
++ Ser0UDCCR |= UDCCR_RESIM; // mask future resume events
++ udelay (i);
++ if (Ser0UDCCR & UDCCR_RESIM || (Ser0UDCSR & UDCSR_RSTIR))
++ break;
++ if (++i == 50) {
++ WARN (&the_controller, "%s could not set RESIM %8.8lX\n",
++ __FUNCTION__, Ser0UDCCR);
++ break;
++ }
++ }
++ i = 0;
++ while (1) {
++ Ser0UDCCR &= ~UDCCR_SUSIM;
++ udelay (i);
++ if ( (Ser0UDCCR & UDCCR_SUSIM) == 0
++ || (Ser0UDCSR & UDCSR_RSTIR))
++ break;
++ if (++i == 50) {
++ WARN (&the_controller, "%s Could not clear SUSIM %8.8lX\n",
++ __FUNCTION__, Ser0UDCCR);
++ break;
++ }
++ }
++}
++
++// HACK DEBUG 3Mar01ww
++// Well, maybe not, it really seems to help! 08Mar01ww
++static void core_kicker (void)
++{
++ u32 car = Ser0UDCAR;
++ u32 imp = Ser0UDCIMP;
++ u32 omp = Ser0UDCOMP;
++
++ UDC_set (Ser0UDCCR, UDCCR_UDD);
++ udelay (300);
++ UDC_clear (Ser0UDCCR, UDCCR_UDD);
++
++ Ser0UDCAR = car;
++ Ser0UDCIMP = imp;
++ Ser0UDCOMP = omp;
++}
++
++static irqreturn_t udc_int_hndlr(int irq, void *_dev)
++{
++ struct sa1100_udc *dev = _dev;
++ u32 status = Ser0UDCSR;
++
++ PRINTKD("%s: status = 0x%x and control = 0x%lx\n", __FUNCTION__,
++ status, Ser0UDCCR);
++ /* ReSeT Interrupt Request - UDC has been reset */
++ if (status & UDCSR_RSTIR) {
++ PRINTKD("%s: processing UDCSR_RSTIR\n", __FUNCTION__);
++ if (usbctl_next_state_on_event(kEvReset) != kError) {
++ /* starting 20ms or so reset sequence now... */
++ INFO (dev, "Resetting\n");
++ ep0_reset(); // just set state to idle
++ ep1_reset(); // flush dma, clear false stall
++ ep2_reset(); // flush dma, clear false stall
++ }
++ // mask reset ints, they flood during sequence, enable
++ // suspend and resume
++ UDC_set(Ser0UDCCR, UDCCR_REM); // mask reset
++ UDC_clear(Ser0UDCCR, (UDCCR_SUSIM | UDCCR_RESIM)); // enable suspend and resume
++ UDC_flip(Ser0UDCSR, status); // clear all pending sources
++ PRINTKD("%s: setting USB_FULL_SPEED\n",__FUNCTION__);
++ dev->gadget.speed = USB_SPEED_FULL;
++ return IRQ_HANDLED; // NCB
++ }
++
++ /* else we have done something other than reset,
++ * so be sure reset enabled
++ */
++ UDC_clear(Ser0UDCCR, UDCCR_REM);
++
++ /* RESume Interrupt Request */
++ if (status & UDCSR_RESIR) {
++ struct usb_gadget_driver *driver = dev->driver;
++
++ PRINTKD("%s: processing UDCSR_RESIR\n",__FUNCTION__);
++ if (driver->resume)
++ driver->resume (&dev->gadget);
++ core_kicker ();
++ enable_suspend_mask_resume ();
++ }
++
++ /* SUSpend Interrupt Request */
++ if (status & UDCSR_SUSIR) {
++ struct usb_gadget_driver *driver = dev->driver;
++
++ PRINTKD("%s: processing UDCSR_SUSIR\n",__FUNCTION__);
++ if (driver->suspend)
++ driver->suspend (&dev->gadget);
++ enable_resume_mask_suspend ();
++ }
++
++ UDC_flip(Ser0UDCSR, status); // clear all pending sources
++
++ if (status & UDCSR_EIR)
++ PRINTKD("%s: processing ep0_int_hndlr\n",__FUNCTION__);
++ ep0_int_hndlr();
++
++ if (status & UDCSR_RIR) {
++ PRINTKD("%s: processing ep1_int_hndlr\n",__FUNCTION__);
++ ep1_int_hndlr(status);
++ }
++ if (status & UDCSR_TIR) {
++ PRINTKD("%s: processing ep2_int_hndlr\n",__FUNCTION__);
++ ep2_int_hndlr(status);
++ }
++
++ return IRQ_HANDLED; // NCB
++}
++
++/* soft_connect_hook ()
++ * Some devices have platform-specific circuitry to make USB
++ * not seem to be plugged in, even when it is. This allows
++ * software to control when a device 'appears' on the USB bus
++ * (after Linux has booted and this driver has loaded, for
++ * example). If you have such a circuit, control it here.
++ */
++#ifdef CONFIG_SA1100_EXTENEX1
++static void soft_connect_hook(int enable)
++{
++ if (machine_is_extenex1 ()) {
++ if (enable) {
++ PPDR |= PPC_USB_SOFT_CON;
++ PPSR |= PPC_USB_SOFT_CON;
++ } else {
++ PPSR &= ~PPC_USB_SOFT_CON;
++ PPDR &= ~PPC_USB_SOFT_CON;
++ }
++ }
++}
++#elif defined(CONFIG_SA1100_BALLOON)
++static void soft_connect_hook(int enable)
++{
++ if (machine_is_balloon()) {
++ if (enable)
++ balloon_cpld_control(BALLOON_UDC_DISCONNECT, 0);
++ else
++ balloon_cpld_control(BALLOON_UDC_DISCONNECT, 1);
++ }
++}
++#elif defined(CONFIG_SA1100_COLLIE)
++extern struct platform_device colliescoop_device;
++
++static void soft_connect_hook(int enable)
++{
++ if(enable)
++ set_scoop_gpio(&colliescoop_device.dev, COLLIE_SCP_LB_VOL_CHG);
++ else
++ reset_scoop_gpio(&colliescoop_device.dev, COLLIE_SCP_LB_VOL_CHG);
++}
++#else
++#define soft_connect_hook(x) do { } while (0);
++#endif
++
++/* "function" sysfs attribute */
++static ssize_t
++show_function(struct device *_dev, struct device_attribute *attr, char *buf)
++{
++ struct sa1100_udc *dev = dev_get_drvdata (_dev);
++
++ if (!dev->driver
++ || !dev->driver->function
++ || strlen(dev->driver->function) > PAGE_SIZE)
++ return 0;
++ return scnprintf (buf, PAGE_SIZE, "%s\n", dev->driver->function);
++}
++static DEVICE_ATTR(function, S_IRUGO, show_function, NULL);
++
++/* disable the UDC at the source */
++static void udc_disable(struct sa1100_udc *dev)
++{
++ soft_connect_hook(0);
++ UDC_set(Ser0UDCCR, UDCCR_UDD);
++ dev->gadget.speed = USB_SPEED_UNKNOWN;
++ ep0_idle(dev);
++}
++
++static void udc_reinit(struct sa1100_udc *dev)
++{
++ u32 i;
++
++ /* Initialize the gadget controller data structure */
++ INIT_LIST_HEAD(&dev->gadget.ep_list);
++ INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
++ ep0_idle(dev);
++ for ( i = 0 ; i < 3 ; i++) {
++ struct sa1100_ep *ep = &dev->ep[i];
++ if (i != 0)
++ list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list);
++ ep->desc = NULL;
++ ep->stopped = 0;
++ INIT_LIST_HEAD(&ep->queue);
++ }
++}
++
++/* enable the udc at the source */
++static void udc_enable(struct sa1100_udc *dev)
++{
++ UDC_clear (Ser0UDCCR, UDCCR_UDD);
++ ep0_idle(dev);
++}
++
++static void ep0_start(struct sa1100_udc *dev)
++{
++ udc_enable(dev);
++ udelay(100);
++
++ /* clear stall - receiver seems to start stalled? 19Jan01ww */
++ /* also clear other stuff just to be thurough 22Feb01ww */
++ UDC_clear(Ser0UDCCS1, UDCCS1_FST | UDCCS1_RPE | UDCCS1_RPC );
++ UDC_clear(Ser0UDCCS2, UDCCS2_FST | UDCCS2_TPE | UDCCS2_TPC );
++
++ /* mask everything */
++ Ser0UDCCR = 0xFC;
++
++ /* flush DMA and fire through some -EAGAINs */
++ ep1_init(dev->ep[1].dmaregs);
++ ep2_init(dev->ep[2].dmaregs);
++
++ /* enable any platform specific hardware */
++ soft_connect_hook(1);
++
++ /* clear all top-level sources */
++ Ser0UDCSR = UDCSR_RSTIR | UDCSR_RESIR | UDCSR_EIR |
++ UDCSR_RIR | UDCSR_TIR | UDCSR_SUSIR ;
++
++ /* EXERIMENT - a short line in the spec says toggling this
++ * bit diddles the internal state machine in the udc to
++ * expect a suspend
++ */
++ Ser0UDCCR |= UDCCR_RESIM;
++ /* END EXPERIMENT 10Feb01ww */
++
++ /* enable any platform specific hardware */
++ soft_connect_hook(1);
++
++ /* Enable interrupts. If you are unplugged you will immediately
++ * get a suspend interrupt. If you are plugged and have a soft
++ * connect-circuit, you will get a reset. If you are plugged
++ * without a soft-connect, I think you also get suspend. In short,
++ * start with suspend masked and everything else enabled
++ */
++ UDC_write(Ser0UDCCR, UDCCR_SUSIM);
++}
++
++
++/* when a driver is successfully registered, it will receive
++ * control requests including set_configuration(), which enables
++ * non-control requests. then usb traffic follows until a
++ * disconnect is reported. then a host may connect again, or
++ * the driver might get unbound.
++ */
++int usb_gadget_register_driver(struct usb_gadget_driver *driver)
++{
++ struct sa1100_udc *dev = the_controller;
++ int retval;
++
++ if (!driver || !driver->bind || !driver->setup)
++ return -EINVAL;
++ if (!dev)
++ return -ENODEV;
++ if (dev->driver)
++ return -EBUSY;
++
++ /* hook up the driver ... */
++ dev->driver = driver;
++ dev->gadget.dev.driver = &driver->driver;
++
++ retval = device_add(&dev->gadget.dev);
++ if (retval != 0) {
++ printk(KERN_ERR "Error in device_add() : %d\n",retval);
++ goto register_error;
++ }
++
++ retval = driver->bind (&dev->gadget);
++ if (retval != 0) {
++ DEBUG(dev, "bind to driver %s --> %d\n",
++ driver->driver.name, retval);
++ device_del(&dev->gadget.dev);
++ goto register_error;
++ }
++
++ retval = device_create_file(dev->dev, &dev_attr_function);
++
++ /* ... then enable host detection and ep0; and we're ready
++ * for set_configuration as well as eventual disconnect.
++ */
++ ep0_start(dev);
++
++ DEBUG(dev, "%s ready\n", driver->driver.name);
++
++ return 0;
++
++register_error:
++ dev->driver = NULL;
++ dev->gadget.dev.driver = NULL;
++ return retval;
++}
++EXPORT_SYMBOL (usb_gadget_register_driver);
++
++static void
++stop_activity(struct sa1100_udc *dev, struct usb_gadget_driver *driver)
++{
++ int i;
++
++ /* don't disconnect if it's not connected */
++ if (dev->gadget.speed == USB_SPEED_UNKNOWN)
++ driver = NULL;
++ dev->gadget.speed = USB_SPEED_UNKNOWN;
++
++ /* mask everything */
++ Ser0UDCCR = 0xFC;
++
++ /* stop hardware; prevent new request submissions;
++ * and kill any outstanding requests.
++ */
++ for (i = 0; i < 3; i++) {
++ struct sa1100_ep *ep = &dev->ep[i];
++ ep->stopped = 1;
++ nuke(ep, -ESHUTDOWN);
++ }
++ udc_disable (dev);
++
++ /* report disconnect; the driver is already quiesced */
++ if (driver)
++ driver->disconnect(&dev->gadget);
++
++ /* re-init driver-visible data structures */
++ udc_reinit(dev);
++}
++
++int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
++{
++ struct sa1100_udc *dev = the_controller;
++
++ if (!dev)
++ return -ENODEV;
++ if (!driver || driver != dev->driver)
++ return -EINVAL;
++
++ local_irq_disable();
++ stop_activity (dev, driver);
++ local_irq_enable();
++ if (driver->unbind)
++ driver->unbind(&dev->gadget);
++ dev->driver = 0;
++
++ device_del(&dev->gadget.dev);
++ device_remove_file(dev->dev, &dev_attr_function);
++
++ DEBUG (dev, "unregistered driver '%s'\n", driver->driver.name);
++ return 0;
++}
++EXPORT_SYMBOL (usb_gadget_unregister_driver);
++
++
++/*-------------------------------------------------------------------------*/
++
++/*-------------------------------------------------------------------------*/
++
++//////////////////////////////////////////////////////////////////////////////
++// Proc Filesystem Support
++//////////////////////////////////////////////////////////////////////////////
++
++#if CONFIG_PROC_FS
++
++#define SAY(fmt,args...) p += sprintf (p, fmt, ## args)
++#define SAYV(num) p += sprintf (p, num_fmt, "Value", num)
++#define SAYC(label,yn) p += sprintf (p, yn_fmt, label, yn)
++#define SAYS(label,v) p += sprintf (p, cnt_fmt, label, v)
++
++static int usbctl_read_proc (char *page, char **start, off_t off,
++ int count, int *eof, void *data)
++{
++ const char * num_fmt = "%25.25s: %8.8lX\n";
++ const char * cnt_fmt = "%25.25s: %lu\n";
++ const char * yn_fmt = "%25.25s: %s\n";
++ const char * yes = "YES";
++ const char * no = "NO";
++ unsigned long v;
++ char * p = page;
++ int len;
++
++ SAY ("SA1100 USB Controller Core\n");
++
++ SAYS ("ep0 bytes read", usbd_info.stats.ep0_bytes_read);
++ SAYS ("ep0 bytes written", usbd_info.stats.ep0_bytes_written);
++ SAYS ("ep0 FIFO read failures", usbd_info.stats.ep0_fifo_read_failures);
++ SAYS ("ep0 FIFO write failures", usbd_info.stats.ep0_fifo_write_failures);
++
++ SAY ("\n");
++
++ v = Ser0UDCAR;
++ SAY ("%25.25s: 0x%8.8lX - %ld\n", "Address Register", v, v);
++ v = Ser0UDCIMP;
++ SAY ("%25.25s: %ld (%8.8lX)\n", "IN max packet size", v+1, v);
++ v = Ser0UDCOMP;
++ SAY ("%25.25s: %ld (%8.8lX)\n", "OUT max packet size", v+1, v);
++
++ v = Ser0UDCCR;
++ SAY ("\nUDC Mask Register\n");
++ SAYV (v);
++ SAYC ("UDC Active", (v & UDCCR_UDA) ? yes : no);
++ SAYC ("Suspend interrupts masked", (v & UDCCR_SUSIM) ? yes : no);
++ SAYC ("Resume interrupts masked", (v & UDCCR_RESIM) ? yes : no);
++ SAYC ("Reset interrupts masked", (v & UDCCR_REM) ? yes : no);
++
++ v = Ser0UDCSR;
++ SAY ("\nUDC Interrupt Request Register\n");
++ SAYV (v);
++ SAYC ("Reset pending", (v & UDCSR_RSTIR) ? yes : no);
++ SAYC ("Suspend pending", (v & UDCSR_SUSIR) ? yes : no);
++ SAYC ("Resume pending", (v & UDCSR_RESIR) ? yes : no);
++ SAYC ("ep0 pending", (v & UDCSR_EIR) ? yes : no);
++ SAYC ("receiver pending", (v & UDCSR_RIR) ? yes : no);
++ SAYC ("tramsitter pending", (v & UDCSR_TIR) ? yes : no);
++
++#ifdef CONFIG_SA1100_EXTENEX1
++ SAYC ("\nSoft connect", (PPSR & PPC_USB_SOFT_CON) ? "Visible" : "Hidden");
++#endif
++
++#if 1
++ SAY ("\nDMA Tx registers\n");
++ {
++ dma_regs_t *r=the_controller->ep[2].dmaregs;
++ SAY (" DDAR");
++ SAYV(r->DDAR);
++ SAY (" DCSR");
++ SAYV(r->RdDCSR);
++ SAY (" DBSA (address buf A) ");
++ SAYV(r->DBSA);
++ SAY (" DBTA (transfer count A) ");
++ SAYV(r->DBTA);
++ SAY (" DBSB (address buf B) ");
++ SAYV(r->DBSB);
++ SAY (" DBTB (transfer count B) ");
++ SAYV(r->DBTB);
++
++ }
++ SAY ("\nDMA Rx registers\n");
++ {
++ dma_regs_t *r=the_controller->ep[1].dmaregs;
++ SAY (" DDAR");
++ SAYV(r->DDAR);
++ SAY (" DCSR");
++ SAYV(r->RdDCSR);
++ SAY (" DBSA (address buf A) ");
++ SAYV(r->DBSA);
++ SAY (" DBTA (transfer count A) ");
++ SAYV(r->DBTA);
++ SAY (" DBSB (address buf B) ");
++ SAYV(r->DBSB);
++ SAY (" DBTB (transfer count B) ");
++ SAYV(r->DBTB);
++
++ }
++#endif
++#if 1
++ v = Ser0UDCCS0;
++ SAY ("\nUDC Endpoint Zero Status Register\n");
++ SAYV (v);
++ SAYC ("Out Packet Ready", (v & UDCCS0_OPR) ? yes : no);
++ SAYC ("In Packet Ready", (v & UDCCS0_IPR) ? yes : no);
++ SAYC ("Sent Stall", (v & UDCCS0_SST) ? yes : no);
++ SAYC ("Force Stall", (v & UDCCS0_FST) ? yes : no);
++ SAYC ("Data End", (v & UDCCS0_DE) ? yes : no);
++ SAYC ("Data Setup End", (v & UDCCS0_SE) ? yes : no);
++ SAYC ("Serviced (SO)", (v & UDCCS0_SO) ? yes : no);
++
++ v = Ser0UDCCS1;
++ SAY ("\nUDC Receiver Status Register\n");
++ SAYV (v);
++ SAYC ("Receive Packet Complete", (v & UDCCS1_RPC) ? yes : no);
++ SAYC ("Sent Stall", (v & UDCCS1_SST) ? yes : no);
++ SAYC ("Force Stall", (v & UDCCS1_FST) ? yes : no);
++ SAYC ("Receive Packet Error", (v & UDCCS1_RPE) ? yes : no);
++ SAYC ("Receive FIFO not empty", (v & UDCCS1_RNE) ? yes : no);
++
++ v = Ser0UDCCS2;
++ SAY ("\nUDC Transmitter Status Register\n");
++ SAYV (v);
++ SAYC ("FIFO has < 8 of 16 chars", (v & UDCCS2_TFS) ? yes : no);
++ SAYC ("Transmit Packet Complete", (v & UDCCS2_TPC) ? yes : no);
++ SAYC ("Transmit FIFO underrun", (v & UDCCS2_TUR) ? yes : no);
++ SAYC ("Transmit Packet Error", (v & UDCCS2_TPE) ? yes : no);
++ SAYC ("Sent Stall", (v & UDCCS2_SST) ? yes : no);
++ SAYC ("Force Stall", (v & UDCCS2_FST) ? yes : no);
++#endif
++
++ len = (p - page) - off;
++ if (len < 0)
++ len = 0;
++ *eof = (len <=count) ? 1 : 0;
++ *start = page + off;
++ return len;
++}
++
++static inline void register_proc_entry (void)
++{
++ create_proc_read_entry (driver_name, 0, NULL,
++ usbctl_read_proc, NULL);
++}
++
++static inline void unregister_proc_entry (void)
++{
++ remove_proc_entry (driver_name, NULL);
++}
++
++#else
++
++#define register_proc_entry() do {} while (0)
++#define unregister_proc_entry() do {} while (0)
++
++#endif /* CONFIG_PROC_FS */
++
++/*-------------------------------------------------------------------------*/
++
++MODULE_DESCRIPTION ("sa1100_udc");
++MODULE_AUTHOR ("Various");
++MODULE_LICENSE ("GPL");
++
++static struct sa1100_udc memory = {
++ .gadget = {
++ .ops = &sa1100_ops,
++ .ep0 = &memory.ep[0].ep,
++ .name = driver_name,
++ .dev = {
++ .bus_id = "gadget",
++ },
++ },
++
++ /* control endpoint */
++ .ep[0] = {
++ .ep = {
++ .name = ep0name,
++ .ops = &sa1100_ep_ops,
++ .maxpacket = EP0_FIFO_SIZE,
++ },
++ .dev = &memory,
++ },
++
++ /* first group of endpoints */
++ .ep[1] = {
++ .ep = {
++ .name = "ep1out-bulk",
++ .ops = &sa1100_ep_ops,
++ .maxpacket = BULK_FIFO_SIZE,
++ },
++ .dev = &memory,
++ },
++ .ep[2] = {
++ .ep = {
++ .name = "ep2in-bulk",
++ .ops = &sa1100_ep_ops,
++ .maxpacket = BULK_FIFO_SIZE,
++ },
++ .dev = &memory,
++ }
++};
++
++static int __init sa1100_udc_probe(struct device *_dev)
++{
++ struct sa1100_udc *dev = &memory;
++ int retval = 0;
++
++ /* setup dev */
++ dev->dev = _dev;
++// dev->mach = _dev->platform_data;
++
++ device_initialize(&dev->gadget.dev);
++ dev->gadget.dev.parent = _dev;
++ dev->gadget.dev.dma_mask = _dev->dma_mask;
++
++ the_controller = dev;
++ dev_set_drvdata(_dev, dev);
++
++ /* controller stays disabled until gadget driver is bound */
++ udc_disable(dev);
++ udc_reinit(dev);
++
++// spin_lock_init(&the_udc.lock);
++ register_proc_entry();
++
++ /* setup dma channels and IRQ */
++ retval = sa1100_request_dma(DMA_Ser0UDCRd, "USB receive",
++ NULL, NULL, &dev->ep[1].dmaregs);
++ if (retval) {
++ ERROR(dev, "couldn't get rx dma, err %d\n", retval);
++ goto err_rx_dma;
++ }
++ retval = sa1100_request_dma(DMA_Ser0UDCWr, "USB transmit",
++ NULL, NULL, &dev->ep[2].dmaregs);
++ if (retval) {
++ ERROR(dev, "couldn't get tx dma, err %d\n", retval);
++ goto err_tx_dma;
++ }
++ retval = request_irq(IRQ_Ser0UDC, udc_int_hndlr, IRQF_DISABLED,
++ driver_name, dev);
++ if (retval) {
++ ERROR(dev, "couldn't get irq, err %d\n", retval);
++ goto err_irq;
++ }
++
++ INFO(dev, "initialized, rx %p tx %p irq %d\n",
++ dev->ep[1].dmaregs, dev->ep[2].dmaregs, IRQ_Ser0UDC);
++ return 0;
++
++err_irq:
++ sa1100_free_dma(dev->ep[2].dmaregs);
++ usbd_info.dmaregs_rx = 0;
++err_tx_dma:
++ sa1100_free_dma(dev->ep[1].dmaregs);
++ usbd_info.dmaregs_tx = 0;
++err_rx_dma:
++ return retval;
++}
++
++static int __exit sa1100_udc_remove(struct device *_dev)
++{
++ struct sa1100_udc *dev = dev_get_drvdata(_dev);
++
++ udc_disable(dev);
++ unregister_proc_entry();
++ usb_gadget_unregister_driver(dev->driver);
++ sa1100_free_dma(dev->ep[1].dmaregs);
++ sa1100_free_dma(dev->ep[2].dmaregs);
++ free_irq(IRQ_Ser0UDC, dev);
++ dev_set_drvdata(_dev,NULL);
++ the_controller = NULL;
++ return 0;
++}
++
++static struct device_driver udc_driver = {
++ .name = "sa11x0-udc",
++ .bus = &platform_bus_type,
++ .probe = sa1100_udc_probe,
++ .remove = __exit_p(sa1100_udc_remove),
++// .suspend = sa1100_udc_suspend,
++// .resume = sa1100_udc_resume,
++ .owner = THIS_MODULE,
++};
++
++static int __init udc_init(void)
++{
++ printk(KERN_INFO "%s: version %s\n", driver_name, DRIVER_VERSION);
++#ifdef NCB_DMA_FIX
++ send_buffer = (char*) kzalloc(SEND_BUFFER_SIZE, GFP_KERNEL | GFP_DMA );
++ receive_buffer = (char*) kzalloc(RECEIVE_BUFFER_SIZE, GFP_KERNEL | GFP_DMA );
++#endif
++ return driver_register(&udc_driver);
++}
++module_init(udc_init);
++
++static void __exit udc_exit(void)
++{
++#ifdef NCB_DMA_FIX
++ if (send_buffer) {
++ kfree(send_buffer);
++ send_buffer = NULL;
++ }
++ if (receive_buffer) {
++ kfree(receive_buffer);
++ receive_buffer = NULL;
++ }
++#endif
++ driver_unregister(&udc_driver);
++}
++module_exit(udc_exit);
+diff --git a/drivers/usb/gadget/sa1100_udc.h b/drivers/usb/gadget/sa1100_udc.h
+new file mode 100644
+index 0000000..acb665a
+--- /dev/null
++++ b/drivers/usb/gadget/sa1100_udc.h
+@@ -0,0 +1,94 @@
++/*
++ * internals of "new style" UDC controller
++ * <linux/usb_gadget.h> replaces ARM-specific "sa1100_usb.h".
++ */
++
++struct sa1100_ep {
++ struct usb_ep ep;
++ struct sa1100_udc *dev;
++ //unsigned long irqs;
++
++ const struct usb_endpoint_descriptor *desc;
++ struct list_head queue;
++ dma_regs_t *dmaregs;
++ unsigned stopped : 1;
++};
++
++struct sa1100_request {
++ struct usb_request req;
++ struct list_head queue;
++// NCB unsigned mapped : 1;
++};
++
++enum ep0_state {
++ EP0_IDLE,
++ EP0_IN_DATA_PHASE,
++ EP0_OUT_DATA_PHASE,
++ EP0_END_XFER,
++ EP0_STALL,
++};
++
++#define EP0_FIFO_SIZE ((unsigned)8)
++#define BULK_FIFO_SIZE ((unsigned)64)
++//#define ISO_FIFO_SIZE ((unsigned)256)
++//#define INT_FIFO_SIZE ((unsigned)8)
++
++struct udc_stats {
++ struct ep0stats {
++ unsigned long ops;
++ unsigned long bytes;
++ } read, write;
++ unsigned long irqs;
++};
++
++struct sa1100_udc {
++ struct usb_gadget gadget;
++ struct usb_gadget_driver *driver;
++ struct device *dev;
++ enum ep0_state ep0state;
++ struct udc_stats stats;
++// NCB spinlock_t lock;
++// NCB dma_regs_t *dmaregs_tx, *dmaregs_rx;
++ unsigned got_irq : 1,
++ vbus : 1,
++ pullup : 1,
++ has_cfr : 1,
++ req_pending : 1,
++ req_std : 1,
++ req_config : 1;
++ struct timer_list timer;
++ u64 dma_mask;
++ unsigned char address;
++ struct sa1100_ep ep[3];
++ int ep0_req_len;
++};
++
++/*-------------------------------------------------------------------------*/
++
++#define xprintk(dev,level,fmt,args...) \
++ printk(level "%s: " fmt , driver_name , ## args)
++
++#ifdef DEBUG
++#undef DEBUG
++#define DEBUG(dev,fmt,args...) \
++ xprintk(dev , KERN_DEBUG , fmt , ## args)
++#else
++#define DEBUG(dev,fmt,args...) \
++ do { } while (0)
++#endif /* DEBUG */
++
++#ifdef VERBOSE
++#define VDEBUG DEBUG
++#else
++#define VDEBUG(dev,fmt,args...) \
++ do { } while (0)
++#endif /* VERBOSE */
++
++#define ERROR(dev,fmt,args...) \
++ xprintk(dev , KERN_ERR , fmt , ## args)
++#define WARN(dev,fmt,args...) \
++ xprintk(dev , KERN_WARNING , fmt , ## args)
++#define INFO(dev,fmt,args...) \
++ xprintk(dev , KERN_INFO , fmt , ## args)
++
++/*-------------------------------------------------------------------------*/
+diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c
+index fa019fa..b3699af 100644
+--- a/drivers/usb/gadget/serial.c
++++ b/drivers/usb/gadget/serial.c
+@@ -1,15 +1,9 @@
+ /*
+- * g_serial.c -- USB gadget serial driver
++ * serial.c -- USB gadget serial driver
+ *
+- * Copyright 2003 (C) Al Borchers (alborchers@steinerpoint.com)
+- *
+- * This code is based in part on the Gadget Zero driver, which
+- * is Copyright (C) 2003 by David Brownell, all rights reserved.
+- *
+- * This code also borrows from usbserial.c, which is
+- * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com)
+- * Copyright (C) 2000 Peter Berger (pberger@brimson.com)
+- * Copyright (C) 2000 Al Borchers (alborchers@steinerpoint.com)
++ * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
++ * Copyright (C) 2008 by David Brownell
++ * Copyright (C) 2008 by Nokia Corporation
+ *
+ * This software is distributed under the terms of the GNU General
+ * Public License ("GPL") as published by the Free Software Foundation,
+@@ -22,2254 +16,237 @@
+ #include <linux/tty.h>
+ #include <linux/tty_flip.h>
+
+-#include <linux/usb/ch9.h>
+-#include <linux/usb/cdc.h>
+-#include <linux/usb/gadget.h>
+-
++#include "u_serial.h"
+ #include "gadget_chips.h"
+
+
+ /* Defines */
+
+-#define GS_VERSION_STR "v2.2"
+-#define GS_VERSION_NUM 0x2200
++#define GS_VERSION_STR "v2.4"
++#define GS_VERSION_NUM 0x2400
+
+ #define GS_LONG_NAME "Gadget Serial"
+-#define GS_SHORT_NAME "g_serial"
+-
+-#define GS_MAJOR 127
+-#define GS_MINOR_START 0
+-
+-/* REVISIT only one port is supported for now;
+- * see gs_{send,recv}_packet() ... no multiplexing,
+- * and no support for multiple ACM devices.
+- */
+-#define GS_NUM_PORTS 1
+-
+-#define GS_NUM_CONFIGS 1
+-#define GS_NO_CONFIG_ID 0
+-#define GS_BULK_CONFIG_ID 1
+-#define GS_ACM_CONFIG_ID 2
+-
+-#define GS_MAX_NUM_INTERFACES 2
+-#define GS_BULK_INTERFACE_ID 0
+-#define GS_CONTROL_INTERFACE_ID 0
+-#define GS_DATA_INTERFACE_ID 1
+-
+-#define GS_MAX_DESC_LEN 256
+-
+-#define GS_DEFAULT_READ_Q_SIZE 32
+-#define GS_DEFAULT_WRITE_Q_SIZE 32
+-
+-#define GS_DEFAULT_WRITE_BUF_SIZE 8192
+-#define GS_TMP_BUF_SIZE 8192
+-
+-#define GS_CLOSE_TIMEOUT 15
+-
+-#define GS_DEFAULT_USE_ACM 0
+-
+-/* 9600-8-N-1 ... matches init_termios.c_cflag and defaults
+- * expected by "usbser.sys" on MS-Windows.
+- */
+-#define GS_DEFAULT_DTE_RATE 9600
+-#define GS_DEFAULT_DATA_BITS 8
+-#define GS_DEFAULT_PARITY USB_CDC_NO_PARITY
+-#define GS_DEFAULT_CHAR_FORMAT USB_CDC_1_STOP_BITS
+-
+-/* maxpacket and other transfer characteristics vary by speed. */
+-static inline struct usb_endpoint_descriptor *
+-choose_ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *hs,
+- struct usb_endpoint_descriptor *fs)
+-{
+- if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
+- return hs;
+- return fs;
+-}
+-
+-
+-/* debug settings */
+-#ifdef DEBUG
+-static int debug = 1;
+-#else
+-#define debug 0
+-#endif
+-
+-#define gs_debug(format, arg...) \
+- do { if (debug) pr_debug(format, ## arg); } while (0)
+-#define gs_debug_level(level, format, arg...) \
+- do { if (debug >= level) pr_debug(format, ## arg); } while (0)
++#define GS_VERSION_NAME GS_LONG_NAME " " GS_VERSION_STR
+
++/*-------------------------------------------------------------------------*/
+
+ /* Thanks to NetChip Technologies for donating this product ID.
+- *
+- * DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
+- * Instead: allocate your own, using normal USB-IF procedures.
+- */
++*
++* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
++* Instead: allocate your own, using normal USB-IF procedures.
++*/
+ #define GS_VENDOR_ID 0x0525 /* NetChip */
+ #define GS_PRODUCT_ID 0xa4a6 /* Linux-USB Serial Gadget */
+ #define GS_CDC_PRODUCT_ID 0xa4a7 /* ... as CDC-ACM */
+
+-#define GS_LOG2_NOTIFY_INTERVAL 5 /* 1 << 5 == 32 msec */
+-#define GS_NOTIFY_MAXPACKET 8
++/* string IDs are assigned dynamically */
+
++#define STRING_MANUFACTURER_IDX 0
++#define STRING_PRODUCT_IDX 1
++#define STRING_DESCRIPTION_IDX 2
+
+-/* circular buffer */
+-struct gs_buf {
+- unsigned int buf_size;
+- char *buf_buf;
+- char *buf_get;
+- char *buf_put;
+-};
++static char manufacturer[50];
+
+-/* the port structure holds info for each port, one for each minor number */
+-struct gs_port {
+- struct gs_dev *port_dev; /* pointer to device struct */
+- struct tty_struct *port_tty; /* pointer to tty struct */
+- spinlock_t port_lock;
+- int port_num;
+- int port_open_count;
+- int port_in_use; /* open/close in progress */
+- wait_queue_head_t port_write_wait;/* waiting to write */
+- struct gs_buf *port_write_buf;
+- struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */
+- u16 port_handshake_bits;
+-#define RS232_RTS (1 << 1)
+-#define RS232_DTE (1 << 0)
++static struct usb_string strings_dev[] = {
++ [STRING_MANUFACTURER_IDX].s = manufacturer,
++ [STRING_PRODUCT_IDX].s = GS_VERSION_NAME,
++ [STRING_DESCRIPTION_IDX].s = NULL /* updated; f(use_acm) */,
++ { } /* end of list */
+ };
+
+-/* the device structure holds info for the USB device */
+-struct gs_dev {
+- struct usb_gadget *dev_gadget; /* gadget device pointer */
+- spinlock_t dev_lock; /* lock for set/reset config */
+- int dev_config; /* configuration number */
+- struct usb_ep *dev_notify_ep; /* address of notify endpoint */
+- struct usb_ep *dev_in_ep; /* address of in endpoint */
+- struct usb_ep *dev_out_ep; /* address of out endpoint */
+- struct usb_endpoint_descriptor /* descriptor of notify ep */
+- *dev_notify_ep_desc;
+- struct usb_endpoint_descriptor /* descriptor of in endpoint */
+- *dev_in_ep_desc;
+- struct usb_endpoint_descriptor /* descriptor of out endpoint */
+- *dev_out_ep_desc;
+- struct usb_request *dev_ctrl_req; /* control request */
+- struct list_head dev_req_list; /* list of write requests */
+- int dev_sched_port; /* round robin port scheduled */
+- struct gs_port *dev_port[GS_NUM_PORTS]; /* the ports */
++static struct usb_gadget_strings stringtab_dev = {
++ .language = 0x0409, /* en-us */
++ .strings = strings_dev,
+ };
+
+-
+-/* Functions */
+-
+-/* tty driver internals */
+-static int gs_send(struct gs_dev *dev);
+-static int gs_send_packet(struct gs_dev *dev, char *packet,
+- unsigned int size);
+-static int gs_recv_packet(struct gs_dev *dev, char *packet,
+- unsigned int size);
+-static void gs_read_complete(struct usb_ep *ep, struct usb_request *req);
+-static void gs_write_complete(struct usb_ep *ep, struct usb_request *req);
+-
+-/* gadget driver internals */
+-static int gs_set_config(struct gs_dev *dev, unsigned config);
+-static void gs_reset_config(struct gs_dev *dev);
+-static int gs_build_config_buf(u8 *buf, struct usb_gadget *g,
+- u8 type, unsigned int index, int is_otg);
+-
+-static struct usb_request *gs_alloc_req(struct usb_ep *ep, unsigned int len,
+- gfp_t kmalloc_flags);
+-static void gs_free_req(struct usb_ep *ep, struct usb_request *req);
+-
+-static int gs_alloc_ports(struct gs_dev *dev, gfp_t kmalloc_flags);
+-static void gs_free_ports(struct gs_dev *dev);
+-
+-/* circular buffer */
+-static struct gs_buf *gs_buf_alloc(unsigned int size, gfp_t kmalloc_flags);
+-static void gs_buf_free(struct gs_buf *gb);
+-static void gs_buf_clear(struct gs_buf *gb);
+-static unsigned int gs_buf_data_avail(struct gs_buf *gb);
+-static unsigned int gs_buf_space_avail(struct gs_buf *gb);
+-static unsigned int gs_buf_put(struct gs_buf *gb, const char *buf,
+- unsigned int count);
+-static unsigned int gs_buf_get(struct gs_buf *gb, char *buf,
+- unsigned int count);
+-
+-
+-/* Globals */
+-
+-static struct gs_dev *gs_device;
+-
+-static struct mutex gs_open_close_lock[GS_NUM_PORTS];
+-
+-
+-/*-------------------------------------------------------------------------*/
+-
+-/* USB descriptors */
+-
+-#define GS_MANUFACTURER_STR_ID 1
+-#define GS_PRODUCT_STR_ID 2
+-#define GS_SERIAL_STR_ID 3
+-#define GS_BULK_CONFIG_STR_ID 4
+-#define GS_ACM_CONFIG_STR_ID 5
+-#define GS_CONTROL_STR_ID 6
+-#define GS_DATA_STR_ID 7
+-
+-/* static strings, in UTF-8 */
+-static char manufacturer[50];
+-static struct usb_string gs_strings[] = {
+- { GS_MANUFACTURER_STR_ID, manufacturer },
+- { GS_PRODUCT_STR_ID, GS_LONG_NAME },
+- { GS_BULK_CONFIG_STR_ID, "Gadget Serial Bulk" },
+- { GS_ACM_CONFIG_STR_ID, "Gadget Serial CDC ACM" },
+- { GS_CONTROL_STR_ID, "Gadget Serial Control" },
+- { GS_DATA_STR_ID, "Gadget Serial Data" },
+- { } /* end of list */
+-};
+-
+-static struct usb_gadget_strings gs_string_table = {
+- .language = 0x0409, /* en-us */
+- .strings = gs_strings,
++static struct usb_gadget_strings *dev_strings[] = {
++ &stringtab_dev,
++ NULL,
+ };
+
+-static struct usb_device_descriptor gs_device_desc = {
++static struct usb_device_descriptor device_desc = {
+ .bLength = USB_DT_DEVICE_SIZE,
+ .bDescriptorType = USB_DT_DEVICE,
+ .bcdUSB = __constant_cpu_to_le16(0x0200),
++ /* .bDeviceClass = f(use_acm) */
+ .bDeviceSubClass = 0,
+ .bDeviceProtocol = 0,
++ /* .bMaxPacketSize0 = f(hardware) */
+ .idVendor = __constant_cpu_to_le16(GS_VENDOR_ID),
+- .idProduct = __constant_cpu_to_le16(GS_PRODUCT_ID),
+- .iManufacturer = GS_MANUFACTURER_STR_ID,
+- .iProduct = GS_PRODUCT_STR_ID,
+- .bNumConfigurations = GS_NUM_CONFIGS,
++ /* .idProduct = f(use_acm) */
++ /* .bcdDevice = f(hardware) */
++ /* .iManufacturer = DYNAMIC */
++ /* .iProduct = DYNAMIC */
++ .bNumConfigurations = 1,
+ };
+
+-static struct usb_otg_descriptor gs_otg_descriptor = {
+- .bLength = sizeof(gs_otg_descriptor),
++static struct usb_otg_descriptor otg_descriptor = {
++ .bLength = sizeof otg_descriptor,
+ .bDescriptorType = USB_DT_OTG,
+- .bmAttributes = USB_OTG_SRP,
+-};
+-
+-static struct usb_config_descriptor gs_bulk_config_desc = {
+- .bLength = USB_DT_CONFIG_SIZE,
+- .bDescriptorType = USB_DT_CONFIG,
+- /* .wTotalLength computed dynamically */
+- .bNumInterfaces = 1,
+- .bConfigurationValue = GS_BULK_CONFIG_ID,
+- .iConfiguration = GS_BULK_CONFIG_STR_ID,
+- .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
+- .bMaxPower = 1,
+-};
+-
+-static struct usb_config_descriptor gs_acm_config_desc = {
+- .bLength = USB_DT_CONFIG_SIZE,
+- .bDescriptorType = USB_DT_CONFIG,
+- /* .wTotalLength computed dynamically */
+- .bNumInterfaces = 2,
+- .bConfigurationValue = GS_ACM_CONFIG_ID,
+- .iConfiguration = GS_ACM_CONFIG_STR_ID,
+- .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
+- .bMaxPower = 1,
+-};
+-
+-static const struct usb_interface_descriptor gs_bulk_interface_desc = {
+- .bLength = USB_DT_INTERFACE_SIZE,
+- .bDescriptorType = USB_DT_INTERFACE,
+- .bInterfaceNumber = GS_BULK_INTERFACE_ID,
+- .bNumEndpoints = 2,
+- .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+- .bInterfaceSubClass = 0,
+- .bInterfaceProtocol = 0,
+- .iInterface = GS_DATA_STR_ID,
+-};
+-
+-static const struct usb_interface_descriptor gs_control_interface_desc = {
+- .bLength = USB_DT_INTERFACE_SIZE,
+- .bDescriptorType = USB_DT_INTERFACE,
+- .bInterfaceNumber = GS_CONTROL_INTERFACE_ID,
+- .bNumEndpoints = 1,
+- .bInterfaceClass = USB_CLASS_COMM,
+- .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
+- .bInterfaceProtocol = USB_CDC_ACM_PROTO_AT_V25TER,
+- .iInterface = GS_CONTROL_STR_ID,
+-};
+-
+-static const struct usb_interface_descriptor gs_data_interface_desc = {
+- .bLength = USB_DT_INTERFACE_SIZE,
+- .bDescriptorType = USB_DT_INTERFACE,
+- .bInterfaceNumber = GS_DATA_INTERFACE_ID,
+- .bNumEndpoints = 2,
+- .bInterfaceClass = USB_CLASS_CDC_DATA,
+- .bInterfaceSubClass = 0,
+- .bInterfaceProtocol = 0,
+- .iInterface = GS_DATA_STR_ID,
+-};
+-
+-static const struct usb_cdc_header_desc gs_header_desc = {
+- .bLength = sizeof(gs_header_desc),
+- .bDescriptorType = USB_DT_CS_INTERFACE,
+- .bDescriptorSubType = USB_CDC_HEADER_TYPE,
+- .bcdCDC = __constant_cpu_to_le16(0x0110),
+-};
+-
+-static const struct usb_cdc_call_mgmt_descriptor gs_call_mgmt_descriptor = {
+- .bLength = sizeof(gs_call_mgmt_descriptor),
+- .bDescriptorType = USB_DT_CS_INTERFACE,
+- .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE,
+- .bmCapabilities = 0,
+- .bDataInterface = 1, /* index of data interface */
+-};
+-
+-static struct usb_cdc_acm_descriptor gs_acm_descriptor = {
+- .bLength = sizeof(gs_acm_descriptor),
+- .bDescriptorType = USB_DT_CS_INTERFACE,
+- .bDescriptorSubType = USB_CDC_ACM_TYPE,
+- .bmCapabilities = (1 << 1),
+-};
+-
+-static const struct usb_cdc_union_desc gs_union_desc = {
+- .bLength = sizeof(gs_union_desc),
+- .bDescriptorType = USB_DT_CS_INTERFACE,
+- .bDescriptorSubType = USB_CDC_UNION_TYPE,
+- .bMasterInterface0 = 0, /* index of control interface */
+- .bSlaveInterface0 = 1, /* index of data interface */
+-};
+-
+-static struct usb_endpoint_descriptor gs_fullspeed_notify_desc = {
+- .bLength = USB_DT_ENDPOINT_SIZE,
+- .bDescriptorType = USB_DT_ENDPOINT,
+- .bEndpointAddress = USB_DIR_IN,
+- .bmAttributes = USB_ENDPOINT_XFER_INT,
+- .wMaxPacketSize = __constant_cpu_to_le16(GS_NOTIFY_MAXPACKET),
+- .bInterval = 1 << GS_LOG2_NOTIFY_INTERVAL,
+-};
+-
+-static struct usb_endpoint_descriptor gs_fullspeed_in_desc = {
+- .bLength = USB_DT_ENDPOINT_SIZE,
+- .bDescriptorType = USB_DT_ENDPOINT,
+- .bEndpointAddress = USB_DIR_IN,
+- .bmAttributes = USB_ENDPOINT_XFER_BULK,
+-};
+-
+-static struct usb_endpoint_descriptor gs_fullspeed_out_desc = {
+- .bLength = USB_DT_ENDPOINT_SIZE,
+- .bDescriptorType = USB_DT_ENDPOINT,
+- .bEndpointAddress = USB_DIR_OUT,
+- .bmAttributes = USB_ENDPOINT_XFER_BULK,
+-};
+-
+-static const struct usb_descriptor_header *gs_bulk_fullspeed_function[] = {
+- (struct usb_descriptor_header *) &gs_otg_descriptor,
+- (struct usb_descriptor_header *) &gs_bulk_interface_desc,
+- (struct usb_descriptor_header *) &gs_fullspeed_in_desc,
+- (struct usb_descriptor_header *) &gs_fullspeed_out_desc,
+- NULL,
+-};
+-
+-static const struct usb_descriptor_header *gs_acm_fullspeed_function[] = {
+- (struct usb_descriptor_header *) &gs_otg_descriptor,
+- (struct usb_descriptor_header *) &gs_control_interface_desc,
+- (struct usb_descriptor_header *) &gs_header_desc,
+- (struct usb_descriptor_header *) &gs_call_mgmt_descriptor,
+- (struct usb_descriptor_header *) &gs_acm_descriptor,
+- (struct usb_descriptor_header *) &gs_union_desc,
+- (struct usb_descriptor_header *) &gs_fullspeed_notify_desc,
+- (struct usb_descriptor_header *) &gs_data_interface_desc,
+- (struct usb_descriptor_header *) &gs_fullspeed_in_desc,
+- (struct usb_descriptor_header *) &gs_fullspeed_out_desc,
+- NULL,
+-};
+
+-static struct usb_endpoint_descriptor gs_highspeed_notify_desc = {
+- .bLength = USB_DT_ENDPOINT_SIZE,
+- .bDescriptorType = USB_DT_ENDPOINT,
+- .bEndpointAddress = USB_DIR_IN,
+- .bmAttributes = USB_ENDPOINT_XFER_INT,
+- .wMaxPacketSize = __constant_cpu_to_le16(GS_NOTIFY_MAXPACKET),
+- .bInterval = GS_LOG2_NOTIFY_INTERVAL+4,
+-};
+-
+-static struct usb_endpoint_descriptor gs_highspeed_in_desc = {
+- .bLength = USB_DT_ENDPOINT_SIZE,
+- .bDescriptorType = USB_DT_ENDPOINT,
+- .bmAttributes = USB_ENDPOINT_XFER_BULK,
+- .wMaxPacketSize = __constant_cpu_to_le16(512),
+-};
+-
+-static struct usb_endpoint_descriptor gs_highspeed_out_desc = {
+- .bLength = USB_DT_ENDPOINT_SIZE,
+- .bDescriptorType = USB_DT_ENDPOINT,
+- .bmAttributes = USB_ENDPOINT_XFER_BULK,
+- .wMaxPacketSize = __constant_cpu_to_le16(512),
+-};
+-
+-static struct usb_qualifier_descriptor gs_qualifier_desc = {
+- .bLength = sizeof(struct usb_qualifier_descriptor),
+- .bDescriptorType = USB_DT_DEVICE_QUALIFIER,
+- .bcdUSB = __constant_cpu_to_le16 (0x0200),
+- /* assumes ep0 uses the same value for both speeds ... */
+- .bNumConfigurations = GS_NUM_CONFIGS,
+-};
+-
+-static const struct usb_descriptor_header *gs_bulk_highspeed_function[] = {
+- (struct usb_descriptor_header *) &gs_otg_descriptor,
+- (struct usb_descriptor_header *) &gs_bulk_interface_desc,
+- (struct usb_descriptor_header *) &gs_highspeed_in_desc,
+- (struct usb_descriptor_header *) &gs_highspeed_out_desc,
+- NULL,
++ /* REVISIT SRP-only hardware is possible, although
++ * it would not be called "OTG" ...
++ */
++ .bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
+ };
+
+-static const struct usb_descriptor_header *gs_acm_highspeed_function[] = {
+- (struct usb_descriptor_header *) &gs_otg_descriptor,
+- (struct usb_descriptor_header *) &gs_control_interface_desc,
+- (struct usb_descriptor_header *) &gs_header_desc,
+- (struct usb_descriptor_header *) &gs_call_mgmt_descriptor,
+- (struct usb_descriptor_header *) &gs_acm_descriptor,
+- (struct usb_descriptor_header *) &gs_union_desc,
+- (struct usb_descriptor_header *) &gs_highspeed_notify_desc,
+- (struct usb_descriptor_header *) &gs_data_interface_desc,
+- (struct usb_descriptor_header *) &gs_highspeed_in_desc,
+- (struct usb_descriptor_header *) &gs_highspeed_out_desc,
++static const struct usb_descriptor_header *otg_desc[] = {
++ (struct usb_descriptor_header *) &otg_descriptor,
+ NULL,
+ };
+
+-
+ /*-------------------------------------------------------------------------*/
+
+ /* Module */
+-MODULE_DESCRIPTION(GS_LONG_NAME);
++MODULE_DESCRIPTION(GS_VERSION_NAME);
+ MODULE_AUTHOR("Al Borchers");
++MODULE_AUTHOR("David Brownell");
+ MODULE_LICENSE("GPL");
+
+-#ifdef DEBUG
+-module_param(debug, int, S_IRUGO|S_IWUSR);
+-MODULE_PARM_DESC(debug, "Enable debugging, 0=off, 1=on");
+-#endif
+-
+-static unsigned int read_q_size = GS_DEFAULT_READ_Q_SIZE;
+-module_param(read_q_size, uint, S_IRUGO);
+-MODULE_PARM_DESC(read_q_size, "Read request queue size, default=32");
+-
+-static unsigned int write_q_size = GS_DEFAULT_WRITE_Q_SIZE;
+-module_param(write_q_size, uint, S_IRUGO);
+-MODULE_PARM_DESC(write_q_size, "Write request queue size, default=32");
++static int use_acm = true;
++module_param(use_acm, bool, 0);
++MODULE_PARM_DESC(use_acm, "Use CDC ACM, default=yes");
+
+-static unsigned int write_buf_size = GS_DEFAULT_WRITE_BUF_SIZE;
+-module_param(write_buf_size, uint, S_IRUGO);
+-MODULE_PARM_DESC(write_buf_size, "Write buffer size, default=8192");
+-
+-static unsigned int use_acm = GS_DEFAULT_USE_ACM;
+-module_param(use_acm, uint, S_IRUGO);
+-MODULE_PARM_DESC(use_acm, "Use CDC ACM, 0=no, 1=yes, default=no");
++static unsigned n_ports = 1;
++module_param(n_ports, uint, 0);
++MODULE_PARM_DESC(n_ports, "number of ports to create, default=1");
+
+ /*-------------------------------------------------------------------------*/
+
+-/* TTY Driver */
+-
+-/*
+- * gs_open
+- */
+-static int gs_open(struct tty_struct *tty, struct file *file)
+-{
+- int port_num;
+- unsigned long flags;
+- struct gs_port *port;
+- struct gs_dev *dev;
+- struct gs_buf *buf;
+- struct mutex *mtx;
+- int ret;
+-
+- port_num = tty->index;
+-
+- gs_debug("gs_open: (%d,%p,%p)\n", port_num, tty, file);
+-
+- if (port_num < 0 || port_num >= GS_NUM_PORTS) {
+- pr_err("gs_open: (%d,%p,%p) invalid port number\n",
+- port_num, tty, file);
+- return -ENODEV;
+- }
+-
+- dev = gs_device;
+-
+- if (dev == NULL) {
+- pr_err("gs_open: (%d,%p,%p) NULL device pointer\n",
+- port_num, tty, file);
+- return -ENODEV;
+- }
+-
+- mtx = &gs_open_close_lock[port_num];
+- if (mutex_lock_interruptible(mtx)) {
+- pr_err("gs_open: (%d,%p,%p) interrupted waiting for mutex\n",
+- port_num, tty, file);
+- return -ERESTARTSYS;
+- }
+-
+- spin_lock_irqsave(&dev->dev_lock, flags);
+-
+- if (dev->dev_config == GS_NO_CONFIG_ID) {
+- pr_err("gs_open: (%d,%p,%p) device is not connected\n",
+- port_num, tty, file);
+- ret = -ENODEV;
+- goto exit_unlock_dev;
+- }
+-
+- port = dev->dev_port[port_num];
+-
+- if (port == NULL) {
+- pr_err("gs_open: (%d,%p,%p) NULL port pointer\n",
+- port_num, tty, file);
+- ret = -ENODEV;
+- goto exit_unlock_dev;
+- }
+-
+- spin_lock(&port->port_lock);
+- spin_unlock(&dev->dev_lock);
+-
+- if (port->port_dev == NULL) {
+- pr_err("gs_open: (%d,%p,%p) port disconnected (1)\n",
+- port_num, tty, file);
+- ret = -EIO;
+- goto exit_unlock_port;
+- }
+-
+- if (port->port_open_count > 0) {
+- ++port->port_open_count;
+- gs_debug("gs_open: (%d,%p,%p) already open\n",
+- port_num, tty, file);
+- ret = 0;
+- goto exit_unlock_port;
+- }
+-
+- tty->driver_data = NULL;
+-
+- /* mark port as in use, we can drop port lock and sleep if necessary */
+- port->port_in_use = 1;
+-
+- /* allocate write buffer on first open */
+- if (port->port_write_buf == NULL) {
+- spin_unlock_irqrestore(&port->port_lock, flags);
+- buf = gs_buf_alloc(write_buf_size, GFP_KERNEL);
+- spin_lock_irqsave(&port->port_lock, flags);
+-
+- /* might have been disconnected while asleep, check */
+- if (port->port_dev == NULL) {
+- pr_err("gs_open: (%d,%p,%p) port disconnected (2)\n",
+- port_num, tty, file);
+- port->port_in_use = 0;
+- ret = -EIO;
+- goto exit_unlock_port;
+- }
+-
+- if ((port->port_write_buf=buf) == NULL) {
+- pr_err("gs_open: (%d,%p,%p) cannot allocate "
+- "port write buffer\n",
+- port_num, tty, file);
+- port->port_in_use = 0;
+- ret = -ENOMEM;
+- goto exit_unlock_port;
+- }
+-
+- }
+-
+- /* wait for carrier detect (not implemented) */
+-
+- /* might have been disconnected while asleep, check */
+- if (port->port_dev == NULL) {
+- pr_err("gs_open: (%d,%p,%p) port disconnected (3)\n",
+- port_num, tty, file);
+- port->port_in_use = 0;
+- ret = -EIO;
+- goto exit_unlock_port;
+- }
+-
+- tty->driver_data = port;
+- port->port_tty = tty;
+- port->port_open_count = 1;
+- port->port_in_use = 0;
+-
+- gs_debug("gs_open: (%d,%p,%p) completed\n", port_num, tty, file);
+-
+- ret = 0;
+-
+-exit_unlock_port:
+- spin_unlock_irqrestore(&port->port_lock, flags);
+- mutex_unlock(mtx);
+- return ret;
+-
+-exit_unlock_dev:
+- spin_unlock_irqrestore(&dev->dev_lock, flags);
+- mutex_unlock(mtx);
+- return ret;
+-
+-}
+-
+-/*
+- * gs_close
+- */
+-
+-static int gs_write_finished_event_safely(struct gs_port *p)
+-{
+- int cond;
+-
+- spin_lock_irq(&(p)->port_lock);
+- cond = !(p)->port_dev || !gs_buf_data_avail((p)->port_write_buf);
+- spin_unlock_irq(&(p)->port_lock);
+- return cond;
+-}
+-
+-static void gs_close(struct tty_struct *tty, struct file *file)
+-{
+- struct gs_port *port = tty->driver_data;
+- struct mutex *mtx;
+-
+- if (port == NULL) {
+- pr_err("gs_close: NULL port pointer\n");
+- return;
+- }
+-
+- gs_debug("gs_close: (%d,%p,%p)\n", port->port_num, tty, file);
+-
+- mtx = &gs_open_close_lock[port->port_num];
+- mutex_lock(mtx);
+-
+- spin_lock_irq(&port->port_lock);
+-
+- if (port->port_open_count == 0) {
+- pr_err("gs_close: (%d,%p,%p) port is already closed\n",
+- port->port_num, tty, file);
+- goto exit;
+- }
+-
+- if (port->port_open_count > 1) {
+- --port->port_open_count;
+- goto exit;
+- }
+-
+- /* free disconnected port on final close */
+- if (port->port_dev == NULL) {
+- kfree(port);
+- goto exit;
+- }
+-
+- /* mark port as closed but in use, we can drop port lock */
+- /* and sleep if necessary */
+- port->port_in_use = 1;
+- port->port_open_count = 0;
+-
+- /* wait for write buffer to drain, or */
+- /* at most GS_CLOSE_TIMEOUT seconds */
+- if (gs_buf_data_avail(port->port_write_buf) > 0) {
+- spin_unlock_irq(&port->port_lock);
+- wait_event_interruptible_timeout(port->port_write_wait,
+- gs_write_finished_event_safely(port),
+- GS_CLOSE_TIMEOUT * HZ);
+- spin_lock_irq(&port->port_lock);
+- }
+-
+- /* free disconnected port on final close */
+- /* (might have happened during the above sleep) */
+- if (port->port_dev == NULL) {
+- kfree(port);
+- goto exit;
+- }
+-
+- gs_buf_clear(port->port_write_buf);
+-
+- tty->driver_data = NULL;
+- port->port_tty = NULL;
+- port->port_in_use = 0;
+-
+- gs_debug("gs_close: (%d,%p,%p) completed\n",
+- port->port_num, tty, file);
+-
+-exit:
+- spin_unlock_irq(&port->port_lock);
+- mutex_unlock(mtx);
+-}
+-
+-/*
+- * gs_write
+- */
+-static int gs_write(struct tty_struct *tty, const unsigned char *buf, int count)
++static int __init serial_bind_config(struct usb_configuration *c)
+ {
+- unsigned long flags;
+- struct gs_port *port = tty->driver_data;
+- int ret;
+-
+- if (port == NULL) {
+- pr_err("gs_write: NULL port pointer\n");
+- return -EIO;
+- }
+-
+- gs_debug("gs_write: (%d,%p) writing %d bytes\n", port->port_num, tty,
+- count);
+-
+- if (count == 0)
+- return 0;
+-
+- spin_lock_irqsave(&port->port_lock, flags);
+-
+- if (port->port_dev == NULL) {
+- pr_err("gs_write: (%d,%p) port is not connected\n",
+- port->port_num, tty);
+- ret = -EIO;
+- goto exit;
+- }
+-
+- if (port->port_open_count == 0) {
+- pr_err("gs_write: (%d,%p) port is closed\n",
+- port->port_num, tty);
+- ret = -EBADF;
+- goto exit;
+- }
+-
+- count = gs_buf_put(port->port_write_buf, buf, count);
+-
+- spin_unlock_irqrestore(&port->port_lock, flags);
+-
+- gs_send(gs_device);
+-
+- gs_debug("gs_write: (%d,%p) wrote %d bytes\n", port->port_num, tty,
+- count);
++ unsigned i;
++ int status = 0;
+
+- return count;
+-
+-exit:
+- spin_unlock_irqrestore(&port->port_lock, flags);
+- return ret;
+-}
+-
+-/*
+- * gs_put_char
+- */
+-static int gs_put_char(struct tty_struct *tty, unsigned char ch)
+-{
+- unsigned long flags;
+- struct gs_port *port = tty->driver_data;
+- int ret = 0;
+-
+- if (port == NULL) {
+- pr_err("gs_put_char: NULL port pointer\n");
+- return 0;
+- }
+-
+- gs_debug("gs_put_char: (%d,%p) char=0x%x, called from %p\n",
+- port->port_num, tty, ch, __builtin_return_address(0));
+-
+- spin_lock_irqsave(&port->port_lock, flags);
+-
+- if (port->port_dev == NULL) {
+- pr_err("gs_put_char: (%d,%p) port is not connected\n",
+- port->port_num, tty);
+- goto exit;
+- }
+-
+- if (port->port_open_count == 0) {
+- pr_err("gs_put_char: (%d,%p) port is closed\n",
+- port->port_num, tty);
+- goto exit;
+- }
+-
+- ret = gs_buf_put(port->port_write_buf, &ch, 1);
+-
+-exit:
+- spin_unlock_irqrestore(&port->port_lock, flags);
+- return ret;
+-}
+-
+-/*
+- * gs_flush_chars
+- */
+-static void gs_flush_chars(struct tty_struct *tty)
+-{
+- unsigned long flags;
+- struct gs_port *port = tty->driver_data;
+-
+- if (port == NULL) {
+- pr_err("gs_flush_chars: NULL port pointer\n");
+- return;
+- }
+-
+- gs_debug("gs_flush_chars: (%d,%p)\n", port->port_num, tty);
+-
+- spin_lock_irqsave(&port->port_lock, flags);
+-
+- if (port->port_dev == NULL) {
+- pr_err("gs_flush_chars: (%d,%p) port is not connected\n",
+- port->port_num, tty);
+- goto exit;
+- }
+-
+- if (port->port_open_count == 0) {
+- pr_err("gs_flush_chars: (%d,%p) port is closed\n",
+- port->port_num, tty);
+- goto exit;
+- }
+-
+- spin_unlock_irqrestore(&port->port_lock, flags);
+-
+- gs_send(gs_device);
+-
+- return;
+-
+-exit:
+- spin_unlock_irqrestore(&port->port_lock, flags);
+-}
+-
+-/*
+- * gs_write_room
+- */
+-static int gs_write_room(struct tty_struct *tty)
+-{
+-
+- int room = 0;
+- unsigned long flags;
+- struct gs_port *port = tty->driver_data;
+-
+-
+- if (port == NULL)
+- return 0;
+-
+- spin_lock_irqsave(&port->port_lock, flags);
+-
+- if (port->port_dev != NULL && port->port_open_count > 0
+- && port->port_write_buf != NULL)
+- room = gs_buf_space_avail(port->port_write_buf);
+-
+- spin_unlock_irqrestore(&port->port_lock, flags);
+-
+- gs_debug("gs_write_room: (%d,%p) room=%d\n",
+- port->port_num, tty, room);
+-
+- return room;
+-}
+-
+-/*
+- * gs_chars_in_buffer
+- */
+-static int gs_chars_in_buffer(struct tty_struct *tty)
+-{
+- int chars = 0;
+- unsigned long flags;
+- struct gs_port *port = tty->driver_data;
+-
+- if (port == NULL)
+- return 0;
+-
+- spin_lock_irqsave(&port->port_lock, flags);
+-
+- if (port->port_dev != NULL && port->port_open_count > 0
+- && port->port_write_buf != NULL)
+- chars = gs_buf_data_avail(port->port_write_buf);
+-
+- spin_unlock_irqrestore(&port->port_lock, flags);
+-
+- gs_debug("gs_chars_in_buffer: (%d,%p) chars=%d\n",
+- port->port_num, tty, chars);
+-
+- return chars;
+-}
+-
+-/*
+- * gs_throttle
+- */
+-static void gs_throttle(struct tty_struct *tty)
+-{
+-}
+-
+-/*
+- * gs_unthrottle
+- */
+-static void gs_unthrottle(struct tty_struct *tty)
+-{
+-}
+-
+-/*
+- * gs_break
+- */
+-static void gs_break(struct tty_struct *tty, int break_state)
+-{
+-}
+-
+-/*
+- * gs_ioctl
+- */
+-static int gs_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
+-{
+- struct gs_port *port = tty->driver_data;
+-
+- if (port == NULL) {
+- pr_err("gs_ioctl: NULL port pointer\n");
+- return -EIO;
++ for (i = 0; i < n_ports && status == 0; i++) {
++ if (use_acm)
++ status = acm_bind_config(c, i);
++ else
++ status = gser_bind_config(c, i);
+ }
+-
+- gs_debug("gs_ioctl: (%d,%p,%p) cmd=0x%4.4x, arg=%lu\n",
+- port->port_num, tty, file, cmd, arg);
+-
+- /* handle ioctls */
+-
+- /* could not handle ioctl */
+- return -ENOIOCTLCMD;
+-}
+-
+-/*
+- * gs_set_termios
+- */
+-static void gs_set_termios(struct tty_struct *tty, struct ktermios *old)
+-{
++ return status;
+ }
+
+-static const struct tty_operations gs_tty_ops = {
+- .open = gs_open,
+- .close = gs_close,
+- .write = gs_write,
+- .put_char = gs_put_char,
+- .flush_chars = gs_flush_chars,
+- .write_room = gs_write_room,
+- .ioctl = gs_ioctl,
+- .set_termios = gs_set_termios,
+- .throttle = gs_throttle,
+- .unthrottle = gs_unthrottle,
+- .break_ctl = gs_break,
+- .chars_in_buffer = gs_chars_in_buffer,
++static struct usb_configuration serial_config_driver = {
++ /* .label = f(use_acm) */
++ .bind = serial_bind_config,
++ /* .bConfigurationValue = f(use_acm) */
++ /* .iConfiguration = DYNAMIC */
++ .bmAttributes = USB_CONFIG_ATT_SELFPOWER,
++ .bMaxPower = 1, /* 2 mA, minimal */
+ };
+
+-/*-------------------------------------------------------------------------*/
+-
+-/*
+-* gs_send
+-*
+-* This function finds available write requests, calls
+-* gs_send_packet to fill these packets with data, and
+-* continues until either there are no more write requests
+-* available or no more data to send. This function is
+-* run whenever data arrives or write requests are available.
+-*/
+-static int gs_send(struct gs_dev *dev)
+-{
+- int ret,len;
+- unsigned long flags;
+- struct usb_ep *ep;
+- struct usb_request *req;
+-
+- if (dev == NULL) {
+- pr_err("gs_send: NULL device pointer\n");
+- return -ENODEV;
+- }
+-
+- spin_lock_irqsave(&dev->dev_lock, flags);
+-
+- ep = dev->dev_in_ep;
+-
+- while(!list_empty(&dev->dev_req_list)) {
+-
+- req = list_entry(dev->dev_req_list.next,
+- struct usb_request, list);
+-
+- len = gs_send_packet(dev, req->buf, ep->maxpacket);
+-
+- if (len > 0) {
+- gs_debug_level(3, "gs_send: len=%d, 0x%2.2x "
+- "0x%2.2x 0x%2.2x ...\n", len,
+- *((unsigned char *)req->buf),
+- *((unsigned char *)req->buf+1),
+- *((unsigned char *)req->buf+2));
+- list_del(&req->list);
+- req->length = len;
+- spin_unlock_irqrestore(&dev->dev_lock, flags);
+- if ((ret=usb_ep_queue(ep, req, GFP_ATOMIC))) {
+- pr_err(
+- "gs_send: cannot queue read request, ret=%d\n",
+- ret);
+- spin_lock_irqsave(&dev->dev_lock, flags);
+- break;
+- }
+- spin_lock_irqsave(&dev->dev_lock, flags);
+- } else {
+- break;
+- }
+-
+- }
+-
+- spin_unlock_irqrestore(&dev->dev_lock, flags);
+-
+- return 0;
+-}
+-
+-/*
+- * gs_send_packet
+- *
+- * If there is data to send, a packet is built in the given
+- * buffer and the size is returned. If there is no data to
+- * send, 0 is returned. If there is any error a negative
+- * error number is returned.
+- *
+- * Called during USB completion routine, on interrupt time.
+- *
+- * We assume that disconnect will not happen until all completion
+- * routines have completed, so we can assume that the dev_port
+- * array does not change during the lifetime of this function.
+- */
+-static int gs_send_packet(struct gs_dev *dev, char *packet, unsigned int size)
+-{
+- unsigned int len;
+- struct gs_port *port;
+-
+- /* TEMPORARY -- only port 0 is supported right now */
+- port = dev->dev_port[0];
+-
+- if (port == NULL) {
+- pr_err("gs_send_packet: port=%d, NULL port pointer\n", 0);
+- return -EIO;
+- }
+-
+- spin_lock(&port->port_lock);
+-
+- len = gs_buf_data_avail(port->port_write_buf);
+- if (len < size)
+- size = len;
+-
+- if (size == 0)
+- goto exit;
+-
+- size = gs_buf_get(port->port_write_buf, packet, size);
+-
+- if (port->port_tty)
+- wake_up_interruptible(&port->port_tty->write_wait);
+-
+-exit:
+- spin_unlock(&port->port_lock);
+- return size;
+-}
+-
+-/*
+- * gs_recv_packet
+- *
+- * Called for each USB packet received. Reads the packet
+- * header and stuffs the data in the appropriate tty buffer.
+- * Returns 0 if successful, or a negative error number.
+- *
+- * Called during USB completion routine, on interrupt time.
+- *
+- * We assume that disconnect will not happen until all completion
+- * routines have completed, so we can assume that the dev_port
+- * array does not change during the lifetime of this function.
+- */
+-static int gs_recv_packet(struct gs_dev *dev, char *packet, unsigned int size)
+-{
+- unsigned int len;
+- struct gs_port *port;
+- int ret;
+- struct tty_struct *tty;
+-
+- /* TEMPORARY -- only port 0 is supported right now */
+- port = dev->dev_port[0];
+-
+- if (port == NULL) {
+- pr_err("gs_recv_packet: port=%d, NULL port pointer\n",
+- port->port_num);
+- return -EIO;
+- }
+-
+- spin_lock(&port->port_lock);
+-
+- if (port->port_open_count == 0) {
+- pr_err("gs_recv_packet: port=%d, port is closed\n",
+- port->port_num);
+- ret = -EIO;
+- goto exit;
+- }
+-
+-
+- tty = port->port_tty;
+-
+- if (tty == NULL) {
+- pr_err("gs_recv_packet: port=%d, NULL tty pointer\n",
+- port->port_num);
+- ret = -EIO;
+- goto exit;
+- }
+-
+- if (port->port_tty->magic != TTY_MAGIC) {
+- pr_err("gs_recv_packet: port=%d, bad tty magic\n",
+- port->port_num);
+- ret = -EIO;
+- goto exit;
+- }
+-
+- len = tty_buffer_request_room(tty, size);
+- if (len > 0) {
+- tty_insert_flip_string(tty, packet, len);
+- tty_flip_buffer_push(port->port_tty);
+- wake_up_interruptible(&port->port_tty->read_wait);
+- }
+- ret = 0;
+-exit:
+- spin_unlock(&port->port_lock);
+- return ret;
+-}
+-
+-/*
+-* gs_read_complete
+-*/
+-static void gs_read_complete(struct usb_ep *ep, struct usb_request *req)
++static int __init gs_bind(struct usb_composite_dev *cdev)
+ {
+- int ret;
+- struct gs_dev *dev = ep->driver_data;
+-
+- if (dev == NULL) {
+- pr_err("gs_read_complete: NULL device pointer\n");
+- return;
+- }
++ int gcnum;
++ struct usb_gadget *gadget = cdev->gadget;
++ int status;
+
+- switch(req->status) {
+- case 0:
+- /* normal completion */
+- gs_recv_packet(dev, req->buf, req->actual);
+-requeue:
+- req->length = ep->maxpacket;
+- if ((ret=usb_ep_queue(ep, req, GFP_ATOMIC))) {
+- pr_err(
+- "gs_read_complete: cannot queue read request, ret=%d\n",
+- ret);
+- }
+- break;
+-
+- case -ESHUTDOWN:
+- /* disconnect */
+- gs_debug("gs_read_complete: shutdown\n");
+- gs_free_req(ep, req);
+- break;
+-
+- default:
+- /* unexpected */
+- pr_err(
+- "gs_read_complete: unexpected status error, status=%d\n",
+- req->status);
+- goto requeue;
+- break;
+- }
+-}
+-
+-/*
+-* gs_write_complete
+-*/
+-static void gs_write_complete(struct usb_ep *ep, struct usb_request *req)
+-{
+- struct gs_dev *dev = ep->driver_data;
+-
+- if (dev == NULL) {
+- pr_err("gs_write_complete: NULL device pointer\n");
+- return;
+- }
++ status = gserial_setup(cdev->gadget, n_ports);
++ if (status < 0)
++ return status;
+
+- switch(req->status) {
+- case 0:
+- /* normal completion */
+-requeue:
+- spin_lock(&dev->dev_lock);
+- list_add(&req->list, &dev->dev_req_list);
+- spin_unlock(&dev->dev_lock);
+-
+- gs_send(dev);
+-
+- break;
+-
+- case -ESHUTDOWN:
+- /* disconnect */
+- gs_debug("gs_write_complete: shutdown\n");
+- gs_free_req(ep, req);
+- break;
+-
+- default:
+- pr_err(
+- "gs_write_complete: unexpected status error, status=%d\n",
+- req->status);
+- goto requeue;
+- break;
+- }
+-}
++ /* Allocate string descriptor numbers ... note that string
++ * contents can be overridden by the composite_dev glue.
++ */
+
+-/*-------------------------------------------------------------------------*/
++ /* device description: manufacturer, product */
++ snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
++ init_utsname()->sysname, init_utsname()->release,
++ gadget->name);
++ status = usb_string_id(cdev);
++ if (status < 0)
++ goto fail;
++ strings_dev[STRING_MANUFACTURER_IDX].id = status;
+
+-/* Gadget Driver */
++ device_desc.iManufacturer = status;
+
+-/*
+- * gs_unbind
+- *
+- * Called on module unload. Frees the control request and device
+- * structure.
+- */
+-static void /* __init_or_exit */ gs_unbind(struct usb_gadget *gadget)
+-{
+- struct gs_dev *dev = get_gadget_data(gadget);
++ status = usb_string_id(cdev);
++ if (status < 0)
++ goto fail;
++ strings_dev[STRING_PRODUCT_IDX].id = status;
+
+- gs_device = NULL;
++ device_desc.iProduct = status;
+
+- /* read/write requests already freed, only control request remains */
+- if (dev != NULL) {
+- if (dev->dev_ctrl_req != NULL) {
+- gs_free_req(gadget->ep0, dev->dev_ctrl_req);
+- dev->dev_ctrl_req = NULL;
+- }
+- gs_reset_config(dev);
+- gs_free_ports(dev);
+- kfree(dev);
+- set_gadget_data(gadget, NULL);
+- }
++ /* config description */
++ status = usb_string_id(cdev);
++ if (status < 0)
++ goto fail;
++ strings_dev[STRING_DESCRIPTION_IDX].id = status;
+
+- pr_info("gs_unbind: %s %s unbound\n", GS_LONG_NAME,
+- GS_VERSION_STR);
+-}
+-
+-/*
+- * gs_bind
+- *
+- * Called on module load. Allocates and initializes the device
+- * structure and a control request.
+- */
+-static int __init gs_bind(struct usb_gadget *gadget)
+-{
+- int ret;
+- struct usb_ep *ep;
+- struct gs_dev *dev;
+- int gcnum;
+-
+- /* Some controllers can't support CDC ACM:
+- * - sh doesn't support multiple interfaces or configs;
+- * - sa1100 doesn't have a third interrupt endpoint
+- */
+- if (gadget_is_sh(gadget) || gadget_is_sa1100(gadget))
+- use_acm = 0;
++ serial_config_driver.iConfiguration = status;
+
++ /* set up other descriptors */
+ gcnum = usb_gadget_controller_number(gadget);
+ if (gcnum >= 0)
+- gs_device_desc.bcdDevice =
+- cpu_to_le16(GS_VERSION_NUM | gcnum);
++ device_desc.bcdDevice = cpu_to_le16(GS_VERSION_NUM | gcnum);
+ else {
++ /* this is so simple (for now, no altsettings) that it
++ * SHOULD NOT have problems with bulk-capable hardware.
++ * so warn about unrcognized controllers -- don't panic.
++ *
++ * things like configuration and altsetting numbering
++ * can need hardware-specific attention though.
++ */
+ pr_warning("gs_bind: controller '%s' not recognized\n",
+ gadget->name);
+- /* unrecognized, but safe unless bulk is REALLY quirky */
+- gs_device_desc.bcdDevice =
+- __constant_cpu_to_le16(GS_VERSION_NUM|0x0099);
+- }
+-
+- dev = kzalloc(sizeof(struct gs_dev), GFP_KERNEL);
+- if (dev == NULL)
+- return -ENOMEM;
+-
+- usb_ep_autoconfig_reset(gadget);
+-
+- ep = usb_ep_autoconfig(gadget, &gs_fullspeed_in_desc);
+- if (!ep)
+- goto autoconf_fail;
+- dev->dev_in_ep = ep;
+- ep->driver_data = dev; /* claim the endpoint */
+-
+- ep = usb_ep_autoconfig(gadget, &gs_fullspeed_out_desc);
+- if (!ep)
+- goto autoconf_fail;
+- dev->dev_out_ep = ep;
+- ep->driver_data = dev; /* claim the endpoint */
+-
+- if (use_acm) {
+- ep = usb_ep_autoconfig(gadget, &gs_fullspeed_notify_desc);
+- if (!ep) {
+- pr_err("gs_bind: cannot run ACM on %s\n", gadget->name);
+- goto autoconf_fail;
+- }
+- gs_device_desc.idProduct = __constant_cpu_to_le16(
+- GS_CDC_PRODUCT_ID),
+- dev->dev_notify_ep = ep;
+- ep->driver_data = dev; /* claim the endpoint */
+- }
+-
+- gs_device_desc.bDeviceClass = use_acm
+- ? USB_CLASS_COMM : USB_CLASS_VENDOR_SPEC;
+- gs_device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
+-
+- if (gadget_is_dualspeed(gadget)) {
+- gs_qualifier_desc.bDeviceClass = use_acm
+- ? USB_CLASS_COMM : USB_CLASS_VENDOR_SPEC;
+- /* assume ep0 uses the same packet size for both speeds */
+- gs_qualifier_desc.bMaxPacketSize0 =
+- gs_device_desc.bMaxPacketSize0;
+- /* assume endpoints are dual-speed */
+- gs_highspeed_notify_desc.bEndpointAddress =
+- gs_fullspeed_notify_desc.bEndpointAddress;
+- gs_highspeed_in_desc.bEndpointAddress =
+- gs_fullspeed_in_desc.bEndpointAddress;
+- gs_highspeed_out_desc.bEndpointAddress =
+- gs_fullspeed_out_desc.bEndpointAddress;
+- }
+-
+- usb_gadget_set_selfpowered(gadget);
+-
+- if (gadget_is_otg(gadget)) {
+- gs_otg_descriptor.bmAttributes |= USB_OTG_HNP,
+- gs_bulk_config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+- gs_acm_config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
++ device_desc.bcdDevice =
++ __constant_cpu_to_le16(GS_VERSION_NUM | 0x0099);
+ }
+
+- gs_device = dev;
+-
+- snprintf(manufacturer, sizeof(manufacturer), "%s %s with %s",
+- init_utsname()->sysname, init_utsname()->release,
+- gadget->name);
+-
+- dev->dev_gadget = gadget;
+- spin_lock_init(&dev->dev_lock);
+- INIT_LIST_HEAD(&dev->dev_req_list);
+- set_gadget_data(gadget, dev);
+-
+- if ((ret=gs_alloc_ports(dev, GFP_KERNEL)) != 0) {
+- pr_err("gs_bind: cannot allocate ports\n");
+- gs_unbind(gadget);
+- return ret;
++ if (gadget_is_otg(cdev->gadget)) {
++ serial_config_driver.descriptors = otg_desc;
++ serial_config_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+ }
+
+- /* preallocate control response and buffer */
+- dev->dev_ctrl_req = gs_alloc_req(gadget->ep0, GS_MAX_DESC_LEN,
+- GFP_KERNEL);
+- if (dev->dev_ctrl_req == NULL) {
+- gs_unbind(gadget);
+- return -ENOMEM;
+- }
+- gadget->ep0->driver_data = dev;
++ /* register our configuration */
++ status = usb_add_config(cdev, &serial_config_driver);
++ if (status < 0)
++ goto fail;
+
+- pr_info("gs_bind: %s %s bound\n",
+- GS_LONG_NAME, GS_VERSION_STR);
++ INFO(cdev, "%s\n", GS_VERSION_NAME);
+
+ return 0;
+
+-autoconf_fail:
+- kfree(dev);
+- pr_err("gs_bind: cannot autoconfigure on %s\n", gadget->name);
+- return -ENODEV;
+-}
+-
+-static int gs_setup_standard(struct usb_gadget *gadget,
+- const struct usb_ctrlrequest *ctrl)
+-{
+- int ret = -EOPNOTSUPP;
+- struct gs_dev *dev = get_gadget_data(gadget);
+- struct usb_request *req = dev->dev_ctrl_req;
+- u16 wIndex = le16_to_cpu(ctrl->wIndex);
+- u16 wValue = le16_to_cpu(ctrl->wValue);
+- u16 wLength = le16_to_cpu(ctrl->wLength);
+-
+- switch (ctrl->bRequest) {
+- case USB_REQ_GET_DESCRIPTOR:
+- if (ctrl->bRequestType != USB_DIR_IN)
+- break;
+-
+- switch (wValue >> 8) {
+- case USB_DT_DEVICE:
+- ret = min(wLength,
+- (u16)sizeof(struct usb_device_descriptor));
+- memcpy(req->buf, &gs_device_desc, ret);
+- break;
+-
+- case USB_DT_DEVICE_QUALIFIER:
+- if (!gadget_is_dualspeed(gadget))
+- break;
+- ret = min(wLength,
+- (u16)sizeof(struct usb_qualifier_descriptor));
+- memcpy(req->buf, &gs_qualifier_desc, ret);
+- break;
+-
+- case USB_DT_OTHER_SPEED_CONFIG:
+- if (!gadget_is_dualspeed(gadget))
+- break;
+- /* fall through */
+- case USB_DT_CONFIG:
+- ret = gs_build_config_buf(req->buf, gadget,
+- wValue >> 8, wValue & 0xff,
+- gadget_is_otg(gadget));
+- if (ret >= 0)
+- ret = min(wLength, (u16)ret);
+- break;
+-
+- case USB_DT_STRING:
+- /* wIndex == language code. */
+- ret = usb_gadget_get_string(&gs_string_table,
+- wValue & 0xff, req->buf);
+- if (ret >= 0)
+- ret = min(wLength, (u16)ret);
+- break;
+- }
+- break;
+-
+- case USB_REQ_SET_CONFIGURATION:
+- if (ctrl->bRequestType != 0)
+- break;
+- spin_lock(&dev->dev_lock);
+- ret = gs_set_config(dev, wValue);
+- spin_unlock(&dev->dev_lock);
+- break;
+-
+- case USB_REQ_GET_CONFIGURATION:
+- if (ctrl->bRequestType != USB_DIR_IN)
+- break;
+- *(u8 *)req->buf = dev->dev_config;
+- ret = min(wLength, (u16)1);
+- break;
+-
+- case USB_REQ_SET_INTERFACE:
+- if (ctrl->bRequestType != USB_RECIP_INTERFACE
+- || !dev->dev_config
+- || wIndex >= GS_MAX_NUM_INTERFACES)
+- break;
+- if (dev->dev_config == GS_BULK_CONFIG_ID
+- && wIndex != GS_BULK_INTERFACE_ID)
+- break;
+- /* no alternate interface settings */
+- if (wValue != 0)
+- break;
+- spin_lock(&dev->dev_lock);
+- /* PXA hardware partially handles SET_INTERFACE;
+- * we need to kluge around that interference. */
+- if (gadget_is_pxa(gadget)) {
+- ret = gs_set_config(dev, use_acm ?
+- GS_ACM_CONFIG_ID : GS_BULK_CONFIG_ID);
+- goto set_interface_done;
+- }
+- if (dev->dev_config != GS_BULK_CONFIG_ID
+- && wIndex == GS_CONTROL_INTERFACE_ID) {
+- if (dev->dev_notify_ep) {
+- usb_ep_disable(dev->dev_notify_ep);
+- usb_ep_enable(dev->dev_notify_ep, dev->dev_notify_ep_desc);
+- }
+- } else {
+- usb_ep_disable(dev->dev_in_ep);
+- usb_ep_disable(dev->dev_out_ep);
+- usb_ep_enable(dev->dev_in_ep, dev->dev_in_ep_desc);
+- usb_ep_enable(dev->dev_out_ep, dev->dev_out_ep_desc);
+- }
+- ret = 0;
+-set_interface_done:
+- spin_unlock(&dev->dev_lock);
+- break;
+-
+- case USB_REQ_GET_INTERFACE:
+- if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)
+- || dev->dev_config == GS_NO_CONFIG_ID)
+- break;
+- if (wIndex >= GS_MAX_NUM_INTERFACES
+- || (dev->dev_config == GS_BULK_CONFIG_ID
+- && wIndex != GS_BULK_INTERFACE_ID)) {
+- ret = -EDOM;
+- break;
+- }
+- /* no alternate interface settings */
+- *(u8 *)req->buf = 0;
+- ret = min(wLength, (u16)1);
+- break;
+-
+- default:
+- pr_err("gs_setup: unknown standard request, type=%02x, "
+- "request=%02x, value=%04x, index=%04x, length=%d\n",
+- ctrl->bRequestType, ctrl->bRequest,
+- wValue, wIndex, wLength);
+- break;
+- }
+-
+- return ret;
++fail:
++ gserial_cleanup();
++ return status;
+ }
+
+-static void gs_setup_complete_set_line_coding(struct usb_ep *ep,
+- struct usb_request *req)
+-{
+- struct gs_dev *dev = ep->driver_data;
+- struct gs_port *port = dev->dev_port[0]; /* ACM only has one port */
+-
+- switch (req->status) {
+- case 0:
+- /* normal completion */
+- if (req->actual != sizeof(port->port_line_coding))
+- usb_ep_set_halt(ep);
+- else if (port) {
+- struct usb_cdc_line_coding *value = req->buf;
+-
+- /* REVISIT: we currently just remember this data.
+- * If we change that, (a) validate it first, then
+- * (b) update whatever hardware needs updating.
+- */
+- spin_lock(&port->port_lock);
+- port->port_line_coding = *value;
+- spin_unlock(&port->port_lock);
+- }
+- break;
+-
+- case -ESHUTDOWN:
+- /* disconnect */
+- gs_free_req(ep, req);
+- break;
+-
+- default:
+- /* unexpected */
+- break;
+- }
+- return;
+-}
+-
+-static int gs_setup_class(struct usb_gadget *gadget,
+- const struct usb_ctrlrequest *ctrl)
+-{
+- int ret = -EOPNOTSUPP;
+- struct gs_dev *dev = get_gadget_data(gadget);
+- struct gs_port *port = dev->dev_port[0]; /* ACM only has one port */
+- struct usb_request *req = dev->dev_ctrl_req;
+- u16 wIndex = le16_to_cpu(ctrl->wIndex);
+- u16 wValue = le16_to_cpu(ctrl->wValue);
+- u16 wLength = le16_to_cpu(ctrl->wLength);
+-
+- switch (ctrl->bRequest) {
+- case USB_CDC_REQ_SET_LINE_CODING:
+- if (wLength != sizeof(struct usb_cdc_line_coding))
+- break;
+- ret = wLength;
+- req->complete = gs_setup_complete_set_line_coding;
+- break;
+-
+- case USB_CDC_REQ_GET_LINE_CODING:
+- ret = min_t(int, wLength, sizeof(struct usb_cdc_line_coding));
+- if (port) {
+- spin_lock(&port->port_lock);
+- memcpy(req->buf, &port->port_line_coding, ret);
+- spin_unlock(&port->port_lock);
+- }
+- break;
+-
+- case USB_CDC_REQ_SET_CONTROL_LINE_STATE:
+- if (wLength != 0)
+- break;
+- ret = 0;
+- if (port) {
+- /* REVISIT: we currently just remember this data.
+- * If we change that, update whatever hardware needs
+- * updating.
+- */
+- spin_lock(&port->port_lock);
+- port->port_handshake_bits = wValue;
+- spin_unlock(&port->port_lock);
+- }
+- break;
+-
+- default:
+- /* NOTE: strictly speaking, we should accept AT-commands
+- * using SEND_ENCPSULATED_COMMAND/GET_ENCAPSULATED_RESPONSE.
+- * But our call management descriptor says we don't handle
+- * call management, so we should be able to get by without
+- * handling those "required" commands (except by stalling).
+- */
+- pr_err("gs_setup: unknown class request, "
+- "type=%02x, request=%02x, value=%04x, "
+- "index=%04x, length=%d\n",
+- ctrl->bRequestType, ctrl->bRequest,
+- wValue, wIndex, wLength);
+- break;
+- }
+-
+- return ret;
+-}
+-
+-/*
+- * gs_setup_complete
+- */
+-static void gs_setup_complete(struct usb_ep *ep, struct usb_request *req)
+-{
+- if (req->status || req->actual != req->length) {
+- pr_err("gs_setup_complete: status error, status=%d, "
+- "actual=%d, length=%d\n",
+- req->status, req->actual, req->length);
+- }
+-}
+-
+-/*
+- * gs_setup
+- *
+- * Implements all the control endpoint functionality that's not
+- * handled in hardware or the hardware driver.
+- *
+- * Returns the size of the data sent to the host, or a negative
+- * error number.
+- */
+-static int gs_setup(struct usb_gadget *gadget,
+- const struct usb_ctrlrequest *ctrl)
+-{
+- int ret = -EOPNOTSUPP;
+- struct gs_dev *dev = get_gadget_data(gadget);
+- struct usb_request *req = dev->dev_ctrl_req;
+- u16 wIndex = le16_to_cpu(ctrl->wIndex);
+- u16 wValue = le16_to_cpu(ctrl->wValue);
+- u16 wLength = le16_to_cpu(ctrl->wLength);
+-
+- req->complete = gs_setup_complete;
+-
+- switch (ctrl->bRequestType & USB_TYPE_MASK) {
+- case USB_TYPE_STANDARD:
+- ret = gs_setup_standard(gadget, ctrl);
+- break;
+-
+- case USB_TYPE_CLASS:
+- ret = gs_setup_class(gadget, ctrl);
+- break;
+-
+- default:
+- pr_err("gs_setup: unknown request, type=%02x, request=%02x, "
+- "value=%04x, index=%04x, length=%d\n",
+- ctrl->bRequestType, ctrl->bRequest,
+- wValue, wIndex, wLength);
+- break;
+- }
+-
+- /* respond with data transfer before status phase? */
+- if (ret >= 0) {
+- req->length = ret;
+- req->zero = ret < wLength
+- && (ret % gadget->ep0->maxpacket) == 0;
+- ret = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
+- if (ret < 0) {
+- pr_err("gs_setup: cannot queue response, ret=%d\n",
+- ret);
+- req->status = 0;
+- gs_setup_complete(gadget->ep0, req);
+- }
+- }
+-
+- /* device either stalls (ret < 0) or reports success */
+- return ret;
+-}
+-
+-/*
+- * gs_disconnect
+- *
+- * Called when the device is disconnected. Frees the closed
+- * ports and disconnects open ports. Open ports will be freed
+- * on close. Then reallocates the ports for the next connection.
+- */
+-static void gs_disconnect(struct usb_gadget *gadget)
+-{
+- unsigned long flags;
+- struct gs_dev *dev = get_gadget_data(gadget);
+-
+- spin_lock_irqsave(&dev->dev_lock, flags);
+-
+- gs_reset_config(dev);
+-
+- /* free closed ports and disconnect open ports */
+- /* (open ports will be freed when closed) */
+- gs_free_ports(dev);
+-
+- /* re-allocate ports for the next connection */
+- if (gs_alloc_ports(dev, GFP_ATOMIC) != 0)
+- pr_err("gs_disconnect: cannot re-allocate ports\n");
+-
+- spin_unlock_irqrestore(&dev->dev_lock, flags);
+-
+- pr_info("gs_disconnect: %s disconnected\n", GS_LONG_NAME);
+-}
+-
+-static struct usb_gadget_driver gs_gadget_driver = {
+-#ifdef CONFIG_USB_GADGET_DUALSPEED
+- .speed = USB_SPEED_HIGH,
+-#else
+- .speed = USB_SPEED_FULL,
+-#endif /* CONFIG_USB_GADGET_DUALSPEED */
+- .function = GS_LONG_NAME,
+- .bind = gs_bind,
+- .unbind = gs_unbind,
+- .setup = gs_setup,
+- .disconnect = gs_disconnect,
+- .driver = {
+- .name = GS_SHORT_NAME,
+- .owner = THIS_MODULE,
+- },
++static struct usb_composite_driver gserial_driver = {
++ .name = "g_serial",
++ .dev = &device_desc,
++ .strings = dev_strings,
++ .bind = gs_bind,
+ };
+
+-/*
+- * gs_set_config
+- *
+- * Configures the device by enabling device specific
+- * optimizations, setting up the endpoints, allocating
+- * read and write requests and queuing read requests.
+- *
+- * The device lock must be held when calling this function.
+- */
+-static int gs_set_config(struct gs_dev *dev, unsigned config)
++static int __init init(void)
+ {
+- int i;
+- int ret = 0;
+- struct usb_gadget *gadget = dev->dev_gadget;
+- struct usb_ep *ep;
+- struct usb_endpoint_descriptor *out, *in, *notify;
+- struct usb_request *req;
+-
+- if (dev == NULL) {
+- pr_err("gs_set_config: NULL device pointer\n");
+- return 0;
+- }
+-
+- if (config == dev->dev_config)
+- return 0;
+-
+- gs_reset_config(dev);
+-
+- switch (config) {
+- case GS_NO_CONFIG_ID:
+- return 0;
+- case GS_BULK_CONFIG_ID:
+- if (use_acm)
+- return -EINVAL;
+- break;
+- case GS_ACM_CONFIG_ID:
+- if (!use_acm)
+- return -EINVAL;
+- break;
+- default:
+- return -EINVAL;
+- }
+-
+- in = choose_ep_desc(gadget,
+- &gs_highspeed_in_desc,
+- &gs_fullspeed_in_desc);
+- out = choose_ep_desc(gadget,
+- &gs_highspeed_out_desc,
+- &gs_fullspeed_out_desc);
+- notify = dev->dev_notify_ep
+- ? choose_ep_desc(gadget,
+- &gs_highspeed_notify_desc,
+- &gs_fullspeed_notify_desc)
+- : NULL;
+-
+- ret = usb_ep_enable(dev->dev_in_ep, in);
+- if (ret == 0) {
+- dev->dev_in_ep_desc = in;
+- } else {
+- pr_debug("%s: cannot enable %s %s, ret=%d\n",
+- __func__, "IN", dev->dev_in_ep->name, ret);
+- return ret;
+- }
+-
+- ret = usb_ep_enable(dev->dev_out_ep, out);
+- if (ret == 0) {
+- dev->dev_out_ep_desc = out;
+- } else {
+- pr_debug("%s: cannot enable %s %s, ret=%d\n",
+- __func__, "OUT", dev->dev_out_ep->name, ret);
+-fail0:
+- usb_ep_disable(dev->dev_in_ep);
+- return ret;
+- }
+-
+- if (notify) {
+- ret = usb_ep_enable(dev->dev_notify_ep, notify);
+- if (ret == 0) {
+- dev->dev_notify_ep_desc = notify;
+- } else {
+- pr_debug("%s: cannot enable %s %s, ret=%d\n",
+- __func__, "NOTIFY",
+- dev->dev_notify_ep->name, ret);
+- usb_ep_disable(dev->dev_out_ep);
+- goto fail0;
+- }
+- }
+-
+- dev->dev_config = config;
+-
+- /* allocate and queue read requests */
+- ep = dev->dev_out_ep;
+- for (i=0; i<read_q_size && ret == 0; i++) {
+- if ((req=gs_alloc_req(ep, ep->maxpacket, GFP_ATOMIC))) {
+- req->complete = gs_read_complete;
+- if ((ret=usb_ep_queue(ep, req, GFP_ATOMIC))) {
+- pr_err("gs_set_config: cannot queue read "
+- "request, ret=%d\n", ret);
+- }
+- } else {
+- pr_err("gs_set_config: cannot allocate "
+- "read requests\n");
+- ret = -ENOMEM;
+- goto exit_reset_config;
+- }
+- }
+-
+- /* allocate write requests, and put on free list */
+- ep = dev->dev_in_ep;
+- for (i=0; i<write_q_size; i++) {
+- req = gs_alloc_req(ep, ep->maxpacket, GFP_ATOMIC);
+- if (req) {
+- req->complete = gs_write_complete;
+- list_add(&req->list, &dev->dev_req_list);
+- } else {
+- pr_err("gs_set_config: cannot allocate "
+- "write requests\n");
+- ret = -ENOMEM;
+- goto exit_reset_config;
+- }
+- }
+-
+- /* REVISIT the ACM mode should be able to actually *issue* some
+- * notifications, for at least serial state change events if
+- * not also for network connection; say so in bmCapabilities.
++ /* We *could* export two configs; that'd be much cleaner...
++ * but neither of these product IDs was defined that way.
+ */
+-
+- pr_info("gs_set_config: %s configured, %s speed %s config\n",
+- GS_LONG_NAME,
+- gadget->speed == USB_SPEED_HIGH ? "high" : "full",
+- config == GS_BULK_CONFIG_ID ? "BULK" : "CDC-ACM");
+-
+- return 0;
+-
+-exit_reset_config:
+- gs_reset_config(dev);
+- return ret;
+-}
+-
+-/*
+- * gs_reset_config
+- *
+- * Mark the device as not configured, disable all endpoints,
+- * which forces completion of pending I/O and frees queued
+- * requests, and free the remaining write requests on the
+- * free list.
+- *
+- * The device lock must be held when calling this function.
+- */
+-static void gs_reset_config(struct gs_dev *dev)
+-{
+- struct usb_request *req;
+-
+- if (dev == NULL) {
+- pr_err("gs_reset_config: NULL device pointer\n");
+- return;
+- }
+-
+- if (dev->dev_config == GS_NO_CONFIG_ID)
+- return;
+-
+- dev->dev_config = GS_NO_CONFIG_ID;
+-
+- /* free write requests on the free list */
+- while(!list_empty(&dev->dev_req_list)) {
+- req = list_entry(dev->dev_req_list.next,
+- struct usb_request, list);
+- list_del(&req->list);
+- gs_free_req(dev->dev_in_ep, req);
+- }
+-
+- /* disable endpoints, forcing completion of pending i/o; */
+- /* completion handlers free their requests in this case */
+- if (dev->dev_notify_ep)
+- usb_ep_disable(dev->dev_notify_ep);
+- usb_ep_disable(dev->dev_in_ep);
+- usb_ep_disable(dev->dev_out_ep);
+-}
+-
+-/*
+- * gs_build_config_buf
+- *
+- * Builds the config descriptors in the given buffer and returns the
+- * length, or a negative error number.
+- */
+-static int gs_build_config_buf(u8 *buf, struct usb_gadget *g,
+- u8 type, unsigned int index, int is_otg)
+-{
+- int len;
+- int high_speed = 0;
+- const struct usb_config_descriptor *config_desc;
+- const struct usb_descriptor_header **function;
+-
+- if (index >= gs_device_desc.bNumConfigurations)
+- return -EINVAL;
+-
+- /* other speed switches high and full speed */
+- if (gadget_is_dualspeed(g)) {
+- high_speed = (g->speed == USB_SPEED_HIGH);
+- if (type == USB_DT_OTHER_SPEED_CONFIG)
+- high_speed = !high_speed;
+- }
+-
+ if (use_acm) {
+- config_desc = &gs_acm_config_desc;
+- function = high_speed
+- ? gs_acm_highspeed_function
+- : gs_acm_fullspeed_function;
++ serial_config_driver.label = "CDC ACM config";
++ serial_config_driver.bConfigurationValue = 2;
++ device_desc.bDeviceClass = USB_CLASS_COMM;
++ device_desc.idProduct =
++ __constant_cpu_to_le16(GS_CDC_PRODUCT_ID);
+ } else {
+- config_desc = &gs_bulk_config_desc;
+- function = high_speed
+- ? gs_bulk_highspeed_function
+- : gs_bulk_fullspeed_function;
+- }
+-
+- /* for now, don't advertise srp-only devices */
+- if (!is_otg)
+- function++;
+-
+- len = usb_gadget_config_buf(config_desc, buf, GS_MAX_DESC_LEN, function);
+- if (len < 0)
+- return len;
+-
+- ((struct usb_config_descriptor *)buf)->bDescriptorType = type;
+-
+- return len;
+-}
+-
+-/*
+- * gs_alloc_req
+- *
+- * Allocate a usb_request and its buffer. Returns a pointer to the
+- * usb_request or NULL if there is an error.
+- */
+-static struct usb_request *
+-gs_alloc_req(struct usb_ep *ep, unsigned int len, gfp_t kmalloc_flags)
+-{
+- struct usb_request *req;
+-
+- if (ep == NULL)
+- return NULL;
+-
+- req = usb_ep_alloc_request(ep, kmalloc_flags);
+-
+- if (req != NULL) {
+- req->length = len;
+- req->buf = kmalloc(len, kmalloc_flags);
+- if (req->buf == NULL) {
+- usb_ep_free_request(ep, req);
+- return NULL;
+- }
++ serial_config_driver.label = "Generic Serial config";
++ serial_config_driver.bConfigurationValue = 1;
++ device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC;
++ device_desc.idProduct =
++ __constant_cpu_to_le16(GS_PRODUCT_ID);
+ }
++ strings_dev[STRING_DESCRIPTION_IDX].s = serial_config_driver.label;
+
+- return req;
++ return usb_composite_register(&gserial_driver);
+ }
++module_init(init);
+
+-/*
+- * gs_free_req
+- *
+- * Free a usb_request and its buffer.
+- */
+-static void gs_free_req(struct usb_ep *ep, struct usb_request *req)
++static void __exit cleanup(void)
+ {
+- if (ep != NULL && req != NULL) {
+- kfree(req->buf);
+- usb_ep_free_request(ep, req);
+- }
+-}
+-
+-/*
+- * gs_alloc_ports
+- *
+- * Allocate all ports and set the gs_dev struct to point to them.
+- * Return 0 if successful, or a negative error number.
+- *
+- * The device lock is normally held when calling this function.
+- */
+-static int gs_alloc_ports(struct gs_dev *dev, gfp_t kmalloc_flags)
+-{
+- int i;
+- struct gs_port *port;
+-
+- if (dev == NULL)
+- return -EIO;
+-
+- for (i=0; i<GS_NUM_PORTS; i++) {
+- if ((port=kzalloc(sizeof(struct gs_port), kmalloc_flags)) == NULL)
+- return -ENOMEM;
+-
+- port->port_dev = dev;
+- port->port_num = i;
+- port->port_line_coding.dwDTERate = cpu_to_le32(GS_DEFAULT_DTE_RATE);
+- port->port_line_coding.bCharFormat = GS_DEFAULT_CHAR_FORMAT;
+- port->port_line_coding.bParityType = GS_DEFAULT_PARITY;
+- port->port_line_coding.bDataBits = GS_DEFAULT_DATA_BITS;
+- spin_lock_init(&port->port_lock);
+- init_waitqueue_head(&port->port_write_wait);
+-
+- dev->dev_port[i] = port;
+- }
+-
+- return 0;
+-}
+-
+-/*
+- * gs_free_ports
+- *
+- * Free all closed ports. Open ports are disconnected by
+- * freeing their write buffers, setting their device pointers
+- * and the pointers to them in the device to NULL. These
+- * ports will be freed when closed.
+- *
+- * The device lock is normally held when calling this function.
+- */
+-static void gs_free_ports(struct gs_dev *dev)
+-{
+- int i;
+- unsigned long flags;
+- struct gs_port *port;
+-
+- if (dev == NULL)
+- return;
+-
+- for (i=0; i<GS_NUM_PORTS; i++) {
+- if ((port=dev->dev_port[i]) != NULL) {
+- dev->dev_port[i] = NULL;
+-
+- spin_lock_irqsave(&port->port_lock, flags);
+-
+- if (port->port_write_buf != NULL) {
+- gs_buf_free(port->port_write_buf);
+- port->port_write_buf = NULL;
+- }
+-
+- if (port->port_open_count > 0 || port->port_in_use) {
+- port->port_dev = NULL;
+- wake_up_interruptible(&port->port_write_wait);
+- if (port->port_tty) {
+- tty_hangup(port->port_tty);
+- }
+- spin_unlock_irqrestore(&port->port_lock, flags);
+- } else {
+- spin_unlock_irqrestore(&port->port_lock, flags);
+- kfree(port);
+- }
+-
+- }
+- }
+-}
+-
+-/*-------------------------------------------------------------------------*/
+-
+-/* Circular Buffer */
+-
+-/*
+- * gs_buf_alloc
+- *
+- * Allocate a circular buffer and all associated memory.
+- */
+-static struct gs_buf *gs_buf_alloc(unsigned int size, gfp_t kmalloc_flags)
+-{
+- struct gs_buf *gb;
+-
+- if (size == 0)
+- return NULL;
+-
+- gb = kmalloc(sizeof(struct gs_buf), kmalloc_flags);
+- if (gb == NULL)
+- return NULL;
+-
+- gb->buf_buf = kmalloc(size, kmalloc_flags);
+- if (gb->buf_buf == NULL) {
+- kfree(gb);
+- return NULL;
+- }
+-
+- gb->buf_size = size;
+- gb->buf_get = gb->buf_put = gb->buf_buf;
+-
+- return gb;
+-}
+-
+-/*
+- * gs_buf_free
+- *
+- * Free the buffer and all associated memory.
+- */
+-static void gs_buf_free(struct gs_buf *gb)
+-{
+- if (gb) {
+- kfree(gb->buf_buf);
+- kfree(gb);
+- }
+-}
+-
+-/*
+- * gs_buf_clear
+- *
+- * Clear out all data in the circular buffer.
+- */
+-static void gs_buf_clear(struct gs_buf *gb)
+-{
+- if (gb != NULL)
+- gb->buf_get = gb->buf_put;
+- /* equivalent to a get of all data available */
+-}
+-
+-/*
+- * gs_buf_data_avail
+- *
+- * Return the number of bytes of data available in the circular
+- * buffer.
+- */
+-static unsigned int gs_buf_data_avail(struct gs_buf *gb)
+-{
+- if (gb != NULL)
+- return (gb->buf_size + gb->buf_put - gb->buf_get) % gb->buf_size;
+- else
+- return 0;
+-}
+-
+-/*
+- * gs_buf_space_avail
+- *
+- * Return the number of bytes of space available in the circular
+- * buffer.
+- */
+-static unsigned int gs_buf_space_avail(struct gs_buf *gb)
+-{
+- if (gb != NULL)
+- return (gb->buf_size + gb->buf_get - gb->buf_put - 1) % gb->buf_size;
+- else
+- return 0;
+-}
+-
+-/*
+- * gs_buf_put
+- *
+- * Copy data data from a user buffer and put it into the circular buffer.
+- * Restrict to the amount of space available.
+- *
+- * Return the number of bytes copied.
+- */
+-static unsigned int
+-gs_buf_put(struct gs_buf *gb, const char *buf, unsigned int count)
+-{
+- unsigned int len;
+-
+- if (gb == NULL)
+- return 0;
+-
+- len = gs_buf_space_avail(gb);
+- if (count > len)
+- count = len;
+-
+- if (count == 0)
+- return 0;
+-
+- len = gb->buf_buf + gb->buf_size - gb->buf_put;
+- if (count > len) {
+- memcpy(gb->buf_put, buf, len);
+- memcpy(gb->buf_buf, buf+len, count - len);
+- gb->buf_put = gb->buf_buf + count - len;
+- } else {
+- memcpy(gb->buf_put, buf, count);
+- if (count < len)
+- gb->buf_put += count;
+- else /* count == len */
+- gb->buf_put = gb->buf_buf;
+- }
+-
+- return count;
+-}
+-
+-/*
+- * gs_buf_get
+- *
+- * Get data from the circular buffer and copy to the given buffer.
+- * Restrict to the amount of data available.
+- *
+- * Return the number of bytes copied.
+- */
+-static unsigned int
+-gs_buf_get(struct gs_buf *gb, char *buf, unsigned int count)
+-{
+- unsigned int len;
+-
+- if (gb == NULL)
+- return 0;
+-
+- len = gs_buf_data_avail(gb);
+- if (count > len)
+- count = len;
+-
+- if (count == 0)
+- return 0;
+-
+- len = gb->buf_buf + gb->buf_size - gb->buf_get;
+- if (count > len) {
+- memcpy(buf, gb->buf_get, len);
+- memcpy(buf+len, gb->buf_buf, count - len);
+- gb->buf_get = gb->buf_buf + count - len;
+- } else {
+- memcpy(buf, gb->buf_get, count);
+- if (count < len)
+- gb->buf_get += count;
+- else /* count == len */
+- gb->buf_get = gb->buf_buf;
+- }
+-
+- return count;
+-}
+-
+-/*-------------------------------------------------------------------------*/
+-
+-static struct tty_driver *gs_tty_driver;
+-
+-/*
+- * gs_module_init
+- *
+- * Register as a USB gadget driver and a tty driver.
+- */
+-static int __init gs_module_init(void)
+-{
+- int i;
+- int retval;
+-
+- retval = usb_gadget_register_driver(&gs_gadget_driver);
+- if (retval) {
+- pr_err("gs_module_init: cannot register gadget driver, "
+- "ret=%d\n", retval);
+- return retval;
+- }
+-
+- gs_tty_driver = alloc_tty_driver(GS_NUM_PORTS);
+- if (!gs_tty_driver)
+- return -ENOMEM;
+- gs_tty_driver->owner = THIS_MODULE;
+- gs_tty_driver->driver_name = GS_SHORT_NAME;
+- gs_tty_driver->name = "ttygs";
+- gs_tty_driver->major = GS_MAJOR;
+- gs_tty_driver->minor_start = GS_MINOR_START;
+- gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
+- gs_tty_driver->subtype = SERIAL_TYPE_NORMAL;
+- gs_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+- gs_tty_driver->init_termios = tty_std_termios;
+- /* must match GS_DEFAULT_DTE_RATE and friends */
+- gs_tty_driver->init_termios.c_cflag =
+- B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+- gs_tty_driver->init_termios.c_ispeed = GS_DEFAULT_DTE_RATE;
+- gs_tty_driver->init_termios.c_ospeed = GS_DEFAULT_DTE_RATE;
+- tty_set_operations(gs_tty_driver, &gs_tty_ops);
+-
+- for (i = 0; i < GS_NUM_PORTS; i++)
+- mutex_init(&gs_open_close_lock[i]);
+-
+- retval = tty_register_driver(gs_tty_driver);
+- if (retval) {
+- usb_gadget_unregister_driver(&gs_gadget_driver);
+- put_tty_driver(gs_tty_driver);
+- pr_err("gs_module_init: cannot register tty driver, "
+- "ret=%d\n", retval);
+- return retval;
+- }
+-
+- pr_info("gs_module_init: %s %s loaded\n",
+- GS_LONG_NAME, GS_VERSION_STR);
+- return 0;
+-}
+-module_init(gs_module_init);
+-
+-/*
+- * gs_module_exit
+- *
+- * Unregister as a tty driver and a USB gadget driver.
+- */
+-static void __exit gs_module_exit(void)
+-{
+- tty_unregister_driver(gs_tty_driver);
+- put_tty_driver(gs_tty_driver);
+- usb_gadget_unregister_driver(&gs_gadget_driver);
+-
+- pr_info("gs_module_exit: %s %s unloaded\n",
+- GS_LONG_NAME, GS_VERSION_STR);
++ usb_composite_unregister(&gserial_driver);
++ gserial_cleanup();
+ }
+-module_exit(gs_module_exit);
++module_exit(cleanup);
+diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c
+new file mode 100644
+index 0000000..3791e62
+--- /dev/null
++++ b/drivers/usb/gadget/u_ether.c
+@@ -0,0 +1,964 @@
++/*
++ * u_ether.c -- Ethernet-over-USB link layer utilities for Gadget stack
++ *
++ * Copyright (C) 2003-2005,2008 David Brownell
++ * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
++ * Copyright (C) 2008 Nokia Corporation
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++/* #define VERBOSE_DEBUG */
++
++#include <linux/kernel.h>
++#include <linux/utsname.h>
++#include <linux/device.h>
++#include <linux/ctype.h>
++#include <linux/etherdevice.h>
++#include <linux/ethtool.h>
++
++#include "u_ether.h"
++
++
++/*
++ * This component encapsulates the Ethernet link glue needed to provide
++ * one (!) network link through the USB gadget stack, normally "usb0".
++ *
++ * The control and data models are handled by the function driver which
++ * connects to this code; such as CDC Ethernet, "CDC Subset", or RNDIS.
++ * That includes all descriptor and endpoint management.
++ *
++ * Link level addressing is handled by this component using module
++ * parameters; if no such parameters are provided, random link level
++ * addresses are used. Each end of the link uses one address. The
++ * host end address is exported in various ways, and is often recorded
++ * in configuration databases.
++ *
++ * The driver which assembles each configuration using such a link is
++ * responsible for ensuring that each configuration includes at most one
++ * instance of is network link. (The network layer provides ways for
++ * this single "physical" link to be used by multiple virtual links.)
++ */
++
++#define DRIVER_VERSION "29-May-2008"
++
++struct eth_dev {
++ /* lock is held while accessing port_usb
++ * or updating its backlink port_usb->ioport
++ */
++ spinlock_t lock;
++ struct gether *port_usb;
++
++ struct net_device *net;
++ struct usb_gadget *gadget;
++
++ spinlock_t req_lock; /* guard {rx,tx}_reqs */
++ struct list_head tx_reqs, rx_reqs;
++ atomic_t tx_qlen;
++
++ unsigned header_len;
++ struct sk_buff *(*wrap)(struct sk_buff *skb);
++ int (*unwrap)(struct sk_buff *skb);
++
++ struct work_struct work;
++
++ unsigned long todo;
++#define WORK_RX_MEMORY 0
++
++ bool zlp;
++ u8 host_mac[ETH_ALEN];
++};
++
++/*-------------------------------------------------------------------------*/
++
++#define RX_EXTRA 20 /* bytes guarding against rx overflows */
++
++#define DEFAULT_QLEN 2 /* double buffering by default */
++
++
++#ifdef CONFIG_USB_GADGET_DUALSPEED
++
++static unsigned qmult = 5;
++module_param(qmult, uint, S_IRUGO|S_IWUSR);
++MODULE_PARM_DESC(qmult, "queue length multiplier at high speed");
++
++#else /* full speed (low speed doesn't do bulk) */
++#define qmult 1
++#endif
++
++/* for dual-speed hardware, use deeper queues at highspeed */
++static inline int qlen(struct usb_gadget *gadget)
++{
++ if (gadget_is_dualspeed(gadget) && gadget->speed == USB_SPEED_HIGH)
++ return qmult * DEFAULT_QLEN;
++ else
++ return DEFAULT_QLEN;
++}
++
++/*-------------------------------------------------------------------------*/
++
++/* REVISIT there must be a better way than having two sets
++ * of debug calls ...
++ */
++
++#undef DBG
++#undef VDBG
++#undef ERROR
++#undef INFO
++
++#define xprintk(d, level, fmt, args...) \
++ printk(level "%s: " fmt , (d)->net->name , ## args)
++
++#ifdef DEBUG
++#undef DEBUG
++#define DBG(dev, fmt, args...) \
++ xprintk(dev , KERN_DEBUG , fmt , ## args)
++#else
++#define DBG(dev, fmt, args...) \
++ do { } while (0)
++#endif /* DEBUG */
++
++#ifdef VERBOSE_DEBUG
++#define VDBG DBG
++#else
++#define VDBG(dev, fmt, args...) \
++ do { } while (0)
++#endif /* DEBUG */
++
++#define ERROR(dev, fmt, args...) \
++ xprintk(dev , KERN_ERR , fmt , ## args)
++#define INFO(dev, fmt, args...) \
++ xprintk(dev , KERN_INFO , fmt , ## args)
++
++/*-------------------------------------------------------------------------*/
++
++/* NETWORK DRIVER HOOKUP (to the layer above this driver) */
++
++static int eth_change_mtu(struct net_device *net, int new_mtu)
++{
++ struct eth_dev *dev = netdev_priv(net);
++ unsigned long flags;
++ int status = 0;
++
++ /* don't change MTU on "live" link (peer won't know) */
++ spin_lock_irqsave(&dev->lock, flags);
++ if (dev->port_usb)
++ status = -EBUSY;
++ else if (new_mtu <= ETH_HLEN || new_mtu > ETH_FRAME_LEN)
++ status = -ERANGE;
++ else
++ net->mtu = new_mtu;
++ spin_unlock_irqrestore(&dev->lock, flags);
++
++ return status;
++}
++
++static void eth_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *p)
++{
++ struct eth_dev *dev = netdev_priv(net);
++
++ strlcpy(p->driver, "g_ether", sizeof p->driver);
++ strlcpy(p->version, DRIVER_VERSION, sizeof p->version);
++ strlcpy(p->fw_version, dev->gadget->name, sizeof p->fw_version);
++ strlcpy(p->bus_info, dev_name(&dev->gadget->dev), sizeof p->bus_info);
++}
++
++static u32 eth_get_link(struct net_device *net)
++{
++ struct eth_dev *dev = netdev_priv(net);
++ return dev->gadget->speed != USB_SPEED_UNKNOWN;
++}
++
++/* REVISIT can also support:
++ * - WOL (by tracking suspends and issuing remote wakeup)
++ * - msglevel (implies updated messaging)
++ * - ... probably more ethtool ops
++ */
++
++static struct ethtool_ops ops = {
++ .get_drvinfo = eth_get_drvinfo,
++ .get_link = eth_get_link
++};
++
++static void defer_kevent(struct eth_dev *dev, int flag)
++{
++ if (test_and_set_bit(flag, &dev->todo))
++ return;
++ if (!schedule_work(&dev->work))
++ ERROR(dev, "kevent %d may have been dropped\n", flag);
++ else
++ DBG(dev, "kevent %d scheduled\n", flag);
++}
++
++static void rx_complete(struct usb_ep *ep, struct usb_request *req);
++
++static int
++rx_submit(struct eth_dev *dev, struct usb_request *req, gfp_t gfp_flags)
++{
++ struct sk_buff *skb;
++ int retval = -ENOMEM;
++ size_t size = 0;
++ struct usb_ep *out;
++ unsigned long flags;
++
++ spin_lock_irqsave(&dev->lock, flags);
++ if (dev->port_usb)
++ out = dev->port_usb->out_ep;
++ else
++ out = NULL;
++ spin_unlock_irqrestore(&dev->lock, flags);
++
++ if (!out)
++ return -ENOTCONN;
++
++
++ /* Padding up to RX_EXTRA handles minor disagreements with host.
++ * Normally we use the USB "terminate on short read" convention;
++ * so allow up to (N*maxpacket), since that memory is normally
++ * already allocated. Some hardware doesn't deal well with short
++ * reads (e.g. DMA must be N*maxpacket), so for now don't trim a
++ * byte off the end (to force hardware errors on overflow).
++ *
++ * RNDIS uses internal framing, and explicitly allows senders to
++ * pad to end-of-packet. That's potentially nice for speed, but
++ * means receivers can't recover lost synch on their own (because
++ * new packets don't only start after a short RX).
++ */
++ size += sizeof(struct ethhdr) + dev->net->mtu + RX_EXTRA;
++ size += dev->port_usb->header_len;
++ size += out->maxpacket - 1;
++ size -= size % out->maxpacket;
++
++ skb = alloc_skb(size + NET_IP_ALIGN, gfp_flags);
++ if (skb == NULL) {
++ DBG(dev, "no rx skb\n");
++ goto enomem;
++ }
++
++ /* Some platforms perform better when IP packets are aligned,
++ * but on at least one, checksumming fails otherwise. Note:
++ * RNDIS headers involve variable numbers of LE32 values.
++ */
++ skb_reserve(skb, NET_IP_ALIGN);
++
++ req->buf = skb->data;
++ req->length = size;
++ req->complete = rx_complete;
++ req->context = skb;
++
++ retval = usb_ep_queue(out, req, gfp_flags);
++ if (retval == -ENOMEM)
++enomem:
++ defer_kevent(dev, WORK_RX_MEMORY);
++ if (retval) {
++ DBG(dev, "rx submit --> %d\n", retval);
++ if (skb)
++ dev_kfree_skb_any(skb);
++ spin_lock_irqsave(&dev->req_lock, flags);
++ list_add(&req->list, &dev->rx_reqs);
++ spin_unlock_irqrestore(&dev->req_lock, flags);
++ }
++ return retval;
++}
++
++static void rx_complete(struct usb_ep *ep, struct usb_request *req)
++{
++ struct sk_buff *skb = req->context;
++ struct eth_dev *dev = ep->driver_data;
++ int status = req->status;
++
++ switch (status) {
++
++ /* normal completion */
++ case 0:
++ skb_put(skb, req->actual);
++ if (dev->unwrap)
++ status = dev->unwrap(skb);
++ if (status < 0
++ || ETH_HLEN > skb->len
++ || skb->len > ETH_FRAME_LEN) {
++ dev->net->stats.rx_errors++;
++ dev->net->stats.rx_length_errors++;
++ DBG(dev, "rx length %d\n", skb->len);
++ break;
++ }
++
++ skb->protocol = eth_type_trans(skb, dev->net);
++ dev->net->stats.rx_packets++;
++ dev->net->stats.rx_bytes += skb->len;
++
++ /* no buffer copies needed, unless hardware can't
++ * use skb buffers.
++ */
++ status = netif_rx(skb);
++ skb = NULL;
++ break;
++
++ /* software-driven interface shutdown */
++ case -ECONNRESET: /* unlink */
++ case -ESHUTDOWN: /* disconnect etc */
++ VDBG(dev, "rx shutdown, code %d\n", status);
++ goto quiesce;
++
++ /* for hardware automagic (such as pxa) */
++ case -ECONNABORTED: /* endpoint reset */
++ DBG(dev, "rx %s reset\n", ep->name);
++ defer_kevent(dev, WORK_RX_MEMORY);
++quiesce:
++ dev_kfree_skb_any(skb);
++ goto clean;
++
++ /* data overrun */
++ case -EOVERFLOW:
++ dev->net->stats.rx_over_errors++;
++ /* FALLTHROUGH */
++
++ default:
++ dev->net->stats.rx_errors++;
++ DBG(dev, "rx status %d\n", status);
++ break;
++ }
++
++ if (skb)
++ dev_kfree_skb_any(skb);
++ if (!netif_running(dev->net)) {
++clean:
++ spin_lock(&dev->req_lock);
++ list_add(&req->list, &dev->rx_reqs);
++ spin_unlock(&dev->req_lock);
++ req = NULL;
++ }
++ if (req)
++ rx_submit(dev, req, GFP_ATOMIC);
++}
++
++static int prealloc(struct list_head *list, struct usb_ep *ep, unsigned n)
++{
++ unsigned i;
++ struct usb_request *req;
++
++ if (!n)
++ return -ENOMEM;
++
++ /* queue/recycle up to N requests */
++ i = n;
++ list_for_each_entry(req, list, list) {
++ if (i-- == 0)
++ goto extra;
++ }
++ while (i--) {
++ req = usb_ep_alloc_request(ep, GFP_ATOMIC);
++ if (!req)
++ return list_empty(list) ? -ENOMEM : 0;
++ list_add(&req->list, list);
++ }
++ return 0;
++
++extra:
++ /* free extras */
++ for (;;) {
++ struct list_head *next;
++
++ next = req->list.next;
++ list_del(&req->list);
++ usb_ep_free_request(ep, req);
++
++ if (next == list)
++ break;
++
++ req = container_of(next, struct usb_request, list);
++ }
++ return 0;
++}
++
++static int alloc_requests(struct eth_dev *dev, struct gether *link, unsigned n)
++{
++ int status;
++
++ spin_lock(&dev->req_lock);
++ status = prealloc(&dev->tx_reqs, link->in_ep, n);
++ if (status < 0)
++ goto fail;
++ status = prealloc(&dev->rx_reqs, link->out_ep, n);
++ if (status < 0)
++ goto fail;
++ goto done;
++fail:
++ DBG(dev, "can't alloc requests\n");
++done:
++ spin_unlock(&dev->req_lock);
++ return status;
++}
++
++static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags)
++{
++ struct usb_request *req;
++ unsigned long flags;
++
++ /* fill unused rxq slots with some skb */
++ spin_lock_irqsave(&dev->req_lock, flags);
++ while (!list_empty(&dev->rx_reqs)) {
++ req = container_of(dev->rx_reqs.next,
++ struct usb_request, list);
++ list_del_init(&req->list);
++ spin_unlock_irqrestore(&dev->req_lock, flags);
++
++ if (rx_submit(dev, req, gfp_flags) < 0) {
++ defer_kevent(dev, WORK_RX_MEMORY);
++ return;
++ }
++
++ spin_lock_irqsave(&dev->req_lock, flags);
++ }
++ spin_unlock_irqrestore(&dev->req_lock, flags);
++}
++
++static void eth_work(struct work_struct *work)
++{
++ struct eth_dev *dev = container_of(work, struct eth_dev, work);
++
++ if (test_and_clear_bit(WORK_RX_MEMORY, &dev->todo)) {
++ if (netif_running(dev->net))
++ rx_fill(dev, GFP_KERNEL);
++ }
++
++ if (dev->todo)
++ DBG(dev, "work done, flags = 0x%lx\n", dev->todo);
++}
++
++static void tx_complete(struct usb_ep *ep, struct usb_request *req)
++{
++ struct sk_buff *skb = req->context;
++ struct eth_dev *dev = ep->driver_data;
++
++ switch (req->status) {
++ default:
++ dev->net->stats.tx_errors++;
++ VDBG(dev, "tx err %d\n", req->status);
++ /* FALLTHROUGH */
++ case -ECONNRESET: /* unlink */
++ case -ESHUTDOWN: /* disconnect etc */
++ break;
++ case 0:
++ dev->net->stats.tx_bytes += skb->len;
++ }
++ dev->net->stats.tx_packets++;
++
++ spin_lock(&dev->req_lock);
++ list_add(&req->list, &dev->tx_reqs);
++ spin_unlock(&dev->req_lock);
++ dev_kfree_skb_any(skb);
++
++ atomic_dec(&dev->tx_qlen);
++ if (netif_carrier_ok(dev->net))
++ netif_wake_queue(dev->net);
++}
++
++static inline int is_promisc(u16 cdc_filter)
++{
++ return cdc_filter & USB_CDC_PACKET_TYPE_PROMISCUOUS;
++}
++
++static int eth_start_xmit(struct sk_buff *skb, struct net_device *net)
++{
++ struct eth_dev *dev = netdev_priv(net);
++ int length = skb->len;
++ int retval;
++ struct usb_request *req = NULL;
++ unsigned long flags;
++ struct usb_ep *in;
++ u16 cdc_filter;
++
++ spin_lock_irqsave(&dev->lock, flags);
++ if (dev->port_usb) {
++ in = dev->port_usb->in_ep;
++ cdc_filter = dev->port_usb->cdc_filter;
++ } else {
++ in = NULL;
++ cdc_filter = 0;
++ }
++ spin_unlock_irqrestore(&dev->lock, flags);
++
++ if (!in) {
++ dev_kfree_skb_any(skb);
++ return 0;
++ }
++
++ /* apply outgoing CDC or RNDIS filters */
++ if (!is_promisc(cdc_filter)) {
++ u8 *dest = skb->data;
++
++ if (is_multicast_ether_addr(dest)) {
++ u16 type;
++
++ /* ignores USB_CDC_PACKET_TYPE_MULTICAST and host
++ * SET_ETHERNET_MULTICAST_FILTERS requests
++ */
++ if (is_broadcast_ether_addr(dest))
++ type = USB_CDC_PACKET_TYPE_BROADCAST;
++ else
++ type = USB_CDC_PACKET_TYPE_ALL_MULTICAST;
++ if (!(cdc_filter & type)) {
++ dev_kfree_skb_any(skb);
++ return 0;
++ }
++ }
++ /* ignores USB_CDC_PACKET_TYPE_DIRECTED */
++ }
++
++ spin_lock_irqsave(&dev->req_lock, flags);
++ /*
++ * this freelist can be empty if an interrupt triggered disconnect()
++ * and reconfigured the gadget (shutting down this queue) after the
++ * network stack decided to xmit but before we got the spinlock.
++ */
++ if (list_empty(&dev->tx_reqs)) {
++ spin_unlock_irqrestore(&dev->req_lock, flags);
++ return 1;
++ }
++
++ req = container_of(dev->tx_reqs.next, struct usb_request, list);
++ list_del(&req->list);
++
++ /* temporarily stop TX queue when the freelist empties */
++ if (list_empty(&dev->tx_reqs))
++ netif_stop_queue(net);
++ spin_unlock_irqrestore(&dev->req_lock, flags);
++
++ /* no buffer copies needed, unless the network stack did it
++ * or the hardware can't use skb buffers.
++ * or there's not enough space for extra headers we need
++ */
++ if (dev->wrap) {
++ struct sk_buff *skb_new;
++
++ skb_new = dev->wrap(skb);
++ if (!skb_new)
++ goto drop;
++
++ dev_kfree_skb_any(skb);
++ skb = skb_new;
++ length = skb->len;
++ }
++ req->buf = skb->data;
++ req->context = skb;
++ req->complete = tx_complete;
++
++ /* use zlp framing on tx for strict CDC-Ether conformance,
++ * though any robust network rx path ignores extra padding.
++ * and some hardware doesn't like to write zlps.
++ */
++ req->zero = 1;
++ if (!dev->zlp && (length % in->maxpacket) == 0)
++ length++;
++
++ req->length = length;
++
++ /* throttle highspeed IRQ rate back slightly */
++ if (gadget_is_dualspeed(dev->gadget))
++ req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH)
++ ? ((atomic_read(&dev->tx_qlen) % qmult) != 0)
++ : 0;
++
++ retval = usb_ep_queue(in, req, GFP_ATOMIC);
++ switch (retval) {
++ default:
++ DBG(dev, "tx queue err %d\n", retval);
++ break;
++ case 0:
++ net->trans_start = jiffies;
++ atomic_inc(&dev->tx_qlen);
++ }
++
++ if (retval) {
++drop:
++ dev->net->stats.tx_dropped++;
++ dev_kfree_skb_any(skb);
++ spin_lock_irqsave(&dev->req_lock, flags);
++ if (list_empty(&dev->tx_reqs))
++ netif_start_queue(net);
++ list_add(&req->list, &dev->tx_reqs);
++ spin_unlock_irqrestore(&dev->req_lock, flags);
++ }
++ return 0;
++}
++
++/*-------------------------------------------------------------------------*/
++
++static void eth_start(struct eth_dev *dev, gfp_t gfp_flags)
++{
++ DBG(dev, "%s\n", __func__);
++
++ /* fill the rx queue */
++ rx_fill(dev, gfp_flags);
++
++ /* and open the tx floodgates */
++ atomic_set(&dev->tx_qlen, 0);
++ netif_wake_queue(dev->net);
++}
++
++static int eth_open(struct net_device *net)
++{
++ struct eth_dev *dev = netdev_priv(net);
++ struct gether *link;
++
++ DBG(dev, "%s\n", __func__);
++ if (netif_carrier_ok(dev->net))
++ eth_start(dev, GFP_KERNEL);
++
++ spin_lock_irq(&dev->lock);
++ link = dev->port_usb;
++ if (link && link->open)
++ link->open(link);
++ spin_unlock_irq(&dev->lock);
++
++ return 0;
++}
++
++static int eth_stop(struct net_device *net)
++{
++ struct eth_dev *dev = netdev_priv(net);
++ unsigned long flags;
++
++ VDBG(dev, "%s\n", __func__);
++ netif_stop_queue(net);
++
++ DBG(dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld\n",
++ dev->net->stats.rx_packets, dev->net->stats.tx_packets,
++ dev->net->stats.rx_errors, dev->net->stats.tx_errors
++ );
++
++ /* ensure there are no more active requests */
++ spin_lock_irqsave(&dev->lock, flags);
++ if (dev->port_usb) {
++ struct gether *link = dev->port_usb;
++
++ if (link->close)
++ link->close(link);
++
++ /* NOTE: we have no abort-queue primitive we could use
++ * to cancel all pending I/O. Instead, we disable then
++ * reenable the endpoints ... this idiom may leave toggle
++ * wrong, but that's a self-correcting error.
++ *
++ * REVISIT: we *COULD* just let the transfers complete at
++ * their own pace; the network stack can handle old packets.
++ * For the moment we leave this here, since it works.
++ */
++ usb_ep_disable(link->in_ep);
++ usb_ep_disable(link->out_ep);
++ if (netif_carrier_ok(net)) {
++ DBG(dev, "host still using in/out endpoints\n");
++ usb_ep_enable(link->in_ep, link->in);
++ usb_ep_enable(link->out_ep, link->out);
++ }
++ }
++ spin_unlock_irqrestore(&dev->lock, flags);
++
++ return 0;
++}
++
++/*-------------------------------------------------------------------------*/
++
++/* initial value, changed by "ifconfig usb0 hw ether xx:xx:xx:xx:xx:xx" */
++static char *dev_addr;
++module_param(dev_addr, charp, S_IRUGO);
++MODULE_PARM_DESC(dev_addr, "Device Ethernet Address");
++
++/* this address is invisible to ifconfig */
++static char *host_addr;
++module_param(host_addr, charp, S_IRUGO);
++MODULE_PARM_DESC(host_addr, "Host Ethernet Address");
++
++
++static u8 __init nibble(unsigned char c)
++{
++ if (isdigit(c))
++ return c - '0';
++ c = toupper(c);
++ if (isxdigit(c))
++ return 10 + c - 'A';
++ return 0;
++}
++
++static int __init get_ether_addr(const char *str, u8 *dev_addr)
++{
++ if (str) {
++ unsigned i;
++
++ for (i = 0; i < 6; i++) {
++ unsigned char num;
++
++ if ((*str == '.') || (*str == ':'))
++ str++;
++ num = nibble(*str++) << 4;
++ num |= (nibble(*str++));
++ dev_addr [i] = num;
++ }
++ if (is_valid_ether_addr(dev_addr))
++ return 0;
++ }
++ random_ether_addr(dev_addr);
++ return 1;
++}
++
++static struct eth_dev *the_dev;
++
++
++/**
++ * gether_setup - initialize one ethernet-over-usb link
++ * @g: gadget to associated with these links
++ * @ethaddr: NULL, or a buffer in which the ethernet address of the
++ * host side of the link is recorded
++ * Context: may sleep
++ *
++ * This sets up the single network link that may be exported by a
++ * gadget driver using this framework. The link layer addresses are
++ * set up using module parameters.
++ *
++ * Returns negative errno, or zero on success
++ */
++int __init gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN])
++{
++ struct eth_dev *dev;
++ struct net_device *net;
++ int status;
++
++ if (the_dev)
++ return -EBUSY;
++
++ net = alloc_etherdev(sizeof *dev);
++ if (!net)
++ return -ENOMEM;
++
++ dev = netdev_priv(net);
++ spin_lock_init(&dev->lock);
++ spin_lock_init(&dev->req_lock);
++ INIT_WORK(&dev->work, eth_work);
++ INIT_LIST_HEAD(&dev->tx_reqs);
++ INIT_LIST_HEAD(&dev->rx_reqs);
++
++ /* network device setup */
++ dev->net = net;
++ strcpy(net->name, "usb%d");
++
++ if (get_ether_addr(dev_addr, net->dev_addr))
++ dev_warn(&g->dev,
++ "using random %s ethernet address\n", "self");
++ if (get_ether_addr(host_addr, dev->host_mac))
++ dev_warn(&g->dev,
++ "using random %s ethernet address\n", "host");
++
++ if (ethaddr)
++ memcpy(ethaddr, dev->host_mac, ETH_ALEN);
++
++ net->change_mtu = eth_change_mtu;
++ net->hard_start_xmit = eth_start_xmit;
++ net->open = eth_open;
++ net->stop = eth_stop;
++ /* watchdog_timeo, tx_timeout ... */
++ /* set_multicast_list */
++ SET_ETHTOOL_OPS(net, &ops);
++
++ /* two kinds of host-initiated state changes:
++ * - iff DATA transfer is active, carrier is "on"
++ * - tx queueing enabled if open *and* carrier is "on"
++ */
++ netif_stop_queue(net);
++ netif_carrier_off(net);
++
++ dev->gadget = g;
++ SET_NETDEV_DEV(net, &g->dev);
++
++ status = register_netdev(net);
++ if (status < 0) {
++ dev_dbg(&g->dev, "register_netdev failed, %d\n", status);
++ free_netdev(net);
++ } else {
++ DECLARE_MAC_BUF(tmp);
++
++ INFO(dev, "MAC %s\n", print_mac(tmp, net->dev_addr));
++ INFO(dev, "HOST MAC %s\n", print_mac(tmp, dev->host_mac));
++
++ the_dev = dev;
++ }
++
++ return status;
++}
++
++/**
++ * gether_cleanup - remove Ethernet-over-USB device
++ * Context: may sleep
++ *
++ * This is called to free all resources allocated by @gether_setup().
++ */
++void gether_cleanup(void)
++{
++ if (!the_dev)
++ return;
++
++ unregister_netdev(the_dev->net);
++ free_netdev(the_dev->net);
++
++ /* assuming we used keventd, it must quiesce too */
++ flush_scheduled_work();
++
++ the_dev = NULL;
++}
++
++
++/**
++ * gether_connect - notify network layer that USB link is active
++ * @link: the USB link, set up with endpoints, descriptors matching
++ * current device speed, and any framing wrapper(s) set up.
++ * Context: irqs blocked
++ *
++ * This is called to activate endpoints and let the network layer know
++ * the connection is active ("carrier detect"). It may cause the I/O
++ * queues to open and start letting network packets flow, but will in
++ * any case activate the endpoints so that they respond properly to the
++ * USB host.
++ *
++ * Verify net_device pointer returned using IS_ERR(). If it doesn't
++ * indicate some error code (negative errno), ep->driver_data values
++ * have been overwritten.
++ */
++struct net_device *gether_connect(struct gether *link)
++{
++ struct eth_dev *dev = the_dev;
++ int result = 0;
++
++ if (!dev)
++ return ERR_PTR(-EINVAL);
++
++ link->in_ep->driver_data = dev;
++ result = usb_ep_enable(link->in_ep, link->in);
++ if (result != 0) {
++ DBG(dev, "enable %s --> %d\n",
++ link->in_ep->name, result);
++ goto fail0;
++ }
++
++ link->out_ep->driver_data = dev;
++ result = usb_ep_enable(link->out_ep, link->out);
++ if (result != 0) {
++ DBG(dev, "enable %s --> %d\n",
++ link->out_ep->name, result);
++ goto fail1;
++ }
++
++ if (result == 0)
++ result = alloc_requests(dev, link, qlen(dev->gadget));
++
++ if (result == 0) {
++ dev->zlp = link->is_zlp_ok;
++ DBG(dev, "qlen %d\n", qlen(dev->gadget));
++
++ dev->header_len = link->header_len;
++ dev->unwrap = link->unwrap;
++ dev->wrap = link->wrap;
++
++ spin_lock(&dev->lock);
++ dev->port_usb = link;
++ link->ioport = dev;
++ spin_unlock(&dev->lock);
++
++ netif_carrier_on(dev->net);
++ if (netif_running(dev->net))
++ eth_start(dev, GFP_ATOMIC);
++
++ /* on error, disable any endpoints */
++ } else {
++ (void) usb_ep_disable(link->out_ep);
++fail1:
++ (void) usb_ep_disable(link->in_ep);
++ }
++fail0:
++ /* caller is responsible for cleanup on error */
++ if (result < 0)
++ return ERR_PTR(result);
++ return dev->net;
++}
++
++/**
++ * gether_disconnect - notify network layer that USB link is inactive
++ * @link: the USB link, on which gether_connect() was called
++ * Context: irqs blocked
++ *
++ * This is called to deactivate endpoints and let the network layer know
++ * the connection went inactive ("no carrier").
++ *
++ * On return, the state is as if gether_connect() had never been called.
++ * The endpoints are inactive, and accordingly without active USB I/O.
++ * Pointers to endpoint descriptors and endpoint private data are nulled.
++ */
++void gether_disconnect(struct gether *link)
++{
++ struct eth_dev *dev = link->ioport;
++ struct usb_request *req;
++
++ WARN_ON(!dev);
++ if (!dev)
++ return;
++
++ DBG(dev, "%s\n", __func__);
++
++ netif_stop_queue(dev->net);
++ netif_carrier_off(dev->net);
++
++ /* disable endpoints, forcing (synchronous) completion
++ * of all pending i/o. then free the request objects
++ * and forget about the endpoints.
++ */
++ usb_ep_disable(link->in_ep);
++ spin_lock(&dev->req_lock);
++ while (!list_empty(&dev->tx_reqs)) {
++ req = container_of(dev->tx_reqs.next,
++ struct usb_request, list);
++ list_del(&req->list);
++
++ spin_unlock(&dev->req_lock);
++ usb_ep_free_request(link->in_ep, req);
++ spin_lock(&dev->req_lock);
++ }
++ spin_unlock(&dev->req_lock);
++ link->in_ep->driver_data = NULL;
++ link->in = NULL;
++
++ usb_ep_disable(link->out_ep);
++ spin_lock(&dev->req_lock);
++ while (!list_empty(&dev->rx_reqs)) {
++ req = container_of(dev->rx_reqs.next,
++ struct usb_request, list);
++ list_del(&req->list);
++
++ spin_unlock(&dev->req_lock);
++ usb_ep_free_request(link->out_ep, req);
++ spin_lock(&dev->req_lock);
++ }
++ spin_unlock(&dev->req_lock);
++ link->out_ep->driver_data = NULL;
++ link->out = NULL;
++
++ /* finish forgetting about this USB link episode */
++ dev->header_len = 0;
++ dev->unwrap = NULL;
++ dev->wrap = NULL;
++
++ spin_lock(&dev->lock);
++ dev->port_usb = NULL;
++ link->ioport = NULL;
++ spin_unlock(&dev->lock);
++}
+diff --git a/drivers/usb/gadget/u_ether.h b/drivers/usb/gadget/u_ether.h
+new file mode 100644
+index 0000000..0d1f7ae
+--- /dev/null
++++ b/drivers/usb/gadget/u_ether.h
+@@ -0,0 +1,127 @@
++/*
++ * u_ether.h -- interface to USB gadget "ethernet link" utilities
++ *
++ * Copyright (C) 2003-2005,2008 David Brownell
++ * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger
++ * Copyright (C) 2008 Nokia Corporation
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++#ifndef __U_ETHER_H
++#define __U_ETHER_H
++
++#include <linux/err.h>
++#include <linux/if_ether.h>
++#include <linux/usb/composite.h>
++#include <linux/usb/cdc.h>
++
++#include "gadget_chips.h"
++
++
++/*
++ * This represents the USB side of an "ethernet" link, managed by a USB
++ * function which provides control and (maybe) framing. Two functions
++ * in different configurations could share the same ethernet link/netdev,
++ * using different host interaction models.
++ *
++ * There is a current limitation that only one instance of this link may
++ * be present in any given configuration. When that's a problem, network
++ * layer facilities can be used to package multiple logical links on this
++ * single "physical" one.
++ */
++struct gether {
++ struct usb_function func;
++
++ /* updated by gether_{connect,disconnect} */
++ struct eth_dev *ioport;
++
++ /* endpoints handle full and/or high speeds */
++ struct usb_ep *in_ep;
++ struct usb_ep *out_ep;
++
++ /* descriptors match device speed at gether_connect() time */
++ struct usb_endpoint_descriptor *in;
++ struct usb_endpoint_descriptor *out;
++
++ bool is_zlp_ok;
++
++ u16 cdc_filter;
++
++ /* hooks for added framing, as needed for RNDIS and EEM.
++ * we currently don't support multiple frames per SKB.
++ */
++ u32 header_len;
++ struct sk_buff *(*wrap)(struct sk_buff *skb);
++ int (*unwrap)(struct sk_buff *skb);
++
++ /* called on network open/close */
++ void (*open)(struct gether *);
++ void (*close)(struct gether *);
++};
++
++#define DEFAULT_FILTER (USB_CDC_PACKET_TYPE_BROADCAST \
++ |USB_CDC_PACKET_TYPE_ALL_MULTICAST \
++ |USB_CDC_PACKET_TYPE_PROMISCUOUS \
++ |USB_CDC_PACKET_TYPE_DIRECTED)
++
++
++/* netdev setup/teardown as directed by the gadget driver */
++int gether_setup(struct usb_gadget *g, u8 ethaddr[ETH_ALEN]);
++void gether_cleanup(void);
++
++/* connect/disconnect is handled by individual functions */
++struct net_device *gether_connect(struct gether *);
++void gether_disconnect(struct gether *);
++
++/* Some controllers can't support CDC Ethernet (ECM) ... */
++static inline bool can_support_ecm(struct usb_gadget *gadget)
++{
++ if (!gadget_supports_altsettings(gadget))
++ return false;
++
++ /* SA1100 can do ECM, *without* status endpoint ... but we'll
++ * only use it in non-ECM mode for backwards compatibility
++ * (and since we currently require a status endpoint)
++ */
++ if (gadget_is_sa1100(gadget))
++ return false;
++
++ /* Everything else is *presumably* fine ... but this is a bit
++ * chancy, so be **CERTAIN** there are no hardware issues with
++ * your controller. Add it above if it can't handle CDC.
++ */
++ return true;
++}
++
++/* each configuration may bind one instance of an ethernet link */
++int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
++int ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
++
++#ifdef CONFIG_USB_ETH_RNDIS
++
++int rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN]);
++
++#else
++
++static inline int
++rndis_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN])
++{
++ return 0;
++}
++
++#endif
++
++#endif /* __U_ETHER_H */
+diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/u_serial.c
+new file mode 100644
+index 0000000..53d5928
+--- /dev/null
++++ b/drivers/usb/gadget/u_serial.c
+@@ -0,0 +1,1330 @@
++/*
++ * u_serial.c - utilities for USB gadget "serial port"/TTY support
++ *
++ * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
++ * Copyright (C) 2008 David Brownell
++ * Copyright (C) 2008 by Nokia Corporation
++ *
++ * This code also borrows from usbserial.c, which is
++ * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com)
++ * Copyright (C) 2000 Peter Berger (pberger@brimson.com)
++ * Copyright (C) 2000 Al Borchers (alborchers@steinerpoint.com)
++ *
++ * This software is distributed under the terms of the GNU General
++ * Public License ("GPL") as published by the Free Software Foundation,
++ * either version 2 of that License or (at your option) any later version.
++ */
++
++/* #define VERBOSE_DEBUG */
++
++#include <linux/kernel.h>
++#include <linux/interrupt.h>
++#include <linux/device.h>
++#include <linux/delay.h>
++#include <linux/tty.h>
++#include <linux/tty_flip.h>
++
++#include "u_serial.h"
++
++
++/*
++ * This component encapsulates the TTY layer glue needed to provide basic
++ * "serial port" functionality through the USB gadget stack. Each such
++ * port is exposed through a /dev/ttyGS* node.
++ *
++ * After initialization (gserial_setup), these TTY port devices stay
++ * available until they are removed (gserial_cleanup). Each one may be
++ * connected to a USB function (gserial_connect), or disconnected (with
++ * gserial_disconnect) when the USB host issues a config change event.
++ * Data can only flow when the port is connected to the host.
++ *
++ * A given TTY port can be made available in multiple configurations.
++ * For example, each one might expose a ttyGS0 node which provides a
++ * login application. In one case that might use CDC ACM interface 0,
++ * while another configuration might use interface 3 for that. The
++ * work to handle that (including descriptor management) is not part
++ * of this component.
++ *
++ * Configurations may expose more than one TTY port. For example, if
++ * ttyGS0 provides login service, then ttyGS1 might provide dialer access
++ * for a telephone or fax link. And ttyGS2 might be something that just
++ * needs a simple byte stream interface for some messaging protocol that
++ * is managed in userspace ... OBEX, PTP, and MTP have been mentioned.
++ */
++
++#define PREFIX "ttyGS"
++
++/*
++ * gserial is the lifecycle interface, used by USB functions
++ * gs_port is the I/O nexus, used by the tty driver
++ * tty_struct links to the tty/filesystem framework
++ *
++ * gserial <---> gs_port ... links will be null when the USB link is
++ * inactive; managed by gserial_{connect,disconnect}(). each gserial
++ * instance can wrap its own USB control protocol.
++ * gserial->ioport == usb_ep->driver_data ... gs_port
++ * gs_port->port_usb ... gserial
++ *
++ * gs_port <---> tty_struct ... links will be null when the TTY file
++ * isn't opened; managed by gs_open()/gs_close()
++ * gserial->port_tty ... tty_struct
++ * tty_struct->driver_data ... gserial
++ */
++
++/* RX and TX queues can buffer QUEUE_SIZE packets before they hit the
++ * next layer of buffering. For TX that's a circular buffer; for RX
++ * consider it a NOP. A third layer is provided by the TTY code.
++ */
++#define QUEUE_SIZE 16
++#define WRITE_BUF_SIZE 8192 /* TX only */
++
++/* circular buffer */
++struct gs_buf {
++ unsigned buf_size;
++ char *buf_buf;
++ char *buf_get;
++ char *buf_put;
++};
++
++/*
++ * The port structure holds info for each port, one for each minor number
++ * (and thus for each /dev/ node).
++ */
++struct gs_port {
++ spinlock_t port_lock; /* guard port_* access */
++
++ struct gserial *port_usb;
++ struct tty_struct *port_tty;
++
++ unsigned open_count;
++ bool openclose; /* open/close in progress */
++ u8 port_num;
++
++ wait_queue_head_t close_wait; /* wait for last close */
++
++ struct list_head read_pool;
++ struct list_head read_queue;
++ unsigned n_read;
++ struct tasklet_struct push;
++
++ struct list_head write_pool;
++ struct gs_buf port_write_buf;
++ wait_queue_head_t drain_wait; /* wait while writes drain */
++
++ /* REVISIT this state ... */
++ struct usb_cdc_line_coding port_line_coding; /* 8-N-1 etc */
++};
++
++/* increase N_PORTS if you need more */
++#define N_PORTS 4
++static struct portmaster {
++ struct mutex lock; /* protect open/close */
++ struct gs_port *port;
++} ports[N_PORTS];
++static unsigned n_ports;
++
++#define GS_CLOSE_TIMEOUT 15 /* seconds */
++
++
++
++#ifdef VERBOSE_DEBUG
++#define pr_vdebug(fmt, arg...) \
++ pr_debug(fmt, ##arg)
++#else
++#define pr_vdebug(fmt, arg...) \
++ ({ if (0) pr_debug(fmt, ##arg); })
++#endif
++
++/*-------------------------------------------------------------------------*/
++
++/* Circular Buffer */
++
++/*
++ * gs_buf_alloc
++ *
++ * Allocate a circular buffer and all associated memory.
++ */
++static int gs_buf_alloc(struct gs_buf *gb, unsigned size)
++{
++ gb->buf_buf = kmalloc(size, GFP_KERNEL);
++ if (gb->buf_buf == NULL)
++ return -ENOMEM;
++
++ gb->buf_size = size;
++ gb->buf_put = gb->buf_buf;
++ gb->buf_get = gb->buf_buf;
++
++ return 0;
++}
++
++/*
++ * gs_buf_free
++ *
++ * Free the buffer and all associated memory.
++ */
++static void gs_buf_free(struct gs_buf *gb)
++{
++ kfree(gb->buf_buf);
++ gb->buf_buf = NULL;
++}
++
++/*
++ * gs_buf_clear
++ *
++ * Clear out all data in the circular buffer.
++ */
++static void gs_buf_clear(struct gs_buf *gb)
++{
++ gb->buf_get = gb->buf_put;
++ /* equivalent to a get of all data available */
++}
++
++/*
++ * gs_buf_data_avail
++ *
++ * Return the number of bytes of data written into the circular
++ * buffer.
++ */
++static unsigned gs_buf_data_avail(struct gs_buf *gb)
++{
++ return (gb->buf_size + gb->buf_put - gb->buf_get) % gb->buf_size;
++}
++
++/*
++ * gs_buf_space_avail
++ *
++ * Return the number of bytes of space available in the circular
++ * buffer.
++ */
++static unsigned gs_buf_space_avail(struct gs_buf *gb)
++{
++ return (gb->buf_size + gb->buf_get - gb->buf_put - 1) % gb->buf_size;
++}
++
++/*
++ * gs_buf_put
++ *
++ * Copy data data from a user buffer and put it into the circular buffer.
++ * Restrict to the amount of space available.
++ *
++ * Return the number of bytes copied.
++ */
++static unsigned
++gs_buf_put(struct gs_buf *gb, const char *buf, unsigned count)
++{
++ unsigned len;
++
++ len = gs_buf_space_avail(gb);
++ if (count > len)
++ count = len;
++
++ if (count == 0)
++ return 0;
++
++ len = gb->buf_buf + gb->buf_size - gb->buf_put;
++ if (count > len) {
++ memcpy(gb->buf_put, buf, len);
++ memcpy(gb->buf_buf, buf+len, count - len);
++ gb->buf_put = gb->buf_buf + count - len;
++ } else {
++ memcpy(gb->buf_put, buf, count);
++ if (count < len)
++ gb->buf_put += count;
++ else /* count == len */
++ gb->buf_put = gb->buf_buf;
++ }
++
++ return count;
++}
++
++/*
++ * gs_buf_get
++ *
++ * Get data from the circular buffer and copy to the given buffer.
++ * Restrict to the amount of data available.
++ *
++ * Return the number of bytes copied.
++ */
++static unsigned
++gs_buf_get(struct gs_buf *gb, char *buf, unsigned count)
++{
++ unsigned len;
++
++ len = gs_buf_data_avail(gb);
++ if (count > len)
++ count = len;
++
++ if (count == 0)
++ return 0;
++
++ len = gb->buf_buf + gb->buf_size - gb->buf_get;
++ if (count > len) {
++ memcpy(buf, gb->buf_get, len);
++ memcpy(buf+len, gb->buf_buf, count - len);
++ gb->buf_get = gb->buf_buf + count - len;
++ } else {
++ memcpy(buf, gb->buf_get, count);
++ if (count < len)
++ gb->buf_get += count;
++ else /* count == len */
++ gb->buf_get = gb->buf_buf;
++ }
++
++ return count;
++}
++
++/*-------------------------------------------------------------------------*/
++
++/* I/O glue between TTY (upper) and USB function (lower) driver layers */
++
++/*
++ * gs_alloc_req
++ *
++ * Allocate a usb_request and its buffer. Returns a pointer to the
++ * usb_request or NULL if there is an error.
++ */
++struct usb_request *
++gs_alloc_req(struct usb_ep *ep, unsigned len, gfp_t kmalloc_flags)
++{
++ struct usb_request *req;
++
++ req = usb_ep_alloc_request(ep, kmalloc_flags);
++
++ if (req != NULL) {
++ req->length = len;
++ req->buf = kmalloc(len, kmalloc_flags);
++ if (req->buf == NULL) {
++ usb_ep_free_request(ep, req);
++ return NULL;
++ }
++ }
++
++ return req;
++}
++
++/*
++ * gs_free_req
++ *
++ * Free a usb_request and its buffer.
++ */
++void gs_free_req(struct usb_ep *ep, struct usb_request *req)
++{
++ kfree(req->buf);
++ usb_ep_free_request(ep, req);
++}
++
++/*
++ * gs_send_packet
++ *
++ * If there is data to send, a packet is built in the given
++ * buffer and the size is returned. If there is no data to
++ * send, 0 is returned.
++ *
++ * Called with port_lock held.
++ */
++static unsigned
++gs_send_packet(struct gs_port *port, char *packet, unsigned size)
++{
++ unsigned len;
++
++ len = gs_buf_data_avail(&port->port_write_buf);
++ if (len < size)
++ size = len;
++ if (size != 0)
++ size = gs_buf_get(&port->port_write_buf, packet, size);
++ return size;
++}
++
++/*
++ * gs_start_tx
++ *
++ * This function finds available write requests, calls
++ * gs_send_packet to fill these packets with data, and
++ * continues until either there are no more write requests
++ * available or no more data to send. This function is
++ * run whenever data arrives or write requests are available.
++ *
++ * Context: caller owns port_lock; port_usb is non-null.
++ */
++static int gs_start_tx(struct gs_port *port)
++/*
++__releases(&port->port_lock)
++__acquires(&port->port_lock)
++*/
++{
++ struct list_head *pool = &port->write_pool;
++ struct usb_ep *in = port->port_usb->in;
++ int status = 0;
++ bool do_tty_wake = false;
++
++ while (!list_empty(pool)) {
++ struct usb_request *req;
++ int len;
++
++ req = list_entry(pool->next, struct usb_request, list);
++ len = gs_send_packet(port, req->buf, in->maxpacket);
++ if (len == 0) {
++ wake_up_interruptible(&port->drain_wait);
++ break;
++ }
++ do_tty_wake = true;
++
++ req->length = len;
++ list_del(&req->list);
++
++ pr_vdebug(PREFIX "%d: tx len=%d, 0x%02x 0x%02x 0x%02x ...\n",
++ port->port_num, len, *((u8 *)req->buf),
++ *((u8 *)req->buf+1), *((u8 *)req->buf+2));
++
++ /* Drop lock while we call out of driver; completions
++ * could be issued while we do so. Disconnection may
++ * happen too; maybe immediately before we queue this!
++ *
++ * NOTE that we may keep sending data for a while after
++ * the TTY closed (dev->ioport->port_tty is NULL).
++ */
++ spin_unlock(&port->port_lock);
++ status = usb_ep_queue(in, req, GFP_ATOMIC);
++ spin_lock(&port->port_lock);
++
++ if (status) {
++ pr_debug("%s: %s %s err %d\n",
++ __func__, "queue", in->name, status);
++ list_add(&req->list, pool);
++ break;
++ }
++
++ /* abort immediately after disconnect */
++ if (!port->port_usb)
++ break;
++ }
++
++ if (do_tty_wake && port->port_tty)
++ tty_wakeup(port->port_tty);
++ return status;
++}
++
++/*
++ * Context: caller owns port_lock, and port_usb is set
++ */
++static unsigned gs_start_rx(struct gs_port *port)
++/*
++__releases(&port->port_lock)
++__acquires(&port->port_lock)
++*/
++{
++ struct list_head *pool = &port->read_pool;
++ struct usb_ep *out = port->port_usb->out;
++ unsigned started = 0;
++
++ while (!list_empty(pool)) {
++ struct usb_request *req;
++ int status;
++ struct tty_struct *tty;
++
++ /* no more rx if closed */
++ tty = port->port_tty;
++ if (!tty)
++ break;
++
++ req = list_entry(pool->next, struct usb_request, list);
++ list_del(&req->list);
++ req->length = out->maxpacket;
++
++ /* drop lock while we call out; the controller driver
++ * may need to call us back (e.g. for disconnect)
++ */
++ spin_unlock(&port->port_lock);
++ status = usb_ep_queue(out, req, GFP_ATOMIC);
++ spin_lock(&port->port_lock);
++
++ if (status) {
++ pr_debug("%s: %s %s err %d\n",
++ __func__, "queue", out->name, status);
++ list_add(&req->list, pool);
++ break;
++ }
++ started++;
++
++ /* abort immediately after disconnect */
++ if (!port->port_usb)
++ break;
++ }
++ return started;
++}
++
++/*
++ * RX tasklet takes data out of the RX queue and hands it up to the TTY
++ * layer until it refuses to take any more data (or is throttled back).
++ * Then it issues reads for any further data.
++ *
++ * If the RX queue becomes full enough that no usb_request is queued,
++ * the OUT endpoint may begin NAKing as soon as its FIFO fills up.
++ * So QUEUE_SIZE packets plus however many the FIFO holds (usually two)
++ * can be buffered before the TTY layer's buffers (currently 64 KB).
++ */
++static void gs_rx_push(unsigned long _port)
++{
++ struct gs_port *port = (void *)_port;
++ struct tty_struct *tty;
++ struct list_head *queue = &port->read_queue;
++ bool disconnect = false;
++ bool do_push = false;
++
++ /* hand any queued data to the tty */
++ spin_lock_irq(&port->port_lock);
++ tty = port->port_tty;
++ while (!list_empty(queue)) {
++ struct usb_request *req;
++
++ req = list_first_entry(queue, struct usb_request, list);
++
++ /* discard data if tty was closed */
++ if (!tty)
++ goto recycle;
++
++ /* leave data queued if tty was rx throttled */
++ if (test_bit(TTY_THROTTLED, &tty->flags))
++ break;
++
++ switch (req->status) {
++ case -ESHUTDOWN:
++ disconnect = true;
++ pr_vdebug(PREFIX "%d: shutdown\n", port->port_num);
++ break;
++
++ default:
++ /* presumably a transient fault */
++ pr_warning(PREFIX "%d: unexpected RX status %d\n",
++ port->port_num, req->status);
++ /* FALLTHROUGH */
++ case 0:
++ /* normal completion */
++ break;
++ }
++
++ /* push data to (open) tty */
++ if (req->actual) {
++ char *packet = req->buf;
++ unsigned size = req->actual;
++ unsigned n;
++ int count;
++
++ /* we may have pushed part of this packet already... */
++ n = port->n_read;
++ if (n) {
++ packet += n;
++ size -= n;
++ }
++
++ count = tty_insert_flip_string(tty, packet, size);
++ if (count)
++ do_push = true;
++ if (count != size) {
++ /* stop pushing; TTY layer can't handle more */
++ port->n_read += count;
++ pr_vdebug(PREFIX "%d: rx block %d/%d\n",
++ port->port_num,
++ count, req->actual);
++ break;
++ }
++ port->n_read = 0;
++ }
++recycle:
++ list_move(&req->list, &port->read_pool);
++ }
++
++ /* Push from tty to ldisc; this is immediate with low_latency, and
++ * may trigger callbacks to this driver ... so drop the spinlock.
++ */
++ if (tty && do_push) {
++ spin_unlock_irq(&port->port_lock);
++ tty_flip_buffer_push(tty);
++ wake_up_interruptible(&tty->read_wait);
++ spin_lock_irq(&port->port_lock);
++
++ /* tty may have been closed */
++ tty = port->port_tty;
++ }
++
++
++ /* We want our data queue to become empty ASAP, keeping data
++ * in the tty and ldisc (not here). If we couldn't push any
++ * this time around, there may be trouble unless there's an
++ * implicit tty_unthrottle() call on its way...
++ *
++ * REVISIT we should probably add a timer to keep the tasklet
++ * from starving ... but it's not clear that case ever happens.
++ */
++ if (!list_empty(queue) && tty) {
++ if (!test_bit(TTY_THROTTLED, &tty->flags)) {
++ if (do_push)
++ tasklet_schedule(&port->push);
++ else
++ pr_warning(PREFIX "%d: RX not scheduled?\n",
++ port->port_num);
++ }
++ }
++
++ /* If we're still connected, refill the USB RX queue. */
++ if (!disconnect && port->port_usb)
++ gs_start_rx(port);
++
++ spin_unlock_irq(&port->port_lock);
++}
++
++static void gs_read_complete(struct usb_ep *ep, struct usb_request *req)
++{
++ struct gs_port *port = ep->driver_data;
++
++ /* Queue all received data until the tty layer is ready for it. */
++ spin_lock(&port->port_lock);
++ list_add_tail(&req->list, &port->read_queue);
++ tasklet_schedule(&port->push);
++ spin_unlock(&port->port_lock);
++}
++
++static void gs_write_complete(struct usb_ep *ep, struct usb_request *req)
++{
++ struct gs_port *port = ep->driver_data;
++
++ spin_lock(&port->port_lock);
++ list_add(&req->list, &port->write_pool);
++
++ switch (req->status) {
++ default:
++ /* presumably a transient fault */
++ pr_warning("%s: unexpected %s status %d\n",
++ __func__, ep->name, req->status);
++ /* FALL THROUGH */
++ case 0:
++ /* normal completion */
++ gs_start_tx(port);
++ break;
++
++ case -ESHUTDOWN:
++ /* disconnect */
++ pr_vdebug("%s: %s shutdown\n", __func__, ep->name);
++ break;
++ }
++
++ spin_unlock(&port->port_lock);
++}
++
++static void gs_free_requests(struct usb_ep *ep, struct list_head *head)
++{
++ struct usb_request *req;
++
++ while (!list_empty(head)) {
++ req = list_entry(head->next, struct usb_request, list);
++ list_del(&req->list);
++ gs_free_req(ep, req);
++ }
++}
++
++static int gs_alloc_requests(struct usb_ep *ep, struct list_head *head,
++ void (*fn)(struct usb_ep *, struct usb_request *))
++{
++ int i;
++ struct usb_request *req;
++
++ /* Pre-allocate up to QUEUE_SIZE transfers, but if we can't
++ * do quite that many this time, don't fail ... we just won't
++ * be as speedy as we might otherwise be.
++ */
++ for (i = 0; i < QUEUE_SIZE; i++) {
++ req = gs_alloc_req(ep, ep->maxpacket, GFP_ATOMIC);
++ if (!req)
++ return list_empty(head) ? -ENOMEM : 0;
++ req->complete = fn;
++ list_add_tail(&req->list, head);
++ }
++ return 0;
++}
++
++/**
++ * gs_start_io - start USB I/O streams
++ * @dev: encapsulates endpoints to use
++ * Context: holding port_lock; port_tty and port_usb are non-null
++ *
++ * We only start I/O when something is connected to both sides of
++ * this port. If nothing is listening on the host side, we may
++ * be pointlessly filling up our TX buffers and FIFO.
++ */
++static int gs_start_io(struct gs_port *port)
++{
++ struct list_head *head = &port->read_pool;
++ struct usb_ep *ep = port->port_usb->out;
++ int status;
++ unsigned started;
++
++ /* Allocate RX and TX I/O buffers. We can't easily do this much
++ * earlier (with GFP_KERNEL) because the requests are coupled to
++ * endpoints, as are the packet sizes we'll be using. Different
++ * configurations may use different endpoints with a given port;
++ * and high speed vs full speed changes packet sizes too.
++ */
++ status = gs_alloc_requests(ep, head, gs_read_complete);
++ if (status)
++ return status;
++
++ status = gs_alloc_requests(port->port_usb->in, &port->write_pool,
++ gs_write_complete);
++ if (status) {
++ gs_free_requests(ep, head);
++ return status;
++ }
++
++ /* queue read requests */
++ port->n_read = 0;
++ started = gs_start_rx(port);
++
++ /* unblock any pending writes into our circular buffer */
++ if (started) {
++ tty_wakeup(port->port_tty);
++ } else {
++ gs_free_requests(ep, head);
++ gs_free_requests(port->port_usb->in, &port->write_pool);
++ status = -EIO;
++ }
++
++ return status;
++}
++
++/*-------------------------------------------------------------------------*/
++
++/* TTY Driver */
++
++/*
++ * gs_open sets up the link between a gs_port and its associated TTY.
++ * That link is broken *only* by TTY close(), and all driver methods
++ * know that.
++ */
++static int gs_open(struct tty_struct *tty, struct file *file)
++{
++ int port_num = tty->index;
++ struct gs_port *port;
++ int status;
++
++ if (port_num < 0 || port_num >= n_ports)
++ return -ENXIO;
++
++ do {
++ mutex_lock(&ports[port_num].lock);
++ port = ports[port_num].port;
++ if (!port)
++ status = -ENODEV;
++ else {
++ spin_lock_irq(&port->port_lock);
++
++ /* already open? Great. */
++ if (port->open_count) {
++ status = 0;
++ port->open_count++;
++
++ /* currently opening/closing? wait ... */
++ } else if (port->openclose) {
++ status = -EBUSY;
++
++ /* ... else we do the work */
++ } else {
++ status = -EAGAIN;
++ port->openclose = true;
++ }
++ spin_unlock_irq(&port->port_lock);
++ }
++ mutex_unlock(&ports[port_num].lock);
++
++ switch (status) {
++ default:
++ /* fully handled */
++ return status;
++ case -EAGAIN:
++ /* must do the work */
++ break;
++ case -EBUSY:
++ /* wait for EAGAIN task to finish */
++ msleep(1);
++ /* REVISIT could have a waitchannel here, if
++ * concurrent open performance is important
++ */
++ break;
++ }
++ } while (status != -EAGAIN);
++
++ /* Do the "real open" */
++ spin_lock_irq(&port->port_lock);
++
++ /* allocate circular buffer on first open */
++ if (port->port_write_buf.buf_buf == NULL) {
++
++ spin_unlock_irq(&port->port_lock);
++ status = gs_buf_alloc(&port->port_write_buf, WRITE_BUF_SIZE);
++ spin_lock_irq(&port->port_lock);
++
++ if (status) {
++ pr_debug("gs_open: ttyGS%d (%p,%p) no buffer\n",
++ port->port_num, tty, file);
++ port->openclose = false;
++ goto exit_unlock_port;
++ }
++ }
++
++ /* REVISIT if REMOVED (ports[].port NULL), abort the open
++ * to let rmmod work faster (but this way isn't wrong).
++ */
++
++ /* REVISIT maybe wait for "carrier detect" */
++
++ tty->driver_data = port;
++ port->port_tty = tty;
++
++ port->open_count = 1;
++ port->openclose = false;
++
++ /* low_latency means ldiscs work in tasklet context, without
++ * needing a workqueue schedule ... easier to keep up.
++ */
++ tty->low_latency = 1;
++
++ /* if connected, start the I/O stream */
++ if (port->port_usb) {
++ struct gserial *gser = port->port_usb;
++
++ pr_debug("gs_open: start ttyGS%d\n", port->port_num);
++ gs_start_io(port);
++
++ if (gser->connect)
++ gser->connect(gser);
++ }
++
++ pr_debug("gs_open: ttyGS%d (%p,%p)\n", port->port_num, tty, file);
++
++ status = 0;
++
++exit_unlock_port:
++ spin_unlock_irq(&port->port_lock);
++ return status;
++}
++
++static int gs_writes_finished(struct gs_port *p)
++{
++ int cond;
++
++ /* return true on disconnect or empty buffer */
++ spin_lock_irq(&p->port_lock);
++ cond = (p->port_usb == NULL) || !gs_buf_data_avail(&p->port_write_buf);
++ spin_unlock_irq(&p->port_lock);
++
++ return cond;
++}
++
++static void gs_close(struct tty_struct *tty, struct file *file)
++{
++ struct gs_port *port = tty->driver_data;
++ struct gserial *gser;
++
++ spin_lock_irq(&port->port_lock);
++
++ if (port->open_count != 1) {
++ if (port->open_count == 0)
++ WARN_ON(1);
++ else
++ --port->open_count;
++ goto exit;
++ }
++
++ pr_debug("gs_close: ttyGS%d (%p,%p) ...\n", port->port_num, tty, file);
++
++ /* mark port as closing but in use; we can drop port lock
++ * and sleep if necessary
++ */
++ port->openclose = true;
++ port->open_count = 0;
++
++ gser = port->port_usb;
++ if (gser && gser->disconnect)
++ gser->disconnect(gser);
++
++ /* wait for circular write buffer to drain, disconnect, or at
++ * most GS_CLOSE_TIMEOUT seconds; then discard the rest
++ */
++ if (gs_buf_data_avail(&port->port_write_buf) > 0 && gser) {
++ spin_unlock_irq(&port->port_lock);
++ wait_event_interruptible_timeout(port->drain_wait,
++ gs_writes_finished(port),
++ GS_CLOSE_TIMEOUT * HZ);
++ spin_lock_irq(&port->port_lock);
++ gser = port->port_usb;
++ }
++
++ /* Iff we're disconnected, there can be no I/O in flight so it's
++ * ok to free the circular buffer; else just scrub it. And don't
++ * let the push tasklet fire again until we're re-opened.
++ */
++ if (gser == NULL)
++ gs_buf_free(&port->port_write_buf);
++ else
++ gs_buf_clear(&port->port_write_buf);
++
++ tty->driver_data = NULL;
++ port->port_tty = NULL;
++
++ port->openclose = false;
++
++ pr_debug("gs_close: ttyGS%d (%p,%p) done!\n",
++ port->port_num, tty, file);
++
++ wake_up_interruptible(&port->close_wait);
++exit:
++ spin_unlock_irq(&port->port_lock);
++}
++
++static int gs_write(struct tty_struct *tty, const unsigned char *buf, int count)
++{
++ struct gs_port *port = tty->driver_data;
++ unsigned long flags;
++ int status;
++
++ pr_vdebug("gs_write: ttyGS%d (%p) writing %d bytes\n",
++ port->port_num, tty, count);
++
++ spin_lock_irqsave(&port->port_lock, flags);
++ if (count)
++ count = gs_buf_put(&port->port_write_buf, buf, count);
++ /* treat count == 0 as flush_chars() */
++ if (port->port_usb)
++ status = gs_start_tx(port);
++ spin_unlock_irqrestore(&port->port_lock, flags);
++
++ return count;
++}
++
++static int gs_put_char(struct tty_struct *tty, unsigned char ch)
++{
++ struct gs_port *port = tty->driver_data;
++ unsigned long flags;
++ int status;
++
++ pr_vdebug("gs_put_char: (%d,%p) char=0x%x, called from %p\n",
++ port->port_num, tty, ch, __builtin_return_address(0));
++
++ spin_lock_irqsave(&port->port_lock, flags);
++ status = gs_buf_put(&port->port_write_buf, &ch, 1);
++ spin_unlock_irqrestore(&port->port_lock, flags);
++
++ return status;
++}
++
++static void gs_flush_chars(struct tty_struct *tty)
++{
++ struct gs_port *port = tty->driver_data;
++ unsigned long flags;
++
++ pr_vdebug("gs_flush_chars: (%d,%p)\n", port->port_num, tty);
++
++ spin_lock_irqsave(&port->port_lock, flags);
++ if (port->port_usb)
++ gs_start_tx(port);
++ spin_unlock_irqrestore(&port->port_lock, flags);
++}
++
++static int gs_write_room(struct tty_struct *tty)
++{
++ struct gs_port *port = tty->driver_data;
++ unsigned long flags;
++ int room = 0;
++
++ spin_lock_irqsave(&port->port_lock, flags);
++ if (port->port_usb)
++ room = gs_buf_space_avail(&port->port_write_buf);
++ spin_unlock_irqrestore(&port->port_lock, flags);
++
++ pr_vdebug("gs_write_room: (%d,%p) room=%d\n",
++ port->port_num, tty, room);
++
++ return room;
++}
++
++static int gs_chars_in_buffer(struct tty_struct *tty)
++{
++ struct gs_port *port = tty->driver_data;
++ unsigned long flags;
++ int chars = 0;
++
++ spin_lock_irqsave(&port->port_lock, flags);
++ chars = gs_buf_data_avail(&port->port_write_buf);
++ spin_unlock_irqrestore(&port->port_lock, flags);
++
++ pr_vdebug("gs_chars_in_buffer: (%d,%p) chars=%d\n",
++ port->port_num, tty, chars);
++
++ return chars;
++}
++
++/* undo side effects of setting TTY_THROTTLED */
++static void gs_unthrottle(struct tty_struct *tty)
++{
++ struct gs_port *port = tty->driver_data;
++ unsigned long flags;
++
++ spin_lock_irqsave(&port->port_lock, flags);
++ if (port->port_usb) {
++ /* Kickstart read queue processing. We don't do xon/xoff,
++ * rts/cts, or other handshaking with the host, but if the
++ * read queue backs up enough we'll be NAKing OUT packets.
++ */
++ tasklet_schedule(&port->push);
++ pr_vdebug(PREFIX "%d: unthrottle\n", port->port_num);
++ }
++ spin_unlock_irqrestore(&port->port_lock, flags);
++}
++
++static int gs_break_ctl(struct tty_struct *tty, int duration)
++{
++ struct gs_port *port = tty->driver_data;
++ int status = 0;
++ struct gserial *gser;
++
++ pr_vdebug("gs_break_ctl: ttyGS%d, send break (%d) \n",
++ port->port_num, duration);
++
++ spin_lock_irq(&port->port_lock);
++ gser = port->port_usb;
++ if (gser && gser->send_break)
++ status = gser->send_break(gser, duration);
++ spin_unlock_irq(&port->port_lock);
++
++ return status;
++}
++
++static const struct tty_operations gs_tty_ops = {
++ .open = gs_open,
++ .close = gs_close,
++ .write = gs_write,
++ .put_char = gs_put_char,
++ .flush_chars = gs_flush_chars,
++ .write_room = gs_write_room,
++ .chars_in_buffer = gs_chars_in_buffer,
++ .unthrottle = gs_unthrottle,
++ .break_ctl = gs_break_ctl,
++};
++
++/*-------------------------------------------------------------------------*/
++
++static struct tty_driver *gs_tty_driver;
++
++static int __init
++gs_port_alloc(unsigned port_num, struct usb_cdc_line_coding *coding)
++{
++ struct gs_port *port;
++
++ port = kzalloc(sizeof(struct gs_port), GFP_KERNEL);
++ if (port == NULL)
++ return -ENOMEM;
++
++ spin_lock_init(&port->port_lock);
++ init_waitqueue_head(&port->close_wait);
++ init_waitqueue_head(&port->drain_wait);
++
++ tasklet_init(&port->push, gs_rx_push, (unsigned long) port);
++
++ INIT_LIST_HEAD(&port->read_pool);
++ INIT_LIST_HEAD(&port->read_queue);
++ INIT_LIST_HEAD(&port->write_pool);
++
++ port->port_num = port_num;
++ port->port_line_coding = *coding;
++
++ ports[port_num].port = port;
++
++ return 0;
++}
++
++/**
++ * gserial_setup - initialize TTY driver for one or more ports
++ * @g: gadget to associate with these ports
++ * @count: how many ports to support
++ * Context: may sleep
++ *
++ * The TTY stack needs to know in advance how many devices it should
++ * plan to manage. Use this call to set up the ports you will be
++ * exporting through USB. Later, connect them to functions based
++ * on what configuration is activated by the USB host; and disconnect
++ * them as appropriate.
++ *
++ * An example would be a two-configuration device in which both
++ * configurations expose port 0, but through different functions.
++ * One configuration could even expose port 1 while the other
++ * one doesn't.
++ *
++ * Returns negative errno or zero.
++ */
++int __init gserial_setup(struct usb_gadget *g, unsigned count)
++{
++ unsigned i;
++ struct usb_cdc_line_coding coding;
++ int status;
++
++ if (count == 0 || count > N_PORTS)
++ return -EINVAL;
++
++ gs_tty_driver = alloc_tty_driver(count);
++ if (!gs_tty_driver)
++ return -ENOMEM;
++
++ gs_tty_driver->owner = THIS_MODULE;
++ gs_tty_driver->driver_name = "g_serial";
++ gs_tty_driver->name = PREFIX;
++ /* uses dynamically assigned dev_t values */
++
++ gs_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
++ gs_tty_driver->subtype = SERIAL_TYPE_NORMAL;
++ gs_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
++ gs_tty_driver->init_termios = tty_std_termios;
++
++ /* 9600-8-N-1 ... matches defaults expected by "usbser.sys" on
++ * MS-Windows. Otherwise, most of these flags shouldn't affect
++ * anything unless we were to actually hook up to a serial line.
++ */
++ gs_tty_driver->init_termios.c_cflag =
++ B9600 | CS8 | CREAD | HUPCL | CLOCAL;
++ gs_tty_driver->init_termios.c_ispeed = 9600;
++ gs_tty_driver->init_termios.c_ospeed = 9600;
++
++ coding.dwDTERate = __constant_cpu_to_le32(9600);
++ coding.bCharFormat = 8;
++ coding.bParityType = USB_CDC_NO_PARITY;
++ coding.bDataBits = USB_CDC_1_STOP_BITS;
++
++ tty_set_operations(gs_tty_driver, &gs_tty_ops);
++
++ /* make devices be openable */
++ for (i = 0; i < count; i++) {
++ mutex_init(&ports[i].lock);
++ status = gs_port_alloc(i, &coding);
++ if (status) {
++ count = i;
++ goto fail;
++ }
++ }
++ n_ports = count;
++
++ /* export the driver ... */
++ status = tty_register_driver(gs_tty_driver);
++ if (status) {
++ put_tty_driver(gs_tty_driver);
++ pr_err("%s: cannot register, err %d\n",
++ __func__, status);
++ goto fail;
++ }
++
++ /* ... and sysfs class devices, so mdev/udev make /dev/ttyGS* */
++ for (i = 0; i < count; i++) {
++ struct device *tty_dev;
++
++ tty_dev = tty_register_device(gs_tty_driver, i, &g->dev);
++ if (IS_ERR(tty_dev))
++ pr_warning("%s: no classdev for port %d, err %ld\n",
++ __func__, i, PTR_ERR(tty_dev));
++ }
++
++ pr_debug("%s: registered %d ttyGS* device%s\n", __func__,
++ count, (count == 1) ? "" : "s");
++
++ return status;
++fail:
++ while (count--)
++ kfree(ports[count].port);
++ put_tty_driver(gs_tty_driver);
++ gs_tty_driver = NULL;
++ return status;
++}
++
++static int gs_closed(struct gs_port *port)
++{
++ int cond;
++
++ spin_lock_irq(&port->port_lock);
++ cond = (port->open_count == 0) && !port->openclose;
++ spin_unlock_irq(&port->port_lock);
++ return cond;
++}
++
++/**
++ * gserial_cleanup - remove TTY-over-USB driver and devices
++ * Context: may sleep
++ *
++ * This is called to free all resources allocated by @gserial_setup().
++ * Accordingly, it may need to wait until some open /dev/ files have
++ * closed.
++ *
++ * The caller must have issued @gserial_disconnect() for any ports
++ * that had previously been connected, so that there is never any
++ * I/O pending when it's called.
++ */
++void gserial_cleanup(void)
++{
++ unsigned i;
++ struct gs_port *port;
++
++ if (!gs_tty_driver)
++ return;
++
++ /* start sysfs and /dev/ttyGS* node removal */
++ for (i = 0; i < n_ports; i++)
++ tty_unregister_device(gs_tty_driver, i);
++
++ for (i = 0; i < n_ports; i++) {
++ /* prevent new opens */
++ mutex_lock(&ports[i].lock);
++ port = ports[i].port;
++ ports[i].port = NULL;
++ mutex_unlock(&ports[i].lock);
++
++ tasklet_kill(&port->push);
++
++ /* wait for old opens to finish */
++ wait_event(port->close_wait, gs_closed(port));
++
++ WARN_ON(port->port_usb != NULL);
++
++ kfree(port);
++ }
++ n_ports = 0;
++
++ tty_unregister_driver(gs_tty_driver);
++ gs_tty_driver = NULL;
++
++ pr_debug("%s: cleaned up ttyGS* support\n", __func__);
++}
++
++/**
++ * gserial_connect - notify TTY I/O glue that USB link is active
++ * @gser: the function, set up with endpoints and descriptors
++ * @port_num: which port is active
++ * Context: any (usually from irq)
++ *
++ * This is called activate endpoints and let the TTY layer know that
++ * the connection is active ... not unlike "carrier detect". It won't
++ * necessarily start I/O queues; unless the TTY is held open by any
++ * task, there would be no point. However, the endpoints will be
++ * activated so the USB host can perform I/O, subject to basic USB
++ * hardware flow control.
++ *
++ * Caller needs to have set up the endpoints and USB function in @dev
++ * before calling this, as well as the appropriate (speed-specific)
++ * endpoint descriptors, and also have set up the TTY driver by calling
++ * @gserial_setup().
++ *
++ * Returns negative errno or zero.
++ * On success, ep->driver_data will be overwritten.
++ */
++int gserial_connect(struct gserial *gser, u8 port_num)
++{
++ struct gs_port *port;
++ unsigned long flags;
++ int status;
++
++ if (!gs_tty_driver || port_num >= n_ports)
++ return -ENXIO;
++
++ /* we "know" gserial_cleanup() hasn't been called */
++ port = ports[port_num].port;
++
++ /* activate the endpoints */
++ status = usb_ep_enable(gser->in, gser->in_desc);
++ if (status < 0)
++ return status;
++ gser->in->driver_data = port;
++
++ status = usb_ep_enable(gser->out, gser->out_desc);
++ if (status < 0)
++ goto fail_out;
++ gser->out->driver_data = port;
++
++ /* then tell the tty glue that I/O can work */
++ spin_lock_irqsave(&port->port_lock, flags);
++ gser->ioport = port;
++ port->port_usb = gser;
++
++ /* REVISIT unclear how best to handle this state...
++ * we don't really couple it with the Linux TTY.
++ */
++ gser->port_line_coding = port->port_line_coding;
++
++ /* REVISIT if waiting on "carrier detect", signal. */
++
++ /* if it's already open, start I/O ... and notify the serial
++ * protocol about open/close status (connect/disconnect).
++ */
++ if (port->open_count) {
++ pr_debug("gserial_connect: start ttyGS%d\n", port->port_num);
++ gs_start_io(port);
++ if (gser->connect)
++ gser->connect(gser);
++ } else {
++ if (gser->disconnect)
++ gser->disconnect(gser);
++ }
++
++ spin_unlock_irqrestore(&port->port_lock, flags);
++
++ return status;
++
++fail_out:
++ usb_ep_disable(gser->in);
++ gser->in->driver_data = NULL;
++ return status;
++}
++
++/**
++ * gserial_disconnect - notify TTY I/O glue that USB link is inactive
++ * @gser: the function, on which gserial_connect() was called
++ * Context: any (usually from irq)
++ *
++ * This is called to deactivate endpoints and let the TTY layer know
++ * that the connection went inactive ... not unlike "hangup".
++ *
++ * On return, the state is as if gserial_connect() had never been called;
++ * there is no active USB I/O on these endpoints.
++ */
++void gserial_disconnect(struct gserial *gser)
++{
++ struct gs_port *port = gser->ioport;
++ unsigned long flags;
++
++ if (!port)
++ return;
++
++ /* tell the TTY glue not to do I/O here any more */
++ spin_lock_irqsave(&port->port_lock, flags);
++
++ /* REVISIT as above: how best to track this? */
++ port->port_line_coding = gser->port_line_coding;
++
++ port->port_usb = NULL;
++ gser->ioport = NULL;
++ if (port->open_count > 0 || port->openclose) {
++ wake_up_interruptible(&port->drain_wait);
++ if (port->port_tty)
++ tty_hangup(port->port_tty);
++ }
++ spin_unlock_irqrestore(&port->port_lock, flags);
++
++ /* disable endpoints, aborting down any active I/O */
++ usb_ep_disable(gser->out);
++ gser->out->driver_data = NULL;
++
++ usb_ep_disable(gser->in);
++ gser->in->driver_data = NULL;
++
++ /* finally, free any unused/unusable I/O buffers */
++ spin_lock_irqsave(&port->port_lock, flags);
++ if (port->open_count == 0 && !port->openclose)
++ gs_buf_free(&port->port_write_buf);
++ gs_free_requests(gser->out, &port->read_pool);
++ gs_free_requests(gser->out, &port->read_queue);
++ gs_free_requests(gser->in, &port->write_pool);
++ spin_unlock_irqrestore(&port->port_lock, flags);
++}
+diff --git a/drivers/usb/gadget/u_serial.h b/drivers/usb/gadget/u_serial.h
+new file mode 100644
+index 0000000..af3910d
+--- /dev/null
++++ b/drivers/usb/gadget/u_serial.h
+@@ -0,0 +1,66 @@
++/*
++ * u_serial.h - interface to USB gadget "serial port"/TTY utilities
++ *
++ * Copyright (C) 2008 David Brownell
++ * Copyright (C) 2008 by Nokia Corporation
++ *
++ * This software is distributed under the terms of the GNU General
++ * Public License ("GPL") as published by the Free Software Foundation,
++ * either version 2 of that License or (at your option) any later version.
++ */
++
++#ifndef __U_SERIAL_H
++#define __U_SERIAL_H
++
++#include <linux/usb/composite.h>
++#include <linux/usb/cdc.h>
++
++/*
++ * One non-multiplexed "serial" I/O port ... there can be several of these
++ * on any given USB peripheral device, if it provides enough endpoints.
++ *
++ * The "u_serial" utility component exists to do one thing: manage TTY
++ * style I/O using the USB peripheral endpoints listed here, including
++ * hookups to sysfs and /dev for each logical "tty" device.
++ *
++ * REVISIT at least ACM could support tiocmget() if needed.
++ *
++ * REVISIT someday, allow multiplexing several TTYs over these endpoints.
++ */
++struct gserial {
++ struct usb_function func;
++
++ /* port is managed by gserial_{connect,disconnect} */
++ struct gs_port *ioport;
++
++ struct usb_ep *in;
++ struct usb_ep *out;
++ struct usb_endpoint_descriptor *in_desc;
++ struct usb_endpoint_descriptor *out_desc;
++
++ /* REVISIT avoid this CDC-ACM support harder ... */
++ struct usb_cdc_line_coding port_line_coding; /* 9600-8-N-1 etc */
++
++ /* notification callbacks */
++ void (*connect)(struct gserial *p);
++ void (*disconnect)(struct gserial *p);
++ int (*send_break)(struct gserial *p, int duration);
++};
++
++/* utilities to allocate/free request and buffer */
++struct usb_request *gs_alloc_req(struct usb_ep *ep, unsigned len, gfp_t flags);
++void gs_free_req(struct usb_ep *, struct usb_request *req);
++
++/* port setup/teardown is handled by gadget driver */
++int gserial_setup(struct usb_gadget *g, unsigned n_ports);
++void gserial_cleanup(void);
++
++/* connect/disconnect is handled by individual functions */
++int gserial_connect(struct gserial *, u8 port_num);
++void gserial_disconnect(struct gserial *);
++
++/* functions are bound to configurations by a config or gadget driver */
++int acm_bind_config(struct usb_configuration *c, u8 port_num);
++int gser_bind_config(struct usb_configuration *c, u8 port_num);
++
++#endif /* __U_SERIAL_H */
+diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c
+index fce4924..aa0bd4f 100644
+--- a/drivers/usb/gadget/zero.c
++++ b/drivers/usb/gadget/zero.c
+@@ -1,8 +1,8 @@
+ /*
+ * zero.c -- Gadget Zero, for USB development
+ *
+- * Copyright (C) 2003-2007 David Brownell
+- * All rights reserved.
++ * Copyright (C) 2003-2008 David Brownell
++ * Copyright (C) 2008 by Nokia Corporation
+ *
+ * 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
+@@ -30,12 +30,7 @@
+ *
+ * It supports two similar configurations. One sinks whatever the usb host
+ * writes, and in return sources zeroes. The other loops whatever the host
+- * writes back, so the host can read it. Module options include:
+- *
+- * buflen=N default N=4096, buffer size used
+- * qlen=N default N=32, how many buffers in the loopback queue
+- * loopdefault default false, list loopback config first
+- * autoresume=N default N=0, seconds before triggering remote wakeup
++ * writes back, so the host can read it.
+ *
+ * Many drivers will only have one configuration, letting them be much
+ * simpler if they also don't support high speed operation (like this
+@@ -47,94 +42,35 @@
+ * work with low capability USB controllers without four bulk endpoints.
+ */
+
++/*
++ * driver assumes self-powered hardware, and
++ * has no way for users to trigger remote wakeup.
++ */
++
+ /* #define VERBOSE_DEBUG */
+
+ #include <linux/kernel.h>
+ #include <linux/utsname.h>
+ #include <linux/device.h>
+
+-#include <linux/usb/ch9.h>
+-#include <linux/usb/gadget.h>
+-
++#include "g_zero.h"
+ #include "gadget_chips.h"
+
+
+ /*-------------------------------------------------------------------------*/
+
+-#define DRIVER_VERSION "Earth Day 2008"
++#define DRIVER_VERSION "Cinco de Mayo 2008"
+
+-static const char shortname[] = "zero";
+ static const char longname[] = "Gadget Zero";
+
+-static const char source_sink[] = "source and sink data";
+-static const char loopback[] = "loop input to output";
+-
+-/*-------------------------------------------------------------------------*/
+-
+-/*
+- * driver assumes self-powered hardware, and
+- * has no way for users to trigger remote wakeup.
+- *
+- * this version autoconfigures as much as possible,
+- * which is reasonable for most "bulk-only" drivers.
+- */
+-static const char *EP_IN_NAME; /* source */
+-static const char *EP_OUT_NAME; /* sink */
+-
+-/*-------------------------------------------------------------------------*/
+-
+-/* big enough to hold our biggest descriptor */
+-#define USB_BUFSIZ 256
+-
+-struct zero_dev {
+- spinlock_t lock;
+- struct usb_gadget *gadget;
+- struct usb_request *req; /* for control responses */
+-
+- /* when configured, we have one of two configs:
+- * - source data (in to host) and sink it (out from host)
+- * - or loop it back (out from host back in to host)
+- */
+- u8 config;
+- struct usb_ep *in_ep, *out_ep;
+-
+- /* autoresume timer */
+- struct timer_list resume;
+-};
+-
+-#define DBG(d, fmt, args...) \
+- dev_dbg(&(d)->gadget->dev , fmt , ## args)
+-#define VDBG(d, fmt, args...) \
+- dev_vdbg(&(d)->gadget->dev , fmt , ## args)
+-#define ERROR(d, fmt, args...) \
+- dev_err(&(d)->gadget->dev , fmt , ## args)
+-#define WARN(d, fmt, args...) \
+- dev_warn(&(d)->gadget->dev , fmt , ## args)
+-#define INFO(d, fmt, args...) \
+- dev_info(&(d)->gadget->dev , fmt , ## args)
+-
+-/*-------------------------------------------------------------------------*/
+-
+-static unsigned buflen = 4096;
+-static unsigned qlen = 32;
+-static unsigned pattern = 0;
+-
+-module_param(buflen, uint, S_IRUGO);
+-module_param(qlen, uint, S_IRUGO);
+-module_param(pattern, uint, S_IRUGO|S_IWUSR);
+-
+-/*
+- * if it's nonzero, autoresume says how many seconds to wait
+- * before trying to wake up the host after suspend.
+- */
+-static unsigned autoresume = 0;
+-module_param(autoresume, uint, 0);
++unsigned buflen = 4096;
++module_param(buflen, uint, 0);
+
+ /*
+ * Normally the "loopback" configuration is second (index 1) so
+ * it's not the default. Here's where to change that order, to
+- * work better with hosts where config changes are problematic.
+- * Or controllers (like superh) that only support one config.
++ * work better with hosts where config changes are problematic or
++ * controllers (like original superh) that only support one config.
+ */
+ static int loopdefault = 0;
+ module_param(loopdefault, bool, S_IRUGO|S_IWUSR);
+@@ -156,24 +92,6 @@ module_param(loopdefault, bool, S_IRUGO|S_IWUSR);
+
+ /*-------------------------------------------------------------------------*/
+
+-/*
+- * DESCRIPTORS ... most are static, but strings and (full)
+- * configuration descriptors are built on demand.
+- */
+-
+-#define STRING_MANUFACTURER 25
+-#define STRING_PRODUCT 42
+-#define STRING_SERIAL 101
+-#define STRING_SOURCE_SINK 250
+-#define STRING_LOOPBACK 251
+-
+-/*
+- * This device advertises two configurations; these numbers work
+- * on a pxa250 as well as more flexible hardware.
+- */
+-#define CONFIG_SOURCE_SINK 3
+-#define CONFIG_LOOPBACK 2
+-
+ static struct usb_device_descriptor device_desc = {
+ .bLength = sizeof device_desc,
+ .bDescriptorType = USB_DT_DEVICE,
+@@ -183,248 +101,64 @@ static struct usb_device_descriptor device_desc = {
+
+ .idVendor = __constant_cpu_to_le16(DRIVER_VENDOR_NUM),
+ .idProduct = __constant_cpu_to_le16(DRIVER_PRODUCT_NUM),
+- .iManufacturer = STRING_MANUFACTURER,
+- .iProduct = STRING_PRODUCT,
+- .iSerialNumber = STRING_SERIAL,
+ .bNumConfigurations = 2,
+ };
+
+-static struct usb_config_descriptor source_sink_config = {
+- .bLength = sizeof source_sink_config,
+- .bDescriptorType = USB_DT_CONFIG,
+-
+- /* compute wTotalLength on the fly */
+- .bNumInterfaces = 1,
+- .bConfigurationValue = CONFIG_SOURCE_SINK,
+- .iConfiguration = STRING_SOURCE_SINK,
+- .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
+- .bMaxPower = 1, /* self-powered */
+-};
+-
+-static struct usb_config_descriptor loopback_config = {
+- .bLength = sizeof loopback_config,
+- .bDescriptorType = USB_DT_CONFIG,
+-
+- /* compute wTotalLength on the fly */
+- .bNumInterfaces = 1,
+- .bConfigurationValue = CONFIG_LOOPBACK,
+- .iConfiguration = STRING_LOOPBACK,
+- .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
+- .bMaxPower = 1, /* self-powered */
+-};
+-
++#ifdef CONFIG_USB_OTG
+ static struct usb_otg_descriptor otg_descriptor = {
+ .bLength = sizeof otg_descriptor,
+ .bDescriptorType = USB_DT_OTG,
+
+- .bmAttributes = USB_OTG_SRP,
+-};
+-
+-/* one interface in each configuration */
+-
+-static const struct usb_interface_descriptor source_sink_intf = {
+- .bLength = sizeof source_sink_intf,
+- .bDescriptorType = USB_DT_INTERFACE,
+-
+- .bNumEndpoints = 2,
+- .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+- .iInterface = STRING_SOURCE_SINK,
+-};
+-
+-static const struct usb_interface_descriptor loopback_intf = {
+- .bLength = sizeof loopback_intf,
+- .bDescriptorType = USB_DT_INTERFACE,
+-
+- .bNumEndpoints = 2,
+- .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+- .iInterface = STRING_LOOPBACK,
+-};
+-
+-/* two full speed bulk endpoints; their use is config-dependent */
+-
+-static struct usb_endpoint_descriptor fs_source_desc = {
+- .bLength = USB_DT_ENDPOINT_SIZE,
+- .bDescriptorType = USB_DT_ENDPOINT,
+-
+- .bEndpointAddress = USB_DIR_IN,
+- .bmAttributes = USB_ENDPOINT_XFER_BULK,
+-};
+-
+-static struct usb_endpoint_descriptor fs_sink_desc = {
+- .bLength = USB_DT_ENDPOINT_SIZE,
+- .bDescriptorType = USB_DT_ENDPOINT,
+-
+- .bEndpointAddress = USB_DIR_OUT,
+- .bmAttributes = USB_ENDPOINT_XFER_BULK,
+-};
+-
+-static const struct usb_descriptor_header *fs_source_sink_function[] = {
+- (struct usb_descriptor_header *) &otg_descriptor,
+- (struct usb_descriptor_header *) &source_sink_intf,
+- (struct usb_descriptor_header *) &fs_sink_desc,
+- (struct usb_descriptor_header *) &fs_source_desc,
+- NULL,
+-};
+-
+-static const struct usb_descriptor_header *fs_loopback_function[] = {
+- (struct usb_descriptor_header *) &otg_descriptor,
+- (struct usb_descriptor_header *) &loopback_intf,
+- (struct usb_descriptor_header *) &fs_sink_desc,
+- (struct usb_descriptor_header *) &fs_source_desc,
+- NULL,
+-};
+-
+-/*
+- * usb 2.0 devices need to expose both high speed and full speed
+- * descriptors, unless they only run at full speed.
+- *
+- * that means alternate endpoint descriptors (bigger packets)
+- * and a "device qualifier" ... plus more construction options
+- * for the config descriptor.
+- */
+-
+-static struct usb_endpoint_descriptor hs_source_desc = {
+- .bLength = USB_DT_ENDPOINT_SIZE,
+- .bDescriptorType = USB_DT_ENDPOINT,
+-
+- .bmAttributes = USB_ENDPOINT_XFER_BULK,
+- .wMaxPacketSize = __constant_cpu_to_le16(512),
+-};
+-
+-static struct usb_endpoint_descriptor hs_sink_desc = {
+- .bLength = USB_DT_ENDPOINT_SIZE,
+- .bDescriptorType = USB_DT_ENDPOINT,
+-
+- .bmAttributes = USB_ENDPOINT_XFER_BULK,
+- .wMaxPacketSize = __constant_cpu_to_le16(512),
+-};
+-
+-static struct usb_qualifier_descriptor dev_qualifier = {
+- .bLength = sizeof dev_qualifier,
+- .bDescriptorType = USB_DT_DEVICE_QUALIFIER,
+-
+- .bcdUSB = __constant_cpu_to_le16(0x0200),
+- .bDeviceClass = USB_CLASS_VENDOR_SPEC,
+-
+- .bNumConfigurations = 2,
++ /* REVISIT SRP-only hardware is possible, although
++ * it would not be called "OTG" ...
++ */
++ .bmAttributes = USB_OTG_SRP | USB_OTG_HNP,
+ };
+
+-static const struct usb_descriptor_header *hs_source_sink_function[] = {
++const struct usb_descriptor_header *otg_desc[] = {
+ (struct usb_descriptor_header *) &otg_descriptor,
+- (struct usb_descriptor_header *) &source_sink_intf,
+- (struct usb_descriptor_header *) &hs_source_desc,
+- (struct usb_descriptor_header *) &hs_sink_desc,
+ NULL,
+ };
++#endif
+
+-static const struct usb_descriptor_header *hs_loopback_function[] = {
+- (struct usb_descriptor_header *) &otg_descriptor,
+- (struct usb_descriptor_header *) &loopback_intf,
+- (struct usb_descriptor_header *) &hs_source_desc,
+- (struct usb_descriptor_header *) &hs_sink_desc,
+- NULL,
+-};
++/* string IDs are assigned dynamically */
+
+-/* maxpacket and other transfer characteristics vary by speed. */
+-static inline struct usb_endpoint_descriptor *
+-ep_desc(struct usb_gadget *g, struct usb_endpoint_descriptor *hs,
+- struct usb_endpoint_descriptor *fs)
+-{
+- if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
+- return hs;
+- return fs;
+-}
++#define STRING_MANUFACTURER_IDX 0
++#define STRING_PRODUCT_IDX 1
++#define STRING_SERIAL_IDX 2
+
+ static char manufacturer[50];
+
+ /* default serial number takes at least two packets */
+ static char serial[] = "0123456789.0123456789.0123456789";
+
+-
+-/* static strings, in UTF-8 */
+-static struct usb_string strings[] = {
+- { STRING_MANUFACTURER, manufacturer, },
+- { STRING_PRODUCT, longname, },
+- { STRING_SERIAL, serial, },
+- { STRING_LOOPBACK, loopback, },
+- { STRING_SOURCE_SINK, source_sink, },
++static struct usb_string strings_dev[] = {
++ [STRING_MANUFACTURER_IDX].s = manufacturer,
++ [STRING_PRODUCT_IDX].s = longname,
++ [STRING_SERIAL_IDX].s = serial,
+ { } /* end of list */
+ };
+
+-static struct usb_gadget_strings stringtab = {
++static struct usb_gadget_strings stringtab_dev = {
+ .language = 0x0409, /* en-us */
+- .strings = strings,
++ .strings = strings_dev,
+ };
+
+-/*
+- * config descriptors are also handcrafted. these must agree with code
+- * that sets configurations, and with code managing interfaces and their
+- * altsettings. other complexity may come from:
+- *
+- * - high speed support, including "other speed config" rules
+- * - multiple configurations
+- * - interfaces with alternate settings
+- * - embedded class or vendor-specific descriptors
+- *
+- * this handles high speed, and has a second config that could as easily
+- * have been an alternate interface setting (on most hardware).
+- *
+- * NOTE: to demonstrate (and test) more USB capabilities, this driver
+- * should include an altsetting to test interrupt transfers, including
+- * high bandwidth modes at high speed. (Maybe work like Intel's test
+- * device?)
+- */
+-static int config_buf(struct usb_gadget *gadget,
+- u8 *buf, u8 type, unsigned index)
+-{
+- int is_source_sink;
+- int len;
+- const struct usb_descriptor_header **function;
+- int hs = 0;
+-
+- /* two configurations will always be index 0 and index 1 */
+- if (index > 1)
+- return -EINVAL;
+- is_source_sink = loopdefault ? (index == 1) : (index == 0);
+-
+- if (gadget_is_dualspeed(gadget)) {
+- hs = (gadget->speed == USB_SPEED_HIGH);
+- if (type == USB_DT_OTHER_SPEED_CONFIG)
+- hs = !hs;
+- }
+- if (hs)
+- function = is_source_sink
+- ? hs_source_sink_function
+- : hs_loopback_function;
+- else
+- function = is_source_sink
+- ? fs_source_sink_function
+- : fs_loopback_function;
+-
+- /* for now, don't advertise srp-only devices */
+- if (!gadget_is_otg(gadget))
+- function++;
+-
+- len = usb_gadget_config_buf(is_source_sink
+- ? &source_sink_config
+- : &loopback_config,
+- buf, USB_BUFSIZ, function);
+- if (len < 0)
+- return len;
+- ((struct usb_config_descriptor *) buf)->bDescriptorType = type;
+- return len;
+-}
++static struct usb_gadget_strings *dev_strings[] = {
++ &stringtab_dev,
++ NULL,
++};
+
+ /*-------------------------------------------------------------------------*/
+
+-static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length)
++struct usb_request *alloc_ep_req(struct usb_ep *ep)
+ {
+ struct usb_request *req;
+
+ req = usb_ep_alloc_request(ep, GFP_ATOMIC);
+ if (req) {
+- req->length = length;
+- req->buf = kmalloc(length, GFP_ATOMIC);
++ req->length = buflen;
++ req->buf = kmalloc(buflen, GFP_ATOMIC);
+ if (!req->buf) {
+ usb_ep_free_request(ep, req);
+ req = NULL;
+@@ -433,681 +167,73 @@ static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length)
+ return req;
+ }
+
+-static void free_ep_req(struct usb_ep *ep, struct usb_request *req)
++void free_ep_req(struct usb_ep *ep, struct usb_request *req)
+ {
+ kfree(req->buf);
+ usb_ep_free_request(ep, req);
+ }
+
+-/*-------------------------------------------------------------------------*/
+-
+-/*
+- * SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripherals,
+- * this just sinks bulk packets OUT to the peripheral and sources them IN
+- * to the host, optionally with specific data patterns.
+- *
+- * In terms of control messaging, this supports all the standard requests
+- * plus two that support control-OUT tests.
+- *
+- * Note that because this doesn't queue more than one request at a time,
+- * some other function must be used to test queueing logic. The network
+- * link (g_ether) is probably the best option for that.
+- */
+-
+-/* optionally require specific source/sink data patterns */
+-
+-static int
+-check_read_data(
+- struct zero_dev *dev,
+- struct usb_ep *ep,
+- struct usb_request *req
+-)
++static void disable_ep(struct usb_composite_dev *cdev, struct usb_ep *ep)
+ {
+- unsigned i;
+- u8 *buf = req->buf;
+-
+- for (i = 0; i < req->actual; i++, buf++) {
+- switch (pattern) {
+- /* all-zeroes has no synchronization issues */
+- case 0:
+- if (*buf == 0)
+- continue;
+- break;
+- /* mod63 stays in sync with short-terminated transfers,
+- * or otherwise when host and gadget agree on how large
+- * each usb transfer request should be. resync is done
+- * with set_interface or set_config.
+- */
+- case 1:
+- if (*buf == (u8)(i % 63))
+- continue;
+- break;
+- }
+- ERROR(dev, "bad OUT byte, buf[%d] = %d\n", i, *buf);
+- usb_ep_set_halt(ep);
+- return -EINVAL;
++ int value;
++
++ if (ep->driver_data) {
++ value = usb_ep_disable(ep);
++ if (value < 0)
++ DBG(cdev, "disable %s --> %d\n",
++ ep->name, value);
++ ep->driver_data = NULL;
+ }
+- return 0;
+ }
+
+-static void reinit_write_data(struct usb_ep *ep, struct usb_request *req)
++void disable_endpoints(struct usb_composite_dev *cdev,
++ struct usb_ep *in, struct usb_ep *out)
+ {
+- unsigned i;
+- u8 *buf = req->buf;
+-
+- switch (pattern) {
+- case 0:
+- memset(req->buf, 0, req->length);
+- break;
+- case 1:
+- for (i = 0; i < req->length; i++)
+- *buf++ = (u8) (i % 63);
+- break;
+- }
+-}
+-
+-/* if there is only one request in the queue, there'll always be an
+- * irq delay between end of one request and start of the next.
+- * that prevents using hardware dma queues.
+- */
+-static void source_sink_complete(struct usb_ep *ep, struct usb_request *req)
+-{
+- struct zero_dev *dev = ep->driver_data;
+- int status = req->status;
+-
+- switch (status) {
+-
+- case 0: /* normal completion? */
+- if (ep == dev->out_ep) {
+- check_read_data(dev, ep, req);
+- memset(req->buf, 0x55, req->length);
+- } else
+- reinit_write_data(ep, req);
+- break;
+-
+- /* this endpoint is normally active while we're configured */
+- case -ECONNABORTED: /* hardware forced ep reset */
+- case -ECONNRESET: /* request dequeued */
+- case -ESHUTDOWN: /* disconnect from host */
+- VDBG(dev, "%s gone (%d), %d/%d\n", ep->name, status,
+- req->actual, req->length);
+- if (ep == dev->out_ep)
+- check_read_data(dev, ep, req);
+- free_ep_req(ep, req);
+- return;
+-
+- case -EOVERFLOW: /* buffer overrun on read means that
+- * we didn't provide a big enough
+- * buffer.
+- */
+- default:
+-#if 1
+- DBG(dev, "%s complete --> %d, %d/%d\n", ep->name,
+- status, req->actual, req->length);
+-#endif
+- case -EREMOTEIO: /* short read */
+- break;
+- }
+-
+- status = usb_ep_queue(ep, req, GFP_ATOMIC);
+- if (status) {
+- ERROR(dev, "kill %s: resubmit %d bytes --> %d\n",
+- ep->name, req->length, status);
+- usb_ep_set_halt(ep);
+- /* FIXME recover later ... somehow */
+- }
+-}
+-
+-static struct usb_request *source_sink_start_ep(struct usb_ep *ep)
+-{
+- struct usb_request *req;
+- int status;
+-
+- req = alloc_ep_req(ep, buflen);
+- if (!req)
+- return NULL;
+-
+- memset(req->buf, 0, req->length);
+- req->complete = source_sink_complete;
+-
+- if (strcmp(ep->name, EP_IN_NAME) == 0)
+- reinit_write_data(ep, req);
+- else
+- memset(req->buf, 0x55, req->length);
+-
+- status = usb_ep_queue(ep, req, GFP_ATOMIC);
+- if (status) {
+- struct zero_dev *dev = ep->driver_data;
+-
+- ERROR(dev, "start %s --> %d\n", ep->name, status);
+- free_ep_req(ep, req);
+- req = NULL;
+- }
+-
+- return req;
+-}
+-
+-static int set_source_sink_config(struct zero_dev *dev)
+-{
+- int result = 0;
+- struct usb_ep *ep;
+- struct usb_gadget *gadget = dev->gadget;
+-
+- gadget_for_each_ep(ep, gadget) {
+- const struct usb_endpoint_descriptor *d;
+-
+- /* one endpoint writes (sources) zeroes in (to the host) */
+- if (strcmp(ep->name, EP_IN_NAME) == 0) {
+- d = ep_desc(gadget, &hs_source_desc, &fs_source_desc);
+- result = usb_ep_enable(ep, d);
+- if (result == 0) {
+- ep->driver_data = dev;
+- if (source_sink_start_ep(ep) != NULL) {
+- dev->in_ep = ep;
+- continue;
+- }
+- usb_ep_disable(ep);
+- result = -EIO;
+- }
+-
+- /* one endpoint reads (sinks) anything out (from the host) */
+- } else if (strcmp(ep->name, EP_OUT_NAME) == 0) {
+- d = ep_desc(gadget, &hs_sink_desc, &fs_sink_desc);
+- result = usb_ep_enable(ep, d);
+- if (result == 0) {
+- ep->driver_data = dev;
+- if (source_sink_start_ep(ep) != NULL) {
+- dev->out_ep = ep;
+- continue;
+- }
+- usb_ep_disable(ep);
+- result = -EIO;
+- }
+-
+- /* ignore any other endpoints */
+- } else
+- continue;
+-
+- /* stop on error */
+- ERROR(dev, "can't start %s, result %d\n", ep->name, result);
+- break;
+- }
+- if (result == 0)
+- DBG(dev, "buflen %d\n", buflen);
+-
+- /* caller is responsible for cleanup on error */
+- return result;
+-}
+-
+-/*-------------------------------------------------------------------------*/
+-
+-static void loopback_complete(struct usb_ep *ep, struct usb_request *req)
+-{
+- struct zero_dev *dev = ep->driver_data;
+- int status = req->status;
+-
+- switch (status) {
+-
+- case 0: /* normal completion? */
+- if (ep == dev->out_ep) {
+- /* loop this OUT packet back IN to the host */
+- req->zero = (req->actual < req->length);
+- req->length = req->actual;
+- status = usb_ep_queue(dev->in_ep, req, GFP_ATOMIC);
+- if (status == 0)
+- return;
+-
+- /* "should never get here" */
+- ERROR(dev, "can't loop %s to %s: %d\n",
+- ep->name, dev->in_ep->name,
+- status);
+- }
+-
+- /* queue the buffer for some later OUT packet */
+- req->length = buflen;
+- status = usb_ep_queue(dev->out_ep, req, GFP_ATOMIC);
+- if (status == 0)
+- return;
+-
+- /* "should never get here" */
+- /* FALLTHROUGH */
+-
+- default:
+- ERROR(dev, "%s loop complete --> %d, %d/%d\n", ep->name,
+- status, req->actual, req->length);
+- /* FALLTHROUGH */
+-
+- /* NOTE: since this driver doesn't maintain an explicit record
+- * of requests it submitted (just maintains qlen count), we
+- * rely on the hardware driver to clean up on disconnect or
+- * endpoint disable.
+- */
+- case -ECONNABORTED: /* hardware forced ep reset */
+- case -ECONNRESET: /* request dequeued */
+- case -ESHUTDOWN: /* disconnect from host */
+- free_ep_req(ep, req);
+- return;
+- }
+-}
+-
+-static int set_loopback_config(struct zero_dev *dev)
+-{
+- int result = 0;
+- struct usb_ep *ep;
+- struct usb_gadget *gadget = dev->gadget;
+-
+- gadget_for_each_ep(ep, gadget) {
+- const struct usb_endpoint_descriptor *d;
+-
+- /* one endpoint writes data back IN to the host */
+- if (strcmp(ep->name, EP_IN_NAME) == 0) {
+- d = ep_desc(gadget, &hs_source_desc, &fs_source_desc);
+- result = usb_ep_enable(ep, d);
+- if (result == 0) {
+- ep->driver_data = dev;
+- dev->in_ep = ep;
+- continue;
+- }
+-
+- /* one endpoint just reads OUT packets */
+- } else if (strcmp(ep->name, EP_OUT_NAME) == 0) {
+- d = ep_desc(gadget, &hs_sink_desc, &fs_sink_desc);
+- result = usb_ep_enable(ep, d);
+- if (result == 0) {
+- ep->driver_data = dev;
+- dev->out_ep = ep;
+- continue;
+- }
+-
+- /* ignore any other endpoints */
+- } else
+- continue;
+-
+- /* stop on error */
+- ERROR(dev, "can't enable %s, result %d\n", ep->name, result);
+- break;
+- }
+-
+- /* allocate a bunch of read buffers and queue them all at once.
+- * we buffer at most 'qlen' transfers; fewer if any need more
+- * than 'buflen' bytes each.
+- */
+- if (result == 0) {
+- struct usb_request *req;
+- unsigned i;
+-
+- ep = dev->out_ep;
+- for (i = 0; i < qlen && result == 0; i++) {
+- req = alloc_ep_req(ep, buflen);
+- if (req) {
+- req->complete = loopback_complete;
+- result = usb_ep_queue(ep, req, GFP_ATOMIC);
+- if (result)
+- DBG(dev, "%s queue req --> %d\n",
+- ep->name, result);
+- } else
+- result = -ENOMEM;
+- }
+- }
+- if (result == 0)
+- DBG(dev, "qlen %d, buflen %d\n", qlen, buflen);
+-
+- /* caller is responsible for cleanup on error */
+- return result;
+-}
+-
+-/*-------------------------------------------------------------------------*/
+-
+-static void zero_reset_config(struct zero_dev *dev)
+-{
+- if (dev->config == 0)
+- return;
+-
+- DBG(dev, "reset config\n");
+-
+- /* just disable endpoints, forcing completion of pending i/o.
+- * all our completion handlers free their requests in this case.
+- */
+- if (dev->in_ep) {
+- usb_ep_disable(dev->in_ep);
+- dev->in_ep = NULL;
+- }
+- if (dev->out_ep) {
+- usb_ep_disable(dev->out_ep);
+- dev->out_ep = NULL;
+- }
+- dev->config = 0;
+- del_timer(&dev->resume);
+-}
+-
+-/* change our operational config. this code must agree with the code
+- * that returns config descriptors, and altsetting code.
+- *
+- * it's also responsible for power management interactions. some
+- * configurations might not work with our current power sources.
+- *
+- * note that some device controller hardware will constrain what this
+- * code can do, perhaps by disallowing more than one configuration or
+- * by limiting configuration choices (like the pxa2xx).
+- */
+-static int zero_set_config(struct zero_dev *dev, unsigned number)
+-{
+- int result = 0;
+- struct usb_gadget *gadget = dev->gadget;
+-
+- if (number == dev->config)
+- return 0;
+-
+- if (gadget_is_sa1100(gadget) && dev->config) {
+- /* tx fifo is full, but we can't clear it...*/
+- ERROR(dev, "can't change configurations\n");
+- return -ESPIPE;
+- }
+- zero_reset_config(dev);
+-
+- switch (number) {
+- case CONFIG_SOURCE_SINK:
+- result = set_source_sink_config(dev);
+- break;
+- case CONFIG_LOOPBACK:
+- result = set_loopback_config(dev);
+- break;
+- default:
+- result = -EINVAL;
+- /* FALL THROUGH */
+- case 0:
+- return result;
+- }
+-
+- if (!result && (!dev->in_ep || !dev->out_ep))
+- result = -ENODEV;
+- if (result)
+- zero_reset_config(dev);
+- else {
+- char *speed;
+-
+- switch (gadget->speed) {
+- case USB_SPEED_LOW: speed = "low"; break;
+- case USB_SPEED_FULL: speed = "full"; break;
+- case USB_SPEED_HIGH: speed = "high"; break;
+- default: speed = "?"; break;
+- }
+-
+- dev->config = number;
+- INFO(dev, "%s speed config #%d: %s\n", speed, number,
+- (number == CONFIG_SOURCE_SINK)
+- ? source_sink : loopback);
+- }
+- return result;
+-}
+-
+-/*-------------------------------------------------------------------------*/
+-
+-static void zero_setup_complete(struct usb_ep *ep, struct usb_request *req)
+-{
+- if (req->status || req->actual != req->length)
+- DBG((struct zero_dev *) ep->driver_data,
+- "setup complete --> %d, %d/%d\n",
+- req->status, req->actual, req->length);
+-}
+-
+-/*
+- * The setup() callback implements all the ep0 functionality that's
+- * not handled lower down, in hardware or the hardware driver (like
+- * device and endpoint feature flags, and their status). It's all
+- * housekeeping for the gadget function we're implementing. Most of
+- * the work is in config-specific setup.
+- */
+-static int
+-zero_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
+-{
+- struct zero_dev *dev = get_gadget_data(gadget);
+- struct usb_request *req = dev->req;
+- int value = -EOPNOTSUPP;
+- u16 w_index = le16_to_cpu(ctrl->wIndex);
+- u16 w_value = le16_to_cpu(ctrl->wValue);
+- u16 w_length = le16_to_cpu(ctrl->wLength);
+-
+- /* usually this stores reply data in the pre-allocated ep0 buffer,
+- * but config change events will reconfigure hardware.
+- */
+- req->zero = 0;
+- switch (ctrl->bRequest) {
+-
+- case USB_REQ_GET_DESCRIPTOR:
+- if (ctrl->bRequestType != USB_DIR_IN)
+- goto unknown;
+- switch (w_value >> 8) {
+-
+- case USB_DT_DEVICE:
+- value = min(w_length, (u16) sizeof device_desc);
+- memcpy(req->buf, &device_desc, value);
+- break;
+- case USB_DT_DEVICE_QUALIFIER:
+- if (!gadget_is_dualspeed(gadget))
+- break;
+- value = min(w_length, (u16) sizeof dev_qualifier);
+- memcpy(req->buf, &dev_qualifier, value);
+- break;
+-
+- case USB_DT_OTHER_SPEED_CONFIG:
+- if (!gadget_is_dualspeed(gadget))
+- break;
+- // FALLTHROUGH
+- case USB_DT_CONFIG:
+- value = config_buf(gadget, req->buf,
+- w_value >> 8,
+- w_value & 0xff);
+- if (value >= 0)
+- value = min(w_length, (u16) value);
+- break;
+-
+- case USB_DT_STRING:
+- /* wIndex == language code.
+- * this driver only handles one language, you can
+- * add string tables for other languages, using
+- * any UTF-8 characters
+- */
+- value = usb_gadget_get_string(&stringtab,
+- w_value & 0xff, req->buf);
+- if (value >= 0)
+- value = min(w_length, (u16) value);
+- break;
+- }
+- break;
+-
+- /* currently two configs, two speeds */
+- case USB_REQ_SET_CONFIGURATION:
+- if (ctrl->bRequestType != 0)
+- goto unknown;
+- if (gadget->a_hnp_support)
+- DBG(dev, "HNP available\n");
+- else if (gadget->a_alt_hnp_support)
+- DBG(dev, "HNP needs a different root port\n");
+- else
+- VDBG(dev, "HNP inactive\n");
+- spin_lock(&dev->lock);
+- value = zero_set_config(dev, w_value);
+- spin_unlock(&dev->lock);
+- break;
+- case USB_REQ_GET_CONFIGURATION:
+- if (ctrl->bRequestType != USB_DIR_IN)
+- goto unknown;
+- *(u8 *)req->buf = dev->config;
+- value = min(w_length, (u16) 1);
+- break;
+-
+- /* until we add altsetting support, or other interfaces,
+- * only 0/0 are possible. pxa2xx only supports 0/0 (poorly)
+- * and already killed pending endpoint I/O.
+- */
+- case USB_REQ_SET_INTERFACE:
+- if (ctrl->bRequestType != USB_RECIP_INTERFACE)
+- goto unknown;
+- spin_lock(&dev->lock);
+- if (dev->config && w_index == 0 && w_value == 0) {
+- u8 config = dev->config;
+-
+- /* resets interface configuration, forgets about
+- * previous transaction state (queued bufs, etc)
+- * and re-inits endpoint state (toggle etc)
+- * no response queued, just zero status == success.
+- * if we had more than one interface we couldn't
+- * use this "reset the config" shortcut.
+- */
+- zero_reset_config(dev);
+- zero_set_config(dev, config);
+- value = 0;
+- }
+- spin_unlock(&dev->lock);
+- break;
+- case USB_REQ_GET_INTERFACE:
+- if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE))
+- goto unknown;
+- if (!dev->config)
+- break;
+- if (w_index != 0) {
+- value = -EDOM;
+- break;
+- }
+- *(u8 *)req->buf = 0;
+- value = min(w_length, (u16) 1);
+- break;
+-
+- /*
+- * These are the same vendor-specific requests supported by
+- * Intel's USB 2.0 compliance test devices. We exceed that
+- * device spec by allowing multiple-packet requests.
+- */
+- case 0x5b: /* control WRITE test -- fill the buffer */
+- if (ctrl->bRequestType != (USB_DIR_OUT|USB_TYPE_VENDOR))
+- goto unknown;
+- if (w_value || w_index)
+- break;
+- /* just read that many bytes into the buffer */
+- if (w_length > USB_BUFSIZ)
+- break;
+- value = w_length;
+- break;
+- case 0x5c: /* control READ test -- return the buffer */
+- if (ctrl->bRequestType != (USB_DIR_IN|USB_TYPE_VENDOR))
+- goto unknown;
+- if (w_value || w_index)
+- break;
+- /* expect those bytes are still in the buffer; send back */
+- if (w_length > USB_BUFSIZ
+- || w_length != req->length)
+- break;
+- value = w_length;
+- break;
+-
+- default:
+-unknown:
+- VDBG(dev,
+- "unknown control req%02x.%02x v%04x i%04x l%d\n",
+- ctrl->bRequestType, ctrl->bRequest,
+- w_value, w_index, w_length);
+- }
+-
+- /* respond with data transfer before status phase? */
+- if (value >= 0) {
+- req->length = value;
+- req->zero = value < w_length;
+- value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
+- if (value < 0) {
+- DBG(dev, "ep_queue --> %d\n", value);
+- req->status = 0;
+- zero_setup_complete(gadget->ep0, req);
+- }
+- }
+-
+- /* device either stalls (value < 0) or reports success */
+- return value;
+-}
+-
+-static void zero_disconnect(struct usb_gadget *gadget)
+-{
+- struct zero_dev *dev = get_gadget_data(gadget);
+- unsigned long flags;
+-
+- spin_lock_irqsave(&dev->lock, flags);
+- zero_reset_config(dev);
+-
+- /* a more significant application might have some non-usb
+- * activities to quiesce here, saving resources like power
+- * or pushing the notification up a network stack.
+- */
+- spin_unlock_irqrestore(&dev->lock, flags);
+-
+- /* next we may get setup() calls to enumerate new connections;
+- * or an unbind() during shutdown (including removing module).
+- */
+-}
+-
+-static void zero_autoresume(unsigned long _dev)
+-{
+- struct zero_dev *dev = (struct zero_dev *) _dev;
+- int status;
+-
+- /* normally the host would be woken up for something
+- * more significant than just a timer firing...
+- */
+- if (dev->gadget->speed != USB_SPEED_UNKNOWN) {
+- status = usb_gadget_wakeup(dev->gadget);
+- DBG(dev, "wakeup --> %d\n", status);
+- }
++ disable_ep(cdev, in);
++ disable_ep(cdev, out);
+ }
+
+ /*-------------------------------------------------------------------------*/
+
+-static void zero_unbind(struct usb_gadget *gadget)
++static int __init zero_bind(struct usb_composite_dev *cdev)
+ {
+- struct zero_dev *dev = get_gadget_data(gadget);
+-
+- DBG(dev, "unbind\n");
+-
+- /* we've already been disconnected ... no i/o is active */
+- if (dev->req) {
+- dev->req->length = USB_BUFSIZ;
+- free_ep_req(gadget->ep0, dev->req);
+- }
+- del_timer_sync(&dev->resume);
+- kfree(dev);
+- set_gadget_data(gadget, NULL);
+-}
+-
+-static int __init zero_bind(struct usb_gadget *gadget)
+-{
+- struct zero_dev *dev;
+- struct usb_ep *ep;
+ int gcnum;
++ struct usb_gadget *gadget = cdev->gadget;
++ int id;
+
+- /* FIXME this can't yet work right with SH ... it has only
+- * one configuration, numbered one.
++ /* Allocate string descriptor numbers ... note that string
++ * contents can be overridden by the composite_dev glue.
+ */
+- if (gadget_is_sh(gadget))
+- return -ENODEV;
+-
+- /* Bulk-only drivers like this one SHOULD be able to
+- * autoconfigure on any sane usb controller driver,
+- * but there may also be important quirks to address.
++ id = usb_string_id(cdev);
++ if (id < 0)
++ return id;
++ strings_dev[STRING_MANUFACTURER_IDX].id = id;
++ device_desc.iManufacturer = id;
++
++ id = usb_string_id(cdev);
++ if (id < 0)
++ return id;
++ strings_dev[STRING_PRODUCT_IDX].id = id;
++ device_desc.iProduct = id;
++
++ id = usb_string_id(cdev);
++ if (id < 0)
++ return id;
++ strings_dev[STRING_SERIAL_IDX].id = id;
++ device_desc.iSerialNumber = id;
++
++ /* Register primary, then secondary configuration. Note that
++ * SH3 only allows one config...
+ */
+- usb_ep_autoconfig_reset(gadget);
+- ep = usb_ep_autoconfig(gadget, &fs_source_desc);
+- if (!ep) {
+-autoconf_fail:
+- pr_err("%s: can't autoconfigure on %s\n",
+- shortname, gadget->name);
+- return -ENODEV;
++ if (loopdefault) {
++ loopback_add(cdev);
++ if (!gadget_is_sh(gadget))
++ sourcesink_add(cdev);
++ } else {
++ sourcesink_add(cdev);
++ if (!gadget_is_sh(gadget))
++ loopback_add(cdev);
+ }
+- EP_IN_NAME = ep->name;
+- ep->driver_data = ep; /* claim */
+-
+- ep = usb_ep_autoconfig(gadget, &fs_sink_desc);
+- if (!ep)
+- goto autoconf_fail;
+- EP_OUT_NAME = ep->name;
+- ep->driver_data = ep; /* claim */
+
+ gcnum = usb_gadget_controller_number(gadget);
+ if (gcnum >= 0)
+@@ -1115,144 +241,44 @@ autoconf_fail:
+ else {
+ /* gadget zero is so simple (for now, no altsettings) that
+ * it SHOULD NOT have problems with bulk-capable hardware.
+- * so warn about unrcognized controllers, don't panic.
++ * so just warn about unrcognized controllers -- don't panic.
+ *
+ * things like configuration and altsetting numbering
+ * can need hardware-specific attention though.
+ */
+ pr_warning("%s: controller '%s' not recognized\n",
+- shortname, gadget->name);
++ longname, gadget->name);
+ device_desc.bcdDevice = __constant_cpu_to_le16(0x9999);
+ }
+
+
+- /* ok, we made sense of the hardware ... */
+- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+- if (!dev)
+- return -ENOMEM;
+- spin_lock_init(&dev->lock);
+- dev->gadget = gadget;
+- set_gadget_data(gadget, dev);
+-
+- init_timer(&dev->resume);
+- dev->resume.function = zero_autoresume;
+- dev->resume.data = (unsigned long) dev;
+-
+- /* preallocate control response and buffer */
+- dev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
+- if (!dev->req)
+- goto enomem;
+- dev->req->buf = kmalloc(USB_BUFSIZ, GFP_KERNEL);
+- if (!dev->req->buf)
+- goto enomem;
+-
+- dev->req->complete = zero_setup_complete;
+-
+- device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
+-
+- if (gadget_is_dualspeed(gadget)) {
+- /* assume ep0 uses the same value for both speeds ... */
+- dev_qualifier.bMaxPacketSize0 = device_desc.bMaxPacketSize0;
+-
+- /* and that all endpoints are dual-speed */
+- hs_source_desc.bEndpointAddress =
+- fs_source_desc.bEndpointAddress;
+- hs_sink_desc.bEndpointAddress =
+- fs_sink_desc.bEndpointAddress;
+- }
+-
+- if (gadget_is_otg(gadget)) {
+- otg_descriptor.bmAttributes |= USB_OTG_HNP,
+- source_sink_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+- loopback_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+- }
+-
+- usb_gadget_set_selfpowered(gadget);
+-
+- if (autoresume) {
+- source_sink_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+- loopback_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
+- }
+-
+- gadget->ep0->driver_data = dev;
+-
+- INFO(dev, "%s, version: " DRIVER_VERSION "\n", longname);
+- INFO(dev, "using %s, OUT %s IN %s\n", gadget->name,
+- EP_OUT_NAME, EP_IN_NAME);
++ INFO(cdev, "%s, version: " DRIVER_VERSION "\n", longname);
+
+ snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
+ init_utsname()->sysname, init_utsname()->release,
+ gadget->name);
+
+ return 0;
+-
+-enomem:
+- zero_unbind(gadget);
+- return -ENOMEM;
+ }
+
+-/*-------------------------------------------------------------------------*/
+-
+-static void zero_suspend(struct usb_gadget *gadget)
+-{
+- struct zero_dev *dev = get_gadget_data(gadget);
+-
+- if (gadget->speed == USB_SPEED_UNKNOWN)
+- return;
+-
+- if (autoresume) {
+- mod_timer(&dev->resume, jiffies + (HZ * autoresume));
+- DBG(dev, "suspend, wakeup in %d seconds\n", autoresume);
+- } else
+- DBG(dev, "suspend\n");
+-}
+-
+-static void zero_resume(struct usb_gadget *gadget)
+-{
+- struct zero_dev *dev = get_gadget_data(gadget);
+-
+- DBG(dev, "resume\n");
+- del_timer(&dev->resume);
+-}
+-
+-
+-/*-------------------------------------------------------------------------*/
+-
+-static struct usb_gadget_driver zero_driver = {
+-#ifdef CONFIG_USB_GADGET_DUALSPEED
+- .speed = USB_SPEED_HIGH,
+-#else
+- .speed = USB_SPEED_FULL,
+-#endif
+- .function = (char *) longname,
++static struct usb_composite_driver zero_driver = {
++ .name = "zero",
++ .dev = &device_desc,
++ .strings = dev_strings,
+ .bind = zero_bind,
+- .unbind = __exit_p(zero_unbind),
+-
+- .setup = zero_setup,
+- .disconnect = zero_disconnect,
+-
+- .suspend = zero_suspend,
+- .resume = zero_resume,
+-
+- .driver = {
+- .name = (char *) shortname,
+- .owner = THIS_MODULE,
+- },
+ };
+
+ MODULE_AUTHOR("David Brownell");
+ MODULE_LICENSE("GPL");
+
+-
+ static int __init init(void)
+ {
+- return usb_gadget_register_driver(&zero_driver);
++ return usb_composite_register(&zero_driver);
+ }
+ module_init(init);
+
+ static void __exit cleanup(void)
+ {
+- usb_gadget_unregister_driver(&zero_driver);
++ usb_composite_unregister(&zero_driver);
+ }
+ module_exit(cleanup);
+-
+diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h
+new file mode 100644
+index 0000000..c932390
+--- /dev/null
++++ b/include/linux/usb/composite.h
+@@ -0,0 +1,338 @@
++/*
++ * composite.h -- framework for usb gadgets which are composite devices
++ *
++ * Copyright (C) 2006-2008 David Brownell
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++#ifndef __LINUX_USB_COMPOSITE_H
++#define __LINUX_USB_COMPOSITE_H
++
++/*
++ * This framework is an optional layer on top of the USB Gadget interface,
++ * making it easier to build (a) Composite devices, supporting multiple
++ * functions within any single configuration, and (b) Multi-configuration
++ * devices, also supporting multiple functions but without necessarily
++ * having more than one function per configuration.
++ *
++ * Example: a device with a single configuration supporting both network
++ * link and mass storage functions is a composite device. Those functions
++ * might alternatively be packaged in individual configurations, but in
++ * the composite model the host can use both functions at the same time.
++ */
++
++#include <linux/usb/ch9.h>
++#include <linux/usb/gadget.h>
++
++
++struct usb_configuration;
++
++/**
++ * struct usb_function - describes one function of a configuration
++ * @name: For diagnostics, identifies the function.
++ * @strings: tables of strings, keyed by identifiers assigned during bind()
++ * and by language IDs provided in control requests
++ * @descriptors: Table of full (or low) speed descriptors, using interface and
++ * string identifiers assigned during @bind(). If this pointer is null,
++ * the function will not be available at full speed (or at low speed).
++ * @hs_descriptors: Table of high speed descriptors, using interface and
++ * string identifiers assigned during @bind(). If this pointer is null,
++ * the function will not be available at high speed.
++ * @config: assigned when @usb_add_function() is called; this is the
++ * configuration with which this function is associated.
++ * @bind: Before the gadget can register, all of its functions bind() to the
++ * available resources including string and interface identifiers used
++ * in interface or class descriptors; endpoints; I/O buffers; and so on.
++ * @unbind: Reverses @bind; called as a side effect of unregistering the
++ * driver which added this function.
++ * @set_alt: (REQUIRED) Reconfigures altsettings; function drivers may
++ * initialize usb_ep.driver data at this time (when it is used).
++ * Note that setting an interface to its current altsetting resets
++ * interface state, and that all interfaces have a disabled state.
++ * @get_alt: Returns the active altsetting. If this is not provided,
++ * then only altsetting zero is supported.
++ * @disable: (REQUIRED) Indicates the function should be disabled. Reasons
++ * include host resetting or reconfiguring the gadget, and disconnection.
++ * @setup: Used for interface-specific control requests.
++ * @suspend: Notifies functions when the host stops sending USB traffic.
++ * @resume: Notifies functions when the host restarts USB traffic.
++ *
++ * A single USB function uses one or more interfaces, and should in most
++ * cases support operation at both full and high speeds. Each function is
++ * associated by @usb_add_function() with a one configuration; that function
++ * causes @bind() to be called so resources can be allocated as part of
++ * setting up a gadget driver. Those resources include endpoints, which
++ * should be allocated using @usb_ep_autoconfig().
++ *
++ * To support dual speed operation, a function driver provides descriptors
++ * for both high and full speed operation. Except in rare cases that don't
++ * involve bulk endpoints, each speed needs different endpoint descriptors.
++ *
++ * Function drivers choose their own strategies for managing instance data.
++ * The simplest strategy just declares it "static', which means the function
++ * can only be activated once. If the function needs to be exposed in more
++ * than one configuration at a given speed, it needs to support multiple
++ * usb_function structures (one for each configuration).
++ *
++ * A more complex strategy might encapsulate a @usb_function structure inside
++ * a driver-specific instance structure to allows multiple activations. An
++ * example of multiple activations might be a CDC ACM function that supports
++ * two or more distinct instances within the same configuration, providing
++ * several independent logical data links to a USB host.
++ */
++struct usb_function {
++ const char *name;
++ struct usb_gadget_strings **strings;
++ struct usb_descriptor_header **descriptors;
++ struct usb_descriptor_header **hs_descriptors;
++
++ struct usb_configuration *config;
++
++ /* REVISIT: bind() functions can be marked __init, which
++ * makes trouble for section mismatch analysis. See if
++ * we can't restructure things to avoid mismatching.
++ * Related: unbind() may kfree() but bind() won't...
++ */
++
++ /* configuration management: bind/unbind */
++ int (*bind)(struct usb_configuration *,
++ struct usb_function *);
++ void (*unbind)(struct usb_configuration *,
++ struct usb_function *);
++
++ /* runtime state management */
++ int (*set_alt)(struct usb_function *,
++ unsigned interface, unsigned alt);
++ int (*get_alt)(struct usb_function *,
++ unsigned interface);
++ void (*disable)(struct usb_function *);
++ int (*setup)(struct usb_function *,
++ const struct usb_ctrlrequest *);
++ void (*suspend)(struct usb_function *);
++ void (*resume)(struct usb_function *);
++
++ /* internals */
++ struct list_head list;
++};
++
++int usb_add_function(struct usb_configuration *, struct usb_function *);
++
++int usb_interface_id(struct usb_configuration *, struct usb_function *);
++
++/**
++ * ep_choose - select descriptor endpoint at current device speed
++ * @g: gadget, connected and running at some speed
++ * @hs: descriptor to use for high speed operation
++ * @fs: descriptor to use for full or low speed operation
++ */
++static inline struct usb_endpoint_descriptor *
++ep_choose(struct usb_gadget *g, struct usb_endpoint_descriptor *hs,
++ struct usb_endpoint_descriptor *fs)
++{
++ if (gadget_is_dualspeed(g) && g->speed == USB_SPEED_HIGH)
++ return hs;
++ return fs;
++}
++
++#define MAX_CONFIG_INTERFACES 16 /* arbitrary; max 255 */
++
++/**
++ * struct usb_configuration - represents one gadget configuration
++ * @label: For diagnostics, describes the configuration.
++ * @strings: Tables of strings, keyed by identifiers assigned during @bind()
++ * and by language IDs provided in control requests.
++ * @descriptors: Table of descriptors preceding all function descriptors.
++ * Examples include OTG and vendor-specific descriptors.
++ * @bind: Called from @usb_add_config() to allocate resources unique to this
++ * configuration and to call @usb_add_function() for each function used.
++ * @unbind: Reverses @bind; called as a side effect of unregistering the
++ * driver which added this configuration.
++ * @setup: Used to delegate control requests that aren't handled by standard
++ * device infrastructure or directed at a specific interface.
++ * @bConfigurationValue: Copied into configuration descriptor.
++ * @iConfiguration: Copied into configuration descriptor.
++ * @bmAttributes: Copied into configuration descriptor.
++ * @bMaxPower: Copied into configuration descriptor.
++ * @cdev: assigned by @usb_add_config() before calling @bind(); this is
++ * the device associated with this configuration.
++ *
++ * Configurations are building blocks for gadget drivers structured around
++ * function drivers. Simple USB gadgets require only one function and one
++ * configuration, and handle dual-speed hardware by always providing the same
++ * functionality. Slightly more complex gadgets may have more than one
++ * single-function configuration at a given speed; or have configurations
++ * that only work at one speed.
++ *
++ * Composite devices are, by definition, ones with configurations which
++ * include more than one function.
++ *
++ * The lifecycle of a usb_configuration includes allocation, initialization
++ * of the fields described above, and calling @usb_add_config() to set up
++ * internal data and bind it to a specific device. The configuration's
++ * @bind() method is then used to initialize all the functions and then
++ * call @usb_add_function() for them.
++ *
++ * Those functions would normally be independant of each other, but that's
++ * not mandatory. CDC WMC devices are an example where functions often
++ * depend on other functions, with some functions subsidiary to others.
++ * Such interdependency may be managed in any way, so long as all of the
++ * descriptors complete by the time the composite driver returns from
++ * its bind() routine.
++ */
++struct usb_configuration {
++ const char *label;
++ struct usb_gadget_strings **strings;
++ const struct usb_descriptor_header **descriptors;
++
++ /* REVISIT: bind() functions can be marked __init, which
++ * makes trouble for section mismatch analysis. See if
++ * we can't restructure things to avoid mismatching...
++ */
++
++ /* configuration management: bind/unbind */
++ int (*bind)(struct usb_configuration *);
++ void (*unbind)(struct usb_configuration *);
++ int (*setup)(struct usb_configuration *,
++ const struct usb_ctrlrequest *);
++
++ /* fields in the config descriptor */
++ u8 bConfigurationValue;
++ u8 iConfiguration;
++ u8 bmAttributes;
++ u8 bMaxPower;
++
++ struct usb_composite_dev *cdev;
++
++ /* internals */
++ struct list_head list;
++ struct list_head functions;
++ u8 next_interface_id;
++ unsigned highspeed:1;
++ unsigned fullspeed:1;
++ struct usb_function *interface[MAX_CONFIG_INTERFACES];
++};
++
++int usb_add_config(struct usb_composite_dev *,
++ struct usb_configuration *);
++
++/**
++ * struct usb_composite_driver - groups configurations into a gadget
++ * @name: For diagnostics, identifies the driver.
++ * @dev: Template descriptor for the device, including default device
++ * identifiers.
++ * @strings: tables of strings, keyed by identifiers assigned during bind()
++ * and language IDs provided in control requests
++ * @bind: (REQUIRED) Used to allocate resources that are shared across the
++ * whole device, such as string IDs, and add its configurations using
++ * @usb_add_config(). This may fail by returning a negative errno
++ * value; it should return zero on successful initialization.
++ * @unbind: Reverses @bind(); called as a side effect of unregistering
++ * this driver.
++ *
++ * Devices default to reporting self powered operation. Devices which rely
++ * on bus powered operation should report this in their @bind() method.
++ *
++ * Before returning from @bind, various fields in the template descriptor
++ * may be overridden. These include the idVendor/idProduct/bcdDevice values
++ * normally to bind the appropriate host side driver, and the three strings
++ * (iManufacturer, iProduct, iSerialNumber) normally used to provide user
++ * meaningful device identifiers. (The strings will not be defined unless
++ * they are defined in @dev and @strings.) The correct ep0 maxpacket size
++ * is also reported, as defined by the underlying controller driver.
++ */
++struct usb_composite_driver {
++ const char *name;
++ const struct usb_device_descriptor *dev;
++ struct usb_gadget_strings **strings;
++
++ /* REVISIT: bind() functions can be marked __init, which
++ * makes trouble for section mismatch analysis. See if
++ * we can't restructure things to avoid mismatching...
++ */
++
++ int (*bind)(struct usb_composite_dev *);
++ int (*unbind)(struct usb_composite_dev *);
++};
++
++extern int usb_composite_register(struct usb_composite_driver *);
++extern void usb_composite_unregister(struct usb_composite_driver *);
++
++
++/**
++ * struct usb_composite_device - represents one composite usb gadget
++ * @gadget: read-only, abstracts the gadget's usb peripheral controller
++ * @req: used for control responses; buffer is pre-allocated
++ * @bufsiz: size of buffer pre-allocated in @req
++ * @config: the currently active configuration
++ *
++ * One of these devices is allocated and initialized before the
++ * associated device driver's bind() is called.
++ *
++ * OPEN ISSUE: it appears that some WUSB devices will need to be
++ * built by combining a normal (wired) gadget with a wireless one.
++ * This revision of the gadget framework should probably try to make
++ * sure doing that won't hurt too much.
++ *
++ * One notion for how to handle Wireless USB devices involves:
++ * (a) a second gadget here, discovery mechanism TBD, but likely
++ * needing separate "register/unregister WUSB gadget" calls;
++ * (b) updates to usb_gadget to include flags "is it wireless",
++ * "is it wired", plus (presumably in a wrapper structure)
++ * bandgroup and PHY info;
++ * (c) presumably a wireless_ep wrapping a usb_ep, and reporting
++ * wireless-specific parameters like maxburst and maxsequence;
++ * (d) configurations that are specific to wireless links;
++ * (e) function drivers that understand wireless configs and will
++ * support wireless for (additional) function instances;
++ * (f) a function to support association setup (like CBAF), not
++ * necessarily requiring a wireless adapter;
++ * (g) composite device setup that can create one or more wireless
++ * configs, including appropriate association setup support;
++ * (h) more, TBD.
++ */
++struct usb_composite_dev {
++ struct usb_gadget *gadget;
++ struct usb_request *req;
++ unsigned bufsiz;
++
++ struct usb_configuration *config;
++
++ /* internals */
++ struct usb_device_descriptor desc;
++ struct list_head configs;
++ struct usb_composite_driver *driver;
++ u8 next_string_id;
++
++ spinlock_t lock;
++
++ /* REVISIT use and existence of lock ... */
++};
++
++extern int usb_string_id(struct usb_composite_dev *c);
++
++/* messaging utils */
++#define DBG(d, fmt, args...) \
++ dev_dbg(&(d)->gadget->dev , fmt , ## args)
++#define VDBG(d, fmt, args...) \
++ dev_vdbg(&(d)->gadget->dev , fmt , ## args)
++#define ERROR(d, fmt, args...) \
++ dev_err(&(d)->gadget->dev , fmt , ## args)
++#define WARNING(d, fmt, args...) \
++ dev_warn(&(d)->gadget->dev , fmt , ## args)
++#define INFO(d, fmt, args...) \
++ dev_info(&(d)->gadget->dev , fmt , ## args)
++
++#endif /* __LINUX_USB_COMPOSITE_H */
+diff --git a/include/linux/usb/ehci_def.h b/include/linux/usb/ehci_def.h
+new file mode 100644
+index 0000000..5b88e36
+--- /dev/null
++++ b/include/linux/usb/ehci_def.h
+@@ -0,0 +1,160 @@
++/*
++ * Copyright (c) 2001-2002 by David Brownell
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software Foundation,
++ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ */
++
++#ifndef __LINUX_USB_EHCI_DEF_H
++#define __LINUX_USB_EHCI_DEF_H
++
++/* EHCI register interface, corresponds to EHCI Revision 0.95 specification */
++
++/* Section 2.2 Host Controller Capability Registers */
++struct ehci_caps {
++ /* these fields are specified as 8 and 16 bit registers,
++ * but some hosts can't perform 8 or 16 bit PCI accesses.
++ */
++ u32 hc_capbase;
++#define HC_LENGTH(p) (((p)>>00)&0x00ff) /* bits 7:0 */
++#define HC_VERSION(p) (((p)>>16)&0xffff) /* bits 31:16 */
++ u32 hcs_params; /* HCSPARAMS - offset 0x4 */
++#define HCS_DEBUG_PORT(p) (((p)>>20)&0xf) /* bits 23:20, debug port? */
++#define HCS_INDICATOR(p) ((p)&(1 << 16)) /* true: has port indicators */
++#define HCS_N_CC(p) (((p)>>12)&0xf) /* bits 15:12, #companion HCs */
++#define HCS_N_PCC(p) (((p)>>8)&0xf) /* bits 11:8, ports per CC */
++#define HCS_PORTROUTED(p) ((p)&(1 << 7)) /* true: port routing */
++#define HCS_PPC(p) ((p)&(1 << 4)) /* true: port power control */
++#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */
++
++ u32 hcc_params; /* HCCPARAMS - offset 0x8 */
++#define HCC_EXT_CAPS(p) (((p)>>8)&0xff) /* for pci extended caps */
++#define HCC_ISOC_CACHE(p) ((p)&(1 << 7)) /* true: can cache isoc frame */
++#define HCC_ISOC_THRES(p) (((p)>>4)&0x7) /* bits 6:4, uframes cached */
++#define HCC_CANPARK(p) ((p)&(1 << 2)) /* true: can park on async qh */
++#define HCC_PGM_FRAMELISTLEN(p) ((p)&(1 << 1)) /* true: periodic_size changes*/
++#define HCC_64BIT_ADDR(p) ((p)&(1)) /* true: can use 64-bit addr */
++ u8 portroute [8]; /* nibbles for routing - offset 0xC */
++} __attribute__ ((packed));
++
++
++/* Section 2.3 Host Controller Operational Registers */
++struct ehci_regs {
++
++ /* USBCMD: offset 0x00 */
++ u32 command;
++/* 23:16 is r/w intr rate, in microframes; default "8" == 1/msec */
++#define CMD_PARK (1<<11) /* enable "park" on async qh */
++#define CMD_PARK_CNT(c) (((c)>>8)&3) /* how many transfers to park for */
++#define CMD_LRESET (1<<7) /* partial reset (no ports, etc) */
++#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */
++#define CMD_ASE (1<<5) /* async schedule enable */
++#define CMD_PSE (1<<4) /* periodic schedule enable */
++/* 3:2 is periodic frame list size */
++#define CMD_RESET (1<<1) /* reset HC not bus */
++#define CMD_RUN (1<<0) /* start/stop HC */
++
++ /* USBSTS: offset 0x04 */
++ u32 status;
++#define STS_ASS (1<<15) /* Async Schedule Status */
++#define STS_PSS (1<<14) /* Periodic Schedule Status */
++#define STS_RECL (1<<13) /* Reclamation */
++#define STS_HALT (1<<12) /* Not running (any reason) */
++/* some bits reserved */
++ /* these STS_* flags are also intr_enable bits (USBINTR) */
++#define STS_IAA (1<<5) /* Interrupted on async advance */
++#define STS_FATAL (1<<4) /* such as some PCI access errors */
++#define STS_FLR (1<<3) /* frame list rolled over */
++#define STS_PCD (1<<2) /* port change detect */
++#define STS_ERR (1<<1) /* "error" completion (overflow, ...) */
++#define STS_INT (1<<0) /* "normal" completion (short, ...) */
++
++ /* USBINTR: offset 0x08 */
++ u32 intr_enable;
++
++ /* FRINDEX: offset 0x0C */
++ u32 frame_index; /* current microframe number */
++ /* CTRLDSSEGMENT: offset 0x10 */
++ u32 segment; /* address bits 63:32 if needed */
++ /* PERIODICLISTBASE: offset 0x14 */
++ u32 frame_list; /* points to periodic list */
++ /* ASYNCLISTADDR: offset 0x18 */
++ u32 async_next; /* address of next async queue head */
++
++ u32 reserved [9];
++
++ /* CONFIGFLAG: offset 0x40 */
++ u32 configured_flag;
++#define FLAG_CF (1<<0) /* true: we'll support "high speed" */
++
++ /* PORTSC: offset 0x44 */
++ u32 port_status [0]; /* up to N_PORTS */
++/* 31:23 reserved */
++#define PORT_WKOC_E (1<<22) /* wake on overcurrent (enable) */
++#define PORT_WKDISC_E (1<<21) /* wake on disconnect (enable) */
++#define PORT_WKCONN_E (1<<20) /* wake on connect (enable) */
++/* 19:16 for port testing */
++#define PORT_LED_OFF (0<<14)
++#define PORT_LED_AMBER (1<<14)
++#define PORT_LED_GREEN (2<<14)
++#define PORT_LED_MASK (3<<14)
++#define PORT_OWNER (1<<13) /* true: companion hc owns this port */
++#define PORT_POWER (1<<12) /* true: has power (see PPC) */
++#define PORT_USB11(x) (((x)&(3<<10)) == (1<<10)) /* USB 1.1 device */
++/* 11:10 for detecting lowspeed devices (reset vs release ownership) */
++/* 9 reserved */
++#define PORT_RESET (1<<8) /* reset port */
++#define PORT_SUSPEND (1<<7) /* suspend port */
++#define PORT_RESUME (1<<6) /* resume it */
++#define PORT_OCC (1<<5) /* over current change */
++#define PORT_OC (1<<4) /* over current active */
++#define PORT_PEC (1<<3) /* port enable change */
++#define PORT_PE (1<<2) /* port enable */
++#define PORT_CSC (1<<1) /* connect status change */
++#define PORT_CONNECT (1<<0) /* device connected */
++#define PORT_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC)
++} __attribute__ ((packed));
++
++#define USBMODE 0x68 /* USB Device mode */
++#define USBMODE_SDIS (1<<3) /* Stream disable */
++#define USBMODE_BE (1<<2) /* BE/LE endianness select */
++#define USBMODE_CM_HC (3<<0) /* host controller mode */
++#define USBMODE_CM_IDLE (0<<0) /* idle state */
++
++/* Appendix C, Debug port ... intended for use with special "debug devices"
++ * that can help if there's no serial console. (nonstandard enumeration.)
++ */
++struct ehci_dbg_port {
++ u32 control;
++#define DBGP_OWNER (1<<30)
++#define DBGP_ENABLED (1<<28)
++#define DBGP_DONE (1<<16)
++#define DBGP_INUSE (1<<10)
++#define DBGP_ERRCODE(x) (((x)>>7)&0x07)
++# define DBGP_ERR_BAD 1
++# define DBGP_ERR_SIGNAL 2
++#define DBGP_ERROR (1<<6)
++#define DBGP_GO (1<<5)
++#define DBGP_OUT (1<<4)
++#define DBGP_LEN(x) (((x)>>0)&0x0f)
++ u32 pids;
++#define DBGP_PID_GET(x) (((x)>>16)&0xff)
++#define DBGP_PID_SET(data, tok) (((data)<<8)|(tok))
++ u32 data03;
++ u32 data47;
++ u32 address;
++#define DBGP_EPADDR(dev, ep) (((dev)<<8)|(ep))
++} __attribute__ ((packed));
++
++#endif /* __LINUX_USB_EHCI_DEF_H */
+diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
+index cf468fb..0460a74 100644
+--- a/include/linux/usb/gadget.h
++++ b/include/linux/usb/gadget.h
+@@ -33,7 +33,8 @@ struct usb_ep;
+ * @short_not_ok: When reading data, makes short packets be
+ * treated as errors (queue stops advancing till cleanup).
+ * @complete: Function called when request completes, so this request and
+- * its buffer may be re-used.
++ * its buffer may be re-used. The function will always be called with
++ * interrupts disabled, and it must not sleep.
+ * Reads terminate with a short packet, or when the buffer fills,
+ * whichever comes first. When writes terminate, some data bytes
+ * will usually still be in flight (often in a hardware fifo).
+@@ -271,7 +272,10 @@ static inline void usb_ep_free_request(struct usb_ep *ep,
+ * (Note that some USB device controllers disallow protocol stall responses
+ * in some cases.) When control responses are deferred (the response is
+ * written after the setup callback returns), then usb_ep_set_halt() may be
+- * used on ep0 to trigger protocol stalls.
++ * used on ep0 to trigger protocol stalls. Depending on the controller,
++ * it may not be possible to trigger a status-stage protocol stall when the
++ * data stage is over, that is, from within the response's completion
++ * routine.
+ *
+ * For periodic endpoints, like interrupt or isochronous ones, the usb host
+ * arranges to poll once per interval, and the gadget driver usually will
+@@ -858,6 +862,25 @@ int usb_descriptor_fillbuf(void *, unsigned,
+ int usb_gadget_config_buf(const struct usb_config_descriptor *config,
+ void *buf, unsigned buflen, const struct usb_descriptor_header **desc);
+
++/* copy a NULL-terminated vector of descriptors */
++struct usb_descriptor_header **usb_copy_descriptors(
++ struct usb_descriptor_header **);
++
++/* return copy of endpoint descriptor given original descriptor set */
++struct usb_endpoint_descriptor *usb_find_endpoint(
++ struct usb_descriptor_header **src,
++ struct usb_descriptor_header **copy,
++ struct usb_endpoint_descriptor *match);
++
++/**
++ * usb_free_descriptors - free descriptors returned by usb_copy_descriptors()
++ * @v: vector of descriptors
++ */
++static inline void usb_free_descriptors(struct usb_descriptor_header **v)
++{
++ kfree(v);
++}
++
+ /*-------------------------------------------------------------------------*/
+
+ /* utility wrapping a simple endpoint selection policy */
+diff --git a/include/linux/usb/irda.h b/include/linux/usb/irda.h
+new file mode 100644
+index 0000000..e345cea
+--- /dev/null
++++ b/include/linux/usb/irda.h
+@@ -0,0 +1,151 @@
++/*
++ * USB IrDA Bridge Device Definition
++ */
++
++#ifndef __LINUX_USB_IRDA_H
++#define __LINUX_USB_IRDA_H
++
++/* This device should use Application-specific class */
++
++#define USB_SUBCLASS_IRDA 0x02
++
++/*-------------------------------------------------------------------------*/
++
++/* Class-Specific requests (bRequest field) */
++
++#define USB_REQ_CS_IRDA_RECEIVING 1
++#define USB_REQ_CS_IRDA_CHECK_MEDIA_BUSY 3
++#define USB_REQ_CS_IRDA_RATE_SNIFF 4
++#define USB_REQ_CS_IRDA_UNICAST_LIST 5
++#define USB_REQ_CS_IRDA_GET_CLASS_DESC 6
++
++/*-------------------------------------------------------------------------*/
++
++/* Class-Specific descriptor */
++
++#define USB_DT_CS_IRDA 0x21
++
++/*-------------------------------------------------------------------------*/
++
++/* Data sizes */
++
++#define USB_IRDA_DS_2048 (1 << 5)
++#define USB_IRDA_DS_1024 (1 << 4)
++#define USB_IRDA_DS_512 (1 << 3)
++#define USB_IRDA_DS_256 (1 << 2)
++#define USB_IRDA_DS_128 (1 << 1)
++#define USB_IRDA_DS_64 (1 << 0)
++
++/* Window sizes */
++
++#define USB_IRDA_WS_7 (1 << 6)
++#define USB_IRDA_WS_6 (1 << 5)
++#define USB_IRDA_WS_5 (1 << 4)
++#define USB_IRDA_WS_4 (1 << 3)
++#define USB_IRDA_WS_3 (1 << 2)
++#define USB_IRDA_WS_2 (1 << 1)
++#define USB_IRDA_WS_1 (1 << 0)
++
++/* Min turnaround times in usecs */
++
++#define USB_IRDA_MTT_0 (1 << 7)
++#define USB_IRDA_MTT_10 (1 << 6)
++#define USB_IRDA_MTT_50 (1 << 5)
++#define USB_IRDA_MTT_100 (1 << 4)
++#define USB_IRDA_MTT_500 (1 << 3)
++#define USB_IRDA_MTT_1000 (1 << 2)
++#define USB_IRDA_MTT_5000 (1 << 1)
++#define USB_IRDA_MTT_10000 (1 << 0)
++
++/* Baud rates */
++
++#define USB_IRDA_BR_4000000 (1 << 8)
++#define USB_IRDA_BR_1152000 (1 << 7)
++#define USB_IRDA_BR_576000 (1 << 6)
++#define USB_IRDA_BR_115200 (1 << 5)
++#define USB_IRDA_BR_57600 (1 << 4)
++#define USB_IRDA_BR_38400 (1 << 3)
++#define USB_IRDA_BR_19200 (1 << 2)
++#define USB_IRDA_BR_9600 (1 << 1)
++#define USB_IRDA_BR_2400 (1 << 0)
++
++/* Additional BOFs */
++
++#define USB_IRDA_AB_0 (1 << 7)
++#define USB_IRDA_AB_1 (1 << 6)
++#define USB_IRDA_AB_2 (1 << 5)
++#define USB_IRDA_AB_3 (1 << 4)
++#define USB_IRDA_AB_6 (1 << 3)
++#define USB_IRDA_AB_12 (1 << 2)
++#define USB_IRDA_AB_24 (1 << 1)
++#define USB_IRDA_AB_48 (1 << 0)
++
++/* IRDA Rate Sniff */
++
++#define USB_IRDA_RATE_SNIFF 1
++
++/*-------------------------------------------------------------------------*/
++
++struct usb_irda_cs_descriptor {
++ __u8 bLength;
++ __u8 bDescriptorType;
++
++ __le16 bcdSpecRevision;
++ __u8 bmDataSize;
++ __u8 bmWindowSize;
++ __u8 bmMinTurnaroundTime;
++ __le16 wBaudRate;
++ __u8 bmAdditionalBOFs;
++ __u8 bIrdaRateSniff;
++ __u8 bMaxUnicastList;
++} __attribute__ ((packed));
++
++/*-------------------------------------------------------------------------*/
++
++/* Data Format */
++
++#define USB_IRDA_STATUS_MEDIA_BUSY (1 << 7)
++
++/* The following is a 4-bit value used for both
++ * inbound and outbound headers:
++ *
++ * 0 - speed ignored
++ * 1 - 2400 bps
++ * 2 - 9600 bps
++ * 3 - 19200 bps
++ * 4 - 38400 bps
++ * 5 - 57600 bps
++ * 6 - 115200 bps
++ * 7 - 576000 bps
++ * 8 - 1.152 Mbps
++ * 9 - 5 mbps
++ * 10..15 - Reserved
++ */
++#define USB_IRDA_STATUS_LINK_SPEED 0x0f
++
++/* The following is a 4-bit value used only for
++ * outbound header:
++ *
++ * 0 - No change (BOF ignored)
++ * 1 - 48 BOFs
++ * 2 - 24 BOFs
++ * 3 - 12 BOFs
++ * 4 - 6 BOFs
++ * 5 - 3 BOFs
++ * 6 - 2 BOFs
++ * 7 - 1 BOFs
++ * 8 - 0 BOFs
++ * 9..15 - Reserved
++ */
++#define USB_IRDA_EXTRA_BOFS 0xf0
++
++struct usb_irda_inbound_header {
++ __u8 bmStatus;
++};
++
++struct usb_irda_outbound_header {
++ __u8 bmChange;
++};
++
++#endif /* __LINUX_USB_IRDA_H */
++
+diff --git a/include/linux/usb/musb.h b/include/linux/usb/musb.h
+new file mode 100644
+index 0000000..630962c
+--- /dev/null
++++ b/include/linux/usb/musb.h
+@@ -0,0 +1,98 @@
++/*
++ * This is used to for host and peripheral modes of the driver for
++ * Inventra (Multidrop) Highspeed Dual-Role Controllers: (M)HDRC.
++ *
++ * Board initialization should put one of these into dev->platform_data,
++ * probably on some platform_device named "musb_hdrc". It encapsulates
++ * key configuration differences between boards.
++ */
++
++/* The USB role is defined by the connector used on the board, so long as
++ * standards are being followed. (Developer boards sometimes won't.)
++ */
++enum musb_mode {
++ MUSB_UNDEFINED = 0,
++ MUSB_HOST, /* A or Mini-A connector */
++ MUSB_PERIPHERAL, /* B or Mini-B connector */
++ MUSB_OTG /* Mini-AB connector */
++};
++
++struct clk;
++
++struct musb_hdrc_eps_bits {
++ const char name[16];
++ u8 bits;
++};
++
++struct musb_hdrc_config {
++ /* MUSB configuration-specific details */
++ unsigned multipoint:1; /* multipoint device */
++ unsigned dyn_fifo:1; /* supports dynamic fifo sizing */
++ unsigned soft_con:1; /* soft connect required */
++ unsigned utm_16:1; /* utm data witdh is 16 bits */
++ unsigned big_endian:1; /* true if CPU uses big-endian */
++ unsigned mult_bulk_tx:1; /* Tx ep required for multbulk pkts */
++ unsigned mult_bulk_rx:1; /* Rx ep required for multbulk pkts */
++ unsigned high_iso_tx:1; /* Tx ep required for HB iso */
++ unsigned high_iso_rx:1; /* Rx ep required for HD iso */
++ unsigned dma:1; /* supports DMA */
++ unsigned vendor_req:1; /* vendor registers required */
++
++ u8 num_eps; /* number of endpoints _with_ ep0 */
++ u8 dma_channels; /* number of dma channels */
++ u8 dyn_fifo_size; /* dynamic size in bytes */
++ u8 vendor_ctrl; /* vendor control reg width */
++ u8 vendor_stat; /* vendor status reg witdh */
++ u8 dma_req_chan; /* bitmask for required dma channels */
++ u8 ram_bits; /* ram address size */
++
++ struct musb_hdrc_eps_bits *eps_bits;
++};
++
++struct musb_hdrc_platform_data {
++ /* MUSB_HOST, MUSB_PERIPHERAL, or MUSB_OTG */
++ u8 mode;
++
++ /* for clk_get() */
++ const char *clock;
++
++ /* (HOST or OTG) switch VBUS on/off */
++ int (*set_vbus)(struct device *dev, int is_on);
++
++ /* (HOST or OTG) mA/2 power supplied on (default = 8mA) */
++ u8 power;
++
++ /* (PERIPHERAL) mA/2 max power consumed (default = 100mA) */
++ u8 min_power;
++
++ /* (HOST or OTG) msec/2 after VBUS on till power good */
++ u8 potpgt;
++
++ /* Power the device on or off */
++ int (*set_power)(int state);
++
++ /* Turn device clock on or off */
++ int (*set_clock)(struct clk *clock, int is_on);
++
++ /* MUSB configuration-specific details */
++ struct musb_hdrc_config *config;
++};
++
++
++/* TUSB 6010 support */
++
++#define TUSB6010_OSCCLK_60 16667 /* psec/clk @ 60.0 MHz */
++#define TUSB6010_REFCLK_24 41667 /* psec/clk @ 24.0 MHz XI */
++#define TUSB6010_REFCLK_19 52083 /* psec/clk @ 19.2 MHz CLKIN */
++
++#ifdef CONFIG_ARCH_OMAP2
++
++extern int __init tusb6010_setup_interface(
++ struct musb_hdrc_platform_data *data,
++ unsigned ps_refclk, unsigned waitpin,
++ unsigned async_cs, unsigned sync_cs,
++ unsigned irq, unsigned dmachan);
++
++extern int tusb6010_platform_retime(unsigned is_refclk);
++
++#endif /* OMAP2 */
+diff --git a/include/linux/usb/rndis_host.h b/include/linux/usb/rndis_host.h
+index 29d6458..0a6e6d4 100644
+--- a/include/linux/usb/rndis_host.h
++++ b/include/linux/usb/rndis_host.h
+@@ -260,7 +260,8 @@ struct rndis_keepalive_c { /* IN (optionally OUT) */
+
+
+ extern void rndis_status(struct usbnet *dev, struct urb *urb);
+-extern int rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf);
++extern int
++rndis_command(struct usbnet *dev, struct rndis_msg_hdr *buf, int buflen);
+ extern int
+ generic_rndis_bind(struct usbnet *dev, struct usb_interface *intf, int flags);
+ extern void rndis_unbind(struct usbnet *dev, struct usb_interface *intf);
+diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h
+index 8f891cb..655341d 100644
+--- a/include/linux/usb/serial.h
++++ b/include/linux/usb/serial.h
+@@ -17,7 +17,8 @@
+ #include <linux/mutex.h>
+
+ #define SERIAL_TTY_MAJOR 188 /* Nice legal number now */
+-#define SERIAL_TTY_MINORS 255 /* loads of devices :) */
++#define SERIAL_TTY_MINORS 254 /* loads of devices :) */
++#define SERIAL_TTY_NO_MINOR 255 /* No minor was assigned */
+
+ /* The maximum number of ports one device can grab at once */
+ #define MAX_NUM_PORTS 8
+@@ -62,7 +63,7 @@
+ */
+ struct usb_serial_port {
+ struct usb_serial *serial;
+- struct tty_struct *tty;
++ struct tty_port port;
+ spinlock_t lock;
+ struct mutex mutex;
+ unsigned char number;
+@@ -89,7 +90,6 @@ struct usb_serial_port {
+
+ wait_queue_head_t write_wait;
+ struct work_struct work;
+- int open_count;
+ char throttled;
+ char throttle_req;
+ char console;
+@@ -217,22 +217,27 @@ struct usb_serial_driver {
+ int (*resume)(struct usb_serial *serial);
+
+ /* serial function calls */
+- int (*open)(struct usb_serial_port *port, struct file *filp);
+- void (*close)(struct usb_serial_port *port, struct file *filp);
+- int (*write)(struct usb_serial_port *port, const unsigned char *buf,
+- int count);
+- int (*write_room)(struct usb_serial_port *port);
+- int (*ioctl)(struct usb_serial_port *port, struct file *file,
++ /* Called by console with tty = NULL and by tty */
++ int (*open)(struct tty_struct *tty,
++ struct usb_serial_port *port, struct file *filp);
++ void (*close)(struct tty_struct *tty,
++ struct usb_serial_port *port, struct file *filp);
++ int (*write)(struct tty_struct *tty, struct usb_serial_port *port,
++ const unsigned char *buf, int count);
++ /* Called only by the tty layer */
++ int (*write_room)(struct tty_struct *tty);
++ int (*ioctl)(struct tty_struct *tty, struct file *file,
+ unsigned int cmd, unsigned long arg);
+- void (*set_termios)(struct usb_serial_port *port, struct ktermios *old);
+- void (*break_ctl)(struct usb_serial_port *port, int break_state);
+- int (*chars_in_buffer)(struct usb_serial_port *port);
+- void (*throttle)(struct usb_serial_port *port);
+- void (*unthrottle)(struct usb_serial_port *port);
+- int (*tiocmget)(struct usb_serial_port *port, struct file *file);
+- int (*tiocmset)(struct usb_serial_port *port, struct file *file,
++ void (*set_termios)(struct tty_struct *tty,
++ struct usb_serial_port *port, struct ktermios *old);
++ void (*break_ctl)(struct tty_struct *tty, int break_state);
++ int (*chars_in_buffer)(struct tty_struct *tty);
++ void (*throttle)(struct tty_struct *tty);
++ void (*unthrottle)(struct tty_struct *tty);
++ int (*tiocmget)(struct tty_struct *tty, struct file *file);
++ int (*tiocmset)(struct tty_struct *tty, struct file *file,
+ unsigned int set, unsigned int clear);
+-
++ /* USB events */
+ void (*read_int_callback)(struct urb *urb);
+ void (*write_int_callback)(struct urb *urb);
+ void (*read_bulk_callback)(struct urb *urb);
+@@ -270,19 +275,19 @@ static inline void usb_serial_console_disconnect(struct usb_serial *serial) {}
+ /* Functions needed by other parts of the usbserial core */
+ extern struct usb_serial *usb_serial_get_by_index(unsigned int minor);
+ extern void usb_serial_put(struct usb_serial *serial);
+-extern int usb_serial_generic_open(struct usb_serial_port *port,
+- struct file *filp);
+-extern int usb_serial_generic_write(struct usb_serial_port *port,
+- const unsigned char *buf, int count);
+-extern void usb_serial_generic_close(struct usb_serial_port *port,
+- struct file *filp);
++extern int usb_serial_generic_open(struct tty_struct *tty,
++ struct usb_serial_port *port, struct file *filp);
++extern int usb_serial_generic_write(struct tty_struct *tty,
++ struct usb_serial_port *port, const unsigned char *buf, int count);
++extern void usb_serial_generic_close(struct tty_struct *tty,
++ struct usb_serial_port *port, struct file *filp);
+ extern int usb_serial_generic_resume(struct usb_serial *serial);
+-extern int usb_serial_generic_write_room(struct usb_serial_port *port);
+-extern int usb_serial_generic_chars_in_buffer(struct usb_serial_port *port);
++extern int usb_serial_generic_write_room(struct tty_struct *tty);
++extern int usb_serial_generic_chars_in_buffer(struct tty_struct *tty);
+ extern void usb_serial_generic_read_bulk_callback(struct urb *urb);
+ extern void usb_serial_generic_write_bulk_callback(struct urb *urb);
+-extern void usb_serial_generic_throttle(struct usb_serial_port *port);
+-extern void usb_serial_generic_unthrottle(struct usb_serial_port *port);
++extern void usb_serial_generic_throttle(struct tty_struct *tty);
++extern void usb_serial_generic_unthrottle(struct tty_struct *tty);
+ extern void usb_serial_generic_shutdown(struct usb_serial *serial);
+ extern int usb_serial_generic_register(int debug);
+ extern void usb_serial_generic_deregister(void);
diff --git a/recipes/linux/linux-rp-2.6.26/versatile-armv6.patch b/recipes/linux/linux-rp-2.6.26/versatile-armv6.patch
new file mode 100644
index 0000000000..6f2ca26a9a
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.26/versatile-armv6.patch
@@ -0,0 +1,17 @@
+---
+ arch/arm/mm/Kconfig | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+Index: linux-2.6.26-rc4/arch/arm/mm/Kconfig
+===================================================================
+--- linux-2.6.26-rc4.orig/arch/arm/mm/Kconfig 2008-05-26 19:08:11.000000000 +0100
++++ linux-2.6.26-rc4/arch/arm/mm/Kconfig 2008-06-01 18:40:07.000000000 +0100
+@@ -387,7 +387,7 @@
+ # ARMv6
+ config CPU_V6
+ bool "Support ARM V6 processor"
+- depends on ARCH_INTEGRATOR || MACH_REALVIEW_EB || ARCH_OMAP2 || ARCH_MX3 || ARCH_MSM7X00A || MACH_REALVIEW_PB11MP || MACH_REALVIEW_PB1176
++ depends on ARCH_INTEGRATOR || MACH_REALVIEW_EB || ARCH_OMAP2 || ARCH_MX3 || ARCH_MSM7X00A || MACH_REALVIEW_PB11MP || MACH_REALVIEW_PB1176 || ARCH_VERSATILE_PB
+ default y if ARCH_MX3
+ default y if ARCH_MSM7X00A
+ select CPU_32v6
diff --git a/recipes/linux/linux-rp-2.6.26/zaurus-i2c-init.patch b/recipes/linux/linux-rp-2.6.26/zaurus-i2c-init.patch
new file mode 100644
index 0000000000..e0db0746cd
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.26/zaurus-i2c-init.patch
@@ -0,0 +1,99 @@
+Typo in poodle chunk fixed: set_pxa_i2c_info->pxa_set_i2c_info.
+
+From: Mark Brown <broonie@opensource.wolfsonmicro.com>
+To: Richard Purdie <rpurdie@rpsys.net>
+Date: Wed, 16 Jul 2008 23:19:36 +0100
+Message-Id: <1216246776-4500-1-git-send-email-broonie@opensource.wolfsonmicro.com>
+List-Id: ARM Linux kernel discussions
+ <linux-arm-kernel.lists.arm.linux.org.uk>
+
+Both spitz and poodle have audio codecs on their primary I2C bus so need
+to call pxa_set_i2c_info() to set it up during init. Tested on spitz by
+Stanislav.
+
+Reported-by: Stanislav Brabec <utx@penguin.cz>
+Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
+---
+ arch/arm/mach-pxa/corgi.c | 2 ++
+ arch/arm/mach-pxa/poodle.c | 2 ++
+ arch/arm/mach-pxa/spitz.c | 2 ++
+ 3 files changed, 6 insertions(+)
+
+--- linux-2.6.26.orig/arch/arm/mach-pxa/corgi.c
++++ linux-2.6.26/arch/arm/mach-pxa/corgi.c
+@@ -38,10 +38,11 @@
+ #include <asm/arch/pxa-regs.h>
+ #include <asm/arch/pxa2xx-gpio.h>
+ #include <asm/arch/irda.h>
+ #include <asm/arch/mmc.h>
+ #include <asm/arch/udc.h>
++#include <asm/arch/i2c.h>
+ #include <asm/arch/corgi.h>
+ #include <asm/arch/sharpsl.h>
+
+ #include <asm/mach/sharpsl_param.h>
+ #include <asm/hardware/scoop.h>
+@@ -529,10 +530,11 @@ static void __init corgi_init(void)
+ pxa_gpio_mode(CORGI_GPIO_HSYNC | GPIO_IN);
+
+ pxa_set_udc_info(&udc_info);
+ pxa_set_mci_info(&corgi_mci_platform_data);
+ pxa_set_ficp_info(&corgi_ficp_platform_data);
++ pxa_set_i2c_info(NULL);
+
+ platform_scoop_config = &corgi_pcmcia_config;
+
+ platform_add_devices(devices, ARRAY_SIZE(devices));
+ }
+--- linux-2.6.26.orig/arch/arm/mach-pxa/poodle.c
++++ linux-2.6.26/arch/arm/mach-pxa/poodle.c
+@@ -33,10 +33,11 @@
+
+ #include <asm/arch/pxa-regs.h>
+ #include <asm/arch/pxa2xx-gpio.h>
+ #include <asm/arch/mmc.h>
+ #include <asm/arch/udc.h>
++#include <asm/arch/i2c.h>
+ #include <asm/arch/irda.h>
+ #include <asm/arch/poodle.h>
+ #include <asm/arch/pxafb.h>
+ #include <asm/arch/sharpsl.h>
+ #include <asm/arch/ssp.h>
+@@ -386,10 +387,11 @@ static void __init poodle_init(void)
+ pxa_gpio_mode(POODLE_GPIO_USB_PULLUP | GPIO_OUT);
+ pxa_gpio_mode(POODLE_GPIO_IR_ON | GPIO_OUT);
+ pxa_set_udc_info(&udc_info);
+ pxa_set_mci_info(&poodle_mci_platform_data);
+ pxa_set_ficp_info(&poodle_ficp_platform_data);
++ pxa_set_i2c_info(NULL);
+
+ platform_scoop_config = &poodle_pcmcia_config;
+
+ ret = platform_add_devices(devices, ARRAY_SIZE(devices));
+ if (ret) {
+--- linux-2.6.26.orig/arch/arm/mach-pxa/spitz.c
++++ linux-2.6.26/arch/arm/mach-pxa/spitz.c
+@@ -36,10 +36,11 @@
+ #include <asm/mach/irq.h>
+
+ #include <asm/arch/pxa-regs.h>
+ #include <asm/arch/pxa2xx-regs.h>
+ #include <asm/arch/pxa2xx-gpio.h>
++#include <asm/arch/i2c.h>
+ #include <asm/arch/irda.h>
+ #include <asm/arch/mmc.h>
+ #include <asm/arch/ohci.h>
+ #include <asm/arch/udc.h>
+ #include <asm/arch/pxafb.h>
+@@ -579,10 +580,11 @@ static void __init common_init(void)
+ pxa_set_mci_info(&spitz_mci_platform_data);
+ pxa_set_ohci_info(&spitz_ohci_platform_data);
+ pxa_set_ficp_info(&spitz_ficp_platform_data);
+ set_pxa_fb_parent(&spitzssp_device.dev);
+ set_pxa_fb_info(&spitz_pxafb_info);
++ pxa_set_i2c_info(NULL);
+ }
+
+ #if defined(CONFIG_MACH_SPITZ) || defined(CONFIG_MACH_BORZOI)
+ static void spitz_bl_set_intensity(int intensity)
+ {
diff --git a/recipes/linux/linux-rp-2.6.26/zylonite-boot.patch b/recipes/linux/linux-rp-2.6.26/zylonite-boot.patch
new file mode 100644
index 0000000000..f41928eca5
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.26/zylonite-boot.patch
@@ -0,0 +1,45 @@
+From 04c42f566c68b757fdadf54e0e0f9dfe9f3f9b06 Mon Sep 17 00:00:00 2001
+From: eric miao <eric.miao@marvell.com>
+Date: Tue, 19 Jun 2007 16:42:53 +0800
+Subject: [PATCH] [PATCH] make zylonite boot
+
+1. reuse head-xscale.S for XSC3
+
+2. add a workaround for machine ID assignment, which should be done
+ by boot loader
+---
+ arch/arm/boot/compressed/Makefile | 4 ++++
+ arch/arm/boot/compressed/head-xscale.S | 5 +++++
+ 2 files changed, 9 insertions(+)
+
+Index: linux-2.6-pxa3/arch/arm/boot/compressed/Makefile
+===================================================================
+--- linux-2.6-pxa3.orig/arch/arm/boot/compressed/Makefile 2007-09-24 11:25:57.000000000 +0200
++++ linux-2.6-pxa3/arch/arm/boot/compressed/Makefile 2007-09-24 12:26:53.000000000 +0200
+@@ -40,6 +40,10 @@
+ OBJS += head-xscale.o
+ endif
+
++ifeq ($(CONFIG_CPU_XSC3),y)
++OBJS += head-xscale.o
++endif
++
+ ifeq ($(CONFIG_PXA_SHARPSL),y)
+ OBJS += head-sharpsl.o
+ endif
+Index: linux-2.6-pxa3/arch/arm/boot/compressed/head-xscale.S
+===================================================================
+--- linux-2.6-pxa3.orig/arch/arm/boot/compressed/head-xscale.S 2007-09-24 11:42:27.000000000 +0200
++++ linux-2.6-pxa3/arch/arm/boot/compressed/head-xscale.S 2007-09-24 12:26:02.000000000 +0200
+@@ -33,6 +33,11 @@
+ bic r0, r0, #0x1000 @ clear Icache
+ mcr p15, 0, r0, c1, c0, 0
+
++#ifdef CONFIG_MACH_ZYLONITE
++ mov r7, #(MACH_TYPE_ZYLONITE & 0xff)
++ orr r7, r7, #(MACH_TYPE_ZYLONITE & 0xff00)
++#endif
++
+ #ifdef CONFIG_ARCH_COTULLA_IDP
+ mov r7, #MACH_TYPE_COTULLA_IDP
+ #endif
diff --git a/recipes/linux/linux-rp-2.6.26/zylonite_keypad-r0.patch b/recipes/linux/linux-rp-2.6.26/zylonite_keypad-r0.patch
new file mode 100644
index 0000000000..1889b3884e
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.26/zylonite_keypad-r0.patch
@@ -0,0 +1,1187 @@
+Eric Miao's pxa keypad patchset backport.
+---
+ arch/arm/mach-pxa/devices.h | 1
+ arch/arm/mach-pxa/generic.c | 31 +
+ arch/arm/mach-pxa/pxa27x.c | 2
+ arch/arm/mach-pxa/pxa300.c | 6
+ arch/arm/mach-pxa/pxa3xx.c | 1
+ arch/arm/mach-pxa/zylonite.c | 68 +++
+ drivers/input/keyboard/Kconfig | 8
+ drivers/input/keyboard/Makefile | 2
+ drivers/input/keyboard/pxa27x_keyboard.c | 273 -------------
+ drivers/input/keyboard/pxa27x_keypad.c | 575 +++++++++++++++++++++++++++++
+ include/asm-arm/arch-pxa/irqs.h | 2
+ include/asm-arm/arch-pxa/pxa27x_keyboard.h | 13
+ include/asm-arm/arch-pxa/pxa27x_keypad.h | 58 ++
+ 13 files changed, 745 insertions(+), 295 deletions(-)
+
+Index: linux-2.6.23-z-input/drivers/input/keyboard/Kconfig
+===================================================================
+--- linux-2.6.23-z-input.orig/drivers/input/keyboard/Kconfig 2008-02-18 01:43:28.000000000 +0100
++++ linux-2.6.23-z-input/drivers/input/keyboard/Kconfig 2008-02-18 01:43:28.000000000 +0100
+@@ -218,13 +218,13 @@
+ module will be called omap-keypad.
+
+ config KEYBOARD_PXA27x
+- tristate "PXA27x keyboard support"
+- depends on PXA27x
++ tristate "PXA27x/PXA3xx keypad support"
++ depends on PXA27x || PXA3xx
+ help
+- Enable support for PXA27x matrix keyboard controller
++ Enable support for PXA27x/PXA3xx keypad controller
+
+ To compile this driver as a module, choose M here: the
+- module will be called pxa27x_keyboard.
++ module will be called pxa27x_keypad.
+
+ config KEYBOARD_AAED2000
+ tristate "AAED-2000 keyboard"
+Index: linux-2.6.23-z-input/drivers/input/keyboard/Makefile
+===================================================================
+--- linux-2.6.23-z-input.orig/drivers/input/keyboard/Makefile 2008-02-18 01:43:28.000000000 +0100
++++ linux-2.6.23-z-input/drivers/input/keyboard/Makefile 2008-02-18 01:43:28.000000000 +0100
+@@ -18,7 +18,7 @@
+ obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o
+ obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o
+ obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o
+-obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keyboard.o
++obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o
+ obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o
+ obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o
+ obj-$(CONFIG_KEYBOARD_ASIC3) += asic3_keys.o
+Index: linux-2.6.23-z-input/drivers/input/keyboard/pxa27x_keypad.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23-z-input/drivers/input/keyboard/pxa27x_keypad.c 2008-02-19 01:40:04.000000000 +0100
+@@ -0,0 +1,575 @@
++/*
++ * linux/drivers/input/keyboard/pxa27x_keypad.c
++ *
++ * Driver for the pxa27x matrix keyboard controller.
++ *
++ * Created: Feb 22, 2007
++ * Author: Rodolfo Giometti <giometti@linux.it>
++ *
++ * Based on a previous implementations by Kevin O'Connor
++ * <kevin_at_koconnor.net> and Alex Osborne <bobofdoom@gmail.com> and
++ * on some suggestions by Nicolas Pitre <nico@cam.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/module.h>
++#include <linux/init.h>
++#include <linux/interrupt.h>
++#include <linux/input.h>
++#include <linux/device.h>
++#include <linux/platform_device.h>
++#include <linux/clk.h>
++#include <linux/err.h>
++
++#include <asm/mach-types.h>
++#include <asm/mach/arch.h>
++#include <asm/mach/map.h>
++
++#include <asm/arch/hardware.h>
++#include <asm/arch/mfp.h>
++#include <asm/arch/pxa27x_keypad.h>
++
++#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG))
++
++/*
++ * Keypad Controller registers
++ */
++#define KPC 0x0000 /* Keypad Control register */
++#define KPDK 0x0008 /* Keypad Direct Key register */
++#define KPREC 0x0010 /* Keypad Rotary Encoder register */
++#define KPMK 0x0018 /* Keypad Matrix Key register */
++#define KPAS 0x0020 /* Keypad Automatic Scan register */
++
++/* Keypad Automatic Scan Multiple Key Presser register 0-3 */
++#define KPASMKP0 0x0028
++#define KPASMKP1 0x0030
++#define KPASMKP2 0x0038
++#define KPASMKP3 0x0040
++#define KPKDI 0x0048
++
++/* bit definitions */
++#define KPC_MKRN(n) ((((n) - 1) & 0x7) << 26) /* matrix key row number */
++#define KPC_MKCN(n) ((((n) - 1) & 0x7) << 23) /* matrix key column number */
++#define KPC_DKN(n) ((((n) - 1) & 0x7) << 6) /* direct key number */
++
++#define KPC_AS (0x1 << 30) /* Automatic Scan bit */
++#define KPC_ASACT (0x1 << 29) /* Automatic Scan on Activity */
++#define KPC_MI (0x1 << 22) /* Matrix interrupt bit */
++#define KPC_IMKP (0x1 << 21) /* Ignore Multiple Key Press */
++
++#define KPC_MS(n) (0x1 << (13 + (n))) /* Matrix scan line 'n' */
++#define KPC_MS_ALL (0xff << 13)
++
++#define KPC_ME (0x1 << 12) /* Matrix Keypad Enable */
++#define KPC_MIE (0x1 << 11) /* Matrix Interrupt Enable */
++#define KPC_DK_DEB_SEL (0x1 << 9) /* Direct Keypad Debounce Select */
++#define KPC_DI (0x1 << 5) /* Direct key interrupt bit */
++#define KPC_RE_ZERO_DEB (0x1 << 4) /* Rotary Encoder Zero Debounce */
++#define KPC_REE1 (0x1 << 3) /* Rotary Encoder1 Enable */
++#define KPC_REE0 (0x1 << 2) /* Rotary Encoder0 Enable */
++#define KPC_DE (0x1 << 1) /* Direct Keypad Enable */
++#define KPC_DIE (0x1 << 0) /* Direct Keypad interrupt Enable */
++
++#define KPDK_DKP (0x1 << 31)
++#define KPDK_DK(n) ((n) & 0xff)
++
++#define KPREC_OF1 (0x1 << 31)
++#define kPREC_UF1 (0x1 << 30)
++#define KPREC_OF0 (0x1 << 15)
++#define KPREC_UF0 (0x1 << 14)
++
++#define KPREC_RECOUNT0(n) ((n) & 0xff)
++#define KPREC_RECOUNT1(n) (((n) >> 16) & 0xff)
++
++#define KPMK_MKP (0x1 << 31)
++#define KPAS_SO (0x1 << 31)
++#define KPASMKPx_SO (0x1 << 31)
++
++#define KPAS_MUKP(n) (((n) >> 26) & 0x1f)
++#define KPAS_RP(n) (((n) >> 4) & 0xf)
++#define KPAS_CP(n) ((n) & 0xf)
++
++#define KPASMKP_MKC_MASK (0xff)
++
++#define keypad_readl(off) __raw_readl(keypad->mmio_base + (off))
++#define keypad_writel(off, v) __raw_writel((v), keypad->mmio_base + (off))
++
++#define MAX_MATRIX_KEY_NUM (8 * 8)
++
++struct pxa27x_keypad {
++ struct pxa27x_keypad_platform_data *pdata;
++
++ struct clk *clk;
++ struct input_dev *input_dev;
++ void __iomem *mmio_base;
++
++ /* matrix key code map */
++ unsigned int matrix_keycodes[MAX_MATRIX_KEY_NUM];
++
++ /* state row bits of each column scan */
++ uint32_t matrix_key_state[MAX_MATRIX_KEY_COLS];
++ uint32_t direct_key_state;
++
++ unsigned int direct_key_mask;
++
++ int rotary_rel_code[2];
++ int rotary_up_key[2];
++ int rotary_down_key[2];
++};
++
++static void pxa27x_keypad_build_keycode(struct pxa27x_keypad *keypad)
++{
++ struct pxa27x_keypad_platform_data *pdata = keypad->pdata;
++ struct input_dev *input_dev = keypad->input_dev;
++ unsigned int *key;
++ int i;
++
++ key = &pdata->matrix_key_map[0];
++ for (i = 0; i < pdata->matrix_key_map_size; i++, key++) {
++ int row = ((*key) >> 28) & 0xf;
++ int col = ((*key) >> 24) & 0xf;
++ int code = (*key) & 0xffffff;
++
++ keypad->matrix_keycodes[(row << 3) + col] = code;
++ set_bit(code, input_dev->keybit);
++ }
++
++ keypad->rotary_up_key[0] = pdata->rotary0_up_key;
++ keypad->rotary_up_key[1] = pdata->rotary1_up_key;
++ keypad->rotary_down_key[0] = pdata->rotary0_down_key;
++ keypad->rotary_down_key[1] = pdata->rotary1_down_key;
++ keypad->rotary_rel_code[0] = pdata->rotary0_rel_code;
++ keypad->rotary_rel_code[1] = pdata->rotary1_rel_code;
++
++ if (pdata->rotary0_up_key && pdata->rotary0_down_key) {
++ set_bit(pdata->rotary0_up_key, input_dev->keybit);
++ set_bit(pdata->rotary0_down_key, input_dev->keybit);
++ } else
++ set_bit(pdata->rotary0_rel_code, input_dev->relbit);
++
++ if (pdata->rotary1_up_key && pdata->rotary1_down_key) {
++ set_bit(pdata->rotary1_up_key, input_dev->keybit);
++ set_bit(pdata->rotary1_down_key, input_dev->keybit);
++ } else
++ set_bit(pdata->rotary1_rel_code, input_dev->relbit);
++}
++
++static inline unsigned int lookup_matrix_keycode(
++ struct pxa27x_keypad *keypad, int row, int col)
++{
++ return keypad->matrix_keycodes[(row << 3) + col];
++}
++
++static void pxa27x_keypad_scan_matrix(struct pxa27x_keypad *keypad)
++{
++ struct pxa27x_keypad_platform_data *pdata = keypad->pdata;
++ int row, col, num_keys_pressed = 0;
++ uint32_t new_state[MAX_MATRIX_KEY_COLS];
++ uint32_t kpas = keypad_readl(KPAS);
++
++ num_keys_pressed = KPAS_MUKP(kpas);
++
++ memset(new_state, 0, sizeof(new_state));
++
++ if (num_keys_pressed == 0)
++ goto scan;
++
++ if (num_keys_pressed == 1) {
++ col = KPAS_CP(kpas);
++ row = KPAS_RP(kpas);
++
++ /* if invalid row/col, treat as no key pressed */
++ if (col >= pdata->matrix_key_cols ||
++ row >= pdata->matrix_key_rows)
++ goto scan;
++
++ new_state[col] = (1 << row);
++ goto scan;
++ }
++
++ if (num_keys_pressed > 1) {
++ uint32_t kpasmkp0 = keypad_readl(KPASMKP0);
++ uint32_t kpasmkp1 = keypad_readl(KPASMKP1);
++ uint32_t kpasmkp2 = keypad_readl(KPASMKP2);
++ uint32_t kpasmkp3 = keypad_readl(KPASMKP3);
++
++ new_state[0] = kpasmkp0 & KPASMKP_MKC_MASK;
++ new_state[1] = (kpasmkp0 >> 16) & KPASMKP_MKC_MASK;
++ new_state[2] = kpasmkp1 & KPASMKP_MKC_MASK;
++ new_state[3] = (kpasmkp1 >> 16) & KPASMKP_MKC_MASK;
++ new_state[4] = kpasmkp2 & KPASMKP_MKC_MASK;
++ new_state[5] = (kpasmkp2 >> 16) & KPASMKP_MKC_MASK;
++ new_state[6] = kpasmkp3 & KPASMKP_MKC_MASK;
++ new_state[7] = (kpasmkp3 >> 16) & KPASMKP_MKC_MASK;
++ }
++scan:
++ for (col = 0; col < pdata->matrix_key_cols; col++) {
++ uint32_t bits_changed;
++
++ bits_changed = keypad->matrix_key_state[col] ^ new_state[col];
++ if (bits_changed == 0)
++ continue;
++
++ for (row = 0; row < pdata->matrix_key_rows; row++) {
++ if ((bits_changed & (1 << row)) == 0)
++ continue;
++
++ input_report_key(keypad->input_dev,
++ lookup_matrix_keycode(keypad, row, col),
++ new_state[col] & (1 << row));
++ }
++ }
++ input_sync(keypad->input_dev);
++ memcpy(keypad->matrix_key_state, new_state, sizeof(new_state));
++}
++
++#define DEFAULT_KPREC (0x007f007f)
++
++static inline int rotary_delta(uint32_t kprec)
++{
++ if (kprec & KPREC_OF0)
++ return (kprec & 0xff) + 0x7f;
++ else if (kprec & KPREC_UF0)
++ return (kprec & 0xff) - 0x7f - 0xff;
++ else
++ return (kprec & 0xff) - 0x7f;
++}
++
++static void report_rotary_event(struct pxa27x_keypad *keypad, int r, int delta)
++{
++ struct input_dev *dev = keypad->input_dev;
++
++ if (delta == 0)
++ return;
++
++ if (keypad->rotary_up_key[r] && keypad->rotary_down_key[r]) {
++ int keycode = (delta > 0) ? keypad->rotary_up_key[r] :
++ keypad->rotary_down_key[r];
++
++ /* simulate a press-n-release */
++ input_report_key(dev, keycode, 1);
++ input_sync(dev);
++ input_report_key(dev, keycode, 0);
++ input_sync(dev);
++ } else {
++ input_report_rel(dev, keypad->rotary_rel_code[r], delta);
++ input_sync(dev);
++ }
++}
++
++static void pxa27x_keypad_scan_rotary(struct pxa27x_keypad *keypad)
++{
++ struct pxa27x_keypad_platform_data *pdata = keypad->pdata;
++ uint32_t kprec;
++
++ /* read and reset to default count value */
++ kprec = keypad_readl(KPREC);
++ keypad_writel(KPREC, DEFAULT_KPREC);
++
++ if (pdata->enable_rotary0)
++ report_rotary_event(keypad, 0, rotary_delta(kprec));
++
++ if (pdata->enable_rotary1)
++ report_rotary_event(keypad, 1, rotary_delta(kprec >> 16));
++}
++
++static void pxa27x_keypad_scan_direct(struct pxa27x_keypad *keypad)
++{
++ struct pxa27x_keypad_platform_data *pdata = keypad->pdata;
++ unsigned int new_state;
++ uint32_t kpdk, bits_changed;
++ int i;
++
++ kpdk = keypad_readl(KPDK);
++
++ if (pdata->enable_rotary0 || pdata->enable_rotary1)
++ pxa27x_keypad_scan_rotary(keypad);
++
++ if (pdata->direct_key_map == NULL)
++ return;
++
++ new_state = KPDK_DK(kpdk) & keypad->direct_key_mask;
++ bits_changed = keypad->direct_key_state ^ new_state;
++
++ if (bits_changed == 0)
++ return;
++
++ for (i = 0; i < pdata->direct_key_num; i++) {
++ if (bits_changed & (1 << i))
++ input_report_key(keypad->input_dev,
++ pdata->direct_key_map[i],
++ (new_state & (1 << i)));
++ }
++ input_sync(keypad->input_dev);
++ keypad->direct_key_state = new_state;
++}
++
++static irqreturn_t pxa27x_keypad_irq_handler(int irq, void *dev_id)
++{
++ struct pxa27x_keypad *keypad = dev_id;
++ unsigned long kpc = keypad_readl(KPC);
++
++ if (kpc & KPC_DI)
++ pxa27x_keypad_scan_direct(keypad);
++
++ if (kpc & KPC_MI)
++ pxa27x_keypad_scan_matrix(keypad);
++
++ return IRQ_HANDLED;
++}
++
++static void pxa27x_keypad_config(struct pxa27x_keypad *keypad)
++{
++ struct pxa27x_keypad_platform_data *pdata = keypad->pdata;
++ unsigned int mask = 0, direct_key_num = 0;
++ unsigned long kpc = 0;
++
++ /* enable matrix keys with automatic scan */
++ if (pdata->matrix_key_rows && pdata->matrix_key_cols) {
++ kpc |= KPC_ASACT | KPC_MIE | KPC_ME | KPC_MS_ALL;
++ kpc |= KPC_MKRN(pdata->matrix_key_rows) |
++ KPC_MKCN(pdata->matrix_key_cols);
++ }
++
++ /* enable rotary key, debounce interval same as direct keys */
++ if (pdata->enable_rotary0) {
++ mask |= 0x03;
++ direct_key_num = 2;
++ kpc |= KPC_REE0;
++ }
++
++ if (pdata->enable_rotary1) {
++ mask |= 0x0c;
++ direct_key_num = 4;
++ kpc |= KPC_REE1;
++ }
++
++ if (pdata->direct_key_num > direct_key_num)
++ direct_key_num = pdata->direct_key_num;
++
++ keypad->direct_key_mask = ((2 << direct_key_num) - 1) & ~mask;
++
++ /* enable direct key */
++ if (direct_key_num)
++ kpc |= KPC_DE | KPC_DIE | KPC_DKN(direct_key_num);
++
++ keypad_writel(KPC, kpc | KPC_RE_ZERO_DEB);
++ keypad_writel(KPREC, DEFAULT_KPREC);
++ keypad_writel(KPKDI, pdata->debounce_interval);
++}
++
++static int pxa27x_keypad_open(struct input_dev *dev)
++{
++ struct pxa27x_keypad *keypad = input_get_drvdata(dev);
++
++ /* Enable unit clock */
++ clk_enable(keypad->clk);
++ pxa27x_keypad_config(keypad);
++
++ return 0;
++}
++
++static void pxa27x_keypad_close(struct input_dev *dev)
++{
++ struct pxa27x_keypad *keypad = input_get_drvdata(dev);
++
++ /* Disable clock unit */
++ clk_disable(keypad->clk);
++}
++
++#ifdef CONFIG_PM
++static int pxa27x_keypad_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ struct pxa27x_keypad *keypad = platform_get_drvdata(pdev);
++
++ clk_disable(keypad->clk);
++ return 0;
++}
++
++static int pxa27x_keypad_resume(struct platform_device *pdev)
++{
++ struct pxa27x_keypad *keypad = platform_get_drvdata(pdev);
++ struct input_dev *input_dev = keypad->input_dev;
++
++ mutex_lock(&input_dev->mutex);
++
++ if (input_dev->users) {
++ /* Enable unit clock */
++ clk_enable(keypad->clk);
++ pxa27x_keypad_config(keypad);
++ }
++
++ mutex_unlock(&input_dev->mutex);
++
++ return 0;
++}
++#else
++#define pxa27x_keypad_suspend NULL
++#define pxa27x_keypad_resume NULL
++#endif
++
++#define res_size(res) ((res)->end - (res)->start + 1)
++
++static int __devinit pxa27x_keypad_probe(struct platform_device *pdev)
++{
++ struct pxa27x_keypad *keypad;
++ struct input_dev *input_dev;
++ struct resource *res;
++ int irq, error;
++
++ keypad = kzalloc(sizeof(struct pxa27x_keypad), GFP_KERNEL);
++ if (keypad == NULL) {
++ dev_err(&pdev->dev, "failed to allocate driver data\n");
++ return -ENOMEM;
++ }
++
++ keypad->pdata = pdev->dev.platform_data;
++ if (keypad->pdata == NULL) {
++ dev_err(&pdev->dev, "no platform data defined\n");
++ error = -EINVAL;
++ goto failed_free;
++ }
++
++ irq = platform_get_irq(pdev, 0);
++ if (irq < 0) {
++ dev_err(&pdev->dev, "failed to get keypad irq\n");
++ error = -ENXIO;
++ goto failed_free;
++ }
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ if (res == NULL) {
++ dev_err(&pdev->dev, "failed to get I/O memory\n");
++ error = -ENXIO;
++ goto failed_free;
++ }
++
++ res = request_mem_region(res->start, res_size(res), pdev->name);
++ if (res == NULL) {
++ dev_err(&pdev->dev, "failed to request I/O memory\n");
++ error = -EBUSY;
++ goto failed_free;
++ }
++
++ keypad->mmio_base = ioremap(res->start, res_size(res));
++ if (keypad->mmio_base == NULL) {
++ dev_err(&pdev->dev, "failed to remap I/O memory\n");
++ error = -ENXIO;
++ goto failed_free_mem;
++ }
++
++ keypad->clk = clk_get(&pdev->dev, "KBDCLK");
++ if (IS_ERR(keypad->clk)) {
++ dev_err(&pdev->dev, "failed to get keypad clock\n");
++ error = PTR_ERR(keypad->clk);
++ goto failed_free_io;
++ }
++
++ /* Create and register the input driver. */
++ input_dev = input_allocate_device();
++ if (!input_dev) {
++ dev_err(&pdev->dev, "failed to allocate input device\n");
++ error = -ENOMEM;
++ goto failed_put_clk;
++ }
++
++ input_dev->name = pdev->name;
++ input_dev->id.bustype = BUS_HOST;
++ input_dev->open = pxa27x_keypad_open;
++ input_dev->close = pxa27x_keypad_close;
++ input_dev->dev.parent = &pdev->dev;
++
++ keypad->input_dev = input_dev;
++ input_set_drvdata(input_dev, keypad);
++
++ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP) |
++ BIT_MASK(EV_REL);
++
++ pxa27x_keypad_build_keycode(keypad);
++ platform_set_drvdata(pdev, keypad);
++
++ error = request_irq(irq, pxa27x_keypad_irq_handler, IRQF_DISABLED,
++ pdev->name, keypad);
++ if (error) {
++ dev_err(&pdev->dev, "failed to request IRQ\n");
++ goto failed_free_dev;
++ }
++
++ /* Register the input device */
++ error = input_register_device(input_dev);
++ if (error) {
++ dev_err(&pdev->dev, "failed to register input device\n");
++ goto failed_free_irq;
++ }
++
++ return 0;
++
++failed_free_irq:
++ free_irq(irq, pdev);
++ platform_set_drvdata(pdev, NULL);
++failed_free_dev:
++ input_free_device(input_dev);
++failed_put_clk:
++ clk_put(keypad->clk);
++failed_free_io:
++ iounmap(keypad->mmio_base);
++failed_free_mem:
++ release_mem_region(res->start, res_size(res));
++failed_free:
++ kfree(keypad);
++ return error;
++}
++
++static int __devexit pxa27x_keypad_remove(struct platform_device *pdev)
++{
++ struct pxa27x_keypad *keypad = platform_get_drvdata(pdev);
++ struct resource *res;
++
++ free_irq(platform_get_irq(pdev, 0), pdev);
++
++ clk_disable(keypad->clk);
++ clk_put(keypad->clk);
++
++ input_unregister_device(keypad->input_dev);
++ input_free_device(keypad->input_dev);
++
++ iounmap(keypad->mmio_base);
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ release_mem_region(res->start, res_size(res));
++
++ platform_set_drvdata(pdev, NULL);
++ kfree(keypad);
++ return 0;
++}
++
++static struct platform_driver pxa27x_keypad_driver = {
++ .probe = pxa27x_keypad_probe,
++ .remove = __devexit_p(pxa27x_keypad_remove),
++ .suspend = pxa27x_keypad_suspend,
++ .resume = pxa27x_keypad_resume,
++ .driver = {
++ .name = "pxa27x-keypad",
++ },
++};
++
++static int __init pxa27x_keypad_init(void)
++{
++ return platform_driver_register(&pxa27x_keypad_driver);
++}
++
++static void __exit pxa27x_keypad_exit(void)
++{
++ platform_driver_unregister(&pxa27x_keypad_driver);
++}
++
++module_init(pxa27x_keypad_init);
++module_exit(pxa27x_keypad_exit);
++
++MODULE_DESCRIPTION("PXA27x Keypad Controller Driver");
++MODULE_LICENSE("GPL");
+Index: linux-2.6.23-z-input/include/asm-arm/arch-pxa/pxa27x_keyboard.h
+===================================================================
+--- linux-2.6.23-z-input.orig/include/asm-arm/arch-pxa/pxa27x_keyboard.h 2007-10-09 22:31:38.000000000 +0200
++++ /dev/null 1970-01-01 00:00:00.000000000 +0000
+@@ -1,13 +0,0 @@
+-#define PXAKBD_MAXROW 8
+-#define PXAKBD_MAXCOL 8
+-
+-struct pxa27x_keyboard_platform_data {
+- int nr_rows, nr_cols;
+- int keycodes[PXAKBD_MAXROW][PXAKBD_MAXCOL];
+- int gpio_modes[PXAKBD_MAXROW + PXAKBD_MAXCOL];
+-
+-#ifdef CONFIG_PM
+- u32 reg_kpc;
+- u32 reg_kprec;
+-#endif
+-};
+Index: linux-2.6.23-z-input/include/asm-arm/arch-pxa/pxa27x_keypad.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23-z-input/include/asm-arm/arch-pxa/pxa27x_keypad.h 2008-02-18 01:43:28.000000000 +0100
+@@ -0,0 +1,58 @@
++#ifndef __ASM_ARCH_PXA27x_KEYPAD_H
++#define __ASM_ARCH_PXA27x_KEYPAD_H
++
++#include <linux/input.h>
++
++#define MAX_MATRIX_KEY_ROWS (8)
++#define MAX_MATRIX_KEY_COLS (8)
++
++/* pxa3xx keypad platform specific parameters
++ *
++ * NOTE:
++ * 1. direct_key_num indicates the number of keys in the direct keypad
++ * _plus_ the number of rotary-encoder sensor inputs, this can be
++ * left as 0 if only rotary encoders are enabled, the driver will
++ * automatically calculate this
++ *
++ * 2. direct_key_map is the key code map for the direct keys, if rotary
++ * encoder(s) are enabled, direct key 0/1(2/3) will be ignored
++ *
++ * 3. rotary can be either interpreted as a relative input event (e.g.
++ * REL_WHEEL/REL_HWHEEL) or specific keys (e.g. UP/DOWN/LEFT/RIGHT)
++ *
++ * 4. matrix key and direct key will use the same debounce_interval by
++ * default, which should be sufficient in most cases
++ */
++struct pxa27x_keypad_platform_data {
++
++ /* code map for the matrix keys */
++ unsigned int matrix_key_rows;
++ unsigned int matrix_key_cols;
++ unsigned int *matrix_key_map;
++ int matrix_key_map_size;
++
++ /* direct keys */
++ int direct_key_num;
++ unsigned int direct_key_map[8];
++
++ /* rotary encoders 0 */
++ int enable_rotary0;
++ int rotary0_rel_code;
++ int rotary0_up_key;
++ int rotary0_down_key;
++
++ /* rotary encoders 1 */
++ int enable_rotary1;
++ int rotary1_rel_code;
++ int rotary1_up_key;
++ int rotary1_down_key;
++
++ /* key debounce interval */
++ unsigned int debounce_interval;
++};
++
++#define KEY(row, col, val) (((row) << 28) | ((col) << 24) | (val))
++
++extern void pxa_set_keypad_info(struct pxa27x_keypad_platform_data *info);
++
++#endif /* __ASM_ARCH_PXA27x_KEYPAD_H */
+Index: linux-2.6.23-z-input/drivers/input/keyboard/pxa27x_keyboard.c
+===================================================================
+--- linux-2.6.23-z-input.orig/drivers/input/keyboard/pxa27x_keyboard.c 2008-02-18 01:43:28.000000000 +0100
++++ /dev/null 1970-01-01 00:00:00.000000000 +0000
+@@ -1,273 +0,0 @@
+-/*
+- * linux/drivers/input/keyboard/pxa27x_keyboard.c
+- *
+- * Driver for the pxa27x matrix keyboard controller.
+- *
+- * Created: Feb 22, 2007
+- * Author: Rodolfo Giometti <giometti@linux.it>
+- *
+- * Based on a previous implementations by Kevin O'Connor
+- * <kevin_at_koconnor.net> and Alex Osborne <bobofdoom@gmail.com> and
+- * on some suggestions by Nicolas Pitre <nico@cam.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/module.h>
+-#include <linux/init.h>
+-#include <linux/interrupt.h>
+-#include <linux/input.h>
+-#include <linux/device.h>
+-#include <linux/platform_device.h>
+-#include <linux/clk.h>
+-#include <linux/err.h>
+-
+-#include <asm/mach-types.h>
+-#include <asm/mach/arch.h>
+-#include <asm/mach/map.h>
+-
+-#include <asm/arch/hardware.h>
+-#include <asm/arch/pxa-regs.h>
+-#include <asm/arch/irqs.h>
+-#include <asm/arch/pxa27x_keyboard.h>
+-
+-#define DRIVER_NAME "pxa27x-keyboard"
+-
+-#define KPASMKP(col) (col/2 == 0 ? KPASMKP0 : \
+- col/2 == 1 ? KPASMKP1 : \
+- col/2 == 2 ? KPASMKP2 : KPASMKP3)
+-#define KPASMKPx_MKC(row, col) (1 << (row + 16 * (col % 2)))
+-
+-static struct clk *pxakbd_clk;
+-
+-static irqreturn_t pxakbd_irq_handler(int irq, void *dev_id)
+-{
+- struct platform_device *pdev = dev_id;
+- struct pxa27x_keyboard_platform_data *pdata = pdev->dev.platform_data;
+- struct input_dev *input_dev = platform_get_drvdata(pdev);
+- unsigned long kpc = KPC;
+- int p, row, col, rel;
+-
+- if (kpc & KPC_DI) {
+- unsigned long kpdk = KPDK;
+-
+- if (!(kpdk & KPDK_DKP)) {
+- /* better luck next time */
+- } else if (kpc & KPC_REE0) {
+- unsigned long kprec = KPREC;
+- KPREC = 0x7f;
+-
+- if (kprec & KPREC_OF0)
+- rel = (kprec & 0xff) + 0x7f;
+- else if (kprec & KPREC_UF0)
+- rel = (kprec & 0xff) - 0x7f - 0xff;
+- else
+- rel = (kprec & 0xff) - 0x7f;
+-
+- if (rel) {
+- input_report_rel(input_dev, REL_WHEEL, rel);
+- input_sync(input_dev);
+- }
+- }
+- }
+-
+- if (kpc & KPC_MI) {
+- /* report the status of every button */
+- for (row = 0; row < pdata->nr_rows; row++) {
+- for (col = 0; col < pdata->nr_cols; col++) {
+- p = KPASMKP(col) & KPASMKPx_MKC(row, col) ?
+- 1 : 0;
+- pr_debug("keycode %x - pressed %x\n",
+- pdata->keycodes[row][col], p);
+- input_report_key(input_dev,
+- pdata->keycodes[row][col], p);
+- }
+- }
+- input_sync(input_dev);
+- }
+-
+- return IRQ_HANDLED;
+-}
+-
+-static int pxakbd_open(struct input_dev *dev)
+-{
+- /* Set keypad control register */
+- KPC |= (KPC_ASACT |
+- KPC_MS_ALL |
+- (2 << 6) | KPC_REE0 | KPC_DK_DEB_SEL |
+- KPC_ME | KPC_MIE | KPC_DE | KPC_DIE);
+-
+- KPC &= ~KPC_AS; /* disable automatic scan */
+- KPC &= ~KPC_IMKP; /* do not ignore multiple keypresses */
+-
+- /* Set rotary count to mid-point value */
+- KPREC = 0x7F;
+-
+- /* Enable unit clock */
+- clk_enable(pxakbd_clk);
+-
+- return 0;
+-}
+-
+-static void pxakbd_close(struct input_dev *dev)
+-{
+- /* Disable clock unit */
+- clk_disable(pxakbd_clk);
+-}
+-
+-#ifdef CONFIG_PM
+-static int pxakbd_suspend(struct platform_device *pdev, pm_message_t state)
+-{
+- struct pxa27x_keyboard_platform_data *pdata = pdev->dev.platform_data;
+-
+- /* Save controller status */
+- pdata->reg_kpc = KPC;
+- pdata->reg_kprec = KPREC;
+-
+- return 0;
+-}
+-
+-static int pxakbd_resume(struct platform_device *pdev)
+-{
+- struct pxa27x_keyboard_platform_data *pdata = pdev->dev.platform_data;
+- struct input_dev *input_dev = platform_get_drvdata(pdev);
+-
+- mutex_lock(&input_dev->mutex);
+-
+- if (input_dev->users) {
+- /* Restore controller status */
+- KPC = pdata->reg_kpc;
+- KPREC = pdata->reg_kprec;
+-
+- /* Enable unit clock */
+- clk_disable(pxakbd_clk);
+- clk_enable(pxakbd_clk);
+- }
+-
+- mutex_unlock(&input_dev->mutex);
+-
+- return 0;
+-}
+-#else
+-#define pxakbd_suspend NULL
+-#define pxakbd_resume NULL
+-#endif
+-
+-static int __devinit pxakbd_probe(struct platform_device *pdev)
+-{
+- struct pxa27x_keyboard_platform_data *pdata = pdev->dev.platform_data;
+- struct input_dev *input_dev;
+- int i, row, col, error;
+-
+- pxakbd_clk = clk_get(&pdev->dev, "KBDCLK");
+- if (IS_ERR(pxakbd_clk)) {
+- error = PTR_ERR(pxakbd_clk);
+- goto err_clk;
+- }
+-
+- /* Create and register the input driver. */
+- input_dev = input_allocate_device();
+- if (!input_dev) {
+- printk(KERN_ERR "Cannot request keypad device\n");
+- error = -ENOMEM;
+- goto err_alloc;
+- }
+-
+- input_dev->name = DRIVER_NAME;
+- input_dev->id.bustype = BUS_HOST;
+- input_dev->open = pxakbd_open;
+- input_dev->close = pxakbd_close;
+- input_dev->dev.parent = &pdev->dev;
+-
+- input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_REL);
+- input_dev->relbit[LONG(REL_WHEEL)] = BIT(REL_WHEEL);
+- for (row = 0; row < pdata->nr_rows; row++) {
+- for (col = 0; col < pdata->nr_cols; col++) {
+- int code = pdata->keycodes[row][col];
+- if (code > 0)
+- set_bit(code, input_dev->keybit);
+- }
+- }
+-
+- error = request_irq(IRQ_KEYPAD, pxakbd_irq_handler, IRQF_DISABLED,
+- DRIVER_NAME, pdev);
+- if (error) {
+- printk(KERN_ERR "Cannot request keypad IRQ\n");
+- goto err_free_dev;
+- }
+-
+- platform_set_drvdata(pdev, input_dev);
+-
+- /* Register the input device */
+- error = input_register_device(input_dev);
+- if (error)
+- goto err_free_irq;
+-
+- /* Setup GPIOs. */
+- for (i = 0; i < pdata->nr_rows + pdata->nr_cols; i++)
+- pxa_gpio_mode(pdata->gpio_modes[i]);
+-
+- /*
+- * Store rows/cols info into keyboard registers.
+- */
+-
+- KPC |= (pdata->nr_rows - 1) << 26;
+- KPC |= (pdata->nr_cols - 1) << 23;
+-
+- for (col = 0; col < pdata->nr_cols; col++)
+- KPC |= KPC_MS0 << col;
+-
+- return 0;
+-
+- err_free_irq:
+- platform_set_drvdata(pdev, NULL);
+- free_irq(IRQ_KEYPAD, pdev);
+- err_free_dev:
+- input_free_device(input_dev);
+- err_alloc:
+- clk_put(pxakbd_clk);
+- err_clk:
+- return error;
+-}
+-
+-static int __devexit pxakbd_remove(struct platform_device *pdev)
+-{
+- struct input_dev *input_dev = platform_get_drvdata(pdev);
+-
+- input_unregister_device(input_dev);
+- free_irq(IRQ_KEYPAD, pdev);
+- clk_put(pxakbd_clk);
+- platform_set_drvdata(pdev, NULL);
+-
+- return 0;
+-}
+-
+-static struct platform_driver pxakbd_driver = {
+- .probe = pxakbd_probe,
+- .remove = __devexit_p(pxakbd_remove),
+- .suspend = pxakbd_suspend,
+- .resume = pxakbd_resume,
+- .driver = {
+- .name = DRIVER_NAME,
+- },
+-};
+-
+-static int __init pxakbd_init(void)
+-{
+- return platform_driver_register(&pxakbd_driver);
+-}
+-
+-static void __exit pxakbd_exit(void)
+-{
+- platform_driver_unregister(&pxakbd_driver);
+-}
+-
+-module_init(pxakbd_init);
+-module_exit(pxakbd_exit);
+-
+-MODULE_DESCRIPTION("PXA27x Matrix Keyboard Driver");
+-MODULE_LICENSE("GPL");
+Index: linux-2.6.23-z-input/arch/arm/mach-pxa/zylonite.c
+===================================================================
+--- linux-2.6.23-z-input.orig/arch/arm/mach-pxa/zylonite.c 2008-02-18 01:43:28.000000000 +0100
++++ linux-2.6.23-z-input/arch/arm/mach-pxa/zylonite.c 2008-02-19 01:31:33.000000000 +0100
+@@ -25,6 +25,7 @@
+ #include <asm/arch/gpio.h>
+ #include <asm/arch/pxafb.h>
+ #include <asm/arch/zylonite.h>
++#include <asm/arch/pxa27x_keypad.h>
+
+ #include "generic.h"
+
+@@ -173,6 +174,72 @@
+ static inline void zylonite_init_lcd(void) {}
+ #endif
+
++#if defined(CONFIG_KEYBOARD_PXA27x) || defined(CONFIG_KEYBOARD_PXA27x_MODULES)
++static unsigned int zylonite_matrix_key_map[] = {
++ /* KEY(row, col, key_code) */
++ KEY(0, 0, KEY_A), KEY(0, 1, KEY_B), KEY(0, 2, KEY_C), KEY(0, 5, KEY_D),
++ KEY(1, 0, KEY_E), KEY(1, 1, KEY_F), KEY(1, 2, KEY_G), KEY(1, 5, KEY_H),
++ KEY(2, 0, KEY_I), KEY(2, 1, KEY_J), KEY(2, 2, KEY_K), KEY(2, 5, KEY_L),
++ KEY(3, 0, KEY_M), KEY(3, 1, KEY_N), KEY(3, 2, KEY_O), KEY(3, 5, KEY_P),
++ KEY(5, 0, KEY_Q), KEY(5, 1, KEY_R), KEY(5, 2, KEY_S), KEY(5, 5, KEY_T),
++ KEY(6, 0, KEY_U), KEY(6, 1, KEY_V), KEY(6, 2, KEY_W), KEY(6, 5, KEY_X),
++ KEY(7, 1, KEY_Y), KEY(7, 2, KEY_Z),
++
++ KEY(4, 4, KEY_0), KEY(1, 3, KEY_1), KEY(4, 1, KEY_2), KEY(1, 4, KEY_3),
++ KEY(2, 3, KEY_4), KEY(4, 2, KEY_5), KEY(2, 4, KEY_6), KEY(3, 3, KEY_7),
++ KEY(4, 3, KEY_8), KEY(3, 4, KEY_9),
++
++ KEY(4, 5, KEY_SPACE),
++ KEY(5, 3, KEY_KPASTERISK), /* * */
++ KEY(5, 4, KEY_KPDOT), /* #" */
++
++ KEY(0, 7, KEY_UP),
++ KEY(1, 7, KEY_DOWN),
++ KEY(2, 7, KEY_LEFT),
++ KEY(3, 7, KEY_RIGHT),
++ KEY(2, 6, KEY_HOME),
++ KEY(3, 6, KEY_END),
++ KEY(6, 4, KEY_DELETE),
++ KEY(6, 6, KEY_BACK),
++ KEY(6, 3, KEY_CAPSLOCK), /* KEY_LEFTSHIFT), */
++
++ KEY(4, 6, KEY_ENTER), /* scroll push */
++ KEY(5, 7, KEY_ENTER), /* keypad action */
++
++ KEY(0, 4, KEY_EMAIL),
++ KEY(5, 6, KEY_SEND),
++ KEY(4, 0, KEY_CALENDAR),
++ KEY(7, 6, KEY_RECORD),
++ KEY(6, 7, KEY_VOLUMEUP),
++ KEY(7, 7, KEY_VOLUMEDOWN),
++
++ KEY(0, 6, KEY_F22), /* soft1 */
++ KEY(1, 6, KEY_F23), /* soft2 */
++ KEY(0, 3, KEY_AUX), /* contact */
++};
++
++static struct pxa27x_keypad_platform_data zylonite_keypad_info = {
++ .matrix_key_rows = 8,
++ .matrix_key_cols = 8,
++ .matrix_key_map = zylonite_matrix_key_map,
++ .matrix_key_map_size = ARRAY_SIZE(zylonite_matrix_key_map),
++
++ .enable_rotary0 = 1,
++ .rotary0_up_key = KEY_UP,
++ .rotary0_down_key = KEY_DOWN,
++
++ .debounce_interval = 30,
++};
++
++static void __init zylonite_init_keypad(void)
++{
++ pxa_set_keypad_info(&zylonite_keypad_info);
++}
++#else
++static inline void zylonite_init_keypad(void) {}
++#endif
++
++
+ static void __init zylonite_init(void)
+ {
+ /* board-processor specific initialization */
+@@ -190,6 +257,7 @@
+ platform_device_register(&touch_device);
+
+ zylonite_init_lcd();
++ zylonite_init_keypad();
+ }
+
+ MACHINE_START(ZYLONITE, "PXA3xx Platform Development Kit (aka Zylonite)")
+Index: linux-2.6.23-z-input/arch/arm/mach-pxa/devices.h
+===================================================================
+--- linux-2.6.23-z-input.orig/arch/arm/mach-pxa/devices.h 2008-02-18 01:43:28.000000000 +0100
++++ linux-2.6.23-z-input/arch/arm/mach-pxa/devices.h 2008-02-18 01:43:28.000000000 +0100
+@@ -12,3 +12,4 @@
+
+ extern struct platform_device pxa27x_device_i2c_power;
+ extern struct platform_device pxa27x_device_ohci;
++extern struct platform_device pxa27x_device_keypad;
+Index: linux-2.6.23-z-input/arch/arm/mach-pxa/generic.c
+===================================================================
+--- linux-2.6.23-z-input.orig/arch/arm/mach-pxa/generic.c 2008-02-18 01:43:28.000000000 +0100
++++ linux-2.6.23-z-input/arch/arm/mach-pxa/generic.c 2008-02-18 01:43:28.000000000 +0100
+@@ -450,3 +450,34 @@
+ .name = "sa1100-rtc",
+ .id = -1,
+ };
++
++static struct resource pxa27x_resource_keypad[] = {
++ [0] = {
++ .start = 0x41500000,
++ .end = 0x4150004c,
++ .flags = IORESOURCE_MEM,
++ },
++ [1] = {
++ .start = IRQ_KEYPAD,
++ .end = IRQ_KEYPAD,
++ .flags = IORESOURCE_IRQ,
++ },
++};
++
++struct platform_device pxa27x_device_keypad = {
++ .name = "pxa27x-keypad",
++ .id = -1,
++ .resource = pxa27x_resource_keypad,
++ .num_resources = ARRAY_SIZE(pxa27x_resource_keypad),
++};
++
++void __init pxa_set_keypad_info(struct pxa27x_keypad_platform_data *info)
++{
++ int ret;
++
++ pxa27x_device_keypad.dev.platform_data = info;
++
++ ret = platform_device_register(&pxa27x_device_keypad);
++ if (ret)
++ dev_err(&pxa27x_device_keypad.dev, "unable to register device: %d\n", ret);
++}
+Index: linux-2.6.23-z-input/arch/arm/mach-pxa/pxa27x.c
+===================================================================
+--- linux-2.6.23-z-input.orig/arch/arm/mach-pxa/pxa27x.c 2008-02-18 01:43:28.000000000 +0100
++++ linux-2.6.23-z-input/arch/arm/mach-pxa/pxa27x.c 2008-02-18 01:43:28.000000000 +0100
+@@ -148,7 +148,7 @@
+
+ INIT_CKEN("USBCLK", USB, 48000000, 0, &pxa27x_device_ohci.dev),
+ INIT_CKEN("I2CCLK", PWRI2C, 13000000, 0, &pxa27x_device_i2c_power.dev),
+- INIT_CKEN("KBDCLK", KEYPAD, 32768, 0, NULL),
++ INIT_CKEN("KBDCLK", KEYPAD, 32768, 0, &pxa27x_device_keypad.dev),
+
+ /*
+ INIT_CKEN("PWMCLK", PWM0, 13000000, 0, NULL),
+Index: linux-2.6.23-z-input/arch/arm/mach-pxa/pxa3xx.c
+===================================================================
+--- linux-2.6.23-z-input.orig/arch/arm/mach-pxa/pxa3xx.c 2008-02-18 01:43:28.000000000 +0100
++++ linux-2.6.23-z-input/arch/arm/mach-pxa/pxa3xx.c 2008-02-19 01:32:40.000000000 +0100
+@@ -159,6 +159,7 @@
+ static struct clk pxa3xx_clks[] = {
+ INIT_CK("LCDCLK", LCD, &clk_pxa3xx_hsio_ops, &pxa_device_fb.dev),
+ INIT_CK("CAMCLK", CAMERA, &clk_pxa3xx_hsio_ops, NULL),
++ INIT_CK("KBDCLK", KEYPAD, &clk_pxa3xx_hsio_ops, &pxa27x_device_keypad.dev),
+
+ INIT_CKEN("UARTCLK", FFUART, 14857000, 1, &pxa_device_ffuart.dev),
+ INIT_CKEN("UARTCLK", BTUART, 14857000, 1, &pxa_device_btuart.dev),
+Index: linux-2.6.23-z-input/include/asm-arm/arch-pxa/irqs.h
+===================================================================
+--- linux-2.6.23-z-input.orig/include/asm-arm/arch-pxa/irqs.h 2008-02-18 01:43:28.000000000 +0100
++++ linux-2.6.23-z-input/include/asm-arm/arch-pxa/irqs.h 2008-02-18 01:43:28.000000000 +0100
+@@ -13,7 +13,7 @@
+
+ #define PXA_IRQ(x) (x)
+
+-#ifdef CONFIG_PXA27x
++#if defined(CONFIG_PXA27x) || defined(CONFIG_PXA3xx)
+ #define IRQ_SSP3 PXA_IRQ(0) /* SSP3 service request */
+ #define IRQ_MSL PXA_IRQ(1) /* MSL Interface interrupt */
+ #define IRQ_USBH2 PXA_IRQ(2) /* USB Host interrupt 1 (OHCI) */
+Index: linux-2.6.23-z-input/arch/arm/mach-pxa/pxa300.c
+===================================================================
+--- linux-2.6.23-z-input.orig/arch/arm/mach-pxa/pxa300.c 2008-02-19 01:33:58.000000000 +0100
++++ linux-2.6.23-z-input/arch/arm/mach-pxa/pxa300.c 2008-02-19 01:34:35.000000000 +0100
+@@ -23,8 +23,10 @@
+
+ MFP_ADDR_X(GPIO0, GPIO2, 0x00b4),
+ MFP_ADDR_X(GPIO3, GPIO26, 0x027c),
+- MFP_ADDR_X(GPIO27, GPIO127, 0x0400),
+- MFP_ADDR_X(GPIO0_2, GPIO6_2, 0x02ec),
++ MFP_ADDR_X(GPIO27, GPIO98, 0x0400),
++ MFP_ADDR_X(GPIO99, GPIO127, 0x0600),
++ MFP_ADDR_X(GPIO0_2, GPIO1_2, 0x0674),
++ MFP_ADDR_X(GPIO2_2, GPIO6_2, 0x02dc),
+
+ MFP_ADDR(nBE0, 0x0204),
+ MFP_ADDR(nBE1, 0x0208),
diff --git a/recipes/linux/linux-rp-2.6.26/zylonite_mtd-r0.patch b/recipes/linux/linux-rp-2.6.26/zylonite_mtd-r0.patch
new file mode 100644
index 0000000000..cb5a9c5f72
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.26/zylonite_mtd-r0.patch
@@ -0,0 +1,4093 @@
+Gross hacks to make the Zylonite boot from flash in VGA.
+
+Flash driver forward ported to 2.6.14
+
+Index: linux-2.6.23/drivers/mtd/nand/Kconfig
+===================================================================
+--- linux-2.6.23.orig/drivers/mtd/nand/Kconfig 2007-10-09 21:31:38.000000000 +0100
++++ linux-2.6.23/drivers/mtd/nand/Kconfig 2008-02-13 00:59:45.000000000 +0000
+@@ -223,6 +223,10 @@
+ tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)"
+ depends on ARCH_PXA
+
++config MTD_NAND_ZYLONITE
++ tristate "Support for NAND Flash on Zylonite"
++ depends on ARCH_PXA
++
+ config MTD_NAND_BASLER_EXCITE
+ tristate "Support for NAND Flash on Basler eXcite"
+ depends on BASLER_EXCITE
+Index: linux-2.6.23/drivers/mtd/nand/Makefile
+===================================================================
+--- linux-2.6.23.orig/drivers/mtd/nand/Makefile 2007-10-09 21:31:38.000000000 +0100
++++ linux-2.6.23/drivers/mtd/nand/Makefile 2008-02-13 00:59:45.000000000 +0000
+@@ -19,6 +19,7 @@
+ obj-$(CONFIG_MTD_NAND_H1900) += h1910.o
+ obj-$(CONFIG_MTD_NAND_RTC_FROM4) += rtc_from4.o
+ obj-$(CONFIG_MTD_NAND_SHARPSL) += sharpsl.o
++obj-$(CONFIG_MTD_NAND_ZYLONITE) += mhn_nand.o
+ obj-$(CONFIG_MTD_NAND_TS7250) += ts7250.o
+ obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o
+ obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o
+Index: linux-2.6.23/drivers/mtd/nand/mhn_nand.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23/drivers/mtd/nand/mhn_nand.c 2008-02-13 00:59:45.000000000 +0000
+@@ -0,0 +1,3869 @@
++/*
++ * drivers/mtd/nand/mhn_nand.c
++ *
++ * Copyright (C) 2005 Intel Coporation (chao.xie@intel.com)
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ *
++ * Overview:
++ * This is a device driver for the NAND flash device on zylonite board
++ * which utilizes the Samsung K9K1216Q0C parts. This is a 64Mibit NAND
++ * flash device.
++
++ *(C) Copyright 2006 Marvell International Ltd.
++ * All Rights Reserved
++ */
++
++#include <linux/slab.h>
++#include <linux/module.h>
++#include <linux/mtd/mtd.h>
++#include <linux/mtd/nand.h>
++#include <linux/mtd/partitions.h>
++#include <linux/interrupt.h>
++#include <linux/device.h>
++#include <linux/platform_device.h>
++#include <linux/delay.h>
++#include <linux/dma-mapping.h>
++#include <asm/hardware.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/delay.h>
++#include <asm/dma.h>
++#include <asm/arch/mfp.h>
++//#include <asm/arch/cpu-freq-voltage-mhn.h>
++
++//#define NDCR 0xf0000000
++//#define NDCR (*((volatile u32 *)0xf0000000))
++//#define NDCR __REG_2(0x43100000) /* Data Flash Control register */
++#define NDCR_SPARE_EN (0x1<<31)
++#define NDCR_ECC_EN (0x1<<30)
++#define NDCR_DMA_EN (0x1<<29)
++#define NDCR_ND_RUN (0x1<<28)
++#define NDCR_DWIDTH_C (0x1<<27)
++#define NDCR_DWIDTH_M (0x1<<26)
++#define NDCR_PAGE_SZ (0x1<<24)
++#define NDCR_NCSX (0x1<<23)
++#define NDCR_ND_MODE (0x3<<21)
++#define NDCR_NAND_MODE 0x0
++#define NDCR_CLR_PG_CNT (0x1<<20)
++#define NDCR_CLR_ECC ( 0x1<<19)
++#define NDCR_RD_ID_CNT_MASK (0x7<<16)
++#define NDCR_RD_ID_CNT(x) (((x) << 16) & NDCR_RD_ID_CNT_MASK)
++#define NDCR_RA_START (0x1<<15)
++#define NDCR_PG_PER_BLK (0x1<<14)
++#define NDCR_ND_ARB_EN (0x1<<12)
++
++//#define NDSR (*((volatile u32 *)0xf0000014))
++//#define NDSR __REG_2(0x43100014) /* Data Controller Status Register */
++#define NDSR_RDY (0x1<<11)
++#define NDSR_CS0_PAGED (0x1<<10)
++#define NDSR_CS1_PAGED (0x1<<9)
++#define NDSR_CS0_CMDD (0x1<<8)
++#define NDSR_CS1_CMDD (0x1<<7)
++#define NDSR_CS0_BBD (0x1<<6)
++#define NDSR_CS1_BBD (0x1<<5)
++#define NDSR_DBERR (0x1<<4)
++#define NDSR_SBERR (0x1<<3)
++#define NDSR_WRDREQ (0x1<<2)
++#define NDSR_RDDREQ (0x1<<1)
++#define NDSR_WRCMDREQ (0x1)
++
++#define OSCR __REG(0x40A00010) /* OS Timer Counter Register */
++//#define NDCB0 __REG_2(0x43100048) /* Data Controller Command Buffer0 */
++//#define NDCB1 __REG_2(0x4310004C) /* Data Controller Command Buffer1 */
++//#define NDCB2 __REG_2(0x43100050) /* Data Controller Command Buffer2 */
++#define NDCB0_AUTO_RS (0x1<<25)
++#define NDCB0_CSEL (0x1<<24)
++#define NDCB0_CMD_TYPE_MASK (0x7<<21)
++#define NDCB0_CMD_TYPE(x) (((x) << 21) & NDCB0_CMD_TYPE_MASK)
++#define NDCB0_NC (0x1<<20)
++#define NDCB0_DBC (0x1<<19)
++#define NDCB0_ADDR_CYC_MASK (0x7<<16)
++#define NDCB0_ADDR_CYC(x) (((x) << 16) & NDCB0_ADDR_CYC_MASK)
++#define NDCB0_CMD2_MASK (0xff<<8)
++#define NDCB0_CMD1_MASK (0xff)
++#define NDCB0_ADDR_CYC_SHIFT (16)
++#define DCMD0 __REG(0x4000020c) /* DMA Command Address Register Channel 0 */
++#define DCMD1 __REG(0x4000021c) /* DMA Command Address Register Channel 1 */
++#define DCMD2 __REG(0x4000022c) /* DMA Command Address Register Channel 2 */
++#define DCMD3 __REG(0x4000023c) /* DMA Command Address Register Channel 3 */
++#define DCMD4 __REG(0x4000024c) /* DMA Command Address Register Channel 4 */
++#define DCMD5 __REG(0x4000025c) /* DMA Command Address Register Channel 5 */
++#define DCMD6 __REG(0x4000026c) /* DMA Command Address Register Channel 6 */
++#define DCMD7 __REG(0x4000027c) /* DMA Command Address Register Channel 7 */
++#define DCMD8 __REG(0x4000028c) /* DMA Command Address Register Channel 8 */
++#define DCMD9 __REG(0x4000029c) /* DMA Command Address Register Channel 9 */
++#define DCMD10 __REG(0x400002ac) /* DMA Command Address Register Channel 10 */
++#define DCMD11 __REG(0x400002bc) /* DMA Command Address Register Channel 11 */
++#define DCMD12 __REG(0x400002cc) /* DMA Command Address Register Channel 12 */
++#define DCMD13 __REG(0x400002dc) /* DMA Command Address Register Channel 13 */
++#define DCMD14 __REG(0x400002ec) /* DMA Command Address Register Channel 14 */
++#define DCMD15 __REG(0x400002fc) /* DMA Command Address Register Channel 15 */
++#define DCMD(x) __REG2(0x4000020c, (x) << 4)
++#define DCMD_INCSRCADDR (1 << 31) /* Source Address Increment Setting. */
++#define DCMD_INCTRGADDR (1 << 30) /* Target Address Increment Setting. */
++#define DCMD_FLOWSRC (1 << 29) /* Flow Control by the source. */
++#define DCMD_FLOWTRG (1 << 28) /* Flow Control by the target. */
++#define DCMD_STARTIRQEN (1 << 22) /* Start Interrupt Enable */
++#define DCMD_ENDIRQEN (1 << 21) /* End Interrupt Enable */
++#define DCMD_ENDIAN (1 << 18) /* Device Endian-ness. */
++#define DCMD_BURST8 (1 << 16) /* 8 byte burst */
++#define DCMD_BURST16 (2 << 16) /* 16 byte burst */
++#define DCMD_BURST32 (3 << 16) /* 32 byte burst */
++#define DCMD_WIDTH1 (1 << 14) /* 1 byte width */
++#define DCMD_WIDTH2 (2 << 14) /* 2 byte width (HalfWord) */
++#define DCMD_WIDTH4 (3 << 14) /* 4 byte width (Word) */
++#define DCMD_LENGTH 0x01fff /* length mask (max = 8K - 1) */
++#define DCMD_RXPCDR (DCMD_INCTRGADDR|DCMD_FLOWSRC|DCMD_BURST32|DCMD_WIDTH4)
++#define DCMD_RXMCDR (DCMD_INCTRGADDR|DCMD_FLOWSRC|DCMD_BURST32|DCMD_WIDTH4)
++#define DCMD_TXPCDR (DCMD_INCSRCADDR|DCMD_FLOWTRG|DCMD_BURST32|DCMD_WIDTH4)
++#define DRCMR(n) __REG2(0x40000100, (n)<<2)
++#define DRCMR97 __REG(0x40001184) /* Request to Channel Map Register for NAND interface data transmit & receive Request */
++#define DRCMR98 __REG(0x40001188) /* Reserved */
++#define DRCMR99 __REG(0x4000118C) /* Request to Channel Map Register for NAND interface command transmit Request */
++#define DRCMRRXSADR DRCMR2
++#define DRCMRTXSADR DRCMR3
++#define DRCMRRXBTRBR DRCMR4
++#define DRCMRTXBTTHR DRCMR5
++#define DRCMRRXFFRBR DRCMR6
++#define DRCMRTXFFTHR DRCMR7
++#define DRCMRRXMCDR DRCMR8
++#define DRCMRRXMODR DRCMR9
++#define DRCMRTXMODR DRCMR10
++#define DRCMRRXPCDR DRCMR11
++#define DRCMRTXPCDR DRCMR12
++#define DRCMRRXSSDR DRCMR13
++#define DRCMRTXSSDR DRCMR14
++#define DRCMRRXICDR DRCMR17
++#define DRCMRTXICDR DRCMR18
++#define DRCMRRXSTRBR DRCMR19
++#define DRCMRTXSTTHR DRCMR20
++#define DRCMRRXMMC DRCMR21
++#define DRCMRTXMMC DRCMR22
++#define DRCMRRXMMC2 DRCMR93
++#define DRCMRTXMMC2 DRCMR94
++#define DRCMRRXMMC3 DRCMR100
++#define DRCMRTXMMC3 DRCMR101
++#define DRCMRUDC(x) DRCMR((x) + 24)
++#define DRCMR_MAPVLD (1 << 7) /* Map Valid (read / write) */
++#define DRCMR_CHLNUM 0x1f /* mask for Channel Number (read / write) */
++#define DCSR0 __REG(0x40000000) /* DMA Control / Status Register for Channel 0 */
++#define DCSR1 __REG(0x40000004) /* DMA Control / Status Register for Channel 1 */
++#define DCSR2 __REG(0x40000008) /* DMA Control / Status Register for Channel 2 */
++#define DCSR3 __REG(0x4000000c) /* DMA Control / Status Register for Channel 3 */
++#define DCSR4 __REG(0x40000010) /* DMA Control / Status Register for Channel 4 */
++#define DCSR5 __REG(0x40000014) /* DMA Control / Status Register for Channel 5 */
++#define DCSR6 __REG(0x40000018) /* DMA Control / Status Register for Channel 6 */
++#define DCSR7 __REG(0x4000001c) /* DMA Control / Status Register for Channel 7 */
++#define DCSR8 __REG(0x40000020) /* DMA Control / Status Register for Channel 8 */
++#define DCSR9 __REG(0x40000024) /* DMA Control / Status Register for Channel 9 */
++#define DCSR10 __REG(0x40000028) /* DMA Control / Status Register for Channel 10 */
++#define DCSR11 __REG(0x4000002c) /* DMA Control / Status Register for Channel 11 */
++#define DCSR12 __REG(0x40000030) /* DMA Control / Status Register for Channel 12 */
++#define DCSR13 __REG(0x40000034) /* DMA Control / Status Register for Channel 13 */
++#define DCSR14 __REG(0x40000038) /* DMA Control / Status Register for Channel 14 */
++#define DCSR15 __REG(0x4000003c) /* DMA Control / Status Register for Channel 15 */
++#define DCSR16 __REG(0x40000040) /* DMA Control / Status Register for Channel 16 */
++#define DCSR17 __REG(0x40000044) /* DMA Control / Status Register for Channel 17 */
++#define DCSR18 __REG(0x40000048) /* DMA Control / Status Register for Channel 18 */
++#define DCSR19 __REG(0x4000004c) /* DMA Control / Status Register for Channel 19 */
++#define DCSR20 __REG(0x40000050) /* DMA Control / Status Register for Channel 20 */
++#define DCSR21 __REG(0x40000054) /* DMA Control / Status Register for Channel 21 */
++#define DCSR22 __REG(0x40000058) /* DMA Control / Status Register for Channel 22 */
++#define DCSR23 __REG(0x4000005c) /* DMA Control / Status Register for Channel 23 */
++#define DCSR24 __REG(0x40000060) /* DMA Control / Status Register for Channel 24 */
++#define DCSR25 __REG(0x40000064) /* DMA Control / Status Register for Channel 25 */
++#define DCSR26 __REG(0x40000068) /* DMA Control / Status Register for Channel 26 */
++#define DCSR27 __REG(0x4000006c) /* DMA Control / Status Register for Channel 27 */
++#define DCSR28 __REG(0x40000070) /* DMA Control / Status Register for Channel 28 */
++#define DCSR29 __REG(0x40000074) /* DMA Control / Status Register for Channel 29 */
++#define DCSR30 __REG(0x40000078) /* DMA Control / Status Register for Channel 30 */
++#define DCSR31 __REG(0x4000007c) /* DMA Control / Status Register for Channel 31 */
++#define DCSR(x) __REG2(0x40000000, (x) << 2)
++#define DCSR_RUN (1 << 31) /* Run Bit (read / write) */
++#define DCSR_NODESC (1 << 30) /* No-Descriptor Fetch (read / write) */
++#define DCSR_STOPIRQEN (1 << 29) /* Stop Interrupt Enable (read / write) */
++#define DCSR_EORIRQEN (1 << 28) /* End of Receive Interrupt Enable (R/W) */
++#define DCSR_EORJMPEN (1 << 27) /* Jump to next descriptor on EOR */
++#define DCSR_EORSTOPEN (1 << 26) /* STOP on an EOR */
++#define DCSR_SETCMPST (1 << 25) /* Set Descriptor Compare Status */
++#define DCSR_CLRCMPST (1 << 24) /* Clear Descriptor Compare Status */
++#define DCSR_CMPST (1 << 10) /* The Descriptor Compare Status */
++#define DCSR_EORINTR (1 << 9) /* The end of Receive */
++#define DCSR_REQPEND (1 << 8) /* Request Pending (read-only) */
++#define DCSR_RASINTR (1 << 4) /* Request After Channel Stopped */
++#define DCSR_STOPSTATE (1 << 3) /* Stop State (read-only) */
++#define DCSR_ENDINTR (1 << 2) /* End Interrupt (read / write) */
++#define DCSR_STARTINTR (1 << 1) /* Start Interrupt (read / write) */
++#define DCSR_BUSERR (1 << 0) /* Bus Error Interrupt (read / write) */
++#define DDADR(x) __REG2(0x40000200, (x) << 4)
++//#define __REG_2(x) (*((volatile u32 *)io_p2v_2(x)))
++#define IRQ_NAND PXA_IRQ(45)
++#define CKEN_NAND 4 ///< NAND Flash Controller Clock Enable
++
++/* #define CONFIG_MTD_NAND_MONAHANS_DEBUG */
++#ifdef CONFIG_MTD_NAND_MONAHANS_DEBUG
++#define D1(x) do { \
++ printk(KERN_DEBUG "%s: ", __FUNCTION__); \
++ x; \
++ }while(0)
++
++#define DPRINTK(fmt,args...) printk(KERN_DEBUG fmt, ##args )
++#define PRINT_BUF(buf, num) print_buf(buf, num)
++#else
++#define D1(x)
++#define DPRINTK(fmt,args...)
++#define PRINT_BUF(buf, num)
++#endif
++
++/* DFC timing 0 register */
++#define DFC_TIMING_tRP 0
++#define DFC_TIMING_tRH 3
++#define DFC_TIMING_tWP 8
++#define DFC_TIMING_tWH 11
++#define DFC_TIMING_tCS 16
++#define DFC_TIMING_tCH 19
++
++/* DFC timing 1 register */
++#define DFC_TIMING_tAR 0
++#define DFC_TIMING_tWHR 4
++#define DFC_TIMING_tR 16
++
++/* max value for each timing setting in DFC */
++#define DFC_TIMING_MAX_tCH 7
++#define DFC_TIMING_MAX_tCS 7
++#define DFC_TIMING_MAX_tWH 7
++#define DFC_TIMING_MAX_tWP 7
++#define DFC_TIMING_MAX_tRH 7
++#define DFC_TIMING_MAX_tRP 7
++#define DFC_TIMING_MAX_tR 65535
++#define DFC_TIMING_MAX_tWHR 15
++#define DFC_TIMING_MAX_tAR 15
++
++/*
++ * The Data Flash Controller Flash timing structure
++ * For NAND flash used on Zylonite board(Samsung K9K1216Q0C),
++ * user should use value at end of each row of following member
++ * bracketed.
++ */
++struct dfc_flash_timing {
++ uint32_t tCH; /* Enable signal hold time */
++ uint32_t tCS; /* Enable signal setup time */
++ uint32_t tWH; /* ND_nWE high duration */
++ uint32_t tWP; /* ND_nWE pulse time */
++ uint32_t tRH; /* ND_nRE high duration */
++ uint32_t tRP; /* ND_nRE pulse width */
++ uint32_t tR; /* ND_nWE high to ND_nRE low for read */
++ uint32_t tWHR;/* ND_nWE high to ND_nRE low delay for status read */
++ uint32_t tAR; /* ND_ALE low to ND_nRE low delay */
++};
++
++/* DFC command type */
++enum {
++ DFC_CMD_READ = 0x00000000,
++ DFC_CMD_PROGRAM = 0x00200000,
++ DFC_CMD_ERASE = 0x00400000,
++ DFC_CMD_READ_ID = 0x00600000,
++ DFC_CMD_STATUS_READ = 0x00800000,
++ DFC_CMD_RESET = 0x00a00000
++};
++
++/*
++ * The Data Flash Controller Flash specification structure
++ * For NAND flash used on Zylonite board(Samsung K9K1216Q0C),
++ * user should use value at end of each row of following member
++ * bracketed.
++ */
++struct dfc_flash_info {
++ struct dfc_flash_timing timing; /* NAND Flash timing */
++
++ int enable_arbiter;/* Data flash bus arbiter enable (ND_ARB_EN) */
++ uint32_t page_per_block;/* Pages per block (PG_PER_BLK) */
++ uint32_t row_addr_start;/* Row address start position (RA_START) */
++ uint32_t read_id_bytes; /* returned ID bytes(RD_ID_CNT) */
++ uint32_t dfc_mode; /* NAND, CARBONDALE, PIXLEY... (ND_MODE) */
++ uint32_t ncsx; /* Chip select don't care bit (NCSX) */
++ uint32_t page_size; /* Page size in bytes (PAGE_SZ) */
++ uint32_t oob_size; /* OOB size */
++ uint32_t flash_width; /* Width of Flash memory (DWIDTH_M) */
++ uint32_t dfc_width; /* Width of flash controller(DWIDTH_C) */
++ uint32_t num_blocks; /* Number of physical blocks in Flash */
++ uint32_t chip_id;
++
++ /* command codes */
++ uint32_t read1; /* Read */
++ uint32_t read2; /* unused, DFC don't support yet */
++ uint32_t program; /* two cycle command */
++ uint32_t read_status;
++ uint32_t read_id;
++ uint32_t erase; /* two cycle command */
++ uint32_t reset;
++ uint32_t lock; /* lock whole flash */
++ uint32_t unlock; /* two cycle command, supporting partial unlock */
++ uint32_t lock_status; /* read block lock status */
++
++ /* addr2ndcb1 - encode address cycles into register NDCB1 */
++ /* ndbbr2addr - convert register NDBBR to bad block address */
++ int (*addr2ndcb1)(uint16_t cmd, uint32_t addr, uint32_t *p);
++ int (*ndbbr2addr)(uint16_t cmd, uint32_t ndbbr,uint32_t *p);
++};
++
++enum {
++ DFC_FLASH_NULL = 0 ,
++ DFC_FLASH_Samsung_512Mb_X_16 = 1,
++ DFC_FLASH_Micron_1Gb_X_8 = 2,
++ DFC_FLASH_Micron_1Gb_X_16 = 3,
++ DFC_FLASH_STM_1Gb_X_16 = 4,
++ DFC_FLASH_STM_2Gb_X_16 = 5,
++ DFC_FLASH_END,
++};
++
++static int dfc_get_flash_info(int type, struct dfc_flash_info **flash_info);
++
++#define DFC_NDCR 0
++#define DFC_NDTR0CS0 1
++#define DFC_NDTR1CS0 3
++#define DFC_NDSR 5
++#define DFC_NDPCR 6
++#define DFC_NDBDR0 7
++#define DFC_NDBDR1 8
++#define DFC_NDDB 16
++#define DFC_NDCB0 18
++#define DFC_NDCB1 19
++#define DFC_NDCB2 20
++
++/* The Data Flash Controller Mode structure */
++struct dfc_mode {
++ int enable_dma; /* DMA, or nonDMA mode */
++ int enable_ecc; /* ECC on/off */
++ int enable_spare; /* Spare enable */
++ int chip_select; /* CS0 or CS1 */
++};
++
++/* The Data Flash Controller Context structure */
++struct dfc_context {
++ unsigned char __iomem *membase; /* DFC register base */
++ struct dfc_mode *dfc_mode; /* DFC mode */
++ int data_dma_ch; /* Data DMA channel number */
++ int cmd_dma_ch; /* CMD DMA channel number */
++ struct dfc_flash_info *flash_info; /* Flash Spec */
++ struct mtd_info *mtd;
++};
++
++#define NDCB0_DMA_ADDR 0x43100048
++#define NDDB_DMA_ADDR 0x43100040
++
++#define NDSR_MASK 0xFFF
++
++/* The following data is a rough evaluation */
++
++/* microsecond, for readID/readStatus/reset */
++#define NAND_OTHER_TIMEOUT 10
++/* microsecond, for readID/readStatus/reset */
++#define NAND_CMD_TIMEOUT 10
++
++#define BBT_BLOCK_BAD 0x03
++#define BBT_BLOCK_GOOD 0x00
++#define BBT_BLOCK_REV1 0x01
++#define BBT_BLOCK_REV2 0x02
++
++#define BUFLEN (2048 + 64)
++
++/*
++ * DFC data size enumeration transfered from/to controller,
++ * including padding (zero)to be a multiple of 32.
++ */
++enum {
++ DFC_DATA_SIZE_STATUS = 8, /* ReadStatus/ReadBlockLockStatus */
++ DFC_DATA_SIZE_ID = 7, /* ReadID */
++
++ DFC_DATA_SIZE_32 = 32,
++ DFC_DATA_SIZE_512 = 512, /* R/W disabling spare area */
++ DFC_DATA_SIZE_520 = 520, /* Spare=1, ECC=1 */
++ DFC_DATA_SIZE_528 = 528, /* Spare=1, ECC=0 */
++ DFC_DATA_SIZE_544 = 544, /* R/W enabling spare area.(DMA mode)*/
++
++ DFC_DATA_SIZE_64 = 64,
++ DFC_DATA_SIZE_2048 = 2048, /* R/W disabling spare area */
++ DFC_DATA_SIZE_2088 = 2088, /* R/W enabling spare area with ecc */
++ DFC_DATA_SIZE_2112 = 2112, /* R/W enabling spare area without ecc*/
++ DFC_DATA_SIZE_2096 = 2096, /* R/W enabling spare area */
++ DFC_DATA_SIZE_UNUSED = 0xFFFF
++};
++
++/* DFC padding size enumeration transfered from/to controller */
++enum {
++ /*
++ * ReadStatus/ReadBlockLockStatus/ReadID/
++ * Read/Program disabling spare area(Both 512 and 2048)
++ * Read/Program enabling spare area, disabling ECC
++ */
++ DFC_PADDING_SIZE_0 = 0,
++
++ /* Read/program with SPARE_EN=1, ECC_EN=0, pgSize=512 */
++ DFC_PADDING_SIZE_16 = 16,
++ /* for read/program with SPARE_EN=1, ECC_EN=1, pgSize=512 and 2048 */
++ DFC_PADDING_SIZE_24 = 24,
++ DFC_PADDING_SIZE_UNUSED = 0xFFFF
++};
++
++static unsigned int flash_config = DFC_FLASH_NULL;
++
++void dfc_set_timing(struct dfc_context *context, struct dfc_flash_timing *t);
++void dfc_set_dma(struct dfc_context *context);
++void dfc_set_ecc(struct dfc_context *context);
++void dfc_set_spare(struct dfc_context *context);
++
++int dfc_get_pattern(struct dfc_context *context, uint16_t cmd,
++ int *data_size, int *padding);
++
++static int dfc_wait_event(struct dfc_context *context, uint32_t event,
++ uint32_t *event_out, uint32_t timeout, int enable_int);
++
++int dfc_send_cmd(struct dfc_context *context, uint16_t cmd,
++ uint32_t addr, int num_pages);
++
++void dfc_stop(struct dfc_context *context);
++void dfc_read_fifo_partial(struct dfc_context *context, uint8_t *buffer,
++ int nbytes, int data_size);
++void dfc_write_fifo_partial(struct dfc_context *context, uint8_t *buffer,
++ int nbytes, int data_size);
++
++void dfc_read_fifo(struct dfc_context *context, uint8_t *buffer, int nbytes);
++void dfc_write_fifo(struct dfc_context *context, uint8_t *buffer, int nbytes);
++
++void dfc_read_badblock_addr(struct dfc_context *context, uint32_t *bbaddr);
++
++void dfc_clear_int(struct dfc_context *context, uint32_t int_mask);
++void dfc_enable_int(struct dfc_context *context, uint32_t int_mask);
++void dfc_disable_int(struct dfc_context *context, uint32_t int_mask);
++
++/* high level primitives */
++int dfc_init(struct dfc_context *context, int type);
++int dfc_init_no_gpio(struct dfc_context *context, int type);
++
++int dfc_reset_flash(struct dfc_context *context);
++
++int dfc_setup_cmd_dma(struct dfc_context *context,
++ uint16_t cmd, uint32_t addr, int num_pages,
++ uint32_t *buf, uint32_t buf_phys,
++ uint32_t next_desc_phys, uint32_t dma_int_en,
++ struct pxa_dma_desc *dma_desc);
++
++int dfc_setup_data_dma(struct dfc_context *context,
++ uint16_t cmd, uint32_t buf_phys,
++ uint32_t next_desc_phys, uint32_t dma_int_en,
++ struct pxa_dma_desc *dma_desc);
++
++void dfc_start_cmd_dma(struct dfc_context *context,
++ struct pxa_dma_desc *dma_desc);
++void dfc_start_data_dma(struct dfc_context *context,
++ struct pxa_dma_desc *dma_desc);
++static int monahans_df_dev_ready(struct mtd_info *mtd);
++
++#ifdef CONFIG_DVFM
++static int mhn_nand_dvfm_notifier(unsigned cmd, void *client_data, void *info);
++static struct mhn_fv_notifier dvfm_notifier = {
++ .name = "monahans-nand-flash",
++ .priority = 0,
++ .notifier_call = mhn_nand_dvfm_notifier,
++};
++#endif
++
++static unsigned short search_rel_block(int block, struct mtd_info *mtd);
++
++/*****************************************************************************
++ * The DFC registers read/write routines
++ *****************************************************************************/
++static inline void dfc_write(struct dfc_context *context, int offset,
++ unsigned long value)
++{
++ offset <<= 2;
++ writel(value, context->membase + offset);
++}
++
++static inline unsigned int dfc_read(struct dfc_context *context, int offset)
++{
++ offset <<= 2;
++ return __raw_readl(context->membase + offset);
++}
++
++/****************************************************************************
++ * Flash Information
++ ***************************************************************************/
++
++static int Samsung512MbX16Addr2NDCB1(uint16_t cmd, uint32_t addr, uint32_t *p);
++static int Samsung512MbX16NDBBR2Addr(uint16_t cmd, uint32_t ndbbr, uint32_t *p);
++
++static struct dfc_flash_info samsung512MbX16 =
++{
++ .timing = {
++ .tCH = 10, /* tCH, Enable signal hold time */
++ .tCS = 0, /* tCS, Enable signal setup time */
++ .tWH = 20, /* tWH, ND_nWE high duration */
++ .tWP = 40, /* tWP, ND_nWE pulse time */
++ .tRH = 30, /* tRH, ND_nRE high duration */
++ .tRP = 40, /* tRP, ND_nRE pulse width */
++ /* tR = tR+tRR+tWB+1, ND_nWE high to ND_nRE low for read */
++ .tR = 11123,
++ /* tWHR, ND_nWE high to ND_nRE low delay for status read */
++ .tWHR = 110,
++ .tAR = 10, /* tAR, ND_ALE low to ND_nRE low delay */
++ },
++ .enable_arbiter = 1, /* Data flash bus arbiter enable */
++ .page_per_block = 32, /* Pages per block */
++ .row_addr_start = 0, /* Second cycle start, Row address start position */
++ .read_id_bytes = 2, /* 2 bytes, returned ID bytes */
++ .dfc_mode = 0, /* NAND mode */
++ .ncsx = 0,
++ .page_size = 512, /* Page size in bytes */
++ .oob_size = 16, /* OOB size in bytes */
++ .flash_width = 16, /* Width of Flash memory */
++ .dfc_width = 16, /* Width of flash controller */
++ .num_blocks = 4096, /* Number of physical blocks in Flash */
++ .chip_id = 0x46ec,
++
++ /* command codes */
++ .read1 = 0x0000, /* Read */
++ .read2 = 0x0050, /* Read1 unused, current DFC don't support */
++ .program = 0x1080, /* Write, two cycle command */
++ .read_status = 0x0070, /* Read status */
++ .read_id = 0x0090, /* Read ID */
++ .erase = 0xD060, /* Erase, two cycle command */
++ .reset = 0x00FF, /* Reset */
++ .lock = 0x002A, /* Lock whole flash */
++ .unlock = 0x2423, /* Unlock, two cycle command, supporting partial unlock */
++ .lock_status = 0x007A, /* Read block lock status */
++ .addr2ndcb1 = Samsung512MbX16Addr2NDCB1,
++ .ndbbr2addr = Samsung512MbX16NDBBR2Addr,
++};
++
++static int Samsung512MbX16Addr2NDCB1(uint16_t cmd, uint32_t addr, uint32_t *p)
++{
++ uint32_t ndcb1 = 0;
++
++ if (addr >= 0x4000000) return -EINVAL;
++
++ if (cmd == samsung512MbX16.read1 || cmd == samsung512MbX16.program) {
++ ndcb1 = (addr & 0xFF) | ((addr >> 1) & 0x01FFFF00);
++ } else if (cmd == samsung512MbX16.erase) {
++ ndcb1 = ((addr >> 9) & 0x00FFFFFF);
++ }
++
++ *p = ndcb1;
++ return 0;
++
++}
++
++static int Samsung512MbX16NDBBR2Addr(uint16_t cmd, uint32_t ndbbr, uint32_t *p)
++{
++ *p = ndbbr << 9;
++ return 0;
++}
++
++static int Micron1GbX8Addr2NDCB1(uint16_t cmd, uint32_t addr, uint32_t *p);
++static int Micron1GbX8NDBBR2Addr(uint16_t cmd, uint32_t ndbbr, uint32_t *p);
++
++static struct dfc_flash_info micron1GbX8 =
++{
++ .timing = {
++ .tCH = 10, /* tCH, Enable signal hold time */
++ .tCS = 25, /* tCS, Enable signal setup time */
++ .tWH = 15, /* tWH, ND_nWE high duration */
++ .tWP = 25, /* tWP, ND_nWE pulse time */
++ .tRH = 15, /* tRH, ND_nRE high duration */
++ .tRP = 25, /* tRP, ND_nRE pulse width */
++ /* tR = tR+tRR+tWB+1, ND_nWE high to ND_nRE low for read */
++ .tR = 25000,
++ /* tWHR, ND_nWE high to ND_nRE low delay for status read */
++ .tWHR = 60,
++ .tAR = 10, /* tAR, ND_ALE low to ND_nRE low delay */
++ },
++ .enable_arbiter = 1, /* Data flash bus arbiter enable */
++ .page_per_block = 64, /* Pages per block */
++ .row_addr_start = 1, /* Second cycle start, Row address start position */
++ .read_id_bytes = 4, /* Returned ID bytes */
++ .dfc_mode = 0, /* NAND mode */
++ .ncsx = 0,
++ .page_size = 2048, /* Page size in bytes */
++ .oob_size = 64, /* OOB size in bytes */
++ .flash_width = 8, /* Width of Flash memory */
++ .dfc_width = 8, /* Width of flash controller */
++ .num_blocks = 1024, /* Number of physical blocks in Flash */
++ .chip_id = 0xa12c,
++ /* command codes */
++ .read1 = 0x3000, /* Read */
++ .read2 = 0x0050, /* Read1 unused, current DFC don't support */
++ .program = 0x1080, /* Write, two cycle command */
++ .read_status = 0x0070, /* Read status */
++ .read_id = 0x0090, /* Read ID */
++ .erase = 0xD060, /* Erase, two cycle command */
++ .reset = 0x00FF, /* Reset */
++ .lock = 0x002A, /* Lock whole flash */
++ .unlock = 0x2423, /* Unlock, two cycle command, supporting partial unlock */
++ .lock_status = 0x007A, /* Read block lock status */
++ .addr2ndcb1 = Micron1GbX8Addr2NDCB1,
++ .ndbbr2addr = Micron1GbX8NDBBR2Addr,
++};
++
++static int Micron1GbX8Addr2NDCB1(uint16_t cmd, uint32_t addr, uint32_t *p)
++{
++ uint32_t ndcb1 = 0;
++ uint32_t page;
++
++ if (addr >= 0x8000000)
++ return -EINVAL;
++ page = addr / micron1GbX8.page_size;
++ addr = (page / micron1GbX8.page_per_block) << 18 |
++ (page % micron1GbX8.page_per_block) << 12;
++
++ if (cmd == micron1GbX8.read1 || cmd == micron1GbX8.program) {
++ ndcb1 = (addr & 0xFFF) | ((addr << 4) & 0xFFFF0000);
++ }
++ else if (cmd == micron1GbX8.erase) {
++ ndcb1 = ((addr >> 18) << 6) & 0xFFFF;
++ }
++
++ *p = ndcb1;
++ return 0;
++}
++
++static int Micron1GbX8NDBBR2Addr(uint16_t cmd, uint32_t ndbbr, uint32_t *p)
++{
++ if (cmd == micron1GbX8.read1 || cmd == micron1GbX8.program) {
++ *p = ((ndbbr & 0xF) << 8) | ((ndbbr >> 8) << 16);
++ }
++ else if (cmd == micron1GbX8.erase) {
++ *p = (ndbbr >> 6) << 18;
++ }
++
++
++ return 0;
++}
++
++
++static int Micron1GbX16Addr2NDCB1(uint16_t cmd, uint32_t addr, uint32_t *p);
++static int Micron1GbX16NDBBR2Addr(uint16_t cmd, uint32_t ndbbr, uint32_t *p);
++
++static struct dfc_flash_info micron1GbX16 =
++{
++ .timing = {
++ .tCH = 10, /* tCH, Enable signal hold time */
++ .tCS = 25, /* tCS, Enable signal setup time */
++ .tWH = 15, /* tWH, ND_nWE high duration */
++ .tWP = 25, /* tWP, ND_nWE pulse time */
++ .tRH = 15, /* tRH, ND_nRE high duration */
++ .tRP = 25, /* tRP, ND_nRE pulse width */
++ /* tR = tR+tRR+tWB+1, ND_nWE high to ND_nRE low for read */
++ .tR = 25000,
++ /* tWHR, ND_nWE high to ND_nRE low delay for status read */
++ .tWHR = 60,
++ .tAR = 10, /* tAR, ND_ALE low to ND_nRE low delay */
++ },
++ .enable_arbiter = 1, /* Data flash bus arbiter enable */
++ .page_per_block = 64, /* Pages per block */
++ .row_addr_start = 1, /* Second cycle start, Row address start position */
++ .read_id_bytes = 4, /* Returned ID bytes */
++ .dfc_mode = 0, /* NAND mode */
++ .ncsx = 0,
++ .page_size = 2048, /* Page size in bytes */
++ .oob_size = 64, /* OOB size in bytes */
++ .flash_width = 16, /* Width of Flash memory */
++ .dfc_width = 16, /* Width of flash controller */
++ .num_blocks = 1024, /* Number of physical blocks in Flash */
++ .chip_id = 0xb12c,
++
++ /* command codes */
++ .read1 = 0x3000, /* Read */
++ .read2 = 0x0050, /* Read1 unused, current DFC don't support */
++ .program = 0x1080, /* Write, two cycle command */
++ .read_status = 0x0070, /* Read status */
++ .read_id = 0x0090, /* Read ID */
++ .erase = 0xD060, /* Erase, two cycle command */
++ .reset = 0x00FF, /* Reset */
++ .lock = 0x002A, /* Lock whole flash */
++ .unlock = 0x2423, /* Unlock, two cycle command, supporting partial unlock */
++ .lock_status = 0x007A, /* Read block lock status */
++ .addr2ndcb1 = Micron1GbX16Addr2NDCB1,
++ .ndbbr2addr = Micron1GbX16NDBBR2Addr,
++};
++
++static int Micron1GbX16Addr2NDCB1(uint16_t cmd, uint32_t addr, uint32_t *p)
++{
++ uint32_t ndcb1 = 0;
++ uint32_t page;
++
++ if (addr >= 0x8000000)
++ return -EINVAL;
++ page = addr / micron1GbX16.page_size;
++ addr = (page / micron1GbX16.page_per_block) << 17 |
++ (page % micron1GbX16.page_per_block) << 11;
++
++ if (cmd == micron1GbX16.read1 || cmd == micron1GbX16.program) {
++ ndcb1 = (addr & 0x7FF) | ((addr << 5) & 0xFFFF0000);
++ }
++ else if (cmd == micron1GbX16.erase) {
++ ndcb1 = ((addr >> 17) << 6) & 0xFFFF;
++ }
++ *p = ndcb1;
++ return 0;
++}
++
++static int Micron1GbX16NDBBR2Addr(uint16_t cmd, uint32_t ndbbr, uint32_t *p)
++{
++ if (cmd == micron1GbX16.read1 || cmd == micron1GbX16.program) {
++ *p = ((ndbbr & 0x7) << 8) | ((ndbbr >> 8) << 16);
++ }
++ else if (cmd == micron1GbX16.erase) {
++ *p = (ndbbr >> 6) << 17;
++ }
++
++ return 0;
++}
++
++static int STM1GbX16Addr2NDCB1(uint16_t cmd, uint32_t addr, uint32_t *p);
++static int STM1GbX16NDBBR2Addr(uint16_t cmd, uint32_t ndbbr, uint32_t *p);
++
++static struct dfc_flash_info stm1GbX16 =
++{
++ .timing = {
++ .tCH = 10, /* tCH, Enable signal hold time */
++ .tCS = 10, /* tCS, Enable signal setup time */
++ .tWH = 20, /* tWH, ND_nWE high duration */
++ .tWP = 25, /* tWP, ND_nWE pulse time */
++ .tRH = 20, /* tRH, ND_nRE high duration */
++ .tRP = 25, /* tRP, ND_nRE pulse width */
++ /* tR = tR+tRR+tWB+1, ND_nWE high to ND_nRE low for read */
++ .tR = 25000,
++ /* tWHR, ND_nWE high to ND_nRE low delay for status read */
++ .tWHR = 60,
++ .tAR = 10, /* tAR, ND_ALE low to ND_nRE low delay */
++ },
++ .enable_arbiter = 1, /* Data flash bus arbiter enable */
++ .page_per_block = 64, /* Pages per block */
++ .row_addr_start = 1, /* Second cycle start, Row address start position */
++ .read_id_bytes = 4, /* Returned ID bytes */
++ .dfc_mode = 0, /* NAND mode */
++ .ncsx = 0,
++ .page_size = 2048, /* Page size in bytes */
++ .oob_size = 64, /* OOB size in bytes */
++ .flash_width = 16, /* Width of Flash memory */
++ .dfc_width = 16, /* Width of flash controller */
++ .num_blocks = 1024, /* Number of physical blocks in Flash */
++ .chip_id = 0xb120,
++
++ /* command codes */
++ .read1 = 0x3000, /* Read */
++ .read2 = 0x0050, /* Read1 unused, current DFC don't support */
++ .program = 0x1080, /* Write, two cycle command */
++ .read_status = 0x0070, /* Read status */
++ .read_id = 0x0090, /* Read ID */
++ .erase = 0xD060, /* Erase, two cycle command */
++ .reset = 0x00FF, /* Reset */
++ .lock = 0x002A, /* Lock whole flash */
++ .unlock = 0x2423, /* Unlock, two cycle command, supporting partial unlock */
++ .lock_status = 0x007A, /* Read block lock status */
++ .addr2ndcb1 = STM1GbX16Addr2NDCB1,
++ .ndbbr2addr = STM1GbX16NDBBR2Addr,
++};
++
++static int STM1GbX16Addr2NDCB1(uint16_t cmd, uint32_t addr, uint32_t *p)
++{
++ uint32_t ndcb1 = 0;
++ uint32_t page;
++
++ if (addr >= 0x8000000)
++ return -EINVAL;
++ page = addr / stm1GbX16.page_size;
++ addr = (page / stm1GbX16.page_per_block) << 17 |
++ (page % stm1GbX16.page_per_block) << 11;
++
++ if (cmd == stm1GbX16.read1 || cmd == stm1GbX16.program) {
++ ndcb1 = (addr & 0x7FF) | ((addr << 5) & 0xFFFF0000);
++ }
++ else if (cmd == stm1GbX16.erase) {
++ ndcb1 = ((addr >> 17) << 6) & 0xFFFF;
++ }
++ *p = ndcb1;
++ return 0;
++}
++
++static int STM1GbX16NDBBR2Addr(uint16_t cmd, uint32_t ndbbr, uint32_t *p)
++{
++ if (cmd == stm1GbX16.read1 || cmd == stm1GbX16.program) {
++ *p = ((ndbbr & 0x7) << 8) | ((ndbbr >> 8) << 16);
++ }
++ else if (cmd == stm1GbX16.erase) {
++ *p = (ndbbr >> 6) << 17;
++ }
++
++ return 0;
++}
++
++static int STM2GbX16Addr2NDCB1(uint16_t cmd, uint32_t addr, uint32_t *p);
++static int STM2GbX16NDBBR2Addr(uint16_t cmd, uint32_t ndbbr, uint32_t *p);
++
++static struct dfc_flash_info stm2GbX16 =
++{
++ .timing = {
++ .tCH = 10, /* tCH, Enable signal hold time */
++ .tCS = 10, /* tCS, Enable signal setup time */
++ .tWH = 20, /* tWH, ND_nWE high duration */
++ .tWP = 25, /* tWP, ND_nWE pulse time */
++ .tRH = 20, /* tRH, ND_nRE high duration */
++ .tRP = 25, /* tRP, ND_nRE pulse width */
++ /* tR = tR+tRR+tWB+1, ND_nWE high to ND_nRE low for read */
++ .tR = 25000,
++ /* tWHR, ND_nWE high to ND_nRE low delay for status read */
++ .tWHR = 60,
++ .tAR = 10, /* tAR, ND_ALE low to ND_nRE low delay */
++ },
++ .enable_arbiter = 1, /* Data flash bus arbiter enable */
++ .page_per_block = 64, /* Pages per block */
++ .row_addr_start = 1, /* Second cycle start, Row address start position */
++ .read_id_bytes = 4, /* Returned ID bytes */
++ .dfc_mode = 0, /* NAND mode */
++ .ncsx = 0,
++ .page_size = 2048, /* Page size in bytes */
++ .oob_size = 64, /* OOB size in bytes */
++ .flash_width = 16, /* Width of Flash memory */
++ .dfc_width = 16, /* Width of flash controller */
++ .num_blocks = 2048, /* Number of physical blocks in Flash */
++ .chip_id = 0xca20,
++
++ /* command codes */
++ .read1 = 0x3000, /* Read */
++ .read2 = 0x0050, /* Read1 unused, current DFC don't support */
++ .program = 0x1080, /* Write, two cycle command */
++ .read_status = 0x0070, /* Read status */
++ .read_id = 0x0090, /* Read ID */
++ .erase = 0xD060, /* Erase, two cycle command */
++ .reset = 0x00FF, /* Reset */
++ .lock = 0x002A, /* Lock whole flash */
++ .unlock = 0x2423, /* Unlock, two cycle command, supporting partial unlock */
++ .lock_status = 0x007A, /* Read block lock status */
++ .addr2ndcb1 = STM2GbX16Addr2NDCB1,
++ .ndbbr2addr = STM2GbX16NDBBR2Addr,
++};
++
++static int STM2GbX16Addr2NDCB1(uint16_t cmd, uint32_t addr, uint32_t *p)
++{
++ uint32_t ndcb1 = 0;
++ uint32_t page;
++
++ if (addr >= 0x8000000)
++ return -EINVAL;
++ page = addr / stm2GbX16.page_size;
++ addr = (page / stm2GbX16.page_per_block) << 17 |
++ (page % stm2GbX16.page_per_block) << 11;
++
++ if (cmd == stm2GbX16.read1 || cmd == stm2GbX16.program) {
++ ndcb1 = (addr & 0x7FF) | ((addr << 5) & 0xFFFF0000);
++ }
++ else if (cmd == stm2GbX16.erase) {
++ ndcb1 = ((addr >> 17) << 6) & 0xFFFF;
++ }
++ *p = ndcb1;
++ return 0;
++}
++
++static int STM2GbX16NDBBR2Addr(uint16_t cmd, uint32_t ndbbr, uint32_t *p)
++{
++ if (cmd == stm2GbX16.read1 || cmd == stm2GbX16.program) {
++ *p = ((ndbbr & 0x7) << 8) | ((ndbbr >> 8) << 16);
++ }
++ else if (cmd == stm2GbX16.erase) {
++ *p = (ndbbr >> 6) << 17;
++ }
++
++ return 0;
++}
++
++static struct {
++ int type;
++ struct dfc_flash_info *flash_info;
++} type_info[] = {
++ { DFC_FLASH_Samsung_512Mb_X_16, &samsung512MbX16},
++ { DFC_FLASH_Micron_1Gb_X_8, &micron1GbX8},
++ { DFC_FLASH_Micron_1Gb_X_16, &micron1GbX16},
++ { DFC_FLASH_STM_1Gb_X_16, &stm1GbX16},
++ { DFC_FLASH_STM_2Gb_X_16, &stm2GbX16},
++ { DFC_FLASH_NULL, NULL},
++};
++
++int dfc_get_flash_info(int type, struct dfc_flash_info **flash_info)
++{
++ uint32_t i = 0;
++
++ while(type_info[i].type != DFC_FLASH_NULL) {
++ if (type_info[i].type == type) {
++ *flash_info = type_info[i].flash_info;
++ return 0;
++ }
++ i++;
++ }
++ *flash_info = NULL;
++ return -EINVAL;
++}
++
++/******************************************************************************
++ dfc_set_timing
++
++ Description:
++ This function sets flash timing property in DFC timing register
++ according to input timing value embodied in context structure.
++ It is called once during the hardware initialization.
++ Input Parameters:
++ Output Parameters:
++ None
++ Returns:
++ None
++*******************************************************************************/
++//#if defined(CONFIG_CPU_MONAHANS_L) || defined(CONFIG_CPU_MONAHANS_LV)
++#define DFC_CLOCK 208
++//#else
++//#define DFC_CLOCK 104
++//#endif
++#define CLOCK_NS DFC_CLOCK/1000
++
++void dfc_set_timing(struct dfc_context *context, struct dfc_flash_timing *t)
++{
++ struct dfc_flash_timing timing = *t;
++
++ uint32_t r0 = 0;
++ uint32_t r1 = 0;
++
++ /*
++ * num of clock cycles = time (ns) / one clock sycle (ns) + 1
++ * - integer division will truncate the result, so add a 1 in all cases
++ * - subtract the extra 1 cycle added to all register timing values
++ */
++ timing.tCH = min(((int) (timing.tCH * CLOCK_NS) + 1),
++ DFC_TIMING_MAX_tCH);
++ timing.tCS = min(((int) (timing.tCS * CLOCK_NS) + 1),
++ DFC_TIMING_MAX_tCS);
++ timing.tWH = min(((int) (timing.tWH * CLOCK_NS) + 1),
++ DFC_TIMING_MAX_tWH);
++ timing.tWP = min(((int) (timing.tWP * CLOCK_NS) + 1),
++ DFC_TIMING_MAX_tWP);
++ timing.tRH = min(((int) (timing.tRH * CLOCK_NS) + 1),
++ DFC_TIMING_MAX_tRH);
++ timing.tRP = min(((int) (timing.tRP * CLOCK_NS) + 1),
++ DFC_TIMING_MAX_tRP);
++
++ r0 = (timing.tCH << DFC_TIMING_tCH) |
++ (timing.tCS << DFC_TIMING_tCS) |
++ (timing.tWH << DFC_TIMING_tWH) |
++ (timing.tWP << DFC_TIMING_tWP) |
++ (timing.tRH << DFC_TIMING_tRH) |
++ (timing.tRP << DFC_TIMING_tRP);
++
++ dfc_write(context, DFC_NDTR0CS0, r0);
++
++ timing.tR = min(((int) (timing.tR * CLOCK_NS) + 1),
++ DFC_TIMING_MAX_tR);
++ timing.tWHR = min(((int) (timing.tWHR * CLOCK_NS) + 1),
++ DFC_TIMING_MAX_tWHR);
++ timing.tAR = min(((int) (timing.tAR * CLOCK_NS) + 1),
++ DFC_TIMING_MAX_tAR);
++
++ r1 = (timing.tR << DFC_TIMING_tR) |
++ (timing.tWHR << DFC_TIMING_tWHR) |
++ (timing.tAR << DFC_TIMING_tAR);
++
++ dfc_write(context, DFC_NDTR1CS0, r1);
++ return;
++}
++
++/******************************************************************************
++ dfc_set_dma
++
++ Description:
++ Enables or Disables DMA in line with setting in DFC mode of context
++ structure. DMA mode of DFC. Performs a read-modify-write operation that
++ only changes the driven DMA_EN bit field In DMA mode, all commands and
++ data are transferred by DMA. DMA can be enable/disable on the fly.
++ Input Parameters:
++ context -Pointer to DFC context structure
++ Output Parameters:
++ None
++ Returns:
++ None
++*******************************************************************************/
++void
++dfc_set_dma(struct dfc_context* context)
++{
++ uint32_t ndcr;
++
++ ndcr = dfc_read(context, DFC_NDCR);
++ if (context->dfc_mode->enable_dma)
++ ndcr |= NDCR_DMA_EN;
++ else
++ ndcr &= ~NDCR_DMA_EN;
++
++ dfc_write(context, DFC_NDCR, ndcr);
++
++ /* Read again to make sure write work */
++ ndcr = dfc_read(context, DFC_NDCR);
++ return;
++}
++
++
++/******************************************************************************
++ dfc_set_ecc
++
++ Description:
++ This function enables or disables hardware ECC capability of DFC in line
++ with setting in DFC mode of context structure.
++ Input Parameters:
++ context -Pointer to DFC context structure
++ Output Parameters:
++ None
++ Returns:
++ None
++*******************************************************************************/
++void
++dfc_set_ecc(struct dfc_context* context)
++{
++ uint32_t ndcr;
++
++ ndcr = dfc_read(context, DFC_NDCR);
++ if (context->dfc_mode->enable_ecc)
++ ndcr |= NDCR_ECC_EN;
++ else
++ ndcr &= ~NDCR_ECC_EN;
++
++ dfc_write(context, DFC_NDCR, ndcr);
++
++ /* Read again to make sure write work */
++ ndcr = dfc_read(context, DFC_NDCR);
++ return;
++}
++
++/******************************************************************************
++ dfc_set_spare
++
++ Description:
++ This function enables or disables accesses to spare area of NAND Flash
++ through DFC in line with setting in DFC mode of context structure.
++ Input Parameters:
++ context -Pointer to DFC context structure
++ Output Parameters:
++ None
++ Returns:
++ None
++*******************************************************************************/
++void
++dfc_set_spare(struct dfc_context* context)
++{
++ uint32_t ndcr;
++
++ ndcr = dfc_read(context, DFC_NDCR);
++ if (context->dfc_mode->enable_spare)
++ ndcr |= NDCR_SPARE_EN;
++ else
++ ndcr &= ~NDCR_SPARE_EN;
++
++ dfc_write(context, DFC_NDCR, ndcr);
++
++ /* Read again to make sure write work */
++ ndcr = dfc_read(context, DFC_NDCR);
++ return;
++}
++
++static unsigned int get_delta (unsigned int start)
++{
++ unsigned int stop = OSCR;
++ return (stop - start);
++}
++
++static int dfc_wait_event(struct dfc_context *context, uint32_t event,
++ uint32_t *event_out, uint32_t timeout, int enable_int)
++{
++ uint32_t ndsr;
++ uint32_t to = 3 * timeout; /* 3 ticks ~ 1us */
++ int status;
++ int start = OSCR;
++
++ if (enable_int)
++ dfc_enable_int(context, event);
++
++ while (1) {
++ ndsr = dfc_read(context, DFC_NDSR);
++ ndsr &= NDSR_MASK;
++ if (ndsr & event) {
++ /* event happened */
++ *event_out = ndsr & event;
++ dfc_clear_int(context, *event_out);
++ status = 0;
++ break;
++ } else if (get_delta(start) > to) {
++ status = -ETIME;
++ break;
++ }
++ }
++
++ if (enable_int)
++ dfc_disable_int(context, event);
++ return status;
++}
++
++/******************************************************************************
++ dfc_get_pattern
++
++ Description:
++ This function is used to retrieve buffer size setting for a transaction
++ based on cmd.
++ Input Parameters:
++ context - Pointer to DFC context structure
++ cmd
++ Specifies type of command to be sent to NAND flash .The LSB of this
++ parameter defines the first command code for 2-cycles command. The
++ MSB defines the second command code for 2-cycles command. If MSB is
++ set to zero, this indicates that one cycle command
++ Output Parameters:
++ data_size
++ It is used to retrieve length of data transferred to/from DFC,
++ which includes padding bytes
++ padding
++ It is used to retrieve how many padding bytes there should be
++ in buffer of data_size.
++ Returns:
++ 0
++ If size setting is returned successfully
++ -EINVAL
++ If page size specified in flash spec of context structure is not 512 or
++ 2048;If specified command index is not read1/program/erase/reset/readID/
++ readStatus.
++*******************************************************************************/
++int dfc_get_pattern(struct dfc_context *context, uint16_t cmd,
++ int *data_size, int *padding)
++{
++ struct dfc_mode* dfc_mode = context->dfc_mode;
++ struct dfc_flash_info * flash_info = context->flash_info;
++ uint32_t page_size = context->flash_info->page_size; /* 512 or 2048 */
++
++ if (cmd == flash_info->read1 ||
++ cmd == flash_info->program) {
++ if (512 == page_size) {
++ /* add for DMA */
++ if (dfc_mode->enable_dma) {
++ *data_size = DFC_DATA_SIZE_544;
++ if (dfc_mode->enable_ecc)
++ *padding = DFC_PADDING_SIZE_24;
++ else
++ *padding = DFC_PADDING_SIZE_16;
++ } else if (!dfc_mode->enable_spare) {
++ *data_size = DFC_DATA_SIZE_512;
++ *padding = DFC_PADDING_SIZE_0;
++ } else {
++
++ if (dfc_mode->enable_ecc)
++ *data_size = DFC_DATA_SIZE_520;
++ else
++ *data_size = DFC_DATA_SIZE_528;
++
++ *padding = DFC_PADDING_SIZE_0;
++ }
++ } else if (2048 == page_size) {
++ /* add for DMA */
++ if (dfc_mode->enable_dma) {
++ *data_size = DFC_DATA_SIZE_2112;
++ if (dfc_mode->enable_ecc)
++ *padding = DFC_PADDING_SIZE_24;
++ else
++ *padding = DFC_PADDING_SIZE_0;
++ } else if (!dfc_mode->enable_spare) {
++ *data_size = DFC_DATA_SIZE_2048;
++ *padding = DFC_PADDING_SIZE_0;
++ } else {
++
++ if (dfc_mode->enable_ecc)
++ *data_size = DFC_DATA_SIZE_2088;
++ else
++ *data_size = DFC_DATA_SIZE_2112;
++
++ *padding = DFC_PADDING_SIZE_0;
++ }
++ } else /* if the page_size is neither 512 or 2048 */
++ return -EINVAL;
++ } else if (cmd == flash_info->read_id) {
++ *data_size = DFC_DATA_SIZE_ID;
++ *padding = DFC_PADDING_SIZE_0;
++ } else if(cmd == flash_info->read_status) {
++ *data_size = DFC_DATA_SIZE_STATUS;
++ *padding = DFC_PADDING_SIZE_0;
++ } else if (cmd == flash_info->erase || cmd == flash_info->reset) {
++ *data_size = DFC_DATA_SIZE_UNUSED;
++ *padding = DFC_PADDING_SIZE_UNUSED;
++ } else
++ return -EINVAL;
++ return 0;
++}
++
++
++/******************************************************************************
++ dfc_send_cmd
++
++ Description:
++ This function configures DFC to send command through DFC to NAND flash
++ Input Parameters:
++ context
++ Pointer to DFC context structure
++ cmd
++ Specifies type of command to be sent to NAND flash .The LSB of this
++ parameter defines the first command code for 2-cycles command. The
++ MSB defines the second command code for 2-cycles command. If MSB is
++ set to zero, this indicates that one cycle command
++ addr
++ Address sent out to the flash device withthis command. For page read/
++ program commands , 4-cycles address is sent. For erase command only
++ 3-cycles address is sent. If it is equal to 0xFFFFFFFF, the address
++ should not be used.
++ num_pages
++ It specifies the number of pages of data to be transferred for
++ a program or read commands. Unused for any other commands than
++ read/program.
++
++ Output Parameters:
++ None
++ Returns:
++ 0
++ If size setting is returned successfully
++ -EINVAL
++ If specified command index is not read1/program/erase/reset/readID/
++ readStatus.
++*******************************************************************************/
++int dfc_send_cmd(struct dfc_context *context, uint16_t cmd,
++ uint32_t addr, int num_pages)
++{
++ struct dfc_flash_info *flash_info = context->flash_info;
++ struct dfc_mode *dfc_mode = context->dfc_mode;
++ uint8_t cmd2;
++ uint32_t event_out;
++ uint32_t ndcb0=0, ndcb1=0, ndcb2=0, ndcr;
++ int status;
++
++ /* It is a must to set ND_RUN firstly, then write command buffer
++ * If conversely,it does not work
++ */
++ dfc_write(context, DFC_NDSR, NDSR_MASK);
++
++ /* Set ND_RUN */
++ ndcr = dfc_read(context, DFC_NDCR);
++ dfc_write(context, DFC_NDCR, (ndcr | NDCR_ND_RUN));
++
++ // Wait for write command request
++ status = dfc_wait_event(context, NDSR_WRCMDREQ,
++ &event_out, NAND_CMD_TIMEOUT, 0);
++
++ if (status) /* Timeout */
++ return status;
++
++ cmd2 = (cmd>>8) & 0xFF;
++ ndcb0 = cmd | (dfc_mode->chip_select<<24) | ((cmd2?1:0)<<19);
++
++ if (cmd == flash_info->read1) {
++ if (0xFFFFFFFF != addr) {
++ ndcb0 |= NDCB0_ADDR_CYC(4);
++ status = flash_info->addr2ndcb1(cmd, addr, &ndcb1);
++ if (status)
++ return status;
++ ndcb2 = (num_pages - 1) << 8;
++ }
++ } else if (cmd == flash_info->program) {
++ ndcb0 |= NDCB0_CMD_TYPE(1) | NDCB0_AUTO_RS;
++ ndcb0 |= NDCB0_ADDR_CYC(4);
++ status = flash_info->addr2ndcb1(cmd, addr, &ndcb1);
++ if (status)
++ return status;
++ ndcb2 = (num_pages-1) << 8;
++ } else if (cmd == flash_info->erase) {
++ ndcb0 |= NDCB0_CMD_TYPE(2) | NDCB0_AUTO_RS;
++ ndcb0 |= NDCB0_ADDR_CYC(3);
++ status = flash_info->addr2ndcb1(cmd, addr, &ndcb1);
++ if (status)
++ return status;
++ } else if (cmd == flash_info->read_id) {
++ ndcb0 |= NDCB0_CMD_TYPE(3);
++ } else if(cmd == flash_info->read_status) {
++ ndcb0 |= NDCB0_CMD_TYPE(4);
++ } else if(cmd == flash_info->reset) {
++ ndcb0 |= NDCB0_CMD_TYPE(5);
++ } else if (cmd == flash_info->lock) {
++ ndcb0 |= NDCB0_CMD_TYPE(5);
++ } else
++ return -EINVAL;
++
++ /* Write to DFC command register */
++ dfc_write(context, DFC_NDCB0, ndcb0);
++ dfc_write(context, DFC_NDCB0, ndcb1);
++ dfc_write(context, DFC_NDCB0, ndcb2);
++
++ return 0;
++}
++
++/******************************************************************************
++ dfc_stop
++
++ Description:
++ This function clears ND_RUN bit of NDCR.
++ Input Parameters:
++ context--Pointer to DFC context structure
++ Output Parameters:
++ None
++ Returns:
++ None
++*******************************************************************************/
++void dfc_stop(struct dfc_context *context)
++{
++ unsigned int ndcr;
++ ndcr = dfc_read(context, DFC_NDCR);
++ dfc_write(context, DFC_NDCR, (ndcr & ~NDCR_ND_RUN));
++ ndcr = dfc_read(context, DFC_NDCR);
++
++ return;
++}
++
++int dfc_setup_cmd_dma(struct dfc_context *context,
++ uint16_t cmd, uint32_t addr, int num_pages,
++ uint32_t *buf, uint32_t buf_phys,
++ uint32_t next_desc_phys, uint32_t dma_int_en,
++ struct pxa_dma_desc *dma_desc)
++{
++ struct dfc_flash_info *flash_info = context->flash_info;
++ struct dfc_mode *dfc_mode = context->dfc_mode;
++ uint8_t cmd2;
++ uint32_t event_out;
++ uint32_t ndcb0=0, ndcb1=0, ndcb2=0, ndcr;
++ int status;
++
++ /*
++ * It is a must to set ND_RUN firstly, then write command buffer
++ * If conversely,it does not work
++ */
++ dfc_write(context, DFC_NDSR, NDSR_MASK);
++
++ /* Set ND_RUN */
++ ndcr = dfc_read(context, DFC_NDCR);
++ ndcr |= NDCR_ND_RUN;
++ dfc_write(context, DFC_NDCR, ndcr);
++
++ /* Wait for write command request */
++ status = dfc_wait_event(context, NDSR_WRCMDREQ,
++ &event_out, NAND_CMD_TIMEOUT, 0);
++
++ if (status)
++ return status; /* Timeout */
++
++ cmd2 = (cmd>>8) & 0xFF;
++ ndcb0 = cmd | (dfc_mode->chip_select<<24) | ((cmd2?1:0)<<19);
++
++ if (cmd == flash_info->read1) {
++ if (0xFFFFFFFF != addr) {
++ ndcb0 |= NDCB0_ADDR_CYC(4);
++ status = flash_info->addr2ndcb1(cmd, addr, &ndcb1);
++ if (status)
++ return status;
++ ndcb2 = (num_pages-1) << 8;
++ }
++ } else if (cmd == flash_info->program) {
++ ndcb0 |= NDCB0_CMD_TYPE(1) | NDCB0_AUTO_RS;
++ ndcb0 |= NDCB0_ADDR_CYC(4);
++
++ status = flash_info->addr2ndcb1(cmd, addr, &ndcb1);
++ if (status)
++ return status;
++ ndcb2 = (num_pages-1) << 8;
++ } else if (cmd == flash_info->erase) {
++ ndcb0 |= NDCB0_CMD_TYPE(2) | NDCB0_AUTO_RS;
++ ndcb0 |= NDCB0_ADDR_CYC(3);
++
++ status = flash_info->addr2ndcb1(cmd, addr, &ndcb1);
++ if (status)
++ return status;
++ } else if (cmd == flash_info->read_id) {
++ ndcb0 |= NDCB0_CMD_TYPE(3);
++ } else if (cmd == flash_info->read_status) {
++ ndcb0 |= NDCB0_CMD_TYPE(4);
++ } else if (cmd == flash_info->reset) {
++ ndcb0 |= NDCB0_CMD_TYPE(5);
++ } else if (cmd == flash_info->lock) {
++ ndcb0 |= NDCB0_CMD_TYPE(5);
++ } else
++ return -EINVAL;
++
++ *((uint32_t *)buf) = ndcb0;
++ *((uint32_t *)buf + 1) = ndcb1;
++ *((uint32_t *)buf + 2) = ndcb2;
++
++ dma_int_en &= (DCMD_STARTIRQEN | DCMD_ENDIRQEN);
++
++ dma_desc->ddadr = next_desc_phys;
++ dma_desc->dsadr = buf_phys;
++ dma_desc->dtadr = NDCB0_DMA_ADDR;
++ dma_desc->dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | dma_int_en |
++ DCMD_WIDTH4 | DCMD_BURST16 | 12;
++ return 0;
++}
++
++int dfc_setup_data_dma(struct dfc_context* context,
++ uint16_t cmd, uint32_t buf_phys,
++ uint32_t next_desc_phys, uint32_t dma_int_en,
++ struct pxa_dma_desc* dma_desc)
++{
++ struct dfc_flash_info * flash_info = context->flash_info;
++ int data_size, padding;
++
++ dfc_get_pattern(context, cmd, &data_size, &padding);
++
++ dma_desc->ddadr = next_desc_phys;
++ dma_int_en &= (DCMD_STARTIRQEN | DCMD_ENDIRQEN);
++
++ if (cmd == flash_info->program) {
++
++ dma_desc->dsadr = buf_phys;
++ dma_desc->dtadr = NDDB_DMA_ADDR;
++ dma_desc->dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG | dma_int_en |
++ DCMD_WIDTH4 | DCMD_BURST32 | data_size;
++
++ } else if (cmd == flash_info->read1 || cmd == flash_info->read_id ||
++ cmd == flash_info->read_status) {
++
++ dma_desc->dsadr = NDDB_DMA_ADDR;
++ dma_desc->dtadr = buf_phys;
++ dma_desc->dcmd = DCMD_INCTRGADDR | DCMD_FLOWSRC | dma_int_en |
++ DCMD_WIDTH4 | DCMD_BURST32 | data_size;
++ }
++ else
++ return -EINVAL;
++ return 0;
++}
++
++void dfc_start_cmd_dma(struct dfc_context* context, struct pxa_dma_desc* dma_desc)
++{
++ DRCMR99 = DRCMR_MAPVLD | context->cmd_dma_ch; /* NAND CMD DRCMR */
++ DDADR(context->cmd_dma_ch) = (uint32_t)dma_desc;
++ DCSR(context->cmd_dma_ch) |= DCSR_RUN;
++}
++
++void dfc_start_data_dma(struct dfc_context* context, struct pxa_dma_desc* dma_desc)
++{
++ DRCMR97 = DRCMR_MAPVLD | context->data_dma_ch;
++ DDADR(context->data_dma_ch) = (uint32_t)dma_desc;
++ DCSR(context->data_dma_ch) |= DCSR_RUN;
++}
++
++/******************************************************************************
++ dfc_read_fifo_partial
++
++ Description:
++ This function reads data from data buffer of DFC.Bytes can be any less than
++ or equal to data_size, the left is ignored by ReadFIFO though they will be
++ read from NDDB to clear data buffer.
++ Input Parameters:
++ context
++ Pointer to DFC context structure
++ nbytes
++ Indicating how much data should be read into buffer.
++ data_size
++ Specifing length of data transferred to/from DFC, which includes
++ padding bytes
++ Output Parameters:
++ pBuffer
++ Pointer to the data buffer where data should be placed.
++ Returns:
++ None
++*******************************************************************************/
++void dfc_read_fifo_partial(struct dfc_context *context, uint8_t *buffer,
++ int nbytes, int data_size)
++{
++ uint32_t data = 0;
++ uint32_t i = 0;
++ uint32_t bytes_multi;
++ uint32_t bytes_remain;
++
++
++ if (1 == data_size) {
++ data = dfc_read(context, DFC_NDDB) & 0xFF;
++ *buffer++ = (uint8_t)data;
++ } else if (2 == data_size) {
++ data = dfc_read(context, DFC_NDDB) & 0xFFFF;
++ *buffer++ = data & 0xFF;
++ *buffer++ = (data >> 8) & 0xFF;
++ } else {
++ bytes_multi = (nbytes & 0xFFFFFFFC);
++ bytes_remain = nbytes & 0x03;
++
++ i = 0;
++ /* Read the bytes_multi*4 bytes data */
++ while (i < bytes_multi) {
++ data = dfc_read(context, DFC_NDDB);
++ /* FIXME: we don't know whether the buffer
++ * align to 4 bytes or not. Cast the buffer
++ * to int is not safe here. Especially under
++ * gcc 4.x. Used memcpy here. But the memcpy
++ * may be not correct on BE architecture.
++ * --by Yin, Fengwei
++ */
++ memcpy(buffer, &data, sizeof(data));
++ i += sizeof(data);
++ buffer += sizeof(data);
++ }
++
++ /* Read the left bytes_remain bytes data */
++ if (bytes_remain) {
++ data = dfc_read(context, DFC_NDDB);
++ for (i = 0; i < bytes_remain; i++)
++ *buffer++ = (uint8_t)((data >> (8*i)) & 0xFF);
++ }
++
++ /* When read the remain bytes, we always read 4 bytes data
++ * to DFC. So the data_size should subtract following number.
++ */
++ data_size -= bytes_multi + (bytes_remain ? sizeof(data) : 0);
++
++ /* We need Read data_size bytes data totally */
++ while (data_size > 0) {
++ data = dfc_read(context, DFC_NDDB);
++ data_size -= sizeof(data);
++ }
++
++/*
++ while(i < ((uint32_t)data_size) ) {
++ if (i < bytes_multi) {
++ temp = (uint32_t *)buffer;
++ *temp = dfc_reg->nddb;
++ } else if (i == bytes_multi && bytes_remain){
++ uint32_t j = 0;
++ data = dfc_reg->nddb;
++ while (j++ < bytes_remain) {
++ *buffer++ = (uint8_t) \
++ ((data>>(8*j)) & 0xFF);
++ }
++ } else {
++ data = dfc_reg->nddb;
++ }
++ i += 4;
++ buffer += 4;
++ }
++*/
++ }
++ return;
++}
++
++/******************************************************************************
++ dfc_write_fifo_partial
++
++ Description:
++ Write to data buffer of DFC from a buffer. Bytes can be same as
++ data_size, also can be data_size-padding, but can¡¯t be random value,
++ the left will be automatically padded by WriteFIFO.
++ Input Parameters:
++ context
++ Pointer to DFC context structure
++ bytes
++ Indicating how much data should be read into buffer.
++ data_size
++ Specifing length of data transferred to/from DFC, which includes
++ padding bytes
++ buffer
++ Pointer to the data buffer where data will be taken from to be written
++ to DFC data buffer
++ Output Parameters:
++ None
++ Returns:
++ None
++*******************************************************************************/
++void dfc_write_fifo_partial(struct dfc_context *context, uint8_t *buffer,
++ int nbytes, int data_size)
++{
++ uint32_t i = 0;
++
++ uint32_t bytes_multi = (nbytes & 0xFFFFFFFC);
++ uint32_t bytes_remain = nbytes & 0x03;
++ uint32_t temp;
++ /*
++ * caller guarantee buffer contains appropriate data thereby
++ * it is impossible for nbytes not to be a multiple of 4 byte
++ */
++
++ /* Write the bytes_multi*4 bytes data */
++ while (i < bytes_multi) {
++ temp = buffer[0] | buffer[1] << 8 |
++ buffer[2] << 16 | buffer[3] << 24;
++ dfc_write(context, DFC_NDDB, temp);
++ buffer += 4;
++ i += 4;
++ }
++
++ /* Write the left bytes_remain bytes data */
++ if (bytes_remain) {
++ temp = 0xFFFFFFFF;
++ for (i = 0; i < bytes_remain; i++)
++ temp &= *buffer++ << i*8;
++
++ dfc_write(context, DFC_NDDB, temp);
++ }
++
++ /* When write the remain bytes, we always write 4 bytes data
++ * to DFC. So the data_size should subtract following number.
++ */
++ data_size -= bytes_multi + (bytes_remain ? sizeof(temp) : 0);
++
++ while (data_size > 0) {
++ dfc_write(context, DFC_NDDB, 0xFFFFFFFF);
++ data_size -= 4;
++ }
++
++/*
++ while (i < ((uint32_t)data_size)) {
++ if (i < bytes_multi) {
++ temp = (uint32_t *)buffer;
++ dfc_reg->nddb = *temp;
++ }
++ else if (i == bytes_multi && bytes_remain) {
++ uint32_t j = 0, data = 0xFFFFFFFF;
++ while (j < bytes_remain) {
++ data &= (uint8_t)(*buffer) << j;
++ buffer++;
++ j++;
++ }
++ dfc_reg->nddb = data;
++ }
++ else {
++ dfc_reg->nddb = 0xFFFFFFFF;
++ }
++ i += 4;
++ buffer += 4;
++ }
++*/
++
++ return;
++}
++
++/******************************************************************************
++ dfc_read_fifo
++ Description:
++ This function reads data from data buffer of DFC.Bytes can be any less
++ than or equal to data_size, the left is ignored by ReadFIFO though they
++ will be read from NDDB to clear data buffer.
++ Input Parameters:
++ context
++ Pointer to DFC context structure
++ nbytes
++ Indicating how much data should be read into buffer.
++ data_size
++ Specifing length of data transferred to/from DFC, which includes
++ padding bytes
++ Output Parameters:
++ buffer
++ Pointer to the data buffer where data should be placed.
++ Returns:
++ None
++*******************************************************************************/
++
++void dfc_read_fifo(struct dfc_context *context, uint8_t *buffer, int nbytes)
++{
++ uint32_t i = 0;
++
++ uint32_t bytes_multi = (nbytes & 0xFFFFFFFC);
++ uint32_t bytes_remain = nbytes & 0x03;
++ uint32_t temp;
++
++ /* Read the bytes_multi*4 bytes data */
++ while (i < bytes_multi) {
++ temp = dfc_read(context, DFC_NDDB);
++ /* FIXME: we don't know whether the buffer
++ * align to 4 bytes or not. Cast the buffer
++ * to int is not safe here. Especially under
++ * gcc 4.x. Used memcpy here. But the memcpy
++ * may be not correct on BE architecture.
++ * --by Yin, Fengwei
++ */
++ memcpy(buffer, &temp, sizeof(temp));
++ i += sizeof(temp);
++ buffer += sizeof(temp);
++ }
++
++ /* Read the left bytes_remain bytes data */
++ temp = dfc_read(context, DFC_NDDB);
++ for (i = 0; i < bytes_remain; i++) {
++ *buffer++ = (uint8_t)((temp >> (8*i)) & 0xFF);
++ }
++
++/*
++ while (i < bytes_multi) {
++ temp = (uint32_t *)buffer;
++ *temp = dfc_reg->nddb;
++ i += 4;
++ buffer += 4;
++ }
++
++ if (bytes_remain) {
++ data = dfc_reg->nddb;
++ for (i = 0; i < bytes_remain; i++) {
++ *buffer++ = (uint8_t)((data>>(8*i)) & 0xFF);
++ }
++ }
++*/
++
++ return;
++}
++
++/******************************************************************************
++ dfc_write_fifo
++ Description:
++ Write to data buffer of DFC from a buffer.Bytes can be same as data_size,
++ also can be data_size-padding, but can¡¯t be random value, the left will
++ be automatically padded by WriteFIFO.
++ Input Parameters:
++ context
++ Pointer to DFC context structure
++ nbytes
++ Indicating how much data should be read into buffer.
++ data_size
++ Specifing length of data transferred to/from DFC, which includes
++ padding bytes
++ buffer
++ Pointer to the data buffer where data will be taken from to be written to
++ DFC data buffer
++ Output Parameters:
++ None
++ Returns:
++ None
++*******************************************************************************/
++void dfc_write_fifo(struct dfc_context *context, uint8_t *buffer, int nbytes)
++{
++ uint32_t bytes_multi = (nbytes & 0xFFFFFFFC);
++ uint32_t bytes_remain = nbytes & 0x03;
++ uint32_t i=0;
++ uint32_t temp;
++
++ /* Write the bytes_multi*4 bytes data */
++ while (i < bytes_multi) {
++ temp = buffer[0] | buffer[1] << 8 |
++ buffer[2] << 16 | buffer[3] << 24;
++ dfc_write(context, DFC_NDDB, temp);
++ buffer += 4;
++ i += 4;
++ }
++
++ /* Write the left bytes_remain bytes data */
++ temp = 0xFFFFFFFF;
++ for (i = 0; i < bytes_remain; i++)
++ temp &= *buffer++ << i*8;
++ dfc_write(context, DFC_NDDB, temp);
++
++/*
++ while (i < nbytes) {
++ temp = (uint32_t *)buffer;
++ dfc_reg->nddb = *temp;
++ i += 4;
++ buffer += 4;
++ }
++*/
++}
++
++/******************************************************************************
++ dfc_read_badblock_addr
++
++ Description:
++ This function reads bad block address in units of block starting from 0
++ if bad block is detected. It takes into the account if the operation is
++ for CS0 or CS1 depending on settings of chip_select parameter of DFC
++ Mode structure.
++ Input Parameters:
++ context
++ Pointer to DFC context structure
++ Output Parameters:
++ pBadBlockAddr
++ Used to retrieve bad block address back to caller if bad block is
++ detected
++ Returns:
++ None
++*******************************************************************************/
++void dfc_read_badblock_addr(struct dfc_context *context, uint32_t *bbaddr)
++{
++ uint32_t ndbdr;
++ if (0 == context->dfc_mode->chip_select)
++ ndbdr = dfc_read(context, DFC_NDBDR0);
++ else
++ ndbdr = dfc_read(context, DFC_NDBDR1);
++
++ if (512 == context->flash_info->page_size) {
++ ndbdr = (ndbdr >> 5) & 0xFFF;
++ *bbaddr = ndbdr;
++ } else if (2048 == context->flash_info->page_size) {
++ /* 16 bits LB */
++ ndbdr = (ndbdr >> 8);
++ *bbaddr = ndbdr;
++ }
++ return;
++}
++
++/******************************************************************************
++ dfc_enable_int
++
++ Description:
++ This function is used to enable DFC interrupts. The bits in int_mask
++ will be used to unmask NDCR register to enable corresponding interrupts.
++ Input Parameters:
++ context
++ Pointer to DFC context structure
++ int_mask
++ Specifies what interrupts to enable
++ Output Parameters:
++ None
++ Returns:
++ None
++*******************************************************************************/
++void dfc_enable_int(struct dfc_context *context, uint32_t int_mask)
++{
++ uint32_t ndcr;
++
++ ndcr = dfc_read(context, DFC_NDCR);
++ ndcr &= ~int_mask;
++ dfc_write(context, DFC_NDCR, ndcr);
++
++ ndcr = dfc_read(context, DFC_NDCR);
++ return;
++}
++
++/******************************************************************************
++ dfc_disable_int
++
++ Description:
++ This function is used to disable DFC interrupts.
++ The bits inint_mask will be used to mask NDCR register to disable
++ corresponding interrupts.
++ Input Parameters:
++ context
++ Pointer to DFC context structure
++ int_mask
++ Specifies what interrupts to disable
++ Output Parameters:
++ None
++ Returns:
++ None
++*******************************************************************************/
++void dfc_disable_int(struct dfc_context *context, uint32_t int_mask)
++{
++ uint32_t ndcr;
++
++ ndcr = dfc_read(context, DFC_NDCR);
++ ndcr |= int_mask;
++ dfc_write(context, DFC_NDCR, ndcr);
++
++ ndcr = dfc_read(context, DFC_NDCR);
++ return;
++}
++
++/******************************************************************************
++ dfc_clear_int
++
++ Description:
++ This function is used to disable DFC interrupts.
++ The bits in int_mask will be used to clear corresponding interrupts
++ in NDCR register
++ Input Parameters:
++ context
++ Pointer to DFC context structure
++ int_mask
++ Specifies what interrupts to clear
++ Output Parameters:
++ None
++ Returns:
++ None
++*******************************************************************************/
++void dfc_clear_int(struct dfc_context *context, uint32_t int_mask)
++{
++ dfc_write(context, DFC_NDSR, int_mask);
++
++ dfc_read(context, DFC_NDSR);
++ return;
++}
++
++/*
++ * high level primitives
++ */
++
++/******************************************************************************
++ dfc_init
++
++ Description:
++ This function does entire DFC initialization according to the NAND
++ flash type currently used with platform, including setting MFP, set
++ flash timing, set DFC mode, configuring specified flash parameters
++ in DFC, clear ECC logic and page count register.
++ Input Parameters:
++ context
++ Pointer to DFC context structure
++ Output Parameters:
++ None
++ Returns:
++ 0
++ if MFPRs are set correctly
++ -EINVAL
++ if specified flash is not support by check bytes per page and pages per
++ block
++******************************************************************************/
++
++static mfp_cfg_t pxa300_nand_cfg[] = {
++ /* NAND */
++ MFP_CFG_X(DF_INT_RnB, AF0, DS10X, PULL_LOW),
++ MFP_CFG_X(DF_nRE_nOE, AF1, DS10X, PULL_LOW),
++ MFP_CFG_X(DF_nWE, AF1, DS10X, PULL_LOW),
++ MFP_CFG_X(DF_CLE_nOE, AF0, DS10X, PULL_LOW),
++ MFP_CFG_X(DF_nADV1_ALE, AF1, DS10X, PULL_LOW),
++ MFP_CFG_X(DF_nCS0, AF1, DS10X, PULL_LOW),
++ MFP_CFG_X(DF_nCS1, AF0, DS10X, PULL_LOW),
++ MFP_CFG_X(DF_IO0, AF1, DS08X, PULL_LOW),
++ MFP_CFG_X(DF_IO1, AF1, DS08X, PULL_LOW),
++ MFP_CFG_X(DF_IO2, AF1, DS08X, PULL_LOW),
++ MFP_CFG_X(DF_IO3, AF1, DS08X, PULL_LOW),
++ MFP_CFG_X(DF_IO4, AF1, DS08X, PULL_LOW),
++ MFP_CFG_X(DF_IO5, AF1, DS08X, PULL_LOW),
++ MFP_CFG_X(DF_IO6, AF1, DS08X, PULL_LOW),
++ MFP_CFG_X(DF_IO7, AF1, DS08X, PULL_LOW),
++ MFP_CFG_X(DF_IO8, AF1, DS08X, PULL_LOW),
++ MFP_CFG_X(DF_IO9, AF1, DS08X, PULL_LOW),
++ MFP_CFG_X(DF_IO10, AF1, DS08X, PULL_LOW),
++ MFP_CFG_X(DF_IO11, AF1, DS08X, PULL_LOW),
++ MFP_CFG_X(DF_IO12, AF1, DS08X, PULL_LOW),
++ MFP_CFG_X(DF_IO13, AF1, DS08X, PULL_LOW),
++ MFP_CFG_X(DF_IO14, AF1, DS08X, PULL_LOW),
++};
++
++#define ARRAY_AND_SIZE(x) (x), ARRAY_SIZE(x)
++
++int dfc_init(struct dfc_context* context, int type)
++{
++ int status;
++ struct dfc_flash_info * flash_info;
++ uint32_t ndcr = 0x00000FFF; /* disable all interrupts */
++
++ status = dfc_get_flash_info(type, &flash_info);
++ if (status)
++ return status;
++ context->flash_info = flash_info;
++
++ pxa3xx_mfp_config(ARRAY_AND_SIZE(pxa300_nand_cfg));
++ //enable_dfc_pins();
++
++ dfc_set_timing(context, &context->flash_info->timing);
++
++ if (flash_info->enable_arbiter)
++ ndcr |= NDCR_ND_ARB_EN;
++
++ if (64 == flash_info->page_per_block)
++ ndcr |= NDCR_PG_PER_BLK;
++ else if (32 != flash_info->page_per_block)
++ return -EINVAL;
++
++ if (flash_info->row_addr_start)
++ ndcr |= NDCR_RA_START;
++
++ ndcr |= (flash_info->read_id_bytes)<<16;
++
++ ndcr |= (flash_info->dfc_mode) << 21;
++
++ if (flash_info->ncsx)
++ ndcr |= NDCR_NCSX;
++
++ if (2048 == flash_info->page_size)
++ ndcr |= NDCR_PAGE_SZ;
++ else if (512 != flash_info->page_size)
++ return -EINVAL;
++
++ if (16 == flash_info->flash_width)
++ ndcr |= NDCR_DWIDTH_M;
++ else if (8 != flash_info->flash_width)
++ return -EINVAL;
++
++ if (16 == flash_info->dfc_width)
++ ndcr |= NDCR_DWIDTH_C;
++ else if (8 != flash_info->dfc_width)
++ return -EINVAL;
++
++ dfc_write(context, DFC_NDCR, ndcr);
++
++ dfc_set_dma(context);
++ dfc_set_ecc(context);
++ dfc_set_spare(context);
++
++ return 0;
++}
++
++/******************************************************************************
++ dfc_init_no_gpio
++
++ Description:
++ This function does entire DFC initialization according to the NAND
++ flash type currently used with platform, including set flash timing,
++ set DFC mode, configuring specified flash parameters in DFC, clear
++ ECC logic and page count register. The only difference with dfc_init
++ is that it does not set MFP&GPIO, very useful in OS loader
++ Input Parameters:
++ context
++ Pointer to DFC context structure
++ Output Parameters:
++ None
++ Returns:
++ 0
++ if MFPRs are set correctly
++ -EINVAL
++ if specified flash is not support by check bytes per page and pages
++ per block
++******************************************************************************/
++int dfc_init_no_gpio(struct dfc_context* context, int type)
++{
++ struct dfc_flash_info * flash_info;
++ uint32_t ndcr = 0x00000FFF; /* disable all interrupts */
++ int status;
++
++ status = dfc_get_flash_info(type, &flash_info);
++ if (status)
++ return status;
++ context->flash_info = flash_info;
++
++ dfc_set_timing(context, &context->flash_info->timing);
++
++ if (flash_info->enable_arbiter)
++ ndcr |= NDCR_ND_ARB_EN;
++
++ if (64 == flash_info->page_per_block)
++ ndcr |= NDCR_PG_PER_BLK;
++ else if (32 != flash_info->page_per_block)
++ return -EINVAL;
++
++ if (flash_info->row_addr_start)
++ ndcr |= NDCR_RA_START;
++
++ ndcr |= (flash_info->read_id_bytes)<<16;
++
++ ndcr |= (flash_info->dfc_mode) << 21;
++
++ if (flash_info->ncsx)
++ ndcr |= NDCR_NCSX;
++
++ if (2048 == flash_info->page_size)
++ ndcr |= NDCR_PAGE_SZ;
++ else if (512 != flash_info->page_size)
++ return -EINVAL;
++
++ if (16 == flash_info->flash_width)
++ ndcr |= NDCR_DWIDTH_M;
++ else if (8 != flash_info->flash_width)
++ return -EINVAL;
++
++ if (16 == flash_info->dfc_width)
++ ndcr |= NDCR_DWIDTH_C;
++ else if (8 != flash_info->dfc_width)
++ return -EINVAL;
++
++ dfc_write(context, DFC_NDCR, ndcr);
++
++ dfc_set_dma(context);
++ dfc_set_ecc(context);
++ dfc_set_spare(context);
++
++ return 0;
++}
++
++/*
++ * This macro will be used in following NAND operation functions.
++ * It is used to clear command buffer to ensure cmd buffer is empty
++ * in case of operation is timeout
++ */
++#define ClearCMDBuf() do { \
++ dfc_stop(context); \
++ udelay(NAND_OTHER_TIMEOUT); \
++ } while (0)
++
++/******************************************************************************
++ dfc_reset_flash
++
++ Description:
++ It reset the flash. The function can be called at any time when the
++ device is in Busy state during random read/program/erase mode and
++ reset operation will abort all these operations. After reset operation
++ the device is ready to wait for next command
++ Input Parameters:
++ context
++ Pointer to DFC context structure
++ Output Parameters:
++ None
++ Returns:
++ 0
++ execution succeeds
++ -ETIME
++ if timeout
++*******************************************************************************/
++int dfc_reset_flash(struct dfc_context *context)
++{
++ struct dfc_flash_info *flash_info = context->flash_info;
++ uint32_t event, event_out;
++ unsigned long timeo;
++ int status;
++
++ /* Send command */
++ dfc_send_cmd(context, (uint16_t)flash_info->reset, 0xFFFFFFFF, 0);
++
++ event = (context->dfc_mode->chip_select)? \
++ NDSR_CS1_CMDD : NDSR_CS0_CMDD;
++
++ /* Wait for CMDDM(command done successfully) */
++ status = dfc_wait_event(context, event, &event_out,
++ NAND_OTHER_TIMEOUT, 0);
++
++ if (status) {
++ ClearCMDBuf();
++ return status;
++ }
++
++
++ /* Wait until flash device is stable or timeout (10ms) */
++ timeo = jiffies + HZ;
++ do {
++ if (monahans_df_dev_ready(context->mtd))
++ break;
++ } while (time_before(jiffies, timeo));
++
++ return 0;
++}
++
++int dfc_readid(struct dfc_context *context, uint32_t *id)
++{
++ struct dfc_flash_info *flash_info = context->flash_info;
++ uint32_t event_out;
++ int status;
++ char tmp[DFC_DATA_SIZE_ID];
++
++ /* Send command */
++ status = dfc_send_cmd(context, (uint16_t)flash_info->read_id,
++ 0xFFFFFFFF, 0);
++ if (status) {
++ ClearCMDBuf();
++ return status;
++ }
++
++ /* Wait for CMDDM(command done successfully) */
++ status = dfc_wait_event(context, NDSR_RDDREQ, &event_out,
++ NAND_OTHER_TIMEOUT, 0);
++ if (status) {
++ ClearCMDBuf();
++ return status;
++ }
++ dfc_read_fifo_partial(context, (unsigned char *)tmp,
++ context->flash_info->read_id_bytes, DFC_DATA_SIZE_ID);
++
++ *id = tmp[0] | (tmp[1] << 8);
++ return 0;
++}
++
++#define ERR_NONE 0x0
++#define ERR_DMABUSERR (-0x01)
++#define ERR_SENDCMD (-0x02)
++#define ERR_DBERR (-0x03)
++#define ERR_BBERR (-0x04)
++#define ERR_BUSY (-0x05)
++
++#define STATE_CMD_SEND 0x1
++#define STATE_CMD_HANDLE 0x2
++#define STATE_DMA_TRANSFER 0x3
++#define STATE_DMA_DONE 0x4
++#define STATE_READY 0x5
++#define STATE_SUSPENDED 0x6
++#define STATE_DATA_TRANSFER 0x7
++
++#define NAND_RELOC_MAX 127
++#define NAND_RELOC_HEADER 0x524e
++#define MAX_CHIP 1
++#define NAND_CMD_DMA_LEN 12
++
++#define MAX_TIM_SIZE 0x1000
++#define MAX_BBT_SLOTS 24
++
++struct reloc_item {
++ unsigned short from;
++ unsigned short to;
++};
++
++struct reloc_table {
++ unsigned short header;
++ unsigned short total;
++ struct reloc_item reloc[NAND_RELOC_MAX];
++};
++
++struct monahans_dfc_info {
++ unsigned int state;
++ struct dfc_context *context;
++#ifdef CONFIG_MTD_NAND_MONAHANS_DMA
++ dma_addr_t data_buf_addr;
++ char *data_buf;
++ int data_dma;
++ struct pxa_dma_desc *data_desc;
++ dma_addr_t data_desc_addr;
++ dma_addr_t cmd_buf_addr;
++ char *cmd_buf;
++ int cmd_dma;
++ struct pxa_dma_desc *cmd_desc;
++ dma_addr_t cmd_desc_addr;
++ u64 dma_mask;
++#else
++ char *data_buf;
++#endif
++ u32 current_slot;
++ struct reloc_table table;
++ unsigned int table_init;
++ /* relate to the command */
++ unsigned int cmd;
++ unsigned int addr;
++ unsigned int column;
++ int retcode;
++ unsigned int buf_count;
++ struct completion cmd_complete;
++};
++
++static struct dfc_mode dfc_mode =
++{
++#ifdef CONFIG_MTD_NAND_MONAHANS_DMA
++ 1, /* enable DMA */
++#else
++ 0,
++#endif
++ 1, /* enable ECC */
++ 1, /* enable SPARE */
++ 0, /* CS0 */
++};
++
++
++struct dfc_context dfc_context =
++{
++ 0, /* Initialized at function monahans_df_init() */
++ &dfc_mode,
++ 0, /* data dma channel */
++ 0, /* cmd dma channel */
++ NULL, /* &zylonite_flashinfo */
++};
++
++
++/*
++ * MTD structure for Zylonite board
++ */
++static struct mtd_info *monahans_mtd = NULL;
++
++/*
++ * BootRom and XDB will use last 127 block, and they will keep all the status
++ * of the bootloader and image, so skip the first 2M size and last 2M size
++ */
++static struct mtd_partition partition_info[] = {
++ {
++ name: "Bootloader",
++//#ifdef CONFIG_CPU_MONAHANS_LV
++ size: 0x00060000,
++//#else
++// size: 0x00040000,
++//#endif
++ offset: 0,
++ mask_flags: MTD_WRITEABLE /* force read-only */
++ },{
++ name: "Kernel",
++ size: 0x00200000,
++//#ifdef CONFIG_CPU_MONAHANS_LV
++ offset: 0x00060000,
++//#else
++// offset: 0x00040000,
++//#endif
++ mask_flags: MTD_WRITEABLE /* force read-only */
++ },{
++ name: "Filesystem",
++ size: 0x05000000,
++//#ifdef CONFIG_CPU_MONAHANS_LV
++ offset: 0x00260000,
++//#else
++// offset: 0x00240000,
++//#endif
++ }, {
++ name: "MassStorage",
++ size: 0x0, /* It will be set at probe function */
++ offset: MTDPART_OFS_APPEND /* Append after fs section */
++ }, {
++ name: "BBT",
++ size: 0x0, /* It will be set at probe function */
++ offset: MTDPART_OFS_APPEND,/* Append after fs section */
++ mask_flags: MTD_WRITEABLE /* force read-only */
++ }
++};
++
++#define PART_NUM ARRAY_SIZE(partition_info)
++
++/* MHN_OBM_V2 is related to BBT in MOBM V2
++ * MHN_OBM_V3 is related to BBT in MOBM V3
++ */
++enum {
++ MHN_OBM_NULL = 0,
++ MHN_OBM_V1,
++ MHN_OBM_V2,
++ MHN_OBM_V3,
++ MHN_OBM_INVAL
++} MHN_OBM_TYPE;
++
++static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
++static uint8_t scan_main_bbt_pattern[] = { 'p', 'x', 'a', '1' };
++static uint8_t scan_mirror_bbt_pattern[] = { '0', 'a', 'x', 'p' };
++
++static struct nand_bbt_descr monahans_bbt_default = {
++ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
++ | NAND_BBT_2BIT | NAND_BBT_VERSION,
++ .maxblocks = 2,
++ .len = 2,
++ .offs = 0,
++ .pattern = scan_ff_pattern,
++};
++
++static struct nand_bbt_descr monahans_bbt_main = {
++ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
++ | NAND_BBT_2BIT | NAND_BBT_VERSION,
++ .veroffs = 6,
++ .maxblocks = 2,
++ .offs = 2,
++ .len = 4,
++ .pattern = scan_main_bbt_pattern,
++};
++
++static struct nand_bbt_descr monahans_bbt_mirror = {
++ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
++ | NAND_BBT_2BIT | NAND_BBT_VERSION,
++ .veroffs = 6,
++ .maxblocks = 2,
++ .offs = 2,
++ .len = 4,
++ .pattern = scan_mirror_bbt_pattern,
++};
++
++#if 0
++static struct nand_bbt_descr monahans_bbt_main = {
++ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
++ | NAND_BBT_2BIT | NAND_BBT_VERSION,
++ .veroffs = 2,
++ .maxblocks = 2,
++ .offs = 0x0,
++ .len = 2,
++ .pattern = scan_ff_pattern
++};
++static struct nand_bbt_descr monahans_bbt_mirror = {
++ .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
++ | NAND_BBT_2BIT | NAND_BBT_VERSION,
++ .veroffs = 2,
++ .maxblocks = 2,
++ .offs = 0x0,
++ .len = 2,
++ .pattern = scan_ff_pattern
++};
++#endif
++
++static struct nand_ecclayout monahans_lb_nand_oob = {
++ .eccbytes = 24,
++ .eccpos = {
++ 40, 41, 42, 43, 44, 45, 46, 47,
++ 48, 49, 50, 51, 52, 53, 54, 55,
++ 56, 57, 58, 59, 60, 61, 62, 63},
++ .oobfree = { {2, 38} }
++};
++
++/*
++ * Monahans OOB size is only 8 bytes, and the rest 8 bytes is controlled by
++ * hardware for ECC. We construct virutal ECC buffer. Acutally, ECC is 6 bytes
++ * and the remain 2 bytes are reserved.
++ */
++static struct nand_ecclayout monahans_sb_nand_oob = {
++ .eccbytes = 6,
++ .eccpos = {8, 9, 10, 11, 12, 13 },
++ .oobfree = { {2, 6} }
++};
++
++
++static inline int is_buf_blank(u8 * buf, int size)
++{
++ int i = 0;
++ while(i < size) {
++ if (*((unsigned long *)(buf + i)) != 0xFFFFFFFF)
++ return 0;
++ i += 4;
++ }
++ if (i > size) {
++ i -= 4;
++ while( i < size) {
++ if(*(buf + i) != 0xFF)
++ return 0;
++ i++;
++ }
++ }
++ return 1;
++}
++
++static void print_buf(char *buf, int num)
++{
++ int i = 0;
++
++ while (i < num) {
++ printk(KERN_ERR "0x%08x: %02x %02x %02x %02x %02x %02x %02x"
++ " %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
++ (unsigned int) (i), buf[i], buf[i+1], buf[i+2],
++ buf[i+3], buf[i+4], buf[i+5], buf[i+6], buf[i+7],
++ buf[i+8], buf[i+9], buf[i+10],buf[i+11], buf[i+12],
++ buf[i+13], buf[i+14], buf[i+15]);
++ i += 16;
++ }
++}
++
++static int inline enable_dfc_dma(struct dfc_context *context, int enable)
++{
++ int ret = dfc_mode.enable_dma;
++ unsigned long ndcr;
++
++ if (!enable) {
++ ndcr = dfc_read(context, DFC_NDCR);
++ ndcr &= ~NDCR_DMA_EN;
++ dfc_write(context, DFC_NDCR, ndcr);
++ dfc_mode.enable_dma = 0;
++ } else {
++ ndcr = dfc_read(context, DFC_NDCR);
++ ndcr |= NDCR_DMA_EN;
++ dfc_write(context, DFC_NDCR, ndcr);
++ dfc_mode.enable_dma = 1;
++ }
++ return ret;
++}
++
++
++static void inline dump_info(struct monahans_dfc_info *info)
++{
++ if (!info)
++ return;
++
++ printk(KERN_ERR "cmd:0x%x; addr:0x%x; retcode:%d; state:%d \n",
++ info->cmd, info->addr, info->retcode, info->state);
++}
++
++static void inline enable_hw_ecc(struct dfc_context* context, int enable)
++{
++ unsigned long ndcr;
++
++ if (!enable) {
++ ndcr = dfc_read(context, DFC_NDCR);
++ ndcr &= ~NDCR_ECC_EN;
++ dfc_write(context, DFC_NDCR, ndcr);
++ dfc_mode.enable_ecc = 0;
++ }
++ else {
++ ndcr = dfc_read(context, DFC_NDCR);
++ ndcr |= NDCR_ECC_EN;
++ dfc_write(context, DFC_NDCR, ndcr);
++ dfc_mode.enable_ecc = 1;
++ }
++}
++
++/*
++ * Now, we are not sure that the NDSR_RDY mean the flash is ready.
++ * Need more test.
++ */
++static int monahans_df_dev_ready(struct mtd_info *mtd)
++{
++ struct monahans_dfc_info *info = (struct monahans_dfc_info *)
++ (((struct nand_chip *)(mtd->priv))->priv);
++
++ struct dfc_context* context = info->context;
++
++ return ((dfc_read(context, DFC_NDSR) & NDSR_RDY));
++}
++
++/* each read, we can only read 4bytes from NDDB, we must buffer it */
++static u_char monahans_df_read_byte(struct mtd_info *mtd)
++{
++ char retval = 0xFF;
++ struct monahans_dfc_info *info = (struct monahans_dfc_info *)
++ (((struct nand_chip *)(mtd->priv))->priv);
++
++ if (info->column < info->buf_count) {
++ /* Has just send a new command? */
++ retval = info->data_buf[info->column++];
++ }
++ return retval;
++}
++
++static void monahans_df_write_byte(struct mtd_info *mtd, u8 byte)
++{
++ struct monahans_dfc_info *info = (struct monahans_dfc_info *)
++ (((struct nand_chip *)(mtd->priv))->priv);
++ info->data_buf[info->column++] = byte;
++}
++
++static u16 monahans_df_read_word(struct mtd_info *mtd)
++{
++ u16 retval = 0xFFFF;
++ struct monahans_dfc_info *info = (struct monahans_dfc_info *)
++ (((struct nand_chip *)(mtd->priv))->priv);
++
++ if (!(info->column & 0x01) && info->column < info->buf_count) {
++ retval = *((u16 *)(info->data_buf+info->column));
++ info->column += 2;
++ }
++ return retval;
++}
++
++static void monahans_df_write_word(struct mtd_info *mtd, u16 word)
++{
++ struct monahans_dfc_info *info = (struct monahans_dfc_info *)
++ (((struct nand_chip *)(mtd->priv))->priv);
++
++ if (!(info->column & 0x01) && info->column < info->buf_count) {
++ *((u16 *)(info->data_buf+info->column)) = word;
++ info->column += 2;
++ }
++}
++
++static void monahans_df_read_buf(struct mtd_info *mtd, u_char *buf, int len)
++{
++ struct monahans_dfc_info *info = (struct monahans_dfc_info *)
++ (((struct nand_chip *)(mtd->priv))->priv);
++ int real_len = min((unsigned int)len, info->buf_count - info->column);
++
++ memcpy(buf, info->data_buf + info->column, real_len);
++ info->column += real_len;
++}
++
++static void monahans_df_write_buf(struct mtd_info *mtd,
++ const u_char *buf, int len)
++{
++ struct monahans_dfc_info *info = (struct monahans_dfc_info *)
++ (((struct nand_chip *)(mtd->priv))->priv);
++ int real_len = min((unsigned int)len, info->buf_count - info->column);
++
++ memcpy(info->data_buf + info->column, buf, real_len);
++ info->column += real_len;
++}
++
++static int monahans_df_verify_buf(struct mtd_info *mtd,
++ const u_char *buf, int len)
++{
++ return 0;
++}
++
++#ifdef CONFIG_MTD_NAND_MONAHANS_DMA
++static void monahans_dfc_cmd_dma_irq(int channel, void *data,
++ struct pt_regs *regs)
++{
++ unsigned int dcsr;
++ struct monahans_dfc_info *info = (struct monahans_dfc_info *)data;
++ struct dfc_context* context = info->context;
++ struct dfc_mode* dfc_mode = context->dfc_mode;
++ unsigned int intm;
++
++ dcsr = DCSR(channel);
++ DCSR(channel) = dcsr;
++
++ intm = (dfc_mode->chip_select) ? \
++ (NDSR_CS1_BBD | NDSR_CS1_CMDD) : (NDSR_CS0_BBD | NDSR_CS0_CMDD);
++
++ D1(printk("cmd dma interrupt, channel:%d, DCSR:0x%08x\n", \
++ channel, dcsr));
++
++ if (dcsr & DCSR_BUSERR) {
++ info->retcode = ERR_DMABUSERR;
++ complete(&info->cmd_complete);
++ } else {
++ if ((info->cmd == NAND_CMD_READ0) ||
++ (info->cmd == NAND_CMD_READOOB)|| \
++ (info->cmd == NAND_CMD_READID) || \
++ (info->cmd == NAND_CMD_STATUS)) {
++ dfc_enable_int(context, NDSR_RDDREQ | NDSR_DBERR);
++ } else if (info->cmd == NAND_CMD_PAGEPROG)
++ dfc_enable_int(context, NDSR_WRDREQ);
++ else if (info->cmd == NAND_CMD_ERASE1)
++ dfc_enable_int(context, intm);
++ }
++
++ return;
++}
++
++
++static void monahans_dfc_data_dma_irq(int channel, void *data,
++ struct pt_regs *regs)
++{
++ unsigned int dcsr, intm;
++ struct monahans_dfc_info *info = (struct monahans_dfc_info *)data;
++ struct dfc_context* context = info->context;
++ struct dfc_mode* dfc_mode = context->dfc_mode;
++
++ dcsr = DCSR(channel);
++ DCSR(channel) = dcsr;
++
++ intm = (dfc_mode->chip_select) ? \
++ (NDSR_CS1_BBD | NDSR_CS1_CMDD) : (NDSR_CS0_BBD | NDSR_CS0_CMDD);
++
++ D1(printk("data dma interrupt, channel:%d, DCSR:0x%08x\n",
++ channel, dcsr));
++ if (dcsr & DCSR_BUSERR) {
++ info->retcode = ERR_DMABUSERR;
++ complete(&info->cmd_complete);
++ }
++
++ if (info->cmd == NAND_CMD_PAGEPROG) {
++ /* DMA interrupt may be interrupted by other IRQs*/
++ info->state = STATE_DMA_DONE;
++ dfc_enable_int(context, intm);
++ } else {
++ info->state = STATE_READY;
++ complete(&info->cmd_complete);
++ }
++
++}
++#endif
++
++static irqreturn_t monahans_dfc_irq(int irq, void *devid)
++{
++ unsigned int status, event, intm, cmd;
++ struct monahans_dfc_info *info = (struct monahans_dfc_info *)devid;
++ struct dfc_context* context = info->context;
++ struct dfc_mode* dfc_mode = context->dfc_mode;
++
++ intm = (dfc_mode->chip_select) ? \
++ (NDSR_CS1_BBD | NDSR_CS1_CMDD) : (NDSR_CS0_BBD | NDSR_CS0_CMDD);
++ event = (dfc_mode->chip_select) ? \
++ (NDSR_CS1_BBD | NDSR_CS1_CMDD) : (NDSR_CS0_BBD | NDSR_CS0_CMDD);
++
++ status = dfc_read(context, DFC_NDSR);
++ D1(printk("DFC irq, NDSR:0x%x\n", status));
++ if (status & (NDSR_RDDREQ | NDSR_DBERR)) {
++ if (status & NDSR_DBERR) {
++ info->retcode = ERR_DBERR;
++ }
++
++ dfc_disable_int(context, NDSR_RDDREQ | NDSR_DBERR);
++ dfc_clear_int(context, NDSR_RDDREQ | NDSR_DBERR);
++ if (info->cmd == NAND_CMD_READID)
++ cmd = context->flash_info->read_id;
++ else if (info->cmd == NAND_CMD_STATUS)
++ cmd = context->flash_info->read_status;
++ else if (info->cmd == NAND_CMD_READ0 ||
++ info->cmd == NAND_CMD_READOOB)
++ cmd = context->flash_info->read1;
++ else {
++ printk(KERN_ERR "No according command:0x%x happens\n",
++ info->cmd);
++ goto out;
++ }
++#ifdef CONFIG_MTD_NAND_MONAHANS_DMA
++ info->state = STATE_DMA_TRANSFER;
++ dfc_start_data_dma(context,
++ (struct pxa_dma_desc*)info->data_desc_addr);
++#else
++ info->state = STATE_DATA_TRANSFER;
++ complete(&info->cmd_complete);
++#endif
++ } else if (status & NDSR_WRDREQ) {
++ dfc_disable_int(context, NDSR_WRDREQ);
++ dfc_clear_int(context, NDSR_WRDREQ);
++#ifdef CONFIG_MTD_NAND_MONAHANS_DMA
++ info->state = STATE_DMA_TRANSFER;
++ dfc_start_data_dma(context,
++ (struct pxa_dma_desc*)info->data_desc_addr);
++#else
++ info->state = STATE_DATA_TRANSFER;
++ complete(&info->cmd_complete);
++#endif
++ } else if (status & event) {
++ if (status & NDSR_CS0_BBD) {
++ info->retcode = ERR_BBERR;
++ }
++
++ dfc_disable_int(context, intm);
++ dfc_clear_int(context, event);
++ info->state = STATE_READY;
++ complete(&info->cmd_complete);
++ }
++out:
++ return IRQ_HANDLED;
++}
++
++static int dfc_send_command(struct mtd_info *mtd, unsigned int cmd,
++ unsigned int addr, unsigned int num_pages,
++ unsigned int event)
++{
++
++ struct monahans_dfc_info *info = (struct monahans_dfc_info *)
++ (((struct nand_chip *)(mtd->priv))->priv);
++ struct dfc_context* context = info->context;
++ int status;
++ int ret;
++
++ D1(printk("ready send command, cmd:0x%x, at address:0x%x,"
++ " num_pages:%d, wait event:0x%x\n", cmd, addr, num_pages, event));
++
++ info->state = STATE_CMD_SEND;
++#ifdef CONFIG_MTD_NAND_MONAHANS_DMA
++ status = dfc_setup_cmd_dma(context, cmd, addr, num_pages,
++ (uint32_t *)info->cmd_buf, info->cmd_buf_addr,
++ DDADR_STOP, DCMD_ENDIRQEN, info->cmd_desc);
++#else
++ status = dfc_send_cmd(context, cmd, addr, num_pages);
++#endif
++ if (status) {
++ info->retcode = ERR_SENDCMD;
++ dfc_stop(context);
++ udelay(20);
++ printk(KERN_ERR "fail send command\n");
++ return info->retcode;
++ }
++ info->state = STATE_CMD_HANDLE;
++#ifdef CONFIG_MTD_NAND_MONAHANS_DMA
++ dfc_setup_data_dma(context, cmd, info->data_buf_addr,
++ DDADR_STOP, DCMD_ENDIRQEN, info->data_desc);
++ dfc_start_cmd_dma(context, (struct pxa_dma_desc*)info->cmd_desc_addr);
++#endif
++#ifndef CONFIG_MTD_NAND_MONAHANS_DMA
++ dfc_enable_int(context, event);
++#endif
++ ret = wait_for_completion_timeout(&info->cmd_complete, 2*HZ);
++ if (!ret){
++ printk(KERN_ERR "Command time out\n");
++ dump_info(info);
++ }
++ D1(printk("command return, cmd:0x%x, retcode:%d\n",
++ info->cmd, info->retcode));
++ return 0;
++}
++
++static void monahans_df_command(struct mtd_info *mtd, unsigned command,
++ int column, int page_addr )
++{
++ struct nand_chip *this = (struct nand_chip *)(mtd->priv);
++ struct monahans_dfc_info *info =
++ (struct monahans_dfc_info *)(this->priv);
++ struct dfc_context *context = info->context;
++ struct dfc_flash_info * flash_info = context->flash_info;
++ int ret, pages_shift;
++ int status;
++#ifndef CONFIG_MTD_NAND_MONAHANS_DMA
++ int datasize;
++ int paddingsize;
++#endif
++ unsigned int to;
++
++ D1(printk("command:0x%x at address:0x%x, column:0x%x\n",
++ command, page_addr, column));
++
++ if (info->state != STATE_READY) {
++ printk(KERN_ERR "CHIP is not ready.\n");
++ dump_info(info);
++ info->retcode = ERR_BUSY;
++ return;
++ }
++ info->retcode = ERR_NONE;
++ pages_shift = this->phys_erase_shift - this->page_shift;
++ if (info->table_init) {
++ to = search_rel_block((page_addr >> pages_shift), mtd);
++ if (to) {
++ page_addr = (to << pages_shift) | (page_addr
++ & ((1 << pages_shift) - 1));
++ }
++ }
++
++ switch ( command ) {
++ case NAND_CMD_READOOB:
++ /*
++ * DFC has mark the last 8 bytes OOB data if HARDEARE_ECC is
++ * enabled. We must first disable the HARDWARE_ECC for getting
++ * all the 16 bytes OOB
++ */
++ enable_hw_ecc(context, 0);
++ info->buf_count = mtd->writesize + mtd->oobsize;
++ info->column = mtd->writesize + column;
++ info->cmd = command;
++ info->addr = page_addr << this->page_shift;
++ ret = dfc_send_command(mtd, flash_info->read1, info->addr,
++ 1, NDSR_RDDREQ | NDSR_DBERR);
++#ifndef CONFIG_MTD_NAND_MONAHANS_DMA
++ dfc_get_pattern(info->context, flash_info->read1, &datasize,
++ &paddingsize);
++ dfc_read_fifo_partial(info->context, info->data_buf,
++ min(info->buf_count, datasize), datasize);
++ info->state = STATE_READY;
++#endif
++ /* We only are OOB, so if the data has error, does not matter */
++ if (info->retcode == ERR_DBERR)
++ info->retcode = ERR_NONE;
++ enable_hw_ecc(context, 1);
++ break;
++
++ case NAND_CMD_READ0:
++ enable_hw_ecc(context, 1);
++ info->column = column;
++ info->cmd = command;
++ info->buf_count = mtd->writesize + mtd->oobsize;
++ memset(info->data_buf, 0xFF, info->buf_count);
++ info->addr = page_addr << this->page_shift;
++
++ ret = dfc_send_command(mtd, flash_info->read1, info->addr,
++ 1, NDSR_RDDREQ | NDSR_DBERR);
++#ifndef CONFIG_MTD_NAND_MONAHANS_DMA
++ dfc_get_pattern(info->context, flash_info->read1, &datasize,
++ &paddingsize);
++ dfc_read_fifo_partial(info->context, info->data_buf,
++ min(info->buf_count, datasize), datasize);
++ info->state = STATE_READY;
++#endif
++ /* When the data buf is blank, the DFC will report DB error */
++ if (info->retcode == ERR_DBERR && is_buf_blank(info->data_buf,
++ mtd->writesize))
++ info->retcode = ERR_NONE;
++
++ if (info->retcode == ERR_DBERR) {
++ printk(KERN_ERR "DB error at address 0x%x\n",
++ info->addr);
++ print_buf(info->data_buf, info->buf_count);
++ }
++ break;
++ case NAND_CMD_SEQIN:
++ /* Write only OOB? */
++
++ info->cmd = command;
++ if (column >= mtd->writesize) {
++ info->buf_count = mtd->writesize + mtd->oobsize;
++ enable_hw_ecc(context, 0);
++ } else {
++ info->buf_count = mtd->writesize + mtd->oobsize;
++ enable_hw_ecc(context, 1);
++ }
++ memset(info->data_buf, 0xFF, mtd->writesize + mtd->oobsize);
++ info->column = column;
++ info->addr = page_addr << this->page_shift;
++ break;
++ case NAND_CMD_PAGEPROG:
++ /* prevois command is NAND_CMD_SEIN ?*/
++ if (info->cmd != NAND_CMD_SEQIN) {
++ info->cmd = command;
++ info->retcode = ERR_SENDCMD;
++ printk(KERN_ERR "Monahans NAND device: "
++ "No NAND_CMD_SEQIN executed before.\n");
++ enable_hw_ecc(context, 1);
++ break;
++ }
++ info->cmd = command;
++ ret = dfc_send_command(mtd, flash_info->program, info->addr,
++ 1, NDSR_WRDREQ);
++
++#ifndef CONFIG_MTD_NAND_MONAHANS_DMA
++ if (ret != 0)
++ break;
++
++ dfc_get_pattern(info->context, flash_info->program, &datasize,
++ &paddingsize);
++ dfc_write_fifo_partial(info->context, info->data_buf, datasize,
++ datasize);
++
++ if (info->context->dfc_mode->chip_select)
++ dfc_enable_int(info->context,
++ NDSR_CS1_BBD | NDSR_CS1_CMDD);
++ else
++ dfc_enable_int(info->context,
++ NDSR_CS0_BBD | NDSR_CS0_CMDD);
++
++ ret = wait_for_completion_timeout(&info->cmd_complete, 2*HZ);
++ if (!ret){
++ printk(KERN_ERR "Programm Command time out\n");
++ dump_info(info);
++ }
++
++ if (info->retcode == ERR_BBERR) {
++ mtd->block_markbad(mtd, info->addr);
++ }
++#endif
++ break;
++ case NAND_CMD_ERASE1:
++ info->cmd = command;
++ info->addr = (page_addr >> pages_shift) << this->phys_erase_shift;
++
++ if (info->context->dfc_mode->chip_select)
++ ret = dfc_send_command(mtd, flash_info->erase,
++ info->addr, 0, NDSR_CS1_BBD | NDSR_CS1_CMDD);
++ else
++ ret = dfc_send_command(mtd, flash_info->erase,
++ info->addr, 0, NDSR_CS0_BBD | NDSR_CS0_CMDD);
++
++ if (info->retcode == ERR_BBERR) {
++ mtd->block_markbad(mtd, info->addr);
++ }
++ break;
++ case NAND_CMD_ERASE2:
++ break;
++ case NAND_CMD_READID:
++ info->cmd = command;
++ info->buf_count = flash_info->read_id_bytes;
++ info->column = 0;
++ info->addr = 0xFFFFFFFF;
++ ret = dfc_send_command(mtd, flash_info->read_id, info->addr,
++ 0, NDSR_RDDREQ);
++#ifndef CONFIG_MTD_NAND_MONAHANS_DMA
++ dfc_get_pattern(info->context, flash_info->read_id, &datasize,
++ &paddingsize);
++ dfc_read_fifo_partial(info->context, info->data_buf,
++ info->buf_count, datasize);
++ info->state = STATE_READY;
++#endif
++ D1(printk("ReadID, [1]:0x%x, [2]:0x%x\n",
++ info->data_buf[0], info->data_buf[1]));
++ break;
++ case NAND_CMD_STATUS:
++ info->cmd = command;
++ info->buf_count = 1;
++ info->column = 0;
++ info->addr = 0xFFFFFFFF;
++ ret = dfc_send_command(mtd, flash_info->read_status,
++ info->addr, 0, NDSR_RDDREQ);
++#ifndef CONFIG_MTD_NAND_MONAHANS_DMA
++ dfc_get_pattern(info->context, flash_info->read_status,
++ &datasize, &paddingsize);
++ dfc_read_fifo_partial(info->context, info->data_buf,
++ info->buf_count, datasize);
++ info->state = STATE_READY;
++#endif
++ break;
++
++ case NAND_CMD_RESET:
++ status = dfc_reset_flash(&dfc_context);
++ if (status) {
++ printk(KERN_WARNING "Monahans NAND device:"
++ "NAND_CMD_RESET error\n");
++ }
++ break;
++ default:
++ printk(KERN_WARNING "Monahans NAND device:"
++ "Non-support the command.\n");
++ break;
++ }
++
++ if (info->retcode != ERR_NONE)
++ dfc_stop(info->context);
++}
++
++static void monahans_df_select_chip(struct mtd_info *mtd, int chip)
++{
++ struct monahans_dfc_info *info = (struct monahans_dfc_info *)
++ (((struct nand_chip *)(mtd->priv))->priv);
++
++ if (chip <= MAX_CHIP)
++ info->context->dfc_mode->chip_select = chip;
++ else
++ printk(KERN_ERR "Monahans NAND device:"
++ "not select the NAND chips!\n");
++}
++
++static int monahans_df_waitfunc(struct mtd_info *mtd,
++ struct nand_chip *this)
++{
++ struct monahans_dfc_info *info = (struct monahans_dfc_info *)
++ (((struct nand_chip *)(mtd->priv))->priv);
++
++ /* monahans_df_send_command has waited for command complete */
++ if (this->state == FL_WRITING || this->state == FL_ERASING) {
++ if (info->retcode == ERR_NONE)
++ return 0;
++ else {
++ /*
++ * any error make it return 0x01 which will tell
++ * the caller the erase and write fail
++ */
++ return 0x01;
++ }
++ }
++
++ return 0;
++}
++
++static int monahans_df_calculate_ecc(struct mtd_info *mtd,
++ const u_char *dat, u_char *ecc_code)
++{
++ return 0;
++}
++
++static int monahans_df_correct_data(struct mtd_info *mtd,
++ u_char *dat, u_char *read_ecc, u_char *calc_ecc)
++{
++ struct monahans_dfc_info *info = (struct monahans_dfc_info *)
++ (((struct nand_chip *)(mtd->priv))->priv);
++
++ /*
++ * Any error include ERR_SEND_CMD, ERR_DBERR, ERR_BUSERR, we
++ * consider it as a ecc error which will tell the caller the
++ * read fail We have distinguish all the errors, but the
++ * nand_read_ecc only check this function return value
++ */
++ if (info->retcode != ERR_NONE)
++ return -1;
++
++ return 0;
++}
++
++static void monahans_df_enable_hwecc(struct mtd_info *mtd, int mode)
++{
++ return;
++}
++
++/*
++ * The relocation table management is different between MOBM V2 and V3.
++ *
++ * MOBM V2 is applied on chips taped out before MhnLV A0.
++ * MOBM V3 is applied on chips taped out after MhnLV A0. It's also applied
++ * on MhnLV A0.
++ */
++static int calc_obm_ver(void)
++{
++ unsigned int cpuid;
++ /* read CPU ID */
++ __asm__ (
++ "mrc p15, 0, %0, c0, c0, 0\n"
++ : "=r" (cpuid)
++ );
++ /* It's not xscale chip. */
++ if ((cpuid & 0xFFFF0000) != 0x69050000)
++ return MHN_OBM_INVAL;
++ /* It's MhnP Ax */
++ if ((cpuid & 0x0000FFF0) == 0x00006420)
++ return MHN_OBM_V2;
++ /* It's MhnP Bx */
++ if ((cpuid & 0x0000FFF0) == 0x00006820) {
++ if ((cpuid & 0x0F) <= 5)
++ return MHN_OBM_V2;
++ else
++ return MHN_OBM_V3;
++ }
++ /* It's MhnL Ax */
++ if ((cpuid & 0x0000FFF0) == 0x00006880) {
++ if ((cpuid & 0x0F) == 0)
++ return MHN_OBM_V2;
++ else
++ return MHN_OBM_V3;
++ }
++ /* It's MhnLV Ax */
++ if ((cpuid & 0x0000FFF0) == 0x00006890)
++ return MHN_OBM_V3;
++ return MHN_OBM_INVAL;
++}
++
++
++/*
++ * MOBM maintains a relocation table. It's used to replace bad blocks.
++ * If block A is bad, it will use block B instead.
++ * There're 127 relocated blocks. All of them reside in the bottom of NAND
++ * flash. So they're reserved and can't be calculated in mtd size and chip
++ * size.
++ */
++static int read_reloc_table(struct mtd_info *mtd)
++{
++ struct nand_chip *this = NULL;
++ struct monahans_dfc_info *info = NULL;
++ struct dfc_context *context = NULL;
++ struct reloc_table *table = NULL;
++ int page, maxslot;
++ int obm, valid;
++
++ obm = calc_obm_ver();
++ this = (struct nand_chip *)(mtd->priv);
++ info = (struct monahans_dfc_info *)(this->priv);
++ context = info->context;
++
++ mtd->size -= (NAND_RELOC_MAX * mtd->erasesize);
++ this->chipsize -= (NAND_RELOC_MAX << this->phys_erase_shift);
++ page = (1 << (this->phys_erase_shift - this->page_shift)) - 1;
++
++ this->select_chip(mtd, 0);
++ valid = 0;
++ if (obm == MHN_OBM_V2) {
++ /* On MOBM V2, the relocation table resides in the last page
++ * of the first block.
++ */
++ memset(info->data_buf, 0, BUFLEN);
++ monahans_df_command(mtd, NAND_CMD_READ0, 0, page);
++ memcpy(((unsigned char *)&(info->table)), info->data_buf,
++ sizeof(struct reloc_table));
++ if (info->table.header == NAND_RELOC_HEADER)
++ valid = 1;
++ } else if (obm == MHN_OBM_V3) {
++ /* On MOBM V3, there're several relocation tables in the first
++ * block.
++ * When new bad blocks are found, a new relocation table will
++ * be generated and written back to the first block. But the
++ * original relocation table won't be erased. Even if the new
++ * relocation table is written wrong, system can still find an
++ * old one.
++ * One page contains one slot.
++ */
++ maxslot = 1 << (this->phys_erase_shift - this->page_shift);
++ page = maxslot - MAX_BBT_SLOTS;
++ for (; page < maxslot; page++) {
++ monahans_df_command(mtd, NAND_CMD_READ0, 0, page);
++ table = (struct reloc_table *)info->data_buf;
++ if (info->retcode == ERR_NONE) {
++ if (table->header != NAND_RELOC_HEADER) {
++ continue;
++ } else {
++ memcpy(((unsigned char *)&(info->table)),
++ table, sizeof(struct reloc_table));
++ valid = 1;
++ break;
++ }
++ }
++ }
++
++ } else {
++ printk(KERN_ERR "The version of MOBM isn't supported\n");
++ }
++ if (valid) {
++ memcpy(((unsigned char *)&(info->table)), info->data_buf,
++ sizeof(struct reloc_table));
++ printk(KERN_DEBUG "relocation table at page:%d\n", page);
++ PRINT_BUF((unsigned char *)&(info->table),
++ sizeof(struct reloc_table));
++ info->table_init = 1;
++ } else {
++ /* There should be a valid relocation table slot at least. */
++ printk(KERN_ERR "NO VALID relocation table can be \
++ recognized\n");
++ printk(KERN_ERR "CAUTION: It may cause unpredicated error\n");
++ printk(KERN_ERR "Please re-initialize the NAND flash.\n");
++ memset((unsigned char *)&(info->table), 0,
++ sizeof(struct reloc_table));
++ info->table_init = 0;
++ return -EINVAL;
++ }
++ return 0;
++}
++
++/* add the relocation entry into the relocation table
++ * It's valid on MOBM V3.
++ * If the relocated block is bad, an new entry will be added into the
++ * bottom of the relocation table.
++ */
++static int update_rel_table(struct mtd_info *mtd, int block)
++{
++ struct nand_chip *this = NULL;
++ struct monahans_dfc_info *info = NULL;
++ struct reloc_table *table = NULL;
++ int obm, reloc_block;
++
++ this = (struct nand_chip *)(mtd->priv);
++ info = (struct monahans_dfc_info *)(this->priv);
++ obm = calc_obm_ver();
++ if (obm == MHN_OBM_V3) {
++ table = &info->table;
++ if (info->table_init == 0) {
++ printk(KERN_ERR "Error: the initial relocation \
++ table can't be read\n");
++ memset(table, 0, sizeof(struct reloc_table));
++ table->header = NAND_RELOC_HEADER;
++ info->table_init = 1;
++ }
++ if (table->total == 0) {
++ /* Point to the first relocated block.
++ * It resides in the last block of flash.
++ * the relocation entry has calculated in
++ * chipsize
++ */
++ reloc_block = (this->chipsize
++ >> this->phys_erase_shift)
++ + NAND_RELOC_MAX - 1;
++ } else if (table->total < NAND_RELOC_MAX) {
++ reloc_block = table->reloc[table->total - 1].to - 1;
++ } else {
++ printk(KERN_ERR "Relocation table exceed max number, \
++ cannot mark block 0x%x as bad block\n", block);
++ return -ENOSPC;
++ }
++ /* Make sure that reloc_block is pointing to a valid block */
++ for (; ; reloc_block--) {
++ /* The relocate table is full */
++ if (reloc_block < (this->chipsize
++ >> this->phys_erase_shift))
++ return -ENOSPC;
++ this->cmdfunc(mtd, NAND_CMD_ERASE1, 0, reloc_block
++ << (this->phys_erase_shift
++ - this->page_shift));
++ if (info->retcode == ERR_NONE)
++ break;
++ }
++ /* Create the relocated block information in the table */
++ table->reloc[table->total].from = block;
++ table->reloc[table->total].to = reloc_block;
++ table->total++;
++ }
++ return 0;
++}
++
++/* Write the relocation table back to device, if there's room. */
++static int sync_rel_table(struct mtd_info *mtd, int *idx)
++{
++ struct nand_chip *this = NULL;
++ struct monahans_dfc_info *info = NULL;
++ int obm, start_page, len;
++
++ if (*idx >= MAX_BBT_SLOTS) {
++ printk(KERN_ERR "Can't write relocation table to device \
++ any more.\n");
++ return -1;
++ }
++ if (*idx < 0) {
++ printk(KERN_ERR "Wrong Slot is specified.\n");
++ return -1;
++ }
++ this = (struct nand_chip *)(mtd->priv);
++ info = (struct monahans_dfc_info *)(this->priv);
++ len = 4;
++ len += info->table.total << 2;
++ obm = calc_obm_ver();
++ if (obm == MHN_OBM_V3) {
++ /* write to device */
++ start_page = 1 << (this->phys_erase_shift - this->page_shift);
++ start_page = start_page - 1 - *idx;
++ memset(&(info->data_buf), 0xFF, BUFLEN);
++ memcpy(&(info->data_buf), &(info->table), len);
++
++ printk(KERN_DEBUG "DUMP relocation table before write. \
++ page:0x%x\n", start_page);
++ monahans_df_command(mtd, NAND_CMD_SEQIN, 0, start_page);
++ monahans_df_command(mtd, NAND_CMD_PAGEPROG, 0, start_page);
++ /* write to idx */
++ (*idx)++;
++ /* dump it */
++ memset(&(info->data_buf), 0, BUFLEN);
++ monahans_df_command(mtd, NAND_CMD_READOOB, 0, start_page);
++ PRINT_BUF(info->data_buf, len);
++ }
++ return 0;
++}
++
++
++/* Find the relocated block of the bad one.
++ * If it's a good block, return 0. Otherwise, return a relocated one.
++ * idx points to the next relocation entry
++ * If the relocated block is bad, an new entry will be added into the
++ * bottom of the relocation table.
++ */
++static unsigned short search_rel_block(int block, struct mtd_info *mtd)
++{
++ struct nand_chip *this = NULL;
++ struct monahans_dfc_info *info = NULL;
++ struct reloc_table *table = NULL;
++ int i, max, reloc_block = 0;
++
++ this = (struct nand_chip *)(mtd->priv);
++ info = (struct monahans_dfc_info *)(this->priv);
++ table = &(info->table);
++ if ((block <= 0) || (block > this->chipsize)
++ || (info->table_init == 0) || (table->total == 0))
++ return 0;
++ if (table->total > NAND_RELOC_MAX)
++ table->total = NAND_RELOC_MAX;
++ max = table->total;
++ for (i = 0; i < max; i++) {
++ if (block == table->reloc[i].from)
++ reloc_block = table->reloc[i].to;
++ }
++ return reloc_block;
++}
++
++/*
++ * Check whether the block is a bad one.
++ * At first, it will search the relocation table.
++ * If necessary, it will search the BBT. Because relocation table can only
++ * maintain limited record. If there're more bad blocks, they can't be
++ * recorded in relocation table. They can only be recorded in BBT.
++ */
++static int monahans_df_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
++{
++ struct nand_chip *this = NULL;
++ int page, block, reloc_block, chipnr, res = 0;
++ u16 bad;
++
++ /* At here, we only support one flash chip */
++ this = (struct nand_chip *)mtd->priv;
++ block = (int)(ofs >> this->phys_erase_shift);
++ /* search the block in the relocation table */
++ reloc_block = search_rel_block(block, mtd);
++ if (reloc_block) {
++ ofs = ((reloc_block << this->phys_erase_shift) |
++ (ofs & ((1 << this->phys_erase_shift) - 1)));
++ }
++
++ /* search BBT
++ * Maybe the relocation table is full, but some bad blocks aren't
++ * recordered in it.
++ * The below code are copied from nand_block_bad().
++ */
++ if (getchip) {
++ page = (int)(ofs >> this->page_shift);
++ chipnr = (int)(ofs >> this->chip_shift);
++
++ /* Select the NAND chips */
++ this->select_chip(mtd, chipnr);
++ } else
++ page = (int)ofs;
++
++ if (this->options & NAND_BUSWIDTH_16) {
++ this->cmdfunc(mtd, NAND_CMD_READOOB, this->badblockpos & 0xFE,
++ page & this->pagemask);
++ bad = cpu_to_le16(this->read_word(mtd));
++ if (this->badblockpos & 0x1)
++ bad >>= 1;
++ if ((bad & 0xFF) != 0xFF)
++ res = 1;
++ } else {
++ this->cmdfunc(mtd, NAND_CMD_READOOB, this->badblockpos,
++ page & this->pagemask);
++ if (this->read_byte(mtd) != 0xFF)
++ res = 1;
++ }
++
++ return res;
++}
++
++static int monahans_df_block_markbad(struct mtd_info *mtd, loff_t ofs)
++{
++ struct nand_chip *this = NULL;
++ struct monahans_dfc_info *info = NULL;
++ unsigned char buf[2] = {0, 0};
++ int block, reloc_block, page, ret;
++
++ this = (struct nand_chip *)mtd->priv;
++ info = (struct monahans_dfc_info *)(this->priv);
++ /* Get block number */
++ block = ((int)ofs) >> this->bbt_erase_shift;
++ ret = update_rel_table(mtd, block);
++ if (!ret) {
++ sync_rel_table(mtd, &(info->current_slot));
++ return 0;
++ } else {
++ reloc_block = search_rel_block(block, mtd);
++ if (reloc_block)
++ block = reloc_block;
++ if (this->bbt)
++ this->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
++ }
++
++ /* Do we have a flash based bad block table ? */
++ if (this->options & NAND_USE_FLASH_BBT)
++ return nand_update_bbt(mtd, ofs);
++
++ /* mark the bad block flag at the first two pages */
++ page = block << (this->phys_erase_shift - this->page_shift);
++ ofs = mtd->writesize + this->badblockpos;
++ this->cmdfunc(mtd, NAND_CMD_SEQIN, ofs, page);
++ this->write_buf(mtd, buf, 2);
++ this->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
++ page++;
++ this->cmdfunc(mtd, NAND_CMD_SEQIN, ofs, page);
++ this->write_buf(mtd, buf, 2);
++ this->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
++ return 0;
++}
++
++static int dump_bbt_flash(struct mtd_info *mtd)
++{
++ struct nand_chip *this = NULL;
++ struct monahans_dfc_info *info = NULL;
++ int block, page, totlen;
++
++ this = (struct nand_chip *)mtd->priv;
++ info = (struct monahans_dfc_info *)this->priv;
++ block = (this->chipsize >> this->phys_erase_shift) - 1;
++ totlen = (this->chipsize >> this->phys_erase_shift) >> 2;
++ printk(KERN_ERR "totlen:0x%x\n", totlen);
++ this->select_chip(mtd, 0);
++ if (this->bbt_td) {
++ printk(KERN_ERR "BBT page:0x%x\n", this->bbt_td->pages[0]);
++ page = this->bbt_td->pages[0];
++ if (this->bbt_td->pages[0] <= 0) {
++ page = block << (this->phys_erase_shift
++ - this->page_shift);
++ }
++ while (totlen > 0) {
++ printk(KERN_ERR "page:0x%x\n", page);
++ monahans_df_command(mtd, NAND_CMD_READ0, 0, page);
++ printk(KERN_ERR "read result:0x%x\n", info->retcode);
++ PRINT_BUF(info->data_buf, BUFLEN);
++ totlen -= (1 << this->page_shift);
++ page++;
++ }
++ }
++ if (this->bbt_md) {
++ printk(KERN_ERR "BBT page:0x%x\n", this->bbt_md->pages[0]);
++ page = this->bbt_md->pages[0];
++ if (this->bbt_td->pages[0] <= 0) {
++ page = block << (this->phys_erase_shift
++ - this->page_shift);
++ }
++ while (totlen > 0) {
++ printk(KERN_ERR "page:0x%x\n", page);
++ monahans_df_command(mtd, NAND_CMD_READ0, 0, page);
++ printk(KERN_ERR "read result:0x%x\n", info->retcode);
++ PRINT_BUF(info->data_buf, BUFLEN);
++ totlen -= (1 << this->page_shift);
++ page++;
++ }
++
++ }
++ return 0;
++}
++
++static int dump_bbt_mem(struct mtd_info *mtd)
++{
++ struct nand_chip *this = NULL;
++
++ this = (struct nand_chip *)mtd->priv;
++ PRINT_BUF(this->bbt, 225);
++ return 0;
++}
++
++static int monahans_df_scan_bbt(struct mtd_info *mtd)
++{
++ struct nand_chip *this = NULL;
++ int ret;
++
++ this = (struct nand_chip *)mtd->priv;
++ ret = read_reloc_table(mtd);
++ if (ret) {
++ printk(KERN_ERR "Failed to get relocation table\n");
++ printk(KERN_ERR "Try to build a new BBT. It may result \
++ unpredicated error.\n");
++ /* Create new memory based and flash based BBT */
++ }
++ nand_scan_bbt(mtd, &monahans_bbt_default);
++ //dump_bbt_flash(mtd);
++ dump_bbt_mem(mtd);
++ return 0;
++#if 0
++ /* Read flashed based BBT from device */
++ return (nand_scan_bbt(mtd, &monahans_bbt_main));
++#endif
++}
++
++
++static int monahans_df_probe(struct platform_device *pdev)
++{
++ struct nand_chip *this;
++ struct monahans_dfc_info *info;
++ int status = -1;
++ unsigned int data_buf_len;
++#ifdef CONFIG_MTD_NAND_MONAHANS_DMA
++ unsigned int buf_len;
++#endif
++ int i, ret = 0;
++
++ printk(KERN_ERR "Nand driver probe\n");
++
++ dfc_context.membase = ioremap_nocache(0x43100000, 0x100000);
++ if (!dfc_context.membase)
++ printk(KERN_ERR "Couldn't ioremap\n");
++
++ pxa_set_cken(CKEN_NAND, 1);
++
++ for (i = DFC_FLASH_NULL + 1; i < DFC_FLASH_END; i++)
++ {
++ uint32_t id;
++
++ status = dfc_init(&dfc_context, i);
++ if (status)
++ continue;
++ status = dfc_readid(&dfc_context, &id);
++ if (status)
++ continue;
++ printk(KERN_DEBUG "id:0x%x, chipid:0x%x\n",
++ id, dfc_context.flash_info->chip_id);
++ if (id == dfc_context.flash_info->chip_id)
++ break;
++ }
++
++ if(i == DFC_FLASH_END) {
++ printk(KERN_ALERT "Monahans NAND device:"
++ "Nand Flash initialize failure!\n");
++ ret = -ENXIO;
++ goto out;
++ }
++ flash_config = i;
++
++ monahans_mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip) +
++ sizeof(struct monahans_dfc_info) , GFP_KERNEL);
++ if (!monahans_mtd) {
++ printk (KERN_ERR "Monahans NAND device:"
++ "Unable to allocate NAND MTD device structure.\n");
++ ret = -ENOMEM;
++ goto out;
++ }
++
++ /* Get pointer to private data */
++ this = (struct nand_chip *)((void *)monahans_mtd + sizeof(struct mtd_info));
++ info = (struct monahans_dfc_info *)((void *)this + sizeof(struct nand_chip));
++ dfc_context.mtd = monahans_mtd;
++
++ monahans_mtd->priv = this;
++ this->priv = info;
++ data_buf_len = dfc_context.flash_info->page_size +
++ dfc_context.flash_info->oob_size;
++ info->state = STATE_READY;
++ init_completion(&info->cmd_complete);
++ info->table_init = 0;
++ memset(&info->table, 0x0, sizeof(struct reloc_table));
++ printk(KERN_DEBUG "%s: this->controller: 0x%x, &this->controller: 0x%x\n",__func__, (unsigned int)this->controller, (unsigned int)&(this->controller));
++#ifdef CONFIG_MTD_NAND_MONAHANS_DMA
++ info->dma_mask = 0xffffffffUL;
++
++ dev->dma_mask = &info->dma_mask;
++ dev->coherent_dma_mask = 0xffffffffUL;
++
++ /* alloc dma data buffer for data
++ * buffer + 2*descriptor + command buffer
++ */
++ buf_len = ALIGN(2*sizeof(struct pxa_dma_desc), 32) +
++ ALIGN(data_buf_len, 32) + ALIGN(NAND_CMD_DMA_LEN, 32);
++
++ printk(KERN_INFO "Try to allocate dma buffer(len:%d)"
++ "for data buffer + 2*descriptor + command buffer\n", buf_len);
++ info->data_desc = (struct pxa_dma_desc*)dma_alloc_writecombine(dev,
++ buf_len, &info->data_desc_addr, GFP_KERNEL);
++ if (!info->data_desc) {
++ printk(KERN_ERR "Monahans NAND device:"
++ "Unable to alloc dma buffer\n");
++ ret = -ENOMEM;
++ goto free_mtd;
++ }
++
++ info->cmd_desc = (struct pxa_dma_desc*)((char *)info->data_desc +
++ sizeof(struct pxa_dma_desc));
++ info->cmd_desc_addr = (dma_addr_t)((char *)info->data_desc_addr +
++ sizeof(struct pxa_dma_desc));
++ info->data_buf = (char *)info->data_desc +
++ ALIGN(2*sizeof(struct pxa_dma_desc), 32);
++ info->data_buf_addr = (dma_addr_t)((char *)info->data_desc_addr +
++ ALIGN(2*sizeof(struct pxa_dma_desc), 32));
++ info->cmd_buf = (char *)info->data_buf + ALIGN(data_buf_len, 32);
++ info->cmd_buf_addr = (dma_addr_t)((char *)info->data_buf_addr +
++ ALIGN(data_buf_len, 32));
++
++ D1(printk("Get dma buffer for data dma descriptor, virt:0x%x, phys0x:%x\n",
++ (unsigned int)info->data_desc, info->data_desc_addr));
++ D1(printk("Get dma buffer for command dma descriptors, virt:0x%x,"
++ "phys0x:%x\n", (unsigned int)info->cmd_desc, info->cmd_desc_addr));
++ D1(printk("Get dma buffer for data, virt:0x%x, phys0x:%x\n",
++ (unsigned int)info->data_buf, info->data_buf_addr));
++ D1(printk("Get dma buffer for command, virt:0x%x, phys0x:%x\n",
++ (unsigned int)info->cmd_buf, info->cmd_buf_addr));
++
++ D1(printk("Try to allocate dma channel for data\n"));
++
++ info->data_dma = pxa_request_dma("NAND DATA", DMA_PRIO_LOW,
++ monahans_dfc_data_dma_irq, info);
++ if (info->data_dma < 0) {
++ printk(KERN_ERR "Monahans NAND device:"
++ "Unable to alloc dma channel for data\n");
++ ret = info->data_dma;
++ goto free_buf;
++ }
++ D1(printk("Get dma channel:%d for data\n", info->data_dma));
++
++ D1(printk("Try to allocate dma channel for command\n"));
++ info->cmd_dma = pxa_request_dma("NAND CMD", DMA_PRIO_LOW,
++ monahans_dfc_cmd_dma_irq, info);
++ if (info->cmd_dma < 0) {
++ printk(KERN_ERR "Monahans NAND device:"
++ "Unable to alloc dma channel for command\n");
++ ret = info->cmd_dma;
++ goto free_data_dma;
++ }
++ D1(printk("Get dma channel:%d for command\n", info->cmd_dma));
++
++ dfc_context.cmd_dma_ch = info->cmd_dma;
++ dfc_context.data_dma_ch = info->data_dma;
++#else
++ printk(KERN_DEBUG "Try to allocate data buffer(len:%d)\n", data_buf_len);
++ info->data_buf = kmalloc(data_buf_len, GFP_KERNEL);
++ if (!info->data_buf) {
++ printk(KERN_ERR "Monahans NAND device:"
++ "Unable to alloc data buffer\n");
++ ret = -ENOMEM;
++ goto free_mtd;
++ }
++#endif
++
++ D1(printk("Try to request irq:%d\n", IRQ_NAND));
++ ret = request_irq(IRQ_NAND, monahans_dfc_irq, 0, pdev->name, info);
++ if (ret < 0) {
++ printk(KERN_ERR "Monahans NAND device: Unable to request irq\n");
++#ifdef CONFIG_MTD_NAND_MONAHANS_DMA
++ goto free_cmd_dma;
++#else
++ goto free_buf;
++#endif
++ }
++
++ D1(printk("Success request irq\n"));
++
++ /* set address of NAND IO lines */
++ this->options = (dfc_context.flash_info->flash_width == 16)? \
++ NAND_BUSWIDTH_16: 0 | NAND_USE_FLASH_BBT;
++
++ /* this->IO_ADDR_R = this->IO_ADDR_W = NDDB */
++ this->waitfunc = monahans_df_waitfunc;
++ this->select_chip = monahans_df_select_chip;
++ this->dev_ready = monahans_df_dev_ready;
++ this->cmdfunc = monahans_df_command;
++ this->read_word= monahans_df_read_word;
++ /*this->write_word= monahans_df_write_word;*/
++ this->read_byte = monahans_df_read_byte;
++ this->read_buf = monahans_df_read_buf;
++ this->write_buf = monahans_df_write_buf;
++ this->verify_buf = monahans_df_verify_buf;
++ this->ecc.hwctl = monahans_df_enable_hwecc;
++ this->ecc.calculate = monahans_df_calculate_ecc;
++ this->ecc.correct = monahans_df_correct_data;
++ this->block_bad = monahans_df_block_bad;
++ this->block_markbad = monahans_df_block_markbad;
++ this->scan_bbt = monahans_df_scan_bbt;
++ this->chip_delay= 25;
++ this->bbt_td = &monahans_bbt_main;
++ this->bbt_md = &monahans_bbt_mirror;
++
++ /* If the NAND flash is small block flash, only 512-byte pagesize
++ * is supported.
++ * Adjust parameters of BBT what is depended on large block nand
++ * flash or small block nand flash.
++ */
++ if (dfc_context.flash_info->oob_size > 16) {
++ this->ecc.layout = &monahans_lb_nand_oob;
++ this->ecc.mode = NAND_ECC_HW;
++ this->ecc.size = 2048;
++ this->ecc.bytes = 24;
++ this->bbt_td->offs = 2;
++ this->bbt_td->veroffs = 6;
++ this->bbt_md->offs = 2;
++ this->bbt_md->veroffs = 6;
++ this->badblockpos = NAND_LARGE_BADBLOCK_POS;
++ monahans_bbt_default.offs = NAND_LARGE_BADBLOCK_POS;
++ monahans_bbt_default.len = 2;
++ /* when scan_bbt() is executed, bbt version can get */
++ monahans_bbt_default.veroffs = 2;
++ } else {
++ this->ecc.layout = &monahans_sb_nand_oob;
++ this->ecc.mode = NAND_ECC_HW;
++ this->ecc.size = 512;
++ this->ecc.bytes = 6;
++ this->bbt_td->offs = 8;
++ this->bbt_td->veroffs = 12;
++ this->bbt_md->offs = 8;
++ this->bbt_md->veroffs = 12;
++ this->badblockpos = NAND_SMALL_BADBLOCK_POS;
++ monahans_bbt_default.offs = NAND_SMALL_BADBLOCK_POS;
++ monahans_bbt_default.len = 1;
++ monahans_bbt_default.veroffs = 8;
++ }
++
++ info->context = &dfc_context;
++ /* TODO: allocate dma buffer and channel */
++
++ platform_set_drvdata(pdev, monahans_mtd);
++
++ if (nand_scan(monahans_mtd, 1)) {
++ printk(KERN_ERR "Nand scan failed\n");
++ ret = -ENXIO;
++ goto free_irq;
++ }
++
++ /* There is a potential limitation that no more partition can be
++ * added between MassStorage and BBT(last block).
++ *
++ * The last 127 blocks is reserved for relocation table, they aren't
++ * statistical data of mtd size and chip size.
++ *
++ * BBT partitions contains 4 blocks. Two blocks are used to store
++ * main descriptor, the other two are used to store mirror descriptor.
++ */
++ partition_info[PART_NUM - 1].size = (monahans_bbt_main.maxblocks
++ + monahans_bbt_mirror.maxblocks)
++ << this->phys_erase_shift;
++ partition_info[PART_NUM - 1].offset = this->chipsize
++ - partition_info[PART_NUM - 1].size;
++ partition_info[PART_NUM - 2].offset = partition_info[PART_NUM - 3].offset
++ + partition_info[PART_NUM - 3].size;
++ partition_info[PART_NUM - 2].size = this->chipsize
++ - partition_info[PART_NUM - 2].offset
++ - partition_info[PART_NUM - 1].size;
++ add_mtd_partitions(monahans_mtd, partition_info, PART_NUM);
++
++#ifdef CONFIG_DVFM
++ dvfm_notifier.client_data = info;
++ mhn_fv_register_notifier(&dvfm_notifier);
++#endif
++
++ return 0;
++
++free_irq:
++ free_irq(IRQ_NAND, info);
++#ifdef CONFIG_MTD_NAND_MONAHANS_DMA
++free_cmd_dma:
++ pxa_free_dma(info->cmd_dma);
++free_data_dma:
++ pxa_free_dma(info->data_dma);
++free_buf:
++ dma_free_writecombine(dev, buf_len, info->data_desc, info->data_desc_addr);
++#else
++free_buf:
++ kfree(info->data_buf);
++#endif
++free_mtd:
++ kfree(monahans_mtd);
++out:
++ return ret;
++
++}
++
++static int __devexit monahans_df_remove(struct platform_device *dev)
++{
++ struct mtd_info *mtd = (struct mtd_info *)platform_get_drvdata(dev);
++ struct monahans_dfc_info *info = (struct monahans_dfc_info *)
++ (((struct nand_chip *)(mtd->priv))->priv);
++#ifdef CONFIG_MTD_NAND_MONAHANS_DMA
++ unsigned int data_buf_len = dfc_context.flash_info->page_size +
++ dfc_context.flash_info->oob_size;
++ unsigned int buf_len = ALIGN(2*sizeof(struct pxa_dma_desc), 32) +
++ ALIGN(data_buf_len, 32) + ALIGN(NAND_CMD_DMA_LEN, 32);
++#endif
++
++#ifdef CONFIG_DVFM
++ mhn_fv_unregister_notifier(&dvfm_notifier);
++#endif
++
++ platform_set_drvdata(dev, NULL);
++
++ del_mtd_device(mtd);
++ del_mtd_partitions(mtd);
++ free_irq(IRQ_NAND, info);
++#ifdef CONFIG_MTD_NAND_MONAHANS_DMA
++ pxa_free_dma(info->cmd_dma);
++ pxa_free_dma(info->data_dma);
++ dma_free_writecombine(dev, buf_len, info->data_desc,
++ info->data_desc_addr);
++#else
++ kfree(info->data_buf);
++#endif
++ kfree(mtd);
++
++ return 0;
++}
++
++#ifdef CONFIG_PM
++static int monahans_df_suspend(struct platform_device *dev, pm_message_t state, u32 level)
++{
++ struct mtd_info *mtd = (struct mtd_info *)platform_get_drvdata(dev);
++ struct monahans_dfc_info *info = (struct monahans_dfc_info *)
++ (((struct nand_chip *)(mtd->priv))->priv);
++
++ if( SUSPEND_DISABLE == level){ /*SUSPEND_NOTIFY*/
++ if (info->state != STATE_READY) {
++ printk(KERN_ERR "current state is %d\n", info->state);
++ return -EAGAIN;
++ }
++ info->state = STATE_SUSPENDED;
++ /*
++ * The PM code need read the mobm from NAND.
++ * So the NAND clock can't be stop here.
++ * The PM code will cover this.
++ */
++ /* pxa_set_cken(CKEN_NAND, 0); */
++ }
++ return 0;
++}
++
++static int monahans_df_resume(struct platform_device *dev, u32 level)
++{
++ struct mtd_info *mtd = (struct mtd_info *)platform_get_drvdata(dev);
++ struct monahans_dfc_info *info = (struct monahans_dfc_info *)
++ (((struct nand_chip *)(mtd->priv))->priv);
++ int status;
++
++ if(RESUME_ENABLE == level){
++ if (info->state != STATE_SUSPENDED)
++ printk(KERN_WARNING "Error State after resume back\n");
++
++ info->state = STATE_READY;
++
++ pxa_set_cken(CKEN_NAND, 1);
++
++ status = dfc_init(&dfc_context, flash_config);
++ if (status) {
++ printk(KERN_ALERT "Monahans NAND device:"
++ "Nand Flash initialize failure!\n");
++ return -ENXIO;
++ }
++ }
++ return 0;
++}
++#endif
++
++#ifdef CONFIG_DVFM
++static int mhn_nand_dvfm_notifier(unsigned cmd, void *client_data, void *info)
++{
++ struct monahans_dfc_info *dfc_info =
++ (struct monahans_dfc_info *)client_data;
++
++ switch (cmd) {
++ case FV_NOTIFIER_QUERY_SET :
++ if (dfc_info->state != STATE_READY)
++ return -1;
++ break;
++
++ case FV_NOTIFIER_PRE_SET :
++ break;
++
++ case FV_NOTIFIER_POST_SET :
++ break;
++ }
++
++ return 0;
++}
++#endif
++
++static struct platform_driver monahans_df_driver = {
++ .probe = monahans_df_probe,
++ .remove = __devexit_p(monahans_df_remove),
++#ifdef CONFIG_PM
++ .suspend = monahans_df_suspend,
++ .resume = monahans_df_resume,
++#endif
++ .driver = {
++ .name = "monahans-nand-flash",
++ }
++};
++
++static void __exit monahans_df_cleanup(void)
++{
++ printk(KERN_ERR "Nand driver registered\n");
++ platform_driver_unregister(&monahans_df_driver);
++}
++
++static int __init monahans_df_init(void)
++{
++ return platform_driver_register(&monahans_df_driver);
++}
++
++module_init(monahans_df_init);
++module_exit(monahans_df_cleanup);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Jingqing.xu (jingqing.xu@intel.com)");
++MODULE_DESCRIPTION("Glue logic layer for NAND flash on monahans DFC");
++
++
+Index: linux-2.6.23/arch/arm/mach-pxa/zylonite.c
+===================================================================
+--- linux-2.6.23.orig/arch/arm/mach-pxa/zylonite.c 2008-02-13 00:59:45.000000000 +0000
++++ linux-2.6.23/arch/arm/mach-pxa/zylonite.c 2008-02-13 09:11:02.000000000 +0000
+@@ -29,6 +29,8 @@
+ #include "generic.h"
+
+ int gpio_backlight;
++int gpio_vsync;
++int gpio_vsync1;
+ int gpio_eth_irq;
+
+ int lcd_id;
+@@ -54,6 +56,16 @@
+ .resource = smc91x_resources,
+ };
+
++static struct platform_device nand_device = {
++ .name = "monahans-nand-flash",
++ .id = -1,
++};
++
++static struct platform_device touch_device = {
++ .name = "pxa2xx-touch",
++ .id = -1,
++};
++
+ #if defined(CONFIG_FB_PXA) || (CONFIG_FB_PXA_MODULES)
+ static void zylonite_backlight_power(int on)
+ {
+@@ -96,7 +108,7 @@
+ };
+
+ static struct pxafb_mode_info sharp_ls037_modes[] = {
+- [0] = {
++ [1] = {
+ .pixclock = 158000,
+ .xres = 240,
+ .yres = 320,
+@@ -109,8 +121,8 @@
+ .lower_margin = 3,
+ .sync = 0,
+ },
+- [1] = {
+- .pixclock = 39700,
++ [0] = {
++ .pixclock = 45000,
+ .xres = 480,
+ .yres = 640,
+ .bpp = 16,
+@@ -137,6 +149,11 @@
+ /* backlight GPIO: output, default on */
+ gpio_direction_output(gpio_backlight, 1);
+
++ gpio_direction_output(gpio_vsync, 0);
++ gpio_direction_output(gpio_vsync1, 0);
++
++ printk(KERN_ERR "LCD ID is %x\n", lcd_id);
++
+ if (lcd_id & 0x20) {
+ set_pxa_fb_info(&zylonite_sharp_lcd_info);
+ return;
+@@ -169,6 +186,8 @@
+ smc91x_resources[1].start = gpio_to_irq(gpio_eth_irq);
+ smc91x_resources[1].end = gpio_to_irq(gpio_eth_irq);
+ platform_device_register(&smc91x_device);
++ platform_device_register(&nand_device);
++ platform_device_register(&touch_device);
+
+ zylonite_init_lcd();
+ }
+Index: linux-2.6.23/arch/arm/mach-pxa/zylonite_pxa300.c
+===================================================================
+--- linux-2.6.23.orig/arch/arm/mach-pxa/zylonite_pxa300.c 2008-02-13 00:59:45.000000000 +0000
++++ linux-2.6.23/arch/arm/mach-pxa/zylonite_pxa300.c 2008-02-13 14:01:13.000000000 +0000
+@@ -62,12 +62,12 @@
+ GPIO110_UART3_RXD,
+
+ /* AC97 */
+- GPIO23_AC97_nACRESET,
++ /*GPIO23_AC97_nACRESET,
+ GPIO24_AC97_SYSCLK,
+ GPIO29_AC97_BITCLK,
+ GPIO25_AC97_SDATA_IN_0,
+ GPIO27_AC97_SDATA_OUT,
+- GPIO28_AC97_SYNC,
++ GPIO28_AC97_SYNC,*/
+
+ /* Keypad */
+ GPIO107_KP_DKIN_0,
+@@ -104,6 +104,41 @@
+ /* Ethernet */
+ GPIO2_nCS3,
+ GPIO99_GPIO,
++
++ /* NAND */
++ MFP_CFG_X(DF_INT_RnB, AF0, DS10X, PULL_LOW),
++ MFP_CFG_X(DF_nRE_nOE, AF1, DS10X, PULL_LOW),
++ MFP_CFG_X(DF_nWE, AF1, DS10X, PULL_LOW),
++ MFP_CFG_X(DF_CLE_nOE, AF0, DS10X, PULL_LOW),
++ MFP_CFG_X(DF_nADV1_ALE, AF1, DS10X, PULL_LOW),
++ MFP_CFG_X(DF_nCS0, AF1, DS10X, PULL_LOW),
++ MFP_CFG_X(DF_nCS1, AF0, DS10X, PULL_LOW),
++ MFP_CFG_X(DF_IO0, AF1, DS08X, PULL_LOW),
++ MFP_CFG_X(DF_IO1, AF1, DS08X, PULL_LOW),
++ MFP_CFG_X(DF_IO2, AF1, DS08X, PULL_LOW),
++ MFP_CFG_X(DF_IO3, AF1, DS08X, PULL_LOW),
++ MFP_CFG_X(DF_IO4, AF1, DS08X, PULL_LOW),
++ MFP_CFG_X(DF_IO5, AF1, DS08X, PULL_LOW),
++ MFP_CFG_X(DF_IO6, AF1, DS08X, PULL_LOW),
++ MFP_CFG_X(DF_IO7, AF1, DS08X, PULL_LOW),
++ MFP_CFG_X(DF_IO8, AF1, DS08X, PULL_LOW),
++ MFP_CFG_X(DF_IO9, AF1, DS08X, PULL_LOW),
++ MFP_CFG_X(DF_IO10, AF1, DS08X, PULL_LOW),
++ MFP_CFG_X(DF_IO11, AF1, DS08X, PULL_LOW),
++ MFP_CFG_X(DF_IO12, AF1, DS08X, PULL_LOW),
++ MFP_CFG_X(DF_IO13, AF1, DS08X, PULL_LOW),
++ MFP_CFG_X(DF_IO14, AF1, DS08X, PULL_LOW),
++
++ /* AC97 */
++ MFP_CFG_X(GPIO23, AF1, DS03X, PULL_LOW),
++ MFP_CFG_X(GPIO27, AF1, DS03X, PULL_LOW),
++ MFP_CFG_X(GPIO28, AF1, DS03X, PULL_LOW),
++ MFP_CFG_X(GPIO29, AF1, DS03X, PULL_LOW),
++ MFP_CFG_X(GPIO25, AF1, DS03X, PULL_LOW),
++
++ MFP_CFG_X(GPIO26, AF0, DS01X, PULL_LOW), /* Interrupt */
++ MFP_CFG_X(GPIO24, AF0, DS03X, PULL_LOW), /*SYSCLK external */
++ MFP_CFG_X(GPIO11, AF0, DS01X, PULL_LOW),
+ };
+
+ static mfp_cfg_t pxa310_mfp_cfg[] __initdata = {
+@@ -163,6 +198,9 @@
+ pxa3xx_mfp_write(lcd_detect_pins[i], mfpr_save[i]);
+ }
+
++extern int gpio_vsync;
++extern int gpio_vsync1;
++
+ void __init zylonite_pxa300_init(void)
+ {
+ if (cpu_is_pxa300() || cpu_is_pxa310()) {
+@@ -174,6 +212,8 @@
+
+ /* GPIO pin assignment */
+ gpio_backlight = mfp_to_gpio(MFP_PIN_GPIO20);
++ gpio_vsync = mfp_to_gpio(GPIO76_LCD_VSYNC);
++ gpio_vsync1 = mfp_to_gpio(GPIO71_LCD_LDD_17);
+ }
+
+ if (cpu_is_pxa300()) {
+Index: linux-2.6.23/drivers/video/pxafb.c
+===================================================================
+--- linux-2.6.23.orig/drivers/video/pxafb.c 2008-02-13 00:59:45.000000000 +0000
++++ linux-2.6.23/drivers/video/pxafb.c 2008-02-13 00:59:45.000000000 +0000
+@@ -1543,9 +1543,9 @@
+ if (inf->lccr0 & LCCR0_INVALID_CONFIG_MASK)
+ dev_warn(&dev->dev, "machine LCCR0 setting contains illegal bits: %08x\n",
+ inf->lccr0 & LCCR0_INVALID_CONFIG_MASK);
+- if (inf->lccr3 & LCCR3_INVALID_CONFIG_MASK)
+- dev_warn(&dev->dev, "machine LCCR3 setting contains illegal bits: %08x\n",
+- inf->lccr3 & LCCR3_INVALID_CONFIG_MASK);
++ //if (inf->lccr3 & LCCR3_INVALID_CONFIG_MASK)
++ // dev_warn(&dev->dev, "machine LCCR3 setting contains illegal bits: %08x\n",
++ // inf->lccr3 & LCCR3_INVALID_CONFIG_MASK);
+ if (inf->lccr0 & LCCR0_DPD &&
+ ((inf->lccr0 & LCCR0_PAS) != LCCR0_Pas ||
+ (inf->lccr0 & LCCR0_SDS) != LCCR0_Sngl ||
+Index: linux-2.6.23/include/asm-arm/arch-pxa/mfp-pxa300.h
+===================================================================
+--- linux-2.6.23.orig/include/asm-arm/arch-pxa/mfp-pxa300.h 2008-02-13 00:59:45.000000000 +0000
++++ linux-2.6.23/include/asm-arm/arch-pxa/mfp-pxa300.h 2008-02-13 00:59:45.000000000 +0000
+@@ -175,13 +175,13 @@
+ #define GPIO68_LCD_LDD_14 MFP_CFG_DRV(GPIO68, AF1, DS01X)
+ #define GPIO69_LCD_LDD_15 MFP_CFG_DRV(GPIO69, AF1, DS01X)
+ #define GPIO70_LCD_LDD_16 MFP_CFG_DRV(GPIO70, AF1, DS01X)
+-#define GPIO71_LCD_LDD_17 MFP_CFG_DRV(GPIO71, AF1, DS01X)
++#define GPIO71_LCD_LDD_17 MFP_CFG_DRV(GPIO71, AF0, DS01X)
+ #define GPIO62_LCD_CS_N MFP_CFG_DRV(GPIO62, AF2, DS01X)
+ #define GPIO72_LCD_FCLK MFP_CFG_DRV(GPIO72, AF1, DS01X)
+ #define GPIO73_LCD_LCLK MFP_CFG_DRV(GPIO73, AF1, DS01X)
+ #define GPIO74_LCD_PCLK MFP_CFG_DRV(GPIO74, AF1, DS01X)
+ #define GPIO75_LCD_BIAS MFP_CFG_DRV(GPIO75, AF1, DS01X)
+-#define GPIO76_LCD_VSYNC MFP_CFG_DRV(GPIO76, AF2, DS01X)
++#define GPIO76_LCD_VSYNC MFP_CFG_DRV(GPIO76, AF0, DS01X)
+
+ #define GPIO15_LCD_CS_N MFP_CFG_DRV(GPIO15, AF2, DS01X)
+ #define GPIO127_LCD_CS_N MFP_CFG_DRV(GPIO127, AF1, DS01X)
diff --git a/recipes/linux/linux-rp-2.6.26/zylonite_touch-r0.patch b/recipes/linux/linux-rp-2.6.26/zylonite_touch-r0.patch
new file mode 100644
index 0000000000..1c00696051
--- /dev/null
+++ b/recipes/linux/linux-rp-2.6.26/zylonite_touch-r0.patch
@@ -0,0 +1,1548 @@
+Index: linux-2.6.23/drivers/input/touchscreen/Kconfig
+===================================================================
+--- linux-2.6.23.orig/drivers/input/touchscreen/Kconfig 2008-02-13 01:12:29.000000000 +0000
++++ linux-2.6.23/drivers/input/touchscreen/Kconfig 2008-02-13 01:13:20.000000000 +0000
+@@ -54,6 +54,12 @@
+ To compile this driver as a module, choose M here: the
+ module will be called corgi_ts.
+
++config TOUCHSCREEN_ZYLONITE
++ tristate "Zylonite touchscreen driver"
++ default y
++ help
++ Say Y here for the Zylonite touchscreen driver
++
+ config TOUCHSCREEN_FUJITSU
+ tristate "Fujitsu serial touchscreen"
+ select SERIO
+Index: linux-2.6.23/drivers/input/touchscreen/Makefile
+===================================================================
+--- linux-2.6.23.orig/drivers/input/touchscreen/Makefile 2008-02-13 01:12:29.000000000 +0000
++++ linux-2.6.23/drivers/input/touchscreen/Makefile 2008-02-13 01:13:38.000000000 +0000
+@@ -19,3 +19,4 @@
+ obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o
+ obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o
+ obj-$(CONFIG_TOUCHSCREEN_TSC2101) += tsc2101_ts.o
++obj-$(CONFIG_TOUCHSCREEN_ZYLONITE) += zylonite-ts.o
+Index: linux-2.6.23/drivers/input/touchscreen/zylonite-ts.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ linux-2.6.23/drivers/input/touchscreen/zylonite-ts.c 2008-02-13 16:19:15.000000000 +0000
+@@ -0,0 +1,1517 @@
++/*
++ * drivers/input/touchscreen/mhn_audio_touch.c.
++ *
++ * Author: bridge.wu@marvell.com
++ * Created: Nov 17, 2006
++ * Copyright: Marvell Corporation.
++ *
++ * 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/init.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/device.h>
++#include <linux/platform_device.h>
++#include <linux/interrupt.h>
++#include <linux/delay.h>
++
++#include <sound/driver.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/initval.h>
++
++#include <asm/semaphore.h>
++#include <asm/hardware.h>
++#include <asm/arch/pxa-regs.h>
++#include <asm/arch/gpio.h>
++#include <asm/arch/mfp.h>
++//#include <asm/arch/ipmc.h>
++#include <linux/suspend.h>
++#include <linux/spinlock.h>
++
++//#include <asm/arch/codec/acodec.h>
++//#include <asm/arch/mhn_audio_plat.h>
++
++#define OSCC __REG(0x41350000) /* Oscillator Configuration Register */
++#define AC97_DIV __REG(0x41340014)
++
++#define ALSA_ZY_CARD_DEBUG
++#undef ALSA_ZY_CARD_DEBUG
++
++#define WM_9713_ID 0x4C13 /* this should be the 7E register value */
++
++#ifdef ALSA_ZY_CARD_DEBUG
++#define dbg(format, arg...) printk(KERN_DEBUG format, ##arg)
++#else
++#define dbg(format, arg...)
++#endif
++
++#define DEBUG
++#undef DEBUG
++#ifdef DEBUG
++unsigned int start_time;
++unsigned int end_time;
++unsigned int time;
++#define PRINT_TIME() do {\
++ time = ((end_time > start_time))?\
++ (end_time - start_time)*100/325:\
++ (0xffffffff - start_time + end_time)*100/325;\
++ printk("\n%s:%dus\n", __FUNCTION__, time);\
++} while(0)
++#endif
++
++
++
++
++/* 9713 specific
++ * Register Name Index
++ */
++#define RESET 0X00
++#define SPEAKER_VOLUME 0X02
++#define HEADPHONE_VOLUME 0X04
++#define OUT3_OUT4_VOLUME 0X06
++#define MONOVOL_MONOINPGA_ROUTE 0X08
++#define LINE_IN_PGA_VOL_ROUTE 0X0A
++#define DAC_PGA_VOL_ROUTE 0X0C
++#define MIC_PGA_VOLUME 0X0E
++#define MIC_ROUTING 0X10
++#define REC_PGA_VOL 0X12
++#define REC_ROUTE_MUX_SEL 0X14
++#define PCBEEP_VOL_ROUTE 0X16
++#define VXDAC_VOLUME_ROUTE 0X18
++#define AUX_DAC_VOL_ROUTE 0X1A
++#define OUTPUT_PGA_MUX 0X1C
++#define DAC_3D_CTRL_INV_MUX_SEL 0X1E
++#define DAC_TONE_CTRL 0X20
++#define MIC_BIAS 0X22
++#define OUTPUT_VOL_MAPPING_JACK 0X24
++#define POWERDOWN_CTRL_STAT 0X26
++#define EXTENDED_AUD_ID 0X28
++#define EXTENDED_AUD_STAT_CTRL 0X2A
++#define AUDIO_DAC_RATE 0X2C
++#define AUX_DAC_RATE 0X2E
++#define AUDIO_ADC_RATE 0X32
++#define PCM_CODEC_CTRL 0X36
++#define SPDIF_CTRL 0X3A
++#define POWER_DOWN_1 0X3C
++#define POWER_DOWN_2 0X3E
++#define GENERAL_PURPOSE_WM_13 0X40
++#define FAST_POWERUP_CTRL 0X42
++#define MCLK_PLL_CTRL_1 0X44
++#define MCLK_PLL_CTRL_2 0X46
++#define GPIO_PIN_CFG 0X4C
++#define GPIO_PIN_POL_TYPE 0X4E
++#define GPIO_PIN_STICKY 0X50
++#define GPIO_PIN_WAKEUP 0X52
++#define GPIO_PIN_STATUS 0X54
++#define GPIO_PIN_SHARING 0X56
++#define GPIO_PULL_UP_DOWN_CTRL 0X58
++#define ADD_FUNC_1 0X5A
++#define ADD_FUNC_2 0X5C
++#define ALC_CTRL 0X60
++#define ALC_NOISE_GATE_CTRL 0X62
++#define AUX_DAC_INPUT_CTRL 0X64
++#define TEST_REG_1 0X68
++#define TEST_REG_2 0X6A
++#define TEST_REG_3 0X6C
++#define TEST_REG_4 0X6E
++#define DIGITIZER_1_WM13 0x74
++#define DIGITIZER_2_WM13 0x76
++#define DIGITIZER_3_WM13 0x78
++#define DIGITIZER_READ_BACK 0x7a
++#define VENDOR_ID1 0x7c
++#define VENDOR_ID2 0x7e
++
++#define ZY_TOUCH_SAMPLE_X 1
++#define ZY_TOUCH_SAMPLE_Y 2
++
++#define ZY_EVENT_TYPE_NONE 0
++#define ZY_EVENT_TYPE_PDN 1
++
++
++typedef enum _zy_acodec_error_t {
++ ZY_ACODEC_SUCCESS = 0, /* successful completion of a function */
++ ZY_ACODEC_GENERAL_SW_ERR, /* null pointer to registers or other software error */
++ ZY_ACODEC_CONTROLLER_INTERFACE_TIMEOUT, /* time-out for waiting for respponse */
++ ZY_ACODEC_SAMPLERATE_NOT_SUPPORTED, /* the sample rate is not supported either in controller or codec */
++ ZY_ACODEC_FEATURE_NO_SUPPORTED, /* this codec feature is not supported */
++ ZY_ACODEC_GENERAL_HW_ERR ,/* other hardware error besides time out */
++ ZY_ACODEC_ROUTE_NO_SUPPORTED, /* the codec can not set the route required */
++ ZY_ACODEC_SLEEP /* the codec is sleep */
++} zy_acodec_error_t;
++
++typedef unsigned int zy_acodec_device_id_t;
++
++typedef enum _codec_state {
++ ZY_CODEC_SLEEP = 0,
++ ZY_CODEC_WAKEUP = 1
++} acodec_state_t;
++
++typedef enum {
++ ZY_FM = 0,
++ ZY_MIC1 = 1,
++ ZY_MIC2 = 2,
++ ZY_SPEAKER =3,
++ ZY_HEADSET =4,
++ ZY_HANDSET =5,
++} vol_port_type_t;
++
++typedef struct _context_t {
++ int use_count; /* probe/remove and suspend/resume usage count, sync among multiple devices */
++ zy_acodec_device_id_t acodec_id;/* - an ID that uniquely identifies the codec to be used */
++ unsigned long init_number; /* used by driver to track whether it is inited or not */
++
++ void *p_voice_reg; /* pointer to Monahans registers that has PCM interface to codec */
++ void *p_hifi_reg; /* pointer to Monahans registers that has hifi interface to codec */
++ void *p_ctrl_reg; /* pointer to Monahans registers that has control interface to codec */
++ int *p_ost_regs; /* needed for time out */
++ void *p_save_memory; /* pointer to a memory region to save context while suspend */
++ void *p_zy_scenario; /* pointer to the scenario data structure */
++ long u_max_read_write_time_out_ms;/* input the max time to wait in milliseconds before giving up on a read or write operation */
++ long u_max_setup_time_out_ms; /* input the maximum time in milliseconds to wait during initial setup of the ACODEC controller and codec */
++
++ /* member functions these pointers must be set by */
++ zy_acodec_error_t (* g_pfn_codec_specific_init) (struct _context_t *p_device_context);
++ zy_acodec_error_t (* g_pfn_codec_specific_dinit) (struct _context_t *p_device_context);
++ zy_acodec_error_t (* g_pfn_acodec_read) (struct _context_t *p_dev_context, unsigned short reg_addr, unsigned short *p_reg_value);
++ zy_acodec_error_t (* g_pfn_acodec_write) (struct _context_t *p_dev_context, unsigned short reg_addr, unsigned short reg_value);
++
++ /* add for route */
++ zy_acodec_error_t (* g_pfn_set_route) (struct _context_t *p_device_context, unsigned short * rout_map ,unsigned short* current_map);
++ /* add for sleep the codec */
++ zy_acodec_error_t (* g_pfn_sleep_codec) (struct _context_t *p_device_context);
++ /* add for Wake up the codec */
++ zy_acodec_error_t (* g_pfn_wake_codec) (struct _context_t *p_device_context);
++ /* add for get codec state */
++ zy_acodec_error_t (* g_pfn_get_state) (struct _context_t *p_device_context, acodec_state_t *p_state);
++ /* add for volume */
++ zy_acodec_error_t (* g_pfn_get_vol)(struct _context_t *p_device_context, vol_port_type_t port, unsigned short *gain_in_db);
++ zy_acodec_error_t (* g_pfn_set_vol)(struct _context_t *p_device_context, vol_port_type_t port, unsigned short gain_in_db);
++
++ void (* g_pfn_get_event)(struct _context_t *p_device_context, unsigned char * event_type);
++ void (* g_pfn_event_ack)(struct _context_t *p_device_context, unsigned char event_type);
++ zy_acodec_error_t (* g_pfn_enable_touch)(struct _context_t *p_device_context);
++ zy_acodec_error_t (* g_pfn_disable_touch)(struct _context_t *p_device_context);
++} zy_acocec_context_t, *p_zy_acocec_context_t;
++
++
++
++
++
++static p_zy_acocec_context_t p_zy_codec_ctxt = NULL;
++
++#include <linux/input.h>
++//#include <asm/arch/codec/wm9713.h>
++
++#define PEN_DOWN 1
++#define PEN_UP 0
++#define TS_SAMPLE_INTERVAL 1
++
++typedef struct {
++ struct input_dev *idev;
++ struct timer_list *timer;
++ int use_count;
++} codec_zy_ts_t;
++
++codec_zy_ts_t codec_zy_ts;
++
++static struct input_dev *codec_zy_ts_input;
++
++#ifdef CONFIG_PM
++static volatile int touch_suspend = 0 ;
++#endif
++
++#define ZY_AC97_CODEC_REGS_NUM 0x40
++
++typedef struct
++{ // Register symbol // Usage
++ volatile unsigned long pocr; // PCM Out Control Register
++ volatile unsigned long picr; // PCM In Control Register
++ volatile unsigned long mccr; // Mic In Control Register
++ volatile unsigned long gcr; // Global Control Register
++ volatile unsigned long posr; // PCM Out Status Register
++ volatile unsigned long pisr; // PCM In Status Register
++ volatile unsigned long mcsr; // Mic In Status Register
++ volatile unsigned long gsr; // Global Status Register
++ volatile unsigned long car; // CODEC Access Register
++ volatile unsigned long pcscr; // PCM Surround Out Control
++ volatile unsigned long pcssr; // PCM Surround Out Status
++ volatile unsigned long pcsdr; // PCM Surround Out Data
++ volatile unsigned long pcclcr; // PCM Center/LFE Out Control
++ volatile unsigned long pcclsr; // PCM Center/LFE Out Status
++ volatile unsigned long pccldr; // PCM Center/LFE Out Data
++ volatile unsigned long reserved1; //
++ volatile unsigned long pcdr; // PCM FIFO Data Register
++ volatile unsigned long reserved2 [0x7]; // 0x4050-0044 through 0x4050-005C
++ volatile unsigned long mcdr; // Mic-in FIFO Data Register
++ volatile unsigned long reserved3 [0x27]; // 0x4050-0064 through 0x4050-00FC
++ volatile unsigned long mocr; // MODEM Out Control Register
++ volatile unsigned long reserved4;
++ volatile unsigned long micr; // MODEM In Control Register
++ volatile unsigned long reserved5;
++ volatile unsigned long mosr; // MODEM Out Status Register
++ volatile unsigned long reserved6;
++ volatile unsigned long misr; // MODEM In Status Register
++ volatile unsigned long reserved7 [0x9]; // 0x4050-011C through 0x4050-013C
++ volatile unsigned long modr; // MODEM FIFO Data Register
++ volatile unsigned long reserved8 [0x2F]; // 0x4050-0144 through 0x4050-01FC
++ // Primary Audio CODEC registers access
++ volatile unsigned long codec_regs_primary_aud [ZY_AC97_CODEC_REGS_NUM];
++ // Secondary ID 01 Audio CODEC registers access
++ volatile unsigned long codec_regs_secondary_aud [ZY_AC97_CODEC_REGS_NUM];
++ // Primary MODEM CODEC registers access
++ volatile unsigned long codec_regs_primary_mdm [ZY_AC97_CODEC_REGS_NUM];
++ // Secondary ID 01 MODEM CODEC registers access
++ volatile unsigned long codec_regs_secondary_mdm [ZY_AC97_CODEC_REGS_NUM];
++ // Secondary ID 10 MODEM CODEC registers access
++ volatile unsigned long codec_regs_third_mdm [ZY_AC97_CODEC_REGS_NUM];
++ // Secondary ID 11 MODEM CODEC registers access
++ volatile unsigned long codec_regs_fouth_mdm [ZY_AC97_CODEC_REGS_NUM];
++ // Secondary ID 10 Audio CODEC registers access
++ volatile unsigned long codec_regs_third_aud [ZY_AC97_CODEC_REGS_NUM];
++ // Secondary ID 11 Audio CODEC registers access
++ volatile unsigned long codec_regs_fouth_aud [ZY_AC97_CODEC_REGS_NUM];
++} zy_ac97_acodec_t, *p_zy_ac97acodec_t ;
++
++
++/* Constants for the Global Control Register and Global Status Register */
++
++// AC97 Global Control Register bit mask constants
++
++#define ZY_AC97_GCR_GIE_MSK (1u << 0 )
++#define ZY_AC97_GCR_COLD_RESET_MSK (1u << 1 )
++#define ZY_AC97_GCR_WARM_RESET_MSK (1u << 2 )
++#define ZY_AC97_GCR_LINK_OFF_MSK (1u << 3 )
++#define ZY_AC97_GCR_PCRSM_IEN_MSK (1u << 4 )
++#define ZY_AC97_GCR_SCRSM_IEN_MSK (1u << 5 )
++#define ZY_AC97_GCR_PCRDY_IEN_MSK (1u << 8 )
++#define ZY_AC97_GCR_SCRDY_IEN_MSK (1u << 9 )
++#define ZY_AC97_GCR_SDONE_IE_MSK (1u << 18)
++#define ZY_AC97_GCR_CDONE_IE_MSK (1u << 19)
++#define ZY_AC97_GCR_nDMAEN_MSK (1u << 24)
++#define ZY_AC97_GCR_CLKBPB_MSK (1u << 31)
++#define ZY_AC97_GCR_FRCRST_MSK (1u << 30)
++// Global Status Register bit mask constants
++
++#define ZY_AC97_GSR_GSCI_MSK (1u << 0 )
++#define ZY_AC97_GSR_MIINT_MSK (1u << 1 )
++#define ZY_AC97_GSR_MOINT_MSK (1u << 2 )
++#define ZY_AC97_GSR_ACOFFD_MSK (1u << 3 )
++#define ZY_AC97_GSR_PIINT_MSK (1u << 5 )
++#define ZY_AC97_GSR_POINT_MSK (1u << 6 )
++#define ZY_AC97_GSR_MINT_MSK (1u << 7 )
++#define ZY_AC97_GSR_PCRDY_MSK (1u << 8 )
++#define ZY_AC97_GSR_SCRDY_MSK (1u << 9 )
++#define ZY_AC97_GSR_PCRSM_MSK (1u << 10)
++#define ZY_AC97_GSR_SCRSM_MSK (1u << 11)
++#define ZY_AC97_GSR_SLT12_BITS_MSK (7u << 12)
++#define ZY_AC97_GSR_RCS_ERR_MSK (1u << 15)
++#define ZY_AC97_GSR_SDONE_MSK (1u << 18)
++#define ZY_AC97_GSR_CDONE_MSK (1u << 19)
++
++
++// Bit mask and values for CAIP bit in car register.
++#define ZY_AC97_CAR_CAIP_MSK (0x1<<0)
++#define ZY_AC97_CAR_CAIP_LOCKED (0x1<<0)
++#define ZY_AC97_CAR_CAIP_CLEAR (0<<0)
++
++/* Constants for FIFO status reporting and control */
++
++// One bit location is used to report FIFO error conditions and clear
++// interrupts on those conditions in the various non-global status registers.
++
++// ZY_AC97_FIFOSTAT_FIFOE is used in:
++ // posr
++ // pisr
++ // mcsr
++ // mosr
++ // misr
++
++#define ZY_AC97_FIFOSTAT_FIFOE (1u << 4)
++#define ZY_AC97_FIFOSTAT_EOC (1u << 3)
++#define ZY_AC97_FIFOSTAT_FSR (1u << 2)
++
++// A different bit location is used to enable or disable interrupts based on
++// FIFO error conditions in the various non-global control registers.
++
++// ZY_AC97_FIFOCTRL_FEIE is used in:
++ // pocr
++ // picr
++ // mccr
++ // mocr
++ // micr
++
++#define ZY_AC97_FIFOCTRL_FEIE (1u << 3)
++#define ZY_AC97_FIFOCTRL_FSRIE (1u << 1)
++
++/*
++*******************************************************************************
++ AC'97 Codec Registers Location and Bit Definition
++*******************************************************************************
++*/
++
++/* */
++
++ // Includes symbolic values for certain proprietary register asssignments
++ // in AC'97 devices that might be used with ZY_AC97.
++
++ // Valid for subset of R 2.1 specification.
++ // Leading "e" in comment means it is an "expanded" register definition as
++ // found in one or more of the Appendices A-D of the R 2.1 specification.
++ // Appendix identifier will immediately follow the "e", such as "eA"
++ // R/O indicates read-only
++ // Registers not supported by the assumed controller will be commented out.
++
++#define ZY_AC97_CR_RESET_ID 0x00 // RESET CODEC TO DEFAULT, get ID info
++#define ZY_AC97_CR_MASTER_VOLUME 0x02 // LINE OUT VOLUME
++#define ZY_AC97_CR_HEADPHONE_VOLUME 0x04 //
++#define ZY_AC97_CR_MASTER_VOLUME_MONO 0x06 //
++#define ZY_AC97_CR_MASTER_TONE_R_L 0x08 //
++#define ZY_AC97_CR_PC_BEEP_VOLUME 0x0A //
++#define ZY_AC97_CR_PHONE_VOLUME 0x0C //
++#define ZY_AC97_CR_MIC_VOLUME 0x0E // micrOPHONE VOLUME/ AGC
++#define ZY_AC97_CR_LINE_IN_VOLUME 0x10 // LINE IN VOLUME
++#define ZY_AC97_CR_CD_VOLUME 0x12 //
++#define ZY_AC97_CR_VIDEO_VOLUME 0x14 //
++#define ZY_AC97_CR_AUX_VOLUME 0x16 //
++#define ZY_AC97_CR_PCM_OUT_VOLUME 0x18 //
++#define ZY_AC97_CR_RECORD_SELECT 0x1A // SELECT LINE IN OR micrOPHONE
++#define ZY_AC97_CR_RECORD_GAIN 0x1C //
++#define ZY_AC97_CR_RECORD_GAIN_MIC 0x1E //
++#define ZY_AC97_CR_GENERAL_PURPOSE 0x20 //
++#define ZY_AC97_CR_CONTROL_3D 0x22 //
++#define ZY_AC97_CR_POWERDOWN_CTRL_STAT 0x26 // POWER MANAGEMENT
++#define ZY_AC97_CR_E_AUDIO_ID 0x28 // eA Extended audio sprt info, R/O
++#define ZY_AC97_CR_E_AUDIO_CTRL_STAT 0x2A // eA Extended audio stat + control
++
++//
++// Audio Sample Rate Control Registers, 0x2C - 0x34
++//
++ // eA PCM Front DAC rate control
++#define ZY_AC97_CR_E_ASR_PCM_FRNT_DAC_RT 0x2C // (output slots 3, 4, 6)
++#define ZY_AC97_CR_E_ASR_PCM_LR_ADC_RT 0x32 // eA PCM L+R ADC rate control (3+4)
++#define ZY_AC97_CR_E_ASR_MIC_ADC_RT 0x34 // eA PCM Mic ADC rate control (5)
++
++
++#define ZY_AC97_CR_E_MDM_GPIO_PIN_STAT 0x54
++//
++// 5Ah-7Ah: Vendor Reserved
++//
++//
++// 7Ch-7Eh: Vendor ID registers. Optional but standardized for Plug'n'Play
++//
++#define ZY_AC97_CR_VENDOR_ID1 0x7C
++#define ZY_AC97_CR_VENDOR_ID2 0x7E
++
++#define ZY_AC97_CR_MAX ZY_AC97_CR_VENDOR_ID2
++
++#define ZY_AC97_CR_END_OF_LIST (ZY_AC97_CR_MAX + 2)
++
++
++
++/* Other Constants */
++
++// For accessing the Codec mixer registers, each increment of one 32-bit word
++// in processor space increments the addressed mixer register by two.
++// This does not cause any ambiguities because only even mixer register
++// addresses are currently supported (AC '97 spec, R 2.2)
++#define ZY_AC97_CODEC_REGS_PER_WORD 2
++
++/* Default timeout and holdtime settings */
++
++// timeout in reading and writing codec registers through AC link
++#define ZY_AC97_RW_TIMEOUT_DEF 200 //unit is us
++
++// timeout in waiting for codec's ready signal during setup process
++#define ZY_AC97_SETUP_TIMEOUT_DEF 500 //unit is us
++
++// timeout in waiting for locking the link successfully
++#define ZY_AC97_LOCK_TIMEOUT_DEF 300 //unit is us
++
++// timeout in shutting down the link
++#define ZY_AC97_LINKOFF_TIMEOUT_DEF 500 //unit is us
++
++// holdtime for keeping nReset signal active(low) in AC link
++#define ZY_AC97_COLD_HOLDTIME 100 //unit is us
++
++/*
++*******************************************************************************
++ ZY AC97 data structure used in function interface
++*******************************************************************************
++*/
++
++typedef struct
++{
++ unsigned long pocr; // PCM Out Control Register
++ unsigned long picr; // PCM In Control Register
++ unsigned long mccr; // Mic In Control Register
++ unsigned long gcr; // Global Control Register
++ unsigned long pcscr; // PCM Surround Out Control
++ unsigned long pcclcr; // PCM Center/LFE Out Control
++ unsigned long mocr; // MODEM Out Control Register
++ unsigned long micr; // MODEM In Control Register
++}zy_ac97_save_context_t;
++
++
++#define AC97_SAVE_CONTEXT_SIZE (sizeof(zy_ac97_save_context_t))
++
++static int zy_ac97_acodec_link_lock(p_zy_ac97acodec_t p_ac97_reg)
++{
++ int status = 1;
++ volatile unsigned long car_tmp;
++
++ car_tmp = p_ac97_reg->car;
++ if (car_tmp & ZY_AC97_CAR_CAIP_MSK) /* "1" in CAIP bit means lock failed. */
++ {
++ status = 0;
++ }
++ return (status);
++}
++
++
++zy_acodec_error_t zy_ac97_acodec_write (zy_acocec_context_t *p_dev_context, unsigned short offset, unsigned short data)
++{
++ zy_acodec_error_t status = ZY_ACODEC_SUCCESS;
++ int got_link;
++ unsigned long time_remaining;
++ volatile unsigned long * p_codec_reg;
++ p_zy_ac97acodec_t p_ac97_reg = (p_zy_ac97acodec_t)(p_dev_context->p_ctrl_reg);
++ unsigned long max_rw_time_out_us = (p_dev_context->u_max_read_write_time_out_ms) * 1000;
++
++
++ if(offset == ZY_AC97_CR_E_MDM_GPIO_PIN_STAT)
++ {/* it is a special register and sent out on slot 12 */
++ p_codec_reg = &(p_ac97_reg->codec_regs_primary_mdm[0]);
++ p_codec_reg += offset / ZY_AC97_CODEC_REGS_PER_WORD;
++ /* The data will be sent out on slot 12. */
++ *p_codec_reg = (unsigned long)data;
++ goto done;
++ }
++
++ /* Point to specified register within area mapped to target codec regs */
++ p_codec_reg = &(p_ac97_reg->codec_regs_primary_aud[0]);
++ p_codec_reg += offset / ZY_AC97_CODEC_REGS_PER_WORD;
++
++
++ /* Lock the ACLINK */
++ time_remaining = ZY_AC97_LOCK_TIMEOUT_DEF;
++ do
++ {
++ got_link = zy_ac97_acodec_link_lock(p_ac97_reg);
++ if (0 == got_link) /* 1 usec is a long time. Skip delay if possible. */
++ {
++ udelay(1);
++ }
++ } /* Wait while time remaining and ACLINK not available */
++ while (time_remaining-- && (0 == got_link));
++
++ if (0 == got_link) /* Didn't get the ACLINK */
++ {
++ status = ZY_ACODEC_CONTROLLER_INTERFACE_TIMEOUT;
++ printk(KERN_ERR "AC97 Write Link Timeout\n");
++ }
++ else /* We got the link. Perform the write operation and don't wait. */
++ {
++ /* First, clear old write status indication CDONE by writing a ONE to that bit. */
++ p_ac97_reg->gsr = ZY_AC97_GSR_CDONE_MSK;
++
++ *p_codec_reg = (unsigned long)data; /* Now the write! */
++
++ /* Wait until write cycle is complete. There should be a way
++ * to do this speculatively at the beginning of the procedure.
++ * Need to discover it. Too inefficient to always wait.
++ */
++
++ time_remaining = max_rw_time_out_us;
++ do
++ {
++ udelay(1);
++ } /* Wait while time remaining and command I/O still incomplete. */
++ while ( (time_remaining--) && !(p_ac97_reg->gsr & ZY_AC97_GSR_CDONE_MSK));
++ if (!(p_ac97_reg->gsr & ZY_AC97_GSR_CDONE_MSK))
++ {
++ status = ZY_ACODEC_CONTROLLER_INTERFACE_TIMEOUT;
++ p_ac97_reg->car = ZY_AC97_CAR_CAIP_CLEAR;
++ }
++ } /* Got AC link */
++
++done:
++ return(status);
++} /* Ac97CtrlCodecWrite() */
++
++#define CKENA __REG(0x4134000C) /* A Clock Enable Register */
++#define CKENB __REG(0x41340010)
++
++zy_acodec_error_t zy_ac97_acodec_read (zy_acocec_context_t *p_dev_context, unsigned short offset, unsigned short *pdata)
++{
++ zy_acodec_error_t status = ZY_ACODEC_SUCCESS;
++ int got_link;
++ unsigned long time_remaining;
++ volatile unsigned long * p_codec_reg;
++ p_zy_ac97acodec_t p_ac97_reg = (p_zy_ac97acodec_t)(p_dev_context->p_ctrl_reg);
++ unsigned long max_rw_time_out_us = (p_dev_context->u_max_read_write_time_out_ms) * 1000;
++
++ /* Point to specified register within area mapped to target codec regs */
++ p_codec_reg = &(p_ac97_reg->codec_regs_primary_aud[0]);
++ p_codec_reg += offset / ZY_AC97_CODEC_REGS_PER_WORD;
++
++ /* Lock the ACLINK */
++ time_remaining = ZY_AC97_LOCK_TIMEOUT_DEF;
++ do
++ {
++ got_link = zy_ac97_acodec_link_lock(p_ac97_reg);
++ if (0 == got_link) /* 1 usec is a long time. Skip delay if possible. */
++ {
++ udelay(1);
++ }
++ } /* Wait while time remaining and ACLINK not available */
++ while (time_remaining-- && (0 == got_link));
++
++ if (0 == got_link) /* Didn't get the ACLINK */
++ {
++ status = ZY_ACODEC_CONTROLLER_INTERFACE_TIMEOUT;
++ printk(KERN_ERR "AC97 Read Link Timeout\n");
++ }
++ else /* We got the link. Perform the write operation and don't wait. */
++ {
++ /* First, clear old read status indications. */
++ p_ac97_reg->gsr = ZY_AC97_GSR_SDONE_MSK | ZY_AC97_GSR_RCS_ERR_MSK;
++
++ *pdata = (unsigned short)(*p_codec_reg); /* This is THE DUMMY READ. */
++
++ /* Wait for read I/O with codec to complete before doing real read. */
++ time_remaining = max_rw_time_out_us;
++ do
++ {
++ udelay(1);
++ } /* Wait while time remaining and read I/O still incomplete */
++ while( (time_remaining--) && (!(p_ac97_reg->gsr & ZY_AC97_GSR_SDONE_MSK)) );
++
++ if ((p_ac97_reg->gsr & ZY_AC97_GSR_SDONE_MSK) && (!(p_ac97_reg->gsr & ZY_AC97_GSR_RCS_ERR_MSK)) )
++ {
++ if (p_ac97_reg->gsr & ZY_AC97_GSR_RCS_ERR_MSK)
++ {/* timeout indicated by RCS bit */
++ status = ZY_ACODEC_CONTROLLER_INTERFACE_TIMEOUT;
++ }
++ /* succeed in reading. clear status bits first. */
++ p_ac97_reg->gsr = ZY_AC97_GSR_SDONE_MSK | ZY_AC97_GSR_RCS_ERR_MSK;
++ *pdata = (unsigned short)(*p_codec_reg); /* THE REAL READ. */
++ if (*pdata == 0xffff)
++ {/* timeout indicated by returned value */
++ status = ZY_ACODEC_CONTROLLER_INTERFACE_TIMEOUT;
++ }
++ /* check later: is second waiting really needed? */
++ time_remaining = max_rw_time_out_us;
++ do
++ {
++ udelay(1);
++ } /* Wait while time remaining and read I/O still incomplete */
++ while( (time_remaining--) && (!(p_ac97_reg->gsr & ZY_AC97_GSR_SDONE_MSK)) );
++ //printk(KERN_ERR "AC97 Read Result %d\n", (p_ac97_reg->gsr & ZY_AC97_GSR_SDONE_MSK) );
++ }
++ else /* failed */
++ {
++ status = ZY_ACODEC_CONTROLLER_INTERFACE_TIMEOUT;
++ p_ac97_reg->car = ZY_AC97_CAR_CAIP_CLEAR;
++ //printk(KERN_ERR "AC97 Read Link Timeout2 %x %x %x\n", CKENA, OSCC, CKENB);
++ } /* else (OK to do real read) */
++
++ } /* else (We got the link. Perform the read operations.) */
++
++ return (status);
++}
++
++
++
++zy_acodec_error_t zy_acodec_get_adc_sample(zy_acocec_context_t *p_device_context, unsigned short *p_sample_data, unsigned short adc_type, int *p_pen_down)
++{
++ zy_acodec_error_t status = ZY_ACODEC_SUCCESS;
++ unsigned short value;
++ unsigned long wait;
++
++ if (adc_type == ZY_TOUCH_SAMPLE_X)
++ {
++ value = 0x202;
++ }
++ else
++ {/* Y sample */
++ value = 0x204;
++ }
++
++ status = zy_ac97_acodec_write(p_device_context, DIGITIZER_1_WM13, value);
++
++ wait = 0;
++ do
++ {
++ status = zy_ac97_acodec_read(p_device_context, DIGITIZER_1_WM13, &value);
++ if ( !(value & 0x200 ) )
++ {
++ break;
++ }
++ }while ( 100 > wait++ );
++
++ status = zy_ac97_acodec_read(p_device_context, DIGITIZER_READ_BACK, &value);
++ if (value & 0x8000)
++ {/* means pen down */
++ *p_pen_down = 1;
++ }
++ else
++ {
++ *p_pen_down = 0;
++ }
++ *p_sample_data = value & 0xfff;
++
++ return status;
++}
++
++
++
++/*
++ * add a touch event
++ */
++static int codec_zy_ts_evt_add(codec_zy_ts_t* ts, u16 pressure, u16 x, u16 y)
++{
++ /* add event and remove adc src bits */
++ static u16 pre_press = 0;
++
++ input_report_abs(ts->idev, ABS_X, x & 0xfff);
++ input_report_abs(ts->idev, ABS_Y, y & 0xfff);
++ if (pressure == pre_press){
++ pressure--;
++ }
++ pre_press = pressure;
++ input_report_abs(ts->idev, ABS_PRESSURE, pressure & 0xfff);
++ input_sync(ts->idev);
++#ifdef CONFIG_IPM
++ ipm_event_notify(IPM_EVENT_UI, IPM_EVENT_DEVICE_TSI, NULL, 0);
++#endif
++ return 0;
++}
++
++/*
++ * add a pen up event
++ */
++static void codec_zy_ts_evt_release(codec_zy_ts_t* ts)
++{
++ input_report_abs(ts->idev, ABS_PRESSURE, 0);
++ input_sync(ts->idev);
++
++#ifdef CONFIG_IPM
++ ipm_event_notify(IPM_EVENT_UI, IPM_EVENT_DEVICE_TSI, NULL, 0);
++#endif
++ p_zy_codec_ctxt->g_pfn_event_ack(p_zy_codec_ctxt,ZY_EVENT_TYPE_PDN);
++}
++
++/*
++ * Kill the touchscreen thread and stop
++ * the touch digitiser.
++ */
++static void codec_zy_ts_input_close(struct input_dev *idev)
++{
++ codec_zy_ts_t *ts = (codec_zy_ts_t *) &codec_zy_ts;
++
++#ifdef CONFIG_PM
++ if(touch_suspend){
++ pr_info("touch is suspended!\n");
++ return -1;
++ }
++#endif
++ dbg("close ts input!\n");
++ if (--ts->use_count == 0) {
++ del_timer(ts->timer);
++ if (ts->timer != NULL)
++ kfree(ts->timer);
++ p_zy_codec_ctxt->g_pfn_disable_touch(p_zy_codec_ctxt);
++ }
++}
++
++/*
++ * Sample the touchscreen
++ */
++int ac97_poll_touch(codec_zy_ts_t *ts)
++{
++ unsigned short x=0, y=0;
++ int if_down= 0;
++ zy_acodec_error_t status = ZY_ACODEC_SUCCESS;
++
++#ifdef DEBUG
++ start_time = OSCR;
++#endif
++
++ /* get x value */
++ status = zy_acodec_get_adc_sample(p_zy_codec_ctxt, &x, ZY_TOUCH_SAMPLE_X, &if_down);
++ if (ZY_ACODEC_SUCCESS != status ){
++ return -EIO;
++ }
++ dbg("x:0x%x\n", x);
++
++ /* the pen is up */
++ if (1 != if_down){
++ return PEN_UP;
++ }
++
++ /* get y vaule */
++ status = zy_acodec_get_adc_sample(p_zy_codec_ctxt, &y, ZY_TOUCH_SAMPLE_Y, &if_down);
++ if (ZY_ACODEC_SUCCESS != status ){
++ return -EIO;
++ }
++ dbg("y:0x%x\n",y);
++
++ /* the pen is up */
++ if (1 != if_down){
++ return PEN_UP;
++ }
++
++ /* the pen is down, can not get the pressure value,
++ * so if pen is down, give the max pressure value
++ */
++ codec_zy_ts_evt_add(ts,0xfff, x, y);
++
++#ifdef DEBUG
++ end_time = OSCR;
++ PRINT_TIME();
++#endif
++
++ return PEN_DOWN;
++}
++
++static void touch_timer_handler(unsigned long unused)
++{
++ int event;
++ codec_zy_ts_t *ts = &codec_zy_ts;
++
++ event = ac97_poll_touch(ts);
++
++ if (event == PEN_DOWN) {
++ dbg("pen down!\n");
++ ts->timer->expires = jiffies + TS_SAMPLE_INTERVAL;
++ add_timer(ts->timer);
++ } else if(event == PEN_UP) {
++ dbg("pen up!\n");
++ codec_zy_ts_evt_release(ts);
++ } else if(event == -EIO) {
++ printk(KERN_ERR "Access touch interface error!\n");
++ }
++ return;
++}
++
++static zy_acodec_error_t zy_ac97_acodec_cold_reset(zy_acocec_context_t * p_ac97_ctxt)
++{
++ zy_acodec_error_t status = ZY_ACODEC_SUCCESS;
++ p_zy_ac97acodec_t p_ac97 = (p_zy_ac97acodec_t)(p_ac97_ctxt->p_ctrl_reg);
++ int pri_codec_ready;
++ unsigned long time_remaining;
++
++ p_ac97->gcr = 0;
++ p_ac97->gcr |= ZY_AC97_GCR_CLKBPB_MSK;
++ /* Hold reset active for a minimum time */
++ udelay(ZY_AC97_COLD_HOLDTIME);
++ p_ac97->gcr &= ~ZY_AC97_GCR_CLKBPB_MSK;
++
++ /* Deactivate cold reset condition */
++ p_ac97->gcr |= (ZY_AC97_GCR_COLD_RESET_MSK | ZY_AC97_GCR_WARM_RESET_MSK);
++
++
++ pri_codec_ready = 0;
++ time_remaining = (p_ac97_ctxt->u_max_setup_time_out_ms) * 10;
++ do
++ {
++ udelay(1);
++ if (p_ac97->gsr & ZY_AC97_GSR_PCRDY_MSK)
++ pri_codec_ready = 1;
++ }
++ while (time_remaining-- && (pri_codec_ready == 0));
++
++ /* Timeout status if some of the devices weren't ready. */
++ if (pri_codec_ready == 0)
++ {
++ status = ZY_ACODEC_CONTROLLER_INTERFACE_TIMEOUT;
++ }
++
++ return (status);
++}
++
++
++zy_acodec_error_t zy_ac97_acodec_init(zy_acocec_context_t *p_ac97_ctxt)
++{
++ zy_acodec_error_t status ;
++
++ status = zy_ac97_acodec_cold_reset(p_ac97_ctxt);
++
++ return (status);
++}
++
++
++/*
++ * Start the touchscreen thread and
++ * the touch digitiser.
++ */
++static int codec_zy_ts_input_open(struct input_dev *idev)
++{
++ codec_zy_ts_t *ts = (codec_zy_ts_t *) &codec_zy_ts;
++
++#ifdef CONFIG_PM
++ if(touch_suspend){
++ pr_info("touch is suspended!\n");
++ return -1;
++ }
++#endif
++
++ if (ts->use_count++ > 0)
++ return 0;
++
++ dbg("Touch is opened. Use count: %d\n", ts->use_count);
++ ts->idev = idev;
++ ts->timer = kmalloc(sizeof(struct timer_list), GFP_KERNEL);
++ if (!ts->timer) {
++ printk(KERN_ERR "Alloc memory error for timer!\n");
++ return -ENOMEM;
++ }
++
++ init_timer(ts->timer);
++ ts->timer->function = touch_timer_handler;
++ ts->timer->data = 0;
++ p_zy_codec_ctxt->g_pfn_enable_touch(p_zy_codec_ctxt);
++
++ return 0;
++}
++
++/*
++ * initilze the pxa touch screen
++ */
++static int alsa_ts_init( void )
++{
++ codec_zy_ts_t* ts = &codec_zy_ts;
++
++ memset(ts, 0, sizeof(codec_zy_ts_t));
++
++ codec_zy_ts_input = input_allocate_device();
++ if (!codec_zy_ts_input)
++ return -ENOMEM;
++
++
++ /* tell input system what we events we accept and register */
++ codec_zy_ts_input->name = "codec zy touchscreen";
++ codec_zy_ts_input->open = codec_zy_ts_input_open;
++ codec_zy_ts_input->close = codec_zy_ts_input_close;
++ __set_bit(EV_ABS, codec_zy_ts_input->evbit);
++ __set_bit(ABS_X, codec_zy_ts_input->absbit);
++ __set_bit(ABS_Y, codec_zy_ts_input->absbit);
++ __set_bit(ABS_PRESSURE, codec_zy_ts_input->absbit);
++ input_register_device(codec_zy_ts_input);
++
++ return 0;
++}
++
++static irqreturn_t pxa_touch_irq(int irq, void *dev)
++{
++ unsigned char event_type;
++
++ //printk(KERN_ERR "%s: enter codec event handler\n", __FUNCTION__);
++
++ dbg("%s: enter codec event handler\n", __FUNCTION__);
++ p_zy_codec_ctxt->g_pfn_get_event(p_zy_codec_ctxt, &event_type);
++ switch (event_type) {
++ case ZY_EVENT_TYPE_PDN:
++ {
++ codec_zy_ts_t *ts = &codec_zy_ts;
++ /*if the touch is not open need not acknowledge the event*/
++ if (ts->use_count <= 0)
++ break;
++ ts->timer->expires = jiffies + TS_SAMPLE_INTERVAL;
++ add_timer(ts->timer);
++ break;
++ }
++ default:
++ printk("unsupported codec event:0x%x\n", event_type);
++ }
++
++ return IRQ_HANDLED;
++}
++
++
++
++
++
++
++
++
++
++static mfp_cfg_t extra_cfg[] = {
++ MFP_CFG_X(GPIO17, AF3, DS03X, PULL_LOW),
++ MFP_CFG_X(GPIO25, AF0, DS01X, PULL_LOW),
++};
++
++#define ARRAY_AND_SIZE(x) (x), ARRAY_SIZE(x)
++
++extern void dump_mfp(void);
++
++zy_acodec_error_t zy_ac97_acodec_mfp_init(zy_acocec_context_t *p_device_context)
++{
++ unsigned short codec_id;
++
++ //mhn_mfp_set_afds(MFP_RSVD_AC97_SDATA_IN_0, MFP_AF0, MFP_DS03X);
++ //enable_ac97_pins();
++ zy_ac97_acodec_init(p_device_context);
++ if (zy_ac97_acodec_read(p_device_context, 0x0, &codec_id)){
++
++ /*
++ * there is a bug on MonahansL/MonhansPL PC card: AC97_SDATA_IN is not connected to CODEC
++ * ECO 72: Connect PWM_0(MFP_RSVD_AC97_SDATA_IN_0) to CODEC as AC97_SDATA_IN
++ */
++
++ //mhn_mfp_set_afds(MFP_RSVD_AC97_SDATA_IN_0, MFP_RSVD_AC97_SDATA_IN_0_AF, MFP_DS03X);
++ //mhn_mfp_set_afds(MFP_AC97_SDATA_IN_0, MFP_AF0, MFP_DS01X);
++
++ gpio_direction_output(mfp_to_gpio(MFP_PIN_GPIO17), 0);
++ pxa3xx_mfp_config(ARRAY_AND_SIZE(extra_cfg));
++ gpio_direction_input(mfp_to_gpio(MFP_PIN_GPIO25));
++ }
++
++
++ return ZY_ACODEC_SUCCESS;
++}
++
++#define ZY_AC97_WM9713_GPIO_PIN_PDN ( 0x1 << 13 ) /* Pen down */
++
++/*power enable bit in 3ch and 3eh */
++/*3ch */
++#define ZY_AC97_9713_PWR_PADCPD ( 0x1 << 15 )
++#define ZY_AC97_9713_PWR_VMID ( 0x1 << 14 )
++#define ZY_AC97_9713_PWR_TSHUT ( 0x1 << 13 )
++#define ZY_AC97_9713_PWR_VXDAC ( 0x1 << 12 )
++#define ZY_AC97_9713_PWR_AUXDAC ( 0x1 << 11 )
++#define ZY_AC97_9713_PWR_MBIAS ( 0x1 << 10 )
++#define ZY_AC97_9713_PWR_PLL ( 0x1 << 9 )
++#define ZY_AC97_9713_PWR_DACL ( 0x1 << 7 )
++#define ZY_AC97_9713_PWR_DACR ( 0x1 << 6 )
++#define ZY_AC97_9713_PWR_ADCL ( 0x1 << 5 )
++#define ZY_AC97_9713_PWR_ADCR ( 0x1 << 4 )
++#define ZY_AC97_9713_PWR_HPLX ( 0x1 << 3 )
++#define ZY_AC97_9713_PWR_HPRX ( 0x1 << 2 )
++#define ZY_AC97_9713_PWR_SPKX ( 0x1 << 1 )
++#define ZY_AC97_9713_PWR_MX ( 0x1 << 0 )
++
++/*3EH */
++#define ZY_AC97_9713_PWR_MCD ( 0x1 << 15 )
++#define ZY_AC97_9713_PWR_MICBIAS ( 0x1 << 14 )
++#define ZY_AC97_9713_PWR_MONO ( 0x1 << 13 )
++#define ZY_AC97_9713_PWR_OUT4 ( 0x1 << 12 )
++#define ZY_AC97_9713_PWR_OUT3 ( 0x1 << 11 )
++#define ZY_AC97_9713_PWR_HPL ( 0x1 << 10 )
++#define ZY_AC97_9713_PWR_HPR ( 0x1 << 9 )
++#define ZY_AC97_9713_PWR_SPKL ( 0x1 << 8 )
++#define ZY_AC97_9713_PWR_SPKR ( 0x1 << 7 )
++#define ZY_AC97_9713_PWR_LL ( 0x1 << 6 )
++#define ZY_AC97_9713_PWR_LR ( 0x1 << 5 )
++#define ZY_AC97_9713_PWR_MOIN ( 0x1 << 4 )
++#define ZY_AC97_9713_PWR_MA ( 0x1 << 3 )
++#define ZY_AC97_9713_PWR_MB ( 0x1 << 2 )
++#define ZY_AC97_9713_PWR_MPA ( 0x1 << 1 )
++#define ZY_AC97_9713_PWR_MPB ( 0x1 << 0 )
++
++
++void zy_wm9713_get_event(zy_acocec_context_t *p_device_context, unsigned char * event_type)
++{
++ unsigned short event_state = 0;
++ zy_ac97_acodec_read(p_device_context, GPIO_PIN_STATUS, &event_state);
++ if(event_state & ZY_AC97_WM9713_GPIO_PIN_PDN){
++ *event_type = ZY_EVENT_TYPE_PDN;
++ return;
++ }
++ return;
++}
++
++void zy_wm9713_event_ack(zy_acocec_context_t *p_device_context, unsigned char event_type)
++{
++ unsigned short event_state = 0;
++ zy_ac97_acodec_read(p_device_context, GPIO_PIN_STATUS, &event_state);
++ if( event_type == ZY_EVENT_TYPE_PDN){
++ zy_ac97_acodec_write(p_device_context,
++ GPIO_PIN_STATUS,
++ (event_state & (~ZY_AC97_WM9713_GPIO_PIN_PDN)));
++ }
++
++ zy_ac97_acodec_read(p_device_context, GPIO_PIN_STATUS, &event_state);
++ return;
++}
++
++static void * p_saved_memory = NULL;
++static void * p_zy_scenario = NULL;
++static p_zy_acocec_context_t p_zy_ctxt = NULL;
++
++#define WM9713_SAVE_REGISTER_NO (64-11)
++typedef struct {
++ unsigned short wm9713RegisterContext [WM9713_SAVE_REGISTER_NO + 1]; /* Fixed (data misalignment error) */
++}ZY_9713_CONTEXT_SAVE_T;
++
++
++/**
++ * alsa_prepare_for_zy - create and initialize the p_zy_acocec_context_t
++ * open the clock of data link
++ * @p_p_zy_ctxt: return the data structure p_zy_acocec_context_t
++ * return: 0 success ; -ENOMEM
++ **/
++int alsa_prepare_for_zy(p_zy_acocec_context_t * p_p_zy_ctxt)
++{
++ if (p_zy_ctxt) {
++ p_zy_ctxt->use_count++;
++ *p_p_zy_ctxt = p_zy_ctxt;
++ return 0;
++ }
++
++ p_zy_ctxt = kzalloc(sizeof(zy_acocec_context_t), GFP_KERNEL);
++ if (!p_zy_ctxt)
++ return -ENOMEM;
++
++ /* enable CLK_POUT as CODEC clock input */
++ OSCC |= 0x800;
++
++ p_saved_memory = kzalloc(sizeof(ZY_9713_CONTEXT_SAVE_T) +
++ sizeof(zy_ac97_save_context_t), GFP_KERNEL);
++ if (NULL == p_saved_memory) {
++ if (p_zy_ctxt)
++ kfree(p_zy_ctxt);
++ return -ENOMEM;
++ }
++
++ p_zy_ctxt->acodec_id = (zy_acodec_device_id_t) (WM_9713_ID);
++ p_zy_ctxt->use_count++;
++ /*
++ p_zy_ctxt->pMfpRegBase = (unsigned long) (MFP_BASE);
++ p_zy_ctxt->pMfpRmDb = ZY_MFP_RM_DATABASE;
++ p_zy_ctxt->p_ost_regs = OST_BASE;
++ */
++ p_zy_ctxt->p_voice_reg = NULL;
++ p_zy_ctxt->p_hifi_reg = (void *) (&POCR);
++ p_zy_ctxt->p_ctrl_reg = (void *) (&POCR);
++ p_zy_ctxt->u_max_read_write_time_out_ms = ZY_AC97_RW_TIMEOUT_DEF;
++ p_zy_ctxt->u_max_setup_time_out_ms = ZY_AC97_SETUP_TIMEOUT_DEF;
++ p_zy_ctxt->p_save_memory = p_saved_memory;
++ p_zy_ctxt->p_zy_scenario = p_zy_scenario;
++// pxa_set_cken(24, 1);
++ CKENA |= (1 << 24);
++ AC97_DIV = 1625<<12 | 128;
++#ifdef DEBUG_ALSA_ZY
++ debug_pac97ctxt = p_zy_ctxt;
++ misc_register(&audio_dev);
++#endif
++
++ (*p_p_zy_ctxt) = p_zy_ctxt;
++
++ return 0;
++}
++
++
++/* this is platform specific */
++/* do later: not to enable recording route and playback route in this function,
++ * leave it to driver to call other function
++ */
++zy_acodec_error_t zy_wm9713_specific_init (zy_acocec_context_t *p_device_context)
++{
++
++ unsigned short value;
++
++ /* this assumes that the aclink is initialized wait some time and then
++ * do a warm reset to enabled the ACLINK, required for wm9713
++ * (not wm9712 or ucb1400)
++ */
++
++ /* pay attention: whether the workaround is still needed? */
++ p_zy_ac97acodec_t p_ac97_reg = (p_zy_ac97acodec_t)(p_device_context->p_ctrl_reg);
++
++ p_ac97_reg->gcr |= ZY_AC97_GCR_WARM_RESET_MSK;
++
++ mdelay(5);
++
++ /* power on all the necessary unit */
++ zy_ac97_acodec_write(p_device_context,POWERDOWN_CTRL_STAT, 0x000); /*26*/
++ /* open left headphone mixer */
++ /* open right headphone mixer */
++ /* open right/left dac */
++ /* open right/left adc */
++ /* open temperature sensor */
++ /* enable reference generator */
++ zy_ac97_acodec_write(p_device_context,POWER_DOWN_1, 0xda00); /*3c */
++ /* open microphone bias */
++ /* open HPL output PGA */
++ /* open HPR output PGA */
++ /* open mic PGA MA */
++ /* open mic pre-amp MPA */
++ /* if here we enable SPKL and SPKR PGA, then Touch screen will doesn't work */
++ zy_ac97_acodec_write(p_device_context,POWER_DOWN_2,0xb9f5); /*3e */
++
++ /* recording route and microphone input */
++ /* microphone selection, now fixed to MIC1 input and mic bias output */
++ /* MIC1 only, MICBIAS enable */
++ zy_ac97_acodec_write (p_device_context, MIC_BIAS, 0xC440); /*0x22h*/
++
++ /* mic pga setting to mixer (side tone) */
++ /* comment the below code to make MICA/B play back volume gain + 0db */
++ /* zy_ac97_acodec_write (p_device_context, MIC_PGA_VOLUME, 0x0000); */ /*0x0eh*/
++
++ /* recording side tone and ADC boost, now fixed to default (14h) */
++ /* recording volume 0dB */
++ zy_ac97_acodec_write(p_device_context, REC_PGA_VOL, 0x0); /*12*/
++
++ /* hifi playback route and output mixer */
++ /* by default, fixed to enable headphone only */
++
++ /* comment the below code to make SPEAKER default MUTE */
++ zy_ac97_acodec_write (p_device_context, SPEAKER_VOLUME, 0x0); /*02*/
++
++ /* comment the below code to make OUT3_OUT4 default MUTE */
++ /* zy_ac97_acodec_write (p_device_context, OUT3_OUT4_VOLUME, 0x8000); */ /*06*/
++
++ /* remove all the mute bit volume gain + 0db */
++ zy_ac97_acodec_write(p_device_context, HEADPHONE_VOLUME, 0x0); /*04*/
++
++ /* DAC route */
++ /* open DAC to headphone mixer path */
++ /* left DAC gain +0db */
++ /* right DAC gain +0db */
++ zy_ac97_acodec_write(p_device_context, DAC_PGA_VOL_ROUTE,0x0808); /*0c*/
++
++ /* out3 configure, invert to HPMIXR */
++ /* zy_ac97_acodec_write(p_device_context,DAC_3D_CTRL_INV_MUX_SEL, 0x8000); */ /*1e*/
++
++ /* output control */
++ /* select HPMIXR HPMIXL out */
++ /* other out are all VIM */
++ zy_ac97_acodec_write(p_device_context,OUTPUT_PGA_MUX, 0x9BA8); /*1c*/
++
++ /* set sample rates */
++ /* enable variable rate conversion */
++ zy_ac97_acodec_write(p_device_context, EXTENDED_AUD_STAT_CTRL , 0x1); /*2a*/
++ /* DAC 44kHZ */
++ zy_ac97_acodec_write(p_device_context,AUDIO_DAC_RATE,0xac44); /*2c*/
++ /* ADC 16KHZ */
++ zy_ac97_acodec_write(p_device_context,AUDIO_ADC_RATE,0x3E80); /*32*/
++
++ /* clock scheme, use external clock, it is 24MHZ from MCLK_A */
++
++
++ zy_ac97_acodec_read(p_device_context, MCLK_PLL_CTRL_1, &value);
++ zy_ac97_acodec_write(p_device_context, MCLK_PLL_CTRL_1, value | 0x2);
++
++ return ZY_ACODEC_SUCCESS;
++}
++
++zy_acodec_error_t zy_wm9713_specific_deinit (zy_acocec_context_t *p_device_context)
++{/* do later: shut down all power */
++ unsigned short value = 0;
++
++ /* close the power of all units */
++ zy_ac97_acodec_write(p_device_context, POWER_DOWN_1, 0xffff);
++ zy_ac97_acodec_write(p_device_context, POWER_DOWN_2, 0xffff);
++ zy_ac97_acodec_read(p_device_context, POWER_DOWN_1, &value);
++ value &= ~(ZY_AC97_9713_PWR_MBIAS);
++ zy_ac97_acodec_write(p_device_context, POWER_DOWN_1, value);
++
++ return ZY_ACODEC_SUCCESS;
++}
++
++zy_acodec_error_t zy_acodec_set_pen_down_interrupt(zy_acocec_context_t *p_device_context, int enable)
++{/* disable/enable pen down interrupt in the codec. This function is not implemented for Wm9713 */
++ /* because the pen down detection could not be disabled in codec */
++ return ZY_ACODEC_SUCCESS;
++}
++
++zy_acodec_error_t zy_wm9713_enable_touch(zy_acocec_context_t *p_device_context)
++{/* enable touch functionality in the codec */
++ zy_acodec_error_t status = ZY_ACODEC_SUCCESS;
++ unsigned short value;
++
++ /* power setting */
++ status = zy_ac97_acodec_read(p_device_context, POWER_DOWN_1, &value);
++ value &= ~(ZY_AC97_9713_PWR_PADCPD);
++ status = zy_ac97_acodec_write(p_device_context, POWER_DOWN_1, value);
++
++ /* basic touch setting */
++ status = zy_ac97_acodec_write(p_device_context, DIGITIZER_3_WM13, 0xc008);
++ status = zy_ac97_acodec_write(p_device_context, DIGITIZER_2_WM13, 0x6);
++
++
++ /* 9713 powerdown virtual gpio setting (polarity, sticky, wakeup) */
++ /* 9713 gpio 2(pin45) route to IRQ */
++ /* Notes: Can use defaults for IRQ polarity, PENDOWN polarity in IRQ, */
++ /* sticky for PENDOWN in IRQ and wakeup for PENDOWN. */
++ status = zy_ac97_acodec_read(p_device_context, GPIO_PIN_CFG, &value);
++ value &= ~(0x4);
++ status = zy_ac97_acodec_write(p_device_context, GPIO_PIN_CFG, value);
++
++ status = zy_ac97_acodec_read(p_device_context, GPIO_PIN_SHARING, &value);
++ value &= ~(0x4);
++ status = zy_ac97_acodec_write(p_device_context, GPIO_PIN_SHARING, value);
++
++ status = zy_ac97_acodec_read(p_device_context, GPIO_PIN_WAKEUP, &value);
++ value |= (0x2000);
++ status = zy_ac97_acodec_write(p_device_context, GPIO_PIN_WAKEUP, value);
++
++ status = zy_ac97_acodec_read(p_device_context, GPIO_PIN_STICKY, &value);
++ value |= (0x2000);
++ status = zy_ac97_acodec_write(p_device_context, GPIO_PIN_STICKY, value);
++
++ return status;
++}
++
++zy_acodec_error_t zy_wm9713_disable_touch(zy_acocec_context_t *p_device_context)
++{/* disable touch functionality in the codec */
++ zy_acodec_error_t status = ZY_ACODEC_SUCCESS;
++ unsigned short value;
++
++ /* power setting */
++ status = zy_ac97_acodec_read(p_device_context, POWER_DOWN_1, &value);
++ value |= (ZY_AC97_9713_PWR_PADCPD);
++ status = zy_ac97_acodec_write(p_device_context, POWER_DOWN_1, value);
++
++ return status;
++}
++zy_acodec_error_t zy_ac97_acodec_mfp_deinit(zy_acocec_context_t *p_device_context)
++{/* do later: free all MFP resources. */
++ return ZY_ACODEC_SUCCESS;
++}
++
++static zy_acodec_error_t zy_ac97_acodec_shut_down_aclink(p_zy_ac97acodec_t p_ac97_reg, int * p_ost_regs)
++{
++ zy_acodec_error_t status = ZY_ACODEC_SUCCESS;
++ unsigned long time_remaining = ZY_AC97_LINKOFF_TIMEOUT_DEF;
++
++ p_ac97_reg->gcr |= ZY_AC97_GCR_LINK_OFF_MSK;
++ p_ac97_reg->gcr |= ZY_AC97_GCR_CLKBPB_MSK;
++
++ while (!(p_ac97_reg->gsr & ZY_AC97_GSR_ACOFFD_MSK))
++ {
++ time_remaining --;
++ if (0 == time_remaining)
++ {
++ status = ZY_ACODEC_CONTROLLER_INTERFACE_TIMEOUT;
++ break;
++ }
++ udelay(1);
++ }
++ p_ac97_reg->gcr |= ZY_AC97_GCR_FRCRST_MSK;
++ /* check later: any delay needed */
++ p_ac97_reg->gcr &= ~ZY_AC97_GCR_FRCRST_MSK;
++ p_ac97_reg->gcr &= ~ZY_AC97_GCR_CLKBPB_MSK;
++
++ return(status);
++}
++
++
++zy_acodec_error_t zy_ac97_acodec_deinit(zy_acocec_context_t * p_ac97_ctxt)
++{
++ zy_acodec_error_t status ;
++
++ status = zy_ac97_acodec_shut_down_aclink((p_zy_ac97acodec_t)(p_ac97_ctxt->p_ctrl_reg), p_ac97_ctxt->p_ost_regs);
++
++ return (status);
++}
++
++zy_acodec_error_t zy_acodec_deinit(zy_acocec_context_t *p_device_context)
++{
++ /* power down codec by codec specific power down function */
++ if (p_device_context->g_pfn_codec_specific_dinit)
++ {
++ p_device_context->g_pfn_codec_specific_dinit(p_device_context);
++ }
++ /* call bus deinit function */
++ zy_ac97_acodec_deinit(p_device_context);
++ /* restore MFP, set GPIO to suitable value */
++ zy_ac97_acodec_mfp_deinit(p_device_context);
++
++ return ZY_ACODEC_SUCCESS;
++}
++
++void alsa_zy_codec_put(p_zy_acocec_context_t p_acodectxt)
++{
++
++ zy_acodec_deinit(p_acodectxt);
++ //pxa_set_cken(24, 0);
++ CKENA &= ~(1 << 24);
++
++ if(p_acodectxt->p_save_memory){
++ kfree(p_saved_memory);
++ }
++ if(p_acodectxt->p_zy_scenario){
++ kfree(p_zy_scenario);
++ }
++}
++
++
++zy_acodec_error_t zy_acodec_init(zy_acocec_context_t *p_device_context, int hw_init)
++{
++ /* set codec specific functions
++ * set mfp for Zylonite platform
++ * call bus init function (AC97, I2S, I2C, SSP)
++ * call specific init of codec
++ */
++ zy_acodec_error_t retval = ZY_ACODEC_SUCCESS;
++
++ if (p_device_context->acodec_id != WM_9713_ID)
++ {/* on Zylonite, it is Wolfson 9713 codec only */
++ return ZY_ACODEC_GENERAL_SW_ERR;
++ }
++
++ if (1 == hw_init)
++ {
++ zy_ac97_acodec_mfp_init(p_device_context);
++ zy_ac97_acodec_init(p_device_context); /* codec init common to ac97 */
++ }
++
++ /* wm9713-specific functions */
++ (p_device_context->g_pfn_codec_specific_init) = zy_wm9713_specific_init;
++ (p_device_context->g_pfn_codec_specific_dinit) = zy_wm9713_specific_deinit;
++ (p_device_context->g_pfn_acodec_read) = zy_ac97_acodec_read;
++ (p_device_context->g_pfn_acodec_write) = zy_ac97_acodec_write;
++
++ (p_device_context->g_pfn_event_ack) = zy_wm9713_event_ack;
++ (p_device_context->g_pfn_get_event) = zy_wm9713_get_event;
++ (p_device_context->g_pfn_disable_touch) = zy_wm9713_disable_touch;
++ (p_device_context->g_pfn_enable_touch) = zy_wm9713_enable_touch;
++
++ if (1 == hw_init)
++ {
++ retval = p_device_context->g_pfn_codec_specific_init(p_device_context);
++ }
++
++ return retval;
++}
++
++static int __devinit touch_codec_zy_probe(struct platform_device *dev)
++{
++ int ret = 0;
++ struct snd_card *card = NULL;
++ zy_acodec_error_t status;
++
++ /* will increase codec context use count */
++ ret = alsa_prepare_for_zy(&p_zy_codec_ctxt);
++ if (ret)
++ goto err;
++
++ /* codec specific initialization, audio will do it either */
++ if (1 == p_zy_codec_ctxt->use_count) {
++ status = zy_acodec_init(p_zy_codec_ctxt, 1);
++ if (ZY_ACODEC_SUCCESS != status) {
++ printk(KERN_ERR "initialize codec error\n");
++ ret = -EIO;
++ goto err;
++ }
++
++ /* power down the units of the acodec, sleep the acodec, zy_acodec_init()
++ * will open all the units' power of the codec while ALSA need all the codec
++ * units power down and the codec should sleep if it can.
++ * So on the zylonite platform we call below function to power down and sleep
++ * wm9713 codec.
++ */
++ p_zy_codec_ctxt->g_pfn_codec_specific_dinit(p_zy_codec_ctxt);
++
++ }
++
++ alsa_ts_init();
++
++ //mhn_mfp_set_afds(MFP_AC97_INT_N_GPIO,0,0);
++ //mhn_gpio_set_direction(MFP_AC97_INT_N_GPIO, GPIO_DIR_IN);
++ //mhn_gpio_clear_edge_detect_status(MFP_AC97_INT_N_GPIO);
++ gpio_direction_input(mfp_to_gpio(MFP_PIN_GPIO26));
++ ret = request_irq(IRQ_GPIO(mfp_to_gpio(MFP_PIN_GPIO26)),
++ pxa_touch_irq, IRQF_TRIGGER_RISING,
++ "wm9713 touch event interrupt", NULL);
++ if (ret) {
++ printk(KERN_ERR "Request IRQ for touch failed (%d).\n", ret);
++ goto err;
++ }
++
++ return 0;
++err:
++ if (p_zy_codec_ctxt && (!--p_zy_codec_ctxt->use_count)) {
++ zy_acodec_deinit(p_zy_codec_ctxt);
++ //pxa_set_cken(24, 0);
++ CKENA &= ~(1 << 24);
++ kfree(p_zy_codec_ctxt);
++ p_zy_codec_ctxt = NULL;
++ }
++
++ if (card)
++ snd_card_free(card);
++
++ return ret;
++}
++
++static int __devexit touch_codec_zy_remove(struct platform_device *dev)
++{
++ struct snd_card *card = platform_get_drvdata(dev);
++
++ input_unregister_device(codec_zy_ts_input);
++
++ if (p_zy_codec_ctxt && (!--p_zy_codec_ctxt->use_count)) {
++ alsa_zy_codec_put(p_zy_codec_ctxt);
++ kfree(p_zy_codec_ctxt);
++ p_zy_codec_ctxt = NULL;
++ }
++
++ if (card) {
++ snd_card_free(card);
++ platform_set_drvdata(dev, NULL);
++ }
++
++ return 0;
++}
++
++#ifdef CONFIG_PM
++static int touch_codec_zy_suspend(struct platform_device *_dev, pm_message_t state, u32 level)
++{
++ int ret=0;
++
++ if (level == SUSPEND_DISABLE) {
++ ret = audio_codec_zy_do_suspend(NULL, SNDRV_CTL_POWER_D3cold, p_zy_codec_ctxt);
++ touch_suspend = 1;
++ }
++ return ret;
++}
++
++static int touch_codec_zy_resume(struct platform_device *_dev, u32 level)
++{
++ int ret = 0;
++
++ if (level == RESUME_ENABLE) {
++ ret = audio_codec_zy_do_resume(NULL, SNDRV_CTL_POWER_D0, p_zy_codec_ctxt);
++ touch_suspend = 0;
++ }
++ return ret;
++}
++#else
++#define touch_codec_zy_suspend NULL
++#define touch_codec_zy_resume NULL
++#endif
++
++static struct platform_driver touch_codec_zy_driver = {
++ .probe = touch_codec_zy_probe,
++ .remove = __devexit_p(touch_codec_zy_remove),
++ .suspend= touch_codec_zy_suspend,
++ .resume = touch_codec_zy_resume,
++ .driver = {
++ .name = "pxa2xx-touch",
++ },
++};
++
++static int __init touch_codec_zy_init(void)
++{
++ return platform_driver_register(&touch_codec_zy_driver);
++}
++
++static void __exit touch_code_zy_exit(void)
++{
++ platform_driver_unregister(&touch_codec_zy_driver);
++}
++module_init(touch_codec_zy_init);
++module_exit(touch_code_zy_exit);
++
++EXPORT_SYMBOL(p_zy_codec_ctxt);
++
++MODULE_AUTHOR("bridge.wu@marvell.com");
++MODULE_DESCRIPTION("zylonite audio touch codec driver on ALSA");
++MODULE_LICENSE("GPL");
++