diff -Nur /home/anderstj/linux-2.4.26/.cvsignore ./.cvsignore
--- /home/anderstj/linux-2.4.26/.cvsignore	1970-01-01 01:00:00.000000000 +0100
+++ ./.cvsignore	2001-11-08 15:19:08.000000000 +0100
@@ -0,0 +1,6 @@
+.config
+vmlinux
+vmlinux.bin
+System.map
+rescue.bin
+decompress.bin
diff -Nur /home/anderstj/linux-2.4.26/Documentation/Configure.help ./Documentation/Configure.help
--- /home/anderstj/linux-2.4.26/Documentation/Configure.help	2004-04-14 15:05:24.000000000 +0200
+++ ./Documentation/Configure.help	2004-04-19 10:16:21.000000000 +0200
@@ -14567,9 +14567,9 @@
 
 Debugging RAM test driver
 CONFIG_MTD_MTDRAM
-  This enables a test MTD device driver which uses vmalloc() to
-  provide storage.  You probably want to say 'N' unless you're
-  testing stuff.
+  This enables a test MTD device driver which uses vmalloc()
+  or an absolute address to provide storage.  
+  You probably want to say 'N' unless you're testing stuff.
 
   This driver is also available as a module ( = code which can be
   inserted in and removed from the running kernel whenever you want).
@@ -14590,6 +14590,8 @@
   emulated by the MTDRAM driver.  If the MTDRAM driver is built
   as a module, it is also possible to specify this as a parameter when
   loading the module.
+  If you want to set the size and position at runtime, set to 0, 
+  in that case set the ABS_POS parameter to 0 as well.
 
 SRAM Hexadecimal Absolute position or 0
 CONFIG_MTDRAM_ABS_POS
@@ -26699,15 +26701,15 @@
   you should say N to this option.
 
 # Choice: cristype
-Etrax-100-LX-v1
+ETRAX-100-LX-v1
 CONFIG_ETRAX100LX
-  Support version 1 of the Etrax 100LX.
+  Support version 1 of the ETRAX 100LX.
 
-Etrax-100-LX-v2
+ETRAX-100-LX-v2
 CONFIG_ETRAX100LX_V2
-  Support version 2 of the Etrax 100LX.
+  Support version 2 of the ETRAX 100LX.
 
-Etrax-100-LX-for-xsim-simulator
+ETRAX-100-LX-for-xsim-simulator
 CONFIG_SVINTO_SIM
   Support the xsim ETRAX Simulator.
 
@@ -26719,10 +26721,18 @@
 CONFIG_ETRAX_FLASH_BUSWIDTH
   Width in bytes of the Flash bus (1, 2 or 4). Is usually 2.
 
+Root filesystem device
+CONFIG_ETRAX_ROOT_DEVICE
+  Specifies the device that should be mounted as root filesystem
+  when booting from flash. The axisflashmap driver adds an additional
+  mtd partition for the appended root filesystem image, so this option
+  should normally be the mtdblock device for the partition after the
+  last partition in the partition table.
+
 # Choice: crisleds
 LED configuration on PA
 CONFIG_ETRAX_PA_LEDS
-  The Etrax network driver is responsible for flashing LED's when
+  The ETRAX network driver is responsible for flashing LED's when
   packets arrive and are sent.  It uses macros defined in
   <file:include/asm-cris/io.h>, and those macros are defined after what
   YOU choose in this option.  The actual bits used are configured
@@ -26731,7 +26741,7 @@
 
 LED configuration on PB
 CONFIG_ETRAX_PB_LEDS
-  The Etrax network driver is responsible for flashing LED's when
+  The ETRAX network driver is responsible for flashing LED's when
   packets arrive and are sent.  It uses macros defined in
   <file:include/asm-cris/io.h>, and those macros are defined after what
   YOU choose in this option.  The actual bits used are configured
@@ -26740,7 +26750,7 @@
 
 LED configuration on CSP0
 CONFIG_ETRAX_CSP0_LEDS
-  The Etrax network driver is responsible for flashing LED's when
+  The ETRAX network driver is responsible for flashing LED's when
   packets arrive and are sent. It uses macros defined in
   <file:include/asm-cris/io.h>, and those macros are defined after what
   YOU choose in this option.  The actual bits used are configured
@@ -26868,14 +26878,34 @@
   For products with only one or two controllable LEDs,
   set this to same as CONFIG_ETRAX_LED1G (normally 2).
 
-Flash LED off during activity
-CONFIG_ETRAX_LED_OFF_DURING_ACTIVITY
-  This option allows you to decide whether the network LED (and
-  Bluetooth LED in case you use Bluetooth) will be on or off when
-  the network is connected, and whether it should flash off or on
-  when there is activity. If you say y to this option the network
-  LED will be lit when there is a connection, and will flash off
-  when there is activity.
+Network LED behavior
+CONFIG_ETRAX_NETWORK_LED_ON_WHEN_LINK
+  Selecting LED_on_when_link will light the LED when there is a 
+  connection and will flash off when there is activity. 
+  
+  Selecting LED_on_when_activity will light the LED only when 
+  there is activity.
+  
+  This setting will also affect the behaviour of other activity LEDs 
+  e.g. Bluetooth. 
+
+Network LED behavior
+CONFIG_ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY
+ Selecting LED_on_when_link will light the LED when there is a 
+  connection and will flash off when there is activity. 
+  
+  Selecting LED_on_when_activity will light the LED only when 
+  there is activity.
+  
+  This setting will also affect the behaviour of other activity LEDs 
+  e.g. Bluetooth. 
+
+Network LED behavior on no connection 
+RED_LED_on_no_connection 
+  Will signal a lost network connection by turning network led red.
+
+LED_OFF_on_no_connection
+  Network led is turned off when network connection is lost.
 
 PA button configuration
 CONFIG_ETRAX_PA_BUTTON_BITMASK
@@ -26885,7 +26915,7 @@
   use 02 here.
   Use 00 if there are no buttons on PA.
   If the bitmask is <> 00 a button driver will be included in the gpio
-  driver. Etrax general I/O support must be enabled.
+  driver. ETRAX general I/O support must be enabled.
 
 PA changeable direction bits
 CONFIG_ETRAX_PA_CHANGEABLE_DIR
@@ -26897,7 +26927,7 @@
 PA changeable data bits
 CONFIG_ETRAX_PA_CHANGEABLE_BITS
   This is a bitmask with information of what bits in PA that a user
-  can change change the value on using ioctl's.
+  can change the value on using ioctl's.
   Bit set = changeable.
   You probably want 00 here.
 
@@ -26928,17 +26958,29 @@
   didn't before).  The kernel halts when it boots, waiting for gdb if
   this option is turned on!
 
-Etrax bus waitstates
+ETRAX interrupt debugging
+CONFIG_ETRAX_DEBUG_INTERRUPT
+  This options enables logging of when interrupts are turned on and off.
+  The log can be seen in /proc/debug_interupt
+
+ETRAX fast timer API
+CONFIG_ETRAX_FAST_TIMER
+  This options enables the API to a fast timer implementation using
+  timer1 to get sub jiffie resolution timers (primarily one-shot 
+  timers).
+  This is needed if CONFIG_ETRAX_SERIAL_FAST_TIMER is enabled.
+
+ETRAX bus waitstates
 CONFIG_ETRAX_DEF_R_WAITSTATES
   Waitstates for SRAM, Flash and peripherals (not DRAM).  95f8 is a
   good choice for most Axis products...
 
-Etrax bus configuration
+ETRAX bus configuration
 CONFIG_ETRAX_DEF_R_BUS_CONFIG
   Assorted bits controlling write mode, DMA burst length etc.  104 is
   a good choice for most Axis products...
 
-Etrax SDRAM configuration
+ETRAX SDRAM configuration
 CONFIG_ETRAX_SDRAM
   Enable this if you use SDRAM chips and configure
   R_SDRAM_CONFIG and R_SDRAM_TIMING as well.
@@ -26946,29 +26988,29 @@
 DRAM size (dec, in MB)
 CONFIG_ETRAX_DEF_R_DRAM_CONFIG
   The R_DRAM_CONFIG register specifies everything on how the DRAM
-  chips in the system are connected to the Etrax CPU.  This is
+  chips in the system are connected to the ETRAX CPU.  This is
   different depending on the manufacturer, chip type and number of
   chips.  So this value often needs to be different for each Axis
   product.
 
-Etrax DRAM timing
+ETRAX DRAM timing
 CONFIG_ETRAX_DEF_R_DRAM_TIMING
   Different DRAM chips have different speeds.  Current Axis products
   use 50ns DRAM chips which can use the timing: 5611.
 
-Etrax SDRAM configuration
+ETRAX SDRAM configuration
 CONFIG_ETRAX_DEF_R_SDRAM_CONFIG
   The R_SDRAM_CONFIG register specifies everything on how the SDRAM
-  chips in the system are connected to the Etrax CPU.  This is
+  chips in the system are connected to the ETRAX CPU.  This is
   different depending on the manufacturer, chip type and number of
   chips.  So this value often needs to be different for each Axis
   product.
 
-Etrax SDRAM timing
+ETRAX SDRAM timing
 CONFIG_ETRAX_DEF_R_SDRAM_TIMING
   Different SDRAM chips have different timing.
 
-Etrax General port A direction
+ETRAX General port A direction
 CONFIG_ETRAX_DEF_R_PORT_PA_DIR
   Configures the direction of general port A bits.  1 is out, 0 is in.
   This is often totally different depending on the product used.
@@ -26979,17 +27021,17 @@
   stuff.  If you don't know what to use, it is always safe to put all
   as inputs, although floating inputs isn't good.
 
-Etrax General port A data
+ETRAX General port A data
 CONFIG_ETRAX_DEF_R_PORT_PA_DATA
   Configures the initial data for the general port A bits.  Most
   products should use 00 here.
 
-Etrax General port B config
+ETRAX General port B config
 CONFIG_ETRAX_DEF_R_PORT_PB_CONFIG
   Configures the type of the general port B bits.  1 is chip select,
   0 is port.  Most products should use 00 here.
 
-Etrax General port B direction
+ETRAX General port B direction
 CONFIG_ETRAX_DEF_R_PORT_PB_DIR
   Configures the direction of general port B bits. 1 is out, 0 is in.
   This is often totally different depending on the product used.  Bits
@@ -26999,14 +27041,28 @@
   If you don't know what to use, it is always safe to put all as
   inputs.
 
-Etrax General port B data
+ETRAX General port B data
 CONFIG_ETRAX_DEF_R_PORT_PB_DATA
-  Configures the initial data for the general port A bits.  Most
+  Configures the initial data for the general port B bits.  Most
   products should use FF here.
 
-Etrax General port device
+ETRAX shutdown support
+CONFIG_ETRAX_SOFT_SHUTDOWN
+  Enable this if ETRAX is used with a power-supply that can be turned
+  off and on with PS_ON signal. Gives the possibility to detect 
+  powerbutton and then do a power off after unmounting disks.
+
+ETRAX shutdown support output
+CONFIG_ETRAX_SHUTDOWN_BIT
+  Configure what pin on CSPO-port that is used for controlling power supply.
+
+ETRAX shutdown support input
+CONFIG_ETRAX_POWERBUTTON_BIT
+  Configure where power button is connected.    
+
+ETRAX General port device
 CONFIG_ETRAX_GPIO
-  Enables the Etrax general port device (major 120, minors 0 and 1).
+  Enables the ETRAX general port device (major 120, minors 0 and 1).
   You can use this driver to access the general port bits. It supports
   these ioctl's:
         #include <linux/etraxgpio.h>
@@ -27017,19 +27073,19 @@
   Remember that you need to setup the port directions appropriately in
   the General configuration.
 
-Etrax parallel data support
+ETRAX parallel data support
 CONFIG_ETRAX_PARDATA
   Adds support for writing data to the parallel port par0 of the ETRAX
   100.  If you create a character special file with major number 126,
   you can write to the data bits of par0.
-  Note: you need to disable Etrax100 parallel port support.
+  Note: you need to disable ETRAX 100 parallel port support.
 
-Etrax parallel LCD (HD44780) Driver
+ETRAX parallel LCD (HD44780) Driver
 CONFIG_ETRAX_LCD_HD44780
   Adds support for a HD44780 controlled LCD connected to the parallel
-  port par0 of the Etrax.
+  port par0 of the ETRAX.
 
-Etrax Serial port ser0 support
+ETRAX Serial port ser0 support
 CONFIG_ETRAX_SERIAL
   Enables the ETRAX 100 serial driver for ser0 (ttyS0)
   You probably want this enabled.
@@ -27039,28 +27095,54 @@
   Enables /proc/serial entry where errors and statistics can be
   viewed.  CONFIG_PROC_FS must also be set for this to work.
 
-Etrax Serial port fast flush of DMA using fast timer API
+ETRAX Serial port fast flush of DMA using fast timer API
 CONFIG_ETRAX_SERIAL_FAST_TIMER
   Select this to have the serial DMAs flushed at a higher rate than
   normally, possible by using the fast timer API, the timeout is
   approx. 4 character times.
   If unsure, say N.
 
-Etrax Serial port fast flush of DMA
+ETRAX Serial port fast flush of DMA
 CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST
   Select this to have the serial DMAs flushed at a higher rate than
   normally possible through a fast timer interrupt (currently at
   15360 Hz).
   If unsure, say N.
 
-Etrax Serial port receive flush timeout
+ETRAX Serial port receive flush timeout
 CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS
   Number of timer ticks between flush of receive fifo (1 tick = 10ms).
-  Try 0-3 for low latency applications.  Approx 5 for high load
+  Try 1-3 for low latency applications.  Approx 5 for high load
   applications (e.g. PPP).  Maybe this should be more adaptive some
   day...
 
-Etrax Serial port ser0 DTR, RI, DSR and CD support on PB
+ETRAX External clock on PB6
+CONFIG_ETRAX_EXTERN_PB6CLK_ENABLED
+  Select this if you intend to use an external clock on PB6
+  for baudrate (or timer0).
+
+ETRAX External clock on PB6 frequency
+CONFIG_ETRAX_EXTERN_PB6CLK_FREQ
+  The frequency of the external clock supplied on PB6.
+  If used as a baudrate clock, the baudrate is this clk/8.
+
+ETRAX Serial port ser0 support
+CONFIG_ETRAX_SERIAL_PORT0
+  Enables the ETRAX 100 serial driver for ser0 (ttyS0)
+  Normally you want this on, unless you use external DMA 1 that uses
+  the same DMA channels.
+
+ETRAX Serial port ser0 uses DMA6 for output
+CONFIG_ETRAX_SERIAL_PORT0_DMA6_OUT
+  ETRAX Serial port ser0 uses DMA6 for output.
+  External DMA 1 uses this DMA channel as well.
+
+ETRAX Serial port ser0 uses DMA7 for input
+CONFIG_ETRAX_SERIAL_PORT0_DMA7_IN
+  ETRAX Serial port ser0 uses DMA7 for input.
+  External DMA 1 uses this DMA channel as well.
+
+ETRAX Serial port ser0 DTR, RI, DSR and CD support on PB
 CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_PB
   Enables the status and control signals DTR, RI, DSR and CD on PB for
   ser0.
@@ -27069,7 +27151,17 @@
 CONFIG_ETRAX_SERIAL_PORT1
   Enables the ETRAX 100 serial driver for ser1 (ttyS1).
 
-Etrax Serial port ser1 DTR, RI, DSR and CD support on PB
+ETRAX Serial port ser1 uses DMA8 for output
+CONFIG_ETRAX_SERIAL_PORT1_DMA8_OUT
+  ETRAX Serial port ser1 uses DMA8 for output.
+  USB uses this DMA channel as well.
+
+ETRAX Serial port ser1 uses DMA9 for input
+CONFIG_ETRAX_SERIAL_PORT1_DMA9_IN
+  ETRAX Serial port ser1 uses DMA9 for input.
+  USB uses this DMA channel as well.
+
+ETRAX Serial port ser1 DTR, RI, DSR and CD support on PB
 CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_ON_PB
   Enables the status and control signals DTR, RI, DSR and CD on PB for
   ser1.
@@ -27078,7 +27170,18 @@
 CONFIG_ETRAX_SERIAL_PORT2
   Enables the ETRAX 100 serial driver for ser2 (ttyS2).
 
-Etrax Serial port ser2 DTR, RI, DSR and CD support on PA
+ETRAX Serial port ser2 uses DMA2 for output
+CONFIG_ETRAX_SERIAL_PORT2_DMA2_OUT
+  ETRAX Serial port ser2 uses DMA2 for output.
+  par0, scsi0 and ATA uses this DMA channel as well.
+
+ETRAX Serial port ser2 uses DMA3 for input
+CONFIG_ETRAX_SERIAL_PORT2_DMA3_IN
+  ETRAX Serial port ser2 uses DMA3 for input.
+  par0, scsi0 and ATA uses this DMA channel as well.
+
+
+ETRAX Serial port ser2 DTR, RI, DSR and CD support on PA
 CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_ON_PA
   Enables the status and control signals DTR, RI, DSR and CD on PA for
   ser2.
@@ -27087,22 +27190,41 @@
 CONFIG_ETRAX_SERIAL_PORT3
   Enables the ETRAX 100 serial driver for ser3 (ttyS3).
 
-Etrax100 RS-485 support
+ETRAX Serial port ser3 uses DMA4 for output
+CONFIG_ETRAX_SERIAL_PORT3_DMA4_OUT
+  ETRAX Serial port ser3 uses DMA4 for output.
+  External DMA 0, par1 and scsi1 uses this DMA channel as well.
+
+ETRAX Serial port ser3 uses DMA5 for input
+CONFIG_ETRAX_SERIAL_PORT3_DMA5_IN
+ ETRAX Serial port ser3 uses DMA5 for input.
+  External DMA 0, par1 and scsi1 uses this DMA channel as well.
+
+ETRAX RS-485 support
 CONFIG_ETRAX_RS485
   Enables support for RS-485 serial communication.  For a primer on
   RS-485, see <http://www.hw.cz/english/docs/rs485/rs485.html>.
 
-Etrax100 RS-485 mode on PA
+ETRAX RS-485 mode on PA
 CONFIG_ETRAX_RS485_ON_PA
   Control Driver Output Enable on RS485 transceiver using a pin on PA
   port:
           Axis 2400/2401 uses PA 3.
 
-Etrax100 RS-485 mode on PA bit
+ETRAX RS-485 mode on PA bit
 CONFIG_ETRAX_RS485_ON_PA_BIT
   Control Driver Output Enable on RS485 transceiver using a this bit
   on PA port.
 
+ETRAX RS-485 mode on G port
+CONFIG_ETRAX_RS485_ON_PORT_G
+  Control Driver Output Enable on RS485 transceiver using a pin on port G
+
+ETRAX RS-485 mode on port G bit
+CONFIG_ETRAX_RS485_ON_PORT_G_BIT
+  Control Driver Output Enable on RS485 transceiver using this bit
+  on G port.
+
 Ser0 DTR on PB bit
 CONFIG_ETRAX_SER0_DTR_ON_PB_BIT
   Specify the pin of the PB port to carry the DTR signal for serial
@@ -27163,22 +27285,33 @@
   Specify the pin of the PA port to carry the CD signal for serial
   port 2.
 
-Etrax100 RS-485 disable receiver
+ETRAX RS-485 disable receiver
 CONFIG_ETRAX_RS485_DISABLE_RECEIVER
   It's necessary to disable the serial receiver to avoid serial
   loopback.  Not all products are able to do this in software only.
   Axis 2400/2401 must disable receiver.
 
-Etrax100 I2C Support
+ETRAX LTC1387 support
+CONFIG_ETRAX_LTC1387
+  Enable support for LTC1387 multiprotocol transceiver on serial
+  port 2.
+
+CONFIG_ETRAX_LTC1387_DXEN_PORT_G_BIT
+  Specify the pin of port G for DXEN on the LTC1387 chip.
+
+CONFIG_ETRAX_LTC1387_RXEN_PORT_G_BIT
+  Specify the pin of port G for RXEN on the LTC1387 chip. 
+
+ETRAX I2C Support
 CONFIG_ETRAX_I2C
-  Enables an I2C driver on PB0 and PB1 on ETRAX100.
+  Enables an I2C driver on ETRAX.
   EXAMPLE usage:
         i2c_arg = I2C_WRITEARG(STA013_WRITE_ADDR, reg, val);
        ioctl(fd, _IO(ETRAXI2C_IOCTYPE, I2C_WRITEREG), i2c_arg);
        i2c_arg = I2C_READARG(STA013_READ_ADDR, reg);
        val = ioctl(fd, _IO(ETRAXI2C_IOCTYPE, I2C_READREG), i2c_arg);
 
-Etrax100 I2C configuration
+ETRAX I2C configuration
 CONFIG_ETRAX_I2C_USES_PB_NOT_PB_I2C
   Select whether to use the special I2C mode in the PB I/O register or
   not.  This option needs to be selected in order to use some drivers
@@ -27186,77 +27319,138 @@
   I2C driver, like the DS1302 realtime-clock driver.  If you are
   uncertain, choose Y here.
 
-Etrax100 I2C EEPROM (NVRAM) support
+ETRAX I2C data pin configuration
+CONFIG_ETRAX_I2C_DATA_PORT
+  Selects the pin on Port B where the data pin is connected
+
+ETRAX I2C clock pin configuration
+CONFIG_ETRAX_I2C_CLK_PORT
+  Select the pin on Port B where the clock pin is connected
+
+ETRAX I2C EEPROM (NVRAM) support
 CONFIG_ETRAX_I2C_EEPROM
   Enables I2C EEPROM (non-volatile RAM) on PB0 and PB1 using the I2C
   driver.  Select size option: Probed, 2k, 8k, 16k.
   (Probing works for 2k and 8k but not that well for 16k)
 
-Etrax100 I2C EEPROM (NVRAM) size/16kB
+ETRAX I2C EEPROM (NVRAM) size/16kB
 CONFIG_ETRAX_I2C_EEPROM_16KB
   Use a 16kB EEPROM.
 
-Etrax100 I2C EEPROM (NVRAM) size/2kB
+ETRAX I2C EEPROM (NVRAM) size/2kB
 CONFIG_ETRAX_I2C_EEPROM_2KB
   Use a 2kB EEPROM.
 
-Etrax100 I2C EEPROM (NVRAM) size/8kB
+ETRAX I2C EEPROM (NVRAM) size/8kB
 CONFIG_ETRAX_I2C_EEPROM_8KB
   Use a 8kB EEPROM.
 
 # Choice: etrax_eeprom
-Etrax100 I2C EEPROM (NVRAM) size/probe
+ETRAX I2C EEPROM (NVRAM) size/probe
 CONFIG_ETRAX_I2C_EEPROM_PROBE
   Specifies size or auto probe of the EEPROM size.
   Options: Probed, 2k, 8k, 16k.
   (Probing works for 2k and 8k but not that well for 16k)
 
-Etrax DS1302 Real-Time Clock driver
-CONFIG_ETRAX_DS1302
-  Enables the driver for the DS1302 Real-Time Clock battery-backed
-  chip on some products.  The kernel reads the time when booting, and
+ETRAX Port G direction
+CONFIG_ETRAX_DEF_R_PORT_G_DIR
+  Set the direction of specified pins to output.
+
+ETRAX Port G0
+CONFIG_ETRAX_DEF_R_PORT_G0_DIR_OUT
+  Set G0 to output.
+
+ETRAX Port G8-G15
+CONFIG_ETRAX_DEF_R_PORT_G8_15_DIR_OUT
+  Set G8-G15 to output.
+
+ETRAX Port G16-G23
+CONFIG_ETRAX_DEF_R_PORT_G16_23_DIR_OUT
+  Set G16-G23 to output.
+
+ETRAX Port G24
+CONFIG_ETRAX_DEF_R_PORT_G24_DIR_OUT
+  Set G24 to output.
+
+ETRAX Real-Time Clock drivers
+CONFIG_ETRAX_RTC
+  Enables drivers for the Real-Time Clock battery-backed chips on 
+  some products. The kernel reads the time when booting, and
   the date can be set using ioctl(fd, RTC_SET_TIME, &rt) with rt a
   rtc_time struct (see <file:include/asm-cris/rtc.h>) on the /dev/rtc
   device, major 121.  You can check the time with cat /proc/rtc, but
   normal time reading should be done using libc function time and
   friends.
 
-Etrax DS1302 RST on the Generic Port
+ETRAX DS1302 Real-Time Clock driver
+CONFIG_ETRAX_DS1302
+  Enables the driver for the DS1302 Real-Time Clock battery-backed
+  chip on some products. 
+
+ETRAX DS1302 RST on the Generic Port
 CONFIG_ETRAX_DS1302_RST_ON_GENERIC_PORT
   If your product has the RST signal line for the DS1302 RTC on the
   Generic Port then say Y here, otherwise leave it as N in which
   case the RST signal line is assumed to be connected to Port PB
   (just like the SCL and SDA lines).
 
-Etrax DS1302 RST bit number
+ETRAX DS1302 RST bit number
 CONFIG_ETRAX_DS1302_RSTBIT
   This is the bit number for the RST signal line of the DS1302 RTC on
   the selected port. If you have selected the generic port then it
   should be bit 27, otherwise your best bet is bit 5.
 
-Etrax DS1302 SCL bit number
+ETRAX DS1302 SCL bit number
 CONFIG_ETRAX_DS1302_SCLBIT
   This is the bit number for the SCL signal line of the DS1302 RTC on
   Port PB. This is probably best left at 3.
 
-Etrax DS1302 SDA bit number
+ETRAX DS1302 SDA bit number
 CONFIG_ETRAX_DS1302_SDABIT
   This is the bit number for the SDA signal line of the DS1302 RTC on
   Port PB. This is probably best left at 2.
 
-Etrax 100 IDE Reset
+ETRAX DS1302 Trickle charger value
+CONFIG_ETRAX_DS1302_TRICKLE_CHARGE
+  This controls the initial value of the trickle charge register.
+  0 = disabled (use this if you are unsure or have a non rechargable battery)
+  Otherwise the following RS and DS values can be OR:ed together to control 
+  the charge current:
+  RS: 1 = 2kohm, 2 = 4kohm, 3 = 8kohm
+  DS: 4 = 1 diode, 8 = 2 diodes
+  Allowed values are (increasing current): 0, 11, 10, 9, 7, 6, 5
+
+ETRAX PCF8563 Real-Time Clock driver
+CONFIG_ETRAX_PCF8563
+  Enables the driver for the PCF8563 Real-Time Clock battery-backed
+  chip on some products.  
+
+ETRAX Real-Time Clock read only mode
+CONFIG_ETRAX_RTC_READONLY
+  Enable this will put the RTC in read only mode. In this mode it is
+  not possible to change the RTC register. The RTC register value can
+  only be read. This should probably be disabled.
+  
+ETRAX 100 ATA/IDE support
+CONFIG_ETRAX_IDE
+  Enable this to get support for ATA/IDE.
+  You can't use parallell ports or SCSI ports
+  at the same time.
+
+ETRAX 100 IDE delay
+CONFIG_ETRAX_IDE_DELAY
+  Number of seconds to wait for IDE drives to spin up after an IDE
+  reset.
+
+ETRAX 100 IDE Reset
 CONFIG_ETRAX_IDE_CSP0_8_RESET
   Configures the pin used to reset the IDE bus.
 
-Etrax 100 IDE Reset
+ETRAX 100 IDE Reset
 CONFIG_ETRAX_IDE_CSPE1_16_RESET
   Configures the pin used to reset the IDE bus.
 
-Delay for drives to regain consciousness
-CONFIG_ETRAX_IDE_DELAY
-  Sets the time to wait for disks to regain consciousness after reset.
-
-Etrax 100 IDE Reset
+ETRAX 100 IDE Reset
 CONFIG_ETRAX_IDE_G27_RESET
   Configures the pin used to reset the IDE bus.
 
@@ -27285,6 +27479,16 @@
   This option enables the ETRAX 100LX built-in 10/100Mbit Ethernet
   controller.
 
+ETRAX 100 Ethernet slave controller
+CONFIG_ETRAX_ETHERNET_LPSLAVE
+  This option enables a slave ETRAX 100 or ETRAX 100LX, connected to a 
+  master ETRAX 100 or ETRAX 100LX through par0 and par1, to act as an 
+  Ethernet controller.
+
+ETRAX 100 Ethernet slave controller LEDS
+CONFIG_ETRAX_ETHERNET_LPSLAVE_HAS_LEDS
+  Enable if the slave has it's own LEDs.
+
 ETRAX 100LX Synchronous serial ports
 CONFIG_ETRAX_SYNCHRONOUS_SERIAL
   This option enables support for the ETRAX 100LX built-in
@@ -27309,11 +27513,6 @@
 CONFIG_ETRAX_SYNCHRONOUS_SERIAL1_DMA
   Makes synchronous serial port 1 use DMA.
 
-Delay for drives to regain consciousness
-CONFIG_IDE_DELAY
-  Number of seconds to wait for IDE drives to spin up after an IDE
-  reset.
-
 ARTPEC-1 support
 CONFIG_JULIETTE
   The ARTPEC-1 is a video-compression chip used in the AXIS 2100
@@ -27334,30 +27533,41 @@
   for changing this is when the flash block size is bigger
   than 64kB (e.g. when using two parallel 16 bit flashes).
 
-Enable Etrax100 watchdog
+Enable ETRAX watchdog
 CONFIG_ETRAX_WATCHDOG
-  Enable the built-in watchdog timer support on Etrax100 embedded
+  Enable the built-in watchdog timer support on ETRAX embedded
   network computers.
 
+ETRAX watchdog
+CONFIG_ETRAX_WATCHDOG_NICE_DOGGY 
+  By enabling this you make sure that the watchdog does not bite while
+  printing oopses. Recommended for development systems but not for 
+  production releases.
+
+Enable reboot at out of memory
+CONFIG_OOM_REBOOT
+  The default behaviour at out of memory is to kill processes. By
+  enabling this option the system will be rebooted instead.
+
 # Choice: crisdebug
 Serial-0
 CONFIG_ETRAX_DEBUG_PORT0
   Choose a serial port for the ETRAX debug console.  Default to
   port 0.
 
-Etrax debug port on ser1
+ETRAX debug port on ser1
 CONFIG_ETRAX_DEBUG_PORT1
   Use serial port 1 for the console.
 
-Etrax debug port on ser2
+ETRAX debug port on ser2
 CONFIG_ETRAX_DEBUG_PORT2
   Use serial port 2 for the console.
 
-Etrax debug port on ser3
+ETRAX debug port on ser3
 CONFIG_ETRAX_DEBUG_PORT3
   Use serial port 3 for the console.
 
-No Etrax debug port
+No ETRAX debug port
 CONFIG_ETRAX_DEBUG_PORT_NULL
   Disable serial-port debugging.
 
@@ -28193,7 +28403,7 @@
   Choose this option if you have a SIS graphics card. AGP support is
   required for this driver to work.
 
-Etrax Ethernet slave support (over lp0/1)
+ETRAX Ethernet slave support (over lp0/1)
 CONFIG_ETRAX_ETHERNET_LPSLAVE
   This option enables a slave ETRAX 100 or ETRAX 100LX, connected to a
   master ETRAX 100 or ETRAX 100LX through par0 and par1, to act as an
@@ -28232,12 +28442,6 @@
   should normally be the mtdblock device for the partition after the
   last partition in the partition table.
 
-Serial port 0 enabled
-CONFIG_ETRAX_SERIAL_PORT0
-  Enables the ETRAX 100 serial driver for ser0 (ttyS0)
-  Normally you want this on, unless you use external DMA 1 that uses
-  the same DMA channels.
-
 Shutdown bit on port CSP0
 CONFIG_ETRAX_SHUTDOWN_BIT
   Configure what pin on CSPO-port that is used for controlling power
@@ -28245,7 +28449,7 @@
 
 Software Shutdown Support
 CONFIG_ETRAX_SOFT_SHUTDOWN
-  Enable this if Etrax is used with a power-supply that can be turned
+  Enable this if ETRAX is used with a power-supply that can be turned
   off and on with PS_ON signal. Gives the possibility to detect
   powerbutton and then do a power off after unmounting disks.
 
diff -Nur /home/anderstj/linux-2.4.26/Documentation/cris/README ./Documentation/cris/README
--- /home/anderstj/linux-2.4.26/Documentation/cris/README	2001-05-02 01:04:56.000000000 +0200
+++ ./Documentation/cris/README	2001-06-05 14:36:26.000000000 +0200
@@ -1,6 +1,6 @@
 Linux 2.4 on the CRIS architecture
 ==================================
-$Id: README,v 1.7 2001/04/19 12:38:32 bjornw Exp $
+$Id: README,v 1.9 2001/06/05 12:36:26 bjornw Exp $
 
 This is a port of Linux 2.4 to Axis Communications ETRAX 100LX embedded 
 network CPU. For more information about CRIS and ETRAX please see further
@@ -103,7 +103,7 @@
 ETRAX 100LX 10/100MBit ethernet v2.0 (c) 2000 Axis Communications AB
 eth0 initialized
 eth0: changed MAC to 00:40:8C:CD:00:00
-ETRAX 100LX serial-driver $Revision: 1.7 $, (c) 2000 Axis Communications AB
+ETRAX 100LX serial-driver $Revision: 1.9 $, (c) 2000 Axis Communications AB
 ttyS0 at 0xb0000060 is a builtin UART with DMA
 ttyS1 at 0xb0000068 is a builtin UART with DMA
 ttyS2 at 0xb0000070 is a builtin UART with DMA
@@ -133,8 +133,7 @@
 Hostname is bbox1
 Telnetd starting, using port 23.
   using /bin/sash as shell.
-sftpd[15]: sftpd $Revision: 1.7 $ starting up
-
+sftpd[15]: sftpd $Revision: 1.9 $ starting up
 
 
 And here is how some /proc entries look:
@@ -181,9 +180,7 @@
 -rwxr-xr-x  1 342      100         48104  Jan 01 00:00 sh
 -rwxr-xr-x  1 342      100         16252  Jan 01 00:00 telnetd
 
-
-(All programs are statically linked to the libc at this point - we have not ported the
- shared libraries yet)
+(Statically linked binaries shown)
 
 
 
diff -Nur /home/anderstj/linux-2.4.26/Documentation/filesystems/metafiles.txt ./Documentation/filesystems/metafiles.txt
--- /home/anderstj/linux-2.4.26/Documentation/filesystems/metafiles.txt	1970-01-01 01:00:00.000000000 +0100
+++ ./Documentation/filesystems/metafiles.txt	2001-09-17 13:38:43.000000000 +0200
@@ -0,0 +1,92 @@
+Metafiles
+=========
+
+Metafiles are used to let a filesystem builder, such as mkcramfs and
+mkfs.jffs2, ignore files and change attributes of files while building
+the filesystem.
+
+This document briefly describes how Axis Communications are using metafiles
+with mkcramfs amd mkfs.jffs2 and is also a proposal of a metafile standard
+for filesystem builders.
+
+
+
+Metafile Format
+===============
+
+An instruction is a line in a metafile telling the filesystem builder if
+anything special should be done with a file or directory before writing it to
+the filesystem. The following instructions should be recognized by the
+filesystem builder:
+
+
+Ignore
+------
+Syntax: "Ignore: FILE"
+
+Ignores a file or directory. I.e. FILE is not included in the filesystem.
+The metafiles themselves are always ignored and do not have to be mentioned
+in any metafiles.
+
+
+IgnoreContents
+--------------
+Syntax: "IgnoreContents: DIRECTORY"
+
+Ignores the contents of a directory. I.e. DIRECTORY is included in the
+filesystem but all files and directories in DIRECTORY are ignored.
+
+
+Include
+-------
+Syntax: "Include: FILE"
+
+Includes a file or directory. If an Include instruction is found in a
+metafile, all files or directories (in the same directory as the metafile)
+that are not included will be ignored (NOTE!).
+
+
+Device
+------
+Syntax: "Device: FILE TYPE MAJOR MINOR"
+
+Convert a regular file to a device special file. If FILE exists it will be
+written to the filesystem as a character or block special file with
+MAJOR and MINOR numbers. TYPE is 'b' for block and 'c' for character device.
+
+Example: To create the character special file /dev/ttyS0 using metafiles,
+add the following line to the metafile in the /dev directory:
+
+Device: ttyS0 c 4 64
+
+
+UserId
+------
+Syntax: "UserId: FILE UID"
+
+Change file owner. The owner of FILE will be UID.
+
+
+DefaultUserId
+-------------
+Syntax: "DefaultUserId: UID"
+
+Change file owner recursively. All files and directories in this directory
+will be owned by UID unless overridden with the UserId instruction.
+
+
+GroupId
+-------
+Syntax: "GroupId: FILE GID"
+
+Change group ownership on a file or directory. The group membership of FILE
+will be GID.
+
+
+DefaultGroupId
+--------------
+Syntax: "DefaultGroupId: UID"
+
+Change gruop ownership recursively. The group membership of all files and
+directories in this directory will be GID unless overridden with the GroupId
+instruction.
diff -Nur /home/anderstj/linux-2.4.26/Makefile ./Makefile
--- /home/anderstj/linux-2.4.26/Makefile	2004-04-14 15:05:41.000000000 +0200
+++ ./Makefile	2004-04-19 10:16:18.000000000 +0200
@@ -5,7 +5,7 @@
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 
-ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
+ARCH := cris
 KERNELPATH=kernel-$(shell echo $(KERNELRELEASE) | sed -e "s/-//g")
 
 CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
@@ -19,7 +19,10 @@
 HOSTCC  	= gcc
 HOSTCFLAGS	= -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer
 
-CROSS_COMPILE 	=
+# Avoid having an absolute path here.  Ask whichever gcc-cris was
+# found in the PATH for the path to the assembler; use that directory.
+# Use a simple make variable to evaluate the test once only.
+CROSS_COMPILE 	:= $(shell dirname `gcc-cris -mlinux -print-prog-name=as`)/
 
 #
 # Include the make variables (CC, etc...)
@@ -267,7 +270,8 @@
 # 'kbuild_2_4_nostdinc :=' or -I/usr/include for kernel code and you are not UML
 # then your code is broken!  KAO.
 
-kbuild_2_4_nostdinc	:= -nostdinc -iwithprefix include
+INCLUDE_PREFIX  = $(shell $(CC) -print-libgcc-file-name | sed -e s/libgcc.a//) 
+kbuild_2_4_nostdinc	:= -nostdinc -iprefix $(INCLUDE_PREFIX) -iwithprefix include
 export kbuild_2_4_nostdinc
 
 export	CPPFLAGS CFLAGS CFLAGS_KERNEL AFLAGS AFLAGS_KERNEL
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/.cvsignore ./arch/cris/.cvsignore
--- /home/anderstj/linux-2.4.26/arch/cris/.cvsignore	1970-01-01 01:00:00.000000000 +0100
+++ ./arch/cris/.cvsignore	2000-11-28 14:13:39.000000000 +0100
@@ -0,0 +1 @@
+cris.ld.tmp
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/boot/compressed/.cvsignore ./arch/cris/boot/compressed/.cvsignore
--- /home/anderstj/linux-2.4.26/arch/cris/boot/compressed/.cvsignore	1970-01-01 01:00:00.000000000 +0100
+++ ./arch/cris/boot/compressed/.cvsignore	2001-11-08 14:56:14.000000000 +0100
@@ -0,0 +1,2 @@
+vmlinuz
+decompress.bin
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/boot/rescue/.cvsignore ./arch/cris/boot/rescue/.cvsignore
--- /home/anderstj/linux-2.4.26/arch/cris/boot/rescue/.cvsignore	1970-01-01 01:00:00.000000000 +0100
+++ ./arch/cris/boot/rescue/.cvsignore	2001-01-31 16:32:09.000000000 +0100
@@ -0,0 +1,3 @@
+rescue.bin
+testrescue.bin
+kimagerescue.bin
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/config.in ./arch/cris/config.in
--- /home/anderstj/linux-2.4.26/arch/cris/config.in	2004-02-18 14:36:30.000000000 +0100
+++ ./arch/cris/config.in	2004-03-29 09:22:16.000000000 +0200
@@ -26,11 +26,23 @@
 comment 'General setup'
 
 bool 'Networking support' CONFIG_NET
+
+bool 'Support for hot-pluggable devices' CONFIG_HOTPLUG
+
+if [ "$CONFIG_HOTPLUG" = "y" ] ; then
+   source drivers/pcmcia/Config.in
+   source drivers/hotplug/Config.in
+else
+   define_bool CONFIG_PCMCIA n
+   define_bool CONFIG_HOTPLUG_PCI n
+fi
+
 bool 'System V IPC' CONFIG_SYSVIPC
 bool 'BSD Process Accounting' CONFIG_BSD_PROCESS_ACCT
 bool 'Sysctl support' CONFIG_SYSCTL
 
 tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
+bool 'Select task to kill on out of memory condition' CONFIG_OOM_KILLER
 
 string 'Kernel command line' CONFIG_ETRAX_CMDLINE "root=/dev/mtdblock3"
 
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/defconfig ./arch/cris/defconfig
--- /home/anderstj/linux-2.4.26/arch/cris/defconfig	2004-02-18 14:36:30.000000000 +0100
+++ ./arch/cris/defconfig	2004-02-19 15:12:57.000000000 +0100
@@ -18,6 +18,7 @@
 # CONFIG_BSD_PROCESS_ACCT is not set
 # CONFIG_SYSCTL is not set
 CONFIG_BINFMT_ELF=y
+CONFIG_OOM_KILLER=y
 # CONFIG_ETRAX_KGDB is not set
 # CONFIG_ETRAX_WATCHDOG is not set
 
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/Config.in ./arch/cris/drivers/802_11/Config.in
--- /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/Config.in	1970-01-01 01:00:00.000000000 +0100
+++ ./arch/cris/drivers/802_11/Config.in	2001-05-02 18:37:11.000000000 +0200
@@ -0,0 +1,6 @@
+bool '802.11 support' CONFIG_ETRAX_802_11 y
+if [ "$CONFIG_ETRAX_802_11" = "y" ]; then
+  choice ' 802.11 hardware driver' \
+   "Rave_ext_DMA CONFIG_ETRAX_RAVE\
+    PVL_mem2mem_DMA CONFIG_ETRAX_NBAND"  Rave_ext_DMA
+fi
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/Makefile ./arch/cris/drivers/802_11/Makefile
--- /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ ./arch/cris/drivers/802_11/Makefile	2003-04-29 16:16:42.000000000 +0200
@@ -0,0 +1,54 @@
+#
+# Makefile for 802.11 device drivers.
+#
+#
+# Add new targets to obj-y (perhaps via obj-$(*)). Will be linked into
+# 802_11.o.
+#
+
+ifndef TOPDIR
+TOPDIR = ../../../..
+OBJS = rave.o mib.o tx.o rx.o ioctl.o ser.o rave_global.o mem2mem.o mlme.o
+
+CC=gcc-cris -mlinux
+CFLAGS += -I$(TOPDIR)/include -D__KERNEL__
+CFLAGS += -Wstrict-prototypes -O2 -fomit-frame-pointer -march=v10
+CFLAGS += -fno-strict-aliasing -pipe -D__linux__
+CFLAGS += -Wall # XXX: add -Werror when mlme.c cleaned up
+#CFLAGS += -E
+
+all: $(OBJS)
+
+%.d: %.c
+	$(CC) -c -MD $(CFLAGS) $<
+
+ifneq ($(MAKECMDGOALS),clean)
+ifneq ($(MAKECMDGOALS),patch)
+-include $(OBJS:.o=.d)
+endif
+endif
+
+.PHONY: clean
+clean:
+	rm -f *.d $(OBJS)
+
+endif # TOPDIR
+
+O_TARGET := 802_11.o
+
+obj-y :=
+
+obj-$(CONFIG_ETRAX_RAVE)   += rave.o mib.o tx.o rx.o ioctl.o ser.o \
+                              rave_global.o
+obj-$(CONFIG_ETRAX_NBAND)  += mem2mem.o
+obj-$(CONFIG_ETRAX_802_11) += mlme.o
+
+include $(TOPDIR)/Rules.make
+
+# The patch target should be after the include
+.PHONY: patch
+patch:
+	-patch -N -F 1 $(TOPDIR)/arch/cris/drivers/Makefile cris_drivers_Makefile.patch
+	-patch -N -F 1 $(TOPDIR)/arch/cris/drivers/Config.in \
+		cris_drivers_Config.in.patch
+	-patch -N -F 1 $(TOPDIR)/include/linux/wireless.h rave_wireless_h.patch
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/README ./arch/cris/drivers/802_11/README
--- /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/README	1970-01-01 01:00:00.000000000 +0100
+++ ./arch/cris/drivers/802_11/README	2003-04-29 16:16:42.000000000 +0200
@@ -0,0 +1,25 @@
+To get started, do: 'make patch' in this directory
+to patch arch/cris/drivers/Makefile and 
+arch/cris/drivers/Config.in to get the code in this
+directory included.
+
+
+-----------------------------------------------------
+ Common management code 
+                   control_types.h
+                   mlme.c
+-----------------------------------------------------
+Rave Hardware driver 
+                      
+rave.c
+rx.c rx.h
+tx.c rx.h
+ioctl.c ioctl.h
+mib.c mib.h
+rave_global.h
+-----------------------------------------------------
+Memorymapped hardware driver
+
+hw_pvl_common.h
+mem2mem.c
+-----------------------------------------------------
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/compat.h ./arch/cris/drivers/802_11/compat.h
--- /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/compat.h	1970-01-01 01:00:00.000000000 +0100
+++ ./arch/cris/drivers/802_11/compat.h	2003-04-29 16:16:42.000000000 +0200
@@ -0,0 +1,63 @@
+/*!***************************************************************************
+*!
+*! FILE NAME  : compat.h
+*!
+*! DESCRIPTION: Type declaration for different platforms.
+*!
+*! FUNCTIONS  : None
+*! (EXPORTED)
+*!
+*! ---------------------------------------------------------------------------
+*! (C) Copyright 2001-2003, Axis Communications AB, LUND, SWEDEN
+*!***************************************************************************/
+
+#ifndef COMPAT_H
+#define COMPAT_H
+
+/****************** INCLUDE FILES SECTION ***********************************/
+
+/****************** CONSTANT AND MACRO SECTION ******************************/
+
+#ifdef __linux__
+/* Linux has these types defined */
+#ifdef __KERNEL__
+#include <asm/types.h>
+#else
+#include <sys/types.h>
+#include <linux/types.h>
+
+typedef __u8 u8;
+typedef __u16 u16;
+typedef __u32 u32;
+#endif
+#else
+/* not __linux__ */
+/* Depending on platform we need some ifdef's here */
+#ifdef __CRIS__
+#define CHAR8_SHORT16_INT32
+#else
+#warning "size not verified assuming char8 short16 int32"
+#define CHAR8_SHORT16_INT32
+#endif
+
+#ifdef CHAR8_SHORT16_INT32
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned int u32;
+#endif
+
+#endif /* not __linux__ */
+
+#define ATTR_DATA __attribute__ ((section (".rxcache_data")))
+#define ATTR_ALIGNED_DATA __attribute__ ((section (".rxcache_data"),aligned(32) ))
+typedef u32 word32;
+
+
+#define __PACKED __attribute__((packed))
+
+/****************** TYPE DEFINITION SECTION *********************************/
+
+/****************** EXPORTED FUNCTION DECLARATION SECTION *******************/
+
+#endif /* COMPAT_H */
+/****************** END OF FILE compat.h ************************************/
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/control_types.h ./arch/cris/drivers/802_11/control_types.h
--- /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/control_types.h	1970-01-01 01:00:00.000000000 +0100
+++ ./arch/cris/drivers/802_11/control_types.h	2001-05-10 12:26:24.000000000 +0200
@@ -0,0 +1,392 @@
+/*!***************************************************************************
+*!
+*! FILE NAME  : control_types.h
+*!
+*! DESCRIPTION: Control message types for management related communication 
+*!              between 802.11 MAC and the Master.
+*!              (Has nothing to do with 802.11 control frames sent through 
+*!               the air)
+*!
+*! FUNCTIONS  : None
+*! (EXPORTED)
+*!
+*! ---------------------------------------------------------------------------
+*! HISTORY
+*!
+*! DATE         NAME               CHANGES
+*! ----         ----               -------
+*! Mar  8 2001  Johan Adolfsson    Initial version
+*! $Log: control_types.h,v $
+*! Revision 1.2  2001/05/10 10:26:24  johana
+*! Added attrid_max and attrid_mac_addr structs.
+*!
+*! Revision 1.1  2001/05/02 16:40:57  johana
+*! Start of PVL mem2mem DMA driver and common MLME stuff.
+*! Some stuff copied from doc/projects/nband/src/ and from rave.c + some new.
+*! Work in progress - not complete!
+*!
+*! Revision 1.17  2001/04/11 11:51:50  johana
+*! Split STRING type in DISPLAYSTRING and OCTETSTRING (as in the MIB)
+*! and clarified the null termination usage. (Review remark 6776)
+*! Changed a couple of "variables" to "attributes".
+*!
+*! Revision 1.16  2001/04/11 09:19:08  johana
+*! Rename par_id_enum to attr_id_enum (Review remark 6773)
+*!
+*! Revision 1.15  2001/04/11 09:14:34  johana
+*! Review changes:
+*! CTRLMSG_ start from 0.
+*! CTRLMSG_DEBUG messages removed, use debug attributes instead.
+*! ATTRID_MACSTATE takes a MAC address to use and
+*! ATTRID_dot11MACAddress is now RO and not RW.
+*!
+*! Revision 1.14  2001/04/10 15:27:21  johana
+*! Fixed review remarks 6566-6569, 6635 by larsv:
+*! * Clarified "control"
+*! * Added that Master is responible to do range checking.
+*! * Replaced parameter and PARID with attribute and ATTR to be more
+*!   SNMP like in terminology.
+*! * Use only GET_REQUEST, SET_REQUEST and RESPONSE
+*! * Added error codes from SNMP (RFC1905)
+*!
+*! Revision 1.13  2001/03/30 01:40:32  larsv
+*! Added status code for bad parameter value.
+*! Changed read-only error to access error.
+*! Changed type for associate AP debug message.
+*!
+*! Revision 1.12  2001/03/27 13:26:21  johana
+*! More comments on CTRLMSG_RESULT_RESPONSE
+*!
+*! Revision 1.11  2001/03/21 15:34:02  johana
+*! Decrease CTRLMSG_MAX_VALUE_LENGTH to 64*6
+*!
+*! Revision 1.10  2001/03/21 14:15:07  johana
+*! Use __PACKED.
+*! Change the pad field to a status field, added CTRLSTATUS enum.
+*! Added RO, WO or RW in PARID names.
+*! Added CTRLMSG_DEBUG_ASSOCIATE_AP.
+*! Changed AddressTable and added PromiscousMode..
+*!
+*! 
+*! ---------------------------------------------------------------------------
+*! (C) Copyright 2001, Axis Communications AB, LUND, SWEDEN
+*!***************************************************************************/
+/* $Id: control_types.h,v 1.2 2001/05/10 10:26:24 johana Exp $ */
+
+#ifndef CONTROL_TYPES_H
+#define CONTROL_TYPES_H
+
+/****************** INCLUDE FILES SECTION ***********************************/
+#include "compat.h"
+
+/****************** CONSTANT AND MACRO SECTION ******************************/
+
+enum{
+  CTRLMSG_MAX_VALUE_LENGTH = 64*6 /* Room for MACLIST with 64 MAC's */
+};
+#define CTRL_MSG_COMMON_SIZE 8 /* Typically sizeof(control_msg_common_type) */
+#define CTRL_MSG_TOTSIZE(p) (CTRL_MSG_COMMON_SIZE + (p)->common.length)
+
+/****************** TYPE DEFINITION SECTION *********************************/
+
+/* 
+  |control_msg_common                     |
+  +---------+---------+---------+---------+---------------------+
+  | type    | length  | id      | status  | value               |
+  +---------+---------+---------+---------+---------------------+
+   2 octets  2 octets  2 octets  2 octets   variable length
+  The value of the type, length, id and status field is encoded as 
+  a little-endian unsigned integer (LSB transmitted first). 
+  The length field specifies the length of the variable value field in octets.
+  The total length of a message is 2+2+2+2+length = 8+length.
+
+  The Id field can be used to handle if multiple messages
+  are sent and the response is asynchronous.
+  Normally the host sets the Id using a counter, a response should
+  always be set to the same Id as the request.
+
+ */
+
+
+/* WARNING! 
+ * DO NOT CHANGE THESE VALUES!!!
+ * If you MUST change any of these values, both the code on the host 
+ * and on the slave must be rebuilt! 
+ */
+
+/* Possible values for the status field
+ */
+typedef enum control_msg_status_enum{
+  CTRLSTATUS_OK = 0x0000,
+  /* Some error */  
+  CTRLSTATUS_ERROR                      = 0x0001, 
+  /* type field is unknown */
+  CTRLSTATUS_ERROR_UNKNOWN_TYPE         = 0x0002, 
+  /* ATTRID unknown in GET/SET request */
+  CTRLSTATUS_ERROR_ATTRID_UNKNOWN       = 0x0003, 
+  /* SNMP Errors according to RFC1905 */
+  /* Operation not allowed on ATTRID in GET/SET request */
+  CTRLSTATUS_ERROR_ATTRID_NO_ACCESS     = 0x0004, 
+  /* Not allowed to modify the attribute with a SET request */
+  CTRLSTATUS_ERROR_ATTRID_NOT_WRITABLE  = 0x0005, 
+  /* Invalid value for ATTRID in GET/SET request */
+  CTRLSTATUS_ERROR_ATTRID_WRONG_TYPE    = 0x0006,
+  /* Invalid length of value for ATTRID in SET request */
+  CTRLSTATUS_ERROR_ATTRID_WRONG_LENGTH   = 0x0007,
+  /* Wrong encoding of value for ATTRID in SET request */
+  CTRLSTATUS_ERROR_ATTRID_WRONG_ENCODING = 0x0008,
+  /* Wrong value for ATTRID in SET request */
+  CTRLSTATUS_ERROR_ATTRID_WRONG_VALUE    = 0x0009,
+  /* end of SNMP Errors according to RFC1905 */
+
+
+  CTRLSTATUS_MAX /* Last */
+} control_msg_status_enum_type;
+
+
+/* Values for the type field */
+typedef enum control_msg_enum
+{
+  CTRLMSG_MIN             = 0x0000,
+  /* Send in response to some commands to indicate if they succeeded. 
+   * Status is enum in control_msg_response_status_enum_type 
+   * CTRLMSG_RESULT_RESPONSE is also used to signal error when a 
+   * received message is incomplete or corrupt.
+   */
+/* Get the value of an attribute. The response is a RESPONSE message */
+  CTRLMSG_GET_REQUEST     = 0x0000, /* val=attrid, return RESPONSE */
+
+/* Set the value of an attribute. The response is a RESPONSE message */
+  CTRLMSG_SET_REQUEST     = 0x0001, /* val=attrid,value, return RESPONSE */
+
+/* Response from a GET_REQUEST or SET_REQUEST, 
+ * return the actual attrid and value. 
+ * If ok, val=attrid,value is returned
+ * If error, val=0x0000 (ATTRID_ERROR)
+ */
+  CTRLMSG_RESPONSE        = 0x0002, /* val=attrid,value  */
+
+
+  CTRLMSG_MAX,                      /* The last value defined */
+}control_msg_enum_type;
+
+
+/* Attribute Ids:
+  naming: ATTRIDprefix_NAME_direction_TYPEsuffix
+  direction:
+    RO = Read only from host side
+    WO = Write only from host side
+    RW = Read/write access
+
+  TYPEsuffixes:
+    NOVALUE = No value, the ATTRID itself contains the info.
+    MAC=6 octets            (transferred as a byte array highest byte first)
+    MACLIST = List of MACs  (transferred as a list of MAC addresses )
+    U16 = 16 bit integer (2 octets) (transferred as little-endian)
+    U32 = 32 bit integer (4 octets) (transferred as little-endian)
+    U32PAIR = 2 x U32 
+    U32PAIRLIST = List of U32PAIRs
+    DISPLAYSTRING = string of octets in NVT ASCII (See MIB doc)
+                    The string should be null terminated and the length
+                    is including the null termination.
+                    The +1 in allowed values in comments after the 
+                    ATTRID_ below indicates the null terminator.
+                    
+    OCTETSTRING = string of octets
+    COUNTER = same as U32 (could be U16 if we're short on space)
+
+  The Master is responsible for doing range checking of attribute values
+  before they are sent to the MAC.
+
+*/
+
+/* WARNING! 
+ * DO NOT CHANGE THESE VALUES!!!
+ * If you MUST change any of these values, both the code on the host 
+ * and on the slave must be rebuilt! 
+ */
+/* For dot11 MIB attributes, the id is using the following bit fields:
+  dot11group << 12 | dot11subgroup << 8 | value  
+ */
+
+
+typedef enum attr_id_enum /* 2 octets */
+{
+  ATTRID_NONE                         = 0x0000,
+  ATTRID_ERROR                        = 0x0000, /* Same as ATTRID_NONE */
+  /* --Generic non dot11 attributes 0x0001-0x00FF ------------------------ */
+  /* Used to START and STOP the MAC and supply it a MAC address to use. 
+   * When starting a MAC address should be supplied, 
+   * when stopping the MAC chould be 000000000000 
+   */
+  ATTRID_MACSTATE_RW_MAC              = 0x0001,
+
+  /* --Debug attributes 0x0100-0x1FF ------------------------------------- */
+  /* Used during testing to skip authorisation ans association sequence before
+   * MAC accepts packets.
+   */
+  ATTRID_DEBUG_ASSOCIATE_AP_WO_MAC    = 0x0100, /* value is a MAC of AP */ 
+
+  /* -------------------------------------------------------------------- */
+  /* dot11 1 dot11smt */
+/* We might add these some day...
+  	dot11StationID                     MacAddress,
+   	dot11MediumOccupancyLimit          INTEGER,
+   	dot11CFPollable                    TruthValue,
+   	dot11CFPPeriod                     INTEGER, 
+   	dot11CFPMaxDuration                INTEGER,
+   	dot11AuthenticationResponseTimeOut INTEGER,
+   	dot11PrivacyOptionImplemented      TruthValue,
+		dot11PowerManagementMode	   INTEGER,
+		dot11DesiredSSID		   OCTET STRING,
+		dot11DesiredBSSType		   INTEGER,
+		dot11OperationalRateSet		   OCTET STRING,
+		dot11BeaconPeriod		   INTEGER,
+		dot11DTIMPeriod			   INTEGER,
+		dot11AssociationResponseTimeOut	   INTEGER,
+   	dot11DisassociateReason            INTEGER,
+   	dot11DisassociateStation           MacAddress,
+   	dot11DeauthenticateReason          INTEGER,
+   	dot11DeauthenticateStation         MacAddress,
+   	dot11AuthenticateFailStatus        INTEGER,
+  	dot11AuthenticateFailStation       MacAddress }
+
+*/
+
+  /* -------------------------------------------------------------------- */  
+  /* dot11 2 dot11mac, 1 Operation */
+  ATTRID_dot11MACAddress_RO_MAC              = 0x2101, 
+   /* Not same as dot11StationID  */
+
+  ATTRID_dot11RTSThreshold_RW_U16            = 0x2102, /* 0..2347 */
+  ATTRID_dot11ShortRetryLimit_RW_U16         = 0x2103, /* 1..255 */
+  ATTRID_dot11LongRetryLimit_RW_U16          = 0x2104, /* 1..255 */
+  ATTRID_dot11FragmentationThreshold_RW_U16  = 0x2105, /* 256..2346 */
+  ATTRID_dot11MaxTransmitMSDULifetime_RW_U32 = 0x2106, /* 1..4294967295 */
+  ATTRID_dot11MaxReceveLifetime_RW_U32       = 0x2107, /* 1..4294967295 */
+  ATTRID_dot11ManufacturerID_RO_DISPLAYSTRING= 0x2108, /* size 0..128+1 */
+  ATTRID_dot11ProductID_RO_DISPLAYSTRING     = 0x2109, /* size 0..128+1 */
+
+  /* dot11 2 dot11mac, 2 Counters */
+  ATTRID_dot11TransmittedFragmentCount_RO_COUNTER       = 0x2201,
+  ATTRID_dot11MulticastTransmittedFrameCount_RO_COUNTER = 0x2202,
+  ATTRID_dot11FailedCount_RO_COUNTER                    = 0x2203,
+  ATTRID_dot11RetryCount_RO_COUNTER                     = 0x2204,
+  ATTRID_dot11MultipleRetryCount_RO_COUNTER             = 0x2205,
+  ATTRID_dot11FrameDuplicateCount_RO_COUNTER            = 0x2206,
+  ATTRID_dot11RTSSuccessCount_RO_COUNTER                = 0x2207,
+  ATTRID_dot11RTSFailureCount_RO_COUNTER                = 0x2208,
+  ATTRID_dot11ACKFailureCount_RO_COUNTER                = 0x2209,
+  ATTRID_dot11ReceivedFragmentCount_RO_COUNTER          = 0x220A,
+  ATTRID_dot11MulticastReceivedFrameCount_RO_COUNTER    = 0x220B,
+  ATTRID_dot11FCSErrorCount_RO_COUNTER                  = 0x220C,
+  ATTRID_dot11TransmittedFrameCount_RO_COUNTER          = 0x220D,
+  ATTRID_dot11WEPUndecryptableCount_RO_COUNTER          = 0x220E,
+
+  /* dot11 2 dot11mac, 3 GroupAddresses */
+  /* We have one list with the MAC's instead of dot11GroupAddressesEntry with
+            dot11GroupAddressesIndex    Integer32,
+            dot11Address                MacAddress,
+            dot11GroupAddressesStatus   RowStatus
+     fields.
+  */
+  /* Setting the ATTRID_XXdot11PromiscousMode_WO_NOVALUE will disable the use
+   * of ATTRID_XXdot11AddressTable_WO_MACLIST.
+   * To disable promiscous mode, set the AddressTable again.
+   */
+  ATTRID_XXdot11AddressTable_WO_MACLIST    = 0x2300, /* NOTE: not MIB compliant */
+  ATTRID_XXdot11PromiscousMode_WO_NOVALUE  = 0x2301, /* NOTE: not MIB compliant */
+  /*dot11Address1,2,-32 and dot11Address1-32 */
+
+  /* -------------------------------------------------------------------- */
+  /* dot11 3 dot11res */
+
+  /* -------------------------------------------------------------------- */
+  /* dot11 4 dot11phy 1 Operation */
+  /* dot11 4 dot11phy 2 Antenna */
+  /* dot11 4 dot11phy 3 TxPower */
+  /* dot11 4 dot11phy 4 FHSS */
+  /* dot11 4 dot11phy 5 DSSS */
+  /* dot11 4 dot11phy 6 IR */
+  /* dot11 4 dot11phy 7 RegDomainsSupported */
+  /* dot11 4 dot11phy 8 AntennasList */
+
+  /* dot11 4 dot11phy 9 SupportedDataRatesTx */
+  /* List of pairs containing 
+   * SupportedDataRatesTxIndex, SupportedDataRatesTxValue 
+   */
+  ATTRID_dot11SupportedDataRatesTxEntry_RO_U32PAIRLIST = 0x4900,
+
+
+  /* dot11 4 dot11phy 10 SupportedDataRatesRx */
+  /* List of pairs containing 
+   * SupportedDataRatesRxIndex, SupportedDataRatesRxValue 
+   */
+  ATTRID_dot11SupportedDataRatesRxEntry_RO_U32PAIRLIST = 0x4A00,
+
+
+  ATTRID_MAX /* Last value */
+} attr_id_type;
+
+#define MAC_ADDRESS_SIZE 6
+typedef u8 mac_address_type[MAC_ADDRESS_SIZE];
+
+
+
+/****************************************
+ * Data structure to pass control data
+ */
+typedef struct control_msg_common_struct
+{
+  u16 type;     /* enum control_msg_type */
+  u16 length;   /* Length of value field */
+  u16 id;       /* transaction id */
+  u16 status;   /* enum control_msg_status_enum_type */
+}control_msg_common_type __PACKED;
+
+typedef struct control_msg_struct
+{
+  control_msg_common_type common;
+  u8  value[1]; /* This is really variable length */
+}control_msg_type __PACKED;
+
+typedef struct control_msg_max_struct
+{
+  control_msg_common_type common;
+  u8  value[CTRLMSG_MAX_VALUE_LENGTH]; 
+}control_msg_max_type __PACKED;
+
+typedef struct control_msg_attrid_max_struct
+{
+  control_msg_common_type common;
+  u16 attrid;
+  u8  value[CTRLMSG_MAX_VALUE_LENGTH-2]; 
+}control_msg_attrid_max_type __PACKED;
+
+typedef struct control_msg_u16_struct
+{
+  control_msg_common_type common;
+  u16 value_u16; 
+}control_msg_u16_type __PACKED;
+
+
+typedef struct control_msg_attrid_val16_struct
+{
+  control_msg_common_type common;
+  u16 attrid; 
+  u16 attrval16;
+}control_msg_attrid_val16_type __PACKED;
+
+typedef struct control_msg_attrid_mac_addr_struct
+{
+  control_msg_common_type common;
+  u16 attrid; 
+  mac_address_type mac_address; 
+}control_msg_attrid_mac_addr_type __PACKED;
+
+
+/****************** EXPORTED FUNCTION DECLARATION SECTION *******************/
+
+#endif /* CONTROL_TYPES_H */
+/****************** END OF FILE control_types.h *****************************/
+
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/cris_drivers_Config.in.patch ./arch/cris/drivers/802_11/cris_drivers_Config.in.patch
--- /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/cris_drivers_Config.in.patch	1970-01-01 01:00:00.000000000 +0100
+++ ./arch/cris/drivers/802_11/cris_drivers_Config.in.patch	2001-05-02 18:35:49.000000000 +0200
@@ -0,0 +1,14 @@
+Index: Config.in
+===================================================================
+RCS file: /n/cvsroot/os/linux/arch/cris/drivers/Config.in,v
+retrieving revision 1.31
+diff -u -p -r1.31 Config.in
+--- Config.in	2001/04/23 13:36:30	1.31
++++ Config.in	2001/05/02 11:51:02
+@@ -176,4 +176,6 @@ if [ "$CONFIG_ETRAX_DS1302" = "y" ]; the
+   int '  DS1302 SDA bit number' CONFIG_ETRAX_DS1302_SDABIT 0
+ fi
+ 
++source arch/cris/drivers/802_11/Config.in
++
+ endmenu
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/cris_drivers_Makefile.patch ./arch/cris/drivers/802_11/cris_drivers_Makefile.patch
--- /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/cris_drivers_Makefile.patch	1970-01-01 01:00:00.000000000 +0100
+++ ./arch/cris/drivers/802_11/cris_drivers_Makefile.patch	2001-09-27 10:48:45.000000000 +0200
@@ -0,0 +1,16 @@
+Index: Makefile
+===================================================================
+RCS file: /n/cvsroot/os/linux/arch/cris/drivers/Makefile,v
+retrieving revision 1.18
+diff -u -r1.18 Makefile
+--- Makefile	2001/06/06 08:56:24	1.18
++++ Makefile	2001/09/27 08:30:09
+@@ -23,5 +23,8 @@
+ obj-$(CONFIG_ETRAX_ETHERNET_LPSLAVE)    += lpslave/lpslavedrivers.o
+ subdir-$(CONFIG_ETRAX_ETHERNET_LPSLAVE) += lpslave
+ 
++obj-$(CONFIG_ETRAX_802_11)             += 802_11/802_11.o
++subdir-$(CONFIG_ETRAX_802_11)          += 802_11
++
+ include $(TOPDIR)/Rules.make
+ 
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/hw_pvl_common.h ./arch/cris/drivers/802_11/hw_pvl_common.h
--- /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/hw_pvl_common.h	1970-01-01 01:00:00.000000000 +0100
+++ ./arch/cris/drivers/802_11/hw_pvl_common.h	2003-04-29 16:16:42.000000000 +0200
@@ -0,0 +1,54 @@
+/*!***************************************************************************
+*!
+*! FILE NAME  : hw_pvl_common.h
+*!
+*! DESCRIPTION: Common things between Master and Slave PVL driver
+*!
+*! FUNCTIONS  :
+*! (EXPORTED)
+*!
+*! ---------------------------------------------------------------------------
+*! (C) Copyright 2001-2003, Axis Communications AB, LUND, SWEDEN
+*!***************************************************************************/
+
+#ifndef HW_PVL_COMMON_H
+#define HW_PVL_COMMON_H
+
+/****************** INCLUDE FILES SECTION ***********************************/
+
+#include "compat.h"
+
+/****************** CONSTANT AND MACRO SECTION ******************************/
+
+/****************** TYPE DEFINITION SECTION *********************************/
+
+/* WARNING!
+ * DO NOT CHANGE THESE VALUES or TYPES!!!
+ * If you MUST change any of these values, both the code on the host
+ * and on the slave must be rebuilt!
+ */
+
+
+/* A PVL transfer is specified in multiples of 32 bytes,
+ * so this structure is 32 bytes long
+ * The info in this struct is used to setup a
+ */
+enum{
+  HW_PVL_PAD_SIZE = 24
+};
+
+
+
+typedef struct hw_pvl_control_transfer_struct
+{
+  u16 type;        /* type of transfer (enum pkttype) */
+  u16 tot_length;  /* Total length of data, always a multiple of 32 bytes */
+  u16 data_ofs;    /* Offset to real data in datastart[] */
+  u16 data_length; /* Length of real data */
+  u8  pad[HW_PVL_PAD_SIZE];   /* Padding, can be used for inline control_msg */
+}hw_pvl_control_transfer_type;
+
+/****************** EXPORTED FUNCTION DECLARATION SECTION *******************/
+
+#endif /* HW_PVL_COMMON_H */
+/****************** END OF FILE hw_pvl_common.h *****************************/
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/ioctl.c ./arch/cris/drivers/802_11/ioctl.c
--- /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/ioctl.c	1970-01-01 01:00:00.000000000 +0100
+++ ./arch/cris/drivers/802_11/ioctl.c	2001-10-17 17:10:09.000000000 +0200
@@ -0,0 +1,95 @@
+#include "mib.h"
+#include <linux/wireless.h>
+
+/* 802.11 counter attribute id:s */
+static const attr_id_type dot11_counter_ids[] =
+{
+	ATTRID_dot11TransmittedFragmentCount_RO_COUNTER,
+	ATTRID_dot11MulticastTransmittedFrameCount_RO_COUNTER,
+	ATTRID_dot11FailedCount_RO_COUNTER,
+/*  dot11_counter dot11RetryCount;         -- not supported */
+/*  dot11_counter dot11MultipleRetryCount; -- not supported */
+	ATTRID_dot11FrameDuplicateCount_RO_COUNTER,
+	ATTRID_dot11RTSSuccessCount_RO_COUNTER,
+	ATTRID_dot11RTSFailureCount_RO_COUNTER,
+	ATTRID_dot11ACKFailureCount_RO_COUNTER,
+	ATTRID_dot11ReceivedFragmentCount_RO_COUNTER,
+	ATTRID_dot11MulticastReceivedFrameCount_RO_COUNTER,
+	ATTRID_dot11FCSErrorCount_RO_COUNTER,
+	ATTRID_dot11TransmittedFrameCount_RO_COUNTER,
+	ATTRID_dot11WEPUndecryptableCount_RO_COUNTER
+};
+
+/* Get 802.11 counters */
+enum {
+	/* odd numbers for get */
+	SIOCGIWCOUNT = (SIOCDEVPRIVATE & ~0x1) + 1
+};
+
+/* Generic ioctl limits */
+enum {
+	IOCTL_GEN_START = SIOCSIWAP,
+	IOCTL_GEN_END   = SIOCGIWRETRY
+};
+
+/* Map generic ioctl number to MIB attribute id */
+static const attr_id_type ioctl_mib_map[] = {
+	ATTRID_dot11MACAddress_RO_MAC, /* SIOCSIWAP, SIOCGIWAP */
+	ATTRID_NONE,                   /* none, SIOCGIWAPLIST */
+	ATTRID_NONE,                   /* none, none */
+	ATTRID_NONE,                   /* SIOCSIWESSID, SIOCGIWESSID */
+	ATTRID_NONE,                   /* SIOCSIWNICKN, SIOCGIWNICKN */
+	ATTRID_NONE,                   /* none, none */
+	ATTRID_NONE,                   /* SIOCSIWRATE, SIOCGIWRATE */
+	ATTRID_dot11RTSThreshold_RW_U16,           /* SIOCSIWRTS, SIOCGIWRTS */
+	ATTRID_dot11FragmentationThreshold_RW_U16, /* SIOCSIWFRAG, 
+						      SIOCGIWFRAG */
+	ATTRID_NONE,                   /* SIOCSIWTXPOW, SIOCGIWTXPOW */
+	ATTRID_NONE, /* unclear how to use SIOCSIWRETRY/SIOCGIWRETRY */
+};
+
+int rave_do_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	struct iwreq *iwr;
+	dot11_counter *counters;
+
+	iwr = (struct iwreq *) ifr;
+	
+	/* convert ioctl number to mib attribute id and determine set/get */
+	if (cmd >= IOCTL_GEN_START && cmd <= IOCTL_GEN_END) {
+		attr_id_type attr_id;
+		
+		rave_assert((IOCTL_GEN_START & 0x1) == 0);
+		attr_id = ioctl_mib_map[(cmd - IOCTL_GEN_START) / 2];
+		if (IW_IS_GET(cmd)) {
+			return rave_mib_request(CTRLMSG_GET_REQUEST, 
+						attr_id, 
+						dev, 
+						iwr);
+		} else {
+			return rave_mib_request(CTRLMSG_SET_REQUEST, 
+						attr_id, 
+						dev, 
+						iwr);
+		}    
+	} else if (cmd == SIOCGIWCOUNT) {
+		int i;
+		int res;
+		
+		counters = (dot11_counter *) iwr->u.data.pointer;
+		for (i = 0; i < sizeof(dot11_counter_ids) / 
+			     sizeof(attr_id_type); i++) {
+			res = rave_mib_request(CTRLMSG_GET_REQUEST, 
+					       dot11_counter_ids[i], 
+					       dev, 
+					       iwr);
+			if (res < 0) {
+				return res;
+			} 
+			*(counters++) = iwr->u.nwid.value;
+		}
+		return 0;
+	}
+	/* posix compliant */
+	return -ENOTTY;
+}
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/ioctl.h ./arch/cris/drivers/802_11/ioctl.h
--- /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/ioctl.h	1970-01-01 01:00:00.000000000 +0100
+++ ./arch/cris/drivers/802_11/ioctl.h	2003-04-29 16:16:42.000000000 +0200
@@ -0,0 +1,8 @@
+/*
+ * Ioctl Interface to the 802.11 driver
+ */
+
+/* Device method "do_ioctl" in Linux network interface for device drivers. */
+int rave_do_ioctl(struct net_device *dev,
+                  struct ifreq *ifr,
+                  int cmd);
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/mem2mem.c ./arch/cris/drivers/802_11/mem2mem.c
--- /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/mem2mem.c	1970-01-01 01:00:00.000000000 +0100
+++ ./arch/cris/drivers/802_11/mem2mem.c	2001-05-10 12:28:25.000000000 +0200
@@ -0,0 +1,604 @@
+/* $Id: mem2mem.c,v 1.2 2001/05/10 10:28:25 johana Exp $
+ *
+ * mem2mem.c: A 802.11 driver using mem2mem DMA
+ * $Log: mem2mem.c,v $
+ * Revision 1.2  2001/05/10 10:28:25  johana
+ * Added some DMA init, packet generation, using mlme_ stuff.
+ * Work in progress...
+ *
+ *
+ * Copyright (C) 2001, Axis Communictaions AB
+ */
+
+static const char* cardname = "ETRAX 100LX 802.11 (PVL) controller Time-stamp: <2001-03-28 13:42:24 johana>";
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <asm/svinto.h>     /* DMA and register descriptions */
+#include <asm/io.h>         /* LED_* I/O functions */
+
+#include "hw_pvl_common.h"
+#include "control_types.h"
+
+#define ETHDEBUG
+#ifdef ETHDEBUG
+#define dprintk printk
+#else
+#define dprintk if(0) printk
+#endif
+
+/* in  mem2mem, ch7 
+   out mem2mem, ch6 
+   ch6 and ch7  collides with ser0 and extDMA1. 
+ */
+
+
+/* These are bits on PA */
+#define MAC_HAS_DATA_CTRL_BIT 0
+#define MAC_HAS_DATA_DATA_BIT 0
+#define MAC_WANT_DATA_CTRL_BIT 0
+#define MAC_WANT_DATA_DATA_BIT 0
+
+/*
+   
+Init.
+   1. Enable mem2mem 0.
+   2. Enable dma6.
+   3. Enable dma7
+   4. Set dir on PA.
+   5. Enable intr on PA.
+   
+Transmission.
+   1. rave_start_xmit is called by kernel to transmit.
+   2. Check MAC_WANT_DATA_CTRL bit or enable interrupt and wait
+   3. Send ToMACctrl data
+   4. tx_interrupt is called when transmission done.
+   5. Check MAC_WANT_DATA_DATA bit or enable interrupt and wait
+   6. Prepend header to packet.
+   7. Append packets to DMA_OUT_CH with eop last in packet.
+   8. tx_interrupt is called when transmission done.
+   9. Free buffers.
+
+Reception.
+   1. MAC_HAS_DATA_CTRL interrupt on PA
+   2. Set up reception of FromMACctrl to DMA_IN_CH (mem2mem)
+   3. rx_interrupt when data received
+   4. Check MAC_HAS_DATA_DATA on PA or enable interrupt and wait
+   5. Set up reception of FromMACdata to DMA_IN_CH (mem2mem)
+   6. rx_interrupt when data received
+   7. We handle packet.
+
+   */
+
+typedef enum pkttype_enum{
+  PKTTYPE_MIB_DATA       = 0x0000, /* control msg */
+  PKTTYPE_PKT_DIX        = 0x0001, /* packet in DIX format */
+} pkttype;
+
+
+
+/* Device functions */
+static int wlan_init(struct net_device *dev);
+static int wlan_open(struct net_device *dev);
+static int wlan_close(struct net_device *dev);
+static int wlan_start_xmit	(struct sk_buff *skb, struct net_device *dev);
+static struct net_device_stats *wlan_get_stats(struct net_device *dev);
+static int wlan_set_mac_address	(struct net_device *dev, void *addr);
+static void	wlan_set_multicast_list	(struct net_device *dev);
+
+/* Internal helper functions */
+static void wlan_hw_ToMAC_send_packet(char *buf, int length);
+
+static void wlan_hw_ToMAC_send_control_msg(control_msg_max_type *msg, 
+                                           int length);
+
+static void wlan_hw_receive_packet(char *buf, int length);
+
+
+
+/* Interrupts */
+static void wlan_hw_GPIOA_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void wlan_hw_DMA_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+
+
+
+
+
+/* Information that need to be kept for each board. */
+struct net_local {
+	struct net_device_stats stats;
+
+	/* Tx control lock.  This protects the transmit buffer ring
+	 * state along with the "tx full" state of the driver.  This
+	 * means all netif_queue flow control actions are protected
+	 * by this lock as well.
+	 */
+	spinlock_t lock;
+};
+
+
+#define ALIGN32BYTE_UP(x) ( ((u32)(x)+31) & ~31)
+#define ALIGN32BYTE_DOWN(x) ( ((u32)(x)) & ~31)
+
+static struct sk_buff *tx_skb;
+
+static int nolink;
+
+
+/* When sending control_msg, we use this buffer,
+ * it is protected by a waitq
+ */
+static control_msg_max_type glob_tx_control_msg __attribute__ ((aligned(4)));
+
+/* Since we use mem2mem DMA there is always a Src and a Dest descriptor
+ * involved in each transfer.
+ * When sending ToMAC we must always wait for ack on PA
+ * after transfering ToMACctrl before we can send ToMACdata.
+ * When receiving FromMAC we must set up the FromMACdata descriptors
+ * when we know the length from FromMACctrl.
+ */
+static etrax_dma_descr ToMacCtrlDescSrc  __attribute__ ((aligned(4)));
+static etrax_dma_descr ToMacCtrlDescDest __attribute__ ((aligned(4)));
+static etrax_dma_descr ToMacDataDescSrc  __attribute__ ((aligned(4)));
+static etrax_dma_descr ToMacDataDescDest __attribute__ ((aligned(4)));
+static hw_pvl_control_transfer_type ToMACctrl __attribute__ ((aligned(4)));
+
+
+static etrax_dma_descr FromMacCtrlDescSrc __attribute__ ((aligned(4)));
+static etrax_dma_descr FromMacCtrlDescDest __attribute__ ((aligned(4)));
+static etrax_dma_descr FromMacDataDescSrc __attribute__ ((aligned(4)));
+static etrax_dma_descr FromMacDataDescDest __attribute__ ((aligned(4)));
+static hw_pvl_control_transfer_type FromMACctrl __attribute__ ((aligned(4)));
+
+#if 0
+/* Fill in where the other chip is memory mapped */
+#define TO_MAC_ADDR    MEM_CSP0_START
+#define FROM_MAC_ADDR  MEM_CSP4_START
+#else
+static unsigned char to_mac_addr[3000];
+static unsigned char from_mac_addr[3000];
+#define TO_MAC_ADDR to_mac_addr
+#define FROM_MAC_ADDR from_mac_addr
+#endif
+
+
+
+static int
+wlan_init(struct net_device *dev)
+{
+        dprintk("\n\n######################################################################\n");
+	dprintk("> mem2mem_init\n");
+	dprintk("%s\n\n", cardname);
+
+	dev->base_addr = (unsigned int)R_EXT_DMA_0_CMD; /* just to have something to show */
+
+	printk("%s initialized\n", dev->name);
+
+	/* make Linux aware of the new hardware  */
+
+	if (!dev) {
+		printk("dev == NULL. Should this happen?\n");
+		dev = init_etherdev(dev, sizeof(struct net_local));
+	}
+
+	/* setup generic handlers and stuff in the dev struct */
+
+	ether_setup(dev);
+
+	/* make room for the local structure containing stats etc */
+
+	dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+	if (dev->priv == NULL)
+		return -ENOMEM;
+	memset(dev->priv, 0, sizeof(struct net_local));
+
+	/* now setup our etrax specific stuff */
+
+
+	/* Receiver DMA */
+	dev->irq = MEM2MEM_DMA_RX_IRQ_NBR;
+	dev->dma = MEM2MEM_RX_DMA_NBR;
+	
+	/* fill in our handlers so the network layer can talk to us in the future */
+
+	dev->open               = wlan_open;
+	dev->hard_start_xmit    = wlan_start_xmit;
+	dev->stop               = wlan_close;
+	dev->get_stats          = wlan_get_stats;
+	dev->set_multicast_list = wlan_set_multicast_list;
+	dev->set_mac_address    = wlan_set_mac_address;
+
+	
+#if 0
+	/* set the default MAC address */
+	if(wlan_set_mac_address(dev, &default_mac)) {
+	  return -ETIMEDOUT;
+	}
+
+	/* Initialise receive descriptors */
+	{
+	  int i;
+	  int anOffset = 0;
+	  
+	  for(i = 0; i < NBR_OF_RX_DESC; i++) {
+	    RxDescList[i].ctrl   = 0;
+	    RxDescList[i].sw_len = RX_DESC_BUF_SIZE;
+	    RxDescList[i].next   = virt_to_phys(&RxDescList[i + 1]);
+	    RxDescList[i].buf    = virt_to_phys(RxBuf + anOffset);
+	    RxDescList[i].status = 0;
+	    RxDescList[i].hw_len = 0;
+	    anOffset += RX_DESC_BUF_SIZE;
+	  }
+	  
+	  /* wrap the last one */
+	  i--;
+	  RxDescList[i].ctrl   = d_eol;
+	  RxDescList[i].next   = virt_to_phys(&RxDescList[0]);
+	}
+	
+	//	print_descr(&RxDescList[0]);
+	//	print_descr(&RxDescList[NBR_OF_RX_DESC-1]);
+
+	/* Initialize initial pointers */
+	myNextRxDesc = &RxDescList[0];
+	myLastRxDesc = &RxDescList[NBR_OF_RX_DESC - 1];
+	myPrevRxDesc = &RxDescList[NBR_OF_RX_DESC - 1];
+
+	/* Initialize descriptor for the header we send before all
+           packets to the FPGA. */
+	RaveHdrDesc.sw_len = sizeof(struct rave_hdr);
+	RaveHdrDesc.ctrl = 0;
+	RaveHdrDesc.buf  = virt_to_phys(&rave_hdr);
+	RaveHdrDesc.next = virt_to_phys(&TxDesc);
+#endif
+
+	/* Initialize speed indicator stuff. */
+	nolink = 0;
+#if 0
+	current_speed = 11;
+	speed_timer.expires = jiffies + NET_LINK_UP_CHECK_INTERVAL;
+	speed_timer.function = rave_check_speed;
+	add_timer(&speed_timer);
+	clear_led_timer.function = rave_clear_network_leds;
+	clear_led_timer.expires = jiffies + 10;
+	add_timer(&clear_led_timer);
+
+	printk("< rave_init\n");
+#endif
+	return 0;
+}
+
+static int
+wlan_open(struct net_device *dev)
+{
+  
+	/* Port PA interrupt */
+	if (request_irq(PA_IRQ_NBR, wlan_hw_GPIOA_interrupt, SA_SHIRQ, cardname, (void *)dev)) {
+	  printk("PA_IRQ_NBR\n");
+	  goto request_fail;
+	}
+  	/* Transmission DMA, needs intr to see when transmission is done.
+     * We only uses the RX interrupt.  
+     */
+	if (request_irq(MEM2MEM_DMA_RX_IRQ_NBR, wlan_hw_DMA_interrupt, 0, cardname, (void *)dev)) {
+	  printk("MEM2MEM_DMA_RX_IRQ_NBR failed\n");
+	  goto request_fail;
+	}
+	
+	if (request_dma(MEM2MEM_RX_DMA_NBR, cardname)) {
+	  printk("MEM2MEM_RX_DMA_NBR failed\n");
+	  goto request_fail;
+	}
+	
+	if (request_dma(MEM2MEM_TX_DMA_NBR, cardname)) {
+	  printk("MEM2MEM_TX_DMA failed\n");
+	  
+	request_fail:
+	  wlan_close(dev);
+	  return -EACCES;
+	}
+	
+	/* Enable pa3 interrupt */
+	*R_IRQ_MASK1_SET = 
+	  IO_STATE(R_IRQ_MASK1_SET, pa3, set);
+}
+
+/* 
+   The inverse routine to net_open(). 
+   No point in changing the channels in R_GEN_CONFIG since
+   there is no 'unused' to set them to. 
+   */
+
+static int
+wlan_close(struct net_device *dev)
+{
+	struct net_local *np = (struct net_local *)dev->priv;
+
+	dprintk("Closing %s.\n", dev->name);
+}
+
+
+/* This will only be invoked if the driver is _not_ in XOFF state.
+ * What this means is that we need not check it, and that this
+ * invariant will hold if we make sure that the netif_*_queue()
+ * calls are done at the proper times.
+ */
+
+static int
+wlan_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	struct net_local *np = (struct net_local *)dev->priv;
+	int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+	unsigned char *buf = skb->data;
+
+	dprintk("> wlan_start_xmit\n");
+
+	spin_lock_irq(&np->lock);  /* protect from tx_interrupt */
+
+	tx_skb = skb; /* remember it so we can free it in the tx irq handler later */
+	dev->trans_start = jiffies;
+	
+	wlan_hw_ToMAC_send_packet(buf, length);
+
+	/* this simple TX driver has only one send-descriptor so we're full
+	 * directly. If this had a send-ring instead, we would only do this if
+	 * the ring got full.
+	 */
+
+	netif_stop_queue(dev);
+
+	spin_unlock_irq(&np->lock);
+
+	dprintk("< wlan_start_xmit\n");
+	return 0;
+}
+
+/*
+ * Get the current statistics.
+ * This may be called with the card open or closed.
+ */
+static struct net_device_stats *
+wlan_get_stats(struct net_device *dev)
+{
+	struct net_local *lp = (struct net_local *)dev->priv;
+
+	printk("> wlan_get_stats\n");
+
+//	update_rx_stats(&lp->stats);
+//	update_tx_stats(&lp->stats);
+
+	return &lp->stats;
+}
+
+/* set MAC address of the interface. called from the core after a
+ * SIOCSIFADDR ioctl, and from the bootup above.
+ */
+
+static int
+wlan_set_mac_address(struct net_device *dev, void *p)
+{
+	struct sockaddr *addr = p;
+  control_msg_max_type *tx_control_msg = &glob_tx_control_msg;
+
+  int len;
+  int result;
+  
+	/* remember it */
+  memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+	
+	dprintk("> wlan_set_mac_address, %s: changed MAC to ", dev->name);
+#ifdef ETHDEBUG
+  {
+    int i;
+  	for (i = 0; i <= 5; i++)
+	    printk("%02X%c", dev->dev_addr[i], i!=5 ? ':' : '\n');
+  }
+  
+#endif
+  len = mlme_set_mac_address(tx_control_msg, dev->dev_addr);
+  
+	/* TODO: Send to MAC and wait for result */
+  result = 0;
+	return result;
+
+}
+
+/*
+ * Set or clear the multicast filter for this adaptor.
+ * num_addrs == -1	Promiscuous mode, receive all packets
+ * num_addrs == 0	Normal mode, clear multicast list
+ * num_addrs > 0	Multicast mode, receive normal and MC packets,
+ *			and do best-effort filtering.
+ */
+
+static void
+wlan_set_multicast_list(struct net_device *dev)
+{
+  int num_addr = dev->mc_count;
+  control_msg_max_type *tx_control_msg = &glob_tx_control_msg;
+  int len;
+
+  printk("> wlan_set_multicast_list\n");
+
+  if (num_addr == -1) {
+    printk("/* promiscuous mode */\n");
+    len = mlme_set_promiscous_mode(tx_control_msg);
+  } else if (num_addr == 0) {
+    printk("/* Clear the mc list */\n");
+    len = mlme_set_multicast_list(tx_control_msg, num_addr, dev->mc_list);
+  } else {
+    printk("/* MC mode, receive normal and MC packets */\n");
+    len = mlme_set_multicast_list(tx_control_msg, num_addr, dev->mc_list);
+  }
+}
+
+
+/****************************************************************************/
+
+void wlan_hw_ToMAC_send_control_msg(control_msg_max_type *msg, int len)
+{
+  /* TODO?: Only two fields are changed in ToMACctrl, optimise it? */
+  ToMACctrl.type = PKTTYPE_MIB_DATA;
+  ToMACctrl.data_ofs = 0;
+  ToMACctrl.data_length = len;
+  len = ALIGN32BYTE_UP(len);
+  ToMACctrl.tot_length = len;
+    
+  /* TODO?: Only DescSrc.buf changes so here is room for imrovments */
+  ToMacCtrlDescSrc.sw_len = sizeof(hw_pvl_control_transfer_type);
+  ToMacCtrlDescSrc.ctrl =  d_eop | d_eol | d_wait;
+  ToMacCtrlDescSrc.next = virt_to_phys(0);
+  ToMacCtrlDescSrc.buf = virt_to_phys(&ToMACctrl);  
+
+  ToMacCtrlDescDest.sw_len = sizeof(hw_pvl_control_transfer_type);
+  ToMacCtrlDescDest.ctrl = d_eop | d_eol | d_wait;
+  ToMacCtrlDescDest.next = virt_to_phys(0);
+  ToMacCtrlDescDest.buf = virt_to_phys(TO_MAC_ADDR);  
+  
+  /* Start RX before TX */
+  /* RX */
+  *R_DMA_CH7_FIRST = virt_to_phys(&ToMacCtrlDescDest);
+  *R_DMA_CH7_CMD   = IO_STATE(R_DMA_CH7_CMD, cmd, start);
+  
+  /* TX */
+  *R_DMA_CH6_FIRST = virt_to_phys(&ToMacCtrlDescSrc);
+  *R_DMA_CH6_CMD   = IO_STATE(R_DMA_CH6_CMD, cmd, start);
+
+  /* Sending these 32 bytes takes only a few cycles,
+     so we wait for it to complete here */
+  WAIT_DMA(MEM2MEM_TX_DMA_NBR);
+  WAIT_DMA(MEM2MEM_RX_DMA_NBR);
+
+  
+  /* TODO?: Only DescSrc.buf and .sw_len changes so here is room for imrovments */
+  ToMacDataDescSrc.sw_len = len;
+  ToMacDataDescSrc.ctrl =  d_eop | d_eol | d_wait;
+  ToMacDataDescSrc.next = virt_to_phys(0);
+  ToMacDataDescSrc.buf = virt_to_phys(msg);  
+
+  ToMacDataDescDest.sw_len = len;
+  ToMacDataDescDest.ctrl = d_eop | d_eol | d_wait;
+  ToMacDataDescDest.next = virt_to_phys(0);
+  ToMacDataDescDest.buf = virt_to_phys(TO_MAC_ADDR);  
+  
+  /* Start RX before TX */
+  /* RX */
+  *R_DMA_CH7_FIRST = virt_to_phys(&ToMacDataDescDest);
+  *R_DMA_CH7_CMD   = IO_STATE(R_DMA_CH7_CMD, cmd, start);
+  
+  /* TX */
+  *R_DMA_CH6_FIRST = virt_to_phys(&ToMacDataDescSrc);
+  *R_DMA_CH6_CMD   = IO_STATE(R_DMA_CH6_CMD, cmd, start);
+
+#if 1 /* TESTING */  
+/* Wait for DMA to be complete and compare contents */  
+  WAIT_DMA(MEM2MEM_TX_DMA_NBR);
+  WAIT_DMA(MEM2MEM_RX_DMA_NBR);
+  if (memcmp(msg, TO_MAC_ADDR, len) != 0)
+  {
+    printk("mem2mem data corrupt!\n");
+  }
+  else
+  {
+    printk("mem2mem data %i ok\n", len);
+  }
+  
+#endif  
+
+  
+}
+
+
+void
+wlan_hw_ToMAC_send_packet(char *buf, int length)
+{
+}
+
+
+void
+wlan_hw_receive_packet(char *buf, int length)
+{
+}
+
+
+
+/****************************************************************************/
+
+static void
+wlan_hw_GPIOA_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+  struct net_device *dev = (struct net_device *)dev_id;
+  unsigned long irqbits = *R_IRQ_MASK1_RD;
+
+  dprintk("> wlan_hw_GPIO_interrupt\n");
+
+  if (irqbits & (1 <<(R_IRQ_MASK1_RD__pa0__BITNR+MAC_HAS_DATA_CTRL_BIT)))
+  {
+    dprintk("MAC_HAS_DATA_CTRL\n");
+  }
+  if (irqbits & (1 <<(R_IRQ_MASK1_RD__pa0__BITNR+MAC_HAS_DATA_DATA_BIT)))
+  {
+    dprintk("MAC_HAS_DATA_DATA\n");
+  }
+  if (irqbits & (1 <<(R_IRQ_MASK1_RD__pa0__BITNR+MAC_WANT_DATA_CTRL_BIT)))
+  {
+    dprintk("MAC_WANT_DATA_CTRL\n");
+  }
+  if (irqbits & (1 <<(R_IRQ_MASK1_RD__pa0__BITNR+MAC_WANT_DATA_DATA_BIT)))
+  {
+    dprintk("MAC_WANT_DATA_DATA\n");
+  }
+}
+
+static void
+wlan_hw_DMA_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+  struct net_device *dev = (struct net_device *)dev_id;
+  dprintk("> wlan_hw_DMA_interrupt\n");
+}
+
+
+
+
+/****************************************************************************/
+
+
+static struct net_device dev_etrax_802_11;  /* only got one */
+static int __init
+mem2mem_init_module(void)
+{
+	struct net_device *d = &dev_etrax_802_11;
+
+	printk("> mem2mem_init_module\n");
+	d->init = wlan_init;
+
+	if(register_netdev(d) == 0)
+		return 0;
+	else
+		return -ENODEV;
+}
+
+module_init(mem2mem_init_module);
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/mib.c ./arch/cris/drivers/802_11/mib.c
--- /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/mib.c	1970-01-01 01:00:00.000000000 +0100
+++ ./arch/cris/drivers/802_11/mib.c	2003-04-29 16:16:42.000000000 +0200
@@ -0,0 +1,373 @@
+#include "mib.h"
+#include "tx.h"
+
+/* MAC Address */
+enum {
+	DOT2_MAC_ADDR_LEN = 6
+};
+
+typedef struct mac_addr {
+	mac_address_type ddr;
+} mac_addr;
+
+typedef u_int16_t mib_type_t;
+typedef u_int16_t mib_len_t;
+typedef u_int16_t mib_id_t;
+typedef u_int16_t mib_stat_t;
+typedef u_int16_t mib_attrid_t;
+
+/* Attribute Types */
+typedef enum mib_attr_type {
+	MIB_ATTR_TYPE_U16 = 0,
+	MIB_ATTR_TYPE_U32,
+	MIB_ATTR_TYPE_MAC,
+	MIB_ATTR_TYPE_GA       /* group addresses */
+} mib_attr_type;
+
+/* Length of value for attribute different types */
+static const mib_len_t mib_attr_len[] = { 2, 4, DOT2_MAC_ADDR_LEN, 0 };
+
+typedef struct mib_attr_info {
+	mib_attrid_t  id;    
+	mib_attr_type type;  
+} mib_attr_info;
+
+/* Attributes */
+static mib_attr_info attrs[] = {
+	/* dot11MacAddress */
+	{ ATTRID_dot11MACAddress_RO_MAC,
+	  MIB_ATTR_TYPE_MAC,
+	},
+	/* dot11RtsThreshold */
+	{ ATTRID_dot11RTSThreshold_RW_U16,
+	  MIB_ATTR_TYPE_U16
+	},
+	/* dot11FragmentationThreshold */
+	{ ATTRID_dot11FragmentationThreshold_RW_U16,
+	  MIB_ATTR_TYPE_U16
+	},
+	/* dot11TransmittedFragmentCount */
+	{ ATTRID_dot11TransmittedFragmentCount_RO_COUNTER,
+	  MIB_ATTR_TYPE_U32
+	},
+	/* dot11MulticastTransmittedFrameCount */
+	{ ATTRID_dot11MulticastTransmittedFrameCount_RO_COUNTER,
+	  MIB_ATTR_TYPE_U32
+	},
+	/* dot11FailedCount */
+	{ ATTRID_dot11FailedCount_RO_COUNTER,
+	  MIB_ATTR_TYPE_U32
+	},
+	/* dot11FrameDuplicateCount */
+	{ ATTRID_dot11FrameDuplicateCount_RO_COUNTER,
+	  MIB_ATTR_TYPE_U32
+	},
+	/* dot11RTSSuccessCount */
+	{ ATTRID_dot11RTSSuccessCount_RO_COUNTER,
+	  MIB_ATTR_TYPE_U32
+	},
+	/* dot11RTSFailureCount */
+	{ ATTRID_dot11RTSFailureCount_RO_COUNTER,
+	  MIB_ATTR_TYPE_U32
+	},
+	/* dot11ACKFailureCount */
+	{ ATTRID_dot11ACKFailureCount_RO_COUNTER,
+	  MIB_ATTR_TYPE_U32
+	},
+	/* dot11ReceivedFragmentCount */
+	{ ATTRID_dot11ReceivedFragmentCount_RO_COUNTER,
+	  MIB_ATTR_TYPE_U32
+	},
+	/* dot11MulticastReceivedFrameCount */
+	{ ATTRID_dot11MulticastReceivedFrameCount_RO_COUNTER,
+	  MIB_ATTR_TYPE_U32
+	},
+	/* dot11FCSErrorCount */
+	{ ATTRID_dot11FCSErrorCount_RO_COUNTER,
+	  MIB_ATTR_TYPE_U32
+	},
+	/* dot11TransmittedFrameCount */
+	{ ATTRID_dot11TransmittedFrameCount_RO_COUNTER,
+	  MIB_ATTR_TYPE_U32
+	},
+	/* dot11WEPUndecryptableCount */
+	{ ATTRID_dot11WEPUndecryptableCount_RO_COUNTER,
+	  MIB_ATTR_TYPE_U32,
+	},
+	/* Group Address List */
+	{ ATTRID_XXdot11AddressTable_WO_MACLIST,
+	  MIB_ATTR_TYPE_GA
+	}
+};
+
+typedef struct mib_msg {
+	mib_type_t type;
+	mib_len_t  len;
+	mib_id_t   id;
+	mib_stat_t stat;
+	u_int8_t     value[1];
+} mib_msg __attribute__((packed));
+
+typedef struct mib_req {
+	mib_attrid_t attrid;
+	union {
+		u_int16_t  u16;
+		u_int32_t  u32;
+		mac_addr mac;
+		u_int8_t   str[1];
+	} value __attribute__((packed));
+} mib_req __attribute__((packed));
+
+enum { 
+	MIB_LEN_TYPE    = 2,
+	MIB_LEN_LEN     = 2,
+	MIB_LEN_ID      = 2,
+	MIB_LEN_STATUS  = 2,
+	MIB_LEN_ATTR_ID = 2,
+	MIB_HDR_LEN     = MIB_LEN_TYPE + MIB_LEN_LEN + MIB_LEN_ID + 
+	                  MIB_LEN_STATUS,
+	MIB_VAL_MAX_LEN = DOT2_MAC_ADDR_LEN,
+	MIB_MSG_MAX_LEN = MIB_HDR_LEN + MIB_LEN_ATTR_ID +
+                          CTRLMSG_MAX_VALUE_LENGTH
+};
+
+/* only support synchronous request handling for now */
+static mib_id_t cur_id = 0;
+/* only support one MIB request at the time for now */
+static bool mib_pending = FALSE;
+/* current receive socket buffer */
+static struct sk_buff *rx_skb = NULL;
+/* wait queue for MIB requests waiting to be processed */
+static wait_queue_head_t mib_wq;
+/* wait queue for MIB requests sent to slave and waiting for response */
+static wait_queue_head_t mib_sent_wq;
+
+/* Find information about an attribute */
+static mib_attr_info *find_attr(mib_attrid_t id);
+/* Build MIB set request */
+static int build_set_req(mib_attr_type type,
+			 attr_id_type *attr_id, 
+			 struct net_device *dev, 
+			 struct iwreq *iwr, 
+			 mib_req *req,
+			 uint16_t *len);
+/* Receive parameters in MIB message return */
+int handle_get_return(mib_attr_type type, struct iwreq *iwr, mib_req *req);
+/* Send the MIB request */
+static int send_req(u_int8_t *req, u_int16_t len);
+static void mib_req_enter(void);
+static void mib_req_exit(void);
+
+void rave_mib_init(void)
+{
+	init_waitqueue_head(&mib_wq);
+	init_waitqueue_head(&mib_sent_wq);
+}
+
+int rave_mib_request(control_msg_enum_type req_type, 
+                attr_id_type attr_id, 
+                struct net_device *dev,
+                struct iwreq *iwr)
+{
+	mib_attr_info *attr;
+	u_int16_t len;
+	static u_int8_t buf[MIB_MSG_MAX_LEN];
+	mib_msg *msg = (mib_msg *) buf;
+	mib_req *req = (mib_req *) msg->value;
+	int res;
+	
+	/* synchronize concurrent requests */
+	mib_req_enter();
+	
+	/* find attribute */
+	attr = find_attr(attr_id);
+	if (attr == NULL) {
+		res = -EINVAL;
+		goto done;
+	}
+	len = MIB_LEN_ATTR_ID;
+	
+	/* set value */
+	if (req_type == CTRLMSG_SET_REQUEST) {
+		res = build_set_req(attr->type, &attr_id, dev, iwr, req, &len);
+		if (res < 0) {
+			goto done;
+		}	
+	}
+	
+	/* set field values */
+	msg->type = req_type;
+	msg->len = len;
+	msg->id  = cur_id;
+	req->attrid = attr_id;
+	
+	/* send request */
+	res = send_req(buf, MIB_HDR_LEN + len);
+	if (res < 0) {
+		goto done;
+	}
+	
+	/* block calling process until response received */
+	/* XXX: handle time out somehow */
+	interruptible_sleep_on(&mib_sent_wq);
+	
+	rave_assert(rx_skb != NULL);
+	msg = (mib_msg *) rx_skb->data;
+	req = (mib_req *) msg->value;
+	
+	if (msg->id != cur_id++ ||  msg->stat != CTRLSTATUS_OK) {
+		/* most likely reason for second condition, for first we would 
+		 * need errno for "internal error" */
+		dev_kfree_skb_any(rx_skb);
+		res = -EACCES;
+		goto done;
+	}
+	
+	if (req_type == CTRLMSG_GET_REQUEST) {
+		res = handle_get_return(attr->type, iwr, req);
+	}
+	
+ done:
+	mib_req_exit();
+	return res;
+}
+
+void rave_mib_response(struct sk_buff *skb)
+{
+	rx_skb = skb;
+	/* wake process waiting for MIB response */
+	/* XXX: a) the woken process will execute in non-interrupt-context? */
+	/*      b) is it ok to wake_up from here (rx interrupt context)? */
+	wake_up_interruptible(&mib_sent_wq);
+}
+
+mib_attr_info *find_attr(mib_attrid_t id)
+{ 
+	unsigned int first = 0;
+	unsigned int last  = sizeof(attrs) / sizeof(mib_attr_info);
+	unsigned int mid;
+	
+	/* search for attribute id in table */
+	while (1) {
+		mid = first + (last - first) / 2;
+		if (id < attrs[mid].id) {
+			if (mid == last) {
+				return NULL;
+			}
+			last = mid;
+		} else if (id > attrs[mid].id) {
+			if (mid == first) {
+				return NULL;
+			}
+			first = mid;
+		} else {
+			return &attrs[mid]; /* got it */
+		}
+	}
+	rave_assert(0);
+	return NULL;
+}
+
+int build_set_req(mib_attr_type type,
+		  attr_id_type *attr_id, 
+		  struct net_device *dev, 
+		  struct iwreq *iwr, 
+		  mib_req *req,
+		  uint16_t *len)
+{
+	*len += mib_attr_len[type];
+	switch (type) {
+	case MIB_ATTR_TYPE_U16:
+		rave_assert(iwr != NULL);
+		req->value.u16 = iwr->u.nwid.value;
+		break;
+	case MIB_ATTR_TYPE_U32:
+		rave_assert(iwr != NULL);
+		req->value.u32 = iwr->u.nwid.value;
+		break;
+	case MIB_ATTR_TYPE_GA:
+		rave_assert(dev != NULL);
+		switch (dev->mc_count) {
+		case -1:
+				/* Promiscues mode */
+			*attr_id =
+				ATTRID_XXdot11PromiscousMode_WO_NOVALUE;
+			break;
+		case 0:
+				/* empty list */
+			break;
+		default:
+		{
+			struct dev_mc_list *mc_list = dev->mc_list;
+			mac_addr *addr  = (mac_addr *) req->value.str;
+			while (mc_list != NULL) {
+				memcpy(addr++, mc_list->dmi_addr, 
+				       DOT2_MAC_ADDR_LEN);
+				*len += DOT2_MAC_ADDR_LEN;
+				mc_list = mc_list->next;
+			}
+		}
+			break;
+		}
+		break;
+	default:
+		rave_assert(0);
+		/* would need errno for "internal error" here */
+		return -EACCES;
+	}
+	return 0;
+}
+
+int handle_get_return(mib_attr_type type, struct iwreq *iwr, mib_req *req)
+{
+	rave_assert(iwr != NULL);
+	switch (type) {
+	case MIB_ATTR_TYPE_U16:
+		iwr->u.nwid.value = req->value.u16;
+		break;
+	case MIB_ATTR_TYPE_U32:
+		iwr->u.nwid.value = req->value.u32;
+		break;
+	case MIB_ATTR_TYPE_MAC:
+		memcpy(iwr->u.data.pointer, &req->value.mac, 
+		       DOT2_MAC_ADDR_LEN);
+		iwr->u.data.length = DOT2_MAC_ADDR_LEN;
+		break;
+	default:
+		rave_assert(0);
+		/* would need errno for "internal error" here */
+		dev_kfree_skb_any(rx_skb);
+		return -EACCES;
+	}
+	return 0;
+}
+
+
+int send_req(u_int8_t *req, u_int16_t len)
+{
+	static struct sk_buff skb;
+	
+	skb.data = req;
+	skb.len  = len;
+	
+	return rave_mib_start_xmit(&skb);
+}
+
+void mib_req_enter(void) 
+{
+	/* since this is only called from non-interrupt context, there are no
+	 * race conditions with respect to the updating of mib_pending */
+	if (mib_pending) {
+		/* block calling process until we are ready with pending 
+		 * request  */
+		interruptible_sleep_on(&mib_wq);
+	}
+	mib_pending = TRUE;
+}
+
+void mib_req_exit(void)
+{
+	mib_pending = FALSE;
+	/* harmless to wake empty queue */
+	wake_up_interruptible(&mib_wq);
+}
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/mib.h ./arch/cris/drivers/802_11/mib.h
--- /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/mib.h	1970-01-01 01:00:00.000000000 +0100
+++ ./arch/cris/drivers/802_11/mib.h	2003-04-29 16:16:42.000000000 +0200
@@ -0,0 +1,23 @@
+/*
+ * MIB Interface to the 802.11 driver
+ */
+
+#ifndef MIB_H
+#define MIB_H
+
+#include "rave_global.h"
+#include "control_types.h"
+#include <linux/wireless.h>
+#include <linux/netdevice.h>
+
+/* Initiate MIB interface */
+void rave_mib_init(void);
+/* Do MIB request. Blocks caller in waitqueue until response received. */
+int rave_mib_request(control_msg_enum_type req_type, 
+                attr_id_type attr_id, 
+                struct net_device *dev,
+                struct iwreq *iwr);
+/* Called by rx interrupt handler to forward response from MIB */
+void rave_mib_response(struct sk_buff *skb);
+
+#endif /* MIB_H */
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/mlme.c ./arch/cris/drivers/802_11/mlme.c
--- /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/mlme.c	1970-01-01 01:00:00.000000000 +0100
+++ ./arch/cris/drivers/802_11/mlme.c	2003-04-29 16:16:42.000000000 +0200
@@ -0,0 +1,142 @@
+/*
+ * mlme.c: MAC Layer Managment Entity code.
+ */
+
+#include "mlme.h"
+#include "control_types.h"
+
+enum
+{
+  ATTRDIR_RO,
+  ATTRDIR_WO,
+  ATTRDIR_RW
+};
+
+enum
+{
+  ATTRTYPE_NOVALUE,
+  ATTRTYPE_MAC,
+  ATTRTYPE_MACLIST,
+  ATTRTYPE_U16,
+  ATTRTYPE_U32,
+  ATTRTYPE_COUNTER,
+  ATTRTYPE_U32PAIR,
+  ATTRTYPE_U32PAIRLIST,
+  ATTRTYPE_OCTETSTRING,
+  ATTRTYPE_DISPLAYSTRING
+};
+
+
+typedef struct attrdef_struct{
+  u16 attrid;
+  const char *attrname;
+  u8 attrdir;
+  u8 attrtype;
+  u32 min;
+  u32 max;
+} attrdef_struct;
+
+
+#define ATTRDEF_MAC(name, dir, type) { ATTRID_##name##_##dir##_##type, #name, ATTRDIR_##dir, ATTRTYPE_##type, 0, 0}
+#define ATTRDEF_MACLIST(name, dir, type) { ATTRID_##name##_##dir##_##type, #name, ATTRDIR_##dir, ATTRTYPE_##type, 0, 0}
+
+#define ATTRDEF(name, dir, type, min, max) { ATTRID_##name##_##dir##_##type, #name, ATTRDIR_##dir, ATTRTYPE_##type, min, max}
+
+#define ATTRDEF_INT(name, dir, type, min, max) { ATTRID_##name##_##dir##_##type, #name, ATTRDIR_##dir, ATTRTYPE_##type, min, max}
+
+#define ATTRDEF_DSTR(name, dir, type, min, max) { ATTRID_##name##_##dir##_##type, #name, ATTRDIR_##dir, ATTRTYPE_##type, min, max}
+
+#define ATTRDEF_CNT(name, dir ) { ATTRID_##name##_##dir##_COUNTER, #name, ATTRDIR_##dir, ATTRTYPE_COUNTER, 0, 0xFFFFFFFFL}
+
+
+
+
+static struct attrdef_struct attrdef[] ={
+  ATTRDEF_MAC(MACSTATE,RW,MAC),
+  ATTRDEF_MAC(DEBUG_ASSOCIATE_AP,WO,MAC),
+ /* dot11 2 dot11mac, 1 Operation */
+
+  ATTRDEF_MAC(dot11MACAddress,RO,MAC),
+  ATTRDEF_INT(dot11RTSThreshold,RW,U16,0,2347),
+  ATTRDEF_INT(dot11ShortRetryLimit,RW,U16,0,255),
+  ATTRDEF_INT(dot11LongRetryLimit,RW,U16,0,255),
+  ATTRDEF_INT(dot11FragmentationThreshold,RW,U16,256,2346),
+  ATTRDEF_INT(dot11MaxTransmitMSDULifetime,RW,U32,1,4294967295UL),
+  ATTRDEF_INT(dot11MaxReceveLifetime,RW,U32,1,4294967295UL),
+  ATTRDEF_DSTR(dot11ManufacturerID,RO,DISPLAYSTRING,0,128),
+  ATTRDEF_DSTR(dot11ProductID,RO,DISPLAYSTRING,0,128),
+
+  /* dot11 2 dot11mac, 2 Counters */
+  ATTRDEF_CNT(dot11TransmittedFragmentCount,RO),
+  ATTRDEF_CNT(dot11MulticastTransmittedFrameCount,RO),
+  ATTRDEF_CNT(dot11FailedCount,RO),
+  ATTRDEF_CNT(dot11RetryCount,RO),
+  ATTRDEF_CNT(dot11MultipleRetryCount,RO),
+  ATTRDEF_CNT(dot11FrameDuplicateCount,RO),
+  ATTRDEF_CNT(dot11RTSSuccessCount,RO),
+  ATTRDEF_CNT(dot11RTSFailureCount,RO),
+  ATTRDEF_CNT(dot11ACKFailureCount,RO),
+  ATTRDEF_CNT(dot11ReceivedFragmentCount,RO),
+  ATTRDEF_CNT(dot11MulticastReceivedFrameCount,RO),
+  ATTRDEF_CNT(dot11FCSErrorCount,RO),
+  ATTRDEF_CNT(dot11TransmittedFrameCount,RO),
+  ATTRDEF_CNT(dot11WEPUndecryptableCount,RO),
+
+/* dot11 2 dot11mac, 3 GroupAddresses */
+  ATTRDEF_MACLIST(XXdot11AddressTable,WO,MACLIST),
+  ATTRDEF(XXdot11PromiscousMode,WO,NOVALUE, 0,0),
+
+ /* dot11 4 dot11phy 9 SupportedDataRatesTx */
+  ATTRDEF(dot11SupportedDataRatesTxEntry,RO,U32PAIRLIST,0,0),
+
+ /* dot11 4 dot11phy 10 SupportedDataRatesRx */
+  ATTRDEF(dot11SupportedDataRatesRxEntry,RO,U32PAIRLIST,0,0),
+};
+
+static u16 nextid = 0;
+
+int mlme_set_mac_address(u8 *buf, u8 *macaddr)
+{
+  control_msg_attrid_mac_addr_type *msg = (control_msg_attrid_mac_addr_type*)buf;
+  msg->common.type = CTRLMSG_SET_REQUEST;
+  msg->common.length = MAC_ADDRESS_SIZE;
+  msg->common.id = nextid++;
+  msg->common.status = CTRLSTATUS_OK;
+  msg->attrid = ATTRID_MACSTATE_RW_MAC;
+  memcpy(msg->mac_address, macaddr, MAC_ADDRESS_SIZE);
+  return CTRL_MSG_TOTSIZE(msg);
+}
+
+int mlme_set_promiscous_mode(u8 *buf)
+{
+  control_msg_attrid_val16_type *msg = (control_msg_attrid_val16_type*)buf;
+  msg->common.type = CTRLMSG_SET_REQUEST;
+  msg->common.length = 2;
+  msg->common.id = nextid++;
+  msg->common.status = CTRLSTATUS_OK;
+  msg->attrid = ATTRID_XXdot11PromiscousMode_WO_NOVALUE;
+  return CTRL_MSG_TOTSIZE(msg);
+}
+
+
+int mlme_set_multicast_list(u8 *buf, int mc_count, 
+                            struct dev_mc_list *mc_list)
+{
+  control_msg_attrid_max_type *msg = (control_msg_attrid_max_type*)buf;
+  int i;
+  u8 *val = msg->value;
+  msg->common.type = CTRLMSG_SET_REQUEST;
+  msg->common.length = 2+MAC_ADDRESS_SIZE*mc_count;
+  msg->common.id = nextid++;
+  msg->common.status = CTRLSTATUS_OK;
+  msg->attrid = ATTRID_XXdot11AddressTable_WO_MACLIST;
+  
+  for (i=0;  i!=mc_count; i++) {
+    memcpy(val, mc_list->dmi_addr, MAC_ADDRESS_SIZE);
+    mc_list = mc_list->next;
+    val += MAC_ADDRESS_SIZE;
+  }
+  
+
+  return CTRL_MSG_TOTSIZE(msg);
+}
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/mlme.h ./arch/cris/drivers/802_11/mlme.h
--- /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/mlme.h	1970-01-01 01:00:00.000000000 +0100
+++ ./arch/cris/drivers/802_11/mlme.h	2003-04-29 16:16:42.000000000 +0200
@@ -0,0 +1,15 @@
+/*
+ * mlme.h
+ */
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include "compat.h"
+
+/* Write mlme message to buf, return length */
+int mlme_set_mac_address(u8 *buf, u8 *macaddr);
+
+int mlme_set_promiscous_mode(u8 *buf);
+
+int mlme_set_multicast_list(u8 *buf, int mc_count,
+                            struct dev_mc_list *mc_list);
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/rave.c ./arch/cris/drivers/802_11/rave.c
--- /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/rave.c	1970-01-01 01:00:00.000000000 +0100
+++ ./arch/cris/drivers/802_11/rave.c	2003-04-29 16:16:42.000000000 +0200
@@ -0,0 +1,623 @@
+/*
+ * rave.c: A 802.11 driver for the rave project.
+ */
+
+/*
+   The name of the card. Is used for messages and in the requests for
+   io regions, irqs and dma channels
+   */
+static const char* cardname = "ETRAX 100LX 802.11 (rave) controller Time-stamp: <2001-08-30 16:05:00 ronny>";
+
+/*
+
+  in  extdma0, ch5, pa3 (RX_EOP), pb0 (RX_ACK)
+  out extdma1, ch6 collides with ser0.
+
+   
+Init.
+   1. Enable extdma 0.
+   2. Enable dma5.
+   3. Enable dma6
+   4. Set dir on PA and PB.
+   5. Enable intr on PA.
+   
+Transmission.
+   1. rave_start_xmit is called by kernel to transmit.
+   2. Prepend header to packet.
+   3. Append packets to ch6 (extdma1) with eop last in packet.
+   4. tx_interrupt is called when transmission done.
+   5. Free buffers.
+
+Reception.
+   1. Reception on DMA5, extdma0.
+   2. FPGA interrupts on PA3 (RX_EOP), rave_rx_interrupt called.
+   3. We handle packet.
+   4. We ack on PB0 (RX_ACK).
+
+   When running, configure and check like this, for example:
+
+   cd /tmp;  chmod 777 xcv_boot;   chmod 777 arp;    ifconfig eth1 up;    ifconfig eth1 11.0.0.1 netmask 255.0.0.0 broadcast 11.255.255.255 hw ether 10:20:30:40:50:60;   route add -net 11.0.0.0 netmask 255.0.0.0 eth1;   ./arp -s 11.1.1.1 10:20:30:40:50:61;  
+
+ ping -c 1 11.1.1.1
+
+eth0 
+setenv IP 10.13.11.150
+setenv NETMASK 255.0.0.0
+setenv BROADCAST 10.255.255.255
+setenv NETWORK 10.0.0.0
+setenv GATEWAY 10.13.11.145
+
+*/
+
+#include "tx.h"
+#include "rx.h"
+#include "ser.h"
+#include "ioctl.h"
+#include "mib.h"
+
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/svinto.h>     /* DMA and register descriptions */
+#include <asm/io.h>         /* LED_* I/O functions */
+
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/types.h>	
+#include <linux/wireless.h>
+#include <linux/types.h>		/* For .config defines, checkint
+					   the checks below for how to
+					   config kernel */
+
+#if 0
+
+#ifdef CONFIG_ETRAX_DEBUG_PORT1
+#error CONFIG_ETRAX_DEBUG_PORT1 defined
+#endif
+
+#ifndef CONFIG_ETRAX_DEBUG_PORT2
+#error CONFIG_ETRAX_DEBUG_PORT2 not defined
+#endif
+
+#ifdef CONFIG_ETRAX_SERIAL_PORT0
+#error CONFIG_ETRAX_SERIAL_PORT0 defined
+#endif
+
+#endif
+
+/* Set to 0 if you want printable control messages. */
+#if 0
+
+#define START_MAC "\000"
+#define START_MAC_LEN 1
+
+#define STOP_MAC "\001"
+#define STOP_MAC_LEN 1
+
+#define SET_STATION_ADDR "\002"
+#define SET_STATION_ADDR_LEN 1
+
+#define MAC_OK "\000"
+#define MAC_OK_LEN 1
+
+#define SET_MULTICAST_LIST "\003"
+#define SET_MULTICAST_LIST_LEN 1
+
+/* Number of jiffies to wait for reply from MAC */
+#define SER1_TIMOUT_JIFFIES (5)
+
+#else
+
+#define SET_MULTICAST_LIST "SET_MULTICAST_LIST"
+#define SET_MULTICAST_LIST_LEN (strlen("SET_MULTICAST_LIST"))
+
+#define SET_STATION_ADDR "SET_STATION_ADDR"
+#define SET_STATION_ADDR_LEN (strlen("SET_STATION_ADDR"))
+
+#define START_MAC "START_MAC"
+#define START_MAC_LEN (strlen("START_MAC"))
+
+#define STOP_MAC "STOP_MAC"
+#define STOP_MAC_LEN (strlen("STOP_MAC"))
+
+#define MAC_OK "o"
+#define MAC_OK_LEN (strlen("o"))
+
+/* Number of jiffies to wait for reply from MAC */ 
+//#define SER1_TIMOUT_JIFFIES (5)
+#define SER1_TIMOUT_JIFFIES (0)
+
+#endif
+
+
+/* CSP4 */
+/* XXX: move also EXT_DMA_0 initializion to rx.c */
+#define EXT_DMA0_ADDR 0xa0000000
+
+/* A default ethernet address. Highlevel SW will set the real one later */
+
+static struct sockaddr default_mac = {
+	0,
+        { 0x10, 0x20, 0x30, 0x40, 0x50, 0x60 }
+};
+
+
+#define MAX_MEDIA_DATA_SIZE 1518
+
+#define MIN_PACKET_LEN      46
+
+extern etrax_dma_descr *myNextRxDesc;  /* Points to the next descriptor to
+                                          to be processed */
+
+#define NET_LINK_UP_CHECK_INTERVAL          200 /* 2 s   */
+
+/* Network speed indication. */
+#if 0
+static struct timer_list speed_timer;
+static struct timer_list clear_led_timer;
+static int current_speed;
+#endif /* 0 */
+
+int led_clear_time;
+int nolink;
+
+/* opened device */
+static struct net_device *opened_dev = NULL;
+
+/* Index to functions, as function prototypes. */
+
+static int	rave_close		(struct net_device *dev);
+static int	rave_open		(struct net_device *dev);
+static int	rave_set_mac_address	(struct net_device *dev, void *addr);
+static int __init rave_init		(struct net_device *dev);
+static struct net_device_stats *rave_get_stats(struct net_device *dev);
+#if 0
+static void	rave_check_speed	(unsigned long dummy);
+static void	rave_clear_network_leds	(unsigned long dummy);
+#endif /* 0 */
+static void	rave_set_multicast_list	(struct net_device *dev);
+
+#define tx_done(dev) (*R_DMA_CH0_CMD == 0)
+
+#define PA_VECTOR (11)
+#define DMA_CH_5_VECTOR (21)
+#define DMA_CH_6_VECTOR (22)
+#define EXT_DMA0_IRQ 21  /* DMA channel 5 in R_VECT_MASK */
+#define EXT_DMA1_IRQ 22  /* DMA channel 6 in R_VECT_MASK */
+
+#define DMA_IN_IRQ (EXT_DMA0_IRQ)
+#define DMA_OUT_IRQ (EXT_DMA1_IRQ)
+const int DMA_IN_CH=5;
+const int DMA_OUT_CH=6;
+
+
+
+/*
+ * Check for a network adaptor of this type, and return '0' if one exists.
+ * If dev->base_addr == 0, probe all likely locations.
+ * If dev->base_addr == 1, always return failure.
+ * If dev->base_addr == 2, allocate space for the device and return success
+ * (detachable devices only).
+ */
+
+/* init is called by kernel at startup */
+
+static int __init
+rave_init(struct net_device *dev)
+{
+        dprintk("\n\n######################################################################\n");
+	dprintk("> rave_init\n");
+	dprintk("%s\n\n", cardname);
+
+	dev->base_addr = (unsigned int)R_EXT_DMA_0_CMD; /* just to have something to show */
+
+	printk("%s initialized\n", dev->name);
+
+	/* make Linux aware of the new hardware  */
+
+	if (!dev) {
+		printk("dev == NULL. Should this happen?\n");
+		dev = init_etherdev(dev, sizeof(struct net_local));
+	}
+
+	/* setup generic handlers and stuff in the dev struct */
+
+	ether_setup(dev);
+
+	/* make room for the local structure containing stats etc */
+
+	dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+	if (dev->priv == NULL)
+		return -ENOMEM;
+	memset(dev->priv, 0, sizeof(struct net_local));
+
+	/* now setup our etrax specific stuff */
+
+	/* Receiver DMA */
+	dev->irq = DMA_IN_IRQ;
+	dev->dma = DMA_IN_CH;
+	
+	/* fill in our handlers so the network layer can talk to us in the future */
+
+	dev->open               = rave_open;
+	dev->hard_start_xmit    = rave_start_xmit;
+	dev->stop               = rave_close;
+	dev->get_stats          = rave_get_stats;
+        dev->do_ioctl           = rave_do_ioctl;
+	dev->set_multicast_list = rave_set_multicast_list;
+	dev->set_mac_address    = rave_set_mac_address;
+
+	ser1_init();
+	
+	/* set the default MAC address */
+	if(rave_set_mac_address(dev, &default_mac)) {
+	  return -ETIMEDOUT;
+	}
+
+        rave_rx_init();
+        rave_tx_init();
+        rave_mib_init();
+
+	/* Initialize speed indicator stuff. */
+	nolink = 0;
+#if 0
+	current_speed = 11;
+	speed_timer.expires = jiffies + NET_LINK_UP_CHECK_INTERVAL;
+	speed_timer.function = rave_check_speed;
+	add_timer(&speed_timer);
+	clear_led_timer.function = rave_clear_network_leds;
+	clear_led_timer.expires = jiffies + 10;
+	add_timer(&clear_led_timer);
+
+	printk("< rave_init\n");
+#endif
+	return 0;
+}
+
+/*
+ * Open/initialize the board. This is called (in the current kernel)
+ * sometime after booting when the 'ifconfig' program is run.
+ *
+ * This routine should set everything up anew at each open, even
+ * registers that "should" only need to be set once at boot, so that
+ * there is non-reboot way to recover if something goes wrong.
+ */
+
+static int
+rave_open(struct net_device *dev)
+{
+        unsigned long flags;
+
+	dprintk("> rave_open\n");
+
+	/* Stop the interface while we configure it */
+	*R_EXT_DMA_0_CMD = IO_STATE(R_EXT_DMA_0_CMD, run, stop);
+	*R_EXT_DMA_1_CMD = IO_STATE(R_EXT_DMA_1_CMD, run, stop);
+	
+	dprintk("1\n");
+	
+	//	dprintk("%8.8x %8.8x %8.8x\n", &rave_hdr.length,&rave_hdr.reserved,&rave_hdr.type);
+	/* Enable external DMA channels. */
+	genconfig_shadow &= ~(IO_MASK(R_GEN_CONFIG, dma5) | IO_MASK(R_GEN_CONFIG, dma6));
+	
+	*R_GEN_CONFIG = genconfig_shadow |= 
+	  (IO_STATE(R_GEN_CONFIG, dma5, extdma0) |
+	   IO_STATE(R_GEN_CONFIG, dma6, extdma1));
+
+	/* clear excessive_col, over/underrun irq mask */
+	/* clear dma0 and 1 eop and descr irq masks */
+
+	/* Reset and wait for the DMA channels */
+	RESET_DMA(5);
+	RESET_DMA(6);
+	WAIT_DMA(5);
+	WAIT_DMA(6);
+
+	*R_DMA_CH5_CLR_INTR = 
+	  (IO_STATE(R_DMA_CH5_CLR_INTR, clr_eop, do) |
+	   IO_STATE(R_DMA_CH5_CLR_INTR, clr_descr, do));
+	
+	*R_DMA_CH6_CLR_INTR = 
+	  (IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do) |
+	   IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do));
+	
+	dprintk("2\n");
+
+	/*
+	 * Allocate IRQ first, then DMA channels and clean up in
+	 * reverse order on failure.
+	 */
+
+
+	/* The following checks could be rolled into one if you want
+           to. If linux is correctly configured they should always
+           succeed. */
+
+	/* Port PA interrupt */
+	if (request_irq(PA_VECTOR, rave_rx_interrupt, 0, cardname, (void *)dev)) {
+	  printk("PA_VECTOR failed\n");
+	  goto request_fail;
+	}
+	
+	dprintk("3\n");
+
+	/* Transmission DMA, needs intr to see when transmission is done. */
+	if (request_irq(DMA_CH_6_VECTOR, rave_tx_interrupt, 0, cardname, (void *)dev)) {
+	  printk("DMA_CH_6_VECTOR failed\n");
+	  goto request_fail;
+	}
+	
+	if (request_dma(DMA_IN_CH, cardname)) {
+	  printk("DMA_IN_CH failed\n");
+	  goto request_fail;
+	}
+	
+	if (request_dma(DMA_OUT_CH, cardname)) {
+	  printk("DMA_OUT_CH failed\n");
+	  
+	request_fail:
+	  rave_close(dev);
+	  return -EACCES;
+	}
+
+	dprintk("4\n");
+
+	
+	/* All the register settings should use hwregs shadow
+           macros. Not my fault!!!! */
+	
+
+	/* Set RX_EOP pin (pa3) to input. */
+	*R_PORT_PA_DIR = port_pa_dir_shadow &= 
+	  ~(IO_STATE(R_PORT_PA_DIR, dir3, output));
+	
+	dprintk("R_PORT_PA_DIR %8.8x\n", port_pa_dir_shadow);
+
+	if (*R_IRQ_READ1 & IO_STATE(R_IRQ_READ1, pa3, active)) {
+	  printk("pa3 already set, cannot enable interrupts.\n");
+	  goto request_fail;
+	}
+
+	/* Enable pa3 interrupt. */
+	*R_IRQ_MASK1_SET = 
+	  IO_STATE(R_IRQ_MASK1_SET, pa3, set);
+	
+	/* Set direction on RX_ACK pin... */
+	*R_PORT_PB_DIR = port_pb_dir_shadow |= 
+	  IO_STATE(R_PORT_PB_DIR, dir0, output);
+
+	/* ... and value. */
+	*R_PORT_PB_DATA = port_pb_data_shadow &= 
+	  ~(1<<RX_ACK_PIN);
+	
+	save_flags(flags);
+	cli();
+
+	dprintk("5\n");
+
+	/* make sure the irqs are cleared */
+	
+	/* enable the irq's for DMA channels */
+
+	dprintk("enabling dma6_eop\n");
+	
+	*R_IRQ_MASK2_SET =
+	  IO_STATE(R_IRQ_MASK2_SET, dma6_eop, set);
+
+	dprintk("starting dma5\n");
+
+	/* start the receiving DMA channel so we can receive packets from now on */
+
+	*R_DMA_CH5_FIRST = virt_to_phys(myNextRxDesc);
+
+	*R_EXT_DMA_0_ADDR = EXT_DMA0_ADDR;
+  
+	*R_DMA_CH5_CMD = IO_STATE(R_DMA_CH5_CMD, cmd, start);
+
+	*R_EXT_DMA_0_CMD = (IO_STATE(R_EXT_DMA_0_CMD, cnt,      disable	) |
+			    IO_STATE(R_EXT_DMA_0_CMD, rqpol,    ahigh	) |
+			    IO_STATE(R_EXT_DMA_0_CMD, apol,     ahigh	) |
+			    IO_STATE(R_EXT_DMA_0_CMD, rq_ack,   handsh	) |
+			    IO_STATE(R_EXT_DMA_0_CMD, wid,      dword	) |
+			    IO_STATE(R_EXT_DMA_0_CMD, dir,      input	) | 
+			    IO_STATE(R_EXT_DMA_0_CMD, run,      stop	) |
+			    IO_FIELD(R_EXT_DMA_0_CMD, trf_count,0	));
+	
+	*R_EXT_DMA_0_CMD = (IO_STATE(R_EXT_DMA_0_CMD, cnt,      disable	) |
+			    IO_STATE(R_EXT_DMA_0_CMD, rqpol,    ahigh	) |
+			    IO_STATE(R_EXT_DMA_0_CMD, apol,     ahigh	) |
+			    IO_STATE(R_EXT_DMA_0_CMD, rq_ack,   handsh	) |
+			    IO_STATE(R_EXT_DMA_0_CMD, wid,      dword	) |
+			    IO_STATE(R_EXT_DMA_0_CMD, dir,      input	) | 
+			    IO_STATE(R_EXT_DMA_0_CMD, run,      start	) |
+			    IO_FIELD(R_EXT_DMA_0_CMD, trf_count,0	));
+
+	restore_flags(flags);
+	
+	ser1_send(START_MAC, START_MAC_LEN);
+	if (ser1_expect(MAC_OK, MAC_OK_LEN, SER1_TIMOUT_JIFFIES)) {
+	  printk("START_MAC failed!\n");
+	  rave_close(dev);
+	  return(-EACCES);
+	}
+	else {
+	  /* We are now ready to accept transmit requests from
+	   * the queueing layer of the networking.
+	   */
+	  netif_start_queue(dev);
+	}
+        
+        rave_rx_open();
+        rave_tx_open(dev);
+        opened_dev = dev;
+	
+	dprintk("< rave_open\n");
+	return 0;
+}
+
+/* 
+   The inverse routine to net_open(). 
+   No point in changing the channels in R_GEN_CONFIG since
+   there is no 'unused' to set them to. 
+   */
+
+static int
+rave_close(struct net_device *dev)
+{
+	struct net_local *np = (struct net_local *)dev->priv;
+
+        opened_dev = NULL;
+	dprintk("Closing %s.\n", dev->name);
+
+	/* Stop all transmissions. */
+	ser1_send(STOP_MAC, STOP_MAC_LEN);
+	if (ser1_expect(MAC_OK, MAC_OK_LEN, SER1_TIMOUT_JIFFIES)) {
+	  printk("STOP_MAC failed!\n");
+	}
+
+#ifndef ETHDEBUG
+	/* Don't do this if we use the debug port. */
+	ser1_close();
+#endif
+
+	*R_EXT_DMA_0_CMD = IO_STATE(R_EXT_DMA_0_CMD, run, stop);
+	*R_EXT_DMA_1_CMD = IO_STATE(R_EXT_DMA_1_CMD, run, stop);
+
+	netif_stop_queue(dev);
+
+	RESET_DMA(5);
+	RESET_DMA(6);
+
+	/* Clear interrupts and masks */
+	*R_DMA_CH6_CLR_INTR = 
+	  IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
+
+	*R_IRQ_MASK1_CLR = 
+	  IO_STATE(R_IRQ_MASK1_CLR, pa3, clr);
+
+	*R_IRQ_MASK2_CLR =
+	  IO_STATE(R_IRQ_MASK2_CLR, dma6_eop, clr);
+
+	/* Free DMA channels */
+	free_dma(DMA_IN_CH);
+	free_dma(DMA_OUT_CH);
+
+	/* This may cause some 'trying to free free irq' but no harm done... */
+	free_irq(PA_VECTOR, (void *)dev);
+	free_irq(DMA_CH_6_VECTOR, (void *)dev);
+
+	/* Update the statistics */
+	rave_update_rx_stats(&np->stats);
+	rave_update_tx_stats(&np->stats);
+
+        rave_tx_close();
+	return 0;
+}
+
+/* Not used */
+
+#if 0
+static 
+void rave_check_speed(unsigned long dummy)
+{
+  dprintk("> rave_check_speed\n");
+}
+#endif /* 0 */
+
+/* set MAC address of the interface. called from the core after a
+ * SIOCSIFADDR ioctl, and from the bootup above.
+ */
+
+static int
+rave_set_mac_address(struct net_device *dev, void *addr)
+{
+        /* Due to MAC address dependent states in 802.11 MAC we can't change
+         * MAC address online but must restart the MAC. For now, we just 
+         * consider the operation not being permitted online. */
+        if (opened_dev != NULL) {
+                return -EPERM; 
+        }
+        /* XXX: If "open" isn't yet called, we should save the MAC address
+         *      until it is, and then forward it in start command to slave */
+        return 0;
+}
+
+/*
+ * Set or clear the multicast filter for this adaptor.
+ * num_addrs == -1	Promiscuous mode, receive all packets
+ * num_addrs == 0	Normal mode, clear multicast list
+ * num_addrs > 0	Multicast mode, receive normal and MC packets,
+ *			and do best-effort filtering.
+ */
+
+static void
+rave_set_multicast_list(struct net_device *dev)
+{
+        rave_mib_request(CTRLMSG_SET_REQUEST, 
+                         ATTRID_XXdot11AddressTable_WO_MACLIST,
+                         dev,
+                         NULL);
+}
+
+/*
+ * Get the current statistics.
+ * This may be called with the card open or closed.
+ */
+static struct net_device_stats *
+rave_get_stats(struct net_device *dev)
+{
+	struct net_local *lp = (struct net_local *)dev->priv;
+
+	printk("> rave_get_stats\n");
+
+	rave_update_rx_stats(&lp->stats);
+	rave_update_tx_stats(&lp->stats);
+
+	return &lp->stats;
+}
+
+#if 0
+static void
+rave_clear_network_leds(unsigned long dummy)
+{
+
+	printk("> rave_clear_network_leds\n");
+
+	if (jiffies > led_clear_time) {
+		if (nolink)
+		  ;//LED_NETWORK_TX_SET(1);
+		else
+		  ;//LED_NETWORK_TX_SET(0);
+		//LED_NETWORK_RX_SET(0); 
+	}
+	
+	clear_led_timer.expires = jiffies + 10;
+	add_timer(&clear_led_timer);
+}
+#endif /* 0 */
+
+static struct net_device dev_etrax_rave;  /* only got one */
+
+void rave_failure(void)
+{
+/* XXX: What to do here? */
+}
+
+static int
+etrax_init_module(void)
+{
+	struct net_device *d = &dev_etrax_rave;
+
+	dprintk("> etrax_init_module\n");
+	d->init = rave_init;
+
+	if(register_netdev(d) == 0)
+		return 0;
+	else
+		return -ENODEV;
+}
+
+module_init(etrax_init_module);
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/rave_global.c ./arch/cris/drivers/802_11/rave_global.c
--- /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/rave_global.c	1970-01-01 01:00:00.000000000 +0100
+++ ./arch/cris/drivers/802_11/rave_global.c	2003-04-29 16:16:42.000000000 +0200
@@ -0,0 +1,56 @@
+#include "rave_global.h"
+#include <asm/io.h>
+
+void 
+rave_print_descr(char* buf_p) 
+{
+  struct dma_descr {
+    unsigned short sw_len;      /* 0-1 */
+    unsigned short ctrl;        /* 2-3 */
+    unsigned int next;          /* 4-7 */
+    unsigned int buf;           /* 8-11 */
+    unsigned short  hw_len;     /* 12-13 */
+    unsigned short  status;     /* 14-15 */
+  } *dma_descr;
+    
+  printk("DMA-descriptor at: 0x%x\r\n", (unsigned)buf_p);
+  
+  dma_descr = (struct dma_descr*) buf_p;
+
+  printk("ctrl   : 0x%x : ", dma_descr->ctrl);
+  if (dma_descr->ctrl & 1)
+    printk("eol ");
+  if (dma_descr->ctrl & 2)
+    printk("eop ");
+  if (dma_descr->ctrl & 4)
+    printk("wait ");
+  if (dma_descr->ctrl & 8)
+    printk("intr ");
+  if (dma_descr->ctrl & 16)
+    printk("ecp ");
+  if (dma_descr->ctrl & 32)
+    printk("pri ");
+  printk("\r\n");
+  
+  printk("sw_len : 0x%x\r\n", dma_descr->sw_len);
+  printk("next   : 0x%8.8x\r\n", (unsigned)phys_to_virt(dma_descr->next));
+  printk("buf    : 0x%8.8x\r\n", (unsigned)phys_to_virt(dma_descr->buf));
+
+  printk("status : 0x%x : ", dma_descr->status);
+  if (dma_descr->status & 1)
+    printk("res ");
+  if (dma_descr->status & 2)
+    printk("eop ");
+  if (dma_descr->status & 16)
+    printk("stop ");
+  if (dma_descr->status & 32)
+    printk("priority ");
+  if (dma_descr->status & 64)
+    printk("align_err ");
+  if (dma_descr->status & 128)
+    printk("crc_err ");
+  printk("\r\n");
+
+  printk("hw_len : 0x%x\r\n", dma_descr->hw_len);
+
+}
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/rave_global.h ./arch/cris/drivers/802_11/rave_global.h
--- /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/rave_global.h	1970-01-01 01:00:00.000000000 +0100
+++ ./arch/cris/drivers/802_11/rave_global.h	2003-04-29 16:16:42.000000000 +0200
@@ -0,0 +1,70 @@
+/*
+ * Global macros, typedefs and constants.
+ */
+
+#ifndef RAVE_GLOBAL_H
+#define RAVE_GLOBAL_H
+
+#include <linux/netdevice.h>
+#include <stddef.h> /* for NULL only */
+
+/* Guess what you can do with this? */
+#define ETHDEBUG 1
+
+#ifdef ETHDEBUG
+#define dprintk printk
+#else
+#define dprintk
+#endif
+
+extern void rave_failure(void);
+
+#define rave_assert(x) \
+if (!(x)) { \
+ printk("RAVE: assertion (" #x ") failed at " __FILE__ "(%d):" \
+                   __FUNCTION__ "\n", __LINE__); \
+ rave_failure(); \
+}
+
+typedef char bool;
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#ifndef TRUE
+#define TRUE  1
+#endif
+
+/* Type of packet from LLC */
+typedef enum llc_type {
+  LLC_TYPE_DATA = 0,
+  LLC_TYPE_MIB,
+  LLC_TYPE_INVALID
+} llc_type;
+
+/* Interface logic header */
+typedef struct rave_header {
+  unsigned short length;
+  unsigned char  reserved;
+  unsigned char  type;
+} __attribute__((packed)) rave_header;
+
+/* Information that need to be kept for each board. */
+typedef struct net_local {
+  struct net_device_stats stats;
+
+  /* Tx control lock.  This protects the transmit buffer ring
+   * state along with the "tx full" state of the driver.  This
+   * means all netif_queue flow control actions are protected
+   * by this lock as well.
+   */
+  spinlock_t lock;
+} net_local;
+
+
+#define RX_ACK_PIN (0)
+
+void rave_print_descr(char* buf_p);
+
+#endif /* RAVE_GLOBAL_H */
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/rave_wireless_h.patch ./arch/cris/drivers/802_11/rave_wireless_h.patch
--- /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/rave_wireless_h.patch	1970-01-01 01:00:00.000000000 +0100
+++ ./arch/cris/drivers/802_11/rave_wireless_h.patch	2001-10-17 17:11:05.000000000 +0200
@@ -0,0 +1,51 @@
+Index: wireless.h
+===================================================================
+RCS file: /n/cvsroot/os/linux/include/linux/wireless.h,v
+retrieving revision 1.3
+diff -u -p -r1.3 wireless.h
+--- wireless.h	2001/05/08 14:37:22	1.3
++++ wireless.h	2001/08/09 13:29:28
+@@ -507,4 +507,43 @@ struct	iw_priv_args
+ 	char		name[IFNAMSIZ];	/* Name of the extension */
+ };
+ 
++
++/* -------------------------- MIB counters -------------------------- */
++
++/*
++ *	Below struct is supposed to be declared in user space when 802.11
++ *	statistics is to be received via ioctl, i.e.:
++ *      
++ *      iwreq req;
++ *      dot11Counters counters;
++ *      inf fd;
++ *      int res;
++ *
++ *      req.union.data
++ *      iwr.u.data.pointer = (caddr_t) &counters;
++ *      fd = ... // open device
++ *      res = ioctl(fd, SIOCGIWCOUNT, (struct ifreq *) &iwr);
++ */
++
++typedef __u32 dot11_counter;
++
++/* 802.11 counters */
++typedef struct dot11Counters
++{
++  dot11_counter dot11TransmittedFragmentCount;
++  dot11_counter dot11MulticastTransmittedFrameCount;
++  dot11_counter dot11FailedCount;
++/*  dot11_counter dot11RetryCount;         -- not supported yet */
++/*  dot11_counter dot11MultipleRetryCount; -- not supported yet */
++  dot11_counter dot11FrameDuplicateCount;
++  dot11_counter dot11RTSSuccessCount;
++  dot11_counter dot11RTSFailureCount;
++  dot11_counter dot11ACKFailureCount;
++  dot11_counter dot11ReceivedFragmentCount;
++  dot11_counter dot11MulticastReceivedFrameCount;
++  dot11_counter dot11FCSErrorCount;
++  dot11_counter dot11TransmittedFrameCount;
++  dot11_counter dot11WEPUndecryptableCount;
++} dot11Counters;
++
+ #endif	/* _LINUX_WIRELESS_H */
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/rx.c ./arch/cris/drivers/802_11/rx.c
--- /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/rx.c	1970-01-01 01:00:00.000000000 +0100
+++ ./arch/cris/drivers/802_11/rx.c	2003-04-29 16:16:42.000000000 +0200
@@ -0,0 +1,316 @@
+#include "rx.h"
+#include "mib.h"
+#include <asm/io.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#define RX_BUF_SIZE 32768
+/* RX_DESC_BUF_SIZE should be a multiple of four to avoid the buffer
+ * alignment bug in Etrax 100 release 1
+ */
+#define RX_DESC_BUF_SIZE   256
+#define NBR_OF_RX_DESC     (RX_BUF_SIZE / \
+			    RX_DESC_BUF_SIZE)
+
+/* Network flash constants */
+#define NET_FLASH_TIME      2 /* 20 ms */
+
+#define ETHER_HEAD_LEN      14
+
+static etrax_dma_descr RxDescList[NBR_OF_RX_DESC];
+unsigned char RxBuf[RX_BUF_SIZE];
+etrax_dma_descr *myNextRxDesc;  /* Points to the next descriptor to
+                                 * to be processed */
+static etrax_dma_descr *myLastRxDesc;  /* The last processed descriptor */
+static etrax_dma_descr *myPrevRxDesc;  /* The descriptor right before 
+                                        * myNextRxDesc */
+/* Network speed indication. */
+extern int led_clear_time;
+extern int nolink;
+
+void rave_rx_init(void)
+{
+	int i;
+	int anOffset;
+	unsigned char *buf;
+	
+	/* Initialise receive descriptors */
+	anOffset = 0;
+	for(i = 0; i < NBR_OF_RX_DESC; i++) {
+		RxDescList[i].ctrl   = 0;
+		RxDescList[i].sw_len = RX_DESC_BUF_SIZE;
+		RxDescList[i].next   = virt_to_phys(&RxDescList[i + 1]);
+		RxDescList[i].buf    = virt_to_phys(RxBuf + anOffset);
+		RxDescList[i].status = 0;
+		RxDescList[i].hw_len = 0;
+		anOffset += RX_DESC_BUF_SIZE;
+	}
+	
+	/* wrap the last one */
+	i--;
+	RxDescList[i].ctrl   = d_eol;
+	RxDescList[i].next   = virt_to_phys(&RxDescList[0]);
+
+	memset(RxBuf, 0, RX_BUF_SIZE);
+  
+	for(i=0; i!= RX_BUF_SIZE; i++) {
+		RxBuf[i] = 0;
+	}
+
+	buf = RxBuf;
+	
+	dprintk("%2.2x %2.2x %2.2x %2.2x %2.2x %2.2x > %2.2x %2.2x %2.2x %2.2x %2.2x %2.2x\n", 
+		buf[6],buf[7],buf[8],buf[9],buf[10],buf[11],
+		buf[0],buf[1],buf[2],buf[3],buf[4],buf[5]);
+	
+	for(i=0; i!= 500; i++) {
+		dprintk("%2.2x ", buf[i]);
+	}
+	
+	dprintk("\n");
+
+	/* Initialize initial pointers */
+	myNextRxDesc = &RxDescList[0];
+	myLastRxDesc = &RxDescList[NBR_OF_RX_DESC - 1];
+	myPrevRxDesc = &RxDescList[NBR_OF_RX_DESC - 1];
+}
+
+void 
+rave_rx_open(void)
+{
+#ifdef ETHDEBUG
+	memset(RxBuf, 0, RX_BUF_SIZE); 
+#endif
+}
+
+
+/*
+  Called by PORT_PA interrupt.
+  Stop the external DMA, acknowledge on PORT_PB, send packet up higher.
+  RXI.
+*/
+void
+rave_rx_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+	struct net_device *dev = (struct net_device *)dev_id;
+	unsigned long irqbits = *R_IRQ_MASK1_RD;
+	
+	dprintk("> rave_rx_interrupt\n");
+	
+#if 0
+	{
+		int i;
+		
+		for(i=0; i != NBR_OF_RX_DESC; i++) {
+			print_descr((char*)&RxDescList[i]);
+		}
+	}
+#endif
+	
+	printk("\n\nR_DMA_CH5_HWSW %8.8x\n", *R_DMA_CH5_HWSW);
+	printk("R_DMA_CH5_FIRST %8.8x\n",    *R_DMA_CH5_FIRST);
+	printk("R_DMA_CH5_FIRST(v) %p\n", phys_to_virt(*R_DMA_CH5_FIRST));
+	printk("R_DMA_CH5_DESCR %8.8x\n",    *R_DMA_CH5_DESCR);
+	printk("myNextRxDesc %p\n",       myNextRxDesc);
+	
+	if (irqbits & IO_STATE(R_IRQ_MASK1_RD, pa3, active)) {
+		dprintk(" pa3 is set\n");
+		
+		/* stop external DMA, automatically sets eop in internal 
+		 * channel  */
+		*R_EXT_DMA_0_CMD = (IO_STATE(R_EXT_DMA_0_CMD, cnt,disable ) |
+			IO_STATE(R_EXT_DMA_0_CMD, rqpol,     ahigh        ) |
+			IO_STATE(R_EXT_DMA_0_CMD, apol,      ahigh        ) |
+			IO_STATE(R_EXT_DMA_0_CMD, rq_ack,    handsh       ) |
+			IO_STATE(R_EXT_DMA_0_CMD, wid,       dword        ) |
+			IO_STATE(R_EXT_DMA_0_CMD, dir,       input        ) | 
+			IO_STATE(R_EXT_DMA_0_CMD, run,       stop         ) |
+			IO_FIELD(R_EXT_DMA_0_CMD, trf_count, 0));
+
+		/* Wait for eop. Could do this with eop interrupt, but this 
+		 * will not take long, just waiting for the FIFO. */
+		while(!(*R_IRQ_READ2 & IO_STATE(R_IRQ_READ2, dma5_eop, active))){};
+		
+		/* check if one or more complete packets were indeed 
+		 * received */
+		
+		while(*R_DMA_CH5_FIRST != virt_to_phys(myNextRxDesc)) {
+			/* Take out the buffer and give it to the OS. */
+			rave_rx(dev);
+			((struct net_local *)dev->priv)->stats.rx_packets++;
+			/* restart/continue on internal channel  */
+			*R_DMA_CH5_CMD = IO_STATE(R_DMA_CH5_CMD, cmd, restart);
+			/* now, we might have gotten another packet so we 
+			 * have to loop back and check if so */
+		}
+		
+		dprintk(" setting rx_ack pin\n");
+		/* acknowledge the packet */
+		*R_PORT_PB_DATA = port_pb_data_shadow | 
+			1<<RX_ACK_PIN;
+		
+		dprintk(" wait for pa3 low\n");
+		/* Wait for external unit clearing interrupt. */
+		while (*R_IRQ_MASK1_RD & 
+		       IO_STATE(R_IRQ_MASK1_RD, pa3,active)) {}
+		
+		dprintk(" clearing rx_ack\n");
+		/* restore port pb */
+		*R_PORT_PB_DATA = port_pb_data_shadow;
+		
+		/* You can use trf_count here to test reception. */
+		*R_EXT_DMA_0_CMD = 
+			(IO_STATE(R_EXT_DMA_0_CMD, cnt,disable ) |
+			 IO_STATE(R_EXT_DMA_0_CMD, rqpol,ahigh ) |
+			 IO_STATE(R_EXT_DMA_0_CMD, apol,      ahigh        ) |
+			 IO_STATE(R_EXT_DMA_0_CMD, rq_ack,    handsh       ) |
+			 IO_STATE(R_EXT_DMA_0_CMD, wid,       dword        ) |
+			 IO_STATE(R_EXT_DMA_0_CMD, dir,       input        ) | 
+			 IO_STATE(R_EXT_DMA_0_CMD, run,       start        ) |
+			 IO_FIELD(R_EXT_DMA_0_CMD, trf_count, 0));
+	}
+	else {
+		printk("  rave_rx_interrupt bogus call!!\n");
+	}
+	
+	dprintk("< rave_rx_interrupt\n\n");
+	
+}
+/* XIT */
+
+/* We have a good packet(s), get it/them out of the buffers. External
+   channel is stopped while in this routine, so we can clear eop etc
+   without troubles.*/
+void
+rave_rx(struct net_device *dev)
+{
+	struct sk_buff *skb;
+	int length=0;
+	struct etrax_dma_descr *mySaveRxDesc = myNextRxDesc;
+	struct etrax_dma_descr *firstDesc = myNextRxDesc;
+	unsigned char *skb_data_ptr;
+	rave_header *rave_hdr;
+	
+	printk("> rave_rx\n");
+	
+	/* blinkenlights! */
+	if (!nolink) {
+		//	  LED_NETWORK_RX_SET(1);
+		/* Set the earliest time we may clear the LED */
+		led_clear_time = jiffies + NET_FLASH_TIME;
+	}
+	
+	/* If the packet is broken down in many small packages then
+	 * merge count how much space we will need to alloc with
+	 * skb_alloc() for it to fit. 
+	 */
+	while (!(myNextRxDesc->status & d_eop)) {
+		length += myNextRxDesc->sw_len; 
+		//	  myNextRxDesc->status = 0; 
+		myNextRxDesc = phys_to_virt(myNextRxDesc->next);
+	}
+	length += myNextRxDesc->hw_len; /* use hw_len for the last descr */
+	
+	{
+		rave_hdr = (struct rave_header*)phys_to_virt(firstDesc->buf);
+		
+		printk(" Got header (%p): length %u, reserved %u, type %u\n", 
+		       rave_hdr, 
+		       rave_hdr->length, 
+		       rave_hdr->reserved, 
+		       rave_hdr->type);
+	}
+	
+	/* Skip the rave header, restore it later. */
+	firstDesc->buf += sizeof(rave_header);
+	length -= sizeof(rave_header);
+	
+#ifdef ETHDEBUG
+	/* dump the packet */
+	skb_data_ptr = (unsigned char *)phys_to_virt(mySaveRxDesc->buf);
+	printk("Got a packet of length %d at 0%p:\n", length, 
+	       skb_data_ptr);
+	{
+	  int i;
+
+	  for(i=0; i!= length; i++) {
+	    if (i%8 == 0) {
+	      printk("\n");
+	    }
+	    printk("%2.2x ", skb_data_ptr[i]);
+	  }
+	  printk("\n");
+	}
+#endif
+
+	skb = dev_alloc_skb(length - ETHER_HEAD_LEN);
+	if (!skb) {
+		printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n",
+		       dev->name);
+		return;
+	}
+
+	skb_put(skb, length - ETHER_HEAD_LEN);        /* allocate room for the packet body */
+	skb_data_ptr = skb_push(skb, ETHER_HEAD_LEN); /* allocate room for the header */
+
+#if 0
+	printk("head = 0x%x, data = 0x%x, tail = 0x%x, end = 0x%x\n",
+	       skb->head, skb->data, skb->tail, skb->end);
+	printk("copying packet to 0x%x.\n", skb_data_ptr);
+#endif
+
+	/* this loop can be made using max two memcpy's if optimized */
+	while(mySaveRxDesc != myNextRxDesc) {
+		memcpy(skb_data_ptr, phys_to_virt(mySaveRxDesc->buf),
+		       mySaveRxDesc->sw_len);
+		skb_data_ptr += mySaveRxDesc->sw_len;
+		mySaveRxDesc = phys_to_virt(mySaveRxDesc->next);
+	}
+
+	memcpy(skb_data_ptr, phys_to_virt(mySaveRxDesc->buf),
+	       mySaveRxDesc->hw_len);
+
+	skb->dev = dev;
+	skb->protocol = eth_type_trans(skb, dev);
+
+	/* Demultiplex packet by type */
+        switch (rave_hdr->type) {
+        case LLC_TYPE_DATA:
+                netif_rx(skb);
+                break;
+        case LLC_TYPE_MIB:
+                rave_mib_response(skb);
+                break;
+        default:
+                /* invalid type, drop */
+        }
+
+	/* Restore first buffer pointer */
+	firstDesc->buf -= sizeof(rave_header);
+
+	/* Prepare for next packet */
+	myNextRxDesc->status = 0;
+	myNextRxDesc->hw_len = 0; /* Not really used. */
+	myPrevRxDesc = myNextRxDesc;
+	myNextRxDesc = phys_to_virt(myNextRxDesc->next);
+
+	myPrevRxDesc->ctrl |= d_eol;
+	myLastRxDesc->ctrl &= ~d_eol;
+	myLastRxDesc = myPrevRxDesc;
+
+	printk("< rave_rx\n");
+	return;
+}
+
+void
+rave_update_rx_stats(struct net_device_stats *es)
+{
+	unsigned long r = *R_REC_COUNTERS;
+	
+	printk("> update_rx_stats\n");
+	/* update stats relevant to reception errors */
+	es->rx_fifo_errors += r >> 24;            /* fifo overrun */
+	es->rx_crc_errors += r & 0xff;            /* crc error */
+	es->rx_frame_errors += (r >> 8) & 0xff;   /* alignment error */
+	es->rx_length_errors += (r >> 16) & 0xff; /* oversized frames */
+}
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/rx.h ./arch/cris/drivers/802_11/rx.h
--- /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/rx.h	1970-01-01 01:00:00.000000000 +0100
+++ ./arch/cris/drivers/802_11/rx.h	2003-04-29 16:16:43.000000000 +0200
@@ -0,0 +1,20 @@
+/*
+ * Rx Interface to the 802.11 driver
+ */
+
+#ifndef RX_H
+#define RX_H
+
+#include "rave_global.h"
+
+/* Initiate rx interface */
+void rave_rx_init(void);
+/* Open itnerface */
+void rave_rx_open(void);
+/* Rx interrupt handler */
+void rave_rx_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+/* Receive rx data */
+void rave_rx(struct net_device *dev);
+void rave_update_rx_stats(struct net_device_stats *es);
+
+#endif /* RX_H */
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/ser.c ./arch/cris/drivers/802_11/ser.c
--- /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/ser.c	1970-01-01 01:00:00.000000000 +0100
+++ ./arch/cris/drivers/802_11/ser.c	2003-04-29 16:16:43.000000000 +0200
@@ -0,0 +1,156 @@
+#include "ser.h"
+#include "rave_global.h"
+
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <asm/io.h>         /* LED_* I/O functions */
+#include <linux/types.h>    /* For .config defines, check the checks below 
+                             * for how to config kernel */
+
+/* Define this if you want slave communication over ser2 instead of
+   ser1. You also have to change the macros in the ser1_*()
+   functions. */
+#define RAVE_SER2 1
+
+#if 0
+
+#ifdef CONFIG_ETRAX_SERIAL_PORT0
+#error CONFIG_ETRAX_SERIAL_PORT0 defined
+#endif
+
+#endif /* 0 */
+
+void
+ser1_init()
+{
+
+  *R_SERIAL2_BAUD = 
+    (IO_STATE(R_SERIAL2_BAUD,	tr_baud,            c115k2Hz) |
+     IO_STATE(R_SERIAL2_BAUD,	rec_baud,           c115k2Hz));
+  
+  *R_SERIAL2_REC_CTRL = 
+    (IO_STATE(R_SERIAL2_REC_CTRL,	dma_err,            stop) |
+     IO_STATE(R_SERIAL2_REC_CTRL,	rec_enable,         enable) | 
+     IO_STATE(R_SERIAL2_REC_CTRL,	rts_,               active) |
+     IO_STATE(R_SERIAL2_REC_CTRL,	sampling,           middle) |
+     IO_STATE(R_SERIAL2_REC_CTRL,	rec_stick_par,      normal) |
+     IO_STATE(R_SERIAL2_REC_CTRL,	rec_par,            even) |
+     IO_STATE(R_SERIAL2_REC_CTRL,	rec_par_en,         disable) |
+     IO_STATE(R_SERIAL2_REC_CTRL,	rec_bitnr,          rec_8bit));
+  
+  *R_SERIAL2_TR_CTRL = 
+    (IO_FIELD(R_SERIAL2_TR_CTRL,	txd,                0) |
+     IO_STATE(R_SERIAL2_TR_CTRL,	tr_enable,          enable) | 
+     IO_STATE(R_SERIAL2_TR_CTRL,	auto_cts,           disabled) |
+     IO_STATE(R_SERIAL2_TR_CTRL,	stop_bits,          one_bit) |
+     IO_STATE(R_SERIAL2_TR_CTRL,	tr_stick_par,       normal) |
+     IO_STATE(R_SERIAL2_TR_CTRL,	tr_par,             even) |
+     IO_STATE(R_SERIAL2_TR_CTRL,	tr_par_en,          disable) |
+     IO_STATE(R_SERIAL2_TR_CTRL,	tr_bitnr,           tr_8bit));
+     
+}
+
+void
+ser1_close()
+{
+
+#if 0
+  while(*R_SERIAL2_STATUS & IO_STATE(R_SERIAL2_STATUS, data_avail, yes));
+  
+  *R_SERIAL2_REC_CTRL = 
+    (IO_STATE(R_SERIAL2_REC_CTRL,	dma_err,            stop) |
+     IO_STATE(R_SERIAL2_REC_CTRL,	rec_enable,         disable) | /* this */
+     IO_STATE(R_SERIAL2_REC_CTRL,	rts_,               active) |
+     IO_STATE(R_SERIAL2_REC_CTRL,	sampling,           middle) |
+     IO_STATE(R_SERIAL2_REC_CTRL,	rec_stick_par,      normal) |
+     IO_STATE(R_SERIAL2_REC_CTRL,	rec_par,            even) |
+     IO_STATE(R_SERIAL2_REC_CTRL,	rec_par_en,         disable) |
+     IO_STATE(R_SERIAL2_REC_CTRL,	rec_bitnr,          rec_8bit));
+  
+  *R_SERIAL2_TR_CTRL = 
+    (IO_FIELD(R_SERIAL2_TR_CTRL,	txd,                0) |
+     IO_STATE(R_SERIAL2_TR_CTRL,	tr_enable,          disable) | /* and this */
+     IO_STATE(R_SERIAL2_TR_CTRL,	auto_cts,           disabled) |
+     IO_STATE(R_SERIAL2_TR_CTRL,	stop_bits,          one_bit) |
+     IO_STATE(R_SERIAL2_TR_CTRL,	tr_stick_par,       normal) |
+     IO_STATE(R_SERIAL2_TR_CTRL,	tr_par,             even) |
+     IO_STATE(R_SERIAL2_TR_CTRL,	tr_par_en,          disable) |
+     IO_STATE(R_SERIAL2_TR_CTRL,	tr_bitnr,           tr_8bit));
+     
+#endif 
+}
+
+/* Send a string on serial port. Turn off dma during
+   transmission. This so we don't have to bother about the linux
+   setup. And so we during debugging can use the normal debug
+   port... */
+
+void 
+ser1_send(char* buf, int len)
+{
+  int i;
+
+#if RAVE_SER2
+  *R_GEN_CONFIG = genconfig_shadow |
+    (IO_STATE(R_GEN_CONFIG, dma3, ata) |
+     IO_STATE(R_GEN_CONFIG, dma2, ata));
+#endif
+
+  for(i=0; i!=len; i++) {
+    while (!(*R_SERIAL2_STATUS & IO_STATE(R_SERIAL2_STATUS, tr_ready, ready))){
+      ;
+    }
+    
+    *R_SERIAL2_TR_DATA = buf[i];
+  }
+
+#if RAVE_SER2
+  /* Just to make sure we wait longer than 120ns between R_GEN_CONFIG
+     writes... */
+  __asm__ ("nop\n\tnop\n\tnop\n\tnop\n\tnop\n\tnop");
+
+  *R_GEN_CONFIG = genconfig_shadow;
+#endif
+
+}
+
+/* Wait for a string on serial port. Waits a max number of jiffies.
+Returns 0 on success */
+
+int
+ser1_expect(char *needle, unsigned int len, unsigned int max_jiffies)
+{
+  int i;
+  unsigned int start_jiffies = jiffies;
+  
+  dprintk("> ser1_expect\n");
+
+#if RAVE_SER2
+  *R_GEN_CONFIG = genconfig_shadow |
+    (IO_STATE(R_GEN_CONFIG, dma3, ata) |
+     IO_STATE(R_GEN_CONFIG, dma2, ata));
+#endif
+
+  for(i=0; i!=len;) {
+    if (*R_SERIAL2_STATUS & IO_STATE(R_SERIAL2_STATUS, data_avail, yes)) {
+      if ( needle[i] != *R_SERIAL2_REC_DATA) {
+	break;
+      }
+      i++;
+    }
+    
+    if ((jiffies-start_jiffies) > max_jiffies) {
+      break;
+    }
+  }
+
+#if RAVE_SER2  
+  *R_GEN_CONFIG = genconfig_shadow;
+#endif
+
+  i = len;			/* always true */
+
+  dprintk("< %s\n", i == len ? "ok" : "fail");
+  return i == len ? 0 : -1;
+
+}
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/ser.h ./arch/cris/drivers/802_11/ser.h
--- /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/ser.h	1970-01-01 01:00:00.000000000 +0100
+++ ./arch/cris/drivers/802_11/ser.h	2003-04-29 16:16:43.000000000 +0200
@@ -0,0 +1,13 @@
+/*
+ * Serial interface to the 802.11 driver
+ */
+
+#ifndef SER_H
+#define SER_H
+
+void ser1_init   (void);
+void ser1_close  (void);
+void ser1_send   (char* buf, int len);
+int  ser1_expect (char *needle, unsigned int len, unsigned int max_jiffies);
+
+#endif
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/tx.c ./arch/cris/drivers/802_11/tx.c
--- /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/tx.c	1970-01-01 01:00:00.000000000 +0100
+++ ./arch/cris/drivers/802_11/tx.c	2003-04-29 16:16:43.000000000 +0200
@@ -0,0 +1,390 @@
+#include "tx.h"
+#include <asm/io.h>
+#include <linux/netdevice.h>
+
+/* CSP0 */
+#define EXT_DMA1_ADDR 0x90000000
+
+/* pending transmission */
+typedef struct tx_pending {
+        struct sk_buff *skb;
+        llc_type type;
+} tx_pending;
+
+/* waiting transmission */
+typedef struct tx_waiting {
+        struct sk_buff *skb;
+        unsigned int len;
+        struct net_device *dev;
+        llc_type type;
+} tx_waiting;
+
+/* Tx DMA busy constants */
+typedef enum tx_status{
+        TX_SENT, /* packet sent */
+        TX_BUSY  /* busy, packet queued */
+} tx_status;
+
+static etrax_dma_descr RaveHdrDesc;
+static etrax_dma_descr TxDesc;
+static rave_header rave_hdr;
+
+/* for debug only */
+extern unsigned char RxBuf;
+
+static tx_pending tx_pend;
+static tx_waiting tx_wait;
+
+/* opened device */
+static struct net_device *opened_dev = NULL;
+
+static void 
+rave_hardware_send_packet (char *buf, int length, llc_type type);
+/* Send packet if not busy with pending packet, else enqueue it for later 
+ * transmission.
+ * Return TX_SENT if sent, else TX_BUSY */
+static tx_status xmit_sync(struct sk_buff *skb, 
+                           unsigned int len, 
+                           struct net_device *dev,
+                           llc_type type);
+void 
+rave_tx_init(void)
+{
+        /* Initialize descriptor for the header we send before all
+           packets to the FPGA. */
+        RaveHdrDesc.sw_len = sizeof(rave_hdr);
+        RaveHdrDesc.ctrl = 0;
+        RaveHdrDesc.buf  = virt_to_phys(&rave_hdr);
+        RaveHdrDesc.next = virt_to_phys(&TxDesc);
+}
+
+void
+rave_tx_open(struct net_device *device)
+{
+	opened_dev  = device;
+	tx_pend.skb = NULL;
+	tx_wait.skb = NULL;
+}
+
+void 
+rave_tx_close(void)
+{
+        opened_dev = NULL;
+}
+
+
+/* This will only be invoked if the driver is _not_ in XOFF state.
+ * What this means is that we need not check it, and that this
+ * invariant will hold if we make sure that the netif_*_queue()
+ * calls are done at the proper times.
+ */
+/* XXX: return value should be != 0 for some error conditions? */
+int 
+rave_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	unsigned int length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+        
+        dprintk("> rave_start_xmit\n");
+        /* this simple TX driver has only one send-descriptor so we're full
+         * directly. If this had a send-ring instead, we would only do this if
+         * the ring got full. Also, the current handling of parallell packets
+         * from network stack and ioctl requests, requires this simple 
+         * approach.
+         */
+        netif_stop_queue(dev);
+        if (xmit_sync(skb, length, dev, LLC_TYPE_DATA) == TX_BUSY)
+        {
+                return 0;
+        }
+        dprintk("< rave_start_xmit\n");
+        return 0;
+}
+
+/* XXX: return value should be != 0 for some error conditions? */
+int 
+rave_mib_start_xmit(struct sk_buff *skb)
+{
+        rave_assert(opened_dev != NULL);
+        
+        dprintk("> rave_mib_start_xmit\n");
+        if (xmit_sync(skb, skb->len, opened_dev, LLC_TYPE_MIB) == TX_BUSY)
+        {
+                return 0;
+        }  
+        dprintk("< rave_mib_start_xmit\n");
+        return 0;
+}
+
+/* the transmit dma channel interrupt
+ *
+ * this is supposed to free the skbuff which was pending during transmission,
+ * and inform the kernel that we can send one more buffer
+ */
+
+/* TXI */
+
+void 
+rave_tx_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+	struct net_device *dev = (struct net_device *)dev_id;
+	unsigned long irqbits = *R_IRQ_MASK2_RD;
+	struct net_local *np = (struct net_local *)dev->priv;
+	
+	printk("> rave_tx_interrupt\n");
+	
+	if (irqbits & IO_STATE(R_IRQ_MASK2_RD, dma6_eop, active)) {
+		printk("  eop\n");
+		/* This protects us from concurrent execution of
+		 * our dev->hard_start_xmit function above.
+		 */
+		spin_lock(&np->lock);
+		
+		/* acknowledge the eop interrupt */
+		*R_DMA_CH6_CLR_INTR = 
+			IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
+		
+#if 0
+		printk("\n\nR_DMA_CH5_HWSW %8.8x\n", *R_DMA_CH5_HWSW);
+		printk("R_DMA_CH5_FIRST %8.8x\n", *R_DMA_CH5_FIRST);
+		printk("R_DMA_CH5_FIRST(v) %8.8x\n", phys_to_virt(*R_DMA_CH5_FIRST));
+		printk("R_DMA_CH5_DESCR %8.8x\n", *R_DMA_CH5_DESCR);
+		printk("myNextRxDesc %8.8x\n", myNextRxDesc);
+		
+		if (phys_to_virt(*R_DMA_CH5_FIRST)) {
+			print_descr(phys_to_virt(*R_DMA_CH5_FIRST));
+			{
+				int i;
+				/* char *buf = 
+				   phys_to_virt(&myNextRxDesc->buf); */
+				unsigned char *b = (unsigned char*)&RxBuf;
+				
+				printk("%2.2x %2.2x %2.2x %2.2x %2.2x %2.2x >"
+				       "%2.2x %2.2x %2.2x %2.2x %2.2x %2.2x\n",
+				       b[6],b[7],b[8],b[9],b[10],b[11],
+				       b[0],b[1],b[2],b[3],b[4],b[5]);
+				
+				for(i=0; i!= 256; i++) {
+					printk("%2.2x ", b[i]);
+				}
+				printk("\n\n");
+			}
+		}
+#endif
+		
+		if(*R_DMA_CH6_FIRST == 0 && tx_pend.skb != NULL) {
+			switch (tx_pend.type) {
+                        case LLC_TYPE_DATA:
+                                np->stats.tx_bytes += tx_pend.skb->len;
+                                np->stats.tx_packets++;
+                                /* dma is ready with the transmission of the 
+                                   data in skb, so now
+                                   we can release the skb memory */
+                                dev_kfree_skb_irq(tx_pend.skb);
+                                netif_wake_queue(dev);
+                                rave_assert (tx_wait.skb == NULL || 
+                                             tx_wait.type == LLC_TYPE_MIB);
+                                break;
+                        case LLC_TYPE_MIB:
+                                /* this skb_buf is statically allocated so
+                                 * don't free it */
+                                rave_assert (tx_wait.skb == NULL || 
+                                             tx_wait.type == LLC_TYPE_DATA);
+                                break;
+                        default:
+                                rave_assert(0);
+                                break;
+                        }
+                        /* Handle wating packet. No need to protect from 
+                         * tx_interrupt:s here. */
+                        /* XXX: If rave_hardware_send_packet takes too long, 
+                         *      we need to use task queues (or 
+                         *      dev_queue_xmit?) to schedule transmission 
+                         *      instead. In that case, a different approach is 
+                         *      needed; rave_hardware_send_packet can't be 
+                         *      called directly and we can't rely on a context 
+                         *      where we're protected from tx_interrupt:s. */
+                        if (tx_wait.skb != NULL) {
+                                tx_pend.skb = tx_wait.skb;
+                                tx_pend.type = tx_wait.type;
+                                tx_wait.dev->trans_start = jiffies;
+                                rave_hardware_send_packet(tx_wait.skb->data, 
+                                                          tx_wait.len, 
+                                                          tx_wait.type);
+                                tx_wait.skb = NULL;
+                        } else {
+                                tx_pend.skb = NULL;
+                        }
+		}
+		
+	} 
+	else {
+		printk("tx weird interrupt\n");
+	}
+	
+	spin_unlock(&np->lock);
+	
+	printk("< rave_tx_interrupt\n\n");
+}
+
+void 
+rave_update_tx_stats(struct net_device_stats *es)
+{
+	unsigned long r = *R_TR_COUNTERS;
+
+	printk("> update_tx_stats\n");
+
+	/* update stats relevant to transmission errors */
+	es->collisions += (r & 0xff) + ((r >> 8) & 0xff); /* single_col + multiple_col */
+	es->tx_errors += (r >> 24) & 0xff; /* deferred transmit frames */
+}
+
+/* This is called from rave_start_xmit etc, to transmit. */
+void 
+rave_hardware_send_packet(char *buf, int length, llc_type type)
+{
+  
+  /* Send modulo dword sized packets. We just add to the length here,
+     a little ugly, but... */
+
+#ifdef ETHDEBUG
+	dprintk(">rave_hardware_send_packet, buf 0%p len %d\n", buf, length);
+	length = length%4 == 0 ? length : length+(4-(length%4));
+	dprintk(">rave_hardware_send_packet, buf 0%p len %d\n", buf, length);
+#endif
+  
+#if 0
+	/* blinkenlights */
+	if (!nolink) {
+		LED_NETWORK_RX_SET(1);
+		/* Set the earliest time we may clear the LED */
+		led_clear_time = jiffies + NET_FLASH_TIME;
+	}
+	
+#endif
+
+  /* configure the tx dma descriptor */
+  
+	TxDesc.sw_len = length;
+	TxDesc.ctrl   = d_eop | d_eol | d_wait;
+	TxDesc.buf    = virt_to_phys(buf);
+	TxDesc.next   = virt_to_phys(0);		/* not used */
+	
+	/* setup the dma channel and start it */
+	
+	rave_assert(type <= 255);
+        dprintk("rave_hardware_send_packet, type = %d", type);
+        rave_hdr.type 	= type;
+        rave_hdr.reserved 	= 0;
+	//  rave_hdr.length 	= htons(length);
+        rave_hdr.length 	= length;
+	
+#ifdef ETHDEBUG
+	
+	{
+		int i;
+		unsigned char *b = (unsigned char*)buf;
+		
+		for(i=0; i!= length; i++) {
+			if (i%8 == 0) {
+				printk("\n");
+			}
+			printk("%2.2x ", b[i]);
+		}
+		printk("\n");
+	}
+#endif
+	
+#if 0
+	printk("RaveHdrDesc %8.8x\n", &RaveHdrDesc);
+	print_descr((char*)&RaveHdrDesc);
+	
+	printk("TxDesc %8.8x\n", &TxDesc);
+	print_descr((char*)&TxDesc);
+#endif
+	
+	*R_EXT_DMA_1_ADDR = EXT_DMA1_ADDR;
+	
+	*R_DMA_CH6_FIRST = virt_to_phys(&RaveHdrDesc);
+	*R_DMA_CH6_CMD   = IO_STATE(R_DMA_CH6_CMD, cmd, start);
+	
+        *R_EXT_DMA_1_CMD = (IO_STATE(R_EXT_DMA_1_CMD, cnt,disable      ) |
+		      IO_STATE(R_EXT_DMA_1_CMD, rqpol,    ahigh        ) |
+		      IO_STATE(R_EXT_DMA_1_CMD, apol,     ahigh        ) |
+		      IO_STATE(R_EXT_DMA_1_CMD, rq_ack,   handsh       ) |
+		      IO_STATE(R_EXT_DMA_1_CMD, wid,      dword        ) |
+		      IO_STATE(R_EXT_DMA_1_CMD, dir,      output       ) | 
+		      IO_STATE(R_EXT_DMA_1_CMD, run,      stop         ) |
+		      IO_FIELD(R_EXT_DMA_1_CMD, trf_count,length+RaveHdrDesc.sw_len));
+
+        *R_EXT_DMA_1_CMD = (IO_STATE(R_EXT_DMA_1_CMD, cnt,disable      ) |
+		      IO_STATE(R_EXT_DMA_1_CMD, rqpol,    ahigh        ) |
+		      IO_STATE(R_EXT_DMA_1_CMD, apol,     ahigh        ) |
+		      IO_STATE(R_EXT_DMA_1_CMD, rq_ack,   handsh       ) |
+		      IO_STATE(R_EXT_DMA_1_CMD, wid,      dword        ) |
+		      IO_STATE(R_EXT_DMA_1_CMD, dir,      output       ) | 
+		      IO_STATE(R_EXT_DMA_1_CMD, run,      start        ) |
+		      IO_FIELD(R_EXT_DMA_1_CMD, trf_count,length+RaveHdrDesc.sw_len));
+
+  dprintk("< rave_hardware_send_packet\n\n");
+}
+
+static tx_status
+xmit_sync(struct sk_buff *skb, 
+          unsigned int len, 
+          struct net_device *dev, 
+          llc_type type) 
+{
+        net_local *np = (net_local *) dev->priv;
+        
+        if (tx_pend.skb != NULL) {
+                rave_assert(tx_wait.skb == NULL);
+                tx_wait.skb = skb;
+                tx_wait.len = len;
+                tx_wait.dev = dev;
+                tx_wait.type = type;
+                return TX_BUSY;
+        }
+        tx_pend.skb = skb;
+        tx_pend.type = type;
+        spin_lock_irq(&np->lock);  /* protect from tx_interrupt */
+        dev->trans_start = jiffies;
+        rave_hardware_send_packet(skb->data, len, type);
+        spin_unlock_irq(&np->lock);
+        return TX_SENT;
+}
+
+/* Called by upper layers if they decide it took too long to complete
+ * sending a packet - we need to reset and stuff.
+ */
+/* XXX: used? */
+#if 0
+void
+rave_tx_timeout(struct net_device *dev)
+{
+        struct net_local *np = (struct net_local *)dev->priv;
+	
+	printk("%s: transmit timed out) | %s?\n", dev->name,
+	       tx_done(dev) ? "IRQ problem" : "network cable problem");
+	
+	/* remember we got an error */
+	
+	np->stats.tx_errors++; 
+	
+	/* reset the TX DMA in case it has hung on something */
+	
+	RESET_DMA(5);
+	WAIT_DMA(5);
+	
+	/* Reset the tranceiver. */
+	
+	
+	/* and get rid of the packet that never got an interrupt */
+	
+	dev_kfree_skb(tx_skb);
+	tx_skb = 0;
+	
+	/* tell the upper layers we're ok again */
+	
+	netif_wake_queue(dev);
+}
+#endif /* 0 */
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/tx.h ./arch/cris/drivers/802_11/tx.h
--- /home/anderstj/linux-2.4.26/arch/cris/drivers/802_11/tx.h	1970-01-01 01:00:00.000000000 +0100
+++ ./arch/cris/drivers/802_11/tx.h	2003-04-29 16:16:43.000000000 +0200
@@ -0,0 +1,25 @@
+/*
+ * Tx Interface to the 802.11 driver
+ */
+
+#ifndef TX_H
+#define TX_H
+
+#include "rave_global.h"
+
+/* Initiate tx interface */
+void rave_tx_init(void);
+/* Open tx interface */
+void rave_tx_open(struct net_device *device);
+/* Close tx interface */
+void rave_tx_close(void);
+/* Device method "hard_start_xmit" in Linux network interface for device
+ * drivers. */
+int rave_start_xmit(struct sk_buff *skb, struct net_device *dev);
+/* Like rave_start_xmit but for MIB requests rather than LLC data. */
+int  rave_mib_start_xmit(struct sk_buff *skb);
+/* Tx interrupt handler */
+void rave_tx_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+void rave_update_tx_stats(struct net_device_stats *);
+
+#endif /* TX_H */
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/drivers/Config.in ./arch/cris/drivers/Config.in
--- /home/anderstj/linux-2.4.26/arch/cris/drivers/Config.in	2004-02-18 14:36:30.000000000 +0100
+++ ./arch/cris/drivers/Config.in	2004-05-25 10:28:00.000000000 +0200
@@ -215,8 +215,17 @@
     if [ "$CONFIG_ETRAX_RS485_ON_PA" = "y" ]; then
       int '      RS-485 mode on PA bit' CONFIG_ETRAX_RS485_ON_PA_BIT 3
     fi
+    bool '    RS-485 mode on port G' CONFIG_ETRAX_RS485_ON_PORT_G
+    if [ "$CONFIG_ETRAX_RS485_ON_PORT_G" = "y" ]; then
+      int '      RS-485 mode on port G bit' CONFIG_ETRAX_RS485_ON_PORT_G_BIT 1
+    fi
     bool '    Disable serial receiver' CONFIG_ETRAX_RS485_DISABLE_RECEIVER
   fi
+  bool '  LTC1387 support' CONFIG_ETRAX_LTC1387
+  if [ "$CONFIG_ETRAX_LTC1387" = "y" ]; then
+    int '      DXEN on port G bit' CONFIG_ETRAX_LTC1387_DXEN_PORT_G_BIT 0
+    int '      RXEN on port G bit' CONFIG_ETRAX_LTC1387_RXEN_PORT_G_BIT 12
+  fi	  
 fi
 
 bool 'Synchronous serial port support' CONFIG_ETRAX_SYNCHRONOUS_SERIAL
@@ -334,10 +343,12 @@
   hex  '  PB user changeable bits mask' CONFIG_ETRAX_PB_CHANGEABLE_BITS FF
 fi
 
-bool 'ARTPEC-1 support' CONFIG_JULIETTE
-
-if [ "$CONFIG_JULIETTE" = "y" ]; then
-
+bool 'Port G Output' CONFIG_ETRAX_DEF_R_PORT_G_DIR
+if [ "$CONFIG_ETRAX_DEF_R_PORT_G_DIR" = "y" ]; then
+  bool '  G0' CONFIG_ETRAX_DEF_R_PORT_G0_DIR_OUT
+  bool '  G8-G15' CONFIG_ETRAX_DEF_R_PORT_G8_15_DIR_OUT
+  bool '  G16-G23' CONFIG_ETRAX_DEF_R_PORT_G16_23_DIR_OUT
+  bool '  G24' CONFIG_ETRAX_DEF_R_PORT_G24_DIR_OUT 
 fi
 
 bool 'VIRTEX FPGA support' CONFIG_ETRAX_VIRTEX_FPGA
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/drivers/Makefile ./arch/cris/drivers/Makefile
--- /home/anderstj/linux-2.4.26/arch/cris/drivers/Makefile	2004-02-18 14:36:30.000000000 +0100
+++ ./arch/cris/drivers/Makefile	2004-05-25 10:28:13.000000000 +0200
@@ -21,8 +21,6 @@
 obj-$(CONFIG_ETRAX_PARPORT)             += parport.o
 obj-$(CONFIG_ETRAX_DS1302)              += ds1302.o
 obj-$(CONFIG_ETRAX_PCF8563)		+= pcf8563.o
-
-
 obj-$(CONFIG_ETRAX_ETHERNET_LPSLAVE)    += lpslave/lpslavedrivers.o
 subdir-$(CONFIG_ETRAX_ETHERNET_LPSLAVE) += lpslave
 
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/drivers/ds1302.c ./arch/cris/drivers/ds1302.c
--- /home/anderstj/linux-2.4.26/arch/cris/drivers/ds1302.c	2004-01-05 14:53:56.000000000 +0100
+++ ./arch/cris/drivers/ds1302.c	2004-01-07 10:49:08.000000000 +0100
@@ -7,6 +7,9 @@
 *! Functions exported: ds1302_readreg, ds1302_writereg, ds1302_init
 *!
 *! $Log: ds1302.c,v $
+*! Revision 1.20  2004/01/07 09:49:08  starvik
+*! Merge of Linux 2.4.24
+*!
 *! Revision 1.19  2003/06/12 08:02:05  johana
 *! Removed faulty comma from printk.
 *! Fixed warning () -> (void)
@@ -108,7 +111,7 @@
 *!
 *! (C) Copyright 1999, 2000, 2001  Axis Communications AB, LUND, SWEDEN
 *!
-*! $Id: ds1302.c,v 1.19 2003/06/12 08:02:05 johana Exp $
+*! $Id: ds1302.c,v 1.20 2004/01/07 09:49:08 starvik Exp $
 *!
 *!***************************************************************************/
 
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/drivers/ethernet.c ./arch/cris/drivers/ethernet.c
--- /home/anderstj/linux-2.4.26/arch/cris/drivers/ethernet.c	2004-02-18 14:36:30.000000000 +0100
+++ ./arch/cris/drivers/ethernet.c	2004-01-28 20:15:54.000000000 +0100
@@ -1,4 +1,4 @@
-/* $Id: ethernet.c,v 1.48 2003/12/03 13:44:39 starvik Exp $
+/* $Id: ethernet.c,v 1.49 2004/01/28 19:15:54 henriken Exp $
  *
  * e100net.c: A network driver for the ETRAX 100LX network controller.
  *
@@ -7,6 +7,11 @@
  * The outline of this driver comes from skeleton.c.
  *
  * $Log: ethernet.c,v $
+ * Revision 1.49  2004/01/28 19:15:54  henriken
+ * Use a shadow register for R_NETWORK_TR_CTRL.
+ * Don't destroy R_NETWORK_TR_CTRL setting in under-/over-run irq handler
+ * (e100nw_interrupt).
+ *
  * Revision 1.48  2003/12/03 13:44:39  starvik
  * Use hardware pad for short packets. This prevents information leakage
  * reported by Nessus.
@@ -382,6 +387,8 @@
 static unsigned int network_rec_config_shadow = 0;
 static unsigned int mdio_phy_addr; /* Transciever address */
 
+static unsigned int network_tr_ctrl_shadow = 0;
+
 /* Network speed indication. */
 static struct timer_list speed_timer;
 static struct timer_list clear_led_timer;
@@ -711,14 +718,14 @@
 		IO_STATE(R_NETWORK_GEN_CONFIG, phy,    mii_clk) |
 		IO_STATE(R_NETWORK_GEN_CONFIG, enable, on);
 
-	*R_NETWORK_TR_CTRL = 
-		IO_STATE(R_NETWORK_TR_CTRL, clr_error, clr) |
-		IO_STATE(R_NETWORK_TR_CTRL, delay, none) |
-		IO_STATE(R_NETWORK_TR_CTRL, cancel, dont) |
-		IO_STATE(R_NETWORK_TR_CTRL, cd, enable) |
-		IO_STATE(R_NETWORK_TR_CTRL, retry, enable) |
-		IO_STATE(R_NETWORK_TR_CTRL, pad, enable) |
-		IO_STATE(R_NETWORK_TR_CTRL, crc, enable);
+	SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, clr_error, clr);
+	SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, delay, none);
+	SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, cancel, dont);
+	SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, cd, enable);
+	SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, retry, enable);
+	SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, pad, enable);
+	SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, crc, enable);
+	*R_NETWORK_TR_CTRL = network_tr_ctrl_shadow;
 
 	save_flags(flags);
 	cli();
@@ -1241,8 +1248,10 @@
 	unsigned long irqbits = *R_IRQ_MASK0_RD;
 
 	/* check for underrun irq */
-	if (irqbits & IO_STATE(R_IRQ_MASK0_RD, underrun, active)) { 
-		*R_NETWORK_TR_CTRL = IO_STATE(R_NETWORK_TR_CTRL, clr_error, clr);
+	if (irqbits & IO_STATE(R_IRQ_MASK0_RD, underrun, active)) {
+		SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, clr_error, clr);	
+		*R_NETWORK_TR_CTRL = network_tr_ctrl_shadow;
+		SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, clr_error, nop);
 		np->stats.tx_errors++;
 		D(printk("ethernet receiver underrun!\n"));
 	}
@@ -1254,7 +1263,9 @@
 	}
 	/* check for excessive collision irq */
 	if (irqbits & IO_STATE(R_IRQ_MASK0_RD, excessive_col, active)) { 
-		*R_NETWORK_TR_CTRL = IO_STATE(R_NETWORK_TR_CTRL, clr_error, clr);
+		SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, clr_error, clr);
+		*R_NETWORK_TR_CTRL = network_tr_ctrl_shadow;
+		SETS(network_tr_ctrl_shadow, R_NETWORK_TR_CTRL, clr_error, nop);
 		np->stats.tx_errors++;
 		D(printk("ethernet excessive collisions!\n"));
 	}
@@ -1511,7 +1522,7 @@
 			struct ethtool_drvinfo info;
 			memset((void *) &info, 0, sizeof (info));
 			strncpy(info.driver, "ETRAX 100LX", sizeof(info.driver) - 1);
-			strncpy(info.version, "$Revision: 1.48 $", sizeof(info.version) - 1);
+			strncpy(info.version, "$Revision: 1.49 $", sizeof(info.version) - 1);
 			strncpy(info.fw_version, "N/A", sizeof(info.fw_version) - 1);
 			strncpy(info.bus_info, "N/A", sizeof(info.bus_info) - 1);
 			info.regdump_len = 0;
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/drivers/ide.c ./arch/cris/drivers/ide.c
--- /home/anderstj/linux-2.4.26/arch/cris/drivers/ide.c	2003-08-25 13:44:39.000000000 +0200
+++ ./arch/cris/drivers/ide.c	2004-01-21 13:14:05.000000000 +0100
@@ -1,4 +1,4 @@
-/* $Id: ide.c,v 1.30 2003/07/08 07:24:47 pkj Exp $
+/* $Id: ide.c,v 1.31 2004/01/21 12:14:05 starvik Exp $
  *
  * Etrax specific IDE functions, like init and PIO-mode setting etc.
  * Almost the entire ide.c is used for the rest of the Etrax ATA driver.
@@ -8,6 +8,9 @@
  *             Mikael Starvik     (pio setup stuff)
  *
  * $Log: ide.c,v $
+ * Revision 1.31  2004/01/21 12:14:05  starvik
+ * Updated for >= Linux 2.4.21
+ *
  * Revision 1.30  2003/07/08 07:24:47  pkj
  * Corrected spelling mistakes originally found in 2.5.x
  *
@@ -170,9 +173,9 @@
 #define D(x) 
 
 void 
-OUT_BYTE(unsigned char data, ide_ioreg_t reg) {
+etrax100_ide_outw(unsigned short data, ide_ioreg_t reg) {
 	int timeleft;
-	LOWDB(printk("ob: data 0x%x, reg 0x%x\n", data, reg));
+	LOWDB(printk("ow: data 0x%x, reg 0x%x\n", data, reg));
 
 	/* note the lack of handling any timeouts. we stop waiting, but we don't
 	 * really notify anybody.
@@ -202,8 +205,20 @@
 		timeleft--;
 }
 
-unsigned short 
-IN_BYTE(ide_ioreg_t reg) {
+void
+etrax100_ide_outb(unsigned char data, ide_ioreg_t reg)
+{
+	etrax100_ide_outw(data, reg);
+}
+
+void
+etrax100_ide_outbsync(ide_drive_t *drive, u8 addr, unsigned long port)
+{
+	etrax100_ide_outw(addr, port);
+}
+
+unsigned short
+etrax100_ide_inw(ide_ioreg_t reg) {
 	int status;
 	int timeleft;
 
@@ -247,7 +262,13 @@
 
 	LOWDB(printk("inb: 0x%x from reg 0x%x\n", status & 0xff, reg));
 
-        return (unsigned char)status;
+        return (unsigned short)status;
+}
+
+unsigned char
+etrax100_ide_inb(ide_ioreg_t reg)
+{
+	return (unsigned char)etrax100_ide_inw(reg);
 }
 
 /* PIO timing (in R_ATA_CONFIG)
@@ -311,6 +332,8 @@
 static void e100_ide_output_data (ide_drive_t *drive, void *, unsigned int);
 static void e100_atapi_input_bytes(ide_drive_t *drive, void *, unsigned int);
 static void e100_atapi_output_bytes(ide_drive_t *drive, void *, unsigned int);
+static int e100_dma_off (ide_drive_t *drive);
+static int e100_dma_verbose (ide_drive_t *drive);
 
 /*
  * good_dma_drives() lists the model names (from "hdparm -i")
@@ -386,6 +409,7 @@
 	
 	for(h = 0; h < MAX_HWIFS; h++) {
 		ide_hwif_t *hwif = &ide_hwifs[h];
+		hwif->mmio = 2;
 		hwif->chipset = ide_etrax100;
 		hwif->tuneproc = &tune_e100_ide;
                 hwif->ata_input_data = &e100_ide_input_data;
@@ -397,6 +421,13 @@
 		hwif->ide_dma_write = &e100_dma_write;
 		hwif->ide_dma_read = &e100_dma_read;
 		hwif->ide_dma_begin = &e100_dma_begin;
+		hwif->OUTB = &etrax100_ide_outb;
+		hwif->OUTW = &etrax100_ide_outw;
+		hwif->OUTBSYNC = &etrax100_ide_outbsync;
+		hwif->INB = &etrax100_ide_inb;
+		hwif->INW = &etrax100_ide_inw;
+		hwif->ide_dma_off_quietly = &e100_dma_off;
+		hwif->ide_dma_verbose = &e100_dma_verbose;
 	}
 
 	/* actually reset and configure the etrax100 ide/ata interface */
@@ -484,6 +515,18 @@
 
 }
 
+static int e100_dma_off (ide_drive_t *drive)
+{
+	return 0;  
+}
+
+static int e100_dma_verbose (ide_drive_t *drive)
+{
+	printk(", DMA(mode 2)");
+	return 0;
+}
+
+
 static etrax_dma_descr mydescr;
 
 /*
@@ -729,7 +772,7 @@
 		*/
 
 		if(ata_tot_size + size > 131072) {
-			printk("too large total ATA DMA request, %d + %d!\n", ata_tot_size, size);
+			printk("too large total ATA DMA request, %d + %d!\n", ata_tot_size, (int)size);
 			return 1;
 		}
 
@@ -873,11 +916,11 @@
                         if ((HWGROUP(drive)->rq->cmd == IDE_DRIVE_TASKFILE) &&
 			    (drive->addressing == 1)) {
 				ide_task_t *args = HWGROUP(drive)->rq->special;
-				OUT_BYTE(args->tfRegister[IDE_COMMAND_OFFSET], IDE_COMMAND_REG);
+				etrax100_ide_outb(args->tfRegister[IDE_COMMAND_OFFSET], IDE_COMMAND_REG);
 			} else if (drive->addressing) {
-				OUT_BYTE(WIN_READDMA_EXT, IDE_COMMAND_REG);
+				etrax100_ide_outb(WIN_READDMA_EXT, IDE_COMMAND_REG);
 			} else {
-				OUT_BYTE(WIN_READDMA, IDE_COMMAND_REG);
+				etrax100_ide_outb(WIN_READDMA, IDE_COMMAND_REG);
 			}
 		}
 
@@ -931,11 +974,11 @@
 			if ((HWGROUP(drive)->rq->cmd == IDE_DRIVE_TASKFILE) &&
 			    (drive->addressing == 1)) {
 				ide_task_t *args = HWGROUP(drive)->rq->special;
-				OUT_BYTE(args->tfRegister[IDE_COMMAND_OFFSET], IDE_COMMAND_REG);
+				etrax100_ide_outb(args->tfRegister[IDE_COMMAND_OFFSET], IDE_COMMAND_REG);
 			} else if (drive->addressing) {
-				OUT_BYTE(WIN_WRITEDMA_EXT, IDE_COMMAND_REG);
+				etrax100_ide_outb(WIN_WRITEDMA_EXT, IDE_COMMAND_REG);
 			} else {
-				OUT_BYTE(WIN_WRITEDMA, IDE_COMMAND_REG);
+				etrax100_ide_outb(WIN_WRITEDMA, IDE_COMMAND_REG);
 			}
 		}
 
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/drivers/pcf8563.c ./arch/cris/drivers/pcf8563.c
--- /home/anderstj/linux-2.4.26/arch/cris/drivers/pcf8563.c	2004-01-05 14:53:56.000000000 +0100
+++ ./arch/cris/drivers/pcf8563.c	2004-01-07 10:49:08.000000000 +0100
@@ -38,7 +38,7 @@
 #define PCF8563_MAJOR	121	/* Local major number. */
 #define DEVICE_NAME	"rtc"	/* Name which is registered in /proc/devices. */
 #define PCF8563_NAME	"PCF8563"
-#define DRIVER_VERSION	"$Revision: 1.15 $"
+#define DRIVER_VERSION	"$Revision: 1.16 $"
 
 /* Two simple wrapper macros, saves a few keystrokes. */
 #define rtc_read(x) i2c_readreg(RTC_I2C_READ, x)
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/drivers/serial.c ./arch/cris/drivers/serial.c
--- /home/anderstj/linux-2.4.26/arch/cris/drivers/serial.c	2004-02-18 14:36:30.000000000 +0100
+++ ./arch/cris/drivers/serial.c	2004-03-30 18:03:57.000000000 +0200
@@ -1,4 +1,4 @@
-/* $Id: serial.c,v 1.58 2003/08/29 17:32:50 johana Exp $
+/* $Id: serial.c,v 1.62 2004/03/30 16:03:57 anderstj Exp $
  *
  * Serial port driver for the ETRAX 100LX chip
  *
@@ -7,6 +7,21 @@
  *    Many, many authors. Based once upon a time on serial.c for 16x50.
  *
  * $Log: serial.c,v $
+ * Revision 1.62  2004/03/30 16:03:57  anderstj
+ * LTC1387 DXEN and RXEN high for both RS232 and RS485 mode.
+ * Changed name of config-option to CONFIG_ETRAX_LTC1387.
+ *
+ * Revision 1.61  2004/03/05 11:56:35  anderstj
+ * Removed debug output when enabling RS485
+ *
+ * Revision 1.60  2004/02/24 09:16:33  anderstj
+ * Use bit in G-port to enable/disable RS485.
+ * Set DXEN and RXEN in LTC1387.
+ *
+ * Revision 1.59  2004/01/27 11:48:12  starvik
+ * Added rs_debug_write_function to make it possible for other kernel code
+ * to put characters into the transmit queue. Used by the printk driver.
+ *
  * Revision 1.58  2003/08/29 17:32:50  johana
  * Fixed CMSPAR (Mark/Space) support. CMSPAR|PARODD = Mark(1) parity.
  *
@@ -455,7 +470,7 @@
  *
  */
 
-static char *serial_version = "$Revision: 1.58 $";
+static char *serial_version = "$Revision: 1.62 $";
 
 #include <linux/config.h>
 #include <linux/version.h>
@@ -509,6 +524,10 @@
 #error "Disable either ETRAX_SERIAL_FLUSH_DMA_FAST or ETRAX_FAST_TIMER"
 #endif
 
+#if defined(CONFIG_ETRAX_RS485_ON_PA) && defined(CONFIG_ETRAX_RS485_ON_PORT_G)
+#error "Disable either CONFIG_ETRAX_RS485_ON_PA or CONFIG_ETRAX_RS485_ON_PORT_G"
+#endif
+
 /*
  * All of the compatibilty code so we can compile serial.c against
  * older kernels is hidden in serial_compat.h
@@ -895,6 +914,9 @@
 #if defined(CONFIG_ETRAX_RS485_ON_PA)
 static int rs485_pa_bit = CONFIG_ETRAX_RS485_ON_PA_BIT;
 #endif
+#if defined(CONFIG_ETRAX_RS485_ON_PORT_G)
+static int rs485_port_g_bit = CONFIG_ETRAX_RS485_ON_PORT_G_BIT;
+#endif
 #endif
 
 /* Info and macros needed for each ports extra control/status signals. */
@@ -1325,11 +1347,6 @@
 };
 #endif /* !CONFIG_ETRAX_SERX_DTR_RI_DSR_CD_MIXED */
 
-#if defined(CONFIG_ETRAX_RS485) && defined(CONFIG_ETRAX_RS485_ON_PA)
-unsigned char rs485_pa_port = CONFIG_ETRAX_RS485_ON_PA_BIT;
-#endif
-
-
 #define E100_RTS_MASK 0x20
 #define E100_CTS_MASK 0x40
 
@@ -1859,7 +1876,10 @@
 #if defined(CONFIG_ETRAX_RS485_ON_PA)	
 	*R_PORT_PA_DATA = port_pa_data_shadow |= (1 << rs485_pa_bit);
 #endif
-
+#if defined(CONFIG_ETRAX_RS485_ON_PORT_G)
+	REG_SHADOW_SET(R_PORT_G_DATA,  port_g_data_shadow,  
+		       rs485_port_g_bit, 1);
+#endif
 	info->rs485.rts_on_send = 0x01 & r->rts_on_send;
 	info->rs485.rts_after_sent = 0x01 & r->rts_after_sent;
 	if (r->delay_rts_before_send >= 1000)
@@ -4482,6 +4502,42 @@
 	
 }
 
+/* In debugport.c - register a console write function that uses the normal
+ * serial driver 
+ */
+typedef int (*debugport_write_function)(int i, const char *buf, unsigned int len);
+
+extern debugport_write_function debug_write_function;
+
+static int rs_debug_write_function(int i, const char *buf, unsigned int len)
+{
+	int cnt;
+        struct tty_struct *tty;
+        static int recurse_cnt = 0;
+
+        tty = rs_table[i].tty;
+        if (tty)  {
+		unsigned long flags;
+		if (recurse_cnt > 5) /* We skip this debug output */
+			return 1;
+
+		local_irq_save(flags); 		
+		recurse_cnt++;
+                do {
+                        cnt = rs_write(tty, 0, buf, len);
+                        if (cnt >= 0) {
+                                buf += cnt;
+                                len -= cnt;
+                        } else
+                                len = cnt;
+                } while(len > 0);
+		recurse_cnt--;
+		local_irq_restore(flags);
+                return 1;
+        }
+        return 0;
+}
+
 /*
  * ------------------------------------------------------------
  * rs_close()
@@ -4603,6 +4659,20 @@
 #if defined(CONFIG_ETRAX_RS485_ON_PA)
 		*R_PORT_PA_DATA = port_pa_data_shadow &= ~(1 << rs485_pa_bit);
 #endif
+#if defined(CONFIG_ETRAX_RS485_ON_PORT_G)
+		REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow,  
+			       rs485_port_g_bit, 0);
+#endif
+#if defined(CONFIG_ETRAX_LTC1387)
+		if (info->line == 2) {
+			REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow,
+				       CONFIG_ETRAX_LTC1387_DXEN_PORT_G_BIT, 
+				       0);
+			REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow,
+				       CONFIG_ETRAX_LTC1387_RXEN_PORT_G_BIT, 
+				       0);
+		}
+#endif
 	}
 #endif
 }
@@ -4904,6 +4974,17 @@
 	DFLIP(	if (info->line == SERIAL_DEBUG_LINE) {
 			info->icount.rx = 0;
 		} );
+#if defined(CONFIG_ETRAX_LTC1387)
+	/*
+	 * Set DXEN and RXEN to enable transceiver for LTC1387
+	 */
+	if (info->line == 2) {
+		REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow,
+			       CONFIG_ETRAX_LTC1387_DXEN_PORT_G_BIT, 1);
+		REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow,
+			       CONFIG_ETRAX_LTC1387_RXEN_PORT_G_BIT, 1);
+	}
+#endif
 	
 	return 0;
 }
@@ -5255,7 +5336,7 @@
 	}
 #endif
 #endif /* CONFIG_SVINTO_SIM */
-
+	debug_write_function = rs_debug_write_function;
 	return 0;
 }
 
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/drivers/sync_serial.c ./arch/cris/drivers/sync_serial.c
--- /home/anderstj/linux-2.4.26/arch/cris/drivers/sync_serial.c	2004-02-18 14:36:30.000000000 +0100
+++ ./arch/cris/drivers/sync_serial.c	2004-03-11 13:55:04.000000000 +0100
@@ -709,6 +709,19 @@
 		SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode1, sync);
 
 	*R_GEN_CONFIG_II = gen_config_ii_shadow;
+	/* Reset DMA. At readout from serial port the data could be shifted
+	 * one byte if not resetting DMA.
+	 */
+	if (port->use_dma) {
+		if (port->port_nbr == 0) {
+			RESET_DMA(9);
+			WAIT_DMA(9);
+		} else {
+			RESET_DMA(5);
+			WAIT_DMA(5);
+		}
+		start_dma_in(port);
+	}
 	restore_flags(flags);	
 	return return_val;
 }
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/drivers/usb-host.c ./arch/cris/drivers/usb-host.c
--- /home/anderstj/linux-2.4.26/arch/cris/drivers/usb-host.c	2004-02-18 14:36:30.000000000 +0100
+++ ./arch/cris/drivers/usb-host.c	2004-05-11 15:10:05.000000000 +0200
@@ -41,7 +41,7 @@
 #define ETRAX_USB_RX_IRQ USB_DMA_RX_IRQ_NBR
 #define ETRAX_USB_TX_IRQ USB_DMA_TX_IRQ_NBR
 
-static const char *usb_hcd_version = "$Revision: 1.19 $";
+static const char *usb_hcd_version = "$Revision: 1.20 $";
 
 #undef KERN_DEBUG
 #define KERN_DEBUG ""
@@ -228,6 +228,9 @@
 /* Support interrupt traffic intervals up to 128 ms. */
 #define MAX_INTR_INTERVAL 128
 
+/* This is the maximum number of linked URBs (urb->next list) we allow. */
+#define MAX_URB_COUNT     (128)
+
 /* If periodic traffic (intr or isoc) is to be used, then one entry in the EP table
    must be "invalid". By this we mean that we shouldn't care about epid attentions
    for this epid, or at least handle them differently from epid attentions for "valid"
@@ -285,6 +288,9 @@
 /* Cache for the registers allocated in the top half. */
 static kmem_cache_t *top_half_reg_cache;
 
+/* Cache for the data allocated in the isoc descr top half. */
+static kmem_cache_t *isoc_compl_cache;
+
 static struct usb_bus *etrax_usb_bus;
 
 /* This is a circular (double-linked) list of the active urbs for each epid.
@@ -304,30 +310,30 @@
 
 static inline int urb_list_empty(int epid)
 {
-        return list_empty(&urb_list[epid]);
+	return list_empty(&urb_list[epid]);
 }
 
 /* Returns first urb for this epid, or NULL if list is empty. */
 static inline urb_t *urb_list_first(int epid)
 {
-        urb_t *first_urb = 0;
+	urb_t *first_urb = 0;
 
-        if (!urb_list_empty(epid)) {
-                /* Get the first urb (i.e. head->next). */
-                urb_entry_t *urb_entry = list_entry((&urb_list[epid])->next, urb_entry_t, list);
-                first_urb = urb_entry->urb;
-        }
-        return first_urb;
+	if (!urb_list_empty(epid)) {
+		/* Get the first urb (i.e. head->next). */
+		urb_entry_t *urb_entry = list_entry((&urb_list[epid])->next, urb_entry_t, list);
+		first_urb = urb_entry->urb;
+	}
+	return first_urb;
 }
 
 /* Adds an urb_entry last in the list for this epid. */
 static inline void urb_list_add(urb_t *urb, int epid)
 {
-        urb_entry_t *urb_entry = (urb_entry_t *)kmalloc(sizeof(urb_entry_t), KMALLOC_FLAG);
-        assert(urb_entry);
+	urb_entry_t *urb_entry = (urb_entry_t *)kmalloc(sizeof(urb_entry_t), KMALLOC_FLAG);
+	assert(urb_entry);
 
-        urb_entry->urb = urb;
-        list_add_tail(&urb_entry->list, &urb_list[epid]);
+	urb_entry->urb = urb;
+	list_add_tail(&urb_entry->list, &urb_list[epid]);
 }
 
 /* Search through the list for an element that contains this urb. (The list
@@ -335,57 +341,75 @@
    the first in the list.) */
 static inline urb_entry_t *__urb_list_entry(urb_t *urb, int epid)
 {
-        struct list_head *entry;
-        struct list_head *tmp;
-        urb_entry_t *urb_entry;
-
-        list_for_each_safe(entry, tmp, &urb_list[epid]) {
-                urb_entry = list_entry(entry, urb_entry_t, list);
-                assert(urb_entry);
-                assert(urb_entry->urb);
-                
-                if (urb_entry->urb == urb) {
-                        return urb_entry;
-                }
-        }
-        return 0;
+	struct list_head *entry;
+	struct list_head *tmp;
+	urb_entry_t *urb_entry;
+
+	list_for_each_safe(entry, tmp, &urb_list[epid]) {
+		urb_entry = list_entry(entry, urb_entry_t, list);
+		assert(urb_entry);
+		assert(urb_entry->urb);
+
+		if (urb_entry->urb == urb) {
+			return urb_entry;
+		}
+	}
+	return 0;
 }
 
 /* Delete an urb from the list. */
 static inline void urb_list_del(urb_t *urb, int epid)
 {
-        urb_entry_t *urb_entry = __urb_list_entry(urb, epid);
-        assert(urb_entry);
-        
-        /* Delete entry and free. */
-        list_del(&urb_entry->list);
-        kfree(urb_entry);
+	urb_entry_t *urb_entry = __urb_list_entry(urb, epid);
+	assert(urb_entry);
+
+	/* Delete entry and free. */
+	list_del(&urb_entry->list);
+	kfree(urb_entry);
 }
 
 /* Move an urb to the end of the list. */
 static inline void urb_list_move_last(urb_t *urb, int epid)
 {
-        urb_entry_t *urb_entry = __urb_list_entry(urb, epid);
-        assert(urb_entry);
+	urb_entry_t *urb_entry = __urb_list_entry(urb, epid);
+	assert(urb_entry);
+
+	list_del(&urb_entry->list);
+	list_add_tail(&urb_entry->list, &urb_list[epid]);
+}
+
+/* Get the next urb in the list. */
+static inline urb_t *urb_list_next(urb_t *urb, int epid)
+{
+	urb_entry_t *urb_entry = __urb_list_entry(urb, epid);
+
+	assert(urb_entry);
 
-        list_del(&urb_entry->list);
-        list_add_tail(&urb_entry->list, &urb_list[epid]);
+	if (urb_entry->list.next != &urb_list[epid]) {
+		struct list_head *elem = urb_entry->list.next;
+		urb_entry = list_entry(elem, urb_entry_t, list);
+		return urb_entry->urb;
+	} else {
+		return NULL;
+	}
 }
 
+
+
 /* For debug purposes only. */
 static inline void urb_list_dump(int epid)
 {
-        struct list_head *entry;
-        struct list_head *tmp;
-        urb_entry_t *urb_entry;
-        int i = 0;
-
-        info("Dumping urb list for epid %d", epid);
-        
-        list_for_each_safe(entry, tmp, &urb_list[epid]) {
-                urb_entry = list_entry(entry, urb_entry_t, list);
-                info("   entry %d, urb = 0x%lx", i, (unsigned long)urb_entry->urb);
-        }
+	struct list_head *entry;
+	struct list_head *tmp;
+	urb_entry_t *urb_entry;
+	int i = 0;
+
+	info("Dumping urb list for epid %d", epid);
+
+	list_for_each_safe(entry, tmp, &urb_list[epid]) {
+		urb_entry = list_entry(entry, urb_entry_t, list);
+		info("   entry %d, urb = 0x%lx", i, (unsigned long)urb_entry->urb);
+	}
 }
 
 static void init_rx_buffers(void);
@@ -422,6 +446,9 @@
 static void etrax_usb_hc_interrupt_top_half(int irq, void *vhc, struct pt_regs *regs);
 static void etrax_usb_hc_interrupt_bottom_half(void *data);
 
+static void etrax_usb_isoc_descr_interrupt_bottom_half(void *data);
+
+
 /* The following is a list of interrupt handlers for the host controller interrupts we use.
    They are called from etrax_usb_hc_interrupt_bottom_half. */
 static void etrax_usb_hc_isoc_eof_interrupt(void);
@@ -471,7 +498,7 @@
 	printk("interval              :%d\n", purb->interval);
 	printk("error_count           :%d\n", purb->error_count);
 	printk("context               :0x%08lx\n", (unsigned long)purb->context);
-        printk("complete              :0x%08lx\n\n", (unsigned long)purb->complete);
+	printk("complete              :0x%08lx\n\n", (unsigned long)purb->complete);
 }
 
 static void __dump_in_desc(volatile USB_IN_Desc_t *in)
@@ -482,7 +509,7 @@
 	printk("  next    : 0x%08lx\n", in->next);
 	printk("  buf     : 0x%08lx\n", in->buf);
 	printk("  hw_len  : 0x%04x (%d)\n", in->hw_len, in->hw_len);
-        printk("  status  : 0x%04x\n\n", in->status);
+	printk("  status  : 0x%04x\n\n", in->status);
 }
 
 static void __dump_sb_desc(volatile USB_SB_Desc_t *sb)
@@ -508,72 +535,72 @@
 	}
 		
 	printk("\n   USB_SB_Desc at 0x%08lx\n", (unsigned long)sb); 
-        printk("     command : 0x%04x\n", sb->command);
+	printk("     command : 0x%04x\n", sb->command);
 	printk("        rem     : %d\n", (sb->command & 0x3f00) >> 8);
 	printk("        full    : %d\n", (sb->command & 0x40) >> 6);
 	printk("        tt      : %d (%s)\n", tt, tt_string);
 	printk("        intr    : %d\n", (sb->command & 0x8) >> 3);
 	printk("        eot     : %d\n", (sb->command & 0x2) >> 1);
 	printk("        eol     : %d\n", sb->command & 0x1);
-        printk("     sw_len  : 0x%04x (%d)\n", sb->sw_len, sb->sw_len);
+	printk("     sw_len  : 0x%04x (%d)\n", sb->sw_len, sb->sw_len);
 	printk("     next    : 0x%08lx\n", sb->next);
-        printk("     buf     : 0x%08lx\n\n", sb->buf);
+	printk("     buf     : 0x%08lx\n\n", sb->buf);
 }
 
 
 static void __dump_ep_desc(volatile USB_EP_Desc_t *ep)
 {
 	printk("\nUSB_EP_Desc at 0x%08lx\n", (unsigned long)ep);
-        printk("  command : 0x%04x\n", ep->command);
+	printk("  command : 0x%04x\n", ep->command);
 	printk("     ep_id   : %d\n", (ep->command & 0x1f00) >> 8);
 	printk("     enable  : %d\n", (ep->command & 0x10) >> 4);
 	printk("     intr    : %d\n", (ep->command & 0x8) >> 3);
 	printk("     eof     : %d\n", (ep->command & 0x2) >> 1);
 	printk("     eol     : %d\n", ep->command & 0x1);
 	printk("  hw_len  : 0x%04x (%d)\n", ep->hw_len, ep->hw_len);
-        printk("  next    : 0x%08lx\n", ep->next);
+	printk("  next    : 0x%08lx\n", ep->next);
 	printk("  sub     : 0x%08lx\n\n", ep->sub);
 }
 
 static inline void __dump_ep_list(int pipe_type)
 {
-        volatile USB_EP_Desc_t *ep;
-        volatile USB_EP_Desc_t *first_ep;
-        volatile USB_SB_Desc_t *sb;
-
-        switch (pipe_type)
-        {
-        case PIPE_BULK:
-                first_ep = &TxBulkEPList[0];
-                break;
-        case PIPE_CONTROL:
-                first_ep = &TxCtrlEPList[0];
-                break;
-        case PIPE_INTERRUPT:
-                first_ep = &TxIntrEPList[0];
-                break;
-        case PIPE_ISOCHRONOUS:
-                first_ep = &TxIsocEPList[0];
-                break;
-        default:
+	volatile USB_EP_Desc_t *ep;
+	volatile USB_EP_Desc_t *first_ep;
+	volatile USB_SB_Desc_t *sb;
+
+	switch (pipe_type)
+	{
+	case PIPE_BULK:
+		first_ep = &TxBulkEPList[0];
+		break;
+	case PIPE_CONTROL:
+		first_ep = &TxCtrlEPList[0];
+		break;
+	case PIPE_INTERRUPT:
+		first_ep = &TxIntrEPList[0];
+		break;
+	case PIPE_ISOCHRONOUS:
+		first_ep = &TxIsocEPList[0];
+		break;
+	default:
 		warn("Cannot dump unknown traffic type");
 		return;
-        }
-        ep = first_ep;
+	}
+	ep = first_ep;
 
-        printk("\n\nDumping EP list...\n\n");
+	printk("\n\nDumping EP list...\n\n");
 
-        do {
+	do {
 		__dump_ep_desc(ep);
 		/* Cannot phys_to_virt on 0 as it turns into 80000000, which is != 0. */
-		sb = ep->sub ? phys_to_virt(ep->sub) : 0;                
+		sb = ep->sub ? phys_to_virt(ep->sub) : 0;
 		while (sb) {
 			__dump_sb_desc(sb);
 			sb = sb->next ? phys_to_virt(sb->next) : 0;
 		}
-                ep = (volatile USB_EP_Desc_t *)(phys_to_virt(ep->next));
-                
-        } while (ep != first_ep);
+		ep = (volatile USB_EP_Desc_t *)(phys_to_virt(ep->next));
+
+	} while (ep != first_ep);
 }
 
 static inline void __dump_ept_data(int epid)
@@ -652,9 +679,9 @@
 		RxDescList[i].hw_len = 0;
 		RxDescList[i].status = 0;
 
-                /* DMA IN cache bug. (struct etrax_dma_descr has the same layout as USB_IN_Desc
+		/* DMA IN cache bug. (struct etrax_dma_descr has the same layout as USB_IN_Desc
 		   for the relevant fields.) */
-                prepare_rx_descriptor((struct etrax_dma_descr*)&RxDescList[i]);
+		prepare_rx_descriptor((struct etrax_dma_descr*)&RxDescList[i]);
 
 	}
 	
@@ -682,7 +709,7 @@
 	DBFENTER;
 	
 	for (i = 0; i < (NBR_OF_EPIDS - 1); i++) {
-                CHECK_ALIGN(&TxBulkEPList[i]);
+		CHECK_ALIGN(&TxBulkEPList[i]);
 		TxBulkEPList[i].hw_len = 0;
 		TxBulkEPList[i].command = IO_FIELD(USB_EP_command, epid, i);
 		TxBulkEPList[i].sub = 0;
@@ -712,10 +739,10 @@
 	}
 
 	/* Configure the last one. */
-        CHECK_ALIGN(&TxBulkEPList[i]);
+	CHECK_ALIGN(&TxBulkEPList[i]);
 	TxBulkEPList[i].hw_len = 0;
 	TxBulkEPList[i].command = (IO_STATE(USB_EP_command, eol, yes) |
-                                   IO_FIELD(USB_EP_command, epid, i));
+				   IO_FIELD(USB_EP_command, epid, i));
 	TxBulkEPList[i].sub = 0;
 	TxBulkEPList[i].next = virt_to_phys(&TxBulkEPList[0]);
 
@@ -738,17 +765,17 @@
 	DBFENTER;
 	
 	for (i = 0; i < (NBR_OF_EPIDS - 1); i++) {
-                CHECK_ALIGN(&TxCtrlEPList[i]);
+		CHECK_ALIGN(&TxCtrlEPList[i]);
 		TxCtrlEPList[i].hw_len = 0;
 		TxCtrlEPList[i].command = IO_FIELD(USB_EP_command, epid, i);
 		TxCtrlEPList[i].sub = 0;
 		TxCtrlEPList[i].next = virt_to_phys(&TxCtrlEPList[i + 1]);
 	}
 	
-        CHECK_ALIGN(&TxCtrlEPList[i]);
+	CHECK_ALIGN(&TxCtrlEPList[i]);
 	TxCtrlEPList[i].hw_len = 0;
 	TxCtrlEPList[i].command = (IO_STATE(USB_EP_command, eol, yes) |
-                                   IO_FIELD(USB_EP_command, epid, i));
+				   IO_FIELD(USB_EP_command, epid, i));
 
 	TxCtrlEPList[i].sub = 0;
 	TxCtrlEPList[i].next = virt_to_phys(&TxCtrlEPList[0]);
@@ -766,23 +793,23 @@
 
 	DBFENTER;
 
-        /* Read comment at zout_buffer declaration for an explanation to this. */
+	/* Read comment at zout_buffer declaration for an explanation to this. */
 	TxIntrSB_zout.sw_len = 1;
 	TxIntrSB_zout.next = 0;
 	TxIntrSB_zout.buf = virt_to_phys(&zout_buffer[0]);
 	TxIntrSB_zout.command = (IO_FIELD(USB_SB_command, rem, 0) |
-                                 IO_STATE(USB_SB_command, tt, zout) |
-                                 IO_STATE(USB_SB_command, full, yes) |
-                                 IO_STATE(USB_SB_command, eot, yes) |
-                                 IO_STATE(USB_SB_command, eol, yes));
+				 IO_STATE(USB_SB_command, tt, zout) |
+				 IO_STATE(USB_SB_command, full, yes) |
+				 IO_STATE(USB_SB_command, eot, yes) |
+				 IO_STATE(USB_SB_command, eol, yes));
 
 	for (i = 0; i < (MAX_INTR_INTERVAL - 1); i++) {
-                CHECK_ALIGN(&TxIntrEPList[i]);
+		CHECK_ALIGN(&TxIntrEPList[i]);
 		TxIntrEPList[i].hw_len = 0;
 		TxIntrEPList[i].command =
 			(IO_STATE(USB_EP_command, eof, yes) |
-                         IO_STATE(USB_EP_command, enable, yes) |
-                         IO_FIELD(USB_EP_command, epid, INVALID_EPID));
+			 IO_STATE(USB_EP_command, enable, yes) |
+			 IO_FIELD(USB_EP_command, epid, INVALID_EPID));
 		TxIntrEPList[i].sub = virt_to_phys(&TxIntrSB_zout);
 		TxIntrEPList[i].next = virt_to_phys(&TxIntrEPList[i + 1]);
 	}
@@ -808,34 +835,34 @@
 	
 	DBFENTER;
 	
-        /* Read comment at zout_buffer declaration for an explanation to this. */
+	/* Read comment at zout_buffer declaration for an explanation to this. */
 	TxIsocSB_zout.sw_len = 1;
 	TxIsocSB_zout.next = 0;
 	TxIsocSB_zout.buf = virt_to_phys(&zout_buffer[0]);
 	TxIsocSB_zout.command = (IO_FIELD(USB_SB_command, rem, 0) |
-                                 IO_STATE(USB_SB_command, tt, zout) |
-                                 IO_STATE(USB_SB_command, full, yes) |
-                                 IO_STATE(USB_SB_command, eot, yes) |
-                                 IO_STATE(USB_SB_command, eol, yes));
-	
+				 IO_STATE(USB_SB_command, tt, zout) |
+				 IO_STATE(USB_SB_command, full, yes) |
+				 IO_STATE(USB_SB_command, eot, yes) |
+				 IO_STATE(USB_SB_command, eol, yes));
+
 	/* The last isochronous EP descriptor is a dummy. */
 	
 	for (i = 0; i < (NBR_OF_EPIDS - 1); i++) {
-                CHECK_ALIGN(&TxIsocEPList[i]);
+		CHECK_ALIGN(&TxIsocEPList[i]);
 		TxIsocEPList[i].hw_len = 0;
 		TxIsocEPList[i].command = IO_FIELD(USB_EP_command, epid, i);
 		TxIsocEPList[i].sub = 0;
 		TxIsocEPList[i].next = virt_to_phys(&TxIsocEPList[i + 1]);
 	}
 
-        CHECK_ALIGN(&TxIsocEPList[i]);
+	CHECK_ALIGN(&TxIsocEPList[i]);
 	TxIsocEPList[i].hw_len = 0;
 
 	/* Must enable the last EP descr to get eof interrupt. */
 	TxIsocEPList[i].command = (IO_STATE(USB_EP_command, enable, yes) |
-                                   IO_STATE(USB_EP_command, eof, yes) |
-                                   IO_STATE(USB_EP_command, eol, yes) |
-                                   IO_FIELD(USB_EP_command, epid, INVALID_EPID));
+				   IO_STATE(USB_EP_command, eof, yes) |
+				   IO_STATE(USB_EP_command, eol, yes) |
+				   IO_FIELD(USB_EP_command, epid, INVALID_EPID));
 	TxIsocEPList[i].sub = virt_to_phys(&TxIsocSB_zout);
 	TxIsocEPList[i].next = virt_to_phys(&TxIsocEPList[0]);
 
@@ -854,7 +881,7 @@
 
 	int epid;
 
-        /* Read 8.8.4 in Designer's Reference, "Removing an EP Descriptor from the List". */
+	/* Read 8.8.4 in Designer's Reference, "Removing an EP Descriptor from the List". */
 
 	DBFENTER;
 
@@ -864,28 +891,28 @@
 	curr_ep = first_ep;
 	
 
-        /* Note that this loop removes all EP descriptors with this epid. This assumes
-           that all EP descriptors belong to the one and only urb for this epid. */
+	/* Note that this loop removes all EP descriptors with this epid. This assumes
+	   that all EP descriptors belong to the one and only urb for this epid. */
 
 	do {
-                next_ep = (USB_EP_Desc_t *)phys_to_virt(curr_ep->next);
+		next_ep = (USB_EP_Desc_t *)phys_to_virt(curr_ep->next);
 
 		if (IO_EXTRACT(USB_EP_command, epid, next_ep->command) == epid) {
 
 			dbg_intr("Found EP to unlink for epid %d", epid);
-                        
-                        /* This is the one we should unlink. */
+
+			/* This is the one we should unlink. */
 			unlink_ep = next_ep;
 
-                        /* Actually unlink the EP from the DMA list. */
+			/* Actually unlink the EP from the DMA list. */
 			curr_ep->next = unlink_ep->next;
 
-                        /* Wait until the DMA is no longer at this descriptor. */
-                        while (*R_DMA_CH8_SUB2_EP == virt_to_phys(unlink_ep));
+			/* Wait until the DMA is no longer at this descriptor. */
+			while (*R_DMA_CH8_SUB2_EP == virt_to_phys(unlink_ep));
 
-                        /* Now we are free to remove it and its SB descriptor.
-                           Note that it is assumed here that there is only one sb in the
-                           sb list for this ep. */
+			/* Now we are free to remove it and its SB descriptor.
+			   Note that it is assumed here that there is only one sb in the
+			   sb list for this ep. */
 			kmem_cache_free(usb_desc_cache, phys_to_virt(unlink_ep->sub));
 			kmem_cache_free(usb_desc_cache, (USB_EP_Desc_t *)unlink_ep);
 		}
@@ -901,12 +928,12 @@
 	USB_EP_Desc_t *first_ep, *tmp_ep;
 
 	DBFENTER;
-        
+
 	first_ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB2_EP);
 	tmp_ep = first_ep;
 
-        /* What this does is simply to walk the list of interrupt
-           ep descriptors and enable those that are disabled. */
+	/* What this does is simply to walk the list of interrupt
+	   ep descriptors and enable those that are disabled. */
  
 	do {
 		if (IO_EXTRACT(USB_EP_command, epid, tmp_ep->command) == epid &&
@@ -919,7 +946,7 @@
 	} while (tmp_ep != first_ep);
 
 
-        DBFEXIT;
+	DBFEXIT;
 }
 
 static int etrax_rh_unlink_urb (urb_t *urb)
@@ -954,8 +981,8 @@
 	data |= (hc->rh.wPortChange_2) ? (1 << 2) : 0;
 
 	*((__u16 *)urb->transfer_buffer) = cpu_to_le16(data);
-        /* FIXME: Why is actual_length set to 1 when data is 2 bytes?
-           Since only 1 byte is used, why not declare data as __u8? */
+	/* FIXME: Why is actual_length set to 1 when data is 2 bytes?
+	   Since only 1 byte is used, why not declare data as __u8? */
 	urb->actual_length = 1;
 	urb->status = 0;
 
@@ -981,8 +1008,8 @@
 	init_timer(&hc->rh.rh_int_timer);
 	hc->rh.rh_int_timer.function = etrax_rh_int_timer_do;
 	hc->rh.rh_int_timer.data = (unsigned long)urb;
-        /* FIXME: Is the jiffies resolution enough? All intervals < 10 ms will be mapped
-           to 0, and the rest to the nearest lower 10 ms. */
+	/* FIXME: Is the jiffies resolution enough? All intervals < 10 ms will be mapped
+	   to 0, and the rest to the nearest lower 10 ms. */
 	hc->rh.rh_int_timer.expires = jiffies + ((HZ * hc->rh.interval) / 1000);
 	add_timer(&hc->rh.rh_int_timer);
 	
@@ -1016,14 +1043,14 @@
 	unsigned long flags;
 
 	DBFENTER;
-	
+
 	epid = etrax_usb_lookup_epid(urb);
 	if ((epid != -1)){
 		/* An epid that fits this urb has been found. */
 		DBFEXIT;
 		return epid;
 	}
-	
+
 	/* We must find and initiate a new epid for this urb. */
 	epid = etrax_usb_allocate_epid();
 
@@ -1040,12 +1067,12 @@
 	endpoint = usb_pipeendpoint(urb->pipe);
 	slow = usb_pipeslow(urb->pipe);
 	maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
-        if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
-                /* We want both IN and OUT control traffic to be put on the same EP/SB list. */
-                out_traffic = 1;
-        } else {
-                out_traffic = usb_pipeout(urb->pipe);
-        }
+	if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
+		/* We want both IN and OUT control traffic to be put on the same EP/SB list. */
+		out_traffic = 1;
+	} else {
+		out_traffic = usb_pipeout(urb->pipe);
+	}
 
 	save_flags(flags);
 	cli();
@@ -1072,14 +1099,14 @@
 	
 	restore_flags(flags);
 
-        if (out_traffic) {
-                set_bit(epid, (void *)&epid_out_traffic);
-        } else {
-                clear_bit(epid, (void *)&epid_out_traffic);
+	if (out_traffic) {
+		set_bit(epid, (void *)&epid_out_traffic);
+	} else {
+		clear_bit(epid, (void *)&epid_out_traffic);
 	}
 
 	dbg_epid("Setting up epid %d with devnum %d, endpoint %d and max_len %d (%s)",
-                 epid, devnum, endpoint, maxlen, out_traffic ? "OUT" : "IN");
+		 epid, devnum, endpoint, maxlen, out_traffic ? "OUT" : "IN");
 
 	DBFEXIT;
 	return epid;
@@ -1103,7 +1130,7 @@
 	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
 	nop();
 	while (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold));
-        /* This will, among other things, set the valid field to 0. */
+	/* This will, among other things, set the valid field to 0. */
 	*R_USB_EPT_DATA = 0;
 	restore_flags(flags);
 
@@ -1129,17 +1156,17 @@
 	endpoint = usb_pipeendpoint(urb->pipe);
 	slow = usb_pipeslow(urb->pipe);
 	maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
-        if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
-                /* We want both IN and OUT control traffic to be put on the same EP/SB list. */
-                out_traffic = 1;
-        } else {
-                out_traffic = usb_pipeout(urb->pipe);
-        }
-        
+	if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
+		/* We want both IN and OUT control traffic to be put on the same EP/SB list. */
+		out_traffic = 1;
+	} else {
+		out_traffic = usb_pipeout(urb->pipe);
+	}
+	
 	/* Step through att epids. */
 	for (i = 0; i < NBR_OF_EPIDS; i++) {
 		if (test_bit(i, (void *)&epid_usage_bitmask) &&
-                    test_bit(i, (void *)&epid_out_traffic) == out_traffic) {
+		    test_bit(i, (void *)&epid_out_traffic) == out_traffic) {
 
 			save_flags(flags);
 			cli();
@@ -1151,13 +1178,13 @@
 				restore_flags(flags);
 
 				if ((IO_MASK(R_USB_EPT_DATA_ISO, valid) & data) &&
-                                    (IO_EXTRACT(R_USB_EPT_DATA_ISO, dev, data) == devnum) &&
-                                    (IO_EXTRACT(R_USB_EPT_DATA_ISO, ep, data) == endpoint) &&
-                                    (IO_EXTRACT(R_USB_EPT_DATA_ISO, max_len, data) == maxlen)) {
-                                        dbg_epid("Found epid %d for devnum %d, endpoint %d (%s)",
-                                                 i, devnum, endpoint, out_traffic ? "OUT" : "IN");
-                                        DBFEXIT;
-                                        return i;
+				    (IO_EXTRACT(R_USB_EPT_DATA_ISO, dev, data) == devnum) &&
+				    (IO_EXTRACT(R_USB_EPT_DATA_ISO, ep, data) == endpoint) &&
+				    (IO_EXTRACT(R_USB_EPT_DATA_ISO, max_len, data) == maxlen)) {
+					dbg_epid("Found epid %d for devnum %d, endpoint %d (%s)",
+						 i, devnum, endpoint, out_traffic ? "OUT" : "IN");
+					DBFEXIT;
+					return i;
 				}
 			} else {
 				data = *R_USB_EPT_DATA;
@@ -1169,7 +1196,7 @@
 				    (IO_EXTRACT(R_USB_EPT_DATA, low_speed, data) == slow) &&
 				    (IO_EXTRACT(R_USB_EPT_DATA, max_len, data) == maxlen)) {	
 					dbg_epid("Found epid %d for devnum %d, endpoint %d (%s)",
-                                                 i, devnum, endpoint, out_traffic ? "OUT" : "IN");
+						 i, devnum, endpoint, out_traffic ? "OUT" : "IN");
 					DBFEXIT;
 					return i;
 				}
@@ -1206,22 +1233,30 @@
 	int ret = -EINVAL;
 	
 	DBFENTER;
+	if (!urb->dev || !urb->dev->bus) {
+		return -ENODEV;
+	}
+	if (usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)) <= 0) {
+		info("Submit urb to pipe with maxpacketlen 0, pipe 0x%X\n", urb->pipe);
+		return -EMSGSIZE;
+	}
 
-        if (!urb->dev || !urb->dev->bus) {
-                return -ENODEV;
-        }
 	if (urb->next != NULL) {
-                /* Is it possible for urb to be the head of a list of urbs (via the urb's
-                   next pointer), used for example in drivers for isochronous traffic.
-                   I haven't seen a device driver that relies on it being used for submit
-                   or unlink, so we warn about it and ignore it. */
-		warn("Urbs are linked, ignoring.");
-	}
-        if (urb->timeout) {
-                /* FIXME. */
-                warn("urb->timeout specified, ignoring.");
-        }
-        
+		/* Is it possible for urb to be the head of a list of urbs (via the urb's
+		   next pointer), used for example in drivers for isochronous traffic.
+		   I haven't seen a device driver that relies on it being used for submit
+		   or unlink, so we warn about it and ignore it. */
+		/* Actually this is used to do automatic resubmit of URBs for periodic
+		   traffic types (INTR, ISOC).  However, this feature is being deprecated
+		   (in 2.6) since it was never well understood, and also very bug prone
+		   so we still warn when encountering it. */
+		warn("Urbs are linked.");
+	}
+	if (urb->timeout) {
+		/* FIXME. */
+		warn("urb->timeout specified, ignoring.");
+	}
+	
 	hc = (etrax_hc_t*)urb->dev->bus->hcpriv;
 	
 	if (usb_pipedevice(urb->pipe) == hc->rh.devnum) {
@@ -1238,7 +1273,7 @@
 
 	} else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
 		int bustime;
-                
+
 		if (urb->bandwidth == 0) {
 			bustime = usb_check_bandwidth(urb->dev, urb);
 			if (bustime < 0) {
@@ -1246,16 +1281,16 @@
 			} else {
 				ret = etrax_usb_submit_intr_urb(urb);
 				if (ret == 0)
-                                        usb_claim_bandwidth(urb->dev, urb, bustime, 0);
+					usb_claim_bandwidth(urb->dev, urb, bustime, 0);
 			}
-                } else {
-                        /* Bandwidth already set. */
-                        ret = etrax_usb_submit_intr_urb(urb);
+		} else {
+			/* Bandwidth already set. */
+			ret = etrax_usb_submit_intr_urb(urb);
 		}
 
 	} else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
-                int bustime;
-        
+		int bustime;
+
 		if (urb->bandwidth == 0) {
 			bustime = usb_check_bandwidth(urb->dev, urb);
 			if (bustime < 0) {
@@ -1263,11 +1298,11 @@
 			} else {
 				ret = etrax_usb_submit_isoc_urb(urb);
 				if (ret == 0)
-                                        usb_claim_bandwidth(urb->dev, urb, bustime, 0);
+					usb_claim_bandwidth(urb->dev, urb, bustime, 0);
 			}
-                } else {
-                        /* Bandwidth already set. */
-                        ret = etrax_usb_submit_isoc_urb(urb);
+		} else {
+			/* Bandwidth already set. */
+			ret = etrax_usb_submit_isoc_urb(urb);
 		}
 	}
 
@@ -1278,159 +1313,231 @@
 
 static int etrax_usb_unlink_urb(urb_t *urb)
 {
-        etrax_hc_t *hc;
-        etrax_urb_priv_t *urb_priv;
-        int epid;
-
-	DBFENTER;
-        
-        if (!urb) {
-                return -EINVAL;
-        }        
-        if (!urb->dev || !urb->dev->bus) {
-                return -ENODEV;
-        }
-        if (!urb->hcpriv) {
-                /* This happens if a device driver calls unlink on an urb that
-                   was never submitted (lazy driver). */
-                return 0;
-        }
-        if (urb->transfer_flags & USB_ASYNC_UNLINK) {
-                /* FIXME. */
-                /* If USB_ASYNC_UNLINK is set:
-                   unlink
-                   move to a separate urb list
-                   call complete at next sof with ECONNRESET
-                   
-                   If not:
-                   wait 1 ms
-                   unlink
-                   call complete with ENOENT
-                */
-                warn("USB_ASYNC_UNLINK set, ignoring.");
-        }
-        
-        /* One might think that urb->status = -EINPROGRESS would be a requirement for unlinking,
-           but that doesn't work for interrupt and isochronous traffic since they are completed
-           repeatedly, and urb->status is set then. That may in itself be a bug though. */
-
-        hc = urb->dev->bus->hcpriv;
-        urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
-        epid = urb_priv->epid;
-
-        /* Set the urb status (synchronous unlink). */
-        urb->status = -ENOENT;
-        urb_priv->urb_state = UNLINK;
-        
+	etrax_hc_t *hc;
+	etrax_urb_priv_t *urb_priv;
+	int epid;
+	unsigned int flags;
+
+	DBFENTER;
+
+	if (!urb) {
+		return -EINVAL;
+	}
+
+	/* Disable interrupts here since a descriptor interrupt for the isoc epid
+	   will modify the sb list.  This could possibly be done more granular, but
+	   unlink_urb should not be used frequently anyway.
+	*/
+
+	save_flags(flags);
+	cli();
+	
+	if (!urb->dev || !urb->dev->bus) {
+		restore_flags(flags);
+		return -ENODEV;
+	}
+	if (!urb->hcpriv) {
+		/* This happens if a device driver calls unlink on an urb that
+		   was never submitted (lazy driver) or if the urb was completed
+		   while unlink was being called. */
+		restore_flags(flags);
+		return 0;
+	}
+	if (urb->transfer_flags & USB_ASYNC_UNLINK) {
+		/* FIXME. */
+		/* If USB_ASYNC_UNLINK is set:
+		   unlink
+		   move to a separate urb list
+		   call complete at next sof with ECONNRESET
+		   
+		   If not:
+		   wait 1 ms
+		   unlink
+		   call complete with ENOENT
+		*/
+		warn("USB_ASYNC_UNLINK set, ignoring.");
+	}
+	
+	/* One might think that urb->status = -EINPROGRESS would be a requirement for unlinking,
+	   but that doesn't work for interrupt and isochronous traffic since they are completed
+	   repeatedly, and urb->status is set then. That may in itself be a bug though. */
+
+	hc = urb->dev->bus->hcpriv;
+	urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+	epid = urb_priv->epid;
+
+	/* Set the urb status (synchronous unlink). */
+	urb->status = -ENOENT;
+	urb_priv->urb_state = UNLINK;
+
 	if (usb_pipedevice(urb->pipe) == hc->rh.devnum) {
 		int ret;
 		ret = etrax_rh_unlink_urb(urb);
 		DBFEXIT;
+		restore_flags(flags);
 		return ret;
 
 	} else if (usb_pipetype(urb->pipe) == PIPE_BULK) {
 
-                dbg_bulk("Unlink of bulk urb (0x%lx)", (unsigned long)urb);
+		dbg_bulk("Unlink of bulk urb (0x%lx)", (unsigned long)urb);
+
+		if (TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
+			/* The EP was enabled, disable it and wait. */
+			TxBulkEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
 
-                if (TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
-                        /* The EP was enabled, disable it and wait. */
-                        TxBulkEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
-                        
-                        /* Ah, the luxury of busy-wait. */
-                        while (*R_DMA_CH8_SUB0_EP == virt_to_phys(&TxBulkEPList[epid]));
-                }
+			/* Ah, the luxury of busy-wait. */
+			while (*R_DMA_CH8_SUB0_EP == virt_to_phys(&TxBulkEPList[epid]));
+		}
 		/* Kicking dummy list out of the party. */
 		TxBulkEPList[epid].next = virt_to_phys(&TxBulkEPList[(epid + 1) % NBR_OF_EPIDS]);
 
-        } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
+	} else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
+
+		dbg_ctrl("Unlink of ctrl urb (0x%lx)", (unsigned long)urb);
 
-                dbg_ctrl("Unlink of ctrl urb (0x%lx)", (unsigned long)urb);
+		if (TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
+			/* The EP was enabled, disable it and wait. */
+			TxCtrlEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
 
-                if (TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
-                        /* The EP was enabled, disable it and wait. */
-                        TxCtrlEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
-                        
-                        /* Ah, the luxury of busy-wait. */
-                        while (*R_DMA_CH8_SUB1_EP == virt_to_phys(&TxCtrlEPList[epid]));
-                }
+			/* Ah, the luxury of busy-wait. */
+			while (*R_DMA_CH8_SUB1_EP == virt_to_phys(&TxCtrlEPList[epid]));
+		}
 
 	} else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
 
-                dbg_intr("Unlink of intr urb (0x%lx)", (unsigned long)urb);
+		dbg_intr("Unlink of intr urb (0x%lx)", (unsigned long)urb);
 
-                /* Separate function because it's a tad more complicated. */
+		/* Separate function because it's a tad more complicated. */
 		etrax_usb_unlink_intr_urb(urb);
 
-        } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+	} else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+
+		dbg_isoc("Unlink of isoc urb (0x%lx)", (unsigned long)urb);
+
+		if (TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
+			/* The EP was enabled, disable it and wait. */
+			TxIsocEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
+			
+			/* Ah, the luxury of busy-wait. */
+			while (*R_DMA_CH8_SUB3_EP == virt_to_phys(&TxIsocEPList[epid]));
+		}
+	}
+
+	/* Note that we need to remove the urb from the urb list *before* removing its SB
+	   descriptors. (This means that the isoc eof handler might get a null urb when we
+	   are unlinking the last urb.) */
+
+	if (usb_pipetype(urb->pipe) == PIPE_BULK) {
+
+		urb_list_del(urb, epid);
+		TxBulkEPList[epid].sub = 0;
+		etrax_remove_from_sb_list(urb);
+
+	} else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
+
+		urb_list_del(urb, epid);
+		TxCtrlEPList[epid].sub = 0;
+		etrax_remove_from_sb_list(urb);
+
+	} else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
+
+		urb_list_del(urb, epid);
+		/* Sanity check (should never happen). */
+		assert(urb_list_empty(epid));
+
+		/* Release allocated bandwidth. */
+		usb_release_bandwidth(urb->dev, urb, 0);
+
+	} else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+
+		if (usb_pipeout(urb->pipe)) {
+
+			USB_SB_Desc_t *iter_sb, *prev_sb, *next_sb;
+
+			if (__urb_list_entry(urb, epid)) {	
+
+				urb_list_del(urb, epid);
+				iter_sb = TxIsocEPList[epid].sub ? phys_to_virt(TxIsocEPList[epid].sub) : 0;
+				prev_sb = 0;
+				while (iter_sb && (iter_sb != urb_priv->first_sb)) {
+					prev_sb = iter_sb;
+					iter_sb = iter_sb->next ? phys_to_virt(iter_sb->next) : 0;
+				}
+
+				if (iter_sb == 0) {
+					/* Unlink of the URB currently being transmitted. */
+					prev_sb = 0;
+					iter_sb = TxIsocEPList[epid].sub ? phys_to_virt(TxIsocEPList[epid].sub) : 0;
+				}
+
+				while (iter_sb && (iter_sb != urb_priv->last_sb)) {
+					iter_sb = iter_sb->next ? phys_to_virt(iter_sb->next) : 0;
+				}
+				if (iter_sb) {
+					next_sb = iter_sb->next ? phys_to_virt(iter_sb->next) : 0;
+				} else {
+					/* This should only happen if the DMA has completed
+					   processing the SB list for this EP while interrupts
+					   are disabled. */
+					dbg_isoc("Isoc urb not found, already sent?");
+					next_sb = 0;
+				}
+				if (prev_sb) {
+					prev_sb->next = next_sb ? virt_to_phys(next_sb) : 0;
+				} else {
+					TxIsocEPList[epid].sub = next_sb ? virt_to_phys(next_sb) : 0;
+				}
+
+				etrax_remove_from_sb_list(urb);
+				if (urb_list_empty(epid)) {
+					TxIsocEPList[epid].sub = 0;
+					dbg_isoc("Last isoc out urb epid %d", epid);
+				} else if (next_sb || prev_sb) {
+					dbg_isoc("Re-enable isoc out epid %d", epid);
+
+					TxIsocEPList[epid].hw_len = 0;
+					TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
+				} else {
+					TxIsocEPList[epid].sub = 0;
+					dbg_isoc("URB list non-empty and no SB list, EP disabled");
+				}
+			} else {
+				dbg_isoc("Urb 0x%p not found, completed already?", urb);
+			}
+		} else {
+
+			urb_list_del(urb, epid);
+
+			/* For in traffic there is only one SB descriptor for each EP even
+			   though there may be several urbs (all urbs point at the same SB). */
+			if (urb_list_empty(epid)) {
+				/* No more urbs, remove the SB. */
+				TxIsocEPList[epid].sub = 0;
+				etrax_remove_from_sb_list(urb);
+			} else {
+				TxIsocEPList[epid].hw_len = 0;
+				TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
+			}
+		}
+		/* Release allocated bandwidth. */
+		usb_release_bandwidth(urb->dev, urb, 1);
+	}
+	/* Free the epid if urb list is empty. */
+	if (urb_list_empty(epid)) {
+		etrax_usb_free_epid(epid);
+	}
+	restore_flags(flags);
 
-                dbg_isoc("Unlink of isoc urb (0x%lx)", (unsigned long)urb);
+	/* Must be done before calling completion handler. */
+	kfree(urb_priv);
+	urb->hcpriv = 0;
 
-                if (TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
-                        /* The EP was enabled, disable it and wait. */
-                        TxIsocEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
-                        
-                        /* Ah, the luxury of busy-wait. */
-                        while (*R_DMA_CH8_SUB3_EP == virt_to_phys(&TxIsocEPList[epid]));
-                }
-        }
-
-        /* Note that we need to remove the urb from the urb list *before* removing its SB
-           descriptors. (This means that the isoc eof handler might get a null urb when we
-           are unlinking the last urb.) */
-
-        urb_list_del(urb, epid);
-
-        if (usb_pipetype(urb->pipe) == PIPE_BULK) {
-
-                TxBulkEPList[epid].sub = 0;
-                etrax_remove_from_sb_list(urb);
-
-        } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
-
-                TxCtrlEPList[epid].sub = 0;
-                etrax_remove_from_sb_list(urb);
-
-        } else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
-
-                /* Sanity check (should never happen). */
-                assert(urb_list_empty(epid));
-
-                /* Release allocated bandwidth. */
-                usb_release_bandwidth(urb->dev, urb, 0);
-
-        } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
-
-                if (usb_pipeout(urb->pipe)) {
-                        /* FIXME: If the SB list isn't empty at this point, we need to set up 
-                           the EP descriptor again. */
-                } else {
-                        /* For in traffic there is only one SB descriptor for each EP even
-                           though there may be several urbs (all urbs point at the same SB). */
-                        if (urb_list_empty(epid)) {
-                                /* No more urbs, remove the SB. */
-                                TxIsocEPList[epid].sub = 0;
-                                etrax_remove_from_sb_list(urb);
-                        }
-                }
-                /* Release allocated bandwidth. */
-                usb_release_bandwidth(urb->dev, urb, 1);
-        } 
-        
-        /* Must be done before calling completion handler. */
-        kfree(urb_priv);
-        urb->hcpriv = 0;
-
-        if (urb->complete) {
-                urb->complete(urb);
-        }
-
-        /* Free the epid if urb list is empty. */
-        if (urb_list_empty(epid)) {
-                etrax_usb_free_epid(epid);
-        }
+	if (urb->complete) {
+		urb->complete(urb);
+	}
 
-        DBFEXIT;
-        return 0;
+	DBFEXIT;
+	return 0;
 }
 
 static int etrax_usb_get_frame_number(struct usb_device *usb_dev)
@@ -1458,7 +1565,7 @@
 {
 	DBFENTER;
 
-        /* This interrupt handler could be used when unlinking EP descriptors. */
+	/* This interrupt handler could be used when unlinking EP descriptors. */
 
 	if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub0_descr)) {
 		USB_EP_Desc_t *ep;
@@ -1484,331 +1591,624 @@
 		*R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start);
 	}
 	if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub1_descr)) {
+		urb_t *urb;
+		int epid;
+		etrax_urb_priv_t *urb_priv;
+		unsigned long int flags;
+
 		dbg_ctrl("dma8_sub1_descr (CTRL) intr.");
 		*R_DMA_CH8_SUB1_CLR_INTR = IO_STATE(R_DMA_CH8_SUB1_CLR_INTR, clr_descr, do);
+
+		/* The complete callback gets called so we cli. */
+		save_flags(flags);
+		cli();
+
+		for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) {
+			if ((TxCtrlEPList[epid].sub == 0) ||
+			    (epid == DUMMY_EPID) ||
+			    (epid == INVALID_EPID)) {
+				/* Nothing here to see. */
+				continue;
+			}
+
+			/* Get the first urb (if any). */
+			urb = urb_list_first(epid);
+
+			if (urb) {
+
+				/* Sanity check. */
+				assert(usb_pipetype(urb->pipe) == PIPE_CONTROL);
+
+				urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+				assert(urb_priv);
+
+				if (urb_priv->urb_state == WAITING_FOR_DESCR_INTR) {
+					assert(!(TxCtrlEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable)));
+
+					etrax_usb_complete_urb(urb, 0);
+				}
+			}
+		}
+		restore_flags(flags);		
 	}
 	if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub2_descr)) {
 		dbg_intr("dma8_sub2_descr (INTR) intr.");
 		*R_DMA_CH8_SUB2_CLR_INTR = IO_STATE(R_DMA_CH8_SUB2_CLR_INTR, clr_descr, do);
 	}
 	if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub3_descr)) {
-		dbg_isoc("dma8_sub3_descr (ISOC) intr.");
-		*R_DMA_CH8_SUB3_CLR_INTR = IO_STATE(R_DMA_CH8_SUB3_CLR_INTR, clr_descr, do);
-	}
-        
-	DBFEXIT;
-}
+		urb_t *urb;
+		int epid;
+		int epid_done;
+		etrax_urb_priv_t *urb_priv;
+		USB_SB_Desc_t *sb_desc;
 
-static void etrax_usb_rx_interrupt(int irq, void *vhc, struct pt_regs *regs)
-{
-	urb_t *urb;
-	etrax_urb_priv_t *urb_priv;
-	int epid = 0;
-        unsigned long flags;
+		usb_isoc_complete_data_t *comp_data = NULL;
+
+		/* One or more isoc out transfers are done. */
+		dbg_isoc("dma8_sub3_descr (ISOC) intr.");
 
-        /* Isoc diagnostics. */
-        static int curr_fm = 0;
-        static int prev_fm = 0;
+		/* For each isoc out EP search for the first sb_desc with the intr flag
+		   set.  This descriptor must be the last packet from an URB.  Then
+		   traverse the URB list for the EP until the URB with urb_priv->last_sb
+		   matching the intr-marked sb_desc is found.  All URBs before this have
+		   been sent.
+		*/
+
+		for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) {
+			/* Skip past epids with no SB lists, epids used for in traffic,
+			   and special (dummy, invalid) epids. */
+			if ((TxIsocEPList[epid].sub == 0) ||
+			    (test_bit(epid, (void *)&epid_out_traffic) == 0) ||
+			    (epid == DUMMY_EPID) ||
+			    (epid == INVALID_EPID)) {
+				/* Nothing here to see. */
+				continue;
+			}
+			sb_desc = phys_to_virt(TxIsocEPList[epid].sub);
 
-        DBFENTER;
+			/* Find the last descriptor of the currently active URB for this ep.
+			   This is the first descriptor in the sub list marked for a descriptor
+			   interrupt. */
+			while (sb_desc && !IO_EXTRACT(USB_SB_command, intr, sb_desc->command)) {
+				sb_desc = sb_desc->next ? phys_to_virt(sb_desc->next) : 0;
+			}
+			assert(sb_desc);
 
-        /* Clear this interrupt. */
-	*R_DMA_CH9_CLR_INTR = IO_STATE(R_DMA_CH9_CLR_INTR, clr_eop, do);
+			dbg_isoc("Check epid %d, sub 0x%p, SB 0x%p",
+				 epid,
+				 phys_to_virt(TxIsocEPList[epid].sub),
+				 sb_desc);
 
-        /* Note that this while loop assumes that all packets span only
-           one rx descriptor. */
+			epid_done = 0;
 
-        /* The reason we cli here is that we call the driver's callback functions. */
-        save_flags(flags);
-        cli();
+			/* Get the first urb (if any). */
+			urb = urb_list_first(epid);
+			assert(urb);
 
+			while (urb && !epid_done) {
 
- 	while (myNextRxDesc->status & IO_MASK(USB_IN_status, eop)) {
+				/* Sanity check. */
+				assert(usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS);
 
-                epid = IO_EXTRACT(USB_IN_status, epid, myNextRxDesc->status);
-                urb = urb_list_first(epid);
+				if (!usb_pipeout(urb->pipe)) {
+					/* descr interrupts are generated only for out pipes. */
+					epid_done = 1;
+					continue;
+				}
 
-                //printk("eop for epid %d, first urb 0x%lx\n", epid, (unsigned long)urb);
-                
-                if (!urb) {
-                        err("No urb for epid %d in rx interrupt", epid);
-			__dump_ept_data(epid);
-                        goto skip_out;
-                }
+				urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+				assert(urb_priv);
 
-                /* Note that we cannot indescriminately assert(usb_pipein(urb->pipe)) since
-                   ctrl pipes are not. */
-                
-		if (myNextRxDesc->status & IO_MASK(USB_IN_status, error)) {
-			__u32 r_usb_ept_data;
+				if (sb_desc != urb_priv->last_sb) {
 
-			warn("error in rx desc->status, epid %d, first urb = 0x%lx", 
-                             epid, (unsigned long)urb);
-			__dump_in_desc(myNextRxDesc);
-
-                        *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); 
-                        nop();
-                        r_usb_ept_data = *R_USB_EPT_DATA;
-			warn("R_USB_EPT_DATA for epid %d = 0x%x", epid, r_usb_ept_data);
-			warn("R_USB_STATUS = 0x%x", *R_USB_STATUS);
+					/* This urb has been sent. */
+					dbg_isoc("out URB 0x%p sent", urb);
 
-                        etrax_usb_complete_urb(urb, -EPROTO);
-			goto skip_out;
-		}
+					urb_priv->urb_state = TRANSFER_DONE;
 
-                urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
-                assert(urb_priv);
-                
-                if ((usb_pipetype(urb->pipe) == PIPE_BULK) || 
-                    (usb_pipetype(urb->pipe) == PIPE_CONTROL) ||
-                    (usb_pipetype(urb->pipe) == PIPE_INTERRUPT)) {
-
-                        if (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata)) {
-                                /* We get nodata for empty data transactions, and the rx descriptor's
-                                   hw_len field is not valid in that case. No data to copy in other 
-                                   words. */
-                        } else {
-                                /* Make sure the data fits in the buffer. */
-                                assert(urb_priv->rx_offset + myNextRxDesc->hw_len 
-                                       <= urb->transfer_buffer_length);
-
-                                memcpy(urb->transfer_buffer + urb_priv->rx_offset,
-                                       phys_to_virt(myNextRxDesc->buf), myNextRxDesc->hw_len);	
-                                urb_priv->rx_offset += myNextRxDesc->hw_len;
-                        }
-
-                        if (myNextRxDesc->status & IO_MASK(USB_IN_status, eot)) {
-                                etrax_usb_complete_urb(urb, 0);
-                        }
-
-                } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
-                        
-                        iso_packet_descriptor_t *packet;
-
-                        if (urb_priv->urb_state == UNLINK) {
-                                info("Ignoring rx data for urb being unlinked.");
-                                goto skip_out;
-                        } else if (urb_priv->urb_state == NOT_STARTED) {
-                                info("What? Got rx data for urb that isn't started?");
-                                goto skip_out;
-                        }
-                                
-                        packet = &urb->iso_frame_desc[urb_priv->isoc_packet_counter];
-                        packet->status = 0;
-
-                         if (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata)) {
-                                /* We get nodata for empty data transactions, and the rx descriptor's
-                                   hw_len field is not valid in that case. We copy 0 bytes however to
-                                   stay in synch. */
-                                packet->actual_length = 0;                                
-                        } else {
-                                packet->actual_length = myNextRxDesc->hw_len;
-                                /* Make sure the data fits in the buffer. */
-                                assert(packet->actual_length <= packet->length);
-                                
-                                memcpy(urb->transfer_buffer + packet->offset,
-                                       phys_to_virt(myNextRxDesc->buf), packet->actual_length);
-                        }
-
-                        /* Increment the packet counter. */
-                        urb_priv->isoc_packet_counter++;	
-                        
-                        /* Note that we don't care about the eot field in the rx descriptor's status.
-                           It will always be set for isoc traffic. */
-			if (urb->number_of_packets == urb_priv->isoc_packet_counter) {
+				} else if ((sb_desc == urb_priv->last_sb) &&
+					   !(TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable))) {
 
-                                /* Out-of-synch diagnostics. */
-                                curr_fm = (*R_USB_FM_NUMBER & 0x7ff);
-                                if (((prev_fm + urb_priv->isoc_packet_counter) % (0x7ff + 1)) != curr_fm) {
-                                        warn("Out of synch? Previous frame = %d, current frame = %d",
-                                             prev_fm, curr_fm);
-                                }
-                                prev_fm = curr_fm;
+					assert((sb_desc->command & IO_MASK(USB_SB_command, eol)) == IO_STATE(USB_SB_command, eol, yes));
+					assert(sb_desc->next == 0);
 
-				/* Complete the urb with status OK. */
-				etrax_usb_complete_isoc_urb(urb, 0);
+					dbg_isoc("out URB 0x%p last in list, epid disabled", urb);
+					TxIsocEPList[epid].sub = 0;
+					TxIsocEPList[epid].hw_len = 0;
+					urb_priv->urb_state = TRANSFER_DONE;
+					
+					epid_done = 1;
 
-				/* Must set this to 0 since this urb is still active after
-				   completion. */
-				urb_priv->isoc_packet_counter = 0;
+				} else {
+					epid_done = 1;
+				}
+				if (!epid_done) {
+					urb = urb_list_next(urb, epid);
+				}
 			}
-                }
-                
-	skip_out:
 
-                /* DMA IN cache bug. Flush the DMA IN buffer from the cache. (struct etrax_dma_descr
-		   has the same layout as USB_IN_Desc for the relevant fields.) */
-                prepare_rx_descriptor((struct etrax_dma_descr*)myNextRxDesc);
+		}
 
-		myPrevRxDesc = myNextRxDesc;
-		myPrevRxDesc->command |= IO_MASK(USB_IN_command, eol);
-		myLastRxDesc->command &= ~IO_MASK(USB_IN_command, eol);
-		myLastRxDesc = myPrevRxDesc;
+		*R_DMA_CH8_SUB3_CLR_INTR = IO_STATE(R_DMA_CH8_SUB3_CLR_INTR, clr_descr, do);
 
-		myNextRxDesc->status = 0;
-		myNextRxDesc = phys_to_virt(myNextRxDesc->next);
+		comp_data = (usb_isoc_complete_data_t*)kmem_cache_alloc(isoc_compl_cache, SLAB_ATOMIC);
+		assert(comp_data != NULL);
+
+		comp_data->usb_bh.sync = 0;
+		comp_data->usb_bh.routine = etrax_usb_isoc_descr_interrupt_bottom_half;
+		comp_data->usb_bh.data = comp_data;
+
+		queue_task(&comp_data->usb_bh, &tq_immediate);
+		mark_bh(IMMEDIATE_BH);
 	}
 
-        restore_flags(flags);
-        
-        DBFEXIT;
-        
+	DBFEXIT;
 }
 
-
-/* This function will unlink the SB descriptors associated with this urb. */
-static int etrax_remove_from_sb_list(urb_t *urb)
+static void etrax_usb_isoc_descr_interrupt_bottom_half(void *data)
 {
-	USB_SB_Desc_t *next_sb, *first_sb, *last_sb;
+	usb_isoc_complete_data_t *comp_data = (usb_isoc_complete_data_t*)data;
+	
+	urb_t *urb;
+	int epid;
+	int epid_done;
 	etrax_urb_priv_t *urb_priv;
-	int i = 0;
 
 	DBFENTER;
+	
+	dbg_isoc("dma8_sub3_descr (ISOC) bottom half.");
 
-	urb_priv = (etrax_urb_priv_t *)urb->hcpriv; 
-	assert(urb_priv);
+	for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) {
+		unsigned long flags;
 
-        /* Just a sanity check. Since we don't fiddle with the DMA list the EP descriptor
-	   doesn't really need to be disabled, it's just that we expect it to be. */
-        if (usb_pipetype(urb->pipe) == PIPE_BULK) {
-                assert(!(TxBulkEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable)));
-        } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
-                assert(!(TxCtrlEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable)));
-        } 
+		save_flags(flags);
+		cli();
 
-	first_sb = urb_priv->first_sb;
-	last_sb = urb_priv->last_sb;
-	
-        assert(first_sb);
-        assert(last_sb);
-        
-	while (first_sb != last_sb) {
-		next_sb = (USB_SB_Desc_t *)phys_to_virt(first_sb->next);
-		kmem_cache_free(usb_desc_cache, first_sb);
-		first_sb = next_sb;
-		i++;
-	}
-	kmem_cache_free(usb_desc_cache, last_sb);
-	i++;
-	dbg_sb("%d SB descriptors freed", i);
-	/* Compare i with urb->number_of_packets for Isoc traffic. 
-	   Should be same when calling unlink_urb */
-	
-	DBFEXIT;
-	
-	return i;
-}
+		epid_done = 0;
 
-static int etrax_usb_submit_bulk_urb(urb_t *urb)
-{
-	int epid;
-        int empty;
-        unsigned long flags;
-        
-	DBFENTER;
+		/* The descriptor interrupt handler has marked all transmitted isoch. out
+		   URBs with TRANSFER_DONE.  Now we traverse all epids and for all that
+ 		   have isoch. out traffic traverse its URB list and complete the
+		   transmitted URB.
+		*/
 
-        /* Epid allocation, empty check and list add must be protected. 
-           Read about this in etrax_usb_submit_ctrl_urb. */
+		while (!epid_done) {
 
-        spin_lock_irqsave(&urb_list_lock, flags);
-	epid = etrax_usb_setup_epid(urb);
-	if (epid == -1) {
-		DBFEXIT;
-                spin_unlock_irqrestore(&urb_list_lock, flags);
-		return -ENOMEM;
-	}
-        empty = urb_list_empty(epid);
-        urb_list_add(urb, epid);
-        spin_unlock_irqrestore(&urb_list_lock, flags);
-
-        /* USB_QUEUE_BULK is UHCI-specific, but we warn anyway. */
-        if (!(urb->transfer_flags & USB_QUEUE_BULK) && !empty) {
-                warn("USB_QUEUE_BULK is not set and urb queue is not empty, ignoring.");
-        }
+			/* Get the first urb (if any). */
+			urb = urb_list_first(epid);
+			if (urb == 0) {
+				epid_done = 1;
+				continue;
+			}
 
-        dbg_bulk("Adding bulk %s urb 0x%lx to %s list, epid %d", 
-                 usb_pipein(urb->pipe) ? "IN" : "OUT", (unsigned long)urb, empty ? "empty" : "", epid);
+			if (usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS) {
+					epid_done = 1;
+					continue;
+			}
+			
+			if (!usb_pipeout(urb->pipe)) {
+				/* descr interrupts are generated only for out pipes. */
+				epid_done = 1;
+				continue;
+			}
 
-        /* Mark the urb as being in progress. */
-	urb->status = -EINPROGRESS;
+			dbg_isoc("Check epid %d, SB 0x%p", epid, (char*)TxIsocEPList[epid].sub);
 
-        if (empty) {
-                etrax_usb_add_to_bulk_sb_list(urb, epid);
-        }
-	        
-	DBFEXIT;
+			urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+			assert(urb_priv);
 
-	return 0;
-}
+			if (urb_priv->urb_state == TRANSFER_DONE) {
+				int i;
+				iso_packet_descriptor_t *packet;
+
+				/* This urb has been sent. */
+				dbg_isoc("Completing isoc out URB 0x%p", urb);
+
+				for (i = 0; i < urb->number_of_packets; i++) {
+					packet = &urb->iso_frame_desc[i];
+					packet->status = 0;
+					packet->actual_length = packet->length;
+				}
 
-static void etrax_usb_add_to_bulk_sb_list(urb_t *urb, int epid)
-{
-	USB_SB_Desc_t *sb_desc;
-	etrax_urb_priv_t *urb_priv;
-	unsigned long flags;
-	char maxlen;
+				etrax_usb_complete_isoc_urb(urb, 0);
 
-	DBFENTER;
+				if (urb_list_empty(epid)) {
+					etrax_usb_free_epid(epid);
+					epid_done = 1;
+				}
+			} else {
+				epid_done = 1;
+			}
+		}
+		restore_flags(flags);
 
-	dbg_bulk("etrax_usb_add_to_bulk_sb_list, urb 0x%lx", (unsigned long)urb);
+	}
+	kmem_cache_free(isoc_compl_cache, comp_data);
 
-	maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+	DBFEXIT;
+}
+
+
+
+static void etrax_usb_rx_interrupt(int irq, void *vhc, struct pt_regs *regs)
+{
+	urb_t *urb;
+	etrax_urb_priv_t *urb_priv;
+	int epid = 0;
+	unsigned long flags;
+
+	/* Isoc diagnostics. */
+	static int curr_fm = 0;
+	static int prev_fm = 0;
+
+	DBFENTER;
+
+	/* Clear this interrupt. */
+	*R_DMA_CH9_CLR_INTR = IO_STATE(R_DMA_CH9_CLR_INTR, clr_eop, do);
+
+	/* Note that this while loop assumes that all packets span only
+	   one rx descriptor. */
+
+	/* The reason we cli here is that we call the driver's callback functions. */
+	save_flags(flags);
+	cli();
+
+	while (myNextRxDesc->status & IO_MASK(USB_IN_status, eop)) {
+
+		epid = IO_EXTRACT(USB_IN_status, epid, myNextRxDesc->status);
+		urb = urb_list_first(epid);
+
+		//printk("eop for epid %d, first urb 0x%lx\n", epid, (unsigned long)urb);
+
+		if (!urb) {
+			err("No urb for epid %d in rx interrupt", epid);
+			__dump_ept_data(epid);
+			goto skip_out;
+		}
+
+		/* Note that we cannot indescriminately assert(usb_pipein(urb->pipe)) since
+		   ctrl pipes are not. */
+
+		if (myNextRxDesc->status & IO_MASK(USB_IN_status, error)) {
+			__u32 r_usb_ept_data;
+			int no_error = 0;
+
+			assert(test_bit(epid, (void *)&epid_usage_bitmask));
+
+			*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); 
+			nop();
+			if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+				r_usb_ept_data = *R_USB_EPT_DATA_ISO;
+
+				if ((r_usb_ept_data & IO_MASK(R_USB_EPT_DATA_ISO, valid)) &&
+				    (IO_EXTRACT(R_USB_EPT_DATA_ISO, error_code, r_usb_ept_data) == 0) &&
+				    (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata))) {
+					/* Not an error, just a failure to receive an expected iso
+					   in packet in this frame.  This is not documented
+					   in the designers reference.
+					*/
+					no_error++;
+				} else {
+					warn("R_USB_EPT_DATA_ISO for epid %d = 0x%x", epid, r_usb_ept_data);
+				}
+			} else {
+				r_usb_ept_data = *R_USB_EPT_DATA;
+				warn("R_USB_EPT_DATA for epid %d = 0x%x", epid, r_usb_ept_data);
+			}
+
+			if (!no_error){
+				warn("error in rx desc->status, epid %d, first urb = 0x%lx", 
+				     epid, (unsigned long)urb);
+				__dump_in_desc(myNextRxDesc);
+
+				warn("R_USB_STATUS = 0x%x", *R_USB_STATUS);
+
+				/* Check that ept was disabled when error occurred. */
+				switch (usb_pipetype(urb->pipe)) {
+				case PIPE_BULK:
+					assert(!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)));
+					break;
+				case PIPE_CONTROL:
+					assert(!(TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)));
+					break;
+				case PIPE_INTERRUPT:
+					assert(!(TxIntrEPList[epid].command & IO_MASK(USB_EP_command, enable)));
+					break;
+				case PIPE_ISOCHRONOUS:	
+					assert(!(TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable)));
+					break;
+				default:
+					warn("etrax_usb_rx_interrupt: bad pipetype %d in urb 0x%p",
+					     usb_pipetype(urb->pipe),
+					     urb);
+				}
+				etrax_usb_complete_urb(urb, -EPROTO);
+				goto skip_out;
+			}
+		}
+
+		urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+		assert(urb_priv);
+
+		if ((usb_pipetype(urb->pipe) == PIPE_BULK) || 
+		    (usb_pipetype(urb->pipe) == PIPE_CONTROL) ||
+		    (usb_pipetype(urb->pipe) == PIPE_INTERRUPT)) {
+
+			if (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata)) {
+				/* We get nodata for empty data transactions, and the rx descriptor's
+				   hw_len field is not valid in that case. No data to copy in other 
+				   words. */
+			} else {
+				/* Make sure the data fits in the buffer. */
+				assert(urb_priv->rx_offset + myNextRxDesc->hw_len 
+				       <= urb->transfer_buffer_length);
+
+				memcpy(urb->transfer_buffer + urb_priv->rx_offset,
+				       phys_to_virt(myNextRxDesc->buf), myNextRxDesc->hw_len);	
+				urb_priv->rx_offset += myNextRxDesc->hw_len;
+			}
+
+			if (myNextRxDesc->status & IO_MASK(USB_IN_status, eot)) {
+				if ((usb_pipetype(urb->pipe) == PIPE_CONTROL) &&
+				    ((TxCtrlEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable)) ==
+				     IO_STATE(USB_EP_command, enable, yes))) {
+					/* The EP is still enabled, so the OUT packet used to ack
+					   the in data is probably not processed yet.  If the EP
+					   sub pointer has not moved beyond urb_priv->last_sb mark
+					   it for a descriptor interrupt and complete the urb in
+					   the descriptor interrupt handler.
+					*/
+					USB_SB_Desc_t *sub = TxCtrlEPList[urb_priv->epid].sub ? phys_to_virt(TxCtrlEPList[urb_priv->epid].sub) : 0;
+
+					while ((sub != NULL) && (sub != urb_priv->last_sb)) {
+						sub = sub->next ? phys_to_virt(sub->next) : 0;
+					}
+					if (sub != NULL) {
+						/* The urb has not been fully processed. */
+						urb_priv->urb_state = WAITING_FOR_DESCR_INTR;
+					} else {
+						warn("(CTRL) epid enabled and urb (0x%p) processed, ep->sub=0x%p", urb, (char*)TxCtrlEPList[urb_priv->epid].sub);
+						etrax_usb_complete_urb(urb, 0);
+					}
+				} else {
+					etrax_usb_complete_urb(urb, 0);
+				}
+			}
+
+		} else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+			
+			iso_packet_descriptor_t *packet;
+
+			if (urb_priv->urb_state == UNLINK) {
+				info("Ignoring rx data for urb being unlinked.");
+				goto skip_out;
+			} else if (urb_priv->urb_state == NOT_STARTED) {
+				info("What? Got rx data for urb that isn't started?");
+				goto skip_out;
+			}
+
+			packet = &urb->iso_frame_desc[urb_priv->isoc_packet_counter];
+			packet->status = 0;
+
+			if (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata)) {
+				/* We get nodata for empty data transactions, and the rx descriptor's
+				   hw_len field is not valid in that case. We copy 0 bytes however to
+				   stay in synch. */
+				packet->actual_length = 0;
+			} else {
+				packet->actual_length = myNextRxDesc->hw_len;
+				/* Make sure the data fits in the buffer. */
+				assert(packet->actual_length <= packet->length);
+				memcpy(urb->transfer_buffer + packet->offset,
+				       phys_to_virt(myNextRxDesc->buf), packet->actual_length);
+			}
+
+			/* Increment the packet counter. */
+			urb_priv->isoc_packet_counter++;
+
+			/* Note that we don't care about the eot field in the rx descriptor's status.
+			   It will always be set for isoc traffic. */
+			if (urb->number_of_packets == urb_priv->isoc_packet_counter) {
+
+				/* Out-of-synch diagnostics. */
+				curr_fm = (*R_USB_FM_NUMBER & 0x7ff);
+				if (((prev_fm + urb_priv->isoc_packet_counter) % (0x7ff + 1)) != curr_fm) {
+					/* This test is wrong, if there is more than one isoc
+					   in endpoint active it will always calculate wrong
+					   since prev_fm is shared by all endpoints.
+					   
+					   FIXME Make this check per URB using urb->start_frame.
+					*/ 
+					dbg_isoc("Out of synch? Previous frame = %d, current frame = %d",
+						 prev_fm, curr_fm);
+
+				}
+				prev_fm = curr_fm;
+
+				/* Complete the urb with status OK. */
+				etrax_usb_complete_isoc_urb(urb, 0);
+			}
+		}
+
+	skip_out:
+
+		/* DMA IN cache bug. Flush the DMA IN buffer from the cache. (struct etrax_dma_descr
+		   has the same layout as USB_IN_Desc for the relevant fields.) */
+		prepare_rx_descriptor((struct etrax_dma_descr*)myNextRxDesc);
+
+		myPrevRxDesc = myNextRxDesc;
+		myPrevRxDesc->command |= IO_MASK(USB_IN_command, eol);
+		myLastRxDesc->command &= ~IO_MASK(USB_IN_command, eol);
+		myLastRxDesc = myPrevRxDesc;
+
+		myNextRxDesc->status = 0;
+		myNextRxDesc = phys_to_virt(myNextRxDesc->next);
+	}
+
+	restore_flags(flags);
+
+	DBFEXIT;
+
+}
+
+
+/* This function will unlink the SB descriptors associated with this urb. */
+static int etrax_remove_from_sb_list(urb_t *urb)
+{
+	USB_SB_Desc_t *next_sb, *first_sb, *last_sb;
+	etrax_urb_priv_t *urb_priv;
+	int i = 0;
+
+	DBFENTER;
+
+	urb_priv = (etrax_urb_priv_t *)urb->hcpriv; 
+	assert(urb_priv);
+
+	/* Just a sanity check. Since we don't fiddle with the DMA list the EP descriptor
+	   doesn't really need to be disabled, it's just that we expect it to be. */
+	if (usb_pipetype(urb->pipe) == PIPE_BULK) {
+		assert(!(TxBulkEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable)));
+	} else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
+		assert(!(TxCtrlEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable)));
+	}
+
+	first_sb = urb_priv->first_sb;
+	last_sb = urb_priv->last_sb;
+	
+	assert(first_sb);
+	assert(last_sb);
 	
+	while (first_sb != last_sb) {
+		next_sb = (USB_SB_Desc_t *)phys_to_virt(first_sb->next);
+		kmem_cache_free(usb_desc_cache, first_sb);
+		first_sb = next_sb;
+		i++;
+	}
+	kmem_cache_free(usb_desc_cache, last_sb);
+	i++;
+	dbg_sb("%d SB descriptors freed", i);
+	/* Compare i with urb->number_of_packets for Isoc traffic. 
+	   Should be same when calling unlink_urb */
+
+	DBFEXIT;
+
+	return i;
+}
+
+static int etrax_usb_submit_bulk_urb(urb_t *urb)
+{
+	int epid;
+	int empty;
+	unsigned long flags;
+	etrax_urb_priv_t *urb_priv;
+
+	DBFENTER;
+
+	/* Epid allocation, empty check and list add must be protected. 
+	   Read about this in etrax_usb_submit_ctrl_urb. */
+
+	spin_lock_irqsave(&urb_list_lock, flags);
+	epid = etrax_usb_setup_epid(urb);
+	if (epid == -1) {
+		DBFEXIT;
+		spin_unlock_irqrestore(&urb_list_lock, flags);
+		return -ENOMEM;
+	}
+	empty = urb_list_empty(epid);
+	urb_list_add(urb, epid);
+	spin_unlock_irqrestore(&urb_list_lock, flags);
+
+	/* USB_QUEUE_BULK is UHCI-specific, but we warn anyway. */
+	if (!(urb->transfer_flags & USB_QUEUE_BULK) && !empty) {
+		warn("USB_QUEUE_BULK is not set and urb queue is not empty, ignoring.");
+	}
+
+	dbg_bulk("Adding bulk %s urb 0x%lx to %s list, epid %d", 
+		 usb_pipein(urb->pipe) ? "IN" : "OUT", (unsigned long)urb, empty ? "empty" : "", epid);
+
+	/* Mark the urb as being in progress. */
+	urb->status = -EINPROGRESS;
+
+	/* Setup the hcpriv data. */
 	urb_priv = kmalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG);
 	assert(urb_priv != NULL);
-        /* This sets rx_offset to 0. */
-        memset(urb_priv, 0, sizeof(etrax_urb_priv_t));
-        
+	/* This sets rx_offset to 0. */
+	memset(urb_priv, 0, sizeof(etrax_urb_priv_t));
+	urb_priv->urb_state = NOT_STARTED;
+	urb->hcpriv = urb_priv;
+
+	if (empty) {
+		etrax_usb_add_to_bulk_sb_list(urb, epid);
+	}
+		
+	DBFEXIT;
+
+	return 0;
+}
+
+static void etrax_usb_add_to_bulk_sb_list(urb_t *urb, int epid)
+{
+	USB_SB_Desc_t *sb_desc;
+	etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+	unsigned long flags;
+	char maxlen;
+
+	DBFENTER;
+
+	dbg_bulk("etrax_usb_add_to_bulk_sb_list, urb 0x%lx", (unsigned long)urb);
+
+	maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));	
+	
 	sb_desc = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
 	assert(sb_desc != NULL);
 	memset(sb_desc, 0, sizeof(USB_SB_Desc_t));
 
 
 	if (usb_pipeout(urb->pipe)) {
-                
+		
 		dbg_bulk("Grabbing bulk OUT, urb 0x%lx, epid %d", (unsigned long)urb, epid);
-                
-                /* This is probably a sanity check of the bulk transaction length
-                   not being larger than 64 kB. */
+		
+		/* This is probably a sanity check of the bulk transaction length
+		   not being larger than 64 kB. */
 		if (urb->transfer_buffer_length > 0xffff) {
 			panic("urb->transfer_buffer_length > 0xffff");
 		}
 
 		sb_desc->sw_len = urb->transfer_buffer_length;
 
-                /* The rem field is don't care if it's not a full-length transfer, so setting
-                   it shouldn't hurt. Also, rem isn't used for OUT traffic. */
+		/* The rem field is don't care if it's not a full-length transfer, so setting
+		   it shouldn't hurt. Also, rem isn't used for OUT traffic. */
 		sb_desc->command = (IO_FIELD(USB_SB_command, rem, 0) |
-                                    IO_STATE(USB_SB_command, tt, out) |
-                                    IO_STATE(USB_SB_command, eot, yes) |
-                                    IO_STATE(USB_SB_command, eol, yes));
-
-                /* The full field is set to yes, even if we don't actually check that this is
-                   a full-length transfer (i.e., that transfer_buffer_length % maxlen = 0).
-                   Setting full prevents the USB controller from sending an empty packet in
-                   that case.  However, if USB_ZERO_PACKET was set we want that. */
-                if (!(urb->transfer_flags & USB_ZERO_PACKET)) {
-                        sb_desc->command |= IO_STATE(USB_SB_command, full, yes);
-                }
+				    IO_STATE(USB_SB_command, tt, out) |
+				    IO_STATE(USB_SB_command, eot, yes) |
+				    IO_STATE(USB_SB_command, eol, yes));
+
+		/* The full field is set to yes, even if we don't actually check that this is
+		   a full-length transfer (i.e., that transfer_buffer_length % maxlen = 0).
+		   Setting full prevents the USB controller from sending an empty packet in
+		   that case.  However, if USB_ZERO_PACKET was set we want that. */
+		if (!(urb->transfer_flags & USB_ZERO_PACKET)) {
+			sb_desc->command |= IO_STATE(USB_SB_command, full, yes);
+		}
 
 		sb_desc->buf = virt_to_phys(urb->transfer_buffer);
 		sb_desc->next = 0;
 		
 	} else if (usb_pipein(urb->pipe)) {
 
-                dbg_bulk("Grabbing bulk IN, urb 0x%lx, epid %d", (unsigned long)urb, epid);
+		dbg_bulk("Grabbing bulk IN, urb 0x%lx, epid %d", (unsigned long)urb, epid);
 
 		sb_desc->sw_len = urb->transfer_buffer_length ?
 			(urb->transfer_buffer_length - 1) / maxlen + 1 : 0;
 		
-                /* The rem field is don't care if it's not a full-length transfer, so setting
-                   it shouldn't hurt. */
+		/* The rem field is don't care if it's not a full-length transfer, so setting
+		   it shouldn't hurt. */
 		sb_desc->command =
 			(IO_FIELD(USB_SB_command, rem,
-                                  urb->transfer_buffer_length % maxlen) |
-                         IO_STATE(USB_SB_command, tt, in) |
-                         IO_STATE(USB_SB_command, eot, yes) |
-                         IO_STATE(USB_SB_command, eol, yes));
+				  urb->transfer_buffer_length % maxlen) |
+			 IO_STATE(USB_SB_command, tt, in) |
+			 IO_STATE(USB_SB_command, eot, yes) |
+			 IO_STATE(USB_SB_command, eol, yes));
 		
 		sb_desc->buf = 0;
 		sb_desc->next = 0;
@@ -1825,46 +2225,46 @@
 	cli();
 
 	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); 
-        nop();
+	nop();
 
-        /* FIXME: Is this a special case since the hold field is checked,
-           or should we check hold in a lot of other cases as well? */
+	/* FIXME: Is this a special case since the hold field is checked,
+	   or should we check hold in a lot of other cases as well? */
 	if (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold)) {
 		panic("Hold was set in %s", __FUNCTION__);
 	}
 
-        /* Reset error counters (regardless of which direction this traffic is). */
+	/* Reset error counters (regardless of which direction this traffic is). */
 	*R_USB_EPT_DATA &=
 		~(IO_MASK(R_USB_EPT_DATA, error_count_in) |
 		  IO_MASK(R_USB_EPT_DATA, error_count_out));
 	
-        /* Software must preset the toggle bits. */
+	/* Software must preset the toggle bits. */
 	if (usb_pipeout(urb->pipe)) {
 		char toggle =
-                        usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
+			usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
 		*R_USB_EPT_DATA &= ~IO_MASK(R_USB_EPT_DATA, t_out);
 		*R_USB_EPT_DATA |= IO_FIELD(R_USB_EPT_DATA, t_out, toggle);
 	} else {
 		char toggle =
-                        usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
+			usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
 		*R_USB_EPT_DATA &= ~IO_MASK(R_USB_EPT_DATA, t_in);
 		*R_USB_EPT_DATA |= IO_FIELD(R_USB_EPT_DATA, t_in, toggle);
 	}
-                
-        /* Assert that the EP descriptor is disabled. */
-        assert(!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)));
-
-        /* The reason we set the EP's sub pointer directly instead of
-           walking the SB list and linking it last in the list is that we only
-           have one active urb at a time (the rest are queued). */
+
+	/* Assert that the EP descriptor is disabled. */
+	assert(!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)));
+
+	/* The reason we set the EP's sub pointer directly instead of
+	   walking the SB list and linking it last in the list is that we only
+	   have one active urb at a time (the rest are queued). */
 
 	/* Note that we cannot have interrupts running when we have set the SB descriptor
 	   but the EP is not yet enabled.  If a bulk eot happens for another EP, we will
 	   find this EP disabled and with a SB != 0, which will make us think that it's done. */
 	TxBulkEPList[epid].sub = virt_to_phys(sb_desc);
 	TxBulkEPList[epid].hw_len = 0;
-        /* Note that we don't have to fill in the ep_id field since this
-           was done when we allocated the EP descriptors in init_tx_bulk_ep. */
+	/* Note that we don't have to fill in the ep_id field since this
+	   was done when we allocated the EP descriptors in init_tx_bulk_ep. */
 
 	/* Check if the dummy list is already with us (if several urbs were queued). */
 	if (TxBulkEPList[epid].next != virt_to_phys(&TxBulkDummyEPList[epid][0])) {
@@ -1880,14 +2280,14 @@
 		   32-bit aligned). */
 		TxBulkEPList[epid].next = virt_to_phys(&TxBulkDummyEPList[epid][0]);
 	}
-        /* Enable the EP descr. */
+	/* Enable the EP descr. */
 	dbg_bulk("Enabling bulk EP for urb 0x%lx, epid %d", (unsigned long)urb, epid);
-        TxBulkEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
+	TxBulkEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
 
 	/* Everything is set up, safe to enable interrupts again. */
 	restore_flags(flags);
 
-        /* If the DMA bulk channel isn't running, we need to restart it if it
+	/* If the DMA bulk channel isn't running, we need to restart it if it
 	   has stopped at the last EP descriptor (DMA stopped because there was
 	   no more traffic) or if it has stopped at a dummy EP with the intr flag
 	   set (DMA stopped because we were too slow in inserting new traffic). */
@@ -1907,7 +2307,7 @@
 			mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL);
 		}
 	}
-        
+	
 	DBFEXIT;
 }
 
@@ -1915,22 +2315,22 @@
 {
 	etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
 	int epid = urb_priv->epid;
-        unsigned long flags;
-        
+	unsigned long flags;
+	
 	DBFENTER;
 
-        if (status)
-                warn("Completing bulk urb with status %d.", status);
+	if (status)
+		warn("Completing bulk urb with status %d.", status);
 
 	dbg_bulk("Completing bulk urb 0x%lx for epid %d", (unsigned long)urb, epid);
 
 	/* Update the urb list. */	
-        urb_list_del(urb, epid);
+	urb_list_del(urb, epid);
 
-        /* For an IN pipe, we always set the actual length, regardless of whether there was
-           an error or not (which means the device driver can use the data if it wants to). */
+	/* For an IN pipe, we always set the actual length, regardless of whether there was
+	   an error or not (which means the device driver can use the data if it wants to). */
 	if (usb_pipein(urb->pipe)) {
-                urb->actual_length = urb_priv->rx_offset;
+		urb->actual_length = urb_priv->rx_offset;
 	} else {
 		/* Set actual_length for OUT urbs also; the USB mass storage driver seems
 		   to want that. We wouldn't know of any partial writes if there was an error. */
@@ -1941,63 +2341,63 @@
 		}
 	}
 
-        /* FIXME: Is there something of the things below we shouldn't do if there was an error?
-           Like, maybe we shouldn't toggle the toggle bits, or maybe we shouldn't insert more traffic. */
+	/* FIXME: Is there something of the things below we shouldn't do if there was an error?
+	   Like, maybe we shouldn't toggle the toggle bits, or maybe we shouldn't insert more traffic. */
 
 	save_flags(flags);
 	cli();
 		
 	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); 
-        nop();
+	nop();
 
-        /* We need to fiddle with the toggle bits because the hardware doesn't do it for us. */
+	/* We need to fiddle with the toggle bits because the hardware doesn't do it for us. */
 	if (usb_pipeout(urb->pipe)) {
 		char toggle =
-                        IO_EXTRACT(R_USB_EPT_DATA, t_out, *R_USB_EPT_DATA);
+			IO_EXTRACT(R_USB_EPT_DATA, t_out, *R_USB_EPT_DATA);
 		usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
 			      usb_pipeout(urb->pipe), toggle);
 	} else {
 		char toggle =
-                        IO_EXTRACT(R_USB_EPT_DATA, t_in, *R_USB_EPT_DATA);
+			IO_EXTRACT(R_USB_EPT_DATA, t_in, *R_USB_EPT_DATA);
 		usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
 			      usb_pipeout(urb->pipe), toggle);
 	}
-        restore_flags(flags);
+	restore_flags(flags);
 
-        /* Remember to free the SBs. */
+	/* Remember to free the SBs. */
 	etrax_remove_from_sb_list(urb);
 	kfree(urb_priv);
-        urb->hcpriv = 0;
-        
+	urb->hcpriv = 0;
+	
 	/* If there are any more urb's in the list we'd better start sending */
 	if (!urb_list_empty(epid)) {
 
-                urb_t *new_urb;
+		urb_t *new_urb;
 
-                /* Get the first urb. */
-                new_urb = urb_list_first(epid);
+		/* Get the first urb. */
+		new_urb = urb_list_first(epid);
 		assert(new_urb);
 
-                dbg_bulk("More bulk for epid %d", epid);
+		dbg_bulk("More bulk for epid %d", epid);
 
 		etrax_usb_add_to_bulk_sb_list(new_urb, epid);
 	}
 
-        urb->status = status;
+	urb->status = status;
 
-        /* We let any non-zero status from the layer above have precedence. */
-        if (status == 0) {
-                /* USB_DISABLE_SPD means that short reads (shorter than the endpoint's max length)
-                   is to be treated as an error. */
-                if (urb->transfer_flags & USB_DISABLE_SPD) {
-                        if (usb_pipein(urb->pipe) && 
-                            (urb->actual_length != 
-                             usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)))) {
-                                urb->status = -EREMOTEIO;
-                        } 
-                }
-        }
-        
+	/* We let any non-zero status from the layer above have precedence. */
+	if (status == 0) {
+		/* USB_DISABLE_SPD means that short reads (shorter than the endpoint's max length)
+		   is to be treated as an error. */
+		if (urb->transfer_flags & USB_DISABLE_SPD) {
+			if (usb_pipein(urb->pipe) && 
+			    (urb->actual_length != 
+			     usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)))) {
+				urb->status = -EREMOTEIO;
+			} 
+		}
+	}
+	
 	if (urb->complete) {
 		urb->complete(urb);
 	}
@@ -2010,8 +2410,8 @@
 		   Must set sub pointer to 0, since we look at the sub pointer when handling
 		   the bulk eot interrupt. */
 
-                dbg_bulk("No bulk for epid %d", epid);
-                
+		dbg_bulk("No bulk for epid %d", epid);
+		
 		TxBulkEPList[epid].sub = 0;
 
 		/* Unlink the dummy list. */
@@ -2032,43 +2432,52 @@
 static int etrax_usb_submit_ctrl_urb(urb_t *urb)
 {
 	int epid;
-        int empty;
-        unsigned long flags;
-        
+	int empty;
+	unsigned long flags;
+	etrax_urb_priv_t *urb_priv;
+	
 	DBFENTER;
 
-        /* FIXME: Return -ENXIO if there is already a queued urb for this endpoint? */
+	/* FIXME: Return -ENXIO if there is already a queued urb for this endpoint? */
 
-        /* Epid allocation, empty check and list add must be protected.
+	/* Epid allocation, empty check and list add must be protected.
 
-           Epid allocation because if we find an existing epid for this endpoint an urb might be
-           completed (emptying the list) before we add the new urb to the list, causing the epid
-           to be de-allocated. We would then start the transfer with an invalid epid -> epid attn.
+	   Epid allocation because if we find an existing epid for this endpoint an urb might be
+	   completed (emptying the list) before we add the new urb to the list, causing the epid
+	   to be de-allocated. We would then start the transfer with an invalid epid -> epid attn.
 
-           Empty check and add because otherwise we might conclude that the list is not empty,
-           after which it becomes empty before we add the new urb to the list, causing us not to 
-           insert the new traffic into the SB list. */
+	   Empty check and add because otherwise we might conclude that the list is not empty,
+	   after which it becomes empty before we add the new urb to the list, causing us not to 
+	   insert the new traffic into the SB list. */
 
-        spin_lock_irqsave(&urb_list_lock, flags);
+	spin_lock_irqsave(&urb_list_lock, flags);
 	epid = etrax_usb_setup_epid(urb);
 	if (epid == -1) {
-                spin_unlock_irqrestore(&urb_list_lock, flags);
+		spin_unlock_irqrestore(&urb_list_lock, flags);
 		DBFEXIT;
 		return -ENOMEM;
 	}
-        empty = urb_list_empty(epid);
-        urb_list_add(urb, epid);
-        spin_unlock_irqrestore(&urb_list_lock, flags);
+	empty = urb_list_empty(epid);
+	urb_list_add(urb, epid);
+	spin_unlock_irqrestore(&urb_list_lock, flags);
 
-        dbg_ctrl("Adding ctrl urb 0x%lx to %s list, epid %d", 
-                 (unsigned long)urb, empty ? "empty" : "", epid);
+	dbg_ctrl("Adding ctrl urb 0x%lx to %s list, epid %d", 
+		 (unsigned long)urb, empty ? "empty" : "", epid);
 
-        /* Mark the urb as being in progress. */
+	/* Mark the urb as being in progress. */
 	urb->status = -EINPROGRESS;
 
-        if (empty) {
-                etrax_usb_add_to_ctrl_sb_list(urb, epid);
-        }
+	/* Setup the hcpriv data. */
+	urb_priv = kmalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG);
+	assert(urb_priv != NULL);
+	/* This sets rx_offset to 0. */
+	memset(urb_priv, 0, sizeof(etrax_urb_priv_t));
+	urb_priv->urb_state = NOT_STARTED;
+	urb->hcpriv = urb_priv;
+
+	if (empty) {
+		etrax_usb_add_to_ctrl_sb_list(urb, epid);
+	}
 
 	DBFEXIT;
 
@@ -2081,20 +2490,15 @@
 	USB_SB_Desc_t *sb_desc_data;
 	USB_SB_Desc_t *sb_desc_status;
 
-	etrax_urb_priv_t *urb_priv;
+	etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
 
 	unsigned long flags;
 	char maxlen;
-	
+
 	DBFENTER;
 
 	maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
-	
-	urb_priv = kmalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG);
-	assert(urb_priv != NULL);
-        /* This sets rx_offset to 0. */
-        memset(urb_priv, 0, sizeof(etrax_urb_priv_t));
-        
+
 	sb_desc_setup = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
 	assert(sb_desc_setup != NULL);
 	sb_desc_status = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
@@ -2103,45 +2507,46 @@
 	/* Initialize the mandatory setup SB descriptor (used only in control transfers) */	
 	sb_desc_setup->sw_len = 8;
 	sb_desc_setup->command = (IO_FIELD(USB_SB_command, rem, 0) |
-                                  IO_STATE(USB_SB_command, tt, setup) |
-                                  IO_STATE(USB_SB_command, full, yes) |
-                                  IO_STATE(USB_SB_command, eot, yes));
-	
+				  IO_STATE(USB_SB_command, tt, setup) |
+				  IO_STATE(USB_SB_command, full, yes) |
+				  IO_STATE(USB_SB_command, eot, yes));
+
 	sb_desc_setup->buf = virt_to_phys(urb->setup_packet);
 
 	if (usb_pipeout(urb->pipe)) {
-                dbg_ctrl("Transfer for epid %d is OUT", epid);
+		dbg_ctrl("Transfer for epid %d is OUT", epid);
 
 		/* If this Control OUT transfer has an optional data stage we add an OUT token
 		   before the mandatory IN (status) token, hence the reordered SB list */
-		
+
 		sb_desc_setup->next = virt_to_phys(sb_desc_status);
 		if (urb->transfer_buffer) {
 
 			dbg_ctrl("This OUT transfer has an extra data stage");
-			
-                        sb_desc_data = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
-                        assert(sb_desc_data != NULL);
-                        
-                        sb_desc_setup->next = virt_to_phys(sb_desc_data);
-                        
-                        sb_desc_data->sw_len = urb->transfer_buffer_length;
-                        sb_desc_data->command = (IO_STATE(USB_SB_command, tt, out) |
-                                                 IO_STATE(USB_SB_command, full, yes) |
-                                                 IO_STATE(USB_SB_command, eot, yes));
-                        sb_desc_data->buf = virt_to_phys(urb->transfer_buffer);
-                        sb_desc_data->next = virt_to_phys(sb_desc_status);
+
+			sb_desc_data = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
+			assert(sb_desc_data != NULL);
+
+			sb_desc_setup->next = virt_to_phys(sb_desc_data);
+
+			sb_desc_data->sw_len = urb->transfer_buffer_length;
+			sb_desc_data->command = (IO_STATE(USB_SB_command, tt, out) |
+						 IO_STATE(USB_SB_command, full, yes) |
+						 IO_STATE(USB_SB_command, eot, yes));
+			sb_desc_data->buf = virt_to_phys(urb->transfer_buffer);
+			sb_desc_data->next = virt_to_phys(sb_desc_status);
 		}
-		
+
 		sb_desc_status->sw_len = 1;
 		sb_desc_status->command = (IO_FIELD(USB_SB_command, rem, 0) |
-                                           IO_STATE(USB_SB_command, tt, in) |
-                                           IO_STATE(USB_SB_command, eot, yes) |
-                                           IO_STATE(USB_SB_command, eol, yes));
+					   IO_STATE(USB_SB_command, tt, in) |
+					   IO_STATE(USB_SB_command, eot, yes) |
+					   IO_STATE(USB_SB_command, intr, yes) |
+					   IO_STATE(USB_SB_command, eol, yes));
 
 		sb_desc_status->buf = 0;
 		sb_desc_status->next = 0;
-                
+
 	} else if (usb_pipein(urb->pipe)) {
 
 		dbg_ctrl("Transfer for epid %d is IN", epid);
@@ -2150,56 +2555,57 @@
 
 		sb_desc_data = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
 		assert(sb_desc_data != NULL);
-		
+
 		sb_desc_setup->next = virt_to_phys(sb_desc_data);
 
 		sb_desc_data->sw_len = urb->transfer_buffer_length ?
 			(urb->transfer_buffer_length - 1) / maxlen + 1 : 0;
 		dbg_ctrl("sw_len got %d", sb_desc_data->sw_len);
-		
+
 		sb_desc_data->command =
 			(IO_FIELD(USB_SB_command, rem,
-                                  urb->transfer_buffer_length % maxlen) |
-                         IO_STATE(USB_SB_command, tt, in) |
-                         IO_STATE(USB_SB_command, eot, yes));
-		
+				  urb->transfer_buffer_length % maxlen) |
+			 IO_STATE(USB_SB_command, tt, in) |
+			 IO_STATE(USB_SB_command, eot, yes));
+
 		sb_desc_data->buf = 0;
 		sb_desc_data->next = virt_to_phys(sb_desc_status);
 
-                /* Read comment at zout_buffer declaration for an explanation to this. */
+		/* Read comment at zout_buffer declaration for an explanation to this. */
 		sb_desc_status->sw_len = 1;
 		sb_desc_status->command = (IO_FIELD(USB_SB_command, rem, 0) |
-                                           IO_STATE(USB_SB_command, tt, zout) |
-                                           IO_STATE(USB_SB_command, full, yes) |
-                                           IO_STATE(USB_SB_command, eot, yes) |
-                                           IO_STATE(USB_SB_command, eol, yes));
+					   IO_STATE(USB_SB_command, tt, zout) |
+					   IO_STATE(USB_SB_command, full, yes) |
+					   IO_STATE(USB_SB_command, eot, yes) |
+					   IO_STATE(USB_SB_command, intr, yes) |
+					   IO_STATE(USB_SB_command, eol, yes));
 				
 		sb_desc_status->buf = virt_to_phys(&zout_buffer[0]);
 		sb_desc_status->next = 0;
 	}
-	
+
 	urb_priv->first_sb = sb_desc_setup;
 	urb_priv->last_sb = sb_desc_status;
 	urb_priv->epid = epid;
 
-	urb->hcpriv = urb_priv;
-	
-	/* Reset toggle bits and reset error count, remeber to di and ei */
+	urb_priv->urb_state = STARTED;
+
+	/* Reset toggle bits and reset error count, remember to di and ei */
 	/* Warning: it is possible that this locking doesn't work with bottom-halves */
 
 	save_flags(flags);
 	cli();
 
 	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); 
-        nop();
+	nop();
 	if (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold)) {
 		panic("Hold was set in %s", __FUNCTION__);
 	}
-	
 
-        /* FIXME: Compare with etrax_usb_add_to_bulk_sb_list where the toggle bits
-           are set to a specific value. Why the difference? Read "Transfer and Toggle Bits
-           in Designer's Reference, p. 8 - 11. */
+
+	/* FIXME: Compare with etrax_usb_add_to_bulk_sb_list where the toggle bits
+	   are set to a specific value. Why the difference? Read "Transfer and Toggle Bits
+	   in Designer's Reference, p. 8 - 11. */
 	*R_USB_EPT_DATA &=
 		~(IO_MASK(R_USB_EPT_DATA, error_count_in) |
 		  IO_MASK(R_USB_EPT_DATA, error_count_out) |
@@ -2210,79 +2616,79 @@
 	   (i.e. we don't check the sub pointer on an eot interrupt like we do for bulk traffic). */
 	restore_flags(flags);
 
-        /* Assert that the EP descriptor is disabled. */
-        assert(!(TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)));
+	/* Assert that the EP descriptor is disabled. */
+	assert(!(TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)));
 
 	/* Set up and enable the EP descriptor. */
 	TxCtrlEPList[epid].sub = virt_to_phys(sb_desc_setup);
 	TxCtrlEPList[epid].hw_len = 0;
 	TxCtrlEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
 
-        /* We start the DMA sub channel without checking if it's running or not, because:
-           1) If it's already running, issuing the start command is a nop.
-           2) We avoid a test-and-set race condition. */
-        *R_DMA_CH8_SUB1_CMD = IO_STATE(R_DMA_CH8_SUB1_CMD, cmd, start);
-	
+	/* We start the DMA sub channel without checking if it's running or not, because:
+	   1) If it's already running, issuing the start command is a nop.
+	   2) We avoid a test-and-set race condition. */
+	*R_DMA_CH8_SUB1_CMD = IO_STATE(R_DMA_CH8_SUB1_CMD, cmd, start);
+
 	DBFEXIT;
 }
 
 static void etrax_usb_complete_ctrl_urb(urb_t *urb, int status)
 {
-        etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
-        int epid = urb_priv->epid;
+	etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+	int epid = urb_priv->epid;
 
 	DBFENTER;
 
-        if (status)
-                warn("Completing ctrl urb with status %d.", status);
+	if (status)
+		warn("Completing ctrl urb with status %d.", status);
 
-        dbg_ctrl("Completing ctrl epid %d, urb 0x%lx", epid, (unsigned long)urb);
+	dbg_ctrl("Completing ctrl epid %d, urb 0x%lx", epid, (unsigned long)urb);
 
 	/* Remove this urb from the list. */
-        urb_list_del(urb, epid);
+	urb_list_del(urb, epid);
 
-        /* For an IN pipe, we always set the actual length, regardless of whether there was
-           an error or not (which means the device driver can use the data if it wants to). */
+	/* For an IN pipe, we always set the actual length, regardless of whether there was
+	   an error or not (which means the device driver can use the data if it wants to). */
 	if (usb_pipein(urb->pipe)) {
-                urb->actual_length = urb_priv->rx_offset;
+		urb->actual_length = urb_priv->rx_offset;
 	}
 
-        /* FIXME: Is there something of the things below we shouldn't do if there was an error?
-           Like, maybe we shouldn't insert more traffic. */
+	/* FIXME: Is there something of the things below we shouldn't do if there was an error?
+	   Like, maybe we shouldn't insert more traffic. */
 
 	/* Remember to free the SBs. */
 	etrax_remove_from_sb_list(urb);
 	kfree(urb_priv);
-        urb->hcpriv = 0;
-        
+	urb->hcpriv = 0;
+
 	/* If there are any more urbs in the list we'd better start sending. */
 	if (!urb_list_empty(epid)) {
-                urb_t *new_urb;
-                
-                /* Get the first urb. */
-                new_urb = urb_list_first(epid);
+		urb_t *new_urb;
+		
+		/* Get the first urb. */
+		new_urb = urb_list_first(epid);
 		assert(new_urb);
 
-                dbg_ctrl("More ctrl for epid %d, first urb = 0x%lx", epid, (unsigned long)new_urb);
+		dbg_ctrl("More ctrl for epid %d, first urb = 0x%lx", epid, (unsigned long)new_urb);
 
 		etrax_usb_add_to_ctrl_sb_list(new_urb, epid);
 	}
 	
 	urb->status = status;
 
-        /* We let any non-zero status from the layer above have precedence. */
-        if (status == 0) {
-                /* USB_DISABLE_SPD means that short reads (shorter than the endpoint's max length)
-                   is to be treated as an error. */
-                if (urb->transfer_flags & USB_DISABLE_SPD) {
-                        if (usb_pipein(urb->pipe) && 
-                            (urb->actual_length != 
-                             usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)))) {
-                                urb->status = -EREMOTEIO;
-                        } 
-                }
-        }
-        
+	/* We let any non-zero status from the layer above have precedence. */
+	if (status == 0) {
+		/* USB_DISABLE_SPD means that short reads (shorter than the endpoint's max length)
+		   is to be treated as an error. */
+		if (urb->transfer_flags & USB_DISABLE_SPD) {
+			if (usb_pipein(urb->pipe) && 
+			    (urb->actual_length != 
+			     usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)))) {
+				urb->status = -EREMOTEIO;
+			} 
+		}
+	}
+
 	if (urb->complete) {
 		urb->complete(urb);
 	}
@@ -2291,7 +2697,7 @@
 		/* No more traffic. Time to clean up. */
 		etrax_usb_free_epid(epid);
 		/* Must set sub pointer to 0. */
-                dbg_ctrl("No ctrl for epid %d", epid);
+		dbg_ctrl("No ctrl for epid %d", epid);
 		TxCtrlEPList[epid].sub = 0;
 	}
 
@@ -2304,14 +2710,14 @@
 	int epid;
 	
 	DBFENTER;
-        
-        if (usb_pipeout(urb->pipe)) {
-                /* Unsupported transfer type.
-                   We don't support interrupt out traffic. (If we do, we can't support
-                   intervals for neither in or out traffic, but are forced to schedule all
-                   interrupt traffic in one frame.) */
-                return -EINVAL;
-        }
+	
+	if (usb_pipeout(urb->pipe)) {
+		/* Unsupported transfer type.
+		   We don't support interrupt out traffic. (If we do, we can't support
+		   intervals for neither in or out traffic, but are forced to schedule all
+		   interrupt traffic in one frame.) */
+		return -EINVAL;
+	}
 
 	epid = etrax_usb_setup_epid(urb);
 	if (epid == -1) {
@@ -2320,16 +2726,16 @@
 	}
 
 	if (!urb_list_empty(epid)) {
-                /* There is already a queued urb for this endpoint. */
-                etrax_usb_free_epid(epid);
-                return -ENXIO;
-        }
-        
-        urb->status = -EINPROGRESS;
+		/* There is already a queued urb for this endpoint. */
+		etrax_usb_free_epid(epid);
+		return -ENXIO;
+	}
+	
+	urb->status = -EINPROGRESS;
 
-        dbg_intr("Add intr urb 0x%lx, to list, epid %d", (unsigned long)urb, epid);
+	dbg_intr("Add intr urb 0x%lx, to list, epid %d", (unsigned long)urb, epid);
 
-        urb_list_add(urb, epid);
+	urb_list_add(urb, epid);
 	etrax_usb_add_to_intr_sb_list(urb, epid);
 
 	return 0;
@@ -2346,7 +2752,7 @@
 	char maxlen;
 	int interval;
 	int i;
-        
+	
 	etrax_urb_priv_t *urb_priv;
 	
 	DBFENTER;
@@ -2356,16 +2762,16 @@
 
 	urb_priv = kmalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG);
 	assert(urb_priv != NULL);
-        memset(urb_priv, 0, sizeof(etrax_urb_priv_t));
+	memset(urb_priv, 0, sizeof(etrax_urb_priv_t));
 	urb->hcpriv = urb_priv;
 
 	first_ep = &TxIntrEPList[0];
 
 	/* Round of the interval to 2^n, it is obvious that this code favours
 	   smaller numbers, but that is actually a good thing */
-        /* FIXME: The "rounding error" for larger intervals will be quite
-           large. For in traffic this shouldn't be a problem since it will only
-           mean that we "poll" more often. */
+	/* FIXME: The "rounding error" for larger intervals will be quite
+	   large. For in traffic this shouldn't be a problem since it will only
+	   mean that we "poll" more often. */
 	for (i = 0; interval; i++) {
 		interval = interval >> 1;
 	}
@@ -2383,32 +2789,32 @@
 				USB_SB_Desc_t *sb_desc;
 
 				dbg_intr("Inserting EP for epid %d", epid);
-                                
+				
 				ep_desc = (USB_EP_Desc_t *)
 					kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
 				sb_desc = (USB_SB_Desc_t *)
 					kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
 				assert(ep_desc != NULL);
-                                CHECK_ALIGN(ep_desc);
+				CHECK_ALIGN(ep_desc);
 				assert(sb_desc != NULL);
 				
 				ep_desc->sub = virt_to_phys(sb_desc);
 				ep_desc->hw_len = 0;
 				ep_desc->command = (IO_FIELD(USB_EP_command, epid, epid) |
-                                                    IO_STATE(USB_EP_command, enable, yes));
+						    IO_STATE(USB_EP_command, enable, yes));
 
 
-                                /* Round upwards the number of packets of size maxlen
-                                   that this SB descriptor should receive. */
-                                sb_desc->sw_len = urb->transfer_buffer_length ?
-                                        (urb->transfer_buffer_length - 1) / maxlen + 1 : 0;
-                                sb_desc->next = 0;
-                                sb_desc->buf = 0;
-                                sb_desc->command = 
-                                        (IO_FIELD(USB_SB_command, rem, urb->transfer_buffer_length % maxlen) |
-                                         IO_STATE(USB_SB_command, tt, in) |
-                                         IO_STATE(USB_SB_command, eot, yes) |
-                                         IO_STATE(USB_SB_command, eol, yes));
+				/* Round upwards the number of packets of size maxlen
+				   that this SB descriptor should receive. */
+				sb_desc->sw_len = urb->transfer_buffer_length ?
+					(urb->transfer_buffer_length - 1) / maxlen + 1 : 0;
+				sb_desc->next = 0;
+				sb_desc->buf = 0;
+				sb_desc->command = 
+					(IO_FIELD(USB_SB_command, rem, urb->transfer_buffer_length % maxlen) |
+					 IO_STATE(USB_SB_command, tt, in) |
+					 IO_STATE(USB_SB_command, eot, yes) |
+					 IO_STATE(USB_SB_command, eol, yes));
 				
 				ep_desc->next = tmp_ep->next;
 				tmp_ep->next = virt_to_phys(ep_desc);
@@ -2419,14 +2825,14 @@
 	} while (tmp_ep != first_ep);
 
 
-        /* Note that first_sb/last_sb doesn't apply to interrupt traffic. */
-        urb_priv->epid = epid;
+	/* Note that first_sb/last_sb doesn't apply to interrupt traffic. */
+	urb_priv->epid = epid;
 
-        /* We start the DMA sub channel without checking if it's running or not, because:
-           1) If it's already running, issuing the start command is a nop.
-           2) We avoid a test-and-set race condition. */
-        *R_DMA_CH8_SUB2_CMD = IO_STATE(R_DMA_CH8_SUB2_CMD, cmd, start);
-        
+	/* We start the DMA sub channel without checking if it's running or not, because:
+	   1) If it's already running, issuing the start command is a nop.
+	   2) We avoid a test-and-set race condition. */
+	*R_DMA_CH8_SUB2_CMD = IO_STATE(R_DMA_CH8_SUB2_CMD, cmd, start);
+	
 	DBFEXIT;
 }
 
@@ -2434,40 +2840,40 @@
 
 static void etrax_usb_complete_intr_urb(urb_t *urb, int status)
 {
-        etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
-        int epid = urb_priv->epid;
-        
-	DBFENTER;
-
-        if (status)
-                warn("Completing intr urb with status %d.", status);
-
-        dbg_intr("Completing intr epid %d, urb 0x%lx", epid, (unsigned long)urb);
-
-        urb->status = status;
-        urb->actual_length = urb_priv->rx_offset;
-
-        dbg_intr("interrupt urb->actual_length = %d", urb->actual_length);
-
-        /* We let any non-zero status from the layer above have precedence. */
-        if (status == 0) {
-                /* USB_DISABLE_SPD means that short reads (shorter than the endpoint's max length)
-                   is to be treated as an error. */
-                if (urb->transfer_flags & USB_DISABLE_SPD) {
-                        if (urb->actual_length != 
-                            usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))) {
-                                urb->status = -EREMOTEIO;
-                        }
-                }
-        }
-        
-        if (urb->complete) {
-                urb->complete(urb);
-        }
+	etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+	int epid = urb_priv->epid;
+	
+	DBFENTER;
+
+	if (status)
+		warn("Completing intr urb with status %d.", status);
+
+	dbg_intr("Completing intr epid %d, urb 0x%lx", epid, (unsigned long)urb);
+
+	urb->status = status;
+	urb->actual_length = urb_priv->rx_offset;
+
+	dbg_intr("interrupt urb->actual_length = %d", urb->actual_length);
+
+	/* We let any non-zero status from the layer above have precedence. */
+	if (status == 0) {
+		/* USB_DISABLE_SPD means that short reads (shorter than the endpoint's max length)
+		   is to be treated as an error. */
+		if (urb->transfer_flags & USB_DISABLE_SPD) {
+			if (urb->actual_length != 
+			    usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))) {
+				urb->status = -EREMOTEIO;
+			}
+		}
+	}
+	
+	if (urb->complete) {
+		urb->complete(urb);
+	}
 			
-        /* Device driver has taken care of the data now. */
-        urb_priv->rx_offset = 0;
-        
+	/* Device driver has taken care of the data now. */
+	urb_priv->rx_offset = 0;
+	
 	DBFEXIT;
 }
 
@@ -2475,220 +2881,421 @@
 static int etrax_usb_submit_isoc_urb(urb_t *urb) 
 {
 	int epid;
+	unsigned long flags;
 
 	DBFENTER;
 
+	dbg_isoc("Submitting isoc urb = 0x%lx", (unsigned long)urb);
+
+	/* Epid allocation, empty check and list add must be protected. 
+	   Read about this in etrax_usb_submit_ctrl_urb. */
+
+	spin_lock_irqsave(&urb_list_lock, flags);
 	/* Is there an active epid for this urb ? */	
 	epid = etrax_usb_setup_epid(urb);
 	if (epid == -1) {
 		DBFEXIT;
+		spin_unlock_irqrestore(&urb_list_lock, flags);
 		return -ENOMEM;
 	}
-	
-        dbg_isoc("Submitting isoc urb = 0x%lx", (unsigned long)urb);
 
 	/* Ok, now we got valid endpoint, lets insert some traffic */
 
 	urb->status = -EINPROGRESS;
 
 	/* Find the last urb in the URB_List and add this urb after that one. 
-	   Also add the traffic, that is do an isoc_hw_add. This is important
-	   to make this in "real time" since isochronous traffic is time sensitive. */
+	   Also add the traffic, that is do an etrax_usb_add_to_isoc_sb_list.  This
+	   is important to make this in "real time" since isochronous traffic is
+	   time sensitive. */
 
-        dbg_isoc("Adding isoc urb to (possibly empty) list");	
+	dbg_isoc("Adding isoc urb to (possibly empty) list");
 	urb_list_add(urb, epid);
 	etrax_usb_add_to_isoc_sb_list(urb, epid);
-		
+	spin_unlock_irqrestore(&urb_list_lock, flags);
+
 	DBFEXIT;
 
 	return 0;
 }
 
-static void etrax_usb_add_to_isoc_sb_list(urb_t *urb, int epid) 
+static void etrax_usb_check_error_isoc_ep(const int epid)
 {
+	unsigned long int flags;
+	int error_code;
+	__u32 r_usb_ept_data;
 
-	int i = 0;	
+	/* We can't read R_USB_EPID_ATTN here since it would clear the iso_eof,
+	   bulk_eot and epid_attn interrupts.  So we just check the status of
+	   the epid without testing if for it in R_USB_EPID_ATTN. */
+
+	
+	save_flags(flags);
+	cli();
+	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
+	nop();
+	/* Note that although there are separate R_USB_EPT_DATA and R_USB_EPT_DATA_ISO
+	   registers, they are located at the same address and are of the same size.
+	   In other words, this read should be ok for isoc also. */
+	r_usb_ept_data = *R_USB_EPT_DATA;
+	restore_flags(flags);
+
+	error_code = IO_EXTRACT(R_USB_EPT_DATA_ISO, error_code, r_usb_ept_data);
+
+	if (r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, hold)) {
+		warn("Hold was set for epid %d.", epid);
+		return;
+	}
 	
+	if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA_ISO, error_code, no_error)) {
+
+		/* This indicates that the SB list of the ept was completed before
+		   new data was appended to it.  This is not an error, but indicates
+		   large system or USB load and could possibly cause trouble for
+		   very timing sensitive USB device drivers so we log it.
+		*/
+		info("Isoc. epid %d disabled with no error", epid);
+		return;
+	
+	} else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA_ISO, error_code, stall)) {
+		/* Not really a protocol error, just says that the endpoint gave
+		   a stall response. Note that error_code cannot be stall for isoc. */
+		panic("Isoc traffic cannot stall");
+
+	} else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA_ISO, error_code, bus_error)) {
+		/* Two devices responded to a transaction request. Must be resolved
+		   by software. FIXME: Reset ports? */
+		panic("Bus error for epid %d."
+		      " Two devices responded to transaction request",
+		      epid);
+
+	} else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, buffer_error)) {
+		/* DMA overrun or underrun. */
+		warn("Buffer overrun/underrun for epid %d. DMA too busy?", epid);
+
+		/* It seems that error_code = buffer_error in 
+		   R_USB_EPT_DATA/R_USB_EPT_DATA_ISO and ourun = yes in R_USB_STATUS
+		   are the same error. */
+	}
+}
+
+
+static void etrax_usb_add_to_isoc_sb_list(urb_t *urb, int epid)
+{
+
+	int i = 0;	
+
 	etrax_urb_priv_t *urb_priv; 
 	USB_SB_Desc_t *prev_sb_desc,  *next_sb_desc, *temp_sb_desc; 
 
 	DBFENTER;
 
-	prev_sb_desc = next_sb_desc = temp_sb_desc = NULL; 
-	
-	urb_priv = kmalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG);
+	prev_sb_desc = next_sb_desc = temp_sb_desc = NULL;
+
+	urb_priv = kmalloc(sizeof(etrax_urb_priv_t), GFP_ATOMIC);
 	assert(urb_priv != NULL);
-        memset(urb_priv, 0, sizeof(etrax_urb_priv_t));
+	memset(urb_priv, 0, sizeof(etrax_urb_priv_t));
 
 	urb->hcpriv = urb_priv;
 	urb_priv->epid = epid;
-	
+
 	if (usb_pipeout(urb->pipe)) {
 
-                /* Not implemented yet! */
+		if (urb->number_of_packets == 0) panic("etrax_usb_add_to_isoc_sb_list 0 packets\n");
+
 		dbg_isoc("Transfer for epid %d is OUT", epid);
-		
+		dbg_isoc("%d packets in URB", urb->number_of_packets);
+
 		/* Create one SB descriptor for each packet and link them together. */
-		for (i = 0; i < urb->number_of_packets; i++) {	
-		
-			next_sb_desc = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
+		for (i = 0; i < urb->number_of_packets; i++) {
+			if (!urb->iso_frame_desc[i].length)
+				continue;
+
+			next_sb_desc = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_ATOMIC);
 			assert(next_sb_desc != NULL);
-			
-			next_sb_desc->command = (IO_STATE(USB_SB_command, tt, out) |
-                                                 IO_STATE(USB_SB_command, eot, yes));
-			
-			next_sb_desc->sw_len = urb->iso_frame_desc[i].length;
-			next_sb_desc->buf = virt_to_phys(urb->transfer_buffer + urb->iso_frame_desc[i].offset);
-			
+
+			if (urb->iso_frame_desc[i].length > 0) {
+
+				next_sb_desc->command = (IO_STATE(USB_SB_command, tt, out) |
+							 IO_STATE(USB_SB_command, eot, yes));
+
+				next_sb_desc->sw_len = urb->iso_frame_desc[i].length;
+				next_sb_desc->buf = virt_to_phys((char*)urb->transfer_buffer + urb->iso_frame_desc[i].offset);
+
+				/* Check if full length transfer. */
+				if (urb->iso_frame_desc[i].length ==
+				    usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))) {
+					next_sb_desc->command |= IO_STATE(USB_SB_command, full, yes);
+				}
+			} else {
+				dbg_isoc("zero len packet");
+				next_sb_desc->command = (IO_FIELD(USB_SB_command, rem, 0) |
+							 IO_STATE(USB_SB_command, tt, zout) |
+							 IO_STATE(USB_SB_command, eot, yes) |
+							 IO_STATE(USB_SB_command, full, yes));
+
+				next_sb_desc->sw_len = 1;
+				next_sb_desc->buf = virt_to_phys(&zout_buffer[0]);
+			}
+
 			/* First SB descriptor that belongs to this urb */	
 			if (i == 0) 
 				urb_priv->first_sb = next_sb_desc;
 			else
 				prev_sb_desc->next = virt_to_phys(next_sb_desc);			
-						
-			prev_sb_desc = next_sb_desc;	
-		}
-		/* Check if full length transfer. */
-		if (urb->iso_frame_desc[urb->number_of_packets - 1].length ==
-		    usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))) {
-			next_sb_desc->command |= IO_STATE(USB_SB_command, full, yes);
+
+			prev_sb_desc = next_sb_desc;
 		}
 
-		next_sb_desc->command |= IO_STATE(USB_SB_command, eol, yes);
+		next_sb_desc->command |= (IO_STATE(USB_SB_command, intr, yes) |
+					  IO_STATE(USB_SB_command, eol, yes));
 		next_sb_desc->next = 0;
 		urb_priv->last_sb = next_sb_desc;
-	
+
 	} else if (usb_pipein(urb->pipe)) {
 
 		dbg_isoc("Transfer for epid %d is IN", epid);
 		dbg_isoc("transfer_buffer_length = %d", urb->transfer_buffer_length);
 		dbg_isoc("rem is calculated to %d", urb->iso_frame_desc[urb->number_of_packets - 1].length);
-		
-                /* Note that in descriptors for periodic traffic are not consumed. This means that
-                   the USB controller never propagates in the SB list. In other words, if there already
-                   is an SB descriptor in the list for this EP we don't have to do anything. */
-                if (TxIsocEPList[epid].sub == 0) {
-                        dbg_isoc("Isoc traffic not already running, allocating SB");
-                        
-                        next_sb_desc = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
-                        assert(next_sb_desc != NULL);
-			
-                        next_sb_desc->command = (IO_STATE(USB_SB_command, tt, in) |
-                                                 IO_STATE(USB_SB_command, eot, yes) | 
-                                                 IO_STATE(USB_SB_command, eol, yes));
-			
-                        next_sb_desc->sw_len = urb->number_of_packets;
-                        next_sb_desc->buf = 0;
 
-                        /* The rem field is don't care for isoc traffic, so we don't set it. */
+		/* Note that in descriptors for periodic traffic are not consumed. This means that
+		   the USB controller never propagates in the SB list. In other words, if there already
+		   is an SB descriptor in the list for this EP we don't have to do anything. */
+		if (TxIsocEPList[epid].sub == 0) {
+			dbg_isoc("Isoc traffic not already running, allocating SB");
+
+			next_sb_desc = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_ATOMIC);
+			assert(next_sb_desc != NULL);
+
+			next_sb_desc->command = (IO_STATE(USB_SB_command, tt, in) |
+						 IO_STATE(USB_SB_command, eot, yes) | 
+						 IO_STATE(USB_SB_command, eol, yes));
+
+			next_sb_desc->next = 0;
+			next_sb_desc->sw_len = 1; /* Actual number of packets is not relevant
+						     for periodic in traffic as long as it is more
+						     than zero.  Set to 1 always. */
+			next_sb_desc->buf = 0;
+
+			/* The rem field is don't care for isoc traffic, so we don't set it. */
+
+			/* Only one SB descriptor that belongs to this urb. */
+			urb_priv->first_sb = next_sb_desc; 	
+			urb_priv->last_sb = next_sb_desc;
+
+		} else {
+
+			dbg_isoc("Isoc traffic already running, just setting first/last_sb");
+
+			/* Each EP for isoc in will have only one SB descriptor, setup when submitting the
+			   already active urb. Note that even though we may have several first_sb/last_sb
+			   pointing at the same SB descriptor, they are freed only once (when the list has
+			   become empty). */
+			urb_priv->first_sb = phys_to_virt(TxIsocEPList[epid].sub);
+			urb_priv->last_sb = phys_to_virt(TxIsocEPList[epid].sub);
+			return;
+		}
 
-                        /* Only one SB descriptor that belongs to this urb. */
-                        urb_priv->first_sb = next_sb_desc; 	
-                        urb_priv->last_sb = next_sb_desc; 	
-                
-                } else {
-                
-                        dbg_isoc("Isoc traffic already running, just setting first/last_sb");
-                        
-                        /* Each EP for isoc in will have only one SB descriptor, setup when submitting the
-                           already active urb. Note that even though we may have several first_sb/last_sb
-                           pointing at the same SB descriptor, they are freed only once (when the list has
-                           become empty). */
-                        urb_priv->first_sb = phys_to_virt(TxIsocEPList[epid].sub);
-                        urb_priv->last_sb = phys_to_virt(TxIsocEPList[epid].sub);
-                        return;
-                }
-                
 	}
-	
+
 	/* Find the spot to insert this urb and add it. */
 	if (TxIsocEPList[epid].sub == 0) {
 		/* First SB descriptor inserted in this list (in or out). */
 		dbg_isoc("Inserting SB desc first in list");
+		TxIsocEPList[epid].hw_len = 0;
 		TxIsocEPList[epid].sub = virt_to_phys(urb_priv->first_sb);
-	} else {	
+
+	} else {
 		/* Isochronous traffic is already running, insert new traffic last (only out). */
 		dbg_isoc("Inserting SB desc last in list");
 		temp_sb_desc = phys_to_virt(TxIsocEPList[epid].sub);
-		while (!(temp_sb_desc->command & IO_MASK(USB_SB_command, eol))) {
+		while ((temp_sb_desc->command & IO_MASK(USB_SB_command, eol)) !=
+		       IO_STATE(USB_SB_command, eol, yes)) {
+			assert(temp_sb_desc->next);
 			temp_sb_desc = phys_to_virt(temp_sb_desc->next);
 		}
+		dbg_isoc("Appending list on desc 0x%p", temp_sb_desc);
+
 		/* Next pointer must be set before eol is removed. */
-		temp_sb_desc->next = virt_to_phys(urb_priv->first_sb);	
+		temp_sb_desc->next = virt_to_phys(urb_priv->first_sb);
 		/* Clear the previous end of list flag since there is a new in the
 		   added SB descriptor list. */
 		temp_sb_desc->command &= ~IO_MASK(USB_SB_command, eol);
+
+		if (!(TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable))) {
+			/* 8.8.5 in Designer's Reference says we should check for and correct
+			   any errors in the EP here.  That should not be necessary if epid_attn
+			   is handled correctly, so we assume all is ok. */
+			dbg_isoc("EP disabled");
+			etrax_usb_check_error_isoc_ep(epid);
+
+			/* The SB list was exhausted. */
+			if (virt_to_phys(urb_priv->last_sb) != TxIsocEPList[epid].sub) {
+				/* The new sublist did not get processed before the EP was
+				   disabled.  Setup the EP again. */
+				dbg_isoc("Set EP sub to new list");
+				TxIsocEPList[epid].hw_len = 0;
+				TxIsocEPList[epid].sub = virt_to_phys(urb_priv->first_sb);
+			}
+		}
 	}
-	
-        if (urb->transfer_flags & USB_ISO_ASAP) {
-                /* The isoc transfer should be started as soon as possible. The start_frame
-                   field is a return value if URB_ISO_ASAP was set. Comparing R_USB_FM_NUMBER
-                   with a USB Chief trace shows that the first isoc IN token is sent 2 frames
-                   later. I'm not sure how this affects usage of the start_frame field by the
-                   device driver, or how it affects things when USB_ISO_ASAP is not set, so
-                   therefore there's no compensation for the 2 frame "lag" here. */
-                urb->start_frame = (*R_USB_FM_NUMBER & 0x7ff);
-                TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
-                urb_priv->urb_state = STARTED;
-                dbg_isoc("URB_ISO_ASAP set, urb->start_frame set to %d", urb->start_frame);
-        } else {
-                /* Not started yet. */
-                urb_priv->urb_state = NOT_STARTED;
-        }
-        
+
+	if (urb->transfer_flags & USB_ISO_ASAP) {
+		/* The isoc transfer should be started as soon as possible. The start_frame
+		   field is a return value if URB_ISO_ASAP was set. Comparing R_USB_FM_NUMBER
+		   with a USB Chief trace shows that the first isoc IN token is sent 2 frames
+		   later. I'm not sure how this affects usage of the start_frame field by the
+		   device driver, or how it affects things when USB_ISO_ASAP is not set, so
+		   therefore there's no compensation for the 2 frame "lag" here. */
+		urb->start_frame = (*R_USB_FM_NUMBER & 0x7ff);
+		TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
+		urb_priv->urb_state = STARTED;
+		dbg_isoc("URB_ISO_ASAP set, urb->start_frame set to %d", urb->start_frame);
+	} else {
+		/* Not started yet. */
+		urb_priv->urb_state = NOT_STARTED;
+		dbg_isoc("urb_priv->urb_state set to NOT_STARTED");
+	}
+
        /* We start the DMA sub channel without checking if it's running or not, because:
-          1) If it's already running, issuing the start command is a nop.
-          2) We avoid a test-and-set race condition. */
-        *R_DMA_CH8_SUB3_CMD = IO_STATE(R_DMA_CH8_SUB3_CMD, cmd, start);
-        
+	  1) If it's already running, issuing the start command is a nop.
+	  2) We avoid a test-and-set race condition. */
+	*R_DMA_CH8_SUB3_CMD = IO_STATE(R_DMA_CH8_SUB3_CMD, cmd, start);
+	
 	DBFEXIT;
 }
 
 static void etrax_usb_complete_isoc_urb(urb_t *urb, int status)
 {
 	etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv; 
-        int epid = urb_priv->epid;
-	
+	int epid = urb_priv->epid;
+	int auto_resubmit = 0;
+
 	DBFENTER;
+	dbg_isoc("complete urb 0x%p, status %d", urb, status);
 
-        if (status)
-                warn("Completing isoc urb with status %d.", status);
-        
-        if (usb_pipein(urb->pipe)) {
-		
-                /* Move this one down the list. */
-                urb_list_move_last(urb, epid);
-                
-                /* Mark the now first urb as started (may already be). */
-                ((etrax_urb_priv_t *)(urb_list_first(epid)->hcpriv))->urb_state = STARTED;
-	}
+	if (status)
+		warn("Completing isoc urb with status %d.", status);
+	
+	if (usb_pipein(urb->pipe)) {
+		struct urb *nurb = urb->next;
+		int nbr_urbs = 0;			
+		int i;
+
+		/* Make that all isoc packets have status and length set before
+		   completing the urb. */
+		for (i = urb_priv->isoc_packet_counter; i < urb->number_of_packets; i++) {
+			urb->iso_frame_desc[i].actual_length = 0;
+			urb->iso_frame_desc[i].status = -EPROTO;
+		}
+
+		while (nurb && (nurb != urb) && (nbr_urbs < MAX_URB_COUNT)) {
+			++nbr_urbs;
+			nurb = nurb->next;
+			/* FIXME
+			   Should we check that all URBs in the list are present in the URB
+			   list for the epid?  Also, there may be more error checking to
+			   do, c.f. uhci.c */
+		}
+		if (nbr_urbs == MAX_URB_COUNT) {
+			err("Number of linked URBs maxed out.");
+		}
+
+		if (urb == nurb) {
+			auto_resubmit = 1;
+			dbg_isoc("Urb 0x%p is automatically resubmitted.", urb);
+		} else
+		{
+			urb_list_del(urb, epid);
+
+			if (!list_empty(&urb_list[epid])) {
+				((etrax_urb_priv_t *)(urb_list_first(epid)->hcpriv))->urb_state = STARTED;
+			} else {
+				unsigned long int flags;
+				if (TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
+					/* The EP was enabled, disable it and wait. */
+					TxIsocEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
 
+					/* Ah, the luxury of busy-wait. */
+					while (*R_DMA_CH8_SUB3_EP == virt_to_phys(&TxIsocEPList[epid]));
+				}
+
+				etrax_remove_from_sb_list(urb);
+				TxIsocEPList[epid].sub = 0;
+				TxIsocEPList[epid].hw_len = 0;
+
+				save_flags(flags);
+				cli();
+				etrax_usb_free_epid(epid);
+				restore_flags(flags);
+			}
+
+			urb->hcpriv = 0;
+			kfree(urb_priv);
+
+			/* Release allocated bandwidth. */
+			usb_release_bandwidth(urb->dev, urb, 0);
+		}
+	} else if (usb_pipeout(urb->pipe)) {
+		int freed_descr;
+
+		dbg_isoc("Isoc out urb complete 0x%p", urb);
+
+		/* Update the urb list. */	
+		urb_list_del(urb, epid);
+
+		freed_descr = etrax_remove_from_sb_list(urb);
+		dbg_isoc("freed %d descriptors of %d packets", freed_descr, urb->number_of_packets);
+		assert(freed_descr == urb->number_of_packets);
+		urb->hcpriv = 0;
+		kfree(urb_priv);
+
+		/* Release allocated bandwidth. */
+		usb_release_bandwidth(urb->dev, urb, 0);
+	}
+	
 	urb->status = status;
 	if (urb->complete) {
 		urb->complete(urb);
 	}
 
+	if (auto_resubmit) {
+		/* Check that urb was not unlinked by the complete callback. */
+		if (__urb_list_entry(urb, epid)) {
+			/* Move this one down the list. */
+			urb_list_move_last(urb, epid);
+
+			/* Mark the now first urb as started (may already be). */
+			((etrax_urb_priv_t *)(urb_list_first(epid)->hcpriv))->urb_state = STARTED;
+
+			/* Must set this to 0 since this urb is still active after
+			   completion. */
+			urb_priv->isoc_packet_counter = 0;
+		} else {
+			warn("(ISOC) automatic resubmit urb 0x%p removed by complete.", urb);
+		}
+	}
+	
 	DBFEXIT;
 }
 
 static void etrax_usb_complete_urb(urb_t *urb, int status)
 {
-        switch (usb_pipetype(urb->pipe)) {
-        case PIPE_BULK:
-                etrax_usb_complete_bulk_urb(urb, status);
-                break;                
-        case PIPE_CONTROL:
-                etrax_usb_complete_ctrl_urb(urb, status);
-                break;
-        case PIPE_INTERRUPT:
-                etrax_usb_complete_intr_urb(urb, status);
-                break;
-        case PIPE_ISOCHRONOUS:
-                etrax_usb_complete_isoc_urb(urb, status);
-                break;
-        default:
-                err("Unknown pipetype");
-        }
+	switch (usb_pipetype(urb->pipe)) {
+	case PIPE_BULK:
+		etrax_usb_complete_bulk_urb(urb, status);
+		break;		
+	case PIPE_CONTROL:
+		etrax_usb_complete_ctrl_urb(urb, status);
+		break;
+	case PIPE_INTERRUPT:
+		etrax_usb_complete_intr_urb(urb, status);
+		break;
+	case PIPE_ISOCHRONOUS:
+		etrax_usb_complete_isoc_urb(urb, status);
+		break;
+	default:
+		err("Unknown pipetype");
+	}
 }
 
 
@@ -2696,39 +3303,39 @@
 static void etrax_usb_hc_interrupt_top_half(int irq, void *vhc, struct pt_regs *regs)
 {
 	usb_interrupt_registers_t *reg;
-        unsigned long flags;
+	unsigned long flags;
 	__u32 irq_mask;
 	__u8 status;
-        __u32 epid_attn;
-        __u16 port_status_1;
+	__u32 epid_attn;
+	__u16 port_status_1;
 	__u16 port_status_2;
-        __u32 fm_number;
+	__u32 fm_number;
 
 	DBFENTER;
 
-        /* Read critical registers into local variables, do kmalloc afterwards. */
-        save_flags(flags);
-        cli();
-        
+	/* Read critical registers into local variables, do kmalloc afterwards. */
+	save_flags(flags);
+	cli();
+	
 	irq_mask = *R_USB_IRQ_MASK_READ;
-        /* Reading R_USB_STATUS clears the ctl_status interrupt. Note that R_USB_STATUS
-           must be read before R_USB_EPID_ATTN since reading the latter clears the 
-           ourun and perror fields of R_USB_STATUS. */
+	/* Reading R_USB_STATUS clears the ctl_status interrupt. Note that R_USB_STATUS
+	   must be read before R_USB_EPID_ATTN since reading the latter clears the 
+	   ourun and perror fields of R_USB_STATUS. */
 	status = *R_USB_STATUS;
 
-        /* Reading R_USB_EPID_ATTN clears the iso_eof, bulk_eot and epid_attn interrupts. */
+	/* Reading R_USB_EPID_ATTN clears the iso_eof, bulk_eot and epid_attn interrupts. */
 	epid_attn = *R_USB_EPID_ATTN;
 
-        /* Reading R_USB_RH_PORT_STATUS_1 and R_USB_RH_PORT_STATUS_2 clears the
-           port_status interrupt. */
+	/* Reading R_USB_RH_PORT_STATUS_1 and R_USB_RH_PORT_STATUS_2 clears the
+	   port_status interrupt. */
 	port_status_1 = *R_USB_RH_PORT_STATUS_1;
 	port_status_2 = *R_USB_RH_PORT_STATUS_2;
 
-        /* Reading R_USB_FM_NUMBER clears the sof interrupt. */
-        /* Note: the lower 11 bits contain the actual frame number, sent with each sof. */
-        fm_number = *R_USB_FM_NUMBER;
+	/* Reading R_USB_FM_NUMBER clears the sof interrupt. */
+	/* Note: the lower 11 bits contain the actual frame number, sent with each sof. */
+	fm_number = *R_USB_FM_NUMBER;
 
-        restore_flags(flags);
+	restore_flags(flags);
 
 	reg = (usb_interrupt_registers_t *)kmem_cache_alloc(top_half_reg_cache, SLAB_ATOMIC);
 
@@ -2736,14 +3343,14 @@
 
 	reg->hc = (etrax_hc_t *)vhc;
 
-        /* Now put register values into kmalloc'd area. */
-        reg->r_usb_irq_mask_read = irq_mask;
-        reg->r_usb_status = status;
-        reg->r_usb_epid_attn = epid_attn;
+	/* Now put register values into kmalloc'd area. */
+	reg->r_usb_irq_mask_read = irq_mask;
+	reg->r_usb_status = status;
+	reg->r_usb_epid_attn = epid_attn;
 	reg->r_usb_rh_port_status_1 = port_status_1;
 	reg->r_usb_rh_port_status_2 = port_status_2;
-        reg->r_usb_fm_number = fm_number;
-        
+	reg->r_usb_fm_number = fm_number;
+	
 	reg->usb_bh.sync = 0;
 	reg->usb_bh.routine = etrax_usb_hc_interrupt_bottom_half;
 	reg->usb_bh.data = reg;
@@ -2757,32 +3364,32 @@
 static void etrax_usb_hc_interrupt_bottom_half(void *data)
 {
 	usb_interrupt_registers_t *reg = (usb_interrupt_registers_t *)data;
-        __u32 irq_mask = reg->r_usb_irq_mask_read;
-	
+	__u32 irq_mask = reg->r_usb_irq_mask_read;
+
 	DBFENTER;
 
-        /* Interrupts are handled in order of priority. */
-        if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, epid_attn)) {
-                etrax_usb_hc_epid_attn_interrupt(reg);
-        }
+	/* Interrupts are handled in order of priority. */
+	if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, epid_attn)) {
+		etrax_usb_hc_epid_attn_interrupt(reg);
+	}
 	if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, port_status)) {
-                etrax_usb_hc_port_status_interrupt(reg);
-        }
-        if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, ctl_status)) {
-                etrax_usb_hc_ctl_status_interrupt(reg);
-        }
+		etrax_usb_hc_port_status_interrupt(reg);
+	}
+	if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, ctl_status)) {
+		etrax_usb_hc_ctl_status_interrupt(reg);
+	}
 	if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, iso_eof)) {
-                etrax_usb_hc_isoc_eof_interrupt();
-        } 
+		etrax_usb_hc_isoc_eof_interrupt();
+	}
 	if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, bulk_eot)) {
-                /* Update/restart the bulk start timer since obviously the channel is running. */
-                mod_timer(&bulk_start_timer, jiffies + BULK_START_TIMER_INTERVAL);
-                /* Update/restart the bulk eot timer since we just received an bulk eot interrupt. */
-                mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL);
-                
-                etrax_usb_hc_bulk_eot_interrupt(0);
-        }
-        
+		/* Update/restart the bulk start timer since obviously the channel is running. */
+		mod_timer(&bulk_start_timer, jiffies + BULK_START_TIMER_INTERVAL);
+		/* Update/restart the bulk eot timer since we just received an bulk eot interrupt. */
+		mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL);
+		
+		etrax_usb_hc_bulk_eot_interrupt(0);
+	}
+	
 	kmem_cache_free(top_half_reg_cache, reg);
 
 	DBFEXIT;
@@ -2794,47 +3401,63 @@
 	urb_t *urb;
 	etrax_urb_priv_t *urb_priv;
 	int epid;
+	unsigned long flags;
 
 	DBFENTER;
-
+	
 	/* Do not check the invalid epid (it has a valid sub pointer). */
 	for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) {
-	
+
+		/* Do not check the invalid epid (it has a valid sub pointer). */
+		if ((epid == DUMMY_EPID) || (epid == INVALID_EPID))
+			continue;
+
+		/* Disable interrupts to block the isoc out descriptor interrupt handler
+		   from being called while the isoc EPID list is being checked.
+		*/
+		save_flags(flags);
+		cli();
+
 		if (TxIsocEPList[epid].sub == 0) {
 			/* Nothing here to see. */
+			restore_flags(flags);
 			continue;
 		}
 
-                /* Get the first urb (if any). */
+		/* Get the first urb (if any). */
 		urb = urb_list_first(epid);
-                if (urb == 0) {
-                        warn("Ignoring NULL urb");
-                        continue;
-                }
+		if (urb == 0) {
+			warn("Ignoring NULL urb");
+			restore_flags(flags);
+			continue;
+		}
+		if (usb_pipein(urb->pipe)) {
 
-                /* Sanity check. */
-                assert(usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS);
+			/* Sanity check. */
+			assert(usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS);
 
-                urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
-		assert(urb_priv);
+			urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+			assert(urb_priv);
+
+			if (urb_priv->urb_state == NOT_STARTED) {
+
+				/* If ASAP is not set and urb->start_frame is the current frame,
+				   start the transfer. */
+				if (!(urb->transfer_flags & USB_ISO_ASAP) && 
+				    (urb->start_frame == (*R_USB_FM_NUMBER & 0x7ff))) {
+
+					dbg_isoc("Enabling isoc IN EP descr for epid %d", epid);
+					TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
+
+					/* This urb is now active. */
+					urb_priv->urb_state = STARTED;
+					continue;
+				}
+			}
+		}
+		restore_flags(flags);
+	}
 
-                if (urb_priv->urb_state == NOT_STARTED) {
-                        
-                        /* If ASAP is not set and urb->start_frame is the current frame,
-                           start the transfer. */
-                        if (!(urb->transfer_flags & USB_ISO_ASAP) && 
-                            (urb->start_frame == (*R_USB_FM_NUMBER & 0x7ff))) {
-
-                                dbg_isoc("Enabling isoc IN EP descr for epid %d", epid);
-                                TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
-
-                                /* This urb is now active. */
-                                urb_priv->urb_state = STARTED;
-                                continue;
-                        }
-                }
-        }
-	
 	DBFEXIT;
 
 }
@@ -2843,397 +3466,399 @@
 {
  	int epid;
  
-        /* The technique is to run one urb at a time, wait for the eot interrupt at which
-           point the EP descriptor has been disabled. */
-        
-        DBFENTER;
-        dbg_bulk("bulk eot%s", timer_induced ? ", called by timer" : "");
-
-        for (epid = 0; epid < NBR_OF_EPIDS; epid++) {
-
-                if (!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) &&
-                    (TxBulkEPList[epid].sub != 0)) {
-
-                        urb_t *urb;
-                        etrax_urb_priv_t *urb_priv;
-                        unsigned long flags;
-                        __u32 r_usb_ept_data;
-
-                        /* Found a disabled EP descriptor which has a non-null sub pointer.
-                           Verify that this ctrl EP descriptor got disabled no errors.
-                           FIXME: Necessary to check error_code? */
-                        dbg_bulk("for epid %d?", epid);
-
-                        /* Get the first urb. */
-                        urb = urb_list_first(epid);
-
-                        /* FIXME: Could this happen for valid reasons? Why did it disappear? Because of
-                           wrong unlinking? */
-                        if (!urb) {
-                                warn("NULL urb for epid %d", epid);
-                                continue;
-                        }
-                        
-                        assert(urb);
-                        urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
-                        assert(urb_priv);
-
-                        /* Sanity checks. */
-                        assert(usb_pipetype(urb->pipe) == PIPE_BULK);
-                        if (phys_to_virt(TxBulkEPList[epid].sub) != urb_priv->last_sb) {
-                                err("bulk endpoint got disabled before reaching last sb");
-                        }
-
-                        /* For bulk IN traffic, there seems to be a race condition between
-                           between the bulk eot and eop interrupts, or rather an uncertainty regarding
-                           the order in which they happen. Normally we expect the eop interrupt from
-                           DMA channel 9 to happen before the eot interrupt.
-
-                           Therefore, we complete the bulk IN urb in the rx interrupt handler instead. */
-                        
-                        if (usb_pipein(urb->pipe)) {
-                                dbg_bulk("in urb, continuing");
-                                continue;
-                        }
-                        
-                        save_flags(flags);
-                        cli();
-                        *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); 
-                        nop();
-                        r_usb_ept_data = *R_USB_EPT_DATA;
-                        restore_flags(flags);
-                        
-                        if (IO_EXTRACT(R_USB_EPT_DATA, error_code, r_usb_ept_data) ==
-                            IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) {
-                                /* This means that the endpoint has no error, is disabled
-                                   and had inserted traffic, i.e. transfer successfully completed. */
-                                etrax_usb_complete_bulk_urb(urb, 0);
-                        } else {
-                                /* Shouldn't happen. We expect errors to be caught by epid attention. */
-                                err("Found disabled bulk EP desc, error_code != no_error");
-                        }
-                }
-        }
-        
-        /* Normally, we should find (at least) one disabled EP descriptor with a valid sub pointer.
-           However, because of the uncertainty in the deliverance of the eop/eot interrupts, we may
-           not.  Also, we might find two disabled EPs when handling an eot interrupt, and then find
-           none the next time. */
+	/* The technique is to run one urb at a time, wait for the eot interrupt at which
+	   point the EP descriptor has been disabled. */
+	
+	DBFENTER;
+	dbg_bulk("bulk eot%s", timer_induced ? ", called by timer" : "");
+
+	for (epid = 0; epid < NBR_OF_EPIDS; epid++) {
+
+		if (!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) &&
+		    (TxBulkEPList[epid].sub != 0)) {
+
+			urb_t *urb;
+			etrax_urb_priv_t *urb_priv;
+			unsigned long flags;
+			__u32 r_usb_ept_data;
+
+			/* Found a disabled EP descriptor which has a non-null sub pointer.
+			   Verify that this ctrl EP descriptor got disabled no errors.
+			   FIXME: Necessary to check error_code? */
+			dbg_bulk("for epid %d?", epid);
+
+			/* Get the first urb. */
+			urb = urb_list_first(epid);
+
+			/* FIXME: Could this happen for valid reasons? Why did it disappear? Because of
+			   wrong unlinking? */
+			if (!urb) {
+				warn("NULL urb for epid %d", epid);
+				continue;
+			}
+			
+			assert(urb);
+			urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+			assert(urb_priv);
+
+			/* Sanity checks. */
+			assert(usb_pipetype(urb->pipe) == PIPE_BULK);
+			if (phys_to_virt(TxBulkEPList[epid].sub) != urb_priv->last_sb) {
+				err("bulk endpoint got disabled before reaching last sb");
+			}
+
+			/* For bulk IN traffic, there seems to be a race condition between
+			   between the bulk eot and eop interrupts, or rather an uncertainty regarding
+			   the order in which they happen. Normally we expect the eop interrupt from
+			   DMA channel 9 to happen before the eot interrupt.
+
+			   Therefore, we complete the bulk IN urb in the rx interrupt handler instead. */
+			
+			if (usb_pipein(urb->pipe)) {
+				dbg_bulk("in urb, continuing");
+				continue;
+			}
+
+			save_flags(flags);
+			cli();
+			*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); 
+			nop();
+			r_usb_ept_data = *R_USB_EPT_DATA;
+			restore_flags(flags);
+
+			if (IO_EXTRACT(R_USB_EPT_DATA, error_code, r_usb_ept_data) ==
+			    IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) {
+				/* This means that the endpoint has no error, is disabled
+				   and had inserted traffic, i.e. transfer successfully completed. */
+				etrax_usb_complete_bulk_urb(urb, 0);
+			} else {
+				/* Shouldn't happen. We expect errors to be caught by epid attention. */
+				err("Found disabled bulk EP desc, error_code != no_error");
+			}
+		}
+	}
+	
+	/* Normally, we should find (at least) one disabled EP descriptor with a valid sub pointer.
+	   However, because of the uncertainty in the deliverance of the eop/eot interrupts, we may
+	   not.  Also, we might find two disabled EPs when handling an eot interrupt, and then find
+	   none the next time. */
 
-        DBFEXIT;
+	DBFEXIT;
 
 }
 
 void etrax_usb_hc_epid_attn_interrupt(usb_interrupt_registers_t *reg)
 {
-        /* This function handles the epid attention interrupt.  There are a variety of reasons
-           for this interrupt to happen (Designer's Reference, p. 8 - 22 for the details):
+	/* This function handles the epid attention interrupt.  There are a variety of reasons
+	   for this interrupt to happen (Designer's Reference, p. 8 - 22 for the details):
+
+	   invalid ep_id  - Invalid epid in an EP (EP disabled).
+	   stall	  - Not strictly an error condition (EP disabled).
+	   3rd error      - Three successive transaction errors  (EP disabled).
+	   buffer ourun   - Buffer overrun or underrun (EP disabled).
+	   past eof1      - Intr or isoc transaction proceeds past EOF1.
+	   near eof       - Intr or isoc transaction would not fit inside the frame.
+	   zout transfer  - If zout transfer for a bulk endpoint (EP disabled).
+	   setup transfer - If setup transfer for a non-ctrl endpoint (EP disabled). */
+
+	int epid;
+
 
-           invalid ep_id  - Invalid epid in an EP (EP disabled).
-           stall          - Not strictly an error condition (EP disabled).
-           3rd error      - Three successive transaction errors  (EP disabled).
-           buffer ourun   - Buffer overrun or underrun (EP disabled).
-           past eof1      - Intr or isoc transaction proceeds past EOF1.
-           near eof       - Intr or isoc transaction would not fit inside the frame.
-           zout transfer  - If zout transfer for a bulk endpoint (EP disabled).
-           setup transfer - If setup transfer for a non-ctrl endpoint (EP disabled). */
-
-        int epid;
-
-
-        DBFENTER;
-        
-        /* Note that we loop through all epids. We still want to catch errors for 
-           the invalid one, even though we might handle them differently. */
-        for (epid = 0; epid < NBR_OF_EPIDS; epid++) {
+	DBFENTER;
+
+	assert(reg != NULL);
+	
+	/* Note that we loop through all epids. We still want to catch errors for 
+	   the invalid one, even though we might handle them differently. */
+	for (epid = 0; epid < NBR_OF_EPIDS; epid++) {
 
 		if (test_bit(epid, (void *)&reg->r_usb_epid_attn)) {
-                        
-                        urb_t *urb;
-                        __u32 r_usb_ept_data;
-                        unsigned long flags;
-                        int error_code;
-
-                        save_flags(flags);
-                        cli();                
-                        *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); 
-                        nop();
-                        /* Note that although there are separate R_USB_EPT_DATA and R_USB_EPT_DATA_ISO
-                           registers, they are located at the same address and are of the same size.
-                           In other words, this read should be ok for isoc also. */
-                        r_usb_ept_data = *R_USB_EPT_DATA;
-                        restore_flags(flags);
-
-                        /* First some sanity checks. */
-                        if (epid == INVALID_EPID) {
-                                /* FIXME: What if it became disabled? Could seriously hurt interrupt
+
+			urb_t *urb;
+			__u32 r_usb_ept_data;
+			unsigned long flags;
+			int error_code;
+
+			save_flags(flags);
+			cli();
+			*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
+			nop();
+			/* Note that although there are separate R_USB_EPT_DATA and R_USB_EPT_DATA_ISO
+			   registers, they are located at the same address and are of the same size.
+			   In other words, this read should be ok for isoc also. */
+			r_usb_ept_data = *R_USB_EPT_DATA;
+			restore_flags(flags);
+
+			/* First some sanity checks. */
+			if (epid == INVALID_EPID) {
+				/* FIXME: What if it became disabled? Could seriously hurt interrupt
 				   traffic. (Use do_intr_recover.) */
-                                warn("Got epid_attn for INVALID_EPID (%d).", epid);
+				warn("Got epid_attn for INVALID_EPID (%d).", epid);
 				err("R_USB_EPT_DATA = 0x%x", r_usb_ept_data);
 				err("R_USB_STATUS = 0x%x", reg->r_usb_status);
 				continue;
-                        } else 	if (epid == DUMMY_EPID) {
-                                /* We definitely don't care about these ones. Besides, they are
+			} else 	if (epid == DUMMY_EPID) {
+				/* We definitely don't care about these ones. Besides, they are
 				   always disabled, so any possible disabling caused by the
 				   epid attention interrupt is irrelevant. */
-                                warn("Got epid_attn for DUMMY_EPID (%d).", epid);
+				warn("Got epid_attn for DUMMY_EPID (%d).", epid);
 				continue;
-                        }
+			}
 
-                        /* Get the first urb in the urb list for this epid. We blatantly assume
-                           that only the first urb could have caused the epid attention.
-                           (For bulk and ctrl, only one urb is active at any one time. For intr
-                           and isoc we remove them once they are completed.) */
-                        urb = urb_list_first(epid);
+			/* Get the first urb in the urb list for this epid. We blatantly assume
+			   that only the first urb could have caused the epid attention.
+			   (For bulk and ctrl, only one urb is active at any one time. For intr
+			   and isoc we remove them once they are completed.) */
+			urb = urb_list_first(epid);
 
 			if (urb == NULL) {
-                                err("Got epid_attn for epid %i with no urb.", epid);
-                                err("R_USB_EPT_DATA = 0x%x", r_usb_ept_data);
-                                err("R_USB_STATUS = 0x%x", reg->r_usb_status);
+				err("Got epid_attn for epid %i with no urb.", epid);
+				err("R_USB_EPT_DATA = 0x%x", r_usb_ept_data);
+				err("R_USB_STATUS = 0x%x", reg->r_usb_status);
 				continue;
 			}
 
-                        switch (usb_pipetype(urb->pipe)) {
-                        case PIPE_BULK:
-                                warn("Got epid attn for bulk endpoint, epid %d", epid);
-                                break;                
-                        case PIPE_CONTROL:
-                                warn("Got epid attn for control endpoint, epid %d", epid);
-                                break;
-                        case PIPE_INTERRUPT:
-                                warn("Got epid attn for interrupt endpoint, epid %d", epid);
-                                break;
-                        case PIPE_ISOCHRONOUS:
-                                warn("Got epid attn for isochronous endpoint, epid %d", epid);
-                                break;
-                        }
-                        
-                        if (usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS) {        
-                                if (r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, hold)) {
-                                        warn("Hold was set for epid %d.", epid);
-                                        continue;
-                                }
-                        }
-                        
-                        /* Even though error_code occupies bits 22 - 23 in both R_USB_EPT_DATA and 
-                           R_USB_EPT_DATA_ISOC, we separate them here so we don't forget in other places. */
-                        if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
-                                error_code = IO_EXTRACT(R_USB_EPT_DATA_ISO, error_code, r_usb_ept_data);
-                        } else {
-                                error_code = IO_EXTRACT(R_USB_EPT_DATA, error_code, r_usb_ept_data);
-                        }
-                        
+			switch (usb_pipetype(urb->pipe)) {
+			case PIPE_BULK:
+				warn("Got epid attn for bulk endpoint, epid %d", epid);
+				break;		
+			case PIPE_CONTROL:
+				warn("Got epid attn for control endpoint, epid %d", epid);
+				break;
+			case PIPE_INTERRUPT:
+				warn("Got epid attn for interrupt endpoint, epid %d", epid);
+				break;
+			case PIPE_ISOCHRONOUS:
+				warn("Got epid attn for isochronous endpoint, epid %d", epid);
+				break;
+			}
+
+			if (usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS) {	
+				if (r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, hold)) {
+					warn("Hold was set for epid %d.", epid);
+					continue;
+				}
+			}
+
+			/* Even though error_code occupies bits 22 - 23 in both R_USB_EPT_DATA and 
+			   R_USB_EPT_DATA_ISOC, we separate them here so we don't forget in other places. */
+			if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+				error_code = IO_EXTRACT(R_USB_EPT_DATA_ISO, error_code, r_usb_ept_data);
+			} else {
+				error_code = IO_EXTRACT(R_USB_EPT_DATA, error_code, r_usb_ept_data);
+			}
+
 			/* Using IO_STATE_VALUE on R_USB_EPT_DATA should be ok for isoc also. */
 			if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) {
 
-                                /* Isoc traffic doesn't have error_count_in/error_count_out. */
+				/* Isoc traffic doesn't have error_count_in/error_count_out. */
 				if ((usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS) &&
-                                    (IO_EXTRACT(R_USB_EPT_DATA, error_count_in, r_usb_ept_data) == 3 ||
-                                     IO_EXTRACT(R_USB_EPT_DATA, error_count_out, r_usb_ept_data) == 3)) {
-				        /* 3rd error. */
+				    (IO_EXTRACT(R_USB_EPT_DATA, error_count_in, r_usb_ept_data) == 3 ||
+				     IO_EXTRACT(R_USB_EPT_DATA, error_count_out, r_usb_ept_data) == 3)) {
+					/* 3rd error. */
 					warn("3rd error for epid %i", epid);
-                                        etrax_usb_complete_urb(urb, -EPROTO);
+					etrax_usb_complete_urb(urb, -EPROTO);
+
+				} else if (reg->r_usb_status & IO_MASK(R_USB_STATUS, perror)) {
+
+					warn("Perror for epid %d", epid);
 
-                                } else if (reg->r_usb_status & IO_MASK(R_USB_STATUS, perror)) {
-                                        
-                                        warn("Perror for epid %d", epid);
-                                        
-                                        if (!(r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, valid))) {
-                                                /* invalid ep_id */
-                                                panic("Perror because of invalid epid."
-                                                      " Deconfigured too early?");
-                                        } else {
-                                                /* past eof1, near eof, zout transfer, setup transfer */
-                                                
-                                                /* Dump the urb and the relevant EP descriptor list. */
+					if (!(r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, valid))) {
+						/* invalid ep_id */
+						panic("Perror because of invalid epid."
+						      " Deconfigured too early?");
+					} else {
+						/* past eof1, near eof, zout transfer, setup transfer */
 
-                                                __dump_urb(urb);
+						/* Dump the urb and the relevant EP descriptor list. */
+
+						__dump_urb(urb);
 						__dump_ept_data(epid);
-                                                __dump_ep_list(usb_pipetype(urb->pipe));
-                                                
-                                                panic("Something wrong with DMA descriptor contents."
-                                                      " Too much traffic inserted?");
-                                        }
-                                } else if (reg->r_usb_status & IO_MASK(R_USB_STATUS, ourun)) {
-                                        /* buffer ourun */
-                                        panic("Buffer overrun/underrun for epid %d. DMA too busy?", epid);
-                                }
+						__dump_ep_list(usb_pipetype(urb->pipe));
+
+						panic("Something wrong with DMA descriptor contents."
+						      " Too much traffic inserted?");
+					}
+				} else if (reg->r_usb_status & IO_MASK(R_USB_STATUS, ourun)) {
+					/* buffer ourun */
+					panic("Buffer overrun/underrun for epid %d. DMA too busy?", epid);
+				}
 
 			} else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, stall)) {
-                                /* Not really a protocol error, just says that the endpoint gave
-                                   a stall response. Note that error_code cannot be stall for isoc. */
-                                if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
-                                        panic("Isoc traffic cannot stall");
-                                }
-                                
-                                warn("Stall for epid %d", epid);
-                                etrax_usb_complete_urb(urb, -EPIPE);
-				
+				/* Not really a protocol error, just says that the endpoint gave
+				   a stall response. Note that error_code cannot be stall for isoc. */
+				if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+					panic("Isoc traffic cannot stall");
+				}
+
+				warn("Stall for epid %d", epid);
+				etrax_usb_complete_urb(urb, -EPIPE);
+
 			} else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, bus_error)) {
-                                /* Two devices responded to a transaction request. Must be resolved
-                                   by software. FIXME: Reset ports? */
+				/* Two devices responded to a transaction request. Must be resolved
+				   by software. FIXME: Reset ports? */
 				panic("Bus error for epid %d."
-                                      " Two devices responded to transaction request",
-                                      epid);
-				
+				      " Two devices responded to transaction request",
+				      epid);
+
 			} else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, buffer_error)) {
-                                /* DMA overrun or underrun. */
+				/* DMA overrun or underrun. */
 				warn("Buffer overrun/underrun for epid %d. DMA too busy?", epid);
 
-                                /* It seems that error_code = buffer_error in 
-                                   R_USB_EPT_DATA/R_USB_EPT_DATA_ISO and ourun = yes in R_USB_STATUS
-                                   are the same error. */
-                                etrax_usb_complete_urb(urb, -EPROTO);
-                        }               
-                }
-        }
+				/* It seems that error_code = buffer_error in 
+				   R_USB_EPT_DATA/R_USB_EPT_DATA_ISO and ourun = yes in R_USB_STATUS
+				   are the same error. */
+				etrax_usb_complete_urb(urb, -EPROTO);
+			}
+		}
+	}
 
-        DBFEXIT;
-        
+	DBFEXIT;
+	
 }
 
 void etrax_usb_bulk_start_timer_func(unsigned long dummy)
 {
        
-        /* We might enable an EP descriptor behind the current DMA position when it's about
-           to decide that there are no more bulk traffic and it should stop the bulk channel.
-           Therefore we periodically check if the bulk channel is stopped and there is an
-           enabled bulk EP descriptor, in which case we start the bulk channel. */
-        dbg_bulk("bulk_start_timer timed out.");        
-
-        if (!(*R_DMA_CH8_SUB0_CMD & IO_MASK(R_DMA_CH8_SUB0_CMD, cmd))) {
-                int epid;
-                
+	/* We might enable an EP descriptor behind the current DMA position when it's about
+	   to decide that there are no more bulk traffic and it should stop the bulk channel.
+	   Therefore we periodically check if the bulk channel is stopped and there is an
+	   enabled bulk EP descriptor, in which case we start the bulk channel. */
+	dbg_bulk("bulk_start_timer timed out.");	
+
+	if (!(*R_DMA_CH8_SUB0_CMD & IO_MASK(R_DMA_CH8_SUB0_CMD, cmd))) {
+		int epid;
+		
 		dbg_bulk("Bulk DMA channel not running.");
 
-                for (epid = 0; epid < NBR_OF_EPIDS; epid++) {
-                        if (TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
-                                printk("Found enabled EP for epid %d, starting bulk channel.\n",
-				       epid);
-                                *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start);
-                                
-                                /* Restart the bulk eot timer since we just started the bulk channel. */
-                                mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL);
-
-                                /* No need to search any further. */
-                                break;
-                        }
-                }
-        } else {
+		for (epid = 0; epid < NBR_OF_EPIDS; epid++) {
+			if (TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
+				dbg_bulk("Found enabled EP for epid %d, starting bulk channel.\n",
+					 epid);
+				*R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start);
+				
+				/* Restart the bulk eot timer since we just started the bulk channel. */
+				mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL);
+
+				/* No need to search any further. */
+				break;
+			}
+		}
+	} else {
 		dbg_bulk("Bulk DMA channel running.");
 	}
 }
 
 void etrax_usb_hc_port_status_interrupt(usb_interrupt_registers_t *reg)
 {
-        etrax_hc_t *hc = reg->hc;
-        __u16 r_usb_rh_port_status_1 = reg->r_usb_rh_port_status_1;
+	etrax_hc_t *hc = reg->hc;
+	__u16 r_usb_rh_port_status_1 = reg->r_usb_rh_port_status_1;
 	__u16 r_usb_rh_port_status_2 = reg->r_usb_rh_port_status_2;
 
-        DBFENTER;
-        
-        /* The Etrax RH does not include a wPortChange register, so this has to be handled in software
-           (by saving the old port status value for comparison when the port status interrupt happens).
-           See section 11.16.2.6.2 in the USB 1.1 spec for details. */
-        
-        dbg_rh("hc->rh.prev_wPortStatus_1 = 0x%x", hc->rh.prev_wPortStatus_1);
-        dbg_rh("hc->rh.prev_wPortStatus_2 = 0x%x", hc->rh.prev_wPortStatus_2);
-        dbg_rh("r_usb_rh_port_status_1 = 0x%x", r_usb_rh_port_status_1);
-        dbg_rh("r_usb_rh_port_status_2 = 0x%x", r_usb_rh_port_status_2);
-        
-        /* C_PORT_CONNECTION is set on any transition. */
-        hc->rh.wPortChange_1 |=
-                ((r_usb_rh_port_status_1 & (1 << RH_PORT_CONNECTION)) !=
-                 (hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_CONNECTION))) ?
-                (1 << RH_PORT_CONNECTION) : 0;
-        
-        hc->rh.wPortChange_2 |=
-                ((r_usb_rh_port_status_2 & (1 << RH_PORT_CONNECTION)) !=
-                 (hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_CONNECTION))) ?
-                (1 << RH_PORT_CONNECTION) : 0;
-        
-        /* C_PORT_ENABLE is _only_ set on a one to zero transition, i.e. when
-           the port is disabled, not when it's enabled. */
-        hc->rh.wPortChange_1 |=
-                ((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_ENABLE))
-                 && !(r_usb_rh_port_status_1 & (1 << RH_PORT_ENABLE))) ?
-                (1 << RH_PORT_ENABLE) : 0;
-	
-        hc->rh.wPortChange_2 |=
-                ((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_ENABLE))
-                 && !(r_usb_rh_port_status_2 & (1 << RH_PORT_ENABLE))) ?
-                (1 << RH_PORT_ENABLE) : 0;
-
-        /* C_PORT_SUSPEND is set to one when the device has transitioned out
-           of the suspended state, i.e. when suspend goes from one to zero. */
-        hc->rh.wPortChange_1 |=
-                ((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_SUSPEND))
-                 && !(r_usb_rh_port_status_1 & (1 << RH_PORT_SUSPEND))) ?
-                (1 << RH_PORT_SUSPEND) : 0;
-        
-        hc->rh.wPortChange_2 |=
-                ((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_SUSPEND))
-                 && !(r_usb_rh_port_status_2 & (1 << RH_PORT_SUSPEND))) ?
-                (1 << RH_PORT_SUSPEND) : 0;
-
-        
-        /* C_PORT_RESET is set when reset processing on this port is complete. */
-        hc->rh.wPortChange_1 |=
-                ((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_RESET))
-                 && !(r_usb_rh_port_status_1 & (1 << RH_PORT_RESET))) ?
-                (1 << RH_PORT_RESET) : 0;
-        
-        hc->rh.wPortChange_2 |=
-                ((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_RESET))
-                 && !(r_usb_rh_port_status_2 & (1 << RH_PORT_RESET))) ?
-                (1 << RH_PORT_RESET) : 0;
-
-        /* Save the new values for next port status change. */
-        hc->rh.prev_wPortStatus_1 = r_usb_rh_port_status_1;
-        hc->rh.prev_wPortStatus_2 = r_usb_rh_port_status_2;	
-
-        dbg_rh("hc->rh.wPortChange_1 set to 0x%x", hc->rh.wPortChange_1);
-        dbg_rh("hc->rh.wPortChange_2 set to 0x%x", hc->rh.wPortChange_2);
-        
-        DBFEXIT;
+	DBFENTER;
+	
+	/* The Etrax RH does not include a wPortChange register, so this has to be handled in software
+	   (by saving the old port status value for comparison when the port status interrupt happens).
+	   See section 11.16.2.6.2 in the USB 1.1 spec for details. */
+	
+	dbg_rh("hc->rh.prev_wPortStatus_1 = 0x%x", hc->rh.prev_wPortStatus_1);
+	dbg_rh("hc->rh.prev_wPortStatus_2 = 0x%x", hc->rh.prev_wPortStatus_2);
+	dbg_rh("r_usb_rh_port_status_1 = 0x%x", r_usb_rh_port_status_1);
+	dbg_rh("r_usb_rh_port_status_2 = 0x%x", r_usb_rh_port_status_2);
+	
+	/* C_PORT_CONNECTION is set on any transition. */
+	hc->rh.wPortChange_1 |=
+		((r_usb_rh_port_status_1 & (1 << RH_PORT_CONNECTION)) !=
+		 (hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_CONNECTION))) ?
+		(1 << RH_PORT_CONNECTION) : 0;
+	
+	hc->rh.wPortChange_2 |=
+		((r_usb_rh_port_status_2 & (1 << RH_PORT_CONNECTION)) !=
+		 (hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_CONNECTION))) ?
+		(1 << RH_PORT_CONNECTION) : 0;
+	
+	/* C_PORT_ENABLE is _only_ set on a one to zero transition, i.e. when
+	   the port is disabled, not when it's enabled. */
+	hc->rh.wPortChange_1 |=
+		((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_ENABLE))
+		 && !(r_usb_rh_port_status_1 & (1 << RH_PORT_ENABLE))) ?
+		(1 << RH_PORT_ENABLE) : 0;
+	
+	hc->rh.wPortChange_2 |=
+		((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_ENABLE))
+		 && !(r_usb_rh_port_status_2 & (1 << RH_PORT_ENABLE))) ?
+		(1 << RH_PORT_ENABLE) : 0;
+
+	/* C_PORT_SUSPEND is set to one when the device has transitioned out
+	   of the suspended state, i.e. when suspend goes from one to zero. */
+	hc->rh.wPortChange_1 |=
+		((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_SUSPEND))
+		 && !(r_usb_rh_port_status_1 & (1 << RH_PORT_SUSPEND))) ?
+		(1 << RH_PORT_SUSPEND) : 0;
+	
+	hc->rh.wPortChange_2 |=
+		((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_SUSPEND))
+		 && !(r_usb_rh_port_status_2 & (1 << RH_PORT_SUSPEND))) ?
+		(1 << RH_PORT_SUSPEND) : 0;
+
+	
+	/* C_PORT_RESET is set when reset processing on this port is complete. */
+	hc->rh.wPortChange_1 |=
+		((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_RESET))
+		 && !(r_usb_rh_port_status_1 & (1 << RH_PORT_RESET))) ?
+		(1 << RH_PORT_RESET) : 0;
+	
+	hc->rh.wPortChange_2 |=
+		((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_RESET))
+		 && !(r_usb_rh_port_status_2 & (1 << RH_PORT_RESET))) ?
+		(1 << RH_PORT_RESET) : 0;
+
+	/* Save the new values for next port status change. */
+	hc->rh.prev_wPortStatus_1 = r_usb_rh_port_status_1;
+	hc->rh.prev_wPortStatus_2 = r_usb_rh_port_status_2;	
+
+	dbg_rh("hc->rh.wPortChange_1 set to 0x%x", hc->rh.wPortChange_1);
+	dbg_rh("hc->rh.wPortChange_2 set to 0x%x", hc->rh.wPortChange_2);
+	
+	DBFEXIT;
 
 }
 
 void etrax_usb_hc_ctl_status_interrupt(usb_interrupt_registers_t *reg)
 {
-        DBFENTER;
+	DBFENTER;
 
-        /* FIXME: What should we do if we get ourun or perror? Dump the EP and SB
-           list for the corresponding epid? */
-        if (reg->r_usb_status & IO_MASK(R_USB_STATUS, ourun)) {
-                panic("USB controller got ourun.");
-        } 
-        if (reg->r_usb_status & IO_MASK(R_USB_STATUS, perror)) {
-
-                /* Before, etrax_usb_do_intr_recover was called on this epid if it was
-                   an interrupt pipe. I don't see how re-enabling all EP descriptors
-                   will help if there was a programming error. */
-                panic("USB controller got perror.");
-        } 
-
-        if (reg->r_usb_status & IO_MASK(R_USB_STATUS, device_mode)) {
-                /* We should never operate in device mode. */
-                panic("USB controller in device mode.");
-        }
-
-        /* These if-statements could probably be nested. */
-        if (reg->r_usb_status & IO_MASK(R_USB_STATUS, host_mode)) {
-                //info("USB controller in host mode.");
-        }
-        if (reg->r_usb_status & IO_MASK(R_USB_STATUS, started)) {
-                //info("USB controller started.");
-        }
-        if (reg->r_usb_status & IO_MASK(R_USB_STATUS, running)) {
-                info("USB controller running.");
-        }
+	/* FIXME: What should we do if we get ourun or perror? Dump the EP and SB
+	   list for the corresponding epid? */
+	if (reg->r_usb_status & IO_MASK(R_USB_STATUS, ourun)) {
+		panic("USB controller got ourun.");
+	} 
+	if (reg->r_usb_status & IO_MASK(R_USB_STATUS, perror)) {
+
+		/* Before, etrax_usb_do_intr_recover was called on this epid if it was
+		   an interrupt pipe. I don't see how re-enabling all EP descriptors
+		   will help if there was a programming error. */
+		panic("USB controller got perror.");
+	} 
+
+	if (reg->r_usb_status & IO_MASK(R_USB_STATUS, device_mode)) {
+		/* We should never operate in device mode. */
+		panic("USB controller in device mode.");
+	}
+
+	/* These if-statements could probably be nested. */
+	if (reg->r_usb_status & IO_MASK(R_USB_STATUS, host_mode)) {
+		//info("USB controller in host mode.");
+	}
+	if (reg->r_usb_status & IO_MASK(R_USB_STATUS, started)) {
+		//info("USB controller started.");
+	}
+	if (reg->r_usb_status & IO_MASK(R_USB_STATUS, running)) {
+		info("USB controller running.");
+	}
 
-        DBFEXIT;
-        
+	DBFEXIT;
+	
 }
 
 
@@ -3255,14 +3880,14 @@
 
 	DBFENTER;
 
-        /* FIXME: What is this interrupt urb that is sent to the root hub? */
+	/* FIXME: What is this interrupt urb that is sent to the root hub? */
 	if (usb_pipetype (pipe) == PIPE_INTERRUPT) {
 		dbg_rh("Root-Hub submit IRQ: every %d ms", urb->interval);
 		hc->rh.urb = urb;
 		hc->rh.send = 1;
-                /* FIXME: We could probably remove this line since it's done
-                   in etrax_rh_init_int_timer. (Don't remove it from
-                   etrax_rh_init_int_timer though.) */
+		/* FIXME: We could probably remove this line since it's done
+		   in etrax_rh_init_int_timer. (Don't remove it from
+		   etrax_rh_init_int_timer though.) */
 		hc->rh.interval = urb->interval;
 		etrax_rh_init_int_timer(urb);
 		DBFEXIT;
@@ -3272,14 +3897,14 @@
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION (2, 4, 20)
 	bmRType_bReq = cmd->bRequestType | (cmd->bRequest << 8);
-        wValue = le16_to_cpu(cmd->wValue);
-        wIndex = le16_to_cpu(cmd->wIndex);
-        wLength = le16_to_cpu(cmd->wLength);
+	wValue = le16_to_cpu(cmd->wValue);
+	wIndex = le16_to_cpu(cmd->wIndex);
+	wLength = le16_to_cpu(cmd->wLength);
 #else
-        bmRType_bReq = cmd->requesttype | (cmd->request << 8);
-        wValue = le16_to_cpu(cmd->value);
-        wIndex = le16_to_cpu(cmd->index);
-        wLength = le16_to_cpu(cmd->length);
+	bmRType_bReq = cmd->requesttype | (cmd->request << 8);
+	wValue = le16_to_cpu(cmd->value);
+	wIndex = le16_to_cpu(cmd->index);
+	wLength = le16_to_cpu(cmd->length);
 #endif
 									
 	dbg_rh("bmRType_bReq : 0x%04x (%d)", bmRType_bReq, bmRType_bReq);
@@ -3374,15 +3999,15 @@
 			OK (0);
 		case (RH_PORT_SUSPEND):
 			/* Opposite to suspend should be resume, so we'll do a resume. */
-                        /* FIXME: USB 1.1, 11.16.2.2 says:
-                           "Clearing the PORT_SUSPEND feature causes a host-initiated resume
-                           on the specified port. If the port is not in the Suspended state,
-                           the hub should treat this request as a functional no-operation."
-                           Shouldn't we check if the port is in a suspended state before
-                           resuming? */
-                        
-                        /* Make sure the controller isn't busy. */
-                        while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+			/* FIXME: USB 1.1, 11.16.2.2 says:
+			   "Clearing the PORT_SUSPEND feature causes a host-initiated resume
+			   on the specified port. If the port is not in the Suspended state,
+			   the hub should treat this request as a functional no-operation."
+			   Shouldn't we check if the port is in a suspended state before
+			   resuming? */
+			
+			/* Make sure the controller isn't busy. */
+			while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
 
 			if (wIndex == 1) {
 				*R_USB_COMMAND =
@@ -3448,8 +4073,8 @@
 		switch (wValue) {
 		case (RH_PORT_SUSPEND):
 
-                        /* Make sure the controller isn't busy. */
-                        while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+			/* Make sure the controller isn't busy. */
+			while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
 
 			if (wIndex == 1) {
 				*R_USB_COMMAND =
@@ -3472,115 +4097,115 @@
 				
 			port_1_reset:
 				dbg_rh("Doing reset of port 1");
-                                
-                                /* Make sure the controller isn't busy. */
-                                while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+				
+				/* Make sure the controller isn't busy. */
+				while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
 
 				*R_USB_COMMAND =
 					IO_STATE(R_USB_COMMAND, port_sel, port1) | 
 					IO_STATE(R_USB_COMMAND, port_cmd, reset) |
 					IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
 
-                                /* We must wait at least 10 ms for the device to recover.
-                                   15 ms should be enough. */
-                                udelay(15000);
-                                
-                                /* Wait for reset bit to go low (should be done by now). */
-                                while (hc->rh.prev_wPortStatus_1 &
-                                       IO_STATE(R_USB_RH_PORT_STATUS_1, reset, yes));
-                                
-                                /* If the port status is
-                                   1) connected and enabled then there is a device and everything is fine
-                                   2) neither connected nor enabled then there is no device, also fine
-                                   3) connected and not enabled then we try again
-                                   (Yes, there are other port status combinations besides these.) */
-
-                                if ((hc->rh.prev_wPortStatus_1 & 
-                                     IO_STATE(R_USB_RH_PORT_STATUS_1, connected, yes)) &&
-                                    (hc->rh.prev_wPortStatus_1 & 
-                                     IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, no))) {
-                                        dbg_rh("Connected device on port 1, but port not enabled?"
-                                               " Trying reset again.");
-                                        goto port_2_reset;
-                                }
-
-                                /* Diagnostic printouts. */
-                                if ((hc->rh.prev_wPortStatus_1 & 
-                                     IO_STATE(R_USB_RH_PORT_STATUS_1, connected, no)) &&
-                                    (hc->rh.prev_wPortStatus_1 & 
-                                     IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, no))) {
-                                        dbg_rh("No connected device on port 1");
-                                } else if ((hc->rh.prev_wPortStatus_1 & 
-                                            IO_STATE(R_USB_RH_PORT_STATUS_1, connected, yes)) &&
-                                           (hc->rh.prev_wPortStatus_1 & 
-                                            IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, yes))) {
-                                        dbg_rh("Connected device on port 1, port 1 enabled");
-                                }
+				/* We must wait at least 10 ms for the device to recover.
+				   15 ms should be enough. */
+				udelay(15000);
+				
+				/* Wait for reset bit to go low (should be done by now). */
+				while (hc->rh.prev_wPortStatus_1 &
+				       IO_STATE(R_USB_RH_PORT_STATUS_1, reset, yes));
+				
+				/* If the port status is
+				   1) connected and enabled then there is a device and everything is fine
+				   2) neither connected nor enabled then there is no device, also fine
+				   3) connected and not enabled then we try again
+				   (Yes, there are other port status combinations besides these.) */
+
+				if ((hc->rh.prev_wPortStatus_1 & 
+				     IO_STATE(R_USB_RH_PORT_STATUS_1, connected, yes)) &&
+				    (hc->rh.prev_wPortStatus_1 & 
+				     IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, no))) {
+					dbg_rh("Connected device on port 1, but port not enabled?"
+					       " Trying reset again.");
+					goto port_2_reset;
+				}
+
+				/* Diagnostic printouts. */
+				if ((hc->rh.prev_wPortStatus_1 & 
+				     IO_STATE(R_USB_RH_PORT_STATUS_1, connected, no)) &&
+				    (hc->rh.prev_wPortStatus_1 & 
+				     IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, no))) {
+					dbg_rh("No connected device on port 1");
+				} else if ((hc->rh.prev_wPortStatus_1 & 
+					    IO_STATE(R_USB_RH_PORT_STATUS_1, connected, yes)) &&
+					   (hc->rh.prev_wPortStatus_1 & 
+					    IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, yes))) {
+					dbg_rh("Connected device on port 1, port 1 enabled");
+				}
 
 			} else if (wIndex == 2) {
-                                
+				
 			port_2_reset:
 				dbg_rh("Doing reset of port 2");
 				
-                                /* Make sure the controller isn't busy. */
-                                while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+				/* Make sure the controller isn't busy. */
+				while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
 
-                                /* Issue the reset command. */
+				/* Issue the reset command. */
 				*R_USB_COMMAND =
 					IO_STATE(R_USB_COMMAND, port_sel, port2) |
 					IO_STATE(R_USB_COMMAND, port_cmd, reset) |
 					IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
 
 				/* We must wait at least 10 ms for the device to recover.
-                                   15 ms should be enough. */
-                                udelay(15000);
-                                
-                                /* Wait for reset bit to go low (should be done by now). */
-                                while (hc->rh.prev_wPortStatus_2 &
-                                       IO_STATE(R_USB_RH_PORT_STATUS_2, reset, yes));
-                                
-                                /* If the port status is
-                                   1) connected and enabled then there is a device and everything is fine
-                                   2) neither connected nor enabled then there is no device, also fine
-                                   3) connected and not enabled then we try again
-                                   (Yes, there are other port status combinations besides these.) */
-
-                                if ((hc->rh.prev_wPortStatus_2 & 
-                                     IO_STATE(R_USB_RH_PORT_STATUS_2, connected, yes)) &&
-                                    (hc->rh.prev_wPortStatus_2 & 
-                                     IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, no))) {
-                                        dbg_rh("Connected device on port 2, but port not enabled?"
-                                               " Trying reset again.");
-                                        goto port_2_reset;
-                                }
-
-                                /* Diagnostic printouts. */
-                                if ((hc->rh.prev_wPortStatus_2 & 
-                                     IO_STATE(R_USB_RH_PORT_STATUS_2, connected, no)) &&
-                                    (hc->rh.prev_wPortStatus_2 & 
-                                     IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, no))) {
-                                        dbg_rh("No connected device on port 2");
-                                } else if ((hc->rh.prev_wPortStatus_2 & 
-                                            IO_STATE(R_USB_RH_PORT_STATUS_2, connected, yes)) &&
-                                           (hc->rh.prev_wPortStatus_2 & 
-                                            IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, yes))) {
-                                        dbg_rh("Connected device on port 2, port 2 enabled");
-                                }
+				   15 ms should be enough. */
+				udelay(15000);
+				
+				/* Wait for reset bit to go low (should be done by now). */
+				while (hc->rh.prev_wPortStatus_2 &
+				       IO_STATE(R_USB_RH_PORT_STATUS_2, reset, yes));
+				
+				/* If the port status is
+				   1) connected and enabled then there is a device and everything is fine
+				   2) neither connected nor enabled then there is no device, also fine
+				   3) connected and not enabled then we try again
+				   (Yes, there are other port status combinations besides these.) */
+
+				if ((hc->rh.prev_wPortStatus_2 & 
+				     IO_STATE(R_USB_RH_PORT_STATUS_2, connected, yes)) &&
+				    (hc->rh.prev_wPortStatus_2 & 
+				     IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, no))) {
+					dbg_rh("Connected device on port 2, but port not enabled?"
+					       " Trying reset again.");
+					goto port_2_reset;
+				}
+
+				/* Diagnostic printouts. */
+				if ((hc->rh.prev_wPortStatus_2 & 
+				     IO_STATE(R_USB_RH_PORT_STATUS_2, connected, no)) &&
+				    (hc->rh.prev_wPortStatus_2 & 
+				     IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, no))) {
+					dbg_rh("No connected device on port 2");
+				} else if ((hc->rh.prev_wPortStatus_2 & 
+					    IO_STATE(R_USB_RH_PORT_STATUS_2, connected, yes)) &&
+					   (hc->rh.prev_wPortStatus_2 & 
+					    IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, yes))) {
+					dbg_rh("Connected device on port 2, port 2 enabled");
+				}
 
 			} else {
 				dbg_rh("RH_SET_FEATURE->RH_PORT_RESET with invalid wIndex = %d", wIndex);
-                        }
-                        
-                        /* Make sure the controller isn't busy. */
-                        while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+			}
+			
+			/* Make sure the controller isn't busy. */
+			while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
 
 			/* If all enabled ports were disabled the host controller goes down into
-                           started mode, so we need to bring it back into the running state.
-                           (This is safe even if it's already in the running state.) */
-                        *R_USB_COMMAND =
-                                IO_STATE(R_USB_COMMAND, port_sel, nop) |
-                                IO_STATE(R_USB_COMMAND, port_cmd, reset) |
-                                IO_STATE(R_USB_COMMAND, ctrl_cmd, host_run);
+			   started mode, so we need to bring it back into the running state.
+			   (This is safe even if it's already in the running state.) */
+			*R_USB_COMMAND =
+				IO_STATE(R_USB_COMMAND, port_sel, nop) |
+				IO_STATE(R_USB_COMMAND, port_cmd, reset) |
+				IO_STATE(R_USB_COMMAND, ctrl_cmd, host_run);
 
 			dbg_rh("...Done");
 			OK(0);
@@ -3589,22 +4214,22 @@
 			OK (0);	/* port power ** */
 		case (RH_PORT_ENABLE):
 			/* There is no port enable command in the host controller, so if the
-                           port is already enabled, we do nothing. If not, we reset the port
-                           (with an ugly goto). */
+			   port is already enabled, we do nothing. If not, we reset the port
+			   (with an ugly goto). */
 
-                        if (wIndex == 1) {
-                                if (hc->rh.prev_wPortStatus_1 & 
-                                    IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, no)) {
-                                        goto port_1_reset;
-                                }
-                        } else if (wIndex == 2) {
-                                if (hc->rh.prev_wPortStatus_2 & 
-                                    IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, no)) {
-                                        goto port_2_reset;
-                                }
-                        } else {
-                                dbg_rh("RH_SET_FEATURE->RH_GET_STATUS with invalid wIndex = %d", wIndex);
-                        }
+			if (wIndex == 1) {
+				if (hc->rh.prev_wPortStatus_1 & 
+				    IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, no)) {
+					goto port_1_reset;
+				}
+			} else if (wIndex == 2) {
+				if (hc->rh.prev_wPortStatus_2 & 
+				    IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, no)) {
+					goto port_2_reset;
+				}
+			} else {
+				dbg_rh("RH_SET_FEATURE->RH_GET_STATUS with invalid wIndex = %d", wIndex);
+			}
 			OK (0);
 		}
 		break;
@@ -3632,8 +4257,8 @@
 				OK(min(leni, len));
 			} else {
 				stat = -EPIPE;
-                        }
-                        
+			}
+			
 		}
 		break;
 		
@@ -3668,11 +4293,11 @@
 static void
 etrax_usb_bulk_eot_timer_func(unsigned long dummy)
 {
-        /* Because of a race condition in the top half, we might miss a bulk eot.
-           This timer "simulates" a bulk eot if we don't get one for a while, hopefully
-           correcting the situation. */
-        dbg_bulk("bulk_eot_timer timed out.");
-        etrax_usb_hc_bulk_eot_interrupt(1);
+	/* Because of a race condition in the top half, we might miss a bulk eot.
+	   This timer "simulates" a bulk eot if we don't get one for a while, hopefully
+	   correcting the situation. */
+	dbg_bulk("bulk_eot_timer timed out.");
+	etrax_usb_hc_bulk_eot_interrupt(1);
 }
 
 static int __init etrax_usb_hc_init(void)
@@ -3681,7 +4306,7 @@
 	struct usb_bus *bus;
 	struct usb_device *usb_rh;
 	int i;
-        
+	
 	DBFENTER;
 
 	info("ETRAX 100LX USB-HCD %s (c) 2001-2003 Axis Communications AB\n", usb_hcd_version);
@@ -3690,19 +4315,24 @@
 	assert(hc != NULL);
 
 	/* We use kmem_cache_* to make sure that all DMA desc. are dword aligned */
-        /* Note that we specify sizeof(USB_EP_Desc_t) as the size, but also allocate
-           SB descriptors from this cache. This is ok since sizeof(USB_EP_Desc_t) ==
-           sizeof(USB_SB_Desc_t). */
+	/* Note that we specify sizeof(USB_EP_Desc_t) as the size, but also allocate
+	   SB descriptors from this cache. This is ok since sizeof(USB_EP_Desc_t) ==
+	   sizeof(USB_SB_Desc_t). */
 
 	usb_desc_cache = kmem_cache_create("usb_desc_cache", sizeof(USB_EP_Desc_t), 0, 
-                                           SLAB_HWCACHE_ALIGN, 0, 0);
+					   SLAB_HWCACHE_ALIGN, 0, 0);
 	assert(usb_desc_cache != NULL);
-	
+
 	top_half_reg_cache = kmem_cache_create("top_half_reg_cache", 
 					       sizeof(usb_interrupt_registers_t), 
 					       0, SLAB_HWCACHE_ALIGN, 0, 0);
 	assert(top_half_reg_cache != NULL);
 
+	isoc_compl_cache = kmem_cache_create("isoc_compl_cache", 
+						sizeof(usb_isoc_complete_data_t),
+						0, SLAB_HWCACHE_ALIGN, 0, 0);
+	assert(isoc_compl_cache != NULL);
+
 	etrax_usb_bus = bus = usb_alloc_bus(&etrax_usb_device_operations);
 	hc->bus = bus;
 #if LINUX_VERSION_CODE >= KERNEL_VERSION (2, 4, 20)
@@ -3722,50 +4352,50 @@
 	hc->rh.prev_wPortStatus_2 = 0;
 
 	/* Initialize the intr-traffic flags */
-        /* FIXME: This isn't used. (Besides, the error field isn't initialized.) */
+	/* FIXME: This isn't used. (Besides, the error field isn't initialized.) */
 	hc->intr.sleeping = 0;
 	hc->intr.wq = NULL;
 
 	epid_usage_bitmask = 0;
 	epid_out_traffic = 0;
 
-        /* Mark the invalid epid as being used. */
+	/* Mark the invalid epid as being used. */
 	set_bit(INVALID_EPID, (void *)&epid_usage_bitmask);
-        *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, INVALID_EPID);
-        nop();
-        /* The valid bit should still be set ('invalid' is in our world; not the hardware's). */
-        *R_USB_EPT_DATA = (IO_STATE(R_USB_EPT_DATA, valid, yes) |
+	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, INVALID_EPID);
+	nop();
+	/* The valid bit should still be set ('invalid' is in our world; not the hardware's). */
+	*R_USB_EPT_DATA = (IO_STATE(R_USB_EPT_DATA, valid, yes) |
 			   IO_FIELD(R_USB_EPT_DATA, max_len, 1));
 
 	/* Mark the dummy epid as being used. */
 	set_bit(DUMMY_EPID, (void *)&epid_usage_bitmask);
-        *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, DUMMY_EPID);
-        nop();
-        *R_USB_EPT_DATA = (IO_STATE(R_USB_EPT_DATA, valid, no) |
+	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, DUMMY_EPID);
+	nop();
+	*R_USB_EPT_DATA = (IO_STATE(R_USB_EPT_DATA, valid, no) |
 			   IO_FIELD(R_USB_EPT_DATA, max_len, 1));
 
-        /* Initialize the urb list by initiating a head for each list. */
-        for (i = 0; i < NBR_OF_EPIDS; i++) {
-                INIT_LIST_HEAD(&urb_list[i]);
-        }
+	/* Initialize the urb list by initiating a head for each list. */
+	for (i = 0; i < NBR_OF_EPIDS; i++) {
+		INIT_LIST_HEAD(&urb_list[i]);
+	}
 	spin_lock_init(&urb_list_lock);
 
-        INIT_LIST_HEAD(&urb_unlink_list);
-                        
+	INIT_LIST_HEAD(&urb_unlink_list);
+
 
-        /* Initiate the bulk start timer. */
-        init_timer(&bulk_start_timer);
-        bulk_start_timer.expires = jiffies + BULK_START_TIMER_INTERVAL;
-        bulk_start_timer.function = etrax_usb_bulk_start_timer_func;
-        add_timer(&bulk_start_timer);
-
-
-        /* Initiate the bulk eot timer. */
-        init_timer(&bulk_eot_timer);
-        bulk_eot_timer.expires = jiffies + BULK_EOT_TIMER_INTERVAL;
-        bulk_eot_timer.function = etrax_usb_bulk_eot_timer_func;
-        add_timer(&bulk_eot_timer);
-        
+	/* Initiate the bulk start timer. */
+	init_timer(&bulk_start_timer);
+	bulk_start_timer.expires = jiffies + BULK_START_TIMER_INTERVAL;
+	bulk_start_timer.function = etrax_usb_bulk_start_timer_func;
+	add_timer(&bulk_start_timer);
+
+
+	/* Initiate the bulk eot timer. */
+	init_timer(&bulk_eot_timer);
+	bulk_eot_timer.expires = jiffies + BULK_EOT_TIMER_INTERVAL;
+	bulk_eot_timer.function = etrax_usb_bulk_eot_timer_func;
+	add_timer(&bulk_eot_timer);
+	
 	/* This code should really be moved */
 
 	if (request_dma(USB_TX_DMA_NBR, "ETRAX 100LX built-in USB (Tx)")) {
@@ -3782,8 +4412,8 @@
 		return -1;
 	}
 
-        /* Set up the data structures for USB traffic. Note that this must be done before
-           any interrupt that relies on sane DMA list occurrs. */
+	/* Set up the data structures for USB traffic. Note that this must be done before
+	   any interrupt that relies on sane DMA list occurrs. */
 	init_rx_buffers();
 	init_tx_bulk_ep();
 	init_tx_ctrl_ep();
@@ -3793,25 +4423,27 @@
 	
 	usb_register_bus(hc->bus);
 
-        /* Note that these interrupts are not used. */
 	*R_IRQ_MASK2_SET =
+		/* Note that these interrupts are not used. */
 		IO_STATE(R_IRQ_MASK2_SET, dma8_sub0_descr, set) |
+		/* Sub channel 1 (ctrl) descr. interrupts are used. */
 		IO_STATE(R_IRQ_MASK2_SET, dma8_sub1_descr, set) |
 		IO_STATE(R_IRQ_MASK2_SET, dma8_sub2_descr, set) |
+		/* Sub channel 3 (isoc) descr. interrupts are used. */
 		IO_STATE(R_IRQ_MASK2_SET, dma8_sub3_descr, set);
 
-        /* Note that the dma9_descr interrupt is not used. */
+	/* Note that the dma9_descr interrupt is not used. */
 	*R_IRQ_MASK2_SET =
 		IO_STATE(R_IRQ_MASK2_SET, dma9_eop, set) |
 		IO_STATE(R_IRQ_MASK2_SET, dma9_descr, set);
 
 	/* FIXME: Enable iso_eof only when isoc traffic is running. */
 	*R_USB_IRQ_MASK_SET = 
-                IO_STATE(R_USB_IRQ_MASK_SET, iso_eof, set) |
+		IO_STATE(R_USB_IRQ_MASK_SET, iso_eof, set) |
 		IO_STATE(R_USB_IRQ_MASK_SET, bulk_eot, set) |
 		IO_STATE(R_USB_IRQ_MASK_SET, epid_attn, set) |
 		IO_STATE(R_USB_IRQ_MASK_SET, port_status, set) |
-                IO_STATE(R_USB_IRQ_MASK_SET, ctl_status, set);
+		IO_STATE(R_USB_IRQ_MASK_SET, ctl_status, set);
 
 	
 	if (request_irq(ETRAX_USB_HC_IRQ, etrax_usb_hc_interrupt_top_half, 0,
@@ -3821,7 +4453,7 @@
 		DBFEXIT;
 		return -1;
 	}
-	
+
 	if (request_irq(ETRAX_USB_RX_IRQ, etrax_usb_rx_interrupt, 0,
 			"ETRAX 100LX built-in USB (Rx)", hc)) {
 		err("Could not allocate IRQ %d for USB", ETRAX_USB_RX_IRQ);
@@ -3829,7 +4461,7 @@
 		DBFEXIT;
 		return -1;
 	}
-	
+
 	if (request_irq(ETRAX_USB_TX_IRQ, etrax_usb_tx_interrupt, 0,
 			"ETRAX 100LX built-in USB (Tx)", hc)) {
 		err("Could not allocate IRQ %d for USB", ETRAX_USB_TX_IRQ);
@@ -3838,81 +4470,81 @@
 		return -1;
 	}
 
-        /* R_USB_COMMAND:
-           USB commands in host mode. The fields in this register should all be
-           written to in one write. Do not read-modify-write one field at a time. A
-           write to this register will trigger events in the USB controller and an
-           incomplete command may lead to unpredictable results, and in worst case
-           even to a deadlock in the controller.
-           (Note however that the busy field is read-only, so no need to write to it.) */
+	/* R_USB_COMMAND:
+	   USB commands in host mode. The fields in this register should all be
+	   written to in one write. Do not read-modify-write one field at a time. A
+	   write to this register will trigger events in the USB controller and an
+	   incomplete command may lead to unpredictable results, and in worst case
+	   even to a deadlock in the controller.
+	   (Note however that the busy field is read-only, so no need to write to it.) */
 
-        /* Check the busy bit before writing to R_USB_COMMAND. */
+	/* Check the busy bit before writing to R_USB_COMMAND. */
 
-        while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+	while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
  
 	/* Reset the USB interface. */
 	*R_USB_COMMAND =
-                IO_STATE(R_USB_COMMAND, port_sel, nop) |
+		IO_STATE(R_USB_COMMAND, port_sel, nop) |
 		IO_STATE(R_USB_COMMAND, port_cmd, reset) |
 		IO_STATE(R_USB_COMMAND, ctrl_cmd, reset);
 	
 	/* Designer's Reference, p. 8 - 10 says we should Initate R_USB_FM_PSTART to 0x2A30 (10800),
-           to guarantee that control traffic gets 10% of the bandwidth, and periodic transfer may
-           allocate the rest (90%). This doesn't work though. Read on for a lenghty explanation.
+	   to guarantee that control traffic gets 10% of the bandwidth, and periodic transfer may
+	   allocate the rest (90%). This doesn't work though. Read on for a lenghty explanation.
 
-           While there is a difference between rev. 2 and rev. 3 of the ETRAX 100LX regarding the NAK
-           behaviour, it doesn't solve this problem. What happens is that a control transfer will not
-           be interrupted in its data stage when PSTART happens (the point at which periodic traffic
-           is started). Thus, if PSTART is set to 10800 and its IN or OUT token is NAKed until just before
-           PSTART happens, it will continue the IN/OUT transfer as long as it's ACKed. After it's done,
-           there may be too little time left for an isochronous transfer, causing an epid attention 
-           interrupt due to perror. The work-around for this is to let the control transfers run at the
-           end of the frame instead of at the beginning, and will be interrupted just fine if it doesn't
-           fit into the frame. However, since there will *always* be a control transfer at the beginning
-           of the frame, regardless of what we set PSTART to, that transfer might be a 64-byte transfer
-           which consumes up to 15% of the frame, leaving only 85% for periodic traffic. The solution to
-           this would be to 'dummy allocate' 5% of the frame with the usb_claim_bandwidth function to make
-           sure that the periodic transfers that are inserted will always fit in the frame.
-
-           The idea was suggested that a control transfer could be split up into several 8 byte transfers,
-           so that it would be interrupted by PSTART, but since this can't be done for an IN transfer this
-           hasn't been implemented.
+	   While there is a difference between rev. 2 and rev. 3 of the ETRAX 100LX regarding the NAK
+	   behaviour, it doesn't solve this problem. What happens is that a control transfer will not
+	   be interrupted in its data stage when PSTART happens (the point at which periodic traffic
+	   is started). Thus, if PSTART is set to 10800 and its IN or OUT token is NAKed until just before
+	   PSTART happens, it will continue the IN/OUT transfer as long as it's ACKed. After it's done,
+	   there may be too little time left for an isochronous transfer, causing an epid attention 
+	   interrupt due to perror. The work-around for this is to let the control transfers run at the
+	   end of the frame instead of at the beginning, and will be interrupted just fine if it doesn't
+	   fit into the frame. However, since there will *always* be a control transfer at the beginning
+	   of the frame, regardless of what we set PSTART to, that transfer might be a 64-byte transfer
+	   which consumes up to 15% of the frame, leaving only 85% for periodic traffic. The solution to
+	   this would be to 'dummy allocate' 5% of the frame with the usb_claim_bandwidth function to make
+	   sure that the periodic transfers that are inserted will always fit in the frame.
+
+	   The idea was suggested that a control transfer could be split up into several 8 byte transfers,
+	   so that it would be interrupted by PSTART, but since this can't be done for an IN transfer this
+	   hasn't been implemented.
 
-           The value 11960 is chosen to be just after the SOF token, with a couple of bit times extra
-           for possible bit stuffing. */
+	   The value 11960 is chosen to be just after the SOF token, with a couple of bit times extra
+	   for possible bit stuffing. */
 
-        *R_USB_FM_PSTART = IO_FIELD(R_USB_FM_PSTART, value, 11960);
+	*R_USB_FM_PSTART = IO_FIELD(R_USB_FM_PSTART, value, 11960);
 
 #ifdef CONFIG_ETRAX_USB_HOST_PORT1
 	*R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, no);
 #endif
-	
+
 #ifdef CONFIG_ETRAX_USB_HOST_PORT2
 	*R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, no);
 #endif
 	
-        while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+	while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
 
-        /* Configure the USB interface as a host controller. */
+	/* Configure the USB interface as a host controller. */
 	*R_USB_COMMAND =
-                IO_STATE(R_USB_COMMAND, port_sel, nop) |
-                IO_STATE(R_USB_COMMAND, port_cmd, reset) |
+		IO_STATE(R_USB_COMMAND, port_sel, nop) |
+		IO_STATE(R_USB_COMMAND, port_cmd, reset) |
 		IO_STATE(R_USB_COMMAND, ctrl_cmd, host_config);
 
-        /* Note: Do not reset any ports here. Await the port status interrupts, to have a controlled
-           sequence of resetting the ports. If we reset both ports now, and there are devices
-           on both ports, we will get a bus error because both devices will answer the set address
-           request. */
+	/* Note: Do not reset any ports here. Await the port status interrupts, to have a controlled
+	   sequence of resetting the ports. If we reset both ports now, and there are devices
+	   on both ports, we will get a bus error because both devices will answer the set address
+	   request. */
 
-        while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+	while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
 
-        /* Start processing of USB traffic. */
+	/* Start processing of USB traffic. */
 	*R_USB_COMMAND =
 		IO_STATE(R_USB_COMMAND, port_sel, nop) |
 		IO_STATE(R_USB_COMMAND, port_cmd, reset) |
 		IO_STATE(R_USB_COMMAND, ctrl_cmd, host_run);
 
-        while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+	while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
 
 	usb_rh = usb_alloc_dev(NULL, hc->bus);
 	hc->bus->root_hub = usb_rh;
@@ -3936,7 +4568,7 @@
 	free_dma(USB_RX_DMA_NBR);
 	usb_deregister_bus(etrax_usb_bus);
 
-        /* FIXME: call kmem_cache_destroy here? */
+	/* FIXME: call kmem_cache_destroy here? */
 
 	DBFEXIT;
 }
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/drivers/usb-host.h ./arch/cris/drivers/usb-host.h
--- /home/anderstj/linux-2.4.26/arch/cris/drivers/usb-host.h	2003-08-25 13:44:39.000000000 +0200
+++ ./arch/cris/drivers/usb-host.h	2004-05-11 15:10:05.000000000 +0200
@@ -9,7 +9,7 @@
 	volatile __u16 command;
 	volatile unsigned long next;
 	volatile unsigned long buf;
-        volatile __u16 hw_len;
+	volatile __u16 hw_len;
 	volatile __u16 status;
 } USB_IN_Desc_t;
 
@@ -56,45 +56,47 @@
 } etrax_hc_t;
 
 typedef enum {
-        STARTED,
-        NOT_STARTED,
-        UNLINK
+	STARTED,
+	NOT_STARTED,
+	UNLINK,
+	TRANSFER_DONE,
+	WAITING_FOR_DESCR_INTR
 } etrax_usb_urb_state_t;
 
 
 
 typedef struct etrax_usb_urb_priv {
-        /* The first_sb field is used for freeing all SB descriptors belonging
-           to an urb. The corresponding ep descriptor's sub pointer cannot be
-           used for this since the DMA advances the sub pointer as it processes
-           the sb list. */
+	/* The first_sb field is used for freeing all SB descriptors belonging
+	   to an urb. The corresponding ep descriptor's sub pointer cannot be
+	   used for this since the DMA advances the sub pointer as it processes
+	   the sb list. */
 	USB_SB_Desc_t *first_sb;
 	/* The last_sb field referes to the last SB descriptor that belongs to
-           this urb. This is important to know so we can free the SB descriptors
+	   this urb. This is important to know so we can free the SB descriptors
 	   that ranges between first_sb and last_sb. */	
 	USB_SB_Desc_t *last_sb;
 
-        /* The rx_offset field is used in ctrl and bulk traffic to keep track
-           of the offset in the urb's transfer_buffer where incoming data should be
-           copied to. */
+	/* The rx_offset field is used in ctrl and bulk traffic to keep track
+	   of the offset in the urb's transfer_buffer where incoming data should be
+	   copied to. */
 	__u32 rx_offset;
 
 	/* Counter used in isochronous transfers to keep track of the
 	   number of packets received/transmitted.  */
-        __u32 isoc_packet_counter;	
-        
-        /* This field is used to pass information about the urb's current state between
-           the various interrupt handlers (thus marked volatile). */
+	__u32 isoc_packet_counter;	
+	
+	/* This field is used to pass information about the urb's current state between
+	   the various interrupt handlers (thus marked volatile). */
 	volatile etrax_usb_urb_state_t urb_state;
 
 	/* Connection between the submitted urb and ETRAX epid number */
 	__u8 epid;
 
-        /* The rx_data_list field is used for periodic traffic, to hold 
-           received data for later processing in the the complete_urb functions,
-           where the data us copied to the urb's transfer_buffer. Basically, we
-           use this intermediate storage because we don't know when it's safe to
-           reuse the transfer_buffer (FIXME?). */
+	/* The rx_data_list field is used for periodic traffic, to hold 
+	   received data for later processing in the the complete_urb functions,
+	   where the data us copied to the urb's transfer_buffer. Basically, we
+	   use this intermediate storage because we don't know when it's safe to
+	   reuse the transfer_buffer (FIXME?). */
 	struct list_head rx_data_list;
 } etrax_urb_priv_t;
 
@@ -107,10 +109,17 @@
 	__u16 r_usb_rh_port_status_1;
 	__u16 r_usb_rh_port_status_2;
 	__u32 r_usb_irq_mask_read;
-        __u32 r_usb_fm_number;
+	__u32 r_usb_fm_number;
 	struct tq_struct usb_bh;
 } usb_interrupt_registers_t;
 
+/* This struct is for passing data from the isoc top half to the isoc bottom half. */
+typedef struct usb_isoc_complete_data
+{
+	struct urb *urb;
+	struct tq_struct usb_bh;
+} usb_isoc_complete_data_t;
+
 /* This struct holds data we get from the rx descriptors for DMA channel 9
    for periodic traffic (intr and isoc). */
 typedef struct rx_data
@@ -122,7 +131,7 @@
 
 typedef struct urb_entry
 {
-        urb_t *urb;
+	urb_t *urb;
 	struct list_head list;
 } urb_entry_t;
 
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/kernel/.cvsignore ./arch/cris/kernel/.cvsignore
--- /home/anderstj/linux-2.4.26/arch/cris/kernel/.cvsignore	1970-01-01 01:00:00.000000000 +0100
+++ ./arch/cris/kernel/.cvsignore	2001-07-25 18:09:21.000000000 +0200
@@ -0,0 +1 @@
+entryoffsets.s
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/kernel/debugport.c ./arch/cris/kernel/debugport.c
--- /home/anderstj/linux-2.4.26/arch/cris/kernel/debugport.c	2003-08-25 13:44:39.000000000 +0200
+++ ./arch/cris/kernel/debugport.c	2004-01-30 08:23:32.000000000 +0100
@@ -12,6 +12,17 @@
  *    init_etrax_debug()
  *
  * $Log: debugport.c,v $
+ * Revision 1.12  2004/01/30 07:23:32  starvik
+ * Disable IRQs in console_write
+ *
+ * Revision 1.11  2004/01/27 12:43:29  starvik
+ * Disable serial port DMAs
+ *
+ * Revision 1.10  2004/01/27 11:45:38  starvik
+ * Removed all the ugly DMA hacks to make it work when the serial port driver
+ * is interrupt driven. Now uses the real serial port driver if available
+ * (fallbacks to manual mode).
+ *
  * Revision 1.9  2003/02/17 07:10:34  starvik
  * Last merge was incomplete
  *
@@ -47,6 +58,7 @@
 #include <asm/system.h>
 #include <asm/svinto.h>
 #include <asm/io.h>             /* Get SIMCOUT. */
+#include <asm/hardirq.h>
 
 /* Which serial-port is our debug port ? */
 
@@ -106,23 +118,29 @@
 #define DEBUG_DMA_IRQ_CLR IO_STATE(R_IRQ_MASK2_CLR, dma4_descr, clr)
 #endif
 
-#define MIN_SIZE 32 /* Size that triggers the FIFO to flush characters to interface */
-
-/* Write a string of count length to the console (debug port) using DMA, polled
- * for completion. Interrupts are disabled during the whole process. Some
- * caution needs to be taken to not interfere with ttyS business on this port.
+/* Used by serial.c to register a debug_write_function so that the normal
+ * serial driver is used for kernel debug output
  */
+typedef int (*debugport_write_function)(int i, const char *buf, unsigned int len);
+
+debugport_write_function debug_write_function = NULL;
+
+static void 
+console_write_direct(struct console *co, const char *buf, unsigned int len)
+{  
+	int i;
+	/* Send data */
+	for (i = 0; i < len; i++) {
+		/* Wait until transmitter is ready and send.*/
+		while(!(*DEBUG_READ & IO_MASK(R_SERIAL0_READ, tr_ready)));
+                *DEBUG_WRITE = buf[i];
+	}
+}
 
 static void 
 console_write(struct console *co, const char *buf, unsigned int len)
 {
-	static struct etrax_dma_descr descr;
-	static struct etrax_dma_descr descr2;
-	static char tmp_buf[MIN_SIZE];
-	static int tmp_size = 0;
-
-	unsigned long flags; 
-	
+	unsigned long flags;
 #ifdef CONFIG_ETRAX_DEBUG_PORT_NULL
         /* no debug printout at all */
         return;
@@ -133,86 +151,19 @@
 	SIMCOUT(buf,len);
 	return;
 #endif
-	
-	save_flags(flags);
-	cli();
 
 #ifdef CONFIG_ETRAX_KGDB
 	/* kgdb needs to output debug info using the gdb protocol */
 	putDebugString(buf, len);
-	restore_flags(flags);
 	return;
 #endif
-	/* To make this work together with the real serial port driver
-	 * we have to make sure that everything is flushed when we leave
-	 * here. The following steps are made to assure this:
-	 * 1. Wait until DMA stops, FIFO is empty and serial port pipeline empty.
-	 * 2. Write at least half the FIFO to trigger flush to serial port.
-	 * 3. Wait until DMA stops, FIFO is empty and serial port pipeline empty.
-         */
-
-	/* Do we have enough characters to make the DMA/FIFO happy? */
-	if (tmp_size + len < MIN_SIZE)
-	{
-		int size = min((int)(MIN_SIZE - tmp_size),(int)len);
-		memcpy(&tmp_buf[tmp_size], buf, size);
-		tmp_size += size;
-		len -= size;
-        
-		/* Pad with space if complete line */
-		if (tmp_buf[tmp_size-1] == '\n')
-		{
-			memset(&tmp_buf[tmp_size-1], ' ', MIN_SIZE - tmp_size);
-			tmp_buf[MIN_SIZE - 1] = '\n';
-			tmp_size = MIN_SIZE;
-			len = 0;
-		}
-		else
-		{
-                  /* Wait for more characters */
-			restore_flags(flags);
-			return;
-		}
-	}
-
-	/* make sure the transmitter is enabled. 
-	 * NOTE: this overrides any setting done in ttySx, to 8N1, no auto-CTS.
-	 * in the future, move the tr/rec_ctrl shadows from etrax100ser.c to
-	 * shadows.c and use it here as well...
-	 */
-
-	*DEBUG_TR_CTRL = 0x40;
-	while(*DEBUG_OCMD & 7); /* Until DMA is not running */
-	while(*DEBUG_STATUS & 0x7f); /* wait until output FIFO is empty as well */
-	udelay(200); /* Wait for last two characters to leave the serial transmitter */
-
-	if (tmp_size)
-	{
-		descr.ctrl = len ?  0 : d_eop | d_wait | d_eol;
-		descr.sw_len = tmp_size;
-		descr.buf = virt_to_phys(tmp_buf);
-		descr.next = virt_to_phys(&descr2);
-		descr2.ctrl = d_eop | d_wait | d_eol;
-		descr2.sw_len = len;
-		descr2.buf = virt_to_phys((char*)buf);
-	}
-	else
-	{
-		descr.ctrl = d_eop | d_wait | d_eol;
-		descr.sw_len = len;
-		descr.buf = virt_to_phys((char*)buf);
-	}
-
-	*DEBUG_FIRST = virt_to_phys(&descr); /* write to R_DMAx_FIRST */
-	*DEBUG_OCMD = 1;       /* dma command start -> R_DMAx_CMD */
 
-	/* wait until the output dma channel is ready again */
-	while(*DEBUG_OCMD & 7);
-	while(*DEBUG_STATUS & 0x7f);
-	udelay(200);
-
-	tmp_size = 0;
-	restore_flags(flags);
+	local_irq_save(flags);
+	if (debug_write_function)
+		if (debug_write_function(co->index, buf, len))
+			return;
+	console_write_direct(co, buf, len);
+	local_irq_restore(flags);
 }
 
 /* legacy function */
@@ -294,4 +245,22 @@
 init_etrax_debug(void)
 {
 	register_console(&sercons);
+#if CONFIG_ETRAX_DEBUG_PORT_NULL
+	return;
+#endif
+
+#if DEBUG_PORT_IDX == 0
+	genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma6);
+	genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma6, unused);
+#elif DEBUG_PORT_IDX == 1
+	genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma8);
+	genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma8, usb);
+#elif DEBUG_PORT_IDX == 2
+	genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma2);
+	genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma2, par0);
+#elif DEBUG_PORT_IDX == 3
+	genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma4);
+	genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma4, par1);     
+#endif
+	*R_GEN_CONFIG = genconfig_shadow;
 }
diff -Nur /home/anderstj/linux-2.4.26/arch/cris/kernel/head.S ./arch/cris/kernel/head.S
--- /home/anderstj/linux-2.4.26/arch/cris/kernel/head.S	2003-08-25 13:44:39.000000000 +0200
+++ ./arch/cris/kernel/head.S	2004-03-29 09:31:02.000000000 +0200
@@ -1,4 +1,4 @@
-/* $Id: head.S,v 1.47 2003/07/08 09:53:35 starvik Exp $
+/* $Id: head.S,v 1.49 2004/03/29 07:31:02 starvik Exp $
  * 
  * Head of the kernel - alter with care
  *
@@ -7,6 +7,13 @@
  * Authors:	Bjorn Wesen (bjornw@axis.com)
  * 
  * $Log: head.S,v $
+ * Revision 1.49  2004/03/29 07:31:02  starvik
+ * Always setup waitstates and busconfig
+ *
+ * Revision 1.48  2004/02/24 09:18:14  anderstj
+ * Removed Bluetooth G-port settings, replaced with more general configuration
+ * of G port direction.
+ *
  * Revision 1.47  2003/07/08 09:53:35  starvik
  * Corrected last CVS log entry
  *
@@ -327,6 +334,15 @@
 	move.d START_ETHERNET_CLOCK, $r0
 	move.d $r0, [R_NETWORK_GEN_CONFIG]
 #endif
+
+	;; Set up waitstates etc according to kernel configuration.
+#ifndef CONFIG_SVINTO_SIM	
+	move.d   CONFIG_ETRAX_DEF_R_WAITSTATES, $r0
+	move.d   $r0, [R_WAITSTATES]
+
+	move.d   CONFIG_ETRAX_DEF_R_BUS_CONFIG, $r0
+	move.d   $r0, [R_BUS_CONFIG]
+#endif
 		
 	;; We need to initialze DRAM registers before we start using the DRAM
 
@@ -640,10 +656,23 @@
 		| IO_STATE (R_GEN_CONFIG, dma4, extdma0),$r0
 #endif
 
-#if defined(CONFIG_BLUETOOTH) && (defined(CONFIG_BLUETOOTH_RESET_G10) || defined(CONFIG_BLUETOOTH_RESET_G11))
-	or.d	  IO_STATE (R_GEN_CONFIG, g8_15dir, out),$r0
+#if defined(CONFIG_ETRAX_DEF_R_PORT_G0_DIR_OUT)
+        or.d      IO_STATE (R_GEN_CONFIG, g0dir, out),$r0
+#endif
+
+#if defined(CONFIG_ETRAX_DEF_R_PORT_G8_15_DIR_OUT)
+        or.d      IO_STATE (R_GEN_CONFIG, g8_15dir, out),$r0
+#endif
+
+#if defined(CONFIG_ETRAX_DEF_R_PORT_G16_23_DIR_OUT)
+	or.d      IO_STATE (R_GEN_CONFIG, g16_23dir, out),$r0
 #endif
 
+#if defined(CONFIG_ETRAX_DEF_R_PORT_G24_DIR_OUT)
+	or.d      IO_STATE (R_GEN_CONFIG, g24dir, out),$r0
+#endif
+
+
 	move.d	$r0,[genconfig_shadow] ; init a shadow register of R_GEN_CONFIG
 
 #ifndef CONFIG_SVINTO_SIM
diff -Nur /home/anderstj/linux-2.4.26/dboot ./dboot
--- /home/anderstj/linux-2.4.26/dboot	1970-01-01 01:00:00.000000000 +0100
+++ ./dboot	2000-11-13 14:18:34.000000000 +0100
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+svinto_boot --setreg b000002c 0 --setreg b0000000 000095f8 --setreg b0000004 104 --setreg b0000008 5611 --setreg b000000c 1a200040 --bootfile DBG0 --file timage c0004000 --jump 40004000
+
+
diff -Nur /home/anderstj/linux-2.4.26/drivers/acpi/acpi_ksyms.c ./drivers/acpi/acpi_ksyms.c
--- /home/anderstj/linux-2.4.26/drivers/acpi/acpi_ksyms.c	2003-08-25 13:44:40.000000000 +0200
+++ ./drivers/acpi/acpi_ksyms.c	2003-08-27 07:38:41.000000000 +0200
@@ -1,5 +1,5 @@
 /*
- *  acpi_ksyms.c - ACPI Kernel Symbols ($Revision: 15 $)
+ *  acpi_ksyms.c - ACPI Kernel Symbols ($Revision: 1.1.1.7 $)
  *
  *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
  *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
diff -Nur /home/anderstj/linux-2.4.26/drivers/ide/ide-geometry.c ./drivers/ide/ide-geometry.c
--- /home/anderstj/linux-2.4.26/drivers/ide/ide-geometry.c	2003-11-28 19:26:20.000000000 +0100
+++ ./drivers/ide/ide-geometry.c	2003-12-04 08:31:56.000000000 +0100
@@ -3,7 +3,9 @@
  */
 #include <linux/config.h>
 #include <linux/ide.h>
+#ifdef __i386__
 #include <linux/mc146818rtc.h>
+#endif
 #include <asm/io.h>
 
 /*
diff -Nur /home/anderstj/linux-2.4.26/drivers/net/bnep.c ./drivers/net/bnep.c
--- /home/anderstj/linux-2.4.26/drivers/net/bnep.c	1970-01-01 01:00:00.000000000 +0100
+++ ./drivers/net/bnep.c	2001-05-03 12:05:13.000000000 +0200
@@ -0,0 +1,1654 @@
+/*
+ * bnep.c -- Implementation of Bluetooth BNEP 
+ *           
+ *
+ * Copyright (C) 2001  Axis Communications AB
+ *
+ * Author: Willy Sagefalk <willy.sagefalk@axis.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; 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.
+ *
+ * Exceptionally, Axis Communications AB grants discretionary and
+ * conditional permissions for additional use of the text contained
+ * in the company's release of the AXIS OpenBT Stack under the
+ * provisions set forth hereunder.
+ *
+ * Provided that, if you use the AXIS OpenBT Stack with other files,
+ * that do not implement functionality as specified in the Bluetooth
+ * System specification, to produce an executable, this does not by
+ * itself cause the resulting executable to be covered by the GNU
+ * General Public License. Your use of that executable is in no way
+ * restricted on account of using the AXIS OpenBT Stack code with it.
+ *
+ * This exception does not however invalidate any other reasons why
+ * the executable file might be covered by the provisions of the GNU
+ * General Public License.
+ *
+ *
+ */
+
+/****************** INCLUDE FILES SECTION ***********************************/
+
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/fs.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/socket.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/in.h>
+#include <linux/init.h>
+
+#include <linux/inet.h>
+#include <linux/ioctl.h>
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/malloc.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_ether.h>
+
+#include <linux/ip.h>
+#include <linux/udp.h>
+
+#include <linux/bluetooth/btdebug.h>
+#include <linux/bluetooth/btcommon.h>
+#include <linux/bluetooth/btmem.h>
+#include <linux/bluetooth/l2cap.h>
+#include <linux/bluetooth/hci.h>
+#include "bnep.h"
+
+/****************** DEBUG CONSTANT AND MACRO SECTION ************************/
+
+
+/* Defines for the debug macros */
+
+#if BNEP_DEBUG_XMIT
+#define D_XMIT(fmt, args...) printk(BNEP_DBG_STR fmt, ## args)
+#else
+#define D_XMIT(fmt, args...)
+#endif
+
+#if BNEP_DEBUG_REC
+#define D_REC(fmt, args...) printk(BNEP_DBG_STR fmt, ## args)
+#else
+#define D_REC(fmt, args...)
+#endif
+
+#if BNEP_DEBUG_MISC
+#define D_MISC(fmt, args...) printk(BNEP_DBG_STR fmt, ## args)
+#else
+#define D_MISC(fmt, args...)
+#endif
+
+#if BNEP_DEBUG_DATA
+#define PRINTPKT(str, data, len) print_data(str, data, len)
+#else
+#define PRINTPKT(str, data, len)
+#endif
+
+/****************** CONSTANT AND MACRO SECTION ******************************/
+
+
+#define BNEP_MAX_CON        7
+#define BNEP_FLUSH_TIMEOUT  0
+#define BNEP_MAX_MULTI     16
+
+/****************** TYPE DEFINITION SECTION *********************************/
+
+/****************** LOCAL FUNCTION DECLARATION SECTION **********************/
+
+/****************** GLOBAL VARIABLE DECLARATION SECTION *********************/
+
+/****************** LOCAL VARIABLE DECLARATION SECTION **********************/
+
+static const char* cardname = "BNEP ethernet encapsulation";
+
+/* A default ethernet address. Highlevel SW will set the real one later */
+
+static struct sockaddr default_mac = {
+	0,
+        { 0x00, 0x40, 0x8C, 0xCD, 0x00, 0x00 }
+};
+
+struct macaddr
+{
+	unsigned char addr[ETH_ALEN];
+};
+
+/* Information that need to be kept for each board. */
+struct net_local {
+	struct net_device_stats stats;
+        l2cap_con *l2cap;
+        int promiscuous;
+        int allmulti;
+        int mc_count;
+        u16 filter_list[BNEP_MAX_FILTER * 2];
+        struct macaddr mc_list[BNEP_MAX_MULTI];
+};
+
+static struct net_device dev_bnep[7];  /* Got 7 Interfaces */
+static char bnep_dev_name[16];
+static protocol_layer this_layer;
+
+struct arprequest
+{
+	unsigned short ar_hrd;  /* format of hardware address	*/
+	unsigned short ar_pro;  /* format of protocol address	*/
+	unsigned char	ar_hln;	  /* length of hardware address	*/
+	unsigned char	ar_pln;	  /* length of protocol address	*/
+	unsigned short ar_op;   /* ARP opcode (command)*/	
+	unsigned char	ar_sha[ETH_ALEN]; /* sender hardware address	*/
+	unsigned char	ar_sip[4];	  /* sender IP address		*/
+	unsigned char	ar_tha[ETH_ALEN]; /* target hardware address	*/
+	unsigned char	ar_tip[4];	  /* target IP address		*/
+};
+
+
+/****************** LOCAL FUNCTION DECLARATION SECTION **********************/
+
+void bnep_init(void);
+void bnep_receive_packet(l2cap_con *l2cap, u8 *data, u32 len);
+static int  bnep_open(struct net_device *dev);
+static int  bnep_send_packet(struct sk_buff *skb, struct net_device *dev);
+static int  bnep_close(struct net_device *dev);
+static int  bnep_set_mac_address(struct net_device *dev, void *p);
+static void bnep_set_multicast_list(struct net_device *dev);
+static struct net_device_stats *bnep_get_stats(struct net_device *dev);
+
+
+
+/****************** FUNCTION DEFINITION SECTION *****************************/
+
+static print_ip_packet(char *data, u16 proto)
+{
+	struct iphdr      *ip;
+	struct arphdr     *arp;
+	if (proto ==  ETH_P_IP)
+	{
+		ip = (struct iphdr *)data;
+		
+		printk("IP: tos %02x tot_len %04x ID %04x \n frag_off %04x ttl %02x protocol %02x check %04x \n saddr %08x daddr %08x\n", ip->tos, be16_to_cpu(ip->tot_len) , be16_to_cpu(ip->id) , be16_to_cpu(ip->frag_off), ip->ttl, ip->protocol, be16_to_cpu(ip->check), ip->saddr , ip->daddr);
+	}
+	else if (proto ==  ETH_P_ARP)
+	{
+		struct arprequest *arpr;
+		arpr = (struct arprequest *)data; 
+		printk("ARP:");
+		switch (arpr->ar_op)
+		{
+		case 1:
+			printk("Request\n");
+			break;
+		case 2:
+			printk("Reply\n");
+			break;
+		default:
+			printk(" %i ",arpr->ar_op);
+		}
+		printk("Sender %02x:%02x:%02x:%02x:%02x:%02x\n ",arpr->ar_sha[0],arpr->ar_sha[1],arpr->ar_sha[2],arpr->ar_sha[3],arpr->ar_sha[4],arpr->ar_sha[5]);
+		printk("Sender IP %i.%i.%i.%i\n",arpr->ar_sip[0],arpr->ar_sip[1],arpr->ar_sip[2],arpr->ar_sip[3]);
+		printk("Target %02x:%02x:%02x:%02x:%02x:%02x\n ",arpr->ar_tha[0],arpr->ar_tha[1],arpr->ar_tha[2],arpr->ar_tha[3],arpr->ar_tha[4],arpr->ar_tha[5]);
+		printk("Target IP %i.%i.%i.%i\n",arpr->ar_tip[0],arpr->ar_tip[1],arpr->ar_tip[2],arpr->ar_tip[3]);
+	} 
+	else 
+	{ 
+		D_REC("Proto %04x \n",proto);
+	}
+}  
+
+static int __init
+bnep_init_driver(struct net_device *dev)
+{
+        struct net_local  *local;
+        
+        printk("BNEP driver v0.1 (c) 2001 Axis Communications AB\n");
+	printk("%s initialized\n", dev->name);
+	
+        if (!dev) {  
+                D_ERR("dev == NULL. Should this happen?\n"); 
+  		dev = init_etherdev(dev, sizeof(struct net_local)); 
+  	} 
+	
+  	/* setup generic handlers and stuff in the dev struct */ 
+  	ether_setup(dev); 
+	
+	/* make room for the local structure containing stats etc */
+        dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+	
+	if (dev->priv == NULL)
+		return -ENOMEM;
+	
+	
+	
+        /* Reset private structure and accept all packet types */
+        memset(dev->priv, 0, sizeof(struct net_local));
+        local = dev->priv;
+        local->filter_list[0] = 0x0000;
+        local->filter_list[1] = 0xffff;
+	
+        dev->irq = 0;
+        dev->dma = 1;
+	
+        dev->open               = bnep_open;
+        dev->hard_start_xmit    = bnep_send_packet;
+        dev->stop               = bnep_close;
+        dev->get_stats          = bnep_get_stats;
+        dev->set_multicast_list = bnep_set_multicast_list;
+        dev->set_mac_address    = bnep_set_mac_address;
+	
+        return 0;
+}
+
+static int
+bnep_set_mac_address(struct net_device *dev, void *p)
+{
+	struct sockaddr *addr = p;
+	
+	int i;  
+	memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
+	
+	D_MISC("%s: changed MAC to ",dev->name);
+	D_MISC("%02X:%02x:%02x:%02x:%02x:%02x\n", dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2], dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
+	return 0;
+}
+
+/*
+ * Open/initialize the board. This is called (in the current kernel)
+ * sometime after booting when the 'ifconfig' program is run.
+ *
+ * This routine should set everything up anew at each open, even
+ * registers that "should" only need to be set once at boot, so that
+ * there is non-reboot way to recover if something goes wrong.
+ */
+
+static int
+bnep_open(struct net_device *dev)
+{
+	D_MISC("open device\n");
+	netif_start_queue(dev);
+	return 0;
+}
+
+int
+bnep_close(struct net_device *dev)
+{
+	D_MISC("close device\n"); 
+	netif_stop_queue(dev);
+	return 0;
+}
+
+/* Send a packet without building a BNEP header  */
+
+static int
+bnep_send_raw_packet(struct sk_buff *skb, struct net_device *dev)
+{
+	bt_tx_buf         *tx_buf;
+	struct net_local  *local;
+	struct ethhdr     *eth;
+	int i,bhdrlen,ethhdrlen;
+	struct bnep_general_ethernet bnep;
+	
+	
+	D_XMIT("bnep_send_packet: transmitting %d bytes\n", skb->len);
+	PRINTPKT("bnep_send_packet:",skb->data,skb->len);
+	
+	local = dev->priv;
+	
+	if (local->l2cap == NULL) {
+		/* no connection; drop the packet */
+		D_XMIT("bnep send packet: no connection exists\n");
+		local->stats.tx_dropped++;
+	}
+	else
+	{
+		/* check whether the we have room for the bt_tx_buf */
+		if (buf_write_room() < (skb->len)) {
+			D_XMIT("bnep send packet: not enough room for a %d byte bt_tx_buf\n", skb->len);
+			/* don't necessarily need to discard the buffer; could set dev->tbusy = 1
+			   and then start a timer to check for free buffer space */
+			local->stats.tx_dropped++;
+		}
+		else
+		{
+			/* subscribe to a bt_tx_buf */
+			D_XMIT("New bt buf length %i \n",skb->len + sizeof(bnep_tx_buf));
+			if ((tx_buf = subscribe_bt_buf(skb->len + sizeof(bnep_tx_buf)  )) == NULL) {
+				D_ERR("bnep send packet: failed to subscribe to a %d byte bt_tx_buf\n", skb->len);
+				local->stats.tx_dropped++;
+			}
+			else
+			{
+				
+				/* copy the frame into the bt_tx_buf */
+				/*  printk("Copy frame into bt_tx_buf\n");*/
+				memcpy(tx_buf->data + sizeof(bnep_tx_buf), skb->data, skb->len);
+				tx_buf->cur_len = skb->len;
+				
+				/* transmit the packet */
+				printk("bnep: tx packet\n");
+				l2cap_send_data(tx_buf, local->l2cap);
+				
+				
+				/* update the device statistics */
+				local->stats.tx_packets++;
+				local->stats.tx_bytes += skb->len;
+			}
+		}
+	}
+	/* free the sk_buff */
+	dev_kfree_skb(skb); 
+	return 0;
+}
+/*********************************************************/
+/* PAN Test cases and some home made tests               */
+/*********************************************************/
+
+u32
+bnep_test(u8 *p)
+{
+	int test,i;
+	struct net_device *dev;
+	struct net_local  *local;
+	struct l2cap_con *l2cap;
+	char temp_addr[6];
+	u8 destip[4];
+	test = p[0];
+	destip[0] = p[1];
+	destip[1] = p[2];
+	destip[2] = p[3];
+	destip[3] = p[4];
+	
+	
+	printk("BNEP:test %i \n",test);
+	
+	dev = &dev_bnep[0];
+	local = dev->priv;
+	l2cap = local->l2cap;
+	
+	switch (test)
+	{
+	case 1:
+	case 7:
+	case 9:
+		printk("Test Case %i -- TX for BNEP Type 0x00 general\n",test);
+		{
+			struct sk_buff *skb;
+			struct bnep_general_ethernet bnep;
+			struct iphdr ip;
+			struct udphdr udp;
+			
+			int len,u,i,pos;
+			
+			bnep.bnep_type = BNEP_GENERAL_ETHERNET;
+			for ( i=0; i<6; i++) {
+				if( test == 7) {
+					bnep.daddr[i] = 0xff;
+				} 
+				else if( test == 1)  {
+					bnep.daddr[i] = l2cap->remote_bd[5-i];
+				}
+				else if( test == 9)  {
+					bnep.daddr[0] = 0x01;
+					bnep.daddr[1] = 0x00;
+					bnep.daddr[2] = 0x5e;
+					bnep.daddr[3] = 0x00;
+					bnep.daddr[4] = 0x00;
+					bnep.daddr[5] = 0x00;
+				}
+			} 
+			for ( i=0; i<6; i++) {
+				bnep.saddr[i] = dev->dev_addr[i];
+			}    
+			bnep.type = cpu_to_be16( 0x0800 );
+			ip.version   = 4;
+			ip.ihl       = 5;
+			ip.frag_off  = 0;
+			ip.ttl       = 15;
+			ip.tot_len   = 0x5dc;
+			ip.protocol  = 0x11;
+			if (test == 7) {
+				ip.saddr     = 0xffffffff;
+			} 
+			else if (test == 1) {
+				ip.saddr     = 0;
+			}
+			else if (test == 9) {
+				ip.saddr     = 0;
+			}
+			ip.daddr     = 0;
+			
+			udp.source = 0x00;
+			udp.dest   = 0x07;
+			udp.len    = 0x5c8;
+			
+			len = sizeof(struct bnep_general_ethernet) + sizeof(struct iphdr) + sizeof(struct udphdr) + 0x5c0;
+			printk("Allocate %i bytes\n",len);
+			
+			if ((skb = dev_alloc_skb(len)) == NULL) {
+				printk("Alloc skb failed \n");
+				return 0;
+			}
+			pos = 0;
+			skb_put(skb,len);
+			memcpy(skb->data , (void*)&bnep, sizeof(struct bnep_general_ethernet));
+			pos += sizeof(struct bnep_general_ethernet);
+			memcpy(skb->data + pos, (void*)&ip, sizeof(struct iphdr));
+			pos += sizeof(struct iphdr);
+			memcpy(skb->data + pos, (void*)&udp, sizeof(struct udphdr));
+			pos += sizeof(struct udphdr);
+			i = 0;
+			while (pos < len)
+			{ 
+				skb->data[pos] = i;
+				printk("%02x",i);
+				i++;
+				pos++;
+				if (i>0xff) i = 0;
+			} 
+			printk("\nSend raw packet.....");
+			bnep_send_raw_packet(skb,&dev_bnep[0]);
+		}
+		break;
+	case 2:
+		printk("Test Case 2 -- TX for BNEP Type 0x02 compressed\n");
+		{
+			struct sk_buff *skb;
+			struct bnep_compressed_ethernet bnep;
+			struct iphdr ip;
+			struct udphdr udp;
+			int len,u,i,pos;
+			
+			bnep.bnep_type = BNEP_COMPRESSED_ETHERNET;
+			bnep.type =  cpu_to_be16(0x0800);
+			ip.tot_len = 0x5dc;
+			ip.protocol = 0x11;
+			ip.saddr = 0;
+			ip.daddr = 0;
+			
+			udp.source = 00;
+			udp.dest = 0x07;
+			udp.len = 0x5c8;
+			
+			len = sizeof(struct bnep_compressed_ethernet) + sizeof(struct iphdr) + sizeof(struct udphdr) + 0x5c0;
+			printk("Allocate %i bytes\n",len);
+			
+			if ((skb = dev_alloc_skb(len)) == NULL) {
+				printk("Alloc skb failed \n");
+				return 0;
+			}
+			pos = 0;
+			skb_put(skb,len);
+			memcpy(skb->data , (void*)&bnep, sizeof(struct bnep_compressed_ethernet));
+			pos += sizeof(struct bnep_compressed_ethernet);
+			memcpy(skb->data + pos, (void*)&ip, sizeof(struct iphdr));
+			pos += sizeof(struct iphdr);
+			memcpy(skb->data + pos, (void*)&udp, sizeof(struct udphdr));
+			pos += sizeof(struct udphdr);
+			i = 0;
+			while (pos < len)
+			{ 
+				skb->data[pos] = i;
+				printk("%02x",i);
+				i++;
+				pos++;
+				if (i>0xff) i = 0;
+			} 
+			printk("\nSend packet.....");
+			bnep_send_raw_packet(skb,&dev_bnep[0]);
+		}
+		break;
+	case 3:
+		printk("Test Case 3 -- TX for BNEP Type 0x03 Source only\n");
+		{
+			struct sk_buff *skb;
+			struct bnep_compressed_ethernet_source_only bnep;
+			struct iphdr ip;
+			struct udphdr udp;
+			
+			int len,u,i,pos;
+			
+			bnep.bnep_type = BNEP_COMPRESSED_ETHERNET_SOURCE_ONLY;
+			for ( i=0; i<6; i++) {
+				bnep.saddr[i] = dev->dev_addr[i];
+			}   
+			bnep.type =  cpu_to_be16(0x0800);
+			ip.tot_len = 0x5dc;
+			ip.protocol = 0x11;
+			ip.saddr = 0;
+			ip.daddr = 0;
+			
+			udp.source = 00;
+			udp.dest = 0x07;
+			udp.len = 0x5c8;
+			
+			len = sizeof(struct bnep_compressed_ethernet_source_only) + sizeof(struct iphdr) + sizeof(struct udphdr) + 0x5c0;
+			printk("Allocate %i bytes\n",len);
+			
+			if ((skb = dev_alloc_skb(len)) == NULL) {
+				printk("Alloc skb failed \n");
+				return 0;
+			}
+			pos = 0;
+			skb_put(skb,len);
+			memcpy(skb->data , (void*)&bnep, sizeof(struct bnep_compressed_ethernet_source_only));
+			pos += sizeof(struct bnep_compressed_ethernet_source_only);
+			memcpy(skb->data + pos, (void*)&ip, sizeof(struct iphdr));
+			pos += sizeof(struct iphdr);
+			memcpy(skb->data + pos, (void*)&udp, sizeof(struct udphdr));
+			pos += sizeof(struct udphdr);
+			i = 0;
+			while (pos < len)
+			{ 
+				skb->data[pos] = i;
+				printk("%02x",i);
+				i++;
+				pos++;
+				if (i>0xff) i = 0;
+			} 
+			printk("\nSend raw packet.....");
+			bnep_send_raw_packet(skb,&dev_bnep[0]);
+		}
+		break;
+	case 4:
+	case 8:
+	case 10:
+		printk("Test Case %i -- TX for BNEP Type 0x04 Dest Only\n",test);
+		{
+			struct sk_buff *skb;
+			struct bnep_compressed_ethernet_dest_only bnep;
+			struct iphdr ip;
+			struct udphdr udp;
+			int len,u,i,pos;
+			
+			bnep.bnep_type = BNEP_COMPRESSED_ETHERNET_DEST_ONLY;
+			for ( i=0; i<6; i++) {
+				if( test == 8) {
+					bnep.daddr[i] = 0xff;
+				} else {
+					bnep.daddr[i] = l2cap->remote_bd[5-i];
+				}
+			} 
+			bnep.type =  cpu_to_be16(0x0800);
+			ip.tot_len = 0x5dc;
+			ip.protocol = 0x11;
+			ip.saddr = 0;
+			if( test == 8) {
+				ip.daddr = 0xffffffff;
+			}
+			else if( test == 10)  {
+				bnep.daddr[0] = 0x01;
+				bnep.daddr[1] = 0x00;
+				bnep.daddr[2] = 0x5e;
+				bnep.daddr[3] = 0x00;
+				bnep.daddr[4] = 0x00;
+				bnep.daddr[5] = 0x00;
+			}
+			else if (test == 4) {
+				ip.daddr = 0;
+			}
+			udp.source = 00;
+			udp.dest = 0x07;
+			udp.len = 0x5c8;
+			
+			len = sizeof(struct bnep_compressed_ethernet_dest_only) + sizeof(struct iphdr) + sizeof(struct udphdr) + 0x5c0;
+			printk("Allocate %i bytes\n",len);
+			
+			if ((skb = dev_alloc_skb(len)) == NULL) {
+				printk("Alloc skb failed \n");
+				return 0;
+			}
+			pos = 0;
+			skb_put(skb,len);
+			memcpy(skb->data , (void*)&bnep, sizeof(struct bnep_compressed_ethernet_dest_only));
+			pos += sizeof(struct bnep_compressed_ethernet_dest_only);
+			memcpy(skb->data + pos, (void*)&ip, sizeof(struct iphdr));
+			pos += sizeof(struct iphdr);
+			memcpy(skb->data + pos, (void*)&udp, sizeof(struct udphdr));
+			pos += sizeof(struct udphdr);
+			i = 0;
+			while (pos < len)
+			{ 
+				skb->data[pos] = i;
+				printk("%02x",i);
+				i++;
+				pos++;
+				if (i>0xff) i = 0;
+			} 
+			printk("\nSend raw packet.....");
+			bnep_send_raw_packet(skb,&dev_bnep[0]);
+		}
+		break;
+	case 5:
+		printk("Test Case 5 -- Broadcast from Bluetooth network to wired\n");
+		{
+			
+			struct bnep_compressed_ethernet_dest_only bnep;
+			struct iphdr ip;
+			struct udphdr udp;
+			struct sk_buff *skb;
+			int len,pos;
+			
+			bnep.bnep_type=BNEP_COMPRESSED_ETHERNET_DEST_ONLY;
+			
+			for ( i=0; i<6; i++) 
+				bnep.daddr[i] = 0xff;
+			
+			bnep.type =  cpu_to_be16(0x0800);
+			ip.tot_len = 0x5dc;
+			ip.protocol = 0x11;
+			ip.saddr = 0; 
+			ip.daddr = 0xffffffff;
+			
+			udp.source = 00;
+			udp.dest = 0x07;
+			udp.len = 0x5c8;
+			
+			len = sizeof(struct bnep_compressed_ethernet_dest_only) + sizeof(struct iphdr) + sizeof(struct udphdr) + 0x5c0;
+			printk("Allocate %i bytes\n",len);
+			
+			if ((skb = dev_alloc_skb(len)) == NULL) {
+				printk("Alloc skb failed \n");
+				return 0;
+			}
+			pos = 0;
+			skb_put(skb,len);
+			memcpy(skb->data , (void*)&bnep, sizeof(struct bnep_compressed_ethernet_dest_only));
+			pos += sizeof(struct bnep_compressed_ethernet_dest_only);
+			memcpy(skb->data + pos, (void*)&ip, sizeof(struct iphdr));
+			pos += sizeof(struct iphdr);
+			memcpy(skb->data + pos, (void*)&udp, sizeof(struct udphdr));
+			pos += sizeof(struct udphdr);
+			i = 0;
+			while (pos < len)
+			{ 
+				skb->data[pos] = i;
+				printk("%02x",i);
+				i++;
+				pos++;
+				if (i>0xff) i = 0;
+			} 
+			printk("\nSend raw packet.....");
+			bnep_send_raw_packet(skb,&dev_bnep[0]);
+		}
+		
+		break;
+		
+	case 6:
+	case 61:
+	case 62:
+	case 63:
+		printk("Set filter \n");
+		{
+			struct sk_buff *skb;
+			struct bnep_filter_set bnep;
+			u16 filter[20];
+			int len,u,i,pos,n;
+			
+			bnep.bnep_type        = BNEP_FILTER_CONTROL;
+			bnep.bnep_filter_type = BNEP_FILTER_SET;
+			if (test == 61) {
+				printk("Send 2 filters \n");
+				n = 2;
+				filter[0] = cpu_to_be16(0x0800);
+				filter[1] = cpu_to_be16(0x1000);
+				filter[2] = cpu_to_be16(0x1100);
+				filter[3] = cpu_to_be16(0x1200);
+			}
+			else if (test == 62) {
+				printk("Send 2 overlapping filters \n");
+				n = 2;
+				filter[0] = cpu_to_be16(0x0800);
+				filter[1] = cpu_to_be16(0x1100);
+				filter[2] = cpu_to_be16(0x1000);
+				filter[3] = cpu_to_be16(0x1200);
+			}
+			else if (test == 63) {
+				printk("Send 2 invalid filters \n");
+				n = 2;
+				filter[0] = cpu_to_be16(0x1000);
+				filter[1] = cpu_to_be16(0x0800);
+				filter[2] = cpu_to_be16(0x1201);
+				filter[3] = cpu_to_be16(0x1200);
+			}
+			else if (test == 64) {
+				printk("Send 10 filters of which 9 is identical \n");
+				n = 10;
+				filter[0] = cpu_to_be16(0x0800);
+				filter[1] = cpu_to_be16(0x1000);
+				filter[2] = cpu_to_be16(0x0002);
+				filter[3] = cpu_to_be16(0x0002);
+				filter[4] = cpu_to_be16(0x0002);
+				filter[5] = cpu_to_be16(0x0002);
+				filter[6] = cpu_to_be16(0x0002);
+				filter[7] = cpu_to_be16(0x0002);
+				filter[8] = cpu_to_be16(0x0002);
+				filter[9] = cpu_to_be16(0x0002);  
+				filter[10] = cpu_to_be16(0x0002);
+				filter[11] = cpu_to_be16(0x0002);
+				filter[12] = cpu_to_be16(0x0002);
+				filter[13] = cpu_to_be16(0x0002);
+				filter[14] = cpu_to_be16(0x0002);
+				filter[15] = cpu_to_be16(0x0002);
+				filter[16] = cpu_to_be16(0x0002);
+				filter[17] = cpu_to_be16(0x0002);
+				filter[18] = cpu_to_be16(0x0002);
+				filter[19] = cpu_to_be16(0x0002);       
+			}
+			else if (test == 6) {
+				printk("Send 5 filters of which 4 is identical \n");
+				n = 5;
+				filter[0] = cpu_to_be16(0x0800);
+				filter[1] = cpu_to_be16(0x1000);
+				filter[2] = cpu_to_be16(0x0000);
+				filter[3] = cpu_to_be16(0x0000);
+				filter[4] = cpu_to_be16(0x0000);
+				filter[5] = cpu_to_be16(0x0000);
+				filter[6] = cpu_to_be16(0x0000);
+				filter[7] = cpu_to_be16(0x0000);
+				filter[8] = cpu_to_be16(0x0000);
+				filter[9] = cpu_to_be16(0x0000);       
+			}
+			
+			
+			len = sizeof(struct bnep_filter_set) + n*4;
+			pos = 0;
+			printk("Allocate %i bytes\n",len);
+			
+			if ((skb = dev_alloc_skb(len)) == NULL) {
+				printk("Alloc skb failed \n");
+				return 0;
+			}
+			
+			skb_put(skb,len);
+			memcpy(skb->data , (void*)&bnep, sizeof(struct bnep_filter_set));
+			pos += sizeof(struct bnep_filter_set);
+			memcpy(skb->data + pos, (void*)&filter[0], n*4);
+			pos += n*4;
+			
+			bnep_send_raw_packet(skb,&dev_bnep[0]);
+		}
+		break;
+	default:
+		break;
+	}  
+}
+
+/* Test function to set a remote filter   */
+
+bnep_set_remote_filter(u16 *filter)
+{
+	D_MISC("Set remote filter \n");
+	{
+		struct sk_buff *skb;
+		struct bnep_filter_set bnep;
+		
+		int len,pos;
+		
+		bnep.bnep_type        = BNEP_FILTER_CONTROL;
+		bnep.bnep_filter_type = BNEP_FILTER_SET;
+		
+		len = sizeof(struct bnep_filter_set) + 20;
+		D_MISC("Allocate %i bytes\n",len);
+		
+		if ((skb = dev_alloc_skb(len)) == NULL) {
+			D_ERR("Alloc skb failed \n");
+			return 0;
+		}
+		pos = 0;
+		skb_put(skb,len);
+		memcpy(skb->data , (void*)&bnep, sizeof(struct bnep_filter_set));
+		pos += sizeof(struct bnep_filter_set);
+		*(u16 *)(skb->data + pos) = filter[0];
+		pos += 2;
+		*(u16 *)(skb->data + pos) =
+			
+			filter[1];
+		pos += 2;
+		bnep_send_raw_packet(skb,&dev_bnep[0]);
+	}
+}
+
+static int
+bnep_send_packet(struct sk_buff *skb, struct net_device *dev)
+{
+	bt_tx_buf         *tx_buf;
+	struct net_local  *local;
+	struct ethhdr     *eth;
+	struct bnep_compressed_ethernet_dest_only   bnep_c_d;
+	struct bnep_general_ethernet                bnep_g; 
+	struct bnep_compressed_ethernet_source_only bnep_c_s;
+	struct bnep_compressed_ethernet             bnep_c;
+	void              *bhdr;
+	int i,bhdrlen,ethhdrlen,buflen;
+	
+	u16 proto; 
+	int send_packet;
+	int dest = 1;
+	int source = 1;
+	
+	eth = (struct ethhdr *)skb->data;
+	proto = be16_to_cpu(eth->h_proto);
+	local = dev->priv;
+	
+	/* Check for multicast packets */
+#if 0
+	if (eth->h_dest[0] == 1) {
+		/* Fixme, filter multicast traffic ?? How */
+		/* Discard for now......    */
+		D_XMIT("Discard multicast packet \n");
+		/* free the sk_buff */
+		dev_kfree_skb(skb); 
+		return 0;
+	}
+#endif  
+	if (local->l2cap == NULL)
+	{
+		D_XMIT("No l2cap. Discard packet.....\n");
+		/* free the sk_buff */
+		dev_kfree_skb(skb); 
+		return 0;
+	}
+	
+	/* Build BNEP header */
+	D_XMIT("Build BNEP header\n");
+	
+	for ( i=0; i<6; i++) {
+		if( eth->h_dest[i] != local->l2cap->remote_bd[5-i]) {
+			dest = 0;
+		}
+	} 
+	for ( i=0; i<6; i++) {
+		if( eth->h_source[i] != dev->dev_addr[i]) {
+			source = 0; 
+		}
+	}    
+	if (( dest == 0 ) && ( source == 0)) {
+		
+		bnep_g.bnep_type = BNEP_GENERAL_ETHERNET;
+		bnep_g.type = cpu_to_be16(proto);
+		for ( i=0; i<6; i++) {
+			bnep_g.daddr[i] = eth->h_dest[i];
+		} 
+		for ( i=0; i<6; i++) {
+			bnep_g.saddr[i] = eth->h_source[i];
+		} 
+		bhdrlen = sizeof(struct bnep_general_ethernet);
+		bhdr = (void*)&bnep_g;
+		D_XMIT("General ethernet saddr %02x.%02x.%02x.%02x.%02x.%02x ",bnep_g.saddr[0],bnep_g.saddr[1],bnep_g.saddr[2],bnep_g.saddr[3],bnep_g.saddr[4],bnep_g.saddr[5]);
+		D_XMIT("daddr %02x.%02x.%02x.%02x.%02x.%02x",bnep_g.daddr[0],bnep_g.daddr[1],bnep_g.daddr[2],bnep_g.daddr[3],bnep_g.daddr[4],bnep_g.daddr[5]);
+	}
+	else if (( dest == 1 ) && ( source == 0)) {
+		
+		bnep_c_s.bnep_type = BNEP_COMPRESSED_ETHERNET_SOURCE_ONLY;
+		bnep_c_s.type = cpu_to_be16(proto);
+		for ( i=0; i<6; i++) {
+			bnep_c_s.saddr[i] = eth->h_source[i];
+		}
+		bhdrlen = sizeof(struct bnep_compressed_ethernet_source_only);   
+		bhdr = (void*)&bnep_c_s;
+		D_XMIT("Compressed ethernet source only saddr %02x.%02x.%02x.%02x.%02x.%02x\n",bnep_c_s.saddr[0],bnep_c_s.saddr[1],bnep_c_s.saddr[2],bnep_c_s.saddr[3],bnep_c_s.saddr[4],bnep_c_s.saddr[5]);
+	}
+	else if (( dest == 0 ) && ( source == 1)) {
+		bnep_c_d.bnep_type = BNEP_COMPRESSED_ETHERNET_DEST_ONLY;
+		for ( i=0; i<6; i++) {
+			bnep_c_d.daddr[i] = eth->h_dest[i];
+		} 
+		bnep_c_d.type = cpu_to_be16(proto);
+		bhdrlen = sizeof(struct bnep_compressed_ethernet_dest_only);
+		bhdr = (void*)&bnep_c_d;
+		D_XMIT("Compressed ethernet dest only daddr %02x.%02x.%02x.%02x.%02x.%02x\n",bnep_c_d.daddr[0],bnep_c_d.daddr[1],bnep_c_d.daddr[2],bnep_c_d.daddr[3],bnep_c_d.daddr[4],bnep_c_d.daddr[5]);
+	}
+	else if (( dest == 1 ) && ( source == 1)) {
+		
+		bnep_c.bnep_type = BNEP_COMPRESSED_ETHERNET;
+		bnep_c.type = cpu_to_be16(proto);
+		bhdrlen = sizeof(struct bnep_compressed_ethernet);
+		bhdr = (void*)&bnep_c;
+		D_XMIT("Compressed ethernet\n");
+	}
+	
+	ethhdrlen = sizeof(struct ethhdr);
+	
+	D_XMIT("send proto %04x bnep header len %i eth header len %i eth packet len %i\n",proto,bhdrlen,ethhdrlen,skb->len);
+	
+	PRINTPKT("bnep_send_packet:",skb->data,skb->len);
+	
+	print_ip_packet(skb->data + ethhdrlen,proto);  
+	
+	send_packet = 0;
+	/* Check filters */
+	for (i=0;i<BNEP_MAX_FILTER;i++)  {
+		D_XMIT("Filter %04x - %04x\n",local->filter_list[i*2],local->filter_list[i*2+1]);
+		if ((local->filter_list[i*2] <= proto) && ((local->filter_list[i*2+1] >= proto))) {
+			send_packet = 1;
+			D_XMIT("Send packet ok\n");
+		}
+	}
+	
+	if (send_packet == 0) {
+		local->stats.tx_dropped++;
+	}
+	else {
+		if (local->l2cap == NULL) {
+			/* no connection; drop the packet */
+			D_ERR("bnep send packet: no connection exists\n");
+			local->stats.tx_dropped++;
+		}
+		else {
+			/* check whether the we have room for the bt_tx_buf */
+			buflen = skb->len + sizeof(bnep_tx_buf) - ethhdrlen + bhdrlen - 4;
+			D_XMIT("buflen %i\n",buflen);
+			if (buf_write_room() < buflen) {
+				D_ERR("bnep send packet: not enough room for a %d byte bt_tx_buf\n", skb->len);
+				/* don't necessarily need to discard the buffer; could set dev->tbusy = 1
+				   and then start a timer to check for free buffer space */
+				local->stats.tx_dropped++;
+			}
+			else
+			{
+				/* subscribe to a bt_tx_buf */
+				D_XMIT("New bt buf length %i \n", buflen );
+				if ((tx_buf = subscribe_bt_buf( buflen  )) == NULL) {
+					D_ERR("bnep send packet: failed to subscribe to a %d byte bt_tx_buf buflen %d\n", skb->len, buflen);
+					local->stats.tx_dropped++;
+				}
+				else
+				{
+					
+					/* copy the frame into the bt_tx_buf */
+					/*  printk("Copy frame into bt_tx_buf\n");*/
+					memcpy(tx_buf->data + sizeof(bnep_tx_buf),bhdr,bhdrlen);
+					memcpy(tx_buf->data + sizeof(bnep_tx_buf) + bhdrlen, skb->data + ethhdrlen, buflen - bhdrlen - sizeof(bnep_tx_buf));
+					tx_buf->cur_len = skb->len - ethhdrlen + bhdrlen - 4;
+					
+					/* transmit the packet */
+					D_XMIT("bnep: tx packet\n");
+					l2cap_send_data(tx_buf, local->l2cap);
+					
+					
+					/* update the device statistics */
+					local->stats.tx_packets++;
+					local->stats.tx_bytes += skb->len - 4;
+				}
+			}
+		}
+	}
+	/* free the sk_buff */
+	dev_kfree_skb(skb); 
+	return 0;
+}
+
+void 
+bnep_filter_control(l2cap_con *l2cap, u8 *data, u32 len,u8 bnep_filter_type)
+{
+	struct net_device *dev;
+	struct net_local  *local;
+	struct ethhdr     *eth;
+	struct bnep_filter_set *bnep;
+	struct bnep_filter_response_msg rsp;  
+	
+	u8  type;
+	u8  eheaders;
+	int blen,skblen;
+	int n,i;
+	bt_tx_buf *tx_buf;
+	u16 *filter;
+	
+	dev = (struct net_device *)l2cap->upper_con;
+	local = (struct net_local *)(dev->priv);  
+	
+	switch (bnep_filter_type) {
+	case BNEP_FILTER_SET: {
+		
+		n = len / 4;
+		rsp.bnep_response_msg = cpu_to_be16(SUCCESS);
+		D_MISC("Len = %i n = %i \n",len,n);
+		if (n == 0) {
+			/* Reset filter */
+			D_MISC("Reset filter\n");
+			for (i=0;i<BNEP_MAX_FILTER;i++)  {
+				local->filter_list[i*2]   = 0;
+				local->filter_list[i*2+1] = 0;
+			}
+			/* Accept all packet types */
+			local->filter_list[0] = 0x0000;
+			local->filter_list[1] = 0xffff;
+			
+		}
+		else {
+			D_MISC("Reset filter before setting new\n");
+			for (i=0;i<BNEP_MAX_FILTER;i++)  {
+				local->filter_list[i*2]   = 0;
+				local->filter_list[i*2+1] = 0;
+			}
+			/* Set filter */
+			/* If more filters than BNEP_MAX_FILTERS discard some */
+			if (n > BNEP_MAX_FILTER) {
+				rsp.bnep_response_msg = cpu_to_be16(MAX_FILTER);
+				n = BNEP_MAX_FILTER;
+			}
+			filter = (u16*)&data[0];
+			for (i=0; i<n; i++) {
+				if (filter[i] > filter[i+1]) {
+					rsp.bnep_response_msg = cpu_to_be16(INVALID_PROTOCOL_ID);
+				}
+				local->filter_list[i*2] = be16_to_cpu(filter[i*2]);
+				local->filter_list[i*2+1] = be16_to_cpu(filter[i*2+1]);
+				D_MISC("Setting filter %04x - %04x \n",local->filter_list[i*2],local->filter_list[i*2+1]);
+			}
+		}
+		
+		rsp.bnep_type         = BNEP_FILTER_CONTROL;
+		rsp.bnep_filter_type  = BNEP_FILTER_RESPONSE_MSG;
+		
+		/* subscribe to a bt_tx_buf */
+		if ((tx_buf = subscribe_bt_buf(sizeof(struct bnep_filter_response_msg) + sizeof(bnep_tx_buf))) == NULL) {
+			D_ERR("bnep send packet: failed to subscribe to a %d byte bt_tx_buf\n", 4);
+		}
+		else {
+			/* copy the frame into the bt_tx_buf */
+			
+			memcpy(tx_buf->data + sizeof(bnep_tx_buf),(void*)&rsp,sizeof(struct bnep_filter_response_msg));
+			tx_buf->cur_len = sizeof(struct bnep_filter_response_msg);
+			D_MISC("BNEP send filter response msg\n");
+			l2cap_send_data(tx_buf, local->l2cap);
+		}
+	}
+	break;
+	case BNEP_FILTER_RESPONSE_MSG:    {
+		u16 bnep_filter_response_msg = be16_to_cpu(*(u16*)(&data[0]));
+		switch (bnep_filter_response_msg) {
+		case SUCCESS:
+			D_MISC("BNEP filter response: Success\n");
+			break;
+		case UNSUPPORTED:
+			D_MISC("BNEP filter response: Unsupported\n");
+			break;
+		case INVALID_PROTOCOL_ID:
+			D_MISC("BNEP filter response: Invalid Protocol ID\n");
+			break;
+		case MAX_FILTER:
+			D_MISC("BNEP filter response: Max filter\n");
+			break;
+		case SECURITY_FAILURE:
+			D_MISC("BNEP filter response: Security Failure\n");
+			break;
+		default:
+			D_MISC("BNEP filter response: Unknown message %04\n",bnep_filter_response_msg);
+			break;
+		}            
+	}
+	break;
+	default:
+		D_MISC("Unknown filter type value\n");
+		break;
+	}
+}
+
+void 
+bnep_receive_packet(l2cap_con *l2cap, u8 *data, u32 len) 
+{
+	struct sk_buff    *skb;
+	struct net_device *dev;
+	struct net_local  *local;
+	struct ethhdr     *eth,eth_header;
+	
+	
+	
+	u8  type;
+	u8  eheaders;
+	int blen,skblen;
+	bt_tx_buf *tx_buf;
+	int eth_frame = 0;
+	int i;
+	int pos = 0;
+	u16 proto = 0;
+	
+	
+	
+	eth = &eth_header;
+	
+	dev = (struct net_device *)l2cap->upper_con;
+	local = (struct net_local *)(dev->priv);
+	
+	type = data[0] & 0x7f;
+	eheaders = ((data[0] & 0x80) >> 7);  
+	
+	D_REC("bnep_receive_data: receiving %d bytes : device %s : Type %02x : Extension headers %i \n", len,dev->name, type ,eheaders);
+	PRINTPKT("bnep_receive_data:", data, len);
+	
+	/* Check for multicast packets */
+	if (eth->h_dest[0] & 1) {
+		D_REC("Multicast packet\n");
+		if (!local->promiscuous && !local->allmulti) {
+			int i, hit=0;
+			D_REC("Check multicast list\n");
+			for (i=0; i<local->mc_count; i++) {
+				if(memcmp((void*)&local->mc_list[i],eth->h_dest, sizeof(struct macaddr)) == 1) {
+					hit = 1;
+				}
+			}
+			if (!hit) {
+				D_REC("Discard packet\n"); 
+				return;
+			}
+		}
+		local->allmulti = 0;
+		local->mc_count = dev->mc_count;
+	}
+	
+	switch (type)
+	{
+	case  BNEP_GENERAL_ETHERNET:
+        {  
+		struct bnep_general_ethernet *bnep;
+		
+		bnep = (struct bnep_general_ethernet *)&data[0];
+		D_REC("BNEP_GENERAL_ETHERNET\n");         
+		for ( i=0; i<6; i++) {
+			eth->h_source[i] = bnep->saddr[i];
+		}  
+		for ( i=0; i<6; i++) {
+			eth->h_dest[i] = bnep->daddr[i];
+		}   
+		proto = be16_to_cpu(bnep->type);
+		eth->h_proto = cpu_to_be16(proto);
+		
+		blen = sizeof(struct bnep_general_ethernet);
+		eth_frame = 1;
+        }
+	break;
+	case  BNEP_FILTER_CONTROL:
+        {
+		D_REC("BNEP_FILTER_CONTROL\n");
+		bnep_filter_control(l2cap,data+2,len-2,data[1]);
+        }
+        break;                  
+	
+	case  BNEP_COMPRESSED_ETHERNET:
+        {
+		struct bnep_compressed_ethernet *bnep;
+		D_REC("BNEP_COMPRESSED_ETHERNET\n");
+		bnep = (struct bnep_compressed_ethernet *)&data[0];
+		for ( i=0; i<6; i++) {
+			eth->h_source[i] = l2cap->remote_bd[5-i];
+		}  
+		for ( i=0; i<6; i++) {
+			eth->h_dest[i] = dev->dev_addr[i];
+		}     
+		proto =  be16_to_cpu(bnep->type);
+		eth->h_proto = cpu_to_be16(proto);
+		blen = sizeof(struct bnep_compressed_ethernet);
+		eth_frame = 1;
+        }
+        break;             
+	case  BNEP_COMPRESSED_ETHERNET_SOURCE_ONLY:
+        {
+		struct bnep_compressed_ethernet_source_only *bnep;  
+		D_REC("BNEP_COMPRESSED_ETHERNET_SOURCE_ONLY\n");
+		bnep = (struct bnep_compressed_ethernet_source_only *)&data[0];
+		for ( i=0; i<6; i++) {
+			eth->h_source[i] = bnep->saddr[i];
+		}  
+		for ( i=0; i<6; i++) {
+			eth->h_dest[i] = dev->dev_addr[i];
+		}     
+		
+		proto = be16_to_cpu(bnep->type);
+		eth->h_proto = cpu_to_be16(proto);
+		blen = sizeof(struct bnep_compressed_ethernet_source_only);
+		eth_frame = 1;
+        }
+        break; 
+	case  BNEP_COMPRESSED_ETHERNET_DEST_ONLY:
+        {
+		struct bnep_compressed_ethernet_dest_only *bnep;
+		D_REC("BNEP_COMPRESSED_ETHERNET_DEST_ONLY\n");
+		bnep = (struct bnep_compressed_ethernet_dest_only *)&data[0];
+		for ( i=0; i<6; i++) {
+			eth->h_source[i] = l2cap->remote_bd[5-i];
+		}  
+		for ( i=0; i<6; i++) {
+			eth->h_dest[i] = bnep->daddr[i];
+		}     
+		proto = be16_to_cpu(bnep->type);
+		eth->h_proto = cpu_to_be16(proto);
+		blen = sizeof(struct bnep_compressed_ethernet_dest_only);
+		eth_frame = 1;
+        }
+        break;   
+	case  BNEP_802_2:                           
+		break;
+	default:
+		D_ERR("Unknown type !!!!!!!!!!!!!\n");
+		break;
+	}
+	
+	print_ip_packet(data + blen,proto);  
+	
+	/* Jump to eheader */
+	pos = blen;
+	D_REC("Change pos to after bnep header pos = %i\n",pos);
+	
+	while (eheaders == 1) {
+		struct bnep_eheader *eheader;
+		eheader = (struct bnep_eheader *)&data[pos];
+		type = eheader->bnep_type & 0x7f;
+		eheaders = ((eheader->bnep_type & 0x80) >> 7);
+		D_REC("Parse extension header type=%02x eheader=%i pos=%08x\n",type,eheaders,pos);
+		switch(type) {
+		case BNEP_EXTENSION_FILTER_CONTROL:
+			D_REC("Extension header filter control length %i \n",eheader->length);
+			bnep_filter_control(l2cap,data+pos+2,eheader->length,type);
+			break;
+		default:
+			D_REC("Unknown extension header %02x length %i\n",type,eheader->length);
+			break;
+		}
+		pos += eheader->length + 2;
+		if (pos > len) {
+			D_ERR("Error: pos > len\n");
+			break;
+		} 
+	}
+	
+	if (eth_frame == 1 ) {
+		D_REC("BNEP: eth hdr src %02x.%02x.%02x.%02x.%02x.%02x dst %02x.%02x.%02x.%02x.%02x.%02x type %04x\n", eth->h_source[0],eth->h_source[1],eth->h_source[2],eth->h_source[3],eth->h_source[4],eth->h_source[5],eth->h_dest[0],eth->h_dest[1],eth->h_dest[2],eth->h_dest[3],eth->h_dest[4],eth->h_dest[5],eth->h_proto);
+		
+		/* Calculate new packet size */
+		skblen = len - pos + sizeof(struct ethhdr);
+		
+		D_REC("len:%i pos:%i blen:%i skblen:%i ethlan:%i \n",len,pos,blen,skblen,sizeof(struct ethhdr)); 
+		
+		/* allocate a sk_buff to hold the received frame */
+		if ((skb = dev_alloc_skb(skblen)) == NULL) {
+			D_ERR("bnep_receive_data: allocation of a %d byte sk_buff failed\n", skblen);
+			
+			local->stats.rx_dropped++;
+			return;
+		}
+		
+		/* copy the received frame into skb */
+		skb_put(skb, skblen);
+		memcpy(skb->data, (void *)eth, sizeof(struct ethhdr));
+		memcpy(skb->data + sizeof(struct ethhdr), &data[pos] , len - pos);
+		
+		/* initialize skb members */
+		skb->dev = dev;
+		skb->protocol = eth_type_trans(skb,dev);
+		
+		/* Send the packet to the upper layers */
+		D_REC("Send packet to upper layers\n");
+		netif_rx(skb);
+		
+		/* update the device statistics */
+		
+		((struct net_local *)(dev->priv))->stats.rx_packets++;
+		((struct net_local *)(dev->priv))->stats.rx_bytes += len; 
+	} else {
+		D_MISC("No ethernet fram in this packet\n");
+	}
+}
+
+void
+bnep_init(void)
+{
+	int i,j;
+	char bd_addr[6],temp[8];
+        /* Set the confirm and indication functions for the L2CAP-layer */
+        this_layer.con_ind = bnep_connect_ind;
+	this_layer.con_pnd = bnep_connect_pnd;
+	this_layer.conf_ind = bnep_config_ind;
+	this_layer.disc_ind = bnep_disconnect_ind;
+	this_layer.con_cfm = bnep_connect_cfm;
+	this_layer.conf_cfm = bnep_config_cfm;
+	this_layer.disc_cfm = bnep_disconnect_cfm;
+	this_layer.receive_data = bnep_receive_packet;
+	
+	/* Register BNEP in the L2CAP layer*/
+	l2cap_register_upper(BNEP_LAYER, &this_layer);
+	
+        /* Allocate name for netdev  */
+        /* register netdev */
+	/*     if ((register_netdev(&bnep_dev)) != 0)
+	       { 
+	       D_ERR("bnep_init: register_netdev failed\n");
+	       return;
+	       }*/
+	
+        /* assign hardware address (converting to big endian) */
+        D_MISC("Read local BD\n");
+        hci_read_local_bd(bd_addr);
+        for (i = 0; i < 6; i++) 
+		temp[7-i]=bd_addr[i];
+	
+        for (j=0;j<7;j++) {
+		bnep_set_mac_address(&dev_bnep[j], &temp);
+        }
+        DSYS("BNEP Initialized \n");
+	
+}
+
+static struct net_device_stats *
+bnep_get_stats(struct net_device *dev)
+{
+        return (struct net_device_stats *)dev->priv;
+}
+
+static void 
+bnep_set_multicast_list(struct net_device *dev)
+{
+	struct net_local *local;
+	struct dev_mc_list *dmi;
+	
+	local = (struct net_local *) dev->priv;
+	
+	D_MISC("%s Set multicast list Rx mode %02x to %d addresses \n",dev->name, dev->flags, dev->mc_count);
+	
+	if ((dev->flags & IFF_PROMISC) || (dev->mc_count > BNEP_MAX_MULTI)) {
+		D_MISC("Receive all packets \n");
+		local->promiscuous = 1;
+		local->allmulti = 0;
+		dev->flags |= IFF_PROMISC;
+	} else 
+		if (dev->flags & IFF_ALLMULTI) {
+			D_MISC("Receive all multicast packets\n");
+			local->promiscuous = 0;
+			local->allmulti = 1;
+		} else
+			if (dev->mc_list != (struct dev_mc_list *) NULL) {
+				int i = 0; 
+				D_MISC("Receive all packets in multicast list\n");
+				local->promiscuous = 0;
+				local->allmulti = 0;
+				local->mc_count = dev->mc_count;
+				for (dmi = dev->mc_list; dmi; dmi = dmi->next) {
+					D_MISC(" %02x:%02x:%02x:%02x:%02x:%02x\n",
+					       dmi->dmi_addr[0], dmi->dmi_addr[1],
+					       dmi->dmi_addr[2], dmi->dmi_addr[3],
+					       dmi->dmi_addr[4], dmi->dmi_addr[5]);
+					memcpy((void*)&(local->mc_list[i++]),dmi->dmi_addr, sizeof(struct macaddr));
+				}
+				
+			} else {
+				D_MISC("Switch to normal mode \n");
+				local->promiscuous = 0;
+				local->allmulti = 0;
+				local->mc_count = 0;
+			}
+}
+
+
+static int
+bnep_init_module(void)
+{
+        int i;
+        struct net_device *d;
+        char name[16];
+        DSYS("BNEP: Init Modules \n");
+        for (i=0; i<7 ; i++) {
+                d = &dev_bnep[i];
+                d->init = bnep_init_driver;
+                sprintf(name , "bnep%i",i);
+                if (dev_alloc_name(d->name, name) < 0)         {
+			D_ERR("bnep_init: dev_alloc_name %s failed\n",name);
+			break;
+		}
+                D_MISC("Register netdev %s \n",d->name);
+                if (register_netdev(d) != 0)
+		        return -ENODEV;
+        }                
+        return 0;
+}
+
+
+module_init(bnep_init_module);
+
+/*  L2cap interface  */
+
+
+u32
+bnep_connect_req(u8 *bd_addr) {
+	D_MISC("BNEP: Connect request  %.2x:%.2x:%.2x:%.2x:%.2x:%.2x \n", bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5]);
+	
+	if (l2ca_connect_req(bd_addr, BNEP_LAYER)) {
+		D_ERR("bnep_connect_req: l2ca_connect_req failed\n");
+		return -1;
+	}
+	
+	return 0;
+}
+
+void 
+bnep_connect_ind(l2cap_con *l2cap) {
+	int i;
+	struct net_local *local;
+	D_MISC("bnep_connect_ind: remote cid %d\n", l2cap->remote_cid);
+	
+	if (l2ca_connect_rsp(l2cap, RES_SUCCESS, STAT_NOINFO)) {
+		D_ERR("bnep_connect_ind: l2ca_connect_rsp failed\n");
+		return;
+	}
+	/* Find free interface */
+	for (i=0;i<BNEP_MAX_CON;i++) {
+		local = dev_bnep[i].priv;       
+		if (local->l2cap == NULL) {
+			D_MISC("bnep_connect_ind: Connection index %i, Interface %s\n",i,dev_bnep[i].name);
+			local->l2cap = l2cap;
+			//      l2cap->dev = &dev_bnep[i];
+			l2cap->upper_con = (void *)&dev_bnep[i];
+			break;
+		}
+	}
+	
+	if (i == BNEP_MAX_CON) {
+		D_ERR("bnep_connect_cfm: No more connections\n");
+		
+	}
+	
+	return;
+}
+
+
+void bnep_connect_pnd(l2cap_con *l2cap, s32 status) {
+	D_MISC("bnep_connect_pnd: status=%d\n", status);
+}
+
+/* Confirms that a connection request has been accepted */
+void bnep_connect_cfm(l2cap_con *l2cap, s32 status) {
+
+#if 0
+	int i;
+	struct net_local *local;
+	printk("bnep_connect_cfm: remote cid %d\n", l2cap->remote_cid);
+	
+	/* did the connection fail? */
+	if (status) {
+		DSYS("bnep_connect_cfm: connection failed\n");
+		 return;
+	}
+	/* Multipoint support */
+	for (i=0;i<BNEP_MAX_CON;i++) {
+		local = dev_bnep[i].priv;
+		if (local->l2cap == NULL) {
+			D_MISC("bnep_connect_cfm: Connection index %i, Interface %s\n",
+			       i , dev_bnep[i].name);
+			local->l2cap = l2cap;
+			l2cap->upper_con = (void *)&dev_bnep[i];
+			break;
+		} 
+	}
+	
+	if (i == BNEP_MAX_CON) {
+		D_ERR("bnep_connect_cfm: No more connections\n");
+		
+	} 
+	else {
+		if (!l2ca_local_conf_done(l2cap))
+		{
+			/* still haven't sent config request yet */
+			/* Zero indicates that default values will be used */
+			if (l2ca_config_req(l2cap, BNEP_MTU, NULL, BNEP_FLUSH_TIMEOUT, 0)) {
+				D_ERR("bnep_connect_cfm: l2ca_config_req failed\n");
+			}
+		} else {
+			DSYS("already sent back a pos response\n");  
+		}
+	}
+#endif
+	
+	int i;
+	struct net_local *local;
+	printk("bnep_connect_cfm: remote cid %d\n", l2cap->remote_cid);
+	
+	/* did the connection fail? */
+	if (status) {
+		DSYS("bnep_connect_cfm: connection failed\n");
+		return;
+	}
+	/* Multipoint support Willy */
+	for (i=0;i<BNEP_MAX_CON;i++) {
+		local = dev_bnep[i].priv;
+		if (local->l2cap == NULL) {
+			printk("bnep_connect_cfm: Connection index %i, Interface %s\n",i,dev_bnep[i].name);
+			local->l2cap = l2cap;
+			l2cap->upper_con = (void *)&dev_bnep[i];
+			break;
+		} 
+	}
+	
+	if (i == BNEP_MAX_CON) {
+		D_ERR("bnep_connect_cfm: No more connections\n");    
+	}
+	else {
+		if (l2ca_config_req(l2cap, BNEP_MTU, NULL, BNEP_FLUSH_TIMEOUT, 0)) {
+			D_ERR("bnep_connect_cfm: l2ca_config_req failed\n");
+		}
+	}
+}
+
+void 
+bnep_config_ind(l2cap_con *l2cap) {
+#define FNC "bnep_config_ind: "
+#if 0
+	D_MISC("bnep_config_ind: remote cid %d\n", l2cap->remote_cid);
+	
+	/* check if we have sent a pos response yet */
+	if (!l2ca_remote_conf_done(l2cap)){
+                /* still haven't sent a pos configure response*/
+		/* send a positive response */
+		if (l2ca_config_rsp(l2cap, BNEP_MTU, NULL, CONF_SUCCESS)) {
+			D_ERR("bnep_config_ind: l2ca_config_rsp failed\n");
+		}
+	} else {
+		DSYS("Already sent back positive response\n");
+	}
+	
+	/* check if we received a pos response on a previous conf req */
+	/* if we weren't the initiator, we now send our configuration request. */
+	/* Fix l2cap->remote_mtu should be max BNEP_MTU???  */
+	if (!l2cap->initiator) {
+		/* check if we received a pos response on a 
+                   previous config req */ 
+		if (!l2ca_local_conf_done(l2cap)) {
+			l2cap->local_mtu = BNEP_MTU;
+			
+			DSYS("Local l2cap mtu set to %d\n", 
+			     l2cap->local_mtu);
+			
+			if (l2ca_config_req(l2cap, BNEP_MTU, NULL, 
+					    BNEP_FLUSH_TIMEOUT, CONF_SUCCESS))   {
+				D_ERR("bnep_config_ind: l2ca_config_req failed\n");
+			}
+		} else {
+			DSYS("Already done with config req\n");
+		}
+	}
+#endif
+	
+	D_MISC("bnep_config_ind: remote cid %d\n", l2cap->remote_cid);
+	
+	/* check if we have sent a pos response yet */
+	if (!l2ca_remote_conf_done(l2cap)) {
+		/* send a positive response */
+		if (l2ca_config_rsp(l2cap, l2cap->remote_mtu, NULL, CONF_SUCCESS)) {
+			D_ERR("bnep_config_ind: l2ca_config_rsp failed\n");
+		}
+	} 
+	else 
+		DSYS(FNC"already have sent back a pos response\n");
+	
+	/* if we weren't the initiator, we now send our configuration request. */
+	/* Fix l2cap->remote_mtu should be max BNEP_MTU???  */
+	if (!l2cap->initiator) {
+		if (!l2ca_local_conf_done(l2cap)) {
+			if (l2ca_config_req(l2cap, l2cap->remote_mtu, 
+					    NULL, BNEP_FLUSH_TIMEOUT, 0))   {
+				D_ERR("bnep_config_ind: l2ca_config_req failed\n");
+			}
+		}
+		else
+			DSYS(FNC"already ready with config req\n");
+		
+	}
+}
+
+
+void bnep_config_cfm(l2cap_con *l2cap, s32 status) {
+	struct net_device *d;
+	D_MISC("bnep_config_cfm: remote cid %d\n", l2cap->remote_cid);
+	DSYS("bnep_config_cfm: bnep channel opened\n");
+	d = l2cap->upper_con;
+	D_MISC("Interface %s\n", d->name);
+} 
+
+
+/* ether_disconnect_ind - called by l2cap to notify us that the peer
+ *                        disconnected.
+ */
+void bnep_disconnect_ind(l2cap_con *l2cap) {
+	struct net_device *dev;
+	struct net_local *local;
+	D_MISC("bnep_disconnect_ind: remote cid %d\n", l2cap->remote_cid);
+	
+	dev = (struct net_device *) l2cap->upper_con;
+	
+	/* acknowledge the disconnect */
+	if (l2ca_disconnect_rsp(l2cap)) {
+		D_ERR("ether_disconnect_ind: l2ca_disconnect_rsp failed\n");
+		return;
+	}
+	local = dev->priv;
+	local->l2cap = NULL;
+}
+
+void 
+bnep_disconnect_cfm(l2cap_con *l2cap) {
+	struct net_device *d;
+	struct net_local *l;
+	
+	D_MISC("bnep_disconnect_cfm: remote cid %d\n", l2cap->remote_cid);
+	
+	d = (struct net_device *)l2cap->upper_con;
+	l = d->priv;
+	l->l2cap = NULL;
+	
+	DSYS("bnep_disconnect_cfm: disconnected\n");
+}
diff -Nur /home/anderstj/linux-2.4.26/drivers/net/bnep.h ./drivers/net/bnep.h
--- /home/anderstj/linux-2.4.26/drivers/net/bnep.h	1970-01-01 01:00:00.000000000 +0100
+++ ./drivers/net/bnep.h	2001-05-03 12:06:47.000000000 +0200
@@ -0,0 +1,95 @@
+/* BNEP Type Values */
+
+#define BNEP_GENERAL_ETHERNET                0x00
+#define BNEP_FILTER_CONTROL                  0x01
+#define BNEP_COMPRESSED_ETHERNET             0x02
+#define BNEP_COMPRESSED_ETHERNET_SOURCE_ONLY 0x03
+#define BNEP_COMPRESSED_ETHERNET_DEST_ONLY   0x04
+#define BNEP_802_2                           0x7e
+
+/* BNEP Extension Type Values */
+
+#define BNEP_EXTENSION_FILTER_CONTROL        0x00
+
+/* BNEP Filter Type Values */
+
+#define BNEP_FILTER_SET             0x00
+#define BNEP_FILTER_RESPONSE_MSG    0x01
+
+/* BNEP Response Messages */
+
+#define SUCCESS              0x0000
+#define UNSUPPORTED          0x0001
+#define INVALID_PROTOCOL_ID  0x0002
+#define MAX_FILTER           0x0003
+#define SECURITY_FAILURE     0x0004
+
+#define BNEP_MTU 1691
+
+/* Max number of filter ranges for Network Protocol Types */
+#define BNEP_MAX_FILTER 5
+
+/* structure used to reserve space in at bt_tx_buf for hci and l2cap headers */
+typedef struct bnep_tx_buf {
+  u8 hci_hdr[5];
+  u8 l2cap_hdr[4];
+  u8 frame[0];
+} bnep_tx_buf;
+
+typedef struct bnep_general_ethernet {
+  u8 bnep_type;
+  u8 daddr[6];
+  u8 saddr[6];
+  u16 type;
+} bnep_general_ethernet;
+
+typedef struct bnep_compressed_ethernet {
+  u8 bnep_type;
+  u16 type;
+} bnep_compressed_ethernet;
+
+typedef struct bnep_compressed_ethernet_source_only {
+  u8 bnep_type;
+  u8 saddr[6];
+  u16 type;
+} bnep_compressed_ethernet_source_only;
+
+typedef struct bnep_compressed_ethernet_dest_only {
+  u8 bnep_type;
+  u8 daddr[6];
+  u16 type;
+} bnep_compressed_ethernet_dest_only;
+
+typedef struct bnep_eheader {
+  u8 bnep_type;
+  u8 length;
+} bnep_eheader;
+
+typedef struct bnep_filter_set {
+  u8 bnep_type;
+  u8 bnep_filter_type;
+} bnep_filter_set;
+
+typedef struct bnep_filter_response_msg {
+  u8 bnep_type;
+  u8 bnep_filter_type;
+  u16 bnep_response_msg;
+} bnep_filter_response_msg;
+
+/****************** EXPORTED FUNCTION DECLARATION SECTION *******************/
+static int __init bnep_init_driver(struct net_device *dev);
+
+
+u32 bnep_connect_req(u8* bd_addr);
+void bnep_connect_ind(l2cap_con *l2cap);
+void bnep_connect_pnd(l2cap_con *l2cap, int status);
+void bnep_connect_cfm(l2cap_con *l2cap, s32 status);
+void bnep_config_ind(l2cap_con* l2cap);
+void bnep_config_cfm(l2cap_con *l2cap, s32 status);
+void bnep_disconnect_ind(l2cap_con *l2cap);
+void bnep_disconnect_cfm(l2cap_con *l2cap);
+void bnep_receive_data(l2cap_con *l2cap, u8 *data, u32 len);
+s32 bnep_send_data(u32 con_id, u8 *data, u32 count);
+void bnep_disconnect_req(u8 line);
+
+u32 bnep_test(u8 *p);
diff -Nur /home/anderstj/linux-2.4.26/drivers/net/hamradio/soundmodem/.cvsignore ./drivers/net/hamradio/soundmodem/.cvsignore
--- /home/anderstj/linux-2.4.26/drivers/net/hamradio/soundmodem/.cvsignore	1970-01-01 01:00:00.000000000 +0100
+++ ./drivers/net/hamradio/soundmodem/.cvsignore	2001-11-08 14:56:16.000000000 +0100
@@ -0,0 +1,8 @@
+gentbl
+sm_tbl_afsk1200.h
+sm_tbl_afsk2666.h
+sm_tbl_psk4800.h
+sm_tbl_hapn4800.h
+sm_tbl_fsk9600.h
+sm_tbl_afsk2400_8.h
+sm_tbl_afsk2400_7.h
diff -Nur /home/anderstj/linux-2.4.26/drivers/parport/init.c ./drivers/parport/init.c
--- /home/anderstj/linux-2.4.26/drivers/parport/init.c	2002-11-29 00:53:14.000000000 +0100
+++ ./drivers/parport/init.c	2002-12-02 09:13:35.000000000 +0100
@@ -31,6 +31,7 @@
 extern int parport_sunbpp_init(void);
 extern int parport_amiga_init(void);
 extern int parport_mfc3_init(void);
+extern int parport_etrax_init(void);
 extern int parport_atari_init(void);
 
 static int parport_setup_ptr __initdata = 0;
@@ -164,6 +165,9 @@
 #ifdef CONFIG_PARPORT_SUNBPP
 	parport_sunbpp_init();
 #endif
+#ifdef CONFIG_ETRAX_PARPORT
+	parport_etrax_init();
+#endif
 	return 0;
 }
 
diff -Nur /home/anderstj/linux-2.4.26/drivers/usb/hcd.c ./drivers/usb/hcd.c
--- /home/anderstj/linux-2.4.26/drivers/usb/hcd.c	2004-04-14 15:05:32.000000000 +0200
+++ ./drivers/usb/hcd.c	2004-04-19 10:16:53.000000000 +0200
@@ -1200,12 +1200,11 @@
 	if (status)
 		return status;
 
-	// NOTE:  2.5 does this if !URB_NO_DMA_MAP transfer flag
-	
 	/* For 2.4, don't map bounce buffer if it's a root hub operation. */
 	if (urb->dev == hcd->bus->root_hub) {
 		status = rh_urb_enqueue (hcd, urb);
 	} else {
+#ifdef CONFIG_PCI
 		if (usb_pipecontrol (urb->pipe))
 			urb->setup_dma = pci_map_single (
 					hcd->pdev,
@@ -1221,6 +1220,7 @@
 					    ? PCI_DMA_FROMDEVICE
 					    : PCI_DMA_TODEVICE);
 		status = hcd->driver->urb_enqueue (hcd, urb, mem_flags);
+#endif
 	}
 	return status;
 }
@@ -1486,6 +1486,7 @@
 	// completions for periodic urbs need hooks inside the HCD.
 	// hcd_monitor_hook(MONITOR_URB_UPDATE, urb, dev)
 
+#ifdef CONFIG_PCI
 	// NOTE:  2.5 does this if !URB_NO_DMA_MAP transfer flag
 	
 	/* For 2.4, don't unmap bounce buffer if it's a root hub operation. */
@@ -1500,6 +1501,7 @@
 				usb_pipein (urb->pipe)
 				    ? PCI_DMA_FROMDEVICE
 				    : PCI_DMA_TODEVICE);
+#endif
 
 	/* pass ownership to the completion handler */
 	urb->complete (urb);
diff -Nur /home/anderstj/linux-2.4.26/fs/cramfs/inode.c ./fs/cramfs/inode.c
--- /home/anderstj/linux-2.4.26/fs/cramfs/inode.c	2002-08-03 02:39:45.000000000 +0200
+++ ./fs/cramfs/inode.c	2002-05-06 18:02:57.000000000 +0200
@@ -28,6 +28,7 @@
 #define CRAMFS_SB_BLOCKS u.cramfs_sb.blocks
 #define CRAMFS_SB_FILES u.cramfs_sb.files
 #define CRAMFS_SB_FLAGS u.cramfs_sb.flags
+#define CRAMFS_SB_FSTIME u.cramfs_sb.fstime
 
 static struct super_operations cramfs_ops;
 static struct inode_operations cramfs_dir_inode_operations;
@@ -54,6 +55,8 @@
 		inode->i_blksize = PAGE_CACHE_SIZE;
 		inode->i_gid = cramfs_inode->gid;
 		inode->i_ino = CRAMINO(cramfs_inode);
+		inode->i_mtime = sb->CRAMFS_SB_FSTIME;
+		inode->i_ctime = sb->CRAMFS_SB_FSTIME;
 		/* inode->i_nlink is left 1 - arguably wrong for directories,
 		   but it's the best we can do without reading the directory
 	           contents.  1 yields the right result in GNU find, even
@@ -144,7 +147,7 @@
 	minor = MINOR(sb->s_dev);
 
 	if (blk_size[major])
-		devsize = blk_size[major][minor] >> 2;
+		devsize = blk_size[major][minor] / (PAGE_CACHE_SIZE / 1024);
 
 	/* Ok, read in BLKS_PER_BUF pages completely first. */
 	unread = 0;
@@ -187,7 +190,6 @@
 	return read_buffers[buffer] + offset;
 }
 
-
 static struct super_block * cramfs_read_super(struct super_block *sb, void *data, int silent)
 {
 	int i;
@@ -230,10 +232,13 @@
 		goto out;
 	}
 	root_offset = super.root.offset << 2;
+	sb->CRAMFS_SB_FSTIME = 0;
 	if (super.flags & CRAMFS_FLAG_FSID_VERSION_2) {
 		sb->CRAMFS_SB_SIZE=super.size;
 		sb->CRAMFS_SB_BLOCKS=super.fsid.blocks;
 		sb->CRAMFS_SB_FILES=super.fsid.files;
+		if (super.flags & CRAMFS_FLAG_EDITION_TIMESTAMP)
+			sb->CRAMFS_SB_FSTIME=super.fsid.edition;
 	} else {
 		sb->CRAMFS_SB_SIZE=1<<28;
 		sb->CRAMFS_SB_BLOCKS=0;
diff -Nur /home/anderstj/linux-2.4.26/fs/jffs/inode-v23.c ./fs/jffs/inode-v23.c
--- /home/anderstj/linux-2.4.26/fs/jffs/inode-v23.c	2001-10-05 00:14:35.000000000 +0200
+++ ./fs/jffs/inode-v23.c	2003-03-13 10:23:37.000000000 +0100
@@ -1742,9 +1742,6 @@
 	printk(KERN_INFO "JFFS version " JFFS_VERSION_STRING
 		", (C) 1999, 2000  Axis Communications AB\n");
 	
-#ifdef CONFIG_JFFS_PROC_FS
-	jffs_proc_root = proc_mkdir("jffs", proc_root_fs);
-#endif
 	fm_cache = kmem_cache_create("jffs_fm", sizeof(struct jffs_fm),
 				     0, SLAB_HWCACHE_ALIGN, NULL, NULL);
 	node_cache = kmem_cache_create("jffs_node",sizeof(struct jffs_node),
diff -Nur /home/anderstj/linux-2.4.26/fs/jffs/jffs_fm.c ./fs/jffs/jffs_fm.c
--- /home/anderstj/linux-2.4.26/fs/jffs/jffs_fm.c	2001-10-05 00:13:18.000000000 +0200
+++ ./fs/jffs/jffs_fm.c	2001-11-08 12:22:49.000000000 +0100
@@ -75,7 +75,7 @@
 	           we don't know why. This scares me - I want formal proof
 		   of correctness of whatever number we put here. dwmw2.
 	*/
-	fmc->min_free_size = fmc->sector_size << 2;
+	fmc->min_free_size = fmc->sector_size << 1;
 	fmc->mtd = mtd;
 	fmc->c = c;
 	fmc->head = 0;
diff -Nur /home/anderstj/linux-2.4.26/fs/jffs/jffs_proc.c ./fs/jffs/jffs_proc.c
--- /home/anderstj/linux-2.4.26/fs/jffs/jffs_proc.c	2001-10-05 00:13:18.000000000 +0200
+++ ./fs/jffs/jffs_proc.c	2003-03-13 10:58:00.000000000 +0100
@@ -72,6 +72,11 @@
 	struct proc_dir_entry *part_layout = 0;
 	struct proc_dir_entry *part_root = 0;
 
+	/*
+	 * Needs to be allocated at each register since it's freed on unregister
+	 */
+	jffs_proc_root = proc_mkdir("jffs", proc_root_fs);
+
 	/* Allocate structure for local JFFS partition table */
 	if (!(part_dir = (struct jffs_partition_dir *)
 		kmalloc (sizeof (struct jffs_partition_dir), GFP_KERNEL))) {
diff -Nur /home/anderstj/linux-2.4.26/fs/partitions/Config.in ./fs/partitions/Config.in
--- /home/anderstj/linux-2.4.26/fs/partitions/Config.in	2002-11-29 00:53:15.000000000 +0100
+++ ./fs/partitions/Config.in	2002-12-02 09:14:05.000000000 +0100
@@ -39,7 +39,7 @@
    fi
    if [ "$CONFIG_AMIGA" != "y" -a "$CONFIG_ATARI" != "y" -a \
         "$CONFIG_MAC" != "y" -a "$CONFIG_SGI_IP22" != "y" -a \
-	"$CONFIG_SGI_IP27" != "y" ]; then
+	"$CONFIG_SGI_IP27" != "y" -a "$ARCH" != "cris" ]; then
       define_bool CONFIG_MSDOS_PARTITION y
    fi
    if [ "$CONFIG_AMIGA" = "y" -o "$CONFIG_AFFS_FS" = "y" ]; then
diff -Nur /home/anderstj/linux-2.4.26/include/.cvsignore ./include/.cvsignore
--- /home/anderstj/linux-2.4.26/include/.cvsignore	1970-01-01 01:00:00.000000000 +0100
+++ ./include/.cvsignore	2002-11-25 17:36:15.000000000 +0100
@@ -0,0 +1 @@
+config
diff -Nur /home/anderstj/linux-2.4.26/include/asm-cris/semaphore-helper.h ./include/asm-cris/semaphore-helper.h
--- /home/anderstj/linux-2.4.26/include/asm-cris/semaphore-helper.h	2003-08-25 13:44:43.000000000 +0200
+++ ./include/asm-cris/semaphore-helper.h	2004-04-02 07:59:48.000000000 +0200
@@ -48,7 +48,7 @@
 		dec(&sem->waking);
 		ret = 1;
 	} else if (signal_pending(tsk)) {
-		count_inc(&sem->count);
+		inc(&sem->count);
 		ret = -EINTR;
 	}
 	restore_flags(flags);
@@ -62,7 +62,7 @@
 
 	save_and_cli(flags);
 	if (read(&sem->waking) <= 0)
-		count_inc(&sem->count);
+		inc(&sem->count);
 	else {
 		dec(&sem->waking);
 		ret = 0;
diff -Nur /home/anderstj/linux-2.4.26/include/asm-cris/semaphore.h ./include/asm-cris/semaphore.h
--- /home/anderstj/linux-2.4.26/include/asm-cris/semaphore.h	2003-08-25 13:44:43.000000000 +0200
+++ ./include/asm-cris/semaphore.h	2004-04-02 07:59:48.000000000 +0200
@@ -19,7 +19,7 @@
 int printk(const char *fmt, ...);
 
 struct semaphore {
-	int count; /* not atomic_t since we do the atomicity here already */
+	atomic_t count; 
 	atomic_t waking;
 	wait_queue_head_t wait;
 #if WAITQUEUE_DEBUG
@@ -34,7 +34,7 @@
 #endif
 
 #define __SEMAPHORE_INITIALIZER(name,count)             \
-        { count, ATOMIC_INIT(0),          \
+        { ATOMIC_INIT(count), ATOMIC_INIT(0),           \
           __WAIT_QUEUE_HEAD_INITIALIZER((name).wait)    \
           __SEM_DEBUG_INIT(name) }
 
@@ -81,7 +81,7 @@
 	/* atomically decrement the semaphores count, and if its negative, we wait */
 	save_flags(flags);
 	cli();
-	failed = --(sem->count) < 0;
+	failed = --(sem->count.counter) < 0;
 	restore_flags(flags);
 	if(failed) {
 		__down(sem);
@@ -106,7 +106,7 @@
 	/* atomically decrement the semaphores count, and if its negative, we wait */
 	save_flags(flags);
 	cli();
-	failed = --(sem->count) < 0;
+	failed = --(sem->count.counter) < 0;
 	restore_flags(flags);
 	if(failed)
 		failed = __down_interruptible(sem);
@@ -124,7 +124,7 @@
 
 	save_flags(flags);
 	cli();
-	failed = --(sem->count) < 0;
+	failed = --(sem->count.counter) < 0;
 	restore_flags(flags);
 	if(failed)
 		failed = __down_trylock(sem);
@@ -149,7 +149,7 @@
 	/* atomically increment the semaphores count, and if it was negative, we wake people */
 	save_flags(flags);
 	cli();
-	wakeup = ++(sem->count) <= 0;
+	wakeup = ++(sem->count.counter) <= 0;
 	restore_flags(flags);
 	if(wakeup) {
 		__up(sem);
@@ -158,7 +158,7 @@
 
 static inline int sem_getcount(struct semaphore *sem)
 {
-	return sem->count;
+	return sem->count.counter;
 }
 
 #endif
diff -Nur /home/anderstj/linux-2.4.26/include/asm-cris/types.h ./include/asm-cris/types.h
--- /home/anderstj/linux-2.4.26/include/asm-cris/types.h	2001-02-09 01:32:44.000000000 +0100
+++ ./include/asm-cris/types.h	2004-04-01 11:05:38.000000000 +0200
@@ -44,6 +44,7 @@
 /* Dma addresses are 32-bits wide, just like our other addresses.  */
  
 typedef u32 dma_addr_t;
+typedef u32 dma64_addr_t;
 
 #endif /* __KERNEL__ */
 
diff -Nur /home/anderstj/linux-2.4.26/include/linux/.cvsignore ./include/linux/.cvsignore
--- /home/anderstj/linux-2.4.26/include/linux/.cvsignore	1970-01-01 01:00:00.000000000 +0100
+++ ./include/linux/.cvsignore	2002-11-25 17:36:38.000000000 +0100
@@ -0,0 +1,5 @@
+autoconf.h
+version.h
+compile.h
+modversions.h
+modules
diff -Nur /home/anderstj/linux-2.4.26/include/linux/cramfs_fs.h ./include/linux/cramfs_fs.h
--- /home/anderstj/linux-2.4.26/include/linux/cramfs_fs.h	2002-08-03 02:39:45.000000000 +0200
+++ ./include/linux/cramfs_fs.h	2002-04-29 14:35:57.000000000 +0200
@@ -49,7 +49,7 @@
 
 struct cramfs_info {
 	u32 crc;
-	u32 edition;
+	u32 edition;	/* contains timestamp if EDITION_TIMESTAMP flag set */
 	u32 blocks;
 	u32 files;
 };
@@ -76,6 +76,7 @@
  */
 #define CRAMFS_FLAG_FSID_VERSION_2	0x00000001	/* fsid version #2 */
 #define CRAMFS_FLAG_SORTED_DIRS		0x00000002	/* sorted dirs */
+#define CRAMFS_FLAG_EDITION_TIMESTAMP	0x00000004	/* fstime in edition */
 #define CRAMFS_FLAG_HOLES		0x00000100	/* support for holes */
 #define CRAMFS_FLAG_WRONG_SIGNATURE	0x00000200	/* reserved */
 #define CRAMFS_FLAG_SHIFTED_ROOT_OFFSET	0x00000400	/* shifted root fs */
diff -Nur /home/anderstj/linux-2.4.26/include/linux/cramfs_fs_sb.h ./include/linux/cramfs_fs_sb.h
--- /home/anderstj/linux-2.4.26/include/linux/cramfs_fs_sb.h	2001-07-20 01:14:53.000000000 +0200
+++ ./include/linux/cramfs_fs_sb.h	2002-04-29 14:35:57.000000000 +0200
@@ -5,11 +5,12 @@
  * cramfs super-block data in memory
  */
 struct cramfs_sb_info {
-			unsigned long magic;
-			unsigned long size;
-			unsigned long blocks;
-			unsigned long files;
-			unsigned long flags;
+	unsigned long magic;
+	unsigned long size;
+	unsigned long blocks;
+	unsigned long files;
+	unsigned long flags;
+	time_t        fstime; /* From the edition field if EDITION_TIMESTAMP */
 };
 
 #endif
diff -Nur /home/anderstj/linux-2.4.26/include/linux/mtd/mtdram.h ./include/linux/mtd/mtdram.h
--- /home/anderstj/linux-2.4.26/include/linux/mtd/mtdram.h	1970-01-01 01:00:00.000000000 +0100
+++ ./include/linux/mtd/mtdram.h	2002-05-06 10:49:26.000000000 +0200
@@ -0,0 +1,10 @@
+#ifndef __MTD_MTDRAM_H__
+#define __MTD_MTDRAM_H__
+
+#ifdef __KERNEL__
+#include <linux/mtd/mtd.h>
+int mtdram_init_device(struct mtd_info *mtd, void *mapped_address, 
+                       unsigned long size, char *name);
+
+#endif /* __KERNEL__ */
+#endif /* __MTD_MTDRAM_H__ */
diff -Nur /home/anderstj/linux-2.4.26/init/main.c ./init/main.c
--- /home/anderstj/linux-2.4.26/init/main.c	2003-11-28 19:26:21.000000000 +0100
+++ ./init/main.c	2003-12-04 08:32:46.000000000 +0100
@@ -166,7 +166,9 @@
 {
 	unsigned long ticks, loopbit;
 	int lps_precision = LPS_PREC;
-
+#ifdef CONFIG_SVINTO_SIM
+	loops_per_jiffy = 33 * 5000;
+#else
 	loops_per_jiffy = (1<<12);
 
 	printk("Calibrating delay loop... ");
@@ -196,7 +198,7 @@
 		if (jiffies != ticks)	/* longer than 1 tick */
 			loops_per_jiffy &= ~loopbit;
 	}
-
+#endif
 /* Round the value and print it */	
 	printk("%lu.%02lu BogoMIPS\n",
 		loops_per_jiffy/(500000/HZ),
@@ -447,10 +449,10 @@
 	initcall_t *call;
 
 	call = &__initcall_start;
-	do {
+	while (call < &__initcall_end) {
 		(*call)();
 		call++;
-	} while (call < &__initcall_end);
+	}
 
 	/* Make sure there is no pending stuff from the initcall sequence */
 	flush_scheduled_tasks();
diff -Nur /home/anderstj/linux-2.4.26/kernel/dma.c ./kernel/dma.c
--- /home/anderstj/linux-2.4.26/kernel/dma.c	2001-02-13 23:14:06.000000000 +0100
+++ ./kernel/dma.c	2003-03-10 08:37:18.000000000 +0100
@@ -45,8 +45,6 @@
 
 
 /* Channel n is busy iff dma_chan_busy[n].lock != 0.
- * DMA0 used to be reserved for DRAM refresh, but apparently not any more...
- * DMA4 is reserved for cascading.
  */
 
 struct dma_chan {
@@ -54,16 +52,7 @@
 	const char *device_id;
 };
 
-static struct dma_chan dma_chan_busy[MAX_DMA_CHANNELS] = {
-	{ 0, 0 },
-	{ 0, 0 },
-	{ 0, 0 },
-	{ 0, 0 },
-	{ 1, "cascade" },
-	{ 0, 0 },
-	{ 0, 0 },
-	{ 0, 0 }
-};
+static struct dma_chan dma_chan_busy[MAX_DMA_CHANNELS];
 
 int get_dma_list(char *buf)
 {
diff -Nur /home/anderstj/linux-2.4.26/lboot ./lboot
--- /home/anderstj/linux-2.4.26/lboot	1970-01-01 01:00:00.000000000 +0100
+++ ./lboot	2000-11-13 14:18:34.000000000 +0100
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+svinto_boot --setreg b000002c 0 --setreg b0000000 000095f8 --setreg b0000004 104 --setreg b0000008 5611 --setreg b000000c 1a000040 --bootfile DBG1 --file timage c0004000 --jump 40004000
+
+
diff -Nur /home/anderstj/linux-2.4.26/mm/oom_kill.c ./mm/oom_kill.c
--- /home/anderstj/linux-2.4.26/mm/oom_kill.c	2004-02-18 14:36:32.000000000 +0100
+++ ./mm/oom_kill.c	2004-02-19 15:14:32.000000000 +0100
@@ -20,6 +20,8 @@
 #include <linux/swap.h>
 #include <linux/swapctl.h>
 #include <linux/timex.h>
+#include <linux/config.h>
+#include <linux/reboot.h>
 
 /* #define DEBUG */
 
@@ -143,7 +145,17 @@
  */
 void oom_kill_task(struct task_struct *p)
 {
+#ifdef CONFIG_OOM_REBOOT
+	static struct timer_list reboot_timer;
+	printk(KERN_EMERG "Out of Memory: Killed process %d (%s), rebooting.\n", 
+	       p->pid, p->comm);
+        /* Wait a while to let e.g. syslogd get the message */
+	reboot_timer.function = machine_restart;
+	init_timer(&reboot_timer);
+	mod_timer(&reboot_timer, jiffies + 5* HZ);
+#else
 	printk(KERN_ERR "Out of Memory: Killed process %d (%s).\n", p->pid, p->comm);
+#endif
 
 	/*
 	 * We give our sacrificial lamb high priority and access to
diff -Nur /home/anderstj/linux-2.4.26/sboot ./sboot
--- /home/anderstj/linux-2.4.26/sboot	1970-01-01 01:00:00.000000000 +0100
+++ ./sboot	2000-11-30 13:56:17.000000000 +0100
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+svinto_boot --setreg b0000000 000095a6 --setreg b0000004 f4 --setreg b0000008 5611 --setreg b000000c dbbb9980 --bootfile DBG0 --file timage c0004000 --jump 40004000
+
+
diff -Nur /home/anderstj/linux-2.4.26/scripts/.cvsignore ./scripts/.cvsignore
--- /home/anderstj/linux-2.4.26/scripts/.cvsignore	1970-01-01 01:00:00.000000000 +0100
+++ ./scripts/.cvsignore	2003-02-28 13:27:42.000000000 +0100
@@ -0,0 +1,5 @@
+mkdep
+split-include
+tkparse
+kconfig.tk
+docproc
diff -Nur /home/anderstj/linux-2.4.26/scripts/Configure ./scripts/Configure
--- /home/anderstj/linux-2.4.26/scripts/Configure	2003-06-13 16:51:39.000000000 +0200
+++ ./scripts/Configure	2003-06-25 18:55:23.000000000 +0200
@@ -48,6 +48,10 @@
 #
 # 24 January 1999, Michael Elizabeth Chastain, <mec@shout.net>
 # - Improve the exit message (Jeff Ronne).
+#
+#  5 April 2000, Johan Adolfsson <Johan.Adolfsson@axis.com>
+# - Support for arch/$ARCH/Configure.help as well.
+
 
 #
 # Make sure we're really running bash.
@@ -81,6 +85,26 @@
 #       help variable
 #
 function help () {
+  if [ -f arch/$ARCH/Configure.help ]
+  then
+     #first escape regexp special characters in the argument:
+     var=$(echo "$1"|sed 's/[][\/.^$*]/\\&/g')
+     #now pick out the right help text:
+     text=$(sed -n "/^$var[ 	]*\$/,\${
+			/^$var[ 	]*\$/c\\
+${var}:\\
+
+			/^#/b
+			/^[^ 	]/q
+			p
+		    }" arch/$ARCH/Configure.help)
+     if [ -n "$text" ]
+     then
+	  (echo; echo "$text") | ${PAGER:-more}
+	  return
+     fi
+  fi
+
   if [ -f Documentation/Configure.help ]
   then
      #first escape regexp special characters in the argument:
@@ -350,7 +374,7 @@
 	def=${old:-$3}
 	while :; do
 	  readln "$1 ($2) [$def] " "$def" "$old"
-	  if expr "$ans" : '[0-9]*$' > /dev/null; then
+	  if expr "$ans" : '[+-]\?[0-9]*$' > /dev/null; then
             define_int "$2" "$ans"
 	    break
           else
diff -Nur /home/anderstj/linux-2.4.26/scripts/header.tk ./scripts/header.tk
--- /home/anderstj/linux-2.4.26/scripts/header.tk	2001-07-02 22:56:40.000000000 +0200
+++ ./scripts/header.tk	2004-03-29 15:30:19.000000000 +0200
@@ -207,7 +207,7 @@
 			set cmd "global $var; set $var 0"
 			eval $cmd
 		}
-		if [regexp {([0-9A-Za-z_]+)=([0-9A-Fa-f]+)} $line foo var value] {
+		if [regexp {([0-9A-Za-z_]+)=([-0-9A-Fa-f]+)} $line foo var value] {
 			set cmd "global $var; set $var $value"
 			eval $cmd
 		}
diff -Nur /home/anderstj/linux-2.4.26/scripts/lxdialog/.cvsignore ./scripts/lxdialog/.cvsignore
--- /home/anderstj/linux-2.4.26/scripts/lxdialog/.cvsignore	1970-01-01 01:00:00.000000000 +0100
+++ ./scripts/lxdialog/.cvsignore	2001-11-08 14:56:21.000000000 +0100
@@ -0,0 +1 @@
+lxdialog
