## Automatically generated incremental diff
## From:   linux-2.4.21-pre2
## To:     linux-2.4.21-pre3
## Robot:  $Id: make-incremental-diff,v 1.11 2002/02/20 02:59:33 hpa Exp $

diff -urN linux-2.4.21-pre2/CREDITS linux-2.4.21-pre3/CREDITS
--- linux-2.4.21-pre2/CREDITS	Mon Jan  6 16:32:26 2003
+++ linux-2.4.21-pre3/CREDITS	Mon Jan  6 16:32:30 2003
@@ -1895,7 +1895,8 @@
 E: pavel@ucw.cz
 E: pavel@suse.cz
 D: Softcursor for vga, hypertech cdrom support, vcsa bugfix, nbd
-D: sun4/330 port, capabilities for elf, speedup for rm on ext2, USB
+D: sun4/330 port, capabilities for elf, speedup for rm on ext2, USB,
+D: x86-64 port, software suspend
 S: Volkova 1131
 S: 198 00 Praha 9
 S: Czech Republic
diff -urN linux-2.4.21-pre2/Documentation/Configure.help linux-2.4.21-pre3/Documentation/Configure.help
--- linux-2.4.21-pre2/Documentation/Configure.help	Mon Jan  6 16:32:26 2003
+++ linux-2.4.21-pre3/Documentation/Configure.help	Mon Jan  6 16:32:30 2003
@@ -11684,6 +11684,18 @@
   module, say M here and read <file:Documentation/modules.txt> as well
   as <file:Documentation/networking/net-modules.txt>.
 
+AMD 8111 (new PCI lance) support
+CONFIG_AMD8111_ETH
+  If you have an AMD 8111-based PCI lance ethernet card,
+  answer Y here and read the Ethernet-HOWTO, available from
+  <http://www.tldp.org/docs.html#howto>.
+
+  This driver is also available as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want).
+  The module will be called amd8111e.o.  If you want to compile it as a
+  module, say M here and read <file:Documentation/modules.txt> as well
+  as <file:Documentation/networking/net-modules.txt>.
+
 Ansel Communications EISA 3200 support
 CONFIG_AC3200
   If you have a network (Ethernet) card of this type, say Y and read
@@ -14010,10 +14022,10 @@
   The module will be called visor.o. If you want to compile it as a
   module, say M here and read <file:Documentation/modules.txt>.
 
-USB Compaq iPAQ Driver
+USB PocketPC PDA Driver
 CONFIG_USB_SERIAL_IPAQ
-  Say Y here if you want to connect to your Compaq iPAQ, HP Jornada 548/568
-  or Casio EM500 running Windows CE 3.0 or PocketPC 2002 using a USB
+  Say Y here if you want to connect to your Compaq iPAQ, HP Jornada,
+  or any other PDA running Windows CE 3.0 or PocketPC 2002 using a USB
   cradle/cable. For information on using the driver,
   read <file:Documentation/usb/usb-serial.txt>.
 
@@ -14427,7 +14439,7 @@
   If in doubt then look at linux/drivers/usb/pegasus.h for the complete
   list of supported devices.
   If your particular adapter is not in the list and you are _sure_ it
-  is Pegasus or Pegasus-II based then send me (pmanolov@users.sourceforge.net)
+  is Pegasus or Pegasus-II based then send me (petkan@users.sourceforge.net)
   vendor and device IDs.
   
   This code is also available as a module ( = code which can be
@@ -24583,13 +24595,24 @@
   say M here and read <file:Documentation/modules.txt>.  The module
   will be called ns83820.o.
 
+Toshiba Type-O IR Port device driver (old driver)
+CONFIG_TOSHIBA_OLD
+  Say Y here if you want to build support for the Toshiba Type-O IR
+  chipset.  This chipset is used by the Toshiba Libretto 100CT, and
+  many more laptops. This driver is obsolete, will no more be
+  maintained and will be removed in favor of the new driver.
+  If you want to compile it as a module, say M here and read
+  <file:Documentation/modules.txt>.
+  The module will be called toshoboe.o.
+
 Toshiba Type-O IR Port device driver
 CONFIG_TOSHIBA_FIR
   Say Y here if you want to build support for the Toshiba Type-O IR
-  chipset.  This chipset is used by the Toshiba Libretto 100CT, and
-  many more laptops.  If you want to compile it as a module, say M
-  here and read <file:Documentation/modules.txt>.  The module will be
-  called toshoboe.o.
+  and Donau oboe chipsets. These chipsets are used by the Toshiba
+  Libretto 100/110CT, Tecra 8100, Portege 7020 and many more laptops.
+  If you want to compile it as a module, say M here and read
+  <file:Documentation/modules.txt>.
+  The module will be called donauboe.o.
 
 SMC IrCC
 CONFIG_SMC_IRCC_FIR
diff -urN linux-2.4.21-pre2/Documentation/DocBook/via-audio.tmpl linux-2.4.21-pre3/Documentation/DocBook/via-audio.tmpl
--- linux-2.4.21-pre2/Documentation/DocBook/via-audio.tmpl	Fri Dec 21 09:41:53 2001
+++ linux-2.4.21-pre3/Documentation/DocBook/via-audio.tmpl	Mon Jan  6 16:32:30 2003
@@ -2,7 +2,7 @@
 
 <book id="ViaAudioGuide">
  <bookinfo>
-  <title>Via 686 Audio Driver for Linux</title>
+  <title>Via 686/8233/8235 Audio Driver for Linux</title>
   
   <authorgroup>
    <author>
@@ -54,7 +54,9 @@
   	The Via VT82C686A "super southbridge" chips contain
 	AC97-compatible audio logic which features dual 16-bit stereo
 	PCM sound channels (full duplex), plus a third PCM channel intended for use
-	in hardware-assisted FM synthesis.
+	in hardware-assisted FM synthesis. The VIA VT8233/8235 extends this
+	support to include six channel output and additional record
+	facilities.
   </para>
   <para>
   	The current Linux kernel audio driver for this family of chips
@@ -199,7 +201,9 @@
   <para>
 	This driver by default supports all PCI audio devices which report
 	a vendor id of 0x1106, and a device id of 0x3058.  Subsystem vendor
-	and device ids are not examined.
+	and device ids are not examined. The 8233 support covers all devices
+	with a device id of 0x3059 and vendor id of 0x1106. Again subsystem
+	ids are ignored as they usually hold the AC97 codec vendor information.
   </para>
   <para>
 	GNU indent formatting options:
@@ -224,6 +228,21 @@
   <chapter id="changelog">
       <title>Driver ChangeLog</title>
 
+<sect1 id="version191ac"><title>
+Version 1.9.1-ac
+</title>
+  <itemizedlist spacing=compact>
+   <listitem>
+    <para>
+    Added VIA 8233/8235 support including six channel support. We don't
+    yet support S/PDIF, EAPD, using the second DSP channel and FM channels
+    as extra dsp devices, or the extra record channel. New features tested
+    on a VIA EPIA-M kindly provided by VIA.
+    </para>
+   </listitem>
+  </itemizedlist>
+</sect1>
+
 <sect1 id="version191"><title>
 Version 1.9.1
 </title>
diff -urN linux-2.4.21-pre2/Documentation/networking/bonding.txt linux-2.4.21-pre3/Documentation/networking/bonding.txt
--- linux-2.4.21-pre2/Documentation/networking/bonding.txt	Mon Jan  6 16:32:27 2003
+++ linux-2.4.21-pre3/Documentation/networking/bonding.txt	Mon Jan  6 16:32:30 2003
@@ -17,6 +17,23 @@
 For new versions of the driver, patches for older kernels and the updated
 userspace tools, please follow the links at the end of this file.
 
+
+Table of Contents
+=================
+ 
+Installation
+Bond Configuration
+Module Parameters
+Configuring Multiple Bonds
+Switch Configuration
+Verifying Bond Configuration
+Frequently Asked Questions
+High Availability
+Promiscuous Sniffing notes
+Limitations
+Resources and Links
+
+
 Installation
 ============
 
@@ -51,16 +68,21 @@
     # gcc -Wall -Wstrict-prototypes -O -I/usr/src/linux/include ifenslave.c -o ifenslave 
     # cp ifenslave /sbin/ifenslave
 
-3) Configure your system
-------------------------
-Also see the following section on the module parameters. You will need to add
-at least the following line to /etc/conf.modules (or /etc/modules.conf):
+
+Bond Configuration
+==================
+
+You will need to add at least the following line to /etc/modules.conf
+so the bonding driver will automatically load when the bond0 interface is 
+configured. Refer to the modules.conf manual page for specific modules.conf 
+syntax details. The Module Parameters section of this document describes each 
+bonding driver parameter. 
 
 	alias bond0 bonding
 
-Use standard distribution techniques to define bond0 network interface. For
-example, on modern RedHat distributions, create ifcfg-bond0 file in
-/etc/sysconfig/network-scripts directory that looks like this:
+Use standard distribution techniques to define the bond0 network interface. For
+example, on modern Red Hat distributions, create an ifcfg-bond0 file in
+the /etc/sysconfig/network-scripts directory that resembles the following:
 
 DEVICE=bond0
 IPADDR=192.168.1.1
@@ -71,12 +93,12 @@
 BOOTPROTO=none
 USERCTL=no
 
-(put the appropriate values for you network instead of 192.168.1).
+(use appropriate values for your network above)
 
-All interfaces that are part of the trunk, should have SLAVE and MASTER
-definitions. For example, in the case of RedHat, if you wish to make eth0 and
-eth1 (or other interfaces) a part of the bonding interface bond0, their config
-files (ifcfg-eth0, ifcfg-eth1, etc.) should look like this:
+All interfaces that are part of a bond should have SLAVE and MASTER
+definitions. For example, in the case of Red Hat, if you wish to make eth0 and
+eth1 a part of the bonding interface bond0, their config files (ifcfg-eth0 and
+ifcfg-eth1) should resemble the following:
 
 DEVICE=eth0
 USERCTL=no
@@ -85,89 +107,261 @@
 SLAVE=yes
 BOOTPROTO=none
 
-(use DEVICE=eth1 for eth1 and MASTER=bond1 for bond1 if you have configured
-second bonding interface). 
+Use DEVICE=eth1 in the ifcfg-eth1 config file. If you configure a second bonding
+interface (bond1), use MASTER=bond1 in the config file to make the network
+interface be a slave of bond1.
 
 Restart the networking subsystem or just bring up the bonding device if your
-administration tools allow it. Otherwise, reboot. (For the case of RedHat
-distros, you can do `ifup bond0' or `/etc/rc.d/init.d/network restart'.)
+administration tools allow it. Otherwise, reboot. On Red Hat distros you can 
+issue `ifup bond0' or `/etc/rc.d/init.d/network restart'.
 
 If the administration tools of your distribution do not support master/slave
-notation in configuration of network interfaces, you will need to configure
-the bonding device with the following commands manually:
+notation in configuring network interfaces, you will need to manually configure 
+the bonding device with the following commands:
+
+    # /sbin/ifconfig bond0 192.168.1.1 netmask 255.255.255.0 \
+      broadcast 192.168.1.255 up
 
-    # /sbin/ifconfig bond0 192.168.1.1 up
     # /sbin/ifenslave bond0 eth0
     # /sbin/ifenslave bond0 eth1
 
-(substitute 192.168.1.1 with your IP address and add custom network and custom
-netmask to the arguments of ifconfig if required).
+(use appropriate values for your network above)
 
-You can then create a script with these commands and put it into the appropriate
-rc directory.
+You can then create a script containing these commands and place it in the 
+appropriate rc directory.
 
-If you specifically need that all your network drivers are loaded before the
-bonding driver, use one of modutils' powerful features : in your modules.conf,
-tell that when asked for bond0, modprobe should first load all your interfaces :
+If you specifically need all network drivers loaded before the bonding driver,
+adding the following line to modules.conf will cause the network driver for 
+eth0 and eth1 to be loaded before the bonding driver.
 
 probeall bond0 eth0 eth1 bonding
 
-Be careful not to reference bond0 itself at the end of the line, or modprobe will
-die in an endless recursive loop.
-
-4) Module parameters.
----------------------
-The following module parameters can be passed:
+Be careful not to reference bond0 itself at the end of the line, or modprobe 
+will die in an endless recursive loop.
 
-    mode=
+To have device characteristics (such as MTU size) propagate to slave devices, 
+set the bond characteristics before enslaving the device.  The characteristics 
+are propagated during the enslave process.
+
+If running SNMP agents, the bonding driver should be loaded before any network 
+drivers participating in a bond. This requirement is due to the the interface 
+index (ipAdEntIfIndex) being associated to the first interface found with a 
+given IP address. That is, there is only one ipAdEntIfIndex for each IP 
+address. For example, if eth0 and eth1 are slaves of bond0 and the driver for 
+eth0 is loaded before the bonding driver, the interface for the IP address 
+will be associated with the eth0 interface. This configuration is shown below, 
+the IP address 192.168.1.1 has an interface index of 2 which indexes to eth0 
+in the ifDescr table (ifDescr.2).
+
+     interfaces.ifTable.ifEntry.ifDescr.1 = lo
+     interfaces.ifTable.ifEntry.ifDescr.2 = eth0
+     interfaces.ifTable.ifEntry.ifDescr.3 = eth1
+     interfaces.ifTable.ifEntry.ifDescr.4 = eth2
+     interfaces.ifTable.ifEntry.ifDescr.5 = eth3
+     interfaces.ifTable.ifEntry.ifDescr.6 = bond0
+     ip.ipAddrTable.ipAddrEntry.ipAdEntIfIndex.10.10.10.10 = 5
+     ip.ipAddrTable.ipAddrEntry.ipAdEntIfIndex.192.168.1.1 = 2
+     ip.ipAddrTable.ipAddrEntry.ipAdEntIfIndex.10.74.20.94 = 4
+     ip.ipAddrTable.ipAddrEntry.ipAdEntIfIndex.127.0.0.1 = 1
+
+This problem is avoided by loading the bonding driver before any network
+drivers participating in a bond. Below is an example of loading the bonding 
+driver first, the IP address 192.168.1.1 is correctly associated with ifDescr.2.
+
+     interfaces.ifTable.ifEntry.ifDescr.1 = lo
+     interfaces.ifTable.ifEntry.ifDescr.2 = bond0
+     interfaces.ifTable.ifEntry.ifDescr.3 = eth0
+     interfaces.ifTable.ifEntry.ifDescr.4 = eth1
+     interfaces.ifTable.ifEntry.ifDescr.5 = eth2
+     interfaces.ifTable.ifEntry.ifDescr.6 = eth3
+     ip.ipAddrTable.ipAddrEntry.ipAdEntIfIndex.10.10.10.10 = 6
+     ip.ipAddrTable.ipAddrEntry.ipAdEntIfIndex.192.168.1.1 = 2
+     ip.ipAddrTable.ipAddrEntry.ipAdEntIfIndex.10.74.20.94 = 5
+     ip.ipAddrTable.ipAddrEntry.ipAdEntIfIndex.127.0.0.1 = 1
+
+While some distributions may not report the interface name in ifDescr,
+the association between the IP address and IfIndex remains and SNMP
+functions such as Interface_Scan_Next will report that association.
 
-Possible values are 0 (round robin policy, default) and 1 (active backup
-policy), and 2 (XOR).  See question 9 and the HA section for additional info.
 
-    miimon=
-
-Use integer value for the frequency (in ms) of MII link monitoring. Zero value
-is default and means the link monitoring will be disabled. A good value is 100
-if you wish to use link monitoring. See HA section for additional info.
+Module Parameters
+=================
 
-    downdelay=
+Optional parameters for the bonding driver can be supplied as command line 
+arguments to the insmod command. Typically, these parameters are specified in 
+the file /etc/modules.conf (see the manual page for modules.conf). The 
+available bonding driver parameters are listed below. If a parameter is not 
+specified the default value is used. When initially configuring a bond, it
+is recommended "tail -f /var/log/messages" be run in a separate window to
+watch for bonding driver error messages.
+
+It is critical that either the miimon or arp_interval and arp_ip_target
+parameters be specified, otherwise serious network degradation will occur
+during link failures.
+
+mode
+        Specifies one of four bonding policies. The default is round-robin.
+        Possible values are:
+ 
+        0       Round-robin policy: Transmit in a sequential order from the
+                first available slave through the last. This mode provides
+                load balancing and fault tolerance.
+
+        1       Active-backup policy: Only one slave in the bond is active. A 
+                different slave becomes active if, and only if, the active slave
+                fails. The bond's MAC address is externally visible on only 
+                one port (network adapter) to avoid confusing the switch.
+                This mode provides fault tolerance.
+ 
+ 
+        2       XOR policy: Transmit based on [(source MAC address XOR'd with 
+                destination MAC address) modula slave count]. This selects the 
+                same slave for each destination MAC address. This mode provides
+                load balancing and fault tolerance.
+
+        3       Broadcast policy: transmits everything on all slave interfaces.
+                This mode provides fault tolerance.
+
+miimon
+ 
+        Specifies the frequency in milli-seconds that MII link monitoring will 
+        occur. A value of zero disables MII link monitoring. A value of 
+        100 is a good starting point. See High Availability section for 
+        additional information. The default value is 0.
+
+downdelay
+ 
+        Specifies the delay time in milli-seconds to disable a link after a 
+        link failure has been detected. This should be a multiple of miimon
+        value, otherwise the value will be rounded. The default value is 0.
+
+updelay
+ 
+        Specifies the delay time in milli-seconds to enable a link after a 
+        link up status has been detected. This should be a multiple of miimon
+        value, otherwise the value will be rounded. The default value is 0.
+ 
+arp_interval
+ 
+        Specifies the ARP monitoring frequency in milli-seconds. 
+        If ARP monitoring is used in a load-balancing mode (mode 0 or 2), the 
+        switch should be configured in a mode that evenly distributes packets 
+        across all links - such as round-robin. If the switch is configured to 
+        distribute the packets in an XOR fashion, all replies from the ARP 
+        targets will be received on the same link which could cause the other 
+        team members to fail. ARP monitoring should not be used in conjunction
+        with miimon. A value of 0 disables ARP monitoring. The default value 
+        is 0.
+ 
+arp_ip_target
+ 
+        Specifies the ip addresses to use when arp_interval is > 0. These are
+        the targets of the ARP request sent to determine the health of the link
+        to the targets. Specify these values in ddd.ddd.ddd.ddd format.
+        Multiple ip adresses must be seperated by a comma. At least one ip
+        address needs to be given for ARP monitoring to work. The maximum number
+        of targets that can be specified is set at 16.
+
+primary
+
+        A string (eth0, eth2, etc) to equate to a primary device. If this 
+        value is entered, and the device is on-line, it will be used first as 
+        the output media. Only when this device is off-line, will alternate 
+        devices be used. Otherwise, once a failover is detected and a new 
+        default output is chosen, it will remain the output media until it too 
+        fails. This is useful when one slave was preferred over another, i.e. 
+        when one slave is 1000Mbps and another is 100Mbps. If the 1000Mbps 
+        slave fails and is later restored, it may be preferred the faster slave
+        gracefully become the active slave - without deliberately failing the
+        100Mbps slave. Specifying a primary is only valid in active-backup mode.
+
+multicast
+
+        Integer value for the mode of operation for multicast support.
+        Possible values are:
+
+        0       Disabled (no multicast support)
+
+        1       Enabled on active slave only, useful in active-backup mode
+
+        2       Enabled on all slaves, this is the default
+
+
+Configuring Multiple Bonds
+==========================
+
+If several bonding interfaces are required, the driver must be loaded
+multiple times. For example, to configure two bonding interfaces with link 
+monitoring performed every 100 milli-seconds, the /etc/conf.modules should
+resemble the following:
 
-Use integer value for delaying disabling a link by this number (in ms) after
-the link failure has been detected. Must be a multiple of miimon. Default
-value is zero. See HA section for additional info.
+alias bond0 bonding
+alias bond1 bonding
 
-    updelay=
+options bond0 miimon=100
+options bond1 -o bonding1 miimon=100
 
-Use integer value for delaying enabling a link by this number (in ms) after
-the "link up" status has been detected. Must be a multiple of miimon. Default
-value is zero. See HA section for additional info.
+Configuring Multiple ARP Targets
+================================
 
-    arp_interval=
+While ARP monitoring can be done with just one target, it can be usefull
+in a High Availability setup to have several targets to monitor. In the
+case of just one target,  the target itself may go down or have a problem
+making it unresponsive to ARP requests. Having an additional target (or
+several) would increase the reliability of the ARP monitoring.
+Multiple ARP targets must be seperated by commas as follows:
 
-Use integer value for the frequency (in ms) of arp monitoring.  Zero value 
-is default and means the arp monitoring will be disabled.  See HA section
-for additional info.   This field is value in active_backup mode only.
+# example options for ARP monitoring with three targets
+alias bond0 bonding
+options bond0 arp_interval=60 arp_ip_target=192.168.0.1,192.168.0.3,192.168.0.9
 
-    arp_ip_target=
+For just a single target the options would resemble:
 
-An ip address to use when arp_interval is > 0.  This is the target of the
-arp request sent to determine the health of the link to the target.  
-Specify this value in ddd.ddd.ddd.ddd format.
+# example options for ARP monitoring with one target
+alias bond0 bonding
+options bond0 arp_interval=60 arp_ip_target=192.168.0.100
 
-If you need to configure several bonding devices, the driver must be loaded
-several times. I.e. for two bonding devices, your /etc/conf.modules must look
-like this:
 
-alias bond0 bonding
-alias bond1 bonding
 
-options bond0 miimon=100
-options bond1 -o bonding1 miimon=100
+Switch Configuration
+====================
 
-5) Testing configuration
-------------------------
-You can test the configuration and transmit policy with ifconfig. For example,
-for round robin policy, you should get something like this:
+While the switch does not need to be configured when the active-backup
+policy is used (mode=1), it does need to be configured for the round-robin, 
+XOR, and broadcast policies (mode=0, mode=2, and mode=3). 
+
+
+Verifying Bond Configuration
+============================
+
+1) Bonding information files
+----------------------------
+The bonding driver information files reside in the /proc/net/bond* directories. 
+
+Sample contents of /proc/net/bond0/info after the driver is loaded with 
+parameters of mode=0 and miimon=1000 is shown below.
+ 
+        Bonding Mode: load balancing (round-robin)
+        Currently Active Slave: eth0
+        MII Status: up
+        MII Polling Interval (ms): 1000
+        Up Delay (ms): 0
+        Down Delay (ms): 0
+ 
+        Slave Interface: eth1
+        MII Status: up
+        Link Failure Count: 1
+ 
+        Slave Interface: eth0
+        MII Status: up
+        Link Failure Count: 1
+
+2) Network verification
+-----------------------
+The network configuration can be verified using the ifconfig command. In
+the example below, the bond0 interface is the master (MASTER) while eth0 and 
+eth1 are slaves (SLAVE). Notice all slaves of bond0 have the same MAC address 
+(HWaddr) as bond0.
 
 [root]# /sbin/ifconfig
 bond0     Link encap:Ethernet  HWaddr 00:C0:F0:1F:37:B4  
@@ -193,12 +387,13 @@
           collisions:0 txqueuelen:100 
           Interrupt:9 Base address:0x1400 
 
-Questions :
-===========
+
+Frequently Asked Questions
+==========================
 
 1.  Is it SMP safe?
 
-	Yes.  The old 2.0.xx channel bonding patch was not SMP safe.
+	Yes. The old 2.0.xx channel bonding patch was not SMP safe.
 	The new driver was designed to be SMP safe from the start.
 
 2.  What type of cards will work with it?
@@ -209,31 +404,30 @@
 
 3.  How many bonding devices can I have?
 
-	One for each module you load. See section on module parameters for how
+	One for each module you load. See section on Module Parameters for how
 	to accomplish this.
 
 4.  How many slaves can a bonding device have?
 
-	Limited by the number of network interfaces Linux supports and the
-	number of cards you can place in your system.
+	Limited by the number of network interfaces Linux supports and/or the
+	number of network cards you can place in your system.
 
 5.  What happens when a slave link dies?
 
-	If your ethernet cards support MII status monitoring and the MII
-	monitoring has been enabled in the driver (see description of module
-	parameters), there will be no adverse consequences. This release
-	of the bonding driver knows how to get the MII information and
+	If your ethernet cards support MII or ETHTOOL link status monitoring 
+        and the MII monitoring has been enabled in the driver (see description 
+        of module parameters), there will be no adverse consequences. This 
+        release of the bonding driver knows how to get the MII information and
 	enables or disables its slaves according to their link status.
-	See section on HA for additional information.
+	See section on High Availability for additional information.
 
-	For ethernet cards not supporting MII status, or if you wish to 
-	verify that packets have been both send and received, you may
-	configure the arp_interval and arp_ip_target.  If packets have
-	not been sent or received during this interval, an arp request
-	is sent to the target to generate send and receive traffic.  
-	If after this interval, either the successful send and/or 
-	receive count has not incremented, the next slave in the sequence
-	will become the active slave.
+	For ethernet cards not supporting MII status, the arp_interval and
+        arp_ip_target parameters must be specified for bonding to work
+        correctly. If packets have not been sent or received during the
+        specified arp_interval durration, an ARP request is sent to the targets 
+        to generate send and receive traffic. If after this interval, either 
+        the successful send and/or receive count has not incremented, the next 
+        slave in the sequence will become the active slave.
 
 	If neither mii_monitor and arp_interval is configured, the bonding
 	driver will not handle this situation very well. The driver will 
@@ -245,11 +439,12 @@
 6.  Can bonding be used for High Availability?
 
 	Yes, if you use MII monitoring and ALL your cards support MII link
-	status reporting. See section on HA for more information.
+	status reporting. See section on High Availability for more information.
 
 7.  Which switches/systems does it work with?
 
-	In round-robin mode, it works with systems that support trunking:
+	In round-robin and XOR mode, it works with systems that support 
+	trunking:
 	
 	* Cisco 5500 series (look for EtherChannel support).
 	* SunTrunking software.
@@ -259,7 +454,8 @@
 	  units.
 	* Linux bonding, of course !
 	
-	In Active-backup mode, it should work with any Layer-II switches.
+	In active-backup mode, it should work with any Layer-II switche.
+
 
 8.  Where does a bonding device get its MAC address from?
 
@@ -297,55 +493,68 @@
 
 9.  Which transmit polices can be used?
 
-	Round robin, based on the order of enslaving, the output device
-	is selected base on the next available slave.  Regardless of
+	Round-robin, based on the order of enslaving, the output device
+	is selected base on the next available slave. Regardless of
 	the source and/or destination of the packet.
 
-	XOR, based on (src hw addr XOR dst hw addr) % slave cnt.  This
-	selects the same slave for each destination hw address.
-
 	Active-backup policy that ensures that one and only one device will
 	transmit at any given moment. Active-backup policy is useful for
 	implementing high availability solutions using two hubs (see
-	section on HA).
+	section on High Availability).
+
+	XOR, based on (src hw addr XOR dst hw addr) % slave count. This
+	policy selects the same slave for each destination hw address.
 
-High availability
+	Broadcast policy transmits everything on all slave interfaces.
+
+
+High Availability
 =================
 
-To implement high availability using the bonding driver, you need to
-compile the driver as module because currently it is the only way to pass
-parameters to the driver. This may change in the future.
-
-High availability is achieved by using MII status reporting. You need to
-verify that all your interfaces support MII link status reporting. On Linux
-kernel 2.2.17, all the 100 Mbps capable drivers and yellowfin gigabit driver
-support it. If your system has an interface that does not support MII status
-reporting, a failure of its link will not be detected!
-
-The bonding driver can regularly check all its slaves links by checking the
-MII status registers. The check interval is specified by the module argument
-"miimon" (MII monitoring). It takes an integer that represents the
-checking time in milliseconds. It should not come to close to (1000/HZ)
-(10 ms on i386) because it may then reduce the system interactivity. 100 ms
-seems to be a good value. It means that a dead link will be detected at most
-100 ms after it goes down.
+To implement high availability using the bonding driver, the driver needs to be
+compiled as a module, because currently it is the only way to pass parameters 
+to the driver. This may change in the future.
+
+High availability is achieved by using MII or ETHTOOL status reporting. You 
+need to verify that all your interfaces support MII or ETHTOOL link status 
+reporting.  On Linux kernel 2.2.17, all the 100 Mbps capable drivers and 
+yellowfin gigabit driver support MII. To determine if ETHTOOL link reporting 
+is available for interface eth0, type "ethtool eth0" and the "Link detected:" 
+line should contain the correct link status. If your system has an interface 
+that does not support MII or ETHTOOL status reporting, a failure of its link 
+will not be detected! A message indicating MII and ETHTOOL is not supported by 
+a network driver is logged when the bonding driver is loaded with a non-zero 
+miimon value.
+
+The bonding driver can regularly check all its slaves links using the ETHTOOL
+IOCTL (ETHTOOL_GLINK command) or by checking the MII status registers. The 
+check interval is specified by the module argument "miimon" (MII monitoring). 
+It takes an integer that represents the checking time in milliseconds. It 
+should not come to close to (1000/HZ) (10 milli-seconds on i386) because it 
+may then reduce the system interactivity. A value of 100 seems to be a good 
+starting point. It means that a dead link will be detected at most 100 
+milli-seconds after it goes down.
 
 Example:
 
    # modprobe bonding miimon=100
 
-Or, put in your /etc/modules.conf :
+Or, put the following lines in /etc/modules.conf:
 
    alias bond0 bonding
    options bond0 miimon=100
 
-There are currently two policies for high availability, depending on whether
-a) hosts are connected to a single host or switch that support trunking
-b) hosts are connected to several different switches or a single switch that
-   does not support trunking.
+There are currently two policies for high availability. They are dependent on 
+whether:
+
+   a) hosts are connected to a single host or switch that support trunking
+
+   b) hosts are connected to several different switches or a single switch that
+      does not support trunking
 
-1) HA on a single switch or host - load balancing
--------------------------------------------------
+
+1) High Availability on a single switch or host - load balancing
+----------------------------------------------------------------
 It is the easiest to set up and to understand. Simply configure the
 remote equipment (host or switch) to aggregate traffic over several
 ports (Trunk, EtherChannel, etc.) and configure the bonding interfaces.
@@ -356,7 +565,7 @@
 long time if all ports in a trunk go down. This is not Linux, but really
 the switch (reboot it to ensure).
 
-Example 1 : host to host at double speed
+Example 1 : host to host at twice the speed
 
           +----------+                          +----------+
           |          |eth0                  eth0|          |
@@ -370,7 +579,7 @@
      # ifconfig bond0 addr
      # ifenslave bond0 eth0 eth1
 
-Example 2 : host to switch at double speed
+Example 2 : host to switch at twice the speed
 
           +----------+                          +----------+
           |          |eth0                 port1|          |
@@ -384,7 +593,9 @@
      # ifconfig bond0 addr                     and port2
      # ifenslave bond0 eth0 eth1
 
-2) HA on two or more switches (or a single switch without trunking support)
+
+2) High Availability on two or more switches (or a single switch without 
+   trunking support)
 ---------------------------------------------------------------------------
 This mode is more problematic because it relies on the fact that there
 are multiple ports and the host's MAC address should be visible on one
@@ -423,14 +634,14 @@
                +--------------+ host2 +----------------+
                          eth0 +-------+ eth1
 
-In this configuration, there are an ISL - Inter Switch Link (could be a trunk),
+In this configuration, there is an ISL - Inter Switch Link (could be a trunk),
 several servers (host1, host2 ...) attached to both switches each, and one or
 more ports to the outside world (port3...). One an only one slave on each host
 is active at a time, while all links are still monitored (the system can
 detect a failure of active and backup links).
 
 Each time a host changes its active interface, it sticks to the new one until
-it goes down. In this example, the hosts are not too much affected by the
+it goes down. In this example, the hosts are negligibly affected by the
 expiration time of the switches' forwarding tables.
 
 If host1 and host2 have the same functionality and are used in load balancing
@@ -460,6 +671,7 @@
 it goes down. In this example, the host is strongly affected by the expiration
 time of the switch forwarding table.
 
+
 3) Adapting to your switches' timing
 ------------------------------------
 If your switches take a long time to go into backup mode, it may be
@@ -488,8 +700,34 @@
     # modprobe bonding miimon=100 mode=1 downdelay=2000 updelay=5000
     # modprobe bonding miimon=100 mode=0 downdelay=0 updelay=5000
 
-4) Limitations
---------------
+
+Promiscuous Sniffing notes
+==========================
+
+If you wish to bond channels together for a network sniffing
+application --- you wish to run tcpdump, or ethereal, or an IDS like
+snort, with its input aggregated from multiple interfaces using the
+bonding driver --- then you need to handle the Promiscuous interface
+setting by hand. Specifically, when you "ifconfing bond0 up" you
+must add the promisc flag there; it will be propagated down to the
+slave interfaces at ifenslave time; a full example might look like:
+
+   grep bond0 /etc/modules.conf || echo alias bond0 bonding >/etc/modules.conf
+   ifconfig bond0 promisc up
+   for if in eth1 eth2 ...;do
+       ifconfig $if up
+       ifenslave bond0 $if
+   done
+   snort ... -i bond0 ...
+
+Ifenslave also wants to propagate addresses from interface to
+interface, appropriately for its design functions in HA and channel
+capacity aggregating; but it works fine for unnumbered interfaces;
+just ignore all the warnings it emits.
+
+
+Limitations
+===========
 The main limitations are :
   - only the link status is monitored. If the switch on the other side is
     partially down (e.g. doesn't forward anymore, but the link is OK), the link
@@ -500,7 +738,13 @@
     Use the arp_interval/arp_ip_target parameters to count incoming/outgoing
     frames.  
 
-Resources and links
+  - A Transmit Load Balancing policy is not currently available. This mode 
+    allows every slave in the bond to transmit while only one receives. If 
+    the "receiving" slave fails, another slave takes over the MAC address of 
+    the failed receiving slave.
+
+
+Resources and Links
 ===================
 
 Current development on this driver is posted to:
diff -urN linux-2.4.21-pre2/Documentation/networking/generic-hdlc.txt linux-2.4.21-pre3/Documentation/networking/generic-hdlc.txt
--- linux-2.4.21-pre2/Documentation/networking/generic-hdlc.txt	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre3/Documentation/networking/generic-hdlc.txt	Mon Jan  6 16:32:30 2003
@@ -0,0 +1,115 @@
+Generic HDLC layer for Linux kernel 2.4/2.5
+Krzysztof Halasa <khc@pm.waw.pl>
+May, 2001
+
+
+Generic HDLC layer currently supports:
+- Frame Relay (ANSI, CCITT and no LMI), with ARP support (no InARP),
+- raw HDLC (IPv4 only),
+- Cisco HDLC,
+- PPP (uses syncppp.c),
+- X.25 (uses X.25 routines).
+
+There are hardware drivers for the following cards:
+- C101 by Moxa Technologies Co., Ltd.
+- RISCom/N2 by SDL Communications Inc.
+- and others, some not in the official kernel.
+
+Make sure the hdlc.o and the hardware driver are loaded. It should
+create a number of "hdlc" (hdlc0 etc) network devices, one for each
+WAN port. You'll need the "sethdlc" utility, get it from:
+	http://hq.pm.waw.pl/hdlc/
+
+Compile sethdlc.c utility:
+	gcc -O2 -Wall -o sethdlc sethdlc.c
+Make sure you're using a correct version of sethdlc for your kernel.
+
+Use sethdlc to set physical interface, clock rate, HDLC mode used,
+and add any required PVCs if using Frame Relay.
+Usually you want something like:
+
+	sethdlc hdlc0 clock int rate 128000
+	sethdlc hdlc0 cisco interval 10 timeout 25
+or
+	sethdlc hdlc0 rs232 clock ext
+	sethdlc fr lmi ansi
+	sethdlc create 99
+
+In Frame Relay mode, ifconfig master hdlc device up (without assigning
+any IP address to it) before using pvc devices.
+
+
+Setting interface:
+
+* v35 | rs232 | x21 | t1 | e1 - sets physical interface for a given port
+                                if the card has software-selectable interfaces
+  loopback - activate hardware loopback (for testing only)
+* clock ext - external clock (uses DTE RX and TX clock)
+* clock int - internal clock (provides clock signal on DCE clock output)
+* clock txint - TX internal, RX external (provides TX clock on DCE output)
+* clock txfromrx - TX clock derived from RX clock (TX clock on DCE output)
+* rate - sets clock rate in bps (not required for external clock or
+                                 for txfromrx)
+
+Setting protocol:
+
+* hdlc - sets raw HDLC (IP-only) mode
+  nrz / nrzi / fm-mark / fm-space / manchester - sets transmission code
+  no-parity / crc16 / crc16-pr0 (CRC16 with preset zeros) / crc32-itu
+  crc16-itu (CRC16 with ITU-T polynomial) / crc16-itu-pr0 - sets parity
+
+* cisco - sets Cisco HDLC mode (IP, IPv6 and IPX supported)
+  interval - time in seconds between keepalive packets
+  timeout - time in seconds after last received keepalive packet before
+            we assume the link is down
+
+* ppp - sets synchronous PPP mode
+
+* x25 - sets X.25 mode
+
+* fr - Frame Relay mode
+  lmi ansi / ccitt / none - LMI (link management) type
+  dce - Frame Relay DCE (network) side LMI instead of default DTE (user).
+  It has nothing to do with clocks!
+  t391 - link integrity verification polling timer (in seconds) - user
+  t392 - polling verification timer (in seconds) - network
+  n391 - full status polling counter - user
+  n392 - error threshold - both user and network
+  n393 - monitored events count - both user and network
+
+* create | delete n - FR only - adds / deletes PVC interface with DLCI #n.
+
+
+
+
+Board-specific issues
+---------------------
+
+n2.o and c101.o need parameters to work (note double quotes):
+
+	insmod n2 hw='"io,irq,ram,ports[:io,irq,...]"'
+example:
+	insmod n2 hw='"0x300,10,0xD0000,01"'
+
+or
+	insmod c101 hw='"irq,ram[:irq,...]"
+example:
+	insmod c101 hw='"9,0xdc000"'
+
+If built into the kernel, these drivers need kernel (command line) parameters:
+	n2=io,irq,ram,ports:...
+or
+	c101=irq,ram:...
+
+
+
+If you have a problem with N2 or C101 card, you can issue the "private"
+command to see port's packet descriptor rings:
+
+	sethdlc hdlc0 private
+
+The hardware driver have to be build with CONFIG_HDLC_DEBUG_RINGS.
+Attaching this info to bug reports would be helpful. Anyway, let me know
+if you have problems using this.
+
+For patches and other info look at http://hq.pm.waw.pl/hdlc/
diff -urN linux-2.4.21-pre2/Documentation/networking/ifenslave.c linux-2.4.21-pre3/Documentation/networking/ifenslave.c
--- linux-2.4.21-pre2/Documentation/networking/ifenslave.c	Fri Aug  2 17:39:42 2002
+++ linux-2.4.21-pre3/Documentation/networking/ifenslave.c	Mon Jan  6 16:32:30 2003
@@ -41,6 +41,16 @@
  *    - 2002/02/18 Erik Habbinga <erik_habbinga @ hp dot com> :
  *       - ifr2.ifr_flags was not initialized in the hwaddr_notset case,
  *         SIOCGIFFLAGS now called before hwaddr_notset test
+ *
+ *    - 2002/10/31 Tony Cureington <tony.cureington * hp_com> :
+ *       - If the master does not have a hardware address when the first slave
+ *         is enslaved, the master is assigned the hardware address of that 
+ *         slave - there is a comment in bonding.c stating "ifenslave takes 
+ *         care of this now." This corrects the problem of slaves having 
+ *         different hardware addresses in active-backup mode when 
+ *         multiple interfaces are specified on a single ifenslave command
+ *         (ifenslave bond0 eth0 eth1).
+ *
  */
 
 static char *version =
@@ -131,6 +141,7 @@
 	sa_family_t master_family;
 	char **spp, *master_ifname, *slave_ifname;
 	int hwaddr_notset;
+	int master_up;
 
 	while ((c = getopt_long(argc, argv, "acdfrvV?h", longopts, 0)) != EOF)
 		switch (c) {
@@ -300,10 +311,86 @@
 				return 1;
 			}
 
-			if (hwaddr_notset) { /* we do nothing */
+			if (hwaddr_notset) {
+				/* assign the slave hw address to the 
+				 * master since it currently does not 
+				 * have one; otherwise, slaves may
+				 * have different hw addresses in 
+				 * active-backup mode as seen when enslaving 
+				 * using "ifenslave bond0 eth0 eth1" because
+				 * hwaddr_notset is set outside this loop.
+				 * TODO: put this and the "else" portion in
+				 *       a function.
+				 */
+				goterr = 0;
+				master_up = 0;
+				if (if_flags.ifr_flags & IFF_UP) {
+					if_flags.ifr_flags &= ~IFF_UP;
+					if (ioctl(skfd, SIOCSIFFLAGS, 
+							&if_flags) < 0) {
+						goterr = 1;
+						fprintf(stderr, 
+							"Shutting down "
+							"interface %s failed: "
+							"%s\n",
+							master_ifname, 
+							strerror(errno));
+					} else {
+						/* we took the master down, 
+						 * so we must bring it up 
+						 */
+						master_up = 1; 
+					}
+				}
 
-			}
-			else {  /* we'll assign master's hwaddr to this slave */
+				if (!goterr) {
+					/* get the slaves MAC address */
+					strncpy(if_hwaddr.ifr_name, 
+							slave_ifname, IFNAMSIZ);
+					if (ioctl(skfd, SIOCGIFHWADDR, 
+							&if_hwaddr) < 0) {
+						fprintf(stderr, 
+							"Could not get MAC "
+							"address of %s: %s\n",
+							slave_ifname, 
+							strerror(errno));
+						strncpy(if_hwaddr.ifr_name, 
+							master_ifname, 
+							IFNAMSIZ);
+						goterr=1;
+					}
+				}
+
+				if (!goterr) {
+					strncpy(if_hwaddr.ifr_name, 
+						master_ifname, IFNAMSIZ);
+					if (ioctl(skfd, SIOCSIFHWADDR, 
+							&if_hwaddr) < 0) {
+						fprintf(stderr, 
+							"Could not set MAC "
+							"address of %s: %s\n",
+							master_ifname, 
+							strerror(errno));
+						goterr=1;
+					} else {
+						hwaddr_notset = 0;
+					}
+				}
+
+				if (master_up) {
+					if_flags.ifr_flags |= IFF_UP;
+					if (ioctl(skfd, SIOCSIFFLAGS, 
+							&if_flags) < 0) {
+						fprintf(stderr, 
+							"Bringing up interface "
+							"%s failed: %s\n",
+							master_ifname, 
+							strerror(errno));
+					}
+				}
+
+			} else {  
+				/* we'll assign master's hwaddr to this slave */
 				if (ifr2.ifr_flags & IFF_UP) {
 					ifr2.ifr_flags &= ~IFF_UP;
 					if (ioctl(skfd, SIOCSIFFLAGS, &ifr2) < 0) {
diff -urN linux-2.4.21-pre2/Documentation/networking/ip-sysctl.txt linux-2.4.21-pre3/Documentation/networking/ip-sysctl.txt
--- linux-2.4.21-pre2/Documentation/networking/ip-sysctl.txt	Fri Aug  2 17:39:42 2002
+++ linux-2.4.21-pre3/Documentation/networking/ip-sysctl.txt	Mon Jan  6 16:32:30 2003
@@ -351,15 +351,26 @@
 	Change the maximum number of multicast groups we can subscribe to.
 	Default: 20
 
-conf/interface/*: 
-conf/all/* is special and changes the settings for all interfaces.
-	Change special settings per interface.
+conf/interface/*  changes special settings per interface (where "interface" is 
+		  the name of your network interface)
+conf/all/*	  is special, changes the settings for all interfaces
+
 
 log_martians - BOOLEAN
 	Log packets with impossible addresses to kernel log.
+	log_martians for the interface will be enabled if at least one of
+	conf/{all,interface}/log_martians is set to TRUE,
+	it will be disabled otherwise
 
 accept_redirects - BOOLEAN
 	Accept ICMP redirect messages.
+	accept_redirects for the interface will be enabled if:
+	- both conf/{all,interface}/accept_redirects are TRUE in the case forwarding
+	  for the interface is enabled
+	or
+	- at least one of conf/{all,interface}/accept_redirects is TRUE in the case
+	  forwarding for the interface is disabled
+	accept_redirects for the interface will be disabled otherwise
 	default TRUE (host)
 		FALSE (router)
 
@@ -369,6 +380,8 @@
 mc_forwarding - BOOLEAN
 	Do multicast routing. The kernel needs to be compiled with CONFIG_MROUTE
 	and a multicast routing daemon is required.
+	conf/all/mc_forwarding must also be set to TRUE to enable multicast routing
+	for the interface
 
 medium_id - INTEGER
 	Integer value used to differentiate the devices by the medium they
@@ -383,30 +396,46 @@
 
 proxy_arp - BOOLEAN
 	Do proxy arp.
+	proxy_arp for the interface will be enabled if at least one of
+	conf/{all,interface}/proxy_arp is set to TRUE,
+	it will be disabled otherwise
 
 shared_media - BOOLEAN
 	Send(router) or accept(host) RFC1620 shared media redirects.
 	Overrides ip_secure_redirects.
+	shared_media for the interface will be enabled if at least one of
+	conf/{all,interface}/shared_media is set to TRUE,
+	it will be disabled otherwise
 	default TRUE
 
 secure_redirects - BOOLEAN
 	Accept ICMP redirect messages only for gateways,
 	listed in default gateway list.
+	secure_redirects for the interface will be enabled if at least one of
+	conf/{all,interface}/secure_redirects is set to TRUE,
+	it will be disabled otherwise
 	default TRUE
 
 send_redirects - BOOLEAN
-	Send redirects, if router. Default: TRUE
+	Send redirects, if router.
+	send_redirects for the interface will be enabled if at least one of
+	conf/{all,interface}/send_redirects is set to TRUE,
+	it will be disabled otherwise
+	Default: TRUE
 
 bootp_relay - BOOLEAN
 	Accept packets with source address 0.b.c.d destined
 	not to this host as local ones. It is supposed, that
 	BOOTP relay daemon will catch and forward such packets.
-
+	conf/all/bootp_relay must also be set to TRUE to enable BOOTP relay
+	for the interface
 	default FALSE
 	Not Implemented Yet.
 
 accept_source_route - BOOLEAN
 	Accept packets with SRR option.
+	conf/all/accept_source_route must also be set to TRUE to accept packets
+	with SRR option on the interface
 	default TRUE (router)
 		FALSE (host)
 
@@ -417,7 +446,10 @@
 	    networks running a slow unreliable protocol (sort of RIP),
 	    or using static routes.
 
-	0 - No source validation. 
+	0 - No source validation.
+
+	conf/all/rp_filter must also be set to TRUE to do source validation
+	on the interface
 
 	Default value is 0. Note that some distributions enable it
 	in startup scripts.
@@ -437,6 +469,10 @@
 	particular interfaces. Only for more complex setups like load-
 	balancing, does this behaviour cause problems.
 
+	arp_filter for the interface will be enabled if at least one of
+	conf/{all,interface}/arp_filter is set to TRUE,
+	it will be disabled otherwise
+
 tag - INTEGER
 	Allows you to write a number, which can be used as required.
 	Default value is 0.
@@ -451,8 +487,8 @@
 Updated by:
 Andi Kleen
 ak@muc.de
-
-
+Nicolas Delon
+delon.nicolas@wanadoo.fr
 
 
 
@@ -462,6 +498,15 @@
 IPv6 has no global variables such as tcp_*.  tcp_* settings under ipv4/ also
 apply to IPv6 [XXX?].
 
+bindv6only - BOOLEAN
+	Default value for IPV6_V6ONLY socket option,
+	which restricts use of the IPv6 socket to IPv6 communication 
+	only.
+		TRUE: disable IPv4-mapped address feature
+		FALSE: enable IPv4-mapped address feature
+
+	Default: FALSE (as specified in RFC2553bis)
+
 conf/default/*:
 	Change the interface-specific default settings.
 
@@ -560,8 +605,14 @@
 	routers are present.
 	Default: 3
 
+icmp/*:
+ratelimit - INTEGER
+	Limit the maximal rates for sending ICMPv6 packets.
+	0 to disable any limiting, otherwise the maximal rate in jiffies(1)
+	Default: 100
+
 IPv6 Update by:
-Pekka Savola
-pekkas@netcore.fi
+Pekka Savola <pekkas@netcore.fi>
+YOSHIFUJI Hideaki / USAGI Project <yoshfuji@linux-ipv6.org>
 
 $Id: ip-sysctl.txt,v 1.19.2.1 2001/12/13 08:59:27 davem Exp $
diff -urN linux-2.4.21-pre2/Documentation/usb/ov511.txt linux-2.4.21-pre3/Documentation/usb/ov511.txt
--- linux-2.4.21-pre2/Documentation/usb/ov511.txt	Thu Nov 28 15:53:08 2002
+++ linux-2.4.21-pre3/Documentation/usb/ov511.txt	Mon Jan  6 16:32:30 2003
@@ -276,6 +276,15 @@
 	might be necessary if your camera has a custom lens assembly. This has
 	no effect with video capture devices.
 
+  NAME: ov518_color
+  TYPE: integer (Boolean)
+  DEFAULT: 0 (off)
+  DESC: Enable OV518 color support. This is off by default since it doesn't
+	work most of the time. If you want to try it, you must also load
+	ov518_decomp with the "nouv=0" parameter. If you get improper colors or
+	diagonal lines through the image, restart your video app and try again.
+	Repeat as necessary.
+
 WORKING FEATURES:
  o Color streaming/capture at most widths and heights that are multiples of 8.
  o RGB24, RGB565, YUV420/YUV420P, YUV422/YUYV, and YUV422P color
@@ -285,6 +294,7 @@
  o /proc status reporting
  o SAA7111A video capture support at 320x240 and 640x480
  o Compression support
+ o SMP compatibility
 
 HOW TO CONTACT ME:
 
diff -urN linux-2.4.21-pre2/Documentation/usb/scanner-hp-sane.txt linux-2.4.21-pre3/Documentation/usb/scanner-hp-sane.txt
--- linux-2.4.21-pre2/Documentation/usb/scanner-hp-sane.txt	Tue May  2 13:49:18 2000
+++ linux-2.4.21-pre3/Documentation/usb/scanner-hp-sane.txt	Wed Dec 31 16:00:00 1969
@@ -1,81 +0,0 @@
-Copyright (C) 1999, 2000 David E. Nelson
-
-April 26, 2000
-
-CHANGES
-
-- Amended for Linux-2.3.99-pre6-3
-- Updated for multiple scanner support
-
-INTRODUCTION
-
-This document will hopefully provide enough info on how to get SANE
-working with a Hewlett Packard USB capable scanner using the USB
-interface.  The majority of HP Scanners support the Scanner Control
-Language (SCL) which is both published by HP and supported by SANE.
-The only HP Scanners that I'm aware of that do not support SCL are the
-4200C ,3300C, and the PhotoSmart S20.  All other HP scanners with USB
-interfaces should work (4100C, 5200C, 6200C, and 6300C) as do models
-that are derived from the models above. ie the 6350C which is a 6300C
-with a transparency adaptor included with the scanner at time of
-purchase.  Of course as HP releases new scanners this information may
-change.
-
-
-REQUIREMENTS
-
-In order to get this running you'll need USB support in your kernel in
-addition to USB Scanner support.  Please refer to scanner.txt for
-issues pertaining to Linux USB and USB Scanner support.
-
-An installed version of SANE which is available from
-http://www.mostang.com/sane/.  Testing has been performed using
-version SANE-1.0.1.  For instructions on building and installing SANE,
-refer to the various README files within the SANE distribution.
-
-The latest SANE HP backend is available from http://www.kirchgessner.net.
-At the time of this writing, version 0.83 was available.
-
-
-OK, I'VE INSTALLED SANE. SO WHAT DO I DO NOW?
-
-NOTE: $INSTALL_DIR is the location where SANE is installed.  It may be
-/usr/local, /usr, /opt or somewhere else.  If you don't know, ask your
-system administrator.
-
-1) Make sure that you have the libsane-hp.* libraries under the
-$INSTALL_DIR/lib/sane/ directory.  If you don't, then the HP backend
-was either not compiled or installed properly.
-
-2) Under the directory $INSTALL_DIR/etc/sane.d/ edit the following
-files: dll.conf, hp.conf.
-
-  dll.conf:  Make sure that the 'hp' entry is present and uncommented.
-
-  hp.conf: This should contain two lines:
-
-    /dev/usbscanner
-    option connect-device
-
-NOTE: If you are using multiple scanners, make sure to have the correct
-device, ie /dev/usbscanner0.  See scanner.txt for more info.
-
-3) You should now be able to use SANE (xscanimage or scanimage).
-
-Don't forget to read any relevant man pages regarding the usage of
-SANE.  If you have other entries uncommented in 'dll.conf', you may
-have to specify the device to (x)scanimage.  Again, `man` is your
-friend.  The xscanimage (1) man page has info on how to get 'The Gimp'
-to work with xscanimage.  Note that Gimp support must be compiled into
-SANE for it to work.  If you are dealing with a RedHat system, this
-means that you'll also need to install the gimp-devel rpm package
-prior to compiling SANE.
-
-NOTE: The issues regarding core dumping by (x)scanimage have (or seem
-to be thus far) been resolved with version 0.2+ of the USB scanner
-driver which should be available in linux-2.3.23.  If you notice
-otherwise, please contact me.
-
-David /\/elson
-dnelson@jump.net
-http://www.jump.net/~dnelson
diff -urN linux-2.4.21-pre2/Documentation/usb/scanner.txt linux-2.4.21-pre3/Documentation/usb/scanner.txt
--- linux-2.4.21-pre2/Documentation/usb/scanner.txt	Fri Dec 21 09:41:53 2001
+++ linux-2.4.21-pre3/Documentation/usb/scanner.txt	Mon Jan  6 16:32:30 2003
@@ -1,154 +1,114 @@
 Copyright (C) 1999, 2000 David E. Nelson <dnelson@jump.net>
+Updated 2003 by Henning Meier-Geinitz <henning@meier-geinitz.de>
 
-April 26, 2000
-
-CHANGES
-
-- Amended for linux-2.4.12
-- Updated devfs support
-- Amended for linux-2.3.99-pre6-3
-- Appended hp_scan.c to end of this README
-- Removed most references to HP
-- Updated uhci/ohci host controller info
-- Updated support for multiple scanner support
-- Updated supported scanners list
-- Updated usbdevfs info
-- Spellcheck
 
 OVERVIEW
 
-This README addresses issues regarding how to configure the kernel
-to access a USB scanner.  Although the driver was originally conceived
-for USB HP scanners, it's general enough so that it can be used with
-other scanners.  Also, one can now pass the USB Vendor and Product
-ID's using module parameters for unknown scanners.  Refer to the
-document scanner-hp-sane.txt for guidance on how to configure SANE to
-use a USB HP Scanner.
+This README addresses issues regarding how to configure the kernel to access a
+USB scanner.  Although the driver was originally conceived for USB HP
+scanners, it's general enough so that it can be used with most other USB
+scanners.  Also, one can pass the USB Vendor and Product IDs using module
+parameters for unknown scanners.
+
+There are two drivers for SCSI-over-USB scanners: 
+* The "hpusbscsi" module for Hewlett-Packard 53xx series, Hewlett-Packard 7400,
+  Minolta Scan Dual II, Minolta Elite II
+* The "microtek" module for the Microtek Scanmaker X6
+
+In addition to the kernel driver, user-space tools like SANE are necessary to
+actually use the scanner.  SANE ("Scanner Access Now Easy") provides drivers
+for a variety of USB scanners.  See the appropriate SANE man page for details,
+e.g. man sane-usb and man sane-hp (for HP scanners).
+
+NOTE: Just because a product is detected by this driver does not mean that
+applications exist that support the product.  It's in the hopes that this will
+allow developers a means to produce applications that will support the listed
+USB products.
 
 
 ADDITIONAL INFORMATION
 
-http://www.linux-usb.org/
+http://www.linux-usb.org/           (General information, mailing lists, links)
+http://www.mostang.com/sane/        (SANE user-space tools)
+http://www.meier-geinitz.de/kernel/ (USB scanner driver information and patches)
 
 
 REQUIREMENTS
 
-A host with a USB port.  Ideally, either a UHCI (Intel) or OHCI
-(Compaq and others) hardware port should work.  At the time of this
-writing, there are two UHCI drivers and one OHCI.
-
-A Linux kernel with USB support enabled or a backported version to
-linux-2.2.x.  See http://www.linux-usb.org for more information on
-accomplishing this.
-
-'lspci' which is only needed to determine the type of USB hardware
-available/installed in your machine.
-
-CONFIGURATION
-
-Using `lspci -v`, determine the type of USB hardware available/installed.
+A host with a USB port.  Ideally, either a UHCI (Intel), OHCI (Compaq and
+others) or EHCI hardware should work.  
 
-  If you see something like:
+Using "make menuconfig" or your preferred method for configuring the kernel,
+select "Support for USB", "OHCI/UHCI/EHCI" depending on your hardware, "USB
+Scanner support", and "Preliminary USB device filesystem".  Compile and
+install the modules (you may need to execute "depmod -a" to update the module
+dependencies).  If any of the USB sections were compiled into the kernel, a
+reboot is necessary.  NOTE: Updating the boot disk with "lilo" may also be
+required.  Testing was performed only as modules, YMMV.
+
+Up to 16 scanners can be connected/used simultaneously.  If devfs support is
+enabled, see next section.  Otherwise, the device files must be created
+manually if they don't exist yet, either by MAKEDEV or mknod.
+
+MAKEDEV method:
+  cd /dev
+  MAKEDEV usb
+  Check that the device files "/dev/usb/scanner0" - "/dev/usb/scanner15" have
+  been created.
+
+mknod method:
+  mknod /dev/usb/scanner0 c 180 48
+  mknod /dev/usb/scanner1 c 180 49
+                  . 
+                  .
+  mknod /dev/usb/scanner15 c 180 63
 
-    USB Controller: ......
-    Flags: .....
-    I/O ports at ....
-
-  Then you have a UHCI based controller.
-
-  If you see something like:
-
-     USB Controller: .....
-     Flags: ....
-     Memory at .....
-
-  Then you have a OHCI based controller.
-
-Using `make menuconfig` or your preferred method for configuring the
-kernel, select 'Support for USB', 'OHCI/UHCI' depending on your
-hardware (determined from the steps above), 'USB Scanner support', and
-'Preliminary USB device filesystem'.  Compile and install the modules
-(you may need to execute `depmod -a` to update the module
-dependencies). If any of the USB sections were compiled into the
-kernel, a reboot is necessary. NOTE: Updating the boot disk with
-'lilo' may also be required. Testing was performed only as modules,
-YMMV.
-
-Beginning with version 0.4 of the driver, up to 16 scanners can be
-connected/used simultaneously.  For devfs support, see next section.
-If you intend to use more than one scanner at a time w/o devfs support:
-
-   Add a device for the USB scanner:
-	`mknod /dev/usbscanner0 c 180 48`
-	`mknod /dev/usbscanner1 c 180 49`
-                      . 
-                      .
-	`mknod /dev/usbscanner15 c 180 63`
-
-
-If you foresee using only one scanner it is best to:
-	`mknod /dev/usbscanner0 c 180 48`
-	`ln -s /dev/usbscanner0 /dev/usbscanner`
-
-
-Set appropriate permissions for /dev/usbscanner[0-15] (don't forget
+Set appropriate permissions for /dev/usb/scanner[0-15] (don't forget
 about group and world permissions).  Both read and write permissions
-are required for proper operation. For example:
-	`chmod 666 /dev/usbscanner0`
+are required for proper operation.  For example:
+  chmod 666 /dev/usb/scanner0
 
 Load the appropriate modules (if compiled as modules):
 
-  OHCI:
-    modprobe usb-ohci
-    modprobe scanner
-
-  UHCI:
-    modprobe usb-uhci
-    modprobe scanner
+  modprobe usb-ohci (or uhci, usb-uhci, ehci)
+  modprobe scanner
+
 
 DEVFS
 
 The later versions of the Linux kernel (2.4.8'ish) included a dynamic
-device filesystem call 'devfs'.  With devfs, there is no need to
+device filesystem call "devfs".  With devfs, there is no need to
 create the device files as explained above; instead, they are
 dynamically created for you.  For USB Scanner, the device is created
 in /dev/usb/scannerX where X can range from 0 to 15 depending on the
 number of scanners connected to the system.
 
-To see if you have devfs, issue the command `cat /proc/filesytems`.
-If devfs is listed you should be ready to go.  You sould also have a
-process running called 'devfsd'.  In order to make sure, issue the
-command `ps aux | grep '[d]evfsd'`.
-
-If you would like to maintain /dev/usbscanner0 in order to maintain
-compatibility with applications, then add the following to
-/etc/devfsd.conf:
-
-REGISTER ^usb/scanner0$ CFUNCTION GLOBAL symlink usb/scanner0 usbscanner0
-UNREGISTER ^usb/scanner0$ CFUNCTION GLOBAL unlink usbscanner0
+To see if you have devfs, issue the command "cat /proc/filesytems".
+If devfs is listed you should be ready to go.  You should also have a
+process running called "devfsd".  In order to make sure, issue the
+command "ps aux | grep '[d]evfsd'".
 
-Then reset the scanner (reseat the USB connector or power cycle). This
-will create the necessary symlinks in /dev to /dev/usb.
 
 CONCLUSION
 
-That's it.  SANE should now be able to access the device.  
+That's it.  SANE should now be able to access the device.  To make sure the
+device was detected, use "cat /proc/bus/usb/devices".  Your scanner should be
+listed and the line starting with "I:" should look similar to this example:
+
+  I:  If#= 0 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=ff Prot=ff Driver=usbscanner
+
+The important part is "Driver=usbscanner".  If it reads "Driver=(none)", the
+USB scanner driver didn't recognize the scanner.  Have a look at the MODULE
+PARAMETERS section for what to do in this case.
+
+For more details on the format of "/proc/bus/usb/devices" see
+Documentation/usb/proc_usb_info.txt.
 
-There is a small test program (hp_scan.c -- appended below) that can
-be used to test the scanner device if it's an HP scanner that supports
-SCL (Scanner Control Language).  Known HP scanner that support SCL are
-the 4100, 5200, 6200, the 6300 -- note that the 4200 is *not*
-supported since it does not understand SCL; it's also strongly
-suspected that the 3300 and the PhotoSmart S20 are not SCL compliant.
-Hp_scan.c's purpose is to test the driver without having to
-retrieve/configure SANE.  Hp_scan.c will scan the entire bed and put
-the output into a file called 'out.dat' in the current directory.  The
-data in the file is raw data so it's not very useful for imaging.
 
 MESSAGES
 
 usb_control/bulk_msg: timeout -- On occasions this message will appear
-in '/var/adm/messages', on the console, or both depending on how
+in "/var/adm/messages", on the console, or both depending on how
 your system is configured.  This is a side effect that scanners are
 sometimes very slow at warming up and/or initializing.  In most cases,
 however, only several of these messages should appear and is generally
@@ -165,122 +125,90 @@
 
 probe_scanner: Endpoint determination failed -- This means that the
 driver is unable to detect a supported configuration for means to
-communicate with the scanner.  See also 'probe_scanner: Undetected
-endpoint'.
+communicate with the scanner.  See also "probe_scanner: Undetected
+endpoint".
 
 funky result -- Most of the time the data flow between the computer
 and the scanner goes smoothly.  However, due to whatever reason,
 whether it be solar flares or stray neutrons, sometimes the
 communications don't work as expected.  The driver tries to handle
 most types of errors but not all.  When this message is seen,
-something weird happened.  Please contact the maintaner listed at the
-top of this file.
-
-SUPPORTED SCANNERS
-
-NOTE: Just because a product is listed here does not mean that
-applications exist that support the product.  It's in the hopes that
-this will allow developers a means to produce applications that will
-support the listed USB products.
-
-At the time of this writing, the following scanners were supported by
-scanner.c:
-
- Acer
-	 Prisa Acerscan 620U & 640U (!)
-	 Prisa AcerScan 620U (!)
- Agfa
-	 SnapScan 1212U
-	 Another SnapScan 1212U (?)
-	 SnapScan Touch
- Colorado -- See Primax/Colorado below
- Epson -- See Seiko/Epson below
- Genius
-         ColorPage-Vivid Pro
- Hewlett Packard
-	 3300C
-	 4100C
-	 4200C
-	 PhotoSmart S20
-	 5200C
-	 6200C
-	 6300C
- Microtek
-	 ScanMaker X6 - X6U
-	 Phantom 336CX - C3
-	 Phantom 336CX - C3 #2
-	 Phantom C6
-	 ScanMaker V6USL
-	 ScanMaker V6USL #2
-	 ScanMaker V6UL - SpicyU
- Mustek
-	 1200 CU
- Primax/Colorado
-	 G2-300 #1
-	 G2-600 #1
-	 G2E-300 #1
-	 ReadyScan 636i
-	 G2-300 #2
-	 G2-600 #2
-	 G2E-300 #2
-	 G2E-600
-	 Colorado USB 9600
-	 Colorado USB 19200
-	 Colorado 600u
-	 Colorado 1200u
- Seiko/Epson Corp.
-	 Perfection 636U and 636Photo
-	 Perfection 610
-	 Perfection 1200U and 1200Photo
- Umax
-	 Astra 1220U
-	 Astra 1236U
-	 Astra 2000U
-	 Astra 2200U
- Visioneer
-	 OneTouch 5300
-	 OneTouch 7600 duplicate ID (!)
-	 6100
+something weird happened.  Please contact the mailing list (see
+CONTACT section for details).
 
 
 MODULE PARAMETERS
 
 If you have a device that you wish to experiment with or try using
-this driver with, but the Vendor and Product ID's are not coded in,
+this driver with, but the Vendor and Product IDs are not coded in,
 don't despair.  If the driver was compiled as a module, you can pass
 options to the driver.  Simply add 
 
   options scanner vendor=0x#### product=0x****
 
 to the /etc/modules.conf file replacing the #'s and the *'s with the
-correct ID's.  The ID's can be retrieved from the messages file or
-using `cat /proc/bus/usb/devices`. Note that USB /proc support must be
-enabled during kernel configuration.  If the 'scanner' module is
-already loaded into memory, it must be reloaded for the module
-parameters to take effect.  In essence, `rmmod scanner; modprobe
-scanner` must be performed.
+correct IDs.  The IDs can be retrieved from the messages file or
+using "cat /proc/bus/usb/devices".
 
-**NOTE**: In later kernels (2.3.38+), a new filesystem was introduced,
-usbdevfs.  To mount the filesystem, issue the command (as root):
+If the default timeout is too low, i.e. there are frequent "timeout" messages,
+you may want to increase the timeout manually by using the parameter
+"read_timeout".  The time is given in seconds.  This is an example for
+modules.conf with a timeout of 60 seconds:
+
+  options scanner read_timeout=60
+ 
+If the "scanner" module is already loaded into memory, it must be reloaded for
+the module parameters to take effect.  In essence, "rmmod scanner; modprobe
+scanner" must be performed.
 
-  mount -t usbdevfs /proc/bus/usb /proc/bus/usb
 
-An alternative and more permanent method would be to add
+BUGS
 
-  none  /proc/bus/usb  usbdevfs  defaults  0  0
+Just look at the list of fixes in the source files. 
 
-to /etc/fstab.  This will mount usbdevfs at each reboot.  You can then
-issue `cat /proc/bus/usb/devices` to extract USB device information.
 
+CONTACT
 
-BUGS
+For asking about problems and fixes, use the linux-usb-users mailing list. For
+patches, linux-usb-devel should be used. Information on both lists can be
+found on http://www.linux-usb.org/.
+
+
+CHANGES
+
+- Added information about read_timeout
+- Added more details about /proc/bus/usb/devices
+- Added/updated links
+- Added pointers two "special" scanner drivers
+- Reordering, spell-checking, formatting
+- Used /dev/usb/scanner[0-15] instead of /dev/usbscanner[0-15]
+- Removed some basic USB configuration stuff
+- Added EHCI
+- Removed some more references to HP
+- Amended for linux-2.4.12
+- Updated devfs support
+- Amended for linux-2.3.99-pre6-3
+- Appended hp_scan.c to end of this README
+- Removed most references to HP
+- Updated uhci/ohci host controller info
+- Updated support for multiple scanner support
+- Updated supported scanners list
+- Updated usbdevfs info
+- Spellcheck
 
-Just look at the list of fixes in the source files.  So, if you
-encounter any problems feel free to drop me an email.
 
-David /\/elson
-dnelson@jump.net
-http://www.jump.net/~dnelson
+HP TEST PROGRAM
+
+There is a small test program (hp_scan.c -- appended below) that can
+be used to test the scanner device if it's an HP scanner that supports
+SCL (Scanner Control Language).  Known HP scanner that support SCL are
+the 4100, 5200, 6200, the 6300 -- note that the 4200 is *not*
+supported since it does not understand SCL; it's also strongly
+suspected that the 3300 and the PhotoSmart S20 are not SCL compliant.
+Hp_scan.c's purpose is to test the driver without having to
+retrieve/configure SANE.  Hp_scan.c will scan the entire bed and put
+the output into a file called "out.dat" in the current directory.  The
+data in the file is raw data so it's not very useful for imaging.
 
 --------------- snip -- hp_scan.c -- snip ---------------
 /*
@@ -347,7 +275,7 @@
 		exit (1);
 	}
 	
-	if((fp=open("/dev/usbscanner", O_RDWR)) < 0) {
+	if((fp=open("/dev/usb/scanner0", O_RDWR)) < 0) {
 		perror("Unable to open scanner device");
 		exit (1);
 	}
diff -urN linux-2.4.21-pre2/Documentation/usb/usb-serial.txt linux-2.4.21-pre3/Documentation/usb/usb-serial.txt
--- linux-2.4.21-pre2/Documentation/usb/usb-serial.txt	Mon Jan  6 16:32:27 2003
+++ linux-2.4.21-pre3/Documentation/usb/usb-serial.txt	Mon Jan  6 16:32:30 2003
@@ -95,27 +95,27 @@
   Kroah-Hartman at greg@kroah.com
 
 
-Compaq iPAQ, HP Jornada and Casio EM500 driver
+PocketPC PDA Driver
 
-  This driver can be used to connect to Compaq iPAQ, HP Jornada and Casio EM500
-  PDAs running Windows CE 3.0 or PocketPC 2002 using a USB cable/cradle.
-  It's very likely that every device supported by ActiveSync USB works with this
-  driver. The driver supports the Compaq iPAQ, Jornada 548/568 and the Casio
-  EM500 out of the box. For others, please use module parameters to specify
-  the product and vendor id. e.g. modprobe ipaq vendor=0x3f0 product=0x1125
+  This driver can be used to connect to Compaq iPAQ, HP Jornada, Casio EM500
+  and other PDAs running Windows CE 3.0 or PocketPC 2002 using a USB
+  cable/cradle.
+  Most devices supported by ActiveSync are supported out of the box.
+  For others, please use module parameters to specify the product and vendor
+  id. e.g. modprobe ipaq vendor=0x3f0 product=0x1125
 
   The driver presents a serial interface (usually on /dev/ttyUSB0) over
-  which one may run ppp and establish a TCP/IP link to the iPAQ. Once this
+  which one may run ppp and establish a TCP/IP link to the PDA. Once this
   is done, you can transfer files, backup, download email etc. The most
-  significant advantage of using USB is speed - you can get 73 to 113
-  kbytes/sec for download/upload to the iPAQ.
+  significant advantage of using USB is speed - I can get 73 to 113
+  kbytes/sec for download/upload to my iPAQ.
 
   This driver is only one of a set of components required to utilize
   the USB connection. Please visit http://synce.sourceforge.net which
   contains the necessary packages and a simple step-by-step howto.
 
   Once connected, you can use Win CE programs like ftpView, Pocket Outlook
-  from the iPAQ and xcerdisp, synce utilities from the Linux side.
+  from the PDA and xcerdisp, synce utilities from the Linux side.
 
   To use Pocket IE, follow the instructions given at
   http://www.tekguru.co.uk/EM500/usbtonet.htm to achieve the same thing
@@ -130,8 +130,18 @@
 
   If it doesn't work for some reason, load both the usbserial and ipaq module
   with the module parameter "debug" set to 1 and examine the system log.
-  You can also try soft-resetting your iPAQ before attempting a connection.
+  You can also try soft-resetting your PDA before attempting a connection.
 
+  Other functionality may be possible depending on your PDA. According to
+  Wes Cilldhaire <billybobjoehenrybob@hotmail.com>, with the Toshiba E570,
+  ...if you boot into the bootloader (hold down the power when hitting the
+  reset button, continuing to hold onto the power until the bootloader screen
+  is displayed), then put it in the cradle with the ipaq driver loaded, open
+  a terminal on /dev/ttyUSB0, it gives you a "USB Reflash" terminal, which can
+  be used to flash the ROM, as well as the microP code..  so much for needing
+  Toshiba's $350 serial cable for flashing!! :D
+  NOTE: This has NOT been tested. Use at your own risk.
+ 
   For any questions or problems with the driver, please contact Ganesh
   Varadarajan <ganesh@veritas.com>
 
diff -urN linux-2.4.21-pre2/Makefile linux-2.4.21-pre3/Makefile
--- linux-2.4.21-pre2/Makefile	Mon Jan  6 16:32:27 2003
+++ linux-2.4.21-pre3/Makefile	Mon Jan  6 16:32:30 2003
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 4
 SUBLEVEL = 21
-EXTRAVERSION = -pre2
+EXTRAVERSION = -pre3
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 
@@ -137,8 +137,7 @@
 DRIVERS-y += drivers/char/char.o \
 	drivers/block/block.o \
 	drivers/misc/misc.o \
-	drivers/net/net.o \
-	drivers/media/media.o
+	drivers/net/net.o
 DRIVERS-$(CONFIG_AGP) += drivers/char/agp/agp.o
 DRIVERS-$(CONFIG_DRM_NEW) += drivers/char/drm/drm.o
 DRIVERS-$(CONFIG_DRM_OLD) += drivers/char/drm-4.0/drm.o
@@ -179,6 +178,7 @@
 DRIVERS-$(CONFIG_HAMRADIO) += drivers/net/hamradio/hamradio.o
 DRIVERS-$(CONFIG_TC) += drivers/tc/tc.a
 DRIVERS-$(CONFIG_USB) += drivers/usb/usbdrv.o
+DRIVERS-y +=drivers/media/media.o
 DRIVERS-$(CONFIG_INPUT) += drivers/input/inputdrv.o
 DRIVERS-$(CONFIG_HIL) += drivers/hil/hil.o
 DRIVERS-$(CONFIG_I2O) += drivers/message/i2o/i2o.o
diff -urN linux-2.4.21-pre2/arch/arm/def-configs/badge4 linux-2.4.21-pre3/arch/arm/def-configs/badge4
--- linux-2.4.21-pre2/arch/arm/def-configs/badge4	Fri Aug  2 17:39:42 2002
+++ linux-2.4.21-pre3/arch/arm/def-configs/badge4	Mon Jan  6 16:32:30 2003
@@ -798,6 +798,7 @@
 # CONFIG_RADIO_MIROPCM20 is not set
 # CONFIG_RADIO_MIROPCM20_RDS is not set
 # CONFIG_RADIO_SF16FMI is not set
+# CONFIG_RADIO_SF16FMR2 is not set
 # CONFIG_RADIO_TERRATEC is not set
 # CONFIG_RADIO_TRUST is not set
 # CONFIG_RADIO_TYPHOON is not set
diff -urN linux-2.4.21-pre2/arch/arm/def-configs/empeg linux-2.4.21-pre3/arch/arm/def-configs/empeg
--- linux-2.4.21-pre2/arch/arm/def-configs/empeg	Wed Oct 20 16:29:08 1999
+++ linux-2.4.21-pre3/arch/arm/def-configs/empeg	Mon Jan  6 16:32:30 2003
@@ -119,6 +119,7 @@
 # CONFIG_VIDEO_PMS is not set
 # CONFIG_VIDEO_SAA5249 is not set
 # CONFIG_RADIO_SF16FMI is not set
+# CONFIG_RADIO_SF16FMR2 is not set
 # CONFIG_RADIO_TYPHOON is not set
 # CONFIG_RADIO_ZOLTRIX is not set
 
diff -urN linux-2.4.21-pre2/arch/arm/def-configs/footbridge linux-2.4.21-pre3/arch/arm/def-configs/footbridge
--- linux-2.4.21-pre2/arch/arm/def-configs/footbridge	Mon Nov 27 17:07:59 2000
+++ linux-2.4.21-pre3/arch/arm/def-configs/footbridge	Mon Jan  6 16:32:30 2003
@@ -599,6 +599,7 @@
 # CONFIG_RADIO_MAESTRO is not set
 # CONFIG_RADIO_MIROPCM20 is not set
 # CONFIG_RADIO_SF16FMI is not set
+# CONFIG_RADIO_SF16FMR2 is not set
 # CONFIG_RADIO_TERRATEC is not set
 # CONFIG_RADIO_TRUST is not set
 # CONFIG_RADIO_TYPHOON is not set
diff -urN linux-2.4.21-pre2/arch/i386/kernel/apm.c linux-2.4.21-pre3/arch/i386/kernel/apm.c
--- linux-2.4.21-pre2/arch/i386/kernel/apm.c	Thu Nov 28 15:53:09 2002
+++ linux-2.4.21-pre3/arch/i386/kernel/apm.c	Mon Jan  6 16:32:30 2003
@@ -247,6 +247,7 @@
  *	    					powering off
  *	    [no-]debug			log some debugging messages
  *	    [no-]power[-_]off		power off on shutdown
+ *	    [no-]smp			Use apm even on an SMP box
  *	    bounce[-_]interval=<n>	number of ticks to ignore suspend
  *	    				bounces
  *          idle[-_]threshold=<n>       System idle percentage above which to
@@ -393,6 +394,7 @@
 static int			got_clock_diff;
 #endif
 static int			debug;
+static int			smp = 0;
 static int			apm_disabled = -1;
 #ifdef CONFIG_SMP
 static int			power_off;
@@ -494,6 +496,40 @@
 }
 
 /*
+ * Lock APM functionality to physical CPU 0
+ */
+ 
+#ifdef CONFIG_SMP
+
+static unsigned long apm_save_cpus(void)
+{
+	unsigned long x = current->cpus_allowed;
+	/* Some bioses don't like being called from CPU != 0 */
+	if (cpu_number_map(smp_processor_id()) != 0) {
+		set_cpus_allowed(current, 1 << cpu_logical_map(0));
+		if (unlikely(cpu_number_map(smp_processor_id()) != 0))
+			BUG();
+	}
+	return x;
+}
+
+static inline void apm_restore_cpus(unsigned long mask)
+{
+	set_cpus_allowed(current, mask);
+}
+
+#else
+
+/*
+ *	No CPU lockdown needed on a uniprocessor
+ */
+ 
+#define apm_save_cpus()	0
+#define apm_restore_cpus(x)	(void)(x)
+
+#endif
+
+/*
  * These are the actual BIOS calls.  Depending on APM_ZERO_SEGS and
  * apm_info.allow_ints, we are being really paranoid here!  Not only
  * are interrupts disabled, but all the segment registers (except SS)
@@ -566,7 +602,8 @@
 {
 	APM_DECL_SEGS
 	unsigned long	flags;
-
+	unsigned long cpus = apm_save_cpus();
+	
 	__save_flags(flags);
 	APM_DO_CLI;
 	APM_DO_SAVE_SEGS;
@@ -588,6 +625,9 @@
 		: "memory", "cc");
 	APM_DO_RESTORE_SEGS;
 	__restore_flags(flags);
+	
+	apm_restore_cpus(cpus);
+	
 	return *eax & 0xff;
 }
 
@@ -611,6 +651,8 @@
 	APM_DECL_SEGS
 	unsigned long	flags;
 
+	unsigned long cpus = apm_save_cpus();
+	
 	__save_flags(flags);
 	APM_DO_CLI;
 	APM_DO_SAVE_SEGS;
@@ -636,6 +678,9 @@
 	}
 	APM_DO_RESTORE_SEGS;
 	__restore_flags(flags);
+
+	apm_restore_cpus(cpus);
+	
 	return error;
 }
 
@@ -751,10 +796,12 @@
 	if (apm_bios_call_simple(APM_FUNC_IDLE, 0, 0, &eax)) {
 		static unsigned long t;
 
-		if (time_after(jiffies, t + 10 * HZ)) {
+		/* This always fails on some SMP boards running UP kernels.
+		 * Only report the failure the first 5 times.
+		 */
+		if (++t < 5) {
 			printk(KERN_DEBUG "apm_do_idle failed (%d)\n",
 					(eax >> 8) & 0xff);
-			t = jiffies;
 		}
 		return -1;
 	}
@@ -888,17 +935,12 @@
 	/*
 	 * This may be called on an SMP machine.
 	 */
-#ifdef CONFIG_SMP
-	/* Some bioses don't like being called from CPU != 0 */
-	if (cpu_number_map(smp_processor_id()) != 0) {
-		current->cpus_allowed = 1;
-		schedule();
-		if (unlikely(cpu_number_map(smp_processor_id()) != 0))
-			BUG();
-	}
-#endif
 	if (apm_info.realmode_power_off)
+	{
+		(void)apm_save_cpus();
 		machine_real_restart(po_bios_call, sizeof(po_bios_call));
+		/* Never returns */
+	}
 	else
 		(void) set_system_power_state(APM_STATE_OFF);
 }
@@ -1074,6 +1116,19 @@
 	}
 	if ((error == APM_SUCCESS) || (error == APM_NO_ERROR))
 		return 1;
+	if (error == APM_NOT_ENGAGED) {
+		static int tried;
+		int eng_error;
+		if (tried++ == 0) {
+			eng_error = apm_engage_power_management(APM_DEVICE_ALL, 1);
+			if (eng_error) {
+				apm_error("set display", error);
+				apm_error("engage interface", eng_error);
+				return 0;
+			} else
+				return apm_console_blank(blank);
+		}
+	}
 	apm_error("set display", error);
 	return 0;
 }
@@ -1397,7 +1452,7 @@
 	as = fp->private_data;
 	if (check_apm_user(as, "read"))
 		return -EIO;
-	if (count < sizeof(apm_event_t))
+	if ((int)count < sizeof(apm_event_t))
 		return -EINVAL;
 	if ((queue_empty(as)) && (fp->f_flags & O_NONBLOCK))
 		return -EAGAIN;
@@ -1571,7 +1626,7 @@
 
 	p = buf;
 
-	if ((smp_num_cpus == 1) &&
+	if ((smp_num_cpus == 1 || smp) &&
 	    !(error = apm_get_power_status(&bx, &cx, &dx))) {
 		ac_line_status = (bx >> 8) & 0xff;
 		battery_status = bx & 0xff;
@@ -1716,7 +1771,7 @@
 		}
 	}
 
-	if (debug) {
+	if (debug && (smp_num_cpus == 1 || smp )) {
 		error = apm_get_power_status(&bx, &cx, &dx);
 		if (error)
 			printk(KERN_INFO "apm: power status not available\n");
@@ -1760,7 +1815,7 @@
 		pm_power_off = apm_power_off;
 	register_sysrq_key('o', &sysrq_poweroff_op);
 
-	if (smp_num_cpus == 1) {
+	if (smp_num_cpus == 1 || smp) {
 #if defined(CONFIG_APM_DISPLAY_BLANK) && defined(CONFIG_VT)
 		console_blank_hook = apm_console_blank;
 #endif
@@ -1799,6 +1854,11 @@
 			str += 3;
 		if (strncmp(str, "debug", 5) == 0)
 			debug = !invert;
+		if (strncmp(str, "smp", 3) == 0)
+		{
+			smp = !invert;
+			idle_threshold = 100;
+		}
 		if ((strncmp(str, "power-off", 9) == 0) ||
 		    (strncmp(str, "power_off", 9) == 0))
 			power_off = !invert;
@@ -1903,7 +1963,7 @@
 		printk(KERN_NOTICE "apm: disabled on user request.\n");
 		return -ENODEV;
 	}
-	if ((smp_num_cpus > 1) && !power_off) {
+	if ((smp_num_cpus > 1) && !power_off && !smp) {
 		printk(KERN_NOTICE "apm: disabled - APM is not SMP safe.\n");
 		return -ENODEV;
 	}
@@ -1957,7 +2017,7 @@
 
 	kernel_thread(apm, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGCHLD);
 
-	if (smp_num_cpus > 1) {
+	if (smp_num_cpus > 1 && !smp) {
 		printk(KERN_NOTICE
 		   "apm: disabled - APM is not SMP safe (power off active).\n");
 		return 0;
@@ -2025,5 +2085,8 @@
 MODULE_PARM(idle_period, "i");
 MODULE_PARM_DESC(idle_period,
 	"Period (in sec/100) over which to caculate the idle percentage");
+MODULE_PARM(smp, "i");
+MODULE_PARM_DESC(smp,
+	"Set this to enable APM use on an SMP platform. Use with caution on older systems");
 
 EXPORT_NO_SYMBOLS;
diff -urN linux-2.4.21-pre2/arch/i386/kernel/scx200.c linux-2.4.21-pre3/arch/i386/kernel/scx200.c
--- linux-2.4.21-pre2/arch/i386/kernel/scx200.c	Mon Jan  6 16:32:27 2003
+++ linux-2.4.21-pre3/arch/i386/kernel/scx200.c	Wed Dec 31 16:00:00 1969
@@ -1,128 +0,0 @@
-/* linux/arch/i386/kernel/scx200.c 
-
-   Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
-
-   National Semiconductor SCx200 support. */
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/pci.h>
-
-#include <linux/scx200.h>
-#include <linux/scx200.h>
-
-#define NAME "scx200"
-
-MODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>");
-MODULE_DESCRIPTION("NatSemi SCx200 Driver");
-MODULE_LICENSE("GPL");
-
-unsigned scx200_gpio_base = 0;
-long scx200_gpio_shadow[2];
-
-spinlock_t scx200_gpio_lock = SPIN_LOCK_UNLOCKED;
-static spinlock_t scx200_gpio_config_lock = SPIN_LOCK_UNLOCKED;
-
-u32 scx200_gpio_configure(int index, u32 mask, u32 bits)
-{
-	u32 config, new_config;
-	unsigned long flags;
-
-	spin_lock_irqsave(&scx200_gpio_config_lock, flags);
-
-	outl(index, scx200_gpio_base + 0x20);
-	config = inl(scx200_gpio_base + 0x24);
-
-	new_config = (config & mask) | bits;
-	outl(new_config, scx200_gpio_base + 0x24);
-
-	spin_unlock_irqrestore(&scx200_gpio_config_lock, flags);
-
-	return config;
-}
-
-void scx200_gpio_dump(unsigned index)
-{
-	u32 config = scx200_gpio_configure(index, ~0, 0);
-	printk(KERN_DEBUG "GPIO%02u: 0x%08lx", index, (unsigned long)config);
-	
-	if (config & 1) 
-		printk(" OE"); /* output enabled */
-	else
-		printk(" TS"); /* tristate */
-	if (config & 2) 
-		printk(" PP"); /* push pull */
-	else
-		printk(" OD"); /* open drain */
-	if (config & 4) 
-		printk(" PUE"); /* pull up enabled */
-	else
-		printk(" PUD"); /* pull up disabled */
-	if (config & 8) 
-		printk(" LOCKED"); /* locked */
-	if (config & 16) 
-		printk(" LEVEL"); /* level input */
-	else
-		printk(" EDGE"); /* edge input */
-	if (config & 32) 
-		printk(" HI"); /* trigger on rising edge */
-	else
-		printk(" LO"); /* trigger on falling edge */
-	if (config & 64) 
-		printk(" DEBOUNCE"); /* debounce */
-	printk("\n");
-}
-
-int __init scx200_init(void)
-{
-	struct pci_dev *bridge;
-	int bank;
-	unsigned base;
-
-	printk(KERN_INFO NAME ": NatSemi SCx200 Driver\n");
-
-	if ((bridge = pci_find_device(PCI_VENDOR_ID_NS, 
-				      PCI_DEVICE_ID_NS_SCx200_BRIDGE,
-				      NULL)) == NULL)
-		return -ENODEV;
-
-	base = pci_resource_start(bridge, 0);
-	printk(KERN_INFO NAME ": GPIO base 0x%x\n", base);
-
-	if (request_region(base, SCx200_GPIO_SIZE, "NatSemi SCx200 GPIO") == 0) {
-		printk(KERN_ERR NAME ": can't allocate I/O for GPIOs\n");
-		return -EBUSY;
-	}
-
-	scx200_gpio_base = base;
-
-	/* read the current values driven on the GPIO signals */
-	for (bank = 0; bank < 2; ++bank)
-		scx200_gpio_shadow[bank] = inl(scx200_gpio_base + 0x10 * bank);
-
-	return 0;
-}
-
-void __exit scx200_cleanup(void)
-{
-	release_region(scx200_gpio_base, SCx200_GPIO_SIZE);
-}
-
-module_init(scx200_init);
-module_exit(scx200_cleanup);
-
-EXPORT_SYMBOL(scx200_gpio_base);
-EXPORT_SYMBOL(scx200_gpio_shadow);
-EXPORT_SYMBOL(scx200_gpio_lock);
-EXPORT_SYMBOL(scx200_gpio_configure);
-EXPORT_SYMBOL(scx200_gpio_dump);
-
-/*
-    Local variables:
-        compile-command: "make -k -C ../../.. SUBDIRS=arch/i386/kernel modules"
-        c-basic-offset: 8
-    End:
-*/
diff -urN linux-2.4.21-pre2/arch/i386/kernel/setup.c linux-2.4.21-pre3/arch/i386/kernel/setup.c
--- linux-2.4.21-pre2/arch/i386/kernel/setup.c	Mon Jan  6 16:32:27 2003
+++ linux-2.4.21-pre3/arch/i386/kernel/setup.c	Mon Jan  6 16:32:30 2003
@@ -1424,8 +1424,20 @@
                                                 &c->x86_capability);
 				}
 			}
-			break;
 
+			/* It's been determined by AMD that Athlons since model 8 stepping 1
+			 * are more robust with CLK_CTL set to 200xxxxx instead of 600xxxxx
+			 * As per AMD technical note 27212 0.2
+			 */
+			if ((c->x86_model == 8 && c->x86_mask>=1) || (c->x86_model > 8)) {
+				rdmsr(MSR_K7_CLK_CTL, l, h);
+				if ((l & 0xfff00000) != 0x20000000) {
+					printk ("CPU: CLK_CTL MSR was %x. Reprogramming to %x\n", l,
+						((l & 0x000fffff)|0x20000000));
+					wrmsr(MSR_K7_CLK_CTL, (l & 0x000fffff)|0x20000000, h);
+				}
+			}
+			break;
 	}
 
 	display_cacheinfo(c);
diff -urN linux-2.4.21-pre2/arch/ppc/boot/common/misc-simple.c linux-2.4.21-pre3/arch/ppc/boot/common/misc-simple.c
--- linux-2.4.21-pre2/arch/ppc/boot/common/misc-simple.c	Thu Nov 28 15:53:11 2002
+++ linux-2.4.21-pre3/arch/ppc/boot/common/misc-simple.c	Mon Jan  6 16:32:30 2003
@@ -74,7 +74,8 @@
 {
 	int timer = 0;
 	char *cp, ch;
-	struct bi_record *rec, *birecs;
+	struct bi_record *rec;
+	unsigned long rec_loc, initrd_loc;
 
 	serial_fixups();
 	com_port = serial_init(0, NULL);
@@ -189,10 +190,29 @@
 	/*
 	 * Create bi_recs for cmd_line and initrds
 	 */
-	rec = (struct bi_record *)_ALIGN((unsigned long)(zimage_size) +
+	rec_loc = _ALIGN((unsigned long)(zimage_size) +
 			(1 << 20) - 1, (1 << 20));
-	birecs = rec;
+	rec = (struct bi_record *)rec_loc;
 
+	/* We need to make sure that the initrd and bi_recs do not
+	 * overlap. */
+	if ( initrd_size ) {
+		initrd_loc = (unsigned long)(&__ramdisk_begin);
+		/* If the bi_recs are in the middle of the current
+		 * initrd, move the initrd to the next MB
+		 * boundary. */
+		if ((rec_loc > initrd_loc) &&
+				((initrd_loc + initrd_size) > rec_loc)) {
+			initrd_loc = _ALIGN((unsigned long)(zimage_size)
+					+ (2 << 20) - 1, (2 << 20));
+		 	memmove((void *)initrd_loc, &__ramdisk_begin,
+				 initrd_size);
+	         	puts("initrd moved:  "); puthex(initrd_loc);
+		 	puts(" "); puthex(initrd_loc + initrd_size);
+		 	puts("\n");
+		}
+	}
+ 
 	rec->tag = BI_FIRST;
 	rec->size = sizeof(struct bi_record);
 	rec = (struct bi_record *)((unsigned long)rec + rec->size);
@@ -204,7 +224,7 @@
 
 	if ( initrd_size ) {
 		rec->tag = BI_INITRD;
-		rec->data[0] = (unsigned long)(&__ramdisk_begin);
+		rec->data[0] = initrd_loc;
 		rec->data[1] = initrd_size;
 		rec->size = sizeof(struct bi_record) + 2 *
 			sizeof(unsigned long);
@@ -218,5 +238,5 @@
 	puts("Now booting the kernel\n");
 	serial_close(com_port);
 
-	return birecs;
+	return (struct bi_record *)rec_loc;
 }
diff -urN linux-2.4.21-pre2/arch/ppc/boot/prep/misc.c linux-2.4.21-pre3/arch/ppc/boot/prep/misc.c
--- linux-2.4.21-pre2/arch/ppc/boot/prep/misc.c	Fri Aug  2 17:39:43 2002
+++ linux-2.4.21-pre3/arch/ppc/boot/prep/misc.c	Mon Jan  6 16:32:30 2003
@@ -343,9 +343,31 @@
 
 	{
 		struct bi_record *rec;
-		
-		rec = (struct bi_record *)_ALIGN((unsigned long)(zimage_size) +
+		unsigned long initrd_loc;
+		unsigned long rec_loc = _ALIGN((unsigned long)(zimage_size) +
 				(1 << 20) - 1, (1 << 20));
+		
+		rec = (struct bi_record *)rec_loc;
+
+		/* We need to make sure that the initrd and bi_recs do not
+		 * overlap. */
+		if ( initrd_size ) {
+			initrd_loc = (unsigned long)(&__ramdisk_begin);
+			/* If the bi_recs are in the middle of the current
+			 * initrd, move the initrd to the next MB
+			 * boundary. */
+			if ((rec_loc > initrd_loc) &&
+					((initrd_loc + initrd_size)
+					 > rec_loc)) {
+				initrd_loc = _ALIGN((unsigned long)(zimage_size)
+						+ (2 << 20) - 1, (2 << 20));
+			 	memmove((void *)initrd_loc, &__ramdisk_begin,
+					 initrd_size);
+		         	puts("initrd moved:  "); puthex(initrd_loc);
+			 	puts(" "); puthex(initrd_loc + initrd_size);
+			 	puts("\n");
+			}
+		}
 
 		rec->tag = BI_FIRST;
 		rec->size = sizeof(struct bi_record);
@@ -370,7 +392,7 @@
 
 		if ( initrd_size ) {
 			rec->tag = BI_INITRD;
-			rec->data[0] = (unsigned long)(&__ramdisk_begin);
+			rec->data[0] = initrd_loc;
 			rec->data[1] = initrd_size;
 			rec->size = sizeof(struct bi_record) + 2 *
 				sizeof(unsigned long);
diff -urN linux-2.4.21-pre2/arch/ppc/boot/simple/misc-embedded.c linux-2.4.21-pre3/arch/ppc/boot/simple/misc-embedded.c
--- linux-2.4.21-pre2/arch/ppc/boot/simple/misc-embedded.c	Thu Nov 28 15:53:11 2002
+++ linux-2.4.21-pre3/arch/ppc/boot/simple/misc-embedded.c	Mon Jan  6 16:32:30 2003
@@ -209,9 +209,30 @@
 	puts("done.\n");
 	{
 		struct bi_record *rec;
+		unsigned long initrd_loc;
+		unsigned long rec_loc = _ALIGN((unsigned long)(zimage_size) +
+				(1 << 20) - 1, (1 << 20));
+		rec = (struct bi_record *)rec_loc;
 
-		rec = (struct bi_record *)_ALIGN((unsigned long)zimage_size +
-				(1 << 20) - 1,(1 << 20));
+		/* We need to make sure that the initrd and bi_recs do not
+		 * overlap. */
+		if ( initrd_size ) {
+			initrd_loc = (unsigned long)(&__ramdisk_begin);
+			/* If the bi_recs are in the middle of the current
+			 * initrd, move the initrd to the next MB
+			 * boundary. */
+			if ((rec_loc > initrd_loc) &&
+					((initrd_loc + initrd_size)
+					 > rec_loc)) {
+				initrd_loc = _ALIGN((unsigned long)(zimage_size)
+						+ (2 << 20) - 1, (2 << 20));
+			 	memmove((void *)initrd_loc, &__ramdisk_begin,
+					 initrd_size);
+		         	puts("initrd moved:  "); puthex(initrd_loc);
+			 	puts(" "); puthex(initrd_loc + initrd_size);
+			 	puts("\n");
+			}
+		}
 
 		rec->tag = BI_FIRST;
 		rec->size = sizeof(struct bi_record);
@@ -224,7 +245,7 @@
 
 		if ( initrd_size ) {
 			rec->tag = BI_INITRD;
-			rec->data[0] = (unsigned long)(&__ramdisk_begin);
+			rec->data[0] = initrd_loc;
 			rec->data[1] = initrd_size;
 			rec->size = sizeof(struct bi_record) + 2 *
 				sizeof(unsigned long);
diff -urN linux-2.4.21-pre2/arch/ppc/boot/simple/misc-spruce.c linux-2.4.21-pre3/arch/ppc/boot/simple/misc-spruce.c
--- linux-2.4.21-pre2/arch/ppc/boot/simple/misc-spruce.c	Thu Nov 28 15:53:11 2002
+++ linux-2.4.21-pre3/arch/ppc/boot/simple/misc-spruce.c	Mon Jan  6 16:32:30 2003
@@ -396,9 +396,30 @@
 
 	{
 		struct bi_record *rec;
-
-		rec = (struct bi_record *)_ALIGN((ulong)zimage_size +
-							(1<<20)-1,(1<<20));
+		unsigned long initrd_loc;
+		unsigned long rec_loc = _ALIGN((unsigned long)(zimage_size) +
+				(1 << 20) - 1, (1 << 20));
+		rec = (struct bi_record *)rec_loc;
+  
+		/* We need to make sure that the initrd and bi_recs do not
+		 * overlap. */
+		if ( initrd_size ) {
+			initrd_loc = (unsigned long)(&__ramdisk_begin);
+			/* If the bi_recs are in the middle of the current
+			 * initrd, move the initrd to the next MB
+			 * boundary. */
+			if ((rec_loc > initrd_loc) &&
+					((initrd_loc + initrd_size)
+					 > rec_loc)) {
+				initrd_loc = _ALIGN((unsigned long)(zimage_size)
+						+ (2 << 20) - 1, (2 << 20));
+			 	memmove((void *)initrd_loc, &__ramdisk_begin,
+					 initrd_size);
+		         	puts("initrd moved:  "); puthex(initrd_loc);
+			 	puts(" "); puthex(initrd_loc + initrd_size);
+			 	puts("\n");
+			}
+		}
 
 		rec->tag = BI_FIRST;
         	rec->size = sizeof(struct bi_record);
@@ -421,7 +442,7 @@
 
 		if ( initrd_size ) {
 			rec->tag = BI_INITRD;
-			rec->data[0] = (unsigned long)(&__ramdisk_begin);
+			rec->data[0] = initrd_loc;
 			rec->data[1] = initrd_size;
 			rec->size = sizeof(struct bi_record) + 2 *
 				sizeof(unsigned long);
diff -urN linux-2.4.21-pre2/arch/ppc/configs/IVMS8_defconfig linux-2.4.21-pre3/arch/ppc/configs/IVMS8_defconfig
--- linux-2.4.21-pre2/arch/ppc/configs/IVMS8_defconfig	Thu Nov 28 15:53:11 2002
+++ linux-2.4.21-pre3/arch/ppc/configs/IVMS8_defconfig	Mon Jan  6 16:32:30 2003
@@ -194,21 +194,13 @@
 CONFIG_BLK_DEV_IDEDISK=y
 CONFIG_IDEDISK_MULTI_MODE=y
 # CONFIG_IDEDISK_STROKE is not set
-# CONFIG_BLK_DEV_IDEDISK_VENDOR is not set
-# CONFIG_BLK_DEV_IDEDISK_FUJITSU is not set
-# CONFIG_BLK_DEV_IDEDISK_IBM is not set
-# CONFIG_BLK_DEV_IDEDISK_MAXTOR is not set
-# CONFIG_BLK_DEV_IDEDISK_QUANTUM is not set
-# CONFIG_BLK_DEV_IDEDISK_SEAGATE is not set
-# CONFIG_BLK_DEV_IDEDISK_WD is not set
-# CONFIG_BLK_DEV_COMMERIAL is not set
-# CONFIG_BLK_DEV_TIVO is not set
 # CONFIG_BLK_DEV_IDECS is not set
 CONFIG_BLK_DEV_IDECD=y
 # CONFIG_BLK_DEV_IDETAPE is not set
 # CONFIG_BLK_DEV_IDEFLOPPY is not set
 # CONFIG_BLK_DEV_IDESCSI is not set
 # CONFIG_IDE_TASK_IOCTL is not set
+# CONFIG_IDE_TASKFILE_IO is not set
 
 #
 # IDE chipset support/bugfixes
@@ -277,6 +269,7 @@
 # CONFIG_NS83820 is not set
 # CONFIG_HAMACHI is not set
 # CONFIG_YELLOWFIN is not set
+# CONFIG_R8169 is not set
 # CONFIG_SK98LIN is not set
 # CONFIG_TIGON3 is not set
 # CONFIG_FDDI is not set
@@ -384,6 +377,8 @@
 # Watchdog Cards
 #
 # CONFIG_WATCHDOG is not set
+# CONFIG_SCx200_GPIO is not set
+# CONFIG_AMD_PM768 is not set
 # CONFIG_NVRAM is not set
 # CONFIG_RTC is not set
 # CONFIG_DTLK is not set
diff -urN linux-2.4.21-pre2/arch/ppc/configs/SM850_defconfig linux-2.4.21-pre3/arch/ppc/configs/SM850_defconfig
--- linux-2.4.21-pre2/arch/ppc/configs/SM850_defconfig	Thu Nov 28 15:53:11 2002
+++ linux-2.4.21-pre3/arch/ppc/configs/SM850_defconfig	Mon Jan  6 16:32:30 2003
@@ -233,6 +233,7 @@
 # CONFIG_NS83820 is not set
 # CONFIG_HAMACHI is not set
 # CONFIG_YELLOWFIN is not set
+# CONFIG_R8169 is not set
 # CONFIG_SK98LIN is not set
 # CONFIG_TIGON3 is not set
 # CONFIG_FDDI is not set
@@ -340,6 +341,8 @@
 # Watchdog Cards
 #
 # CONFIG_WATCHDOG is not set
+# CONFIG_SCx200_GPIO is not set
+# CONFIG_AMD_PM768 is not set
 # CONFIG_NVRAM is not set
 # CONFIG_RTC is not set
 # CONFIG_DTLK is not set
diff -urN linux-2.4.21-pre2/arch/ppc/configs/SPD823TS_defconfig linux-2.4.21-pre3/arch/ppc/configs/SPD823TS_defconfig
--- linux-2.4.21-pre2/arch/ppc/configs/SPD823TS_defconfig	Thu Nov 28 15:53:11 2002
+++ linux-2.4.21-pre3/arch/ppc/configs/SPD823TS_defconfig	Mon Jan  6 16:32:30 2003
@@ -232,6 +232,7 @@
 # CONFIG_NS83820 is not set
 # CONFIG_HAMACHI is not set
 # CONFIG_YELLOWFIN is not set
+# CONFIG_R8169 is not set
 # CONFIG_SK98LIN is not set
 # CONFIG_TIGON3 is not set
 # CONFIG_FDDI is not set
@@ -339,6 +340,8 @@
 # Watchdog Cards
 #
 # CONFIG_WATCHDOG is not set
+# CONFIG_SCx200_GPIO is not set
+# CONFIG_AMD_PM768 is not set
 # CONFIG_NVRAM is not set
 # CONFIG_RTC is not set
 # CONFIG_DTLK is not set
diff -urN linux-2.4.21-pre2/arch/ppc/configs/TQM823L_defconfig linux-2.4.21-pre3/arch/ppc/configs/TQM823L_defconfig
--- linux-2.4.21-pre2/arch/ppc/configs/TQM823L_defconfig	Thu Nov 28 15:53:11 2002
+++ linux-2.4.21-pre3/arch/ppc/configs/TQM823L_defconfig	Mon Jan  6 16:32:30 2003
@@ -233,6 +233,7 @@
 # CONFIG_NS83820 is not set
 # CONFIG_HAMACHI is not set
 # CONFIG_YELLOWFIN is not set
+# CONFIG_R8169 is not set
 # CONFIG_SK98LIN is not set
 # CONFIG_TIGON3 is not set
 # CONFIG_FDDI is not set
@@ -340,6 +341,8 @@
 # Watchdog Cards
 #
 # CONFIG_WATCHDOG is not set
+# CONFIG_SCx200_GPIO is not set
+# CONFIG_AMD_PM768 is not set
 # CONFIG_NVRAM is not set
 # CONFIG_RTC is not set
 # CONFIG_DTLK is not set
diff -urN linux-2.4.21-pre2/arch/ppc/configs/TQM850L_defconfig linux-2.4.21-pre3/arch/ppc/configs/TQM850L_defconfig
--- linux-2.4.21-pre2/arch/ppc/configs/TQM850L_defconfig	Thu Nov 28 15:53:11 2002
+++ linux-2.4.21-pre3/arch/ppc/configs/TQM850L_defconfig	Mon Jan  6 16:32:30 2003
@@ -233,6 +233,7 @@
 # CONFIG_NS83820 is not set
 # CONFIG_HAMACHI is not set
 # CONFIG_YELLOWFIN is not set
+# CONFIG_R8169 is not set
 # CONFIG_SK98LIN is not set
 # CONFIG_TIGON3 is not set
 # CONFIG_FDDI is not set
@@ -340,6 +341,8 @@
 # Watchdog Cards
 #
 # CONFIG_WATCHDOG is not set
+# CONFIG_SCx200_GPIO is not set
+# CONFIG_AMD_PM768 is not set
 # CONFIG_NVRAM is not set
 # CONFIG_RTC is not set
 # CONFIG_DTLK is not set
diff -urN linux-2.4.21-pre2/arch/ppc/configs/TQM860L_defconfig linux-2.4.21-pre3/arch/ppc/configs/TQM860L_defconfig
--- linux-2.4.21-pre2/arch/ppc/configs/TQM860L_defconfig	Thu Nov 28 15:53:11 2002
+++ linux-2.4.21-pre3/arch/ppc/configs/TQM860L_defconfig	Mon Jan  6 16:32:30 2003
@@ -195,21 +195,13 @@
 CONFIG_BLK_DEV_IDEDISK=y
 # CONFIG_IDEDISK_MULTI_MODE is not set
 # CONFIG_IDEDISK_STROKE is not set
-# CONFIG_BLK_DEV_IDEDISK_VENDOR is not set
-# CONFIG_BLK_DEV_IDEDISK_FUJITSU is not set
-# CONFIG_BLK_DEV_IDEDISK_IBM is not set
-# CONFIG_BLK_DEV_IDEDISK_MAXTOR is not set
-# CONFIG_BLK_DEV_IDEDISK_QUANTUM is not set
-# CONFIG_BLK_DEV_IDEDISK_SEAGATE is not set
-# CONFIG_BLK_DEV_IDEDISK_WD is not set
-# CONFIG_BLK_DEV_COMMERIAL is not set
-# CONFIG_BLK_DEV_TIVO is not set
 # CONFIG_BLK_DEV_IDECS is not set
 # CONFIG_BLK_DEV_IDECD is not set
 # CONFIG_BLK_DEV_IDETAPE is not set
 # CONFIG_BLK_DEV_IDEFLOPPY is not set
 # CONFIG_BLK_DEV_IDESCSI is not set
 # CONFIG_IDE_TASK_IOCTL is not set
+# CONFIG_IDE_TASKFILE_IO is not set
 
 #
 # IDE chipset support/bugfixes
@@ -278,6 +270,7 @@
 # CONFIG_NS83820 is not set
 # CONFIG_HAMACHI is not set
 # CONFIG_YELLOWFIN is not set
+# CONFIG_R8169 is not set
 # CONFIG_SK98LIN is not set
 # CONFIG_TIGON3 is not set
 # CONFIG_FDDI is not set
@@ -385,6 +378,8 @@
 # Watchdog Cards
 #
 # CONFIG_WATCHDOG is not set
+# CONFIG_SCx200_GPIO is not set
+# CONFIG_AMD_PM768 is not set
 # CONFIG_NVRAM is not set
 # CONFIG_RTC is not set
 # CONFIG_DTLK is not set
diff -urN linux-2.4.21-pre2/arch/ppc/configs/apus_defconfig linux-2.4.21-pre3/arch/ppc/configs/apus_defconfig
--- linux-2.4.21-pre2/arch/ppc/configs/apus_defconfig	Thu Nov 28 15:53:11 2002
+++ linux-2.4.21-pre3/arch/ppc/configs/apus_defconfig	Mon Jan  6 16:32:30 2003
@@ -265,21 +265,13 @@
 CONFIG_BLK_DEV_IDEDISK=y
 # CONFIG_IDEDISK_MULTI_MODE is not set
 # CONFIG_IDEDISK_STROKE is not set
-# CONFIG_BLK_DEV_IDEDISK_VENDOR is not set
-# CONFIG_BLK_DEV_IDEDISK_FUJITSU is not set
-# CONFIG_BLK_DEV_IDEDISK_IBM is not set
-# CONFIG_BLK_DEV_IDEDISK_MAXTOR is not set
-# CONFIG_BLK_DEV_IDEDISK_QUANTUM is not set
-# CONFIG_BLK_DEV_IDEDISK_SEAGATE is not set
-# CONFIG_BLK_DEV_IDEDISK_WD is not set
-# CONFIG_BLK_DEV_COMMERIAL is not set
-# CONFIG_BLK_DEV_TIVO is not set
 # CONFIG_BLK_DEV_IDECS is not set
 CONFIG_BLK_DEV_IDECD=y
 CONFIG_BLK_DEV_IDETAPE=m
 CONFIG_BLK_DEV_IDEFLOPPY=y
 CONFIG_BLK_DEV_IDESCSI=m
 # CONFIG_IDE_TASK_IOCTL is not set
+# CONFIG_IDE_TASKFILE_IO is not set
 
 #
 # IDE chipset support/bugfixes
@@ -287,7 +279,6 @@
 # CONFIG_BLK_DEV_CMD640 is not set
 # CONFIG_BLK_DEV_CMD640_ENHANCED is not set
 # CONFIG_BLK_DEV_ISAPNP is not set
-# CONFIG_BLK_DEV_RZ1000 is not set
 # CONFIG_BLK_DEV_IDEPCI is not set
 CONFIG_BLK_DEV_GAYLE=y
 CONFIG_BLK_DEV_IDEDOUBLER=y
@@ -295,7 +286,7 @@
 # CONFIG_IDE_CHIPSETS is not set
 # CONFIG_IDEDMA_AUTO is not set
 # CONFIG_DMA_NONPCI is not set
-# CONFIG_BLK_DEV_IDE_MODES is not set
+CONFIG_BLK_DEV_IDE_MODES=y
 # CONFIG_BLK_DEV_ATARAID is not set
 # CONFIG_BLK_DEV_ATARAID_PDC is not set
 # CONFIG_BLK_DEV_ATARAID_HPT is not set
@@ -374,6 +365,7 @@
 # CONFIG_SCSI_DC390T is not set
 # CONFIG_SCSI_T128 is not set
 # CONFIG_SCSI_U14_34F is not set
+# CONFIG_SCSI_NSP32 is not set
 # CONFIG_SCSI_DEBUG is not set
 CONFIG_A3000_SCSI=y
 CONFIG_A4000T_SCSI=y
@@ -444,6 +436,7 @@
 # CONFIG_NS83820 is not set
 # CONFIG_HAMACHI is not set
 # CONFIG_YELLOWFIN is not set
+# CONFIG_R8169 is not set
 # CONFIG_SK98LIN is not set
 # CONFIG_TIGON3 is not set
 # CONFIG_FDDI is not set
@@ -600,6 +593,7 @@
 CONFIG_PRINTER=m
 # CONFIG_LP_CONSOLE is not set
 # CONFIG_PPDEV is not set
+# CONFIG_TIPAR is not set
 
 #
 # I2C support
@@ -660,6 +654,8 @@
 # Watchdog Cards
 #
 # CONFIG_WATCHDOG is not set
+# CONFIG_SCx200_GPIO is not set
+# CONFIG_AMD_PM768 is not set
 # CONFIG_NVRAM is not set
 # CONFIG_RTC is not set
 # CONFIG_DTLK is not set
@@ -855,6 +851,7 @@
 CONFIG_SOUND_TRACEINIT=y
 CONFIG_SOUND_DMAP=y
 # CONFIG_SOUND_AD1816 is not set
+# CONFIG_SOUND_AD1889 is not set
 # CONFIG_SOUND_SGALAXY is not set
 # CONFIG_SOUND_ADLIB is not set
 # CONFIG_SOUND_ACI_MIXER is not set
diff -urN linux-2.4.21-pre2/arch/ppc/configs/briq_defconfig linux-2.4.21-pre3/arch/ppc/configs/briq_defconfig
--- linux-2.4.21-pre2/arch/ppc/configs/briq_defconfig	Thu Nov 28 15:53:11 2002
+++ linux-2.4.21-pre3/arch/ppc/configs/briq_defconfig	Mon Jan  6 16:32:30 2003
@@ -236,21 +236,13 @@
 CONFIG_BLK_DEV_IDEDISK=y
 # CONFIG_IDEDISK_MULTI_MODE is not set
 # CONFIG_IDEDISK_STROKE is not set
-# CONFIG_BLK_DEV_IDEDISK_VENDOR is not set
-# CONFIG_BLK_DEV_IDEDISK_FUJITSU is not set
-# CONFIG_BLK_DEV_IDEDISK_IBM is not set
-# CONFIG_BLK_DEV_IDEDISK_MAXTOR is not set
-# CONFIG_BLK_DEV_IDEDISK_QUANTUM is not set
-# CONFIG_BLK_DEV_IDEDISK_SEAGATE is not set
-# CONFIG_BLK_DEV_IDEDISK_WD is not set
-# CONFIG_BLK_DEV_COMMERIAL is not set
-# CONFIG_BLK_DEV_TIVO is not set
 # CONFIG_BLK_DEV_IDECS is not set
 CONFIG_BLK_DEV_IDECD=y
 # CONFIG_BLK_DEV_IDETAPE is not set
 CONFIG_BLK_DEV_IDEFLOPPY=y
 CONFIG_BLK_DEV_IDESCSI=y
 # CONFIG_IDE_TASK_IOCTL is not set
+# CONFIG_IDE_TASKFILE_IO is not set
 
 #
 # IDE chipset support/bugfixes
@@ -258,8 +250,8 @@
 # CONFIG_BLK_DEV_CMD640 is not set
 # CONFIG_BLK_DEV_CMD640_ENHANCED is not set
 # CONFIG_BLK_DEV_ISAPNP is not set
-# CONFIG_BLK_DEV_RZ1000 is not set
 CONFIG_BLK_DEV_IDEPCI=y
+CONFIG_BLK_DEV_GENERIC=y
 CONFIG_IDEPCI_SHARE_IRQ=y
 CONFIG_BLK_DEV_IDEDMA_PCI=y
 # CONFIG_BLK_DEV_OFFBOARD is not set
@@ -268,28 +260,30 @@
 # CONFIG_IDEDMA_ONLYDISK is not set
 CONFIG_BLK_DEV_IDEDMA=y
 # CONFIG_IDEDMA_PCI_WIP is not set
-# CONFIG_BLK_DEV_IDEDMA_TIMEOUT is not set
-# CONFIG_IDEDMA_NEW_DRIVE_LISTINGS is not set
 CONFIG_BLK_DEV_ADMA=y
 # CONFIG_BLK_DEV_AEC62XX is not set
-# CONFIG_AEC62XX_TUNING is not set
 # CONFIG_BLK_DEV_ALI15X3 is not set
 # CONFIG_WDC_ALI15X3 is not set
 # CONFIG_BLK_DEV_AMD74XX is not set
 # CONFIG_AMD74XX_OVERRIDE is not set
 # CONFIG_BLK_DEV_CMD64X is not set
-# CONFIG_BLK_DEV_CMD680 is not set
 # CONFIG_BLK_DEV_CY82C693 is not set
 # CONFIG_BLK_DEV_CS5530 is not set
 # CONFIG_BLK_DEV_HPT34X is not set
 # CONFIG_HPT34X_AUTODMA is not set
 # CONFIG_BLK_DEV_HPT366 is not set
+# CONFIG_BLK_DEV_PIIX is not set
+# CONFIG_BLK_DEV_NFORCE is not set
 # CONFIG_BLK_DEV_NS87415 is not set
 # CONFIG_BLK_DEV_OPTI621 is not set
-# CONFIG_BLK_DEV_PDC202XX is not set
+# CONFIG_BLK_DEV_PDC202XX_OLD is not set
 # CONFIG_PDC202XX_BURST is not set
+# CONFIG_BLK_DEV_PDC202XX_NEW is not set
 # CONFIG_PDC202XX_FORCE is not set
+# CONFIG_BLK_DEV_RZ1000 is not set
+# CONFIG_BLK_DEV_SC1200 is not set
 # CONFIG_BLK_DEV_SVWKS is not set
+# CONFIG_BLK_DEV_SIIMAGE is not set
 # CONFIG_BLK_DEV_SIS5513 is not set
 # CONFIG_BLK_DEV_SLC90E66 is not set
 # CONFIG_BLK_DEV_TRM290 is not set
@@ -379,6 +373,7 @@
 # CONFIG_SCSI_DC390T is not set
 # CONFIG_SCSI_T128 is not set
 # CONFIG_SCSI_U14_34F is not set
+# CONFIG_SCSI_NSP32 is not set
 # CONFIG_SCSI_DEBUG is not set
 # CONFIG_SCSI_MESH is not set
 # CONFIG_SCSI_MAC53C94 is not set
@@ -465,6 +460,7 @@
 # CONFIG_NS83820 is not set
 # CONFIG_HAMACHI is not set
 # CONFIG_YELLOWFIN is not set
+# CONFIG_R8169 is not set
 # CONFIG_SK98LIN is not set
 # CONFIG_TIGON3 is not set
 # CONFIG_FDDI is not set
@@ -556,7 +552,6 @@
 # CONFIG_SERIAL_NONSTANDARD is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
-CONFIG_BRIQ_PANEL=y
 
 #
 # I2C support
@@ -599,6 +594,8 @@
 # Watchdog Cards
 #
 # CONFIG_WATCHDOG is not set
+# CONFIG_SCx200_GPIO is not set
+# CONFIG_AMD_PM768 is not set
 CONFIG_NVRAM=y
 # CONFIG_RTC is not set
 # CONFIG_DTLK is not set
diff -urN linux-2.4.21-pre2/arch/ppc/configs/bseip_defconfig linux-2.4.21-pre3/arch/ppc/configs/bseip_defconfig
--- linux-2.4.21-pre2/arch/ppc/configs/bseip_defconfig	Thu Nov 28 15:53:11 2002
+++ linux-2.4.21-pre3/arch/ppc/configs/bseip_defconfig	Mon Jan  6 16:32:30 2003
@@ -232,6 +232,7 @@
 # CONFIG_NS83820 is not set
 # CONFIG_HAMACHI is not set
 # CONFIG_YELLOWFIN is not set
+# CONFIG_R8169 is not set
 # CONFIG_SK98LIN is not set
 # CONFIG_TIGON3 is not set
 # CONFIG_FDDI is not set
@@ -339,6 +340,8 @@
 # Watchdog Cards
 #
 # CONFIG_WATCHDOG is not set
+# CONFIG_SCx200_GPIO is not set
+# CONFIG_AMD_PM768 is not set
 # CONFIG_NVRAM is not set
 # CONFIG_RTC is not set
 # CONFIG_DTLK is not set
diff -urN linux-2.4.21-pre2/arch/ppc/configs/common_defconfig linux-2.4.21-pre3/arch/ppc/configs/common_defconfig
--- linux-2.4.21-pre2/arch/ppc/configs/common_defconfig	Thu Nov 28 15:53:11 2002
+++ linux-2.4.21-pre3/arch/ppc/configs/common_defconfig	Mon Jan  6 16:32:30 2003
@@ -242,21 +242,13 @@
 CONFIG_BLK_DEV_IDEDISK=y
 # CONFIG_IDEDISK_MULTI_MODE is not set
 # CONFIG_IDEDISK_STROKE is not set
-# CONFIG_BLK_DEV_IDEDISK_VENDOR is not set
-# CONFIG_BLK_DEV_IDEDISK_FUJITSU is not set
-# CONFIG_BLK_DEV_IDEDISK_IBM is not set
-# CONFIG_BLK_DEV_IDEDISK_MAXTOR is not set
-# CONFIG_BLK_DEV_IDEDISK_QUANTUM is not set
-# CONFIG_BLK_DEV_IDEDISK_SEAGATE is not set
-# CONFIG_BLK_DEV_IDEDISK_WD is not set
-# CONFIG_BLK_DEV_COMMERIAL is not set
-# CONFIG_BLK_DEV_TIVO is not set
 # CONFIG_BLK_DEV_IDECS is not set
 CONFIG_BLK_DEV_IDECD=y
 # CONFIG_BLK_DEV_IDETAPE is not set
 CONFIG_BLK_DEV_IDEFLOPPY=y
 CONFIG_BLK_DEV_IDESCSI=y
 # CONFIG_IDE_TASK_IOCTL is not set
+CONFIG_IDE_TASKFILE_IO=y
 
 #
 # IDE chipset support/bugfixes
@@ -264,8 +256,8 @@
 # CONFIG_BLK_DEV_CMD640 is not set
 # CONFIG_BLK_DEV_CMD640_ENHANCED is not set
 # CONFIG_BLK_DEV_ISAPNP is not set
-# CONFIG_BLK_DEV_RZ1000 is not set
 CONFIG_BLK_DEV_IDEPCI=y
+CONFIG_BLK_DEV_GENERIC=y
 CONFIG_IDEPCI_SHARE_IRQ=y
 CONFIG_BLK_DEV_IDEDMA_PCI=y
 # CONFIG_BLK_DEV_OFFBOARD is not set
@@ -274,28 +266,30 @@
 # CONFIG_IDEDMA_ONLYDISK is not set
 CONFIG_BLK_DEV_IDEDMA=y
 # CONFIG_IDEDMA_PCI_WIP is not set
-# CONFIG_BLK_DEV_IDEDMA_TIMEOUT is not set
-# CONFIG_IDEDMA_NEW_DRIVE_LISTINGS is not set
 CONFIG_BLK_DEV_ADMA=y
 # CONFIG_BLK_DEV_AEC62XX is not set
-# CONFIG_AEC62XX_TUNING is not set
 # CONFIG_BLK_DEV_ALI15X3 is not set
 # CONFIG_WDC_ALI15X3 is not set
 # CONFIG_BLK_DEV_AMD74XX is not set
 # CONFIG_AMD74XX_OVERRIDE is not set
 CONFIG_BLK_DEV_CMD64X=y
-# CONFIG_BLK_DEV_CMD680 is not set
 # CONFIG_BLK_DEV_CY82C693 is not set
 # CONFIG_BLK_DEV_CS5530 is not set
 # CONFIG_BLK_DEV_HPT34X is not set
 # CONFIG_HPT34X_AUTODMA is not set
 # CONFIG_BLK_DEV_HPT366 is not set
+# CONFIG_BLK_DEV_PIIX is not set
+# CONFIG_BLK_DEV_NFORCE is not set
 # CONFIG_BLK_DEV_NS87415 is not set
 # CONFIG_BLK_DEV_OPTI621 is not set
-# CONFIG_BLK_DEV_PDC202XX is not set
+# CONFIG_BLK_DEV_PDC202XX_OLD is not set
 # CONFIG_PDC202XX_BURST is not set
+# CONFIG_BLK_DEV_PDC202XX_NEW is not set
 # CONFIG_PDC202XX_FORCE is not set
+# CONFIG_BLK_DEV_RZ1000 is not set
+# CONFIG_BLK_DEV_SC1200 is not set
 # CONFIG_BLK_DEV_SVWKS is not set
+# CONFIG_BLK_DEV_SIIMAGE is not set
 # CONFIG_BLK_DEV_SIS5513 is not set
 # CONFIG_BLK_DEV_SLC90E66 is not set
 # CONFIG_BLK_DEV_TRM290 is not set
@@ -396,6 +390,7 @@
 # CONFIG_SCSI_DC390T is not set
 # CONFIG_SCSI_T128 is not set
 # CONFIG_SCSI_U14_34F is not set
+# CONFIG_SCSI_NSP32 is not set
 # CONFIG_SCSI_DEBUG is not set
 CONFIG_SCSI_MESH=y
 CONFIG_SCSI_MESH_SYNC_RATE=5
@@ -487,6 +482,7 @@
 # CONFIG_NS83820 is not set
 # CONFIG_HAMACHI is not set
 # CONFIG_YELLOWFIN is not set
+# CONFIG_R8169 is not set
 # CONFIG_SK98LIN is not set
 # CONFIG_TIGON3 is not set
 # CONFIG_FDDI is not set
@@ -647,7 +643,6 @@
 # CONFIG_SERIAL_NONSTANDARD is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
-# CONFIG_BRIQ_PANEL is not set
 
 #
 # I2C support
@@ -712,6 +707,8 @@
 # Watchdog Cards
 #
 # CONFIG_WATCHDOG is not set
+# CONFIG_SCx200_GPIO is not set
+# CONFIG_AMD_PM768 is not set
 CONFIG_NVRAM=y
 # CONFIG_RTC is not set
 # CONFIG_DTLK is not set
@@ -880,6 +877,8 @@
 CONFIG_SOUND=m
 CONFIG_DMASOUND_PMAC=m
 CONFIG_DMASOUND=m
+CONFIG_I2C=m
+CONFIG_I2C_KEYWEST=m
 # CONFIG_SOUND_ALI5455 is not set
 # CONFIG_SOUND_BT878 is not set
 # CONFIG_SOUND_CMPCI is not set
@@ -915,7 +914,6 @@
 #
 CONFIG_USB_DEVICEFS=y
 # CONFIG_USB_BANDWIDTH is not set
-# CONFIG_USB_LONG_TIMEOUT is not set
 
 #
 # USB Host Controller Drivers
@@ -953,6 +951,7 @@
 # CONFIG_USB_HIDDEV is not set
 # CONFIG_USB_AIPTEK is not set
 # CONFIG_USB_WACOM is not set
+# CONFIG_USB_POWERMATE is not set
 
 #
 # USB Imaging devices
@@ -1006,6 +1005,7 @@
 # CONFIG_USB_SERIAL_KEYSPAN is not set
 # CONFIG_USB_SERIAL_MCT_U232 is not set
 # CONFIG_USB_SERIAL_KLSI is not set
+# CONFIG_USB_SERIAL_KOBIL_SCT is not set
 # CONFIG_USB_SERIAL_PL2303 is not set
 # CONFIG_USB_SERIAL_CYBERJACK is not set
 # CONFIG_USB_SERIAL_XIRCOM is not set
diff -urN linux-2.4.21-pre2/arch/ppc/configs/est8260_defconfig linux-2.4.21-pre3/arch/ppc/configs/est8260_defconfig
--- linux-2.4.21-pre2/arch/ppc/configs/est8260_defconfig	Thu Nov 28 15:53:11 2002
+++ linux-2.4.21-pre3/arch/ppc/configs/est8260_defconfig	Mon Jan  6 16:32:30 2003
@@ -215,6 +215,7 @@
 # CONFIG_NS83820 is not set
 # CONFIG_HAMACHI is not set
 # CONFIG_YELLOWFIN is not set
+# CONFIG_R8169 is not set
 # CONFIG_SK98LIN is not set
 # CONFIG_TIGON3 is not set
 # CONFIG_FDDI is not set
@@ -323,6 +324,8 @@
 # Watchdog Cards
 #
 # CONFIG_WATCHDOG is not set
+# CONFIG_SCx200_GPIO is not set
+# CONFIG_AMD_PM768 is not set
 # CONFIG_NVRAM is not set
 # CONFIG_RTC is not set
 # CONFIG_DTLK is not set
diff -urN linux-2.4.21-pre2/arch/ppc/configs/gemini_defconfig linux-2.4.21-pre3/arch/ppc/configs/gemini_defconfig
--- linux-2.4.21-pre2/arch/ppc/configs/gemini_defconfig	Thu Nov 28 15:53:11 2002
+++ linux-2.4.21-pre3/arch/ppc/configs/gemini_defconfig	Mon Jan  6 16:32:30 2003
@@ -257,6 +257,7 @@
 # CONFIG_SCSI_DC390T is not set
 # CONFIG_SCSI_T128 is not set
 # CONFIG_SCSI_U14_34F is not set
+# CONFIG_SCSI_NSP32 is not set
 # CONFIG_SCSI_DEBUG is not set
 
 #
@@ -310,6 +311,7 @@
 # CONFIG_NS83820 is not set
 # CONFIG_HAMACHI is not set
 # CONFIG_YELLOWFIN is not set
+# CONFIG_R8169 is not set
 # CONFIG_SK98LIN is not set
 # CONFIG_TIGON3 is not set
 # CONFIG_FDDI is not set
@@ -420,6 +422,8 @@
 # Watchdog Cards
 #
 # CONFIG_WATCHDOG is not set
+# CONFIG_SCx200_GPIO is not set
+# CONFIG_AMD_PM768 is not set
 # CONFIG_NVRAM is not set
 # CONFIG_RTC is not set
 # CONFIG_DTLK is not set
diff -urN linux-2.4.21-pre2/arch/ppc/configs/ibmchrp_defconfig linux-2.4.21-pre3/arch/ppc/configs/ibmchrp_defconfig
--- linux-2.4.21-pre2/arch/ppc/configs/ibmchrp_defconfig	Thu Nov 28 15:53:11 2002
+++ linux-2.4.21-pre3/arch/ppc/configs/ibmchrp_defconfig	Mon Jan  6 16:32:30 2003
@@ -298,6 +298,7 @@
 # CONFIG_SCSI_DC390T is not set
 # CONFIG_SCSI_T128 is not set
 # CONFIG_SCSI_U14_34F is not set
+# CONFIG_SCSI_NSP32 is not set
 # CONFIG_SCSI_DEBUG is not set
 # CONFIG_SCSI_MESH is not set
 # CONFIG_SCSI_MAC53C94 is not set
@@ -384,6 +385,7 @@
 # CONFIG_NS83820 is not set
 # CONFIG_HAMACHI is not set
 # CONFIG_YELLOWFIN is not set
+# CONFIG_R8169 is not set
 # CONFIG_SK98LIN is not set
 # CONFIG_TIGON3 is not set
 # CONFIG_FDDI is not set
@@ -520,7 +522,6 @@
 # CONFIG_SERIAL_NONSTANDARD is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
-# CONFIG_BRIQ_PANEL is not set
 
 #
 # I2C support
@@ -580,6 +581,8 @@
 # Watchdog Cards
 #
 # CONFIG_WATCHDOG is not set
+# CONFIG_SCx200_GPIO is not set
+# CONFIG_AMD_PM768 is not set
 CONFIG_NVRAM=y
 # CONFIG_RTC is not set
 # CONFIG_DTLK is not set
diff -urN linux-2.4.21-pre2/arch/ppc/configs/mbx_defconfig linux-2.4.21-pre3/arch/ppc/configs/mbx_defconfig
--- linux-2.4.21-pre2/arch/ppc/configs/mbx_defconfig	Thu Nov 28 15:53:11 2002
+++ linux-2.4.21-pre3/arch/ppc/configs/mbx_defconfig	Mon Jan  6 16:32:30 2003
@@ -229,6 +229,7 @@
 # CONFIG_NS83820 is not set
 # CONFIG_HAMACHI is not set
 # CONFIG_YELLOWFIN is not set
+# CONFIG_R8169 is not set
 # CONFIG_SK98LIN is not set
 # CONFIG_TIGON3 is not set
 # CONFIG_FDDI is not set
@@ -335,6 +336,8 @@
 # Watchdog Cards
 #
 # CONFIG_WATCHDOG is not set
+# CONFIG_SCx200_GPIO is not set
+# CONFIG_AMD_PM768 is not set
 # CONFIG_NVRAM is not set
 # CONFIG_RTC is not set
 # CONFIG_DTLK is not set
diff -urN linux-2.4.21-pre2/arch/ppc/configs/oak_defconfig linux-2.4.21-pre3/arch/ppc/configs/oak_defconfig
--- linux-2.4.21-pre2/arch/ppc/configs/oak_defconfig	Thu Nov 28 15:53:11 2002
+++ linux-2.4.21-pre3/arch/ppc/configs/oak_defconfig	Mon Jan  6 16:32:30 2003
@@ -215,6 +215,7 @@
 # CONFIG_NS83820 is not set
 # CONFIG_HAMACHI is not set
 # CONFIG_YELLOWFIN is not set
+# CONFIG_R8169 is not set
 # CONFIG_SK98LIN is not set
 # CONFIG_TIGON3 is not set
 # CONFIG_FDDI is not set
@@ -322,6 +323,8 @@
 # Watchdog Cards
 #
 # CONFIG_WATCHDOG is not set
+# CONFIG_SCx200_GPIO is not set
+# CONFIG_AMD_PM768 is not set
 # CONFIG_NVRAM is not set
 # CONFIG_RTC is not set
 # CONFIG_DTLK is not set
diff -urN linux-2.4.21-pre2/arch/ppc/configs/pal4_defconfig linux-2.4.21-pre3/arch/ppc/configs/pal4_defconfig
--- linux-2.4.21-pre2/arch/ppc/configs/pal4_defconfig	Thu Nov 28 15:53:11 2002
+++ linux-2.4.21-pre3/arch/ppc/configs/pal4_defconfig	Mon Jan  6 16:32:30 2003
@@ -309,6 +309,7 @@
 # CONFIG_NS83820 is not set
 # CONFIG_HAMACHI is not set
 # CONFIG_YELLOWFIN is not set
+# CONFIG_R8169 is not set
 # CONFIG_SK98LIN is not set
 # CONFIG_TIGON3 is not set
 # CONFIG_FDDI is not set
@@ -433,6 +434,8 @@
 # Watchdog Cards
 #
 # CONFIG_WATCHDOG is not set
+# CONFIG_SCx200_GPIO is not set
+# CONFIG_AMD_PM768 is not set
 # CONFIG_NVRAM is not set
 # CONFIG_RTC is not set
 # CONFIG_DTLK is not set
diff -urN linux-2.4.21-pre2/arch/ppc/configs/pmac_defconfig linux-2.4.21-pre3/arch/ppc/configs/pmac_defconfig
--- linux-2.4.21-pre2/arch/ppc/configs/pmac_defconfig	Thu Nov 28 15:53:11 2002
+++ linux-2.4.21-pre3/arch/ppc/configs/pmac_defconfig	Mon Jan  6 16:32:30 2003
@@ -245,21 +245,13 @@
 CONFIG_BLK_DEV_IDEDISK=y
 # CONFIG_IDEDISK_MULTI_MODE is not set
 # CONFIG_IDEDISK_STROKE is not set
-# CONFIG_BLK_DEV_IDEDISK_VENDOR is not set
-# CONFIG_BLK_DEV_IDEDISK_FUJITSU is not set
-# CONFIG_BLK_DEV_IDEDISK_IBM is not set
-# CONFIG_BLK_DEV_IDEDISK_MAXTOR is not set
-# CONFIG_BLK_DEV_IDEDISK_QUANTUM is not set
-# CONFIG_BLK_DEV_IDEDISK_SEAGATE is not set
-# CONFIG_BLK_DEV_IDEDISK_WD is not set
-# CONFIG_BLK_DEV_COMMERIAL is not set
-# CONFIG_BLK_DEV_TIVO is not set
 CONFIG_BLK_DEV_IDECS=m
 CONFIG_BLK_DEV_IDECD=y
 # CONFIG_BLK_DEV_IDETAPE is not set
 CONFIG_BLK_DEV_IDEFLOPPY=y
 CONFIG_BLK_DEV_IDESCSI=y
 # CONFIG_IDE_TASK_IOCTL is not set
+# CONFIG_IDE_TASKFILE_IO is not set
 
 #
 # IDE chipset support/bugfixes
@@ -267,8 +259,8 @@
 # CONFIG_BLK_DEV_CMD640 is not set
 # CONFIG_BLK_DEV_CMD640_ENHANCED is not set
 # CONFIG_BLK_DEV_ISAPNP is not set
-# CONFIG_BLK_DEV_RZ1000 is not set
 CONFIG_BLK_DEV_IDEPCI=y
+CONFIG_BLK_DEV_GENERIC=y
 CONFIG_IDEPCI_SHARE_IRQ=y
 CONFIG_BLK_DEV_IDEDMA_PCI=y
 # CONFIG_BLK_DEV_OFFBOARD is not set
@@ -277,28 +269,30 @@
 # CONFIG_IDEDMA_ONLYDISK is not set
 CONFIG_BLK_DEV_IDEDMA=y
 # CONFIG_IDEDMA_PCI_WIP is not set
-# CONFIG_BLK_DEV_IDEDMA_TIMEOUT is not set
-# CONFIG_IDEDMA_NEW_DRIVE_LISTINGS is not set
 CONFIG_BLK_DEV_ADMA=y
 # CONFIG_BLK_DEV_AEC62XX is not set
-# CONFIG_AEC62XX_TUNING is not set
 # CONFIG_BLK_DEV_ALI15X3 is not set
 # CONFIG_WDC_ALI15X3 is not set
 # CONFIG_BLK_DEV_AMD74XX is not set
 # CONFIG_AMD74XX_OVERRIDE is not set
 CONFIG_BLK_DEV_CMD64X=y
-# CONFIG_BLK_DEV_CMD680 is not set
 # CONFIG_BLK_DEV_CY82C693 is not set
 # CONFIG_BLK_DEV_CS5530 is not set
 # CONFIG_BLK_DEV_HPT34X is not set
 # CONFIG_HPT34X_AUTODMA is not set
 # CONFIG_BLK_DEV_HPT366 is not set
+# CONFIG_BLK_DEV_PIIX is not set
+# CONFIG_BLK_DEV_NFORCE is not set
 # CONFIG_BLK_DEV_NS87415 is not set
 # CONFIG_BLK_DEV_OPTI621 is not set
-# CONFIG_BLK_DEV_PDC202XX is not set
+# CONFIG_BLK_DEV_PDC202XX_OLD is not set
 # CONFIG_PDC202XX_BURST is not set
+CONFIG_BLK_DEV_PDC202XX_NEW=m
 # CONFIG_PDC202XX_FORCE is not set
+# CONFIG_BLK_DEV_RZ1000 is not set
+# CONFIG_BLK_DEV_SC1200 is not set
 # CONFIG_BLK_DEV_SVWKS is not set
+# CONFIG_BLK_DEV_SIIMAGE is not set
 # CONFIG_BLK_DEV_SIS5513 is not set
 # CONFIG_BLK_DEV_SLC90E66 is not set
 # CONFIG_BLK_DEV_TRM290 is not set
@@ -313,6 +307,7 @@
 CONFIG_IDEDMA_AUTO=y
 # CONFIG_IDEDMA_IVB is not set
 # CONFIG_DMA_NONPCI is not set
+CONFIG_BLK_DEV_PDC202XX=y
 CONFIG_BLK_DEV_IDE_MODES=y
 # CONFIG_BLK_DEV_ATARAID is not set
 # CONFIG_BLK_DEV_ATARAID_PDC is not set
@@ -399,6 +394,7 @@
 # CONFIG_SCSI_DC390T is not set
 # CONFIG_SCSI_T128 is not set
 # CONFIG_SCSI_U14_34F is not set
+# CONFIG_SCSI_NSP32 is not set
 # CONFIG_SCSI_DEBUG is not set
 CONFIG_SCSI_MESH=y
 CONFIG_SCSI_MESH_SYNC_RATE=5
@@ -516,6 +512,7 @@
 # CONFIG_NS83820 is not set
 # CONFIG_HAMACHI is not set
 # CONFIG_YELLOWFIN is not set
+# CONFIG_R8169 is not set
 # CONFIG_SK98LIN is not set
 # CONFIG_TIGON3 is not set
 # CONFIG_FDDI is not set
@@ -748,7 +745,6 @@
 # CONFIG_SERIAL_NONSTANDARD is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
-# CONFIG_BRIQ_PANEL is not set
 
 #
 # I2C support
@@ -813,6 +809,8 @@
 # Watchdog Cards
 #
 # CONFIG_WATCHDOG is not set
+# CONFIG_SCx200_GPIO is not set
+# CONFIG_AMD_PM768 is not set
 CONFIG_NVRAM=y
 # CONFIG_RTC is not set
 # CONFIG_DTLK is not set
@@ -988,6 +986,8 @@
 CONFIG_SOUND=m
 CONFIG_DMASOUND_PMAC=m
 CONFIG_DMASOUND=m
+CONFIG_I2C=m
+CONFIG_I2C_KEYWEST=m
 # CONFIG_SOUND_ALI5455 is not set
 # CONFIG_SOUND_BT878 is not set
 # CONFIG_SOUND_CMPCI is not set
@@ -1023,7 +1023,6 @@
 #
 CONFIG_USB_DEVICEFS=y
 # CONFIG_USB_BANDWIDTH is not set
-# CONFIG_USB_LONG_TIMEOUT is not set
 
 #
 # USB Host Controller Drivers
@@ -1061,6 +1060,7 @@
 # CONFIG_USB_HIDDEV is not set
 # CONFIG_USB_AIPTEK is not set
 # CONFIG_USB_WACOM is not set
+# CONFIG_USB_POWERMATE is not set
 
 #
 # USB Imaging devices
@@ -1114,6 +1114,7 @@
 # CONFIG_USB_SERIAL_KEYSPAN is not set
 # CONFIG_USB_SERIAL_MCT_U232 is not set
 # CONFIG_USB_SERIAL_KLSI is not set
+# CONFIG_USB_SERIAL_KOBIL_SCT is not set
 # CONFIG_USB_SERIAL_PL2303 is not set
 # CONFIG_USB_SERIAL_CYBERJACK is not set
 # CONFIG_USB_SERIAL_XIRCOM is not set
diff -urN linux-2.4.21-pre2/arch/ppc/configs/power3_defconfig linux-2.4.21-pre3/arch/ppc/configs/power3_defconfig
--- linux-2.4.21-pre2/arch/ppc/configs/power3_defconfig	Thu Nov 28 15:53:11 2002
+++ linux-2.4.21-pre3/arch/ppc/configs/power3_defconfig	Mon Jan  6 16:32:30 2003
@@ -259,6 +259,7 @@
 # CONFIG_SCSI_DC390T is not set
 # CONFIG_SCSI_T128 is not set
 # CONFIG_SCSI_U14_34F is not set
+# CONFIG_SCSI_NSP32 is not set
 # CONFIG_SCSI_DEBUG is not set
 # CONFIG_SCSI_MESH is not set
 # CONFIG_SCSI_MAC53C94 is not set
@@ -345,6 +346,7 @@
 # CONFIG_NS83820 is not set
 # CONFIG_HAMACHI is not set
 # CONFIG_YELLOWFIN is not set
+# CONFIG_R8169 is not set
 # CONFIG_SK98LIN is not set
 # CONFIG_TIGON3 is not set
 # CONFIG_FDDI is not set
@@ -486,7 +488,7 @@
 CONFIG_PRINTER=m
 # CONFIG_LP_CONSOLE is not set
 # CONFIG_PPDEV is not set
-# CONFIG_BRIQ_PANEL is not set
+# CONFIG_TIPAR is not set
 
 #
 # I2C support
@@ -496,6 +498,8 @@
 # CONFIG_I2C_PHILIPSPAR is not set
 # CONFIG_I2C_ELV is not set
 # CONFIG_I2C_VELLEMAN is not set
+# CONFIG_SCx200_I2C is not set
+# CONFIG_SCx200_ACB is not set
 CONFIG_I2C_ALGOPCF=y
 # CONFIG_I2C_ELEKTOR is not set
 # CONFIG_I2C_KEYWEST is not set
@@ -552,6 +556,8 @@
 # Watchdog Cards
 #
 # CONFIG_WATCHDOG is not set
+# CONFIG_SCx200_GPIO is not set
+# CONFIG_AMD_PM768 is not set
 CONFIG_NVRAM=y
 # CONFIG_RTC is not set
 # CONFIG_DTLK is not set
@@ -730,6 +736,7 @@
 CONFIG_SOUND_TRACEINIT=y
 # CONFIG_SOUND_DMAP is not set
 # CONFIG_SOUND_AD1816 is not set
+# CONFIG_SOUND_AD1889 is not set
 # CONFIG_SOUND_SGALAXY is not set
 # CONFIG_SOUND_ADLIB is not set
 # CONFIG_SOUND_ACI_MIXER is not set
diff -urN linux-2.4.21-pre2/arch/ppc/configs/rpxcllf_defconfig linux-2.4.21-pre3/arch/ppc/configs/rpxcllf_defconfig
--- linux-2.4.21-pre2/arch/ppc/configs/rpxcllf_defconfig	Thu Nov 28 15:53:11 2002
+++ linux-2.4.21-pre3/arch/ppc/configs/rpxcllf_defconfig	Mon Jan  6 16:32:30 2003
@@ -232,6 +232,7 @@
 # CONFIG_NS83820 is not set
 # CONFIG_HAMACHI is not set
 # CONFIG_YELLOWFIN is not set
+# CONFIG_R8169 is not set
 # CONFIG_SK98LIN is not set
 # CONFIG_TIGON3 is not set
 # CONFIG_FDDI is not set
@@ -339,6 +340,8 @@
 # Watchdog Cards
 #
 # CONFIG_WATCHDOG is not set
+# CONFIG_SCx200_GPIO is not set
+# CONFIG_AMD_PM768 is not set
 # CONFIG_NVRAM is not set
 # CONFIG_RTC is not set
 # CONFIG_DTLK is not set
diff -urN linux-2.4.21-pre2/arch/ppc/configs/rpxlite_defconfig linux-2.4.21-pre3/arch/ppc/configs/rpxlite_defconfig
--- linux-2.4.21-pre2/arch/ppc/configs/rpxlite_defconfig	Thu Nov 28 15:53:11 2002
+++ linux-2.4.21-pre3/arch/ppc/configs/rpxlite_defconfig	Mon Jan  6 16:32:30 2003
@@ -232,6 +232,7 @@
 # CONFIG_NS83820 is not set
 # CONFIG_HAMACHI is not set
 # CONFIG_YELLOWFIN is not set
+# CONFIG_R8169 is not set
 # CONFIG_SK98LIN is not set
 # CONFIG_TIGON3 is not set
 # CONFIG_FDDI is not set
@@ -339,6 +340,8 @@
 # Watchdog Cards
 #
 # CONFIG_WATCHDOG is not set
+# CONFIG_SCx200_GPIO is not set
+# CONFIG_AMD_PM768 is not set
 # CONFIG_NVRAM is not set
 # CONFIG_RTC is not set
 # CONFIG_DTLK is not set
diff -urN linux-2.4.21-pre2/arch/ppc/configs/spruce_defconfig linux-2.4.21-pre3/arch/ppc/configs/spruce_defconfig
--- linux-2.4.21-pre2/arch/ppc/configs/spruce_defconfig	Thu Nov 28 15:53:11 2002
+++ linux-2.4.21-pre3/arch/ppc/configs/spruce_defconfig	Mon Jan  6 16:32:30 2003
@@ -260,6 +260,7 @@
 # CONFIG_NS83820 is not set
 # CONFIG_HAMACHI is not set
 # CONFIG_YELLOWFIN is not set
+# CONFIG_R8169 is not set
 # CONFIG_SK98LIN is not set
 # CONFIG_TIGON3 is not set
 # CONFIG_FDDI is not set
@@ -373,6 +374,8 @@
 # Watchdog Cards
 #
 # CONFIG_WATCHDOG is not set
+# CONFIG_SCx200_GPIO is not set
+# CONFIG_AMD_PM768 is not set
 # CONFIG_NVRAM is not set
 # CONFIG_RTC is not set
 # CONFIG_DTLK is not set
diff -urN linux-2.4.21-pre2/arch/ppc/configs/walnut_defconfig linux-2.4.21-pre3/arch/ppc/configs/walnut_defconfig
--- linux-2.4.21-pre2/arch/ppc/configs/walnut_defconfig	Thu Nov 28 15:53:11 2002
+++ linux-2.4.21-pre3/arch/ppc/configs/walnut_defconfig	Mon Jan  6 16:32:30 2003
@@ -215,6 +215,7 @@
 # CONFIG_NS83820 is not set
 # CONFIG_HAMACHI is not set
 # CONFIG_YELLOWFIN is not set
+# CONFIG_R8169 is not set
 # CONFIG_SK98LIN is not set
 # CONFIG_TIGON3 is not set
 # CONFIG_FDDI is not set
@@ -326,6 +327,8 @@
 # Watchdog Cards
 #
 # CONFIG_WATCHDOG is not set
+# CONFIG_SCx200_GPIO is not set
+# CONFIG_AMD_PM768 is not set
 # CONFIG_NVRAM is not set
 # CONFIG_RTC is not set
 # CONFIG_DTLK is not set
diff -urN linux-2.4.21-pre2/arch/ppc/defconfig linux-2.4.21-pre3/arch/ppc/defconfig
--- linux-2.4.21-pre2/arch/ppc/defconfig	Thu Nov 28 15:53:11 2002
+++ linux-2.4.21-pre3/arch/ppc/defconfig	Mon Jan  6 16:32:30 2003
@@ -242,21 +242,13 @@
 CONFIG_BLK_DEV_IDEDISK=y
 # CONFIG_IDEDISK_MULTI_MODE is not set
 # CONFIG_IDEDISK_STROKE is not set
-# CONFIG_BLK_DEV_IDEDISK_VENDOR is not set
-# CONFIG_BLK_DEV_IDEDISK_FUJITSU is not set
-# CONFIG_BLK_DEV_IDEDISK_IBM is not set
-# CONFIG_BLK_DEV_IDEDISK_MAXTOR is not set
-# CONFIG_BLK_DEV_IDEDISK_QUANTUM is not set
-# CONFIG_BLK_DEV_IDEDISK_SEAGATE is not set
-# CONFIG_BLK_DEV_IDEDISK_WD is not set
-# CONFIG_BLK_DEV_COMMERIAL is not set
-# CONFIG_BLK_DEV_TIVO is not set
 # CONFIG_BLK_DEV_IDECS is not set
 CONFIG_BLK_DEV_IDECD=y
 # CONFIG_BLK_DEV_IDETAPE is not set
 CONFIG_BLK_DEV_IDEFLOPPY=y
 CONFIG_BLK_DEV_IDESCSI=y
 # CONFIG_IDE_TASK_IOCTL is not set
+# CONFIG_IDE_TASKFILE_IO is not set
 
 #
 # IDE chipset support/bugfixes
@@ -264,8 +256,8 @@
 # CONFIG_BLK_DEV_CMD640 is not set
 # CONFIG_BLK_DEV_CMD640_ENHANCED is not set
 # CONFIG_BLK_DEV_ISAPNP is not set
-# CONFIG_BLK_DEV_RZ1000 is not set
 CONFIG_BLK_DEV_IDEPCI=y
+CONFIG_BLK_DEV_GENERIC=y
 CONFIG_IDEPCI_SHARE_IRQ=y
 CONFIG_BLK_DEV_IDEDMA_PCI=y
 # CONFIG_BLK_DEV_OFFBOARD is not set
@@ -274,28 +266,30 @@
 # CONFIG_IDEDMA_ONLYDISK is not set
 CONFIG_BLK_DEV_IDEDMA=y
 # CONFIG_IDEDMA_PCI_WIP is not set
-# CONFIG_BLK_DEV_IDEDMA_TIMEOUT is not set
-# CONFIG_IDEDMA_NEW_DRIVE_LISTINGS is not set
 CONFIG_BLK_DEV_ADMA=y
 # CONFIG_BLK_DEV_AEC62XX is not set
-# CONFIG_AEC62XX_TUNING is not set
 # CONFIG_BLK_DEV_ALI15X3 is not set
 # CONFIG_WDC_ALI15X3 is not set
 # CONFIG_BLK_DEV_AMD74XX is not set
 # CONFIG_AMD74XX_OVERRIDE is not set
 CONFIG_BLK_DEV_CMD64X=y
-# CONFIG_BLK_DEV_CMD680 is not set
 # CONFIG_BLK_DEV_CY82C693 is not set
 # CONFIG_BLK_DEV_CS5530 is not set
 # CONFIG_BLK_DEV_HPT34X is not set
 # CONFIG_HPT34X_AUTODMA is not set
 # CONFIG_BLK_DEV_HPT366 is not set
+# CONFIG_BLK_DEV_PIIX is not set
+# CONFIG_BLK_DEV_NFORCE is not set
 # CONFIG_BLK_DEV_NS87415 is not set
 # CONFIG_BLK_DEV_OPTI621 is not set
-# CONFIG_BLK_DEV_PDC202XX is not set
+# CONFIG_BLK_DEV_PDC202XX_OLD is not set
 # CONFIG_PDC202XX_BURST is not set
+# CONFIG_BLK_DEV_PDC202XX_NEW is not set
 # CONFIG_PDC202XX_FORCE is not set
+# CONFIG_BLK_DEV_RZ1000 is not set
+# CONFIG_BLK_DEV_SC1200 is not set
 # CONFIG_BLK_DEV_SVWKS is not set
+# CONFIG_BLK_DEV_SIIMAGE is not set
 # CONFIG_BLK_DEV_SIS5513 is not set
 # CONFIG_BLK_DEV_SLC90E66 is not set
 # CONFIG_BLK_DEV_TRM290 is not set
@@ -396,6 +390,7 @@
 # CONFIG_SCSI_DC390T is not set
 # CONFIG_SCSI_T128 is not set
 # CONFIG_SCSI_U14_34F is not set
+# CONFIG_SCSI_NSP32 is not set
 # CONFIG_SCSI_DEBUG is not set
 CONFIG_SCSI_MESH=y
 CONFIG_SCSI_MESH_SYNC_RATE=5
@@ -487,6 +482,7 @@
 # CONFIG_NS83820 is not set
 # CONFIG_HAMACHI is not set
 # CONFIG_YELLOWFIN is not set
+# CONFIG_R8169 is not set
 # CONFIG_SK98LIN is not set
 # CONFIG_TIGON3 is not set
 # CONFIG_FDDI is not set
@@ -647,7 +643,6 @@
 # CONFIG_SERIAL_NONSTANDARD is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
-# CONFIG_BRIQ_PANEL is not set
 
 #
 # I2C support
@@ -712,6 +707,8 @@
 # Watchdog Cards
 #
 # CONFIG_WATCHDOG is not set
+# CONFIG_SCx200_GPIO is not set
+# CONFIG_AMD_PM768 is not set
 CONFIG_NVRAM=y
 # CONFIG_RTC is not set
 # CONFIG_DTLK is not set
@@ -880,6 +877,8 @@
 CONFIG_SOUND=m
 CONFIG_DMASOUND_PMAC=m
 CONFIG_DMASOUND=m
+CONFIG_I2C=m
+CONFIG_I2C_KEYWEST=m
 # CONFIG_SOUND_ALI5455 is not set
 # CONFIG_SOUND_BT878 is not set
 # CONFIG_SOUND_CMPCI is not set
@@ -915,7 +914,6 @@
 #
 CONFIG_USB_DEVICEFS=y
 # CONFIG_USB_BANDWIDTH is not set
-# CONFIG_USB_LONG_TIMEOUT is not set
 
 #
 # USB Host Controller Drivers
@@ -953,6 +951,7 @@
 # CONFIG_USB_HIDDEV is not set
 # CONFIG_USB_AIPTEK is not set
 # CONFIG_USB_WACOM is not set
+# CONFIG_USB_POWERMATE is not set
 
 #
 # USB Imaging devices
@@ -1006,6 +1005,7 @@
 # CONFIG_USB_SERIAL_KEYSPAN is not set
 # CONFIG_USB_SERIAL_MCT_U232 is not set
 # CONFIG_USB_SERIAL_KLSI is not set
+# CONFIG_USB_SERIAL_KOBIL_SCT is not set
 # CONFIG_USB_SERIAL_PL2303 is not set
 # CONFIG_USB_SERIAL_CYBERJACK is not set
 # CONFIG_USB_SERIAL_XIRCOM is not set
diff -urN linux-2.4.21-pre2/arch/ppc/kernel/i8259.c linux-2.4.21-pre3/arch/ppc/kernel/i8259.c
--- linux-2.4.21-pre2/arch/ppc/kernel/i8259.c	Thu Nov 28 15:53:11 2002
+++ linux-2.4.21-pre3/arch/ppc/kernel/i8259.c	Mon Jan  6 16:32:30 2003
@@ -21,17 +21,37 @@
 
 int i8259_pic_irq_offset;
 
-/* Acknowledge the irq using the PCI host bridge's interrupt acknowledge
- * feature. (Polling is somehow broken on some IBM and Motorola PReP boxes.)
+/*
+ * Acknowledge the IRQ using either the PCI host bridge's interrupt
+ * acknowledge feature or poll.  How i8259_init() is called determines
+ * which is called.  It should be noted that polling is broken on some
+ * IBM and Motorola PReP boxes so we must use the int-ack feature on them.
  */
-int i8259_irq(struct pt_regs *regs)
+int
+i8259_irq(struct pt_regs *regs)
 {
 	int irq;
 
-	spin_lock/*_irqsave*/(&i8259_lock/*, flags*/);
+	spin_lock(&i8259_lock);
 
-	irq = *pci_intack;
-	if (irq==7) {
+	/* Either int-ack or poll for the IRQ */
+	if (pci_intack)
+		irq = *pci_intack;
+	else {
+		/* Perform an interrupt acknowledge cycle on controller 1. */
+		outb(0x0C, 0x20);		/* prepare for poll */
+		irq = inb(0x20) & 7;
+		if (irq == 2 ) {
+			/*
+			 * Interrupt is cascaded so perform interrupt
+			 * acknowledge on controller 2.
+			 */
+			outb(0x0C, 0xA0);	/* prepare for poll */
+			irq = (inb(0xA0) & 7) + 8;
+		}
+	}
+
+	if (irq == 7) {
 		/*
 		 * This may be a spurious interrupt.
 		 *
@@ -39,47 +59,13 @@
 		 * significant bit is not set then there is no valid
 		 * interrupt.
 		 */
-		if(~inb(0x20)&0x80) {
+		if (!pci_intack)
+			outb(0x0B, 0x20);	/* ISR register */
+		if(~inb(0x20) & 0x80)
 			irq = -1;
-		}
 	}
-	spin_unlock/*_irqrestore*/(&i8259_lock/*, flags*/);
-	return irq;
-}
-
-/* Poke the 8259's directly using poll commands. */
-int i8259_poll(struct pt_regs *regs)
-{
-	int irq;
 
-	spin_lock/*_irqsave*/(&i8259_lock/*, flags*/);
-	/*
-	 * Perform an interrupt acknowledge cycle on controller 1
-	 */
-	outb(0x0C, 0x20); /* prepare for poll */
-	irq = inb(0x20) & 7;
-	if (irq == 2) {
-		/*
-		 * Interrupt is cascaded so perform interrupt
-		 * acknowledge on controller 2
-		 */
-		outb(0x0C, 0xA0); /* prepare for poll */
-		irq = (inb(0xA0) & 7) + 8;
-	} else if (irq==7) {
-		/*
-		 * This may be a spurious interrupt
-		 *
-		 * Read the interrupt status register. If the most
-		 * significant bit is not set then there is no valid
-		 * interrupt
-		 */
-		outb(0x0b, 0x20);
-		if(~inb(0x20)&0x80) {
-			spin_unlock/*_irqrestore*/(&i8259_lock/*, flags*/);
-			return -1;
-		}
-	}
-	spin_unlock/*_irqrestore*/(&i8259_lock/*, flags*/);
+	spin_unlock(&i8259_lock);
 	return irq;
 }
 
diff -urN linux-2.4.21-pre2/arch/ppc/kernel/misc.S linux-2.4.21-pre3/arch/ppc/kernel/misc.S
--- linux-2.4.21-pre2/arch/ppc/kernel/misc.S	Thu Nov 28 15:53:11 2002
+++ linux-2.4.21-pre3/arch/ppc/kernel/misc.S	Mon Jan  6 16:32:30 2003
@@ -1062,10 +1062,10 @@
 	.long sys_newlstat
 	.long sys_newfstat
 	.long sys_uname
-	.long sys_iopl		/* 110 */
+	.long sys_ni_syscall	/* 110 old iopl syscall */
 	.long sys_vhangup
 	.long sys_ni_syscall	/* old 'idle' syscall */
-	.long sys_vm86
+	.long sys_ni_syscall	/* old vm86 syscall */
 	.long sys_wait4
 	.long sys_swapoff	/* 115 */
 	.long sys_sysinfo
@@ -1075,7 +1075,7 @@
 	.long sys_clone		/* 120 */
 	.long sys_setdomainname
 	.long sys_newuname
-	.long sys_modify_ldt
+	.long sys_ni_syscall	/* old modify_ldt syscall */
 	.long sys_adjtimex
 	.long sys_mprotect	/* 125 */
 	.long sys_sigprocmask
diff -urN linux-2.4.21-pre2/arch/ppc/kernel/open_pic.c linux-2.4.21-pre3/arch/ppc/kernel/open_pic.c
--- linux-2.4.21-pre2/arch/ppc/kernel/open_pic.c	Thu Nov 28 15:53:11 2002
+++ linux-2.4.21-pre3/arch/ppc/kernel/open_pic.c	Mon Jan  6 16:32:30 2003
@@ -30,6 +30,11 @@
 
 void* OpenPIC_Addr;
 static volatile struct OpenPIC *OpenPIC = NULL;
+/*
+ * We define OpenPIC_InitSenses table thusly:
+ * bit 0x1: sense, 0 for edge and 1 for level.
+ * bit 0x2: polarity, 0 for negative, 1 for positive.
+ */
 u_int OpenPIC_NumInitSenses __initdata = 0;
 u_char *OpenPIC_InitSenses __initdata = NULL;
 extern int use_of_interrupt_tree;
@@ -38,7 +43,8 @@
 static u_int NumSources;
 static int open_pic_irq_offset;
 static volatile OpenPIC_Source *ISR[NR_IRQS];
-static volatile unsigned char* chrp_int_ack_special;
+static int openpic_cascade_irq = -1;
+static int (*openpic_cascade_fn)(struct pt_regs *);
 
 /* Global Operations */
 static void openpic_disable_8259_pass_through(void);
@@ -247,6 +253,31 @@
 }
 #endif /* CONFIG_SMP */
 
+#ifdef CONFIG_EPIC_SERIAL_MODE
+static void __init openpic_eicr_set_clk(u_int clkval)
+{
+	openpic_writefield(&OpenPIC->Global.Global_Configuration1,
+			OPENPIC_EICR_S_CLK_MASK, (clkval << 28));
+}
+
+static void __init openpic_enable_sie(void)
+{
+	openpic_setfield(&OpenPIC->Global.Global_Configuration1,
+			OPENPIC_EICR_SIE);
+}
+#endif
+
+#if defined(CONFIG_EPIC_SERIAL_MODE) || defined(CONFIG_PMAC_PBOOK)
+static void openpic_reset(void)
+{
+	openpic_setfield(&OpenPIC->Global.Global_Configuration0,
+			 OPENPIC_CONFIG_RESET);
+	while (openpic_readfield(&OpenPIC->Global.Global_Configuration0,
+				 OPENPIC_CONFIG_RESET))
+		mb();
+}
+#endif
+
 void __init openpic_set_sources(int first_irq, int num_irqs, void *first_ISR)
 {
 	volatile OpenPIC_Source *src = first_ISR;
@@ -261,8 +292,14 @@
 		ISR[i] = src;
 }
 
-void __init openpic_init(int main_pic, int offset, unsigned char* chrp_ack,
-			 int programmer_switch_irq)
+/*
+ * The `offset' parameter defines where the interrupts handled by the
+ * OpenPIC start in the space of interrupt numbers that the kernel knows
+ * about.  In other words, the OpenPIC's IRQ0 is numbered `offset' in the
+ * kernel's interrupt numbering scheme.
+ * We assume there is only one OpenPIC.
+ */
+void __init openpic_init(int offset)
 {
 	u_int t, i;
 	u_int timerfreq;
@@ -274,6 +311,12 @@
 	}
 	OpenPIC = (volatile struct OpenPIC *)OpenPIC_Addr;
 
+#ifdef CONFIG_EPIC_SERIAL_MODE
+	/* Have to start from ground zero.
+	*/
+	openpic_reset();
+#endif
+
 	if (ppc_md.progress) ppc_md.progress("openpic: enter", 0x122);
 
 	t = openpic_read(&OpenPIC->Global.Feature_Reporting0);
@@ -305,11 +348,7 @@
 		printk("OpenPIC timer frequency is %d.%06d MHz\n",
 		       timerfreq / 1000000, timerfreq % 1000000);
 
-	if (!main_pic)
-		return;
-
 	open_pic_irq_offset = offset;
-	chrp_int_ack_special = (volatile unsigned char*)chrp_ack;
 
 	/* Initialize timer interrupts */
 	if ( ppc_md.progress ) ppc_md.progress("openpic: timer",0x3ba);
@@ -333,19 +372,13 @@
 #endif
 
 	/* Initialize external interrupts */
-	if (ppc_md.progress) ppc_md.progress("openpic ext",0x3bc);
+	if (ppc_md.progress) ppc_md.progress("openpic: external",0x3bc);
 
 	openpic_set_priority(0xf);
 
-	/* SIOint (8259 cascade) is special */
-	if (offset) {
-		openpic_initirq(0, 8, offset, 1, 1);
-		openpic_mapirq(0, 1<<0, 0);
-	}
-
-	/* Init all external sources */
-	for (i = 1; i < NumSources; i++) {
-		int pri, sense;
+	/* Init all external sources, including possibly the cascade. */
+	for (i = 0; i < NumSources; i++) {
+		int sense;
 
 		if (ISR[i] == 0)
 			continue;
@@ -353,13 +386,15 @@
 		/* the bootloader may have left it enabled (bad !) */
 		openpic_disable_irq(i+offset);
 
-		pri = (i == programmer_switch_irq)? 9: 8;
-		sense = (i < OpenPIC_NumInitSenses)? OpenPIC_InitSenses[i]: 1;
-		if (sense)
+		sense = (i < OpenPIC_NumInitSenses)? OpenPIC_InitSenses[i]: \
+				(IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE);
+
+		if (sense & IRQ_SENSE_MASK)
 			irq_desc[i+offset].status = IRQ_LEVEL;
 
-		/* Enabled, Priority 8 or 9 */
-		openpic_initirq(i, pri, i+offset, !sense, sense);
+		/* Enabled, Priority 8 */
+		openpic_initirq(i, 8, i+offset, (sense & IRQ_POLARITY_MASK),
+				(sense & IRQ_SENSE_MASK));
 		/* Processor 0 */
 		openpic_mapirq(i, 1<<0, 0);
 	}
@@ -372,25 +407,14 @@
 	if (ppc_md.progress) ppc_md.progress("openpic: spurious",0x3bd);
 	openpic_set_spurious(OPENPIC_VEC_SPURIOUS+offset);
 
-	/* Initialize the cascade */
-	if (offset) {
-		if (request_irq(offset, no_action, SA_INTERRUPT,
-				"82c59 cascade", NULL))
-			printk("Unable to get OpenPIC IRQ 0 for cascade\n");
-	}
-	openpic_set_priority(0);
 	openpic_disable_8259_pass_through();
+#ifdef CONFIG_EPIC_SERIAL_MODE
+	openpic_eicr_set_clk(7);	/* Slowest value until we know better */
+	openpic_enable_sie();
+#endif
+	openpic_set_priority(0);
 
-	if (ppc_md.progress) ppc_md.progress("openpic exit",0x222);
-}
-
-static void openpic_reset(void)
-{
-	openpic_setfield(&OpenPIC->Global.Global_Configuration0,
-			 OPENPIC_CONFIG_RESET);
-	while (openpic_readfield(&OpenPIC->Global.Global_Configuration0,
-				 OPENPIC_CONFIG_RESET))
-		mb();
+	if (ppc_md.progress) ppc_md.progress("openpic: exit",0x222);
 }
 
 #ifdef notused
@@ -614,6 +638,21 @@
 		      physmask(cpumask));
 }
 
+/*
+ * Initalize the interrupt source which will generate an NMI.
+ * This raises the interrupt's priority from 8 to 9.
+ *
+ * irq: The logical IRQ which generates an NMI.
+ */
+void __init
+openpic_init_nmi_irq(u_int irq)
+{
+	check_arg_irq(irq);
+	openpic_safe_writefield(&ISR[irq - open_pic_irq_offset]->Vector_Priority,
+				OPENPIC_PRIORITY_MASK,
+				9 << OPENPIC_PRIORITY_SHIFT);
+}
+
 
 /*
  *
@@ -621,6 +660,19 @@
  *
  */
 
+/*
+ * Hookup a cascade to the OpenPIC.
+ */
+void __init
+openpic_hookup_cascade(u_int irq, char *name,
+	int (*cascade_fn)(struct pt_regs *))
+{
+	openpic_cascade_irq = irq;
+	openpic_cascade_fn = cascade_fn;
+	if (request_irq(irq, no_action, SA_INTERRUPT, name, NULL))
+		printk("Unable to get OpenPIC IRQ %d for cascade\n",
+				irq - open_pic_irq_offset);
+}
 
 /*
  *  Enable/disable an external interrupt source
@@ -762,32 +814,25 @@
 
 #endif /* CONFIG_SMP */
 
-/* This one may be merged with PReP and CHRP */
 int
 openpic_get_irq(struct pt_regs *regs)
 {
-/*
- * Clean up needed. -VAL
- */
 	int irq = openpic_irq();
 
-	/* Management of the cascade should be moved out of here */
-
-	/* Yep - because openpic !=> i8259, for one thing. -VAL */
-        if (open_pic_irq_offset && irq == open_pic_irq_offset)
-        {
-                /*
-                 * This magic address generates a PCI IACK cycle.
-                 */
-		if ( chrp_int_ack_special )
-			irq = *chrp_int_ack_special;
-#ifndef CONFIG_GEMINI
-		else
-			irq = i8259_poll(regs);
-#endif
-		openpic_eoi();
-        }
-	if (irq == OPENPIC_VEC_SPURIOUS + open_pic_irq_offset)
+	/*
+	 * Check for the cascade interrupt and call the cascaded
+	 * interrupt controller function (usually i8259_irq) if so.
+	 * This should move to irq.c eventually.  -- paulus
+	 */
+	if (irq == openpic_cascade_irq && openpic_cascade_fn != NULL) {
+		int cirq = openpic_cascade_fn(regs);
+
+		/* Allow for the cascade being shared with other devices */
+		if (cirq != -1) {
+			irq = cirq;
+			openpic_eoi();
+		}
+        } else if (irq == OPENPIC_VEC_SPURIOUS + open_pic_irq_offset)
 		irq = -1;
 	return irq;
 }
diff -urN linux-2.4.21-pre2/arch/ppc/kernel/open_pic_defs.h linux-2.4.21-pre3/arch/ppc/kernel/open_pic_defs.h
--- linux-2.4.21-pre2/arch/ppc/kernel/open_pic_defs.h	Tue Aug 28 06:58:33 2001
+++ linux-2.4.21-pre3/arch/ppc/kernel/open_pic_defs.h	Mon Jan  6 16:32:30 2003
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.open_pic_defs.h 1.8 08/20/01 22:33:28 paulus
+ * BK Id: %F% %I% %G% %U% %#%
  */
 /*
  *  linux/openpic.h -- OpenPIC definitions
@@ -209,6 +209,14 @@
 #define OPENPIC_CONFIG_BASE_MASK		0x000fffff
 
     /*
+     *  Global Configuration Register 1
+     *  This is the EICR on EPICs.
+     */
+
+#define OPENPIC_EICR_S_CLK_MASK			0x70000000
+#define OPENPIC_EICR_SIE			0x08000000
+
+    /*
      *  Vendor Identification Register
      */
 
diff -urN linux-2.4.21-pre2/arch/ppc/kernel/ppc_ksyms.c linux-2.4.21-pre3/arch/ppc/kernel/ppc_ksyms.c
--- linux-2.4.21-pre2/arch/ppc/kernel/ppc_ksyms.c	Thu Nov 28 15:53:11 2002
+++ linux-2.4.21-pre3/arch/ppc/kernel/ppc_ksyms.c	Mon Jan  6 16:32:30 2003
@@ -58,7 +58,6 @@
 /* Tell string.h we don't want memcpy etc. as cpp defines */
 #define EXPORT_SYMTAB_STROPS
 
-extern void ppc_generic_ide_fix_driveid(struct hd_driveid *id);
 extern void transfer_to_handler(void);
 extern void syscall_trace(void);
 extern void do_IRQ(struct pt_regs *regs);
@@ -170,7 +169,6 @@
 #if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) \
 	|| defined(CONFIG_USB_STORAGE) || defined(CONFIG_USB_STORAGE_MODULE)
 EXPORT_SYMBOL(ppc_ide_md);
-EXPORT_SYMBOL(ppc_generic_ide_fix_driveid);
 #endif
 
 #ifdef CONFIG_PCI
diff -urN linux-2.4.21-pre2/arch/ppc/kernel/prom.c linux-2.4.21-pre3/arch/ppc/kernel/prom.c
--- linux-2.4.21-pre2/arch/ppc/kernel/prom.c	Thu Nov 28 15:53:11 2002
+++ linux-2.4.21-pre3/arch/ppc/kernel/prom.c	Mon Jan  6 16:32:30 2003
@@ -506,6 +506,38 @@
 }
 
 static unsigned long __init
+map_addr(struct device_node *np, unsigned long space, unsigned long addr)
+{
+	int na;
+	unsigned int *ranges;
+	int rlen = 0;
+	unsigned int type;
+
+	type = (space >> 24) & 3;
+	if (type == 0)
+		return addr;
+
+	while ((np = np->parent) != NULL) {
+		if (strcmp(np->type, "pci") != 0)
+			continue;
+		/* PCI bridge: map the address through the ranges property */
+		na = prom_n_addr_cells(np);
+		ranges = (unsigned int *) get_property(np, "ranges", &rlen);
+		while ((rlen -= (na + 5) * sizeof(unsigned int)) >= 0) {
+			if (((ranges[0] >> 24) & 3) == type
+			    && ranges[2] <= addr
+			    && addr - ranges[2] < ranges[na+4]) {
+				/* ok, this matches, translate it */
+				addr += ranges[na+2] - ranges[2];
+				break;
+			}
+			ranges += na + 5;
+		}
+	}
+	return addr;
+}
+
+static unsigned long __init
 interpret_pci_props(struct device_node *np, unsigned long mem_start,
 		    int naddrc, int nsizec)
 {
@@ -519,9 +551,9 @@
 		i = 0;
 		adr = (struct address_range *) mem_start;
 		while ((l -= sizeof(struct pci_reg_property)) >= 0) {
-			/* XXX assumes PCI addresses mapped 1-1 to physical */
 			adr[i].space = pci_addrs[i].addr.a_hi;
-			adr[i].address = pci_addrs[i].addr.a_lo;
+			adr[i].address = map_addr(np, pci_addrs[i].addr.a_hi,
+						  pci_addrs[i].addr.a_lo);
 			adr[i].size = pci_addrs[i].size_lo;
 			++i;
 		}
@@ -758,8 +790,15 @@
 	for (np = allnodes; np != 0; np = np->allnext) {
 		for (j = 0; j < np->n_intrs; j++) {
 			i = np->intrs[j].line;
-			if (i >= off && i < max)
-				senses[i-off] = np->intrs[j].sense;
+			if (i >= off && i < max) {
+				if (np->intrs[j].sense == 1) {
+					senses[i-off] = (IRQ_SENSE_LEVEL |
+							IRQ_POLARITY_NEGATIVE);
+				} else {
+					senses[i-off] = (IRQ_SENSE_EDGE |
+							IRQ_POLARITY_POSITIVE);
+				}
+			}
 		}
 	}
 }
diff -urN linux-2.4.21-pre2/arch/ppc/kernel/setup.c linux-2.4.21-pre3/arch/ppc/kernel/setup.c
--- linux-2.4.21-pre2/arch/ppc/kernel/setup.c	Mon Jan  6 16:32:27 2003
+++ linux-2.4.21-pre3/arch/ppc/kernel/setup.c	Mon Jan  6 16:32:30 2003
@@ -603,103 +603,3 @@
 	/* this is for modules since _machine can be a define -- Cort */
 	ppc_md.ppc_machine = _machine;
 }
-
-#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) \
-	|| defined(CONFIG_USB_STORAGE) || defined(CONFIG_USB_STORAGE_MODULE)
-/* Convert the shorts/longs in hd_driveid from little to big endian;
- * chars are endian independant, of course, but strings need to be flipped.
- * (Despite what it says in drivers/block/ide.h, they come up as little
- * endian...)
- *
- * Changes to linux/hdreg.h may require changes here. */
-void ppc_generic_ide_fix_driveid(struct hd_driveid *id)
-{
-        int i;
-	unsigned short *stringcast;
-
-	id->config         = __le16_to_cpu(id->config);
-	id->cyls           = __le16_to_cpu(id->cyls);
-	id->reserved2      = __le16_to_cpu(id->reserved2);
-	id->heads          = __le16_to_cpu(id->heads);
-	id->track_bytes    = __le16_to_cpu(id->track_bytes);
-	id->sector_bytes   = __le16_to_cpu(id->sector_bytes);
-	id->sectors        = __le16_to_cpu(id->sectors);
-	id->vendor0        = __le16_to_cpu(id->vendor0);
-	id->vendor1        = __le16_to_cpu(id->vendor1);
-	id->vendor2        = __le16_to_cpu(id->vendor2);
-	stringcast = (unsigned short *)&id->serial_no[0];
-	for (i = 0; i < (20/2); i++)
-	        stringcast[i] = __le16_to_cpu(stringcast[i]);
-	id->buf_type       = __le16_to_cpu(id->buf_type);
-	id->buf_size       = __le16_to_cpu(id->buf_size);
-	id->ecc_bytes      = __le16_to_cpu(id->ecc_bytes);
-	stringcast = (unsigned short *)&id->fw_rev[0];
-	for (i = 0; i < (8/2); i++)
-	        stringcast[i] = __le16_to_cpu(stringcast[i]);
-	stringcast = (unsigned short *)&id->model[0];
-	for (i = 0; i < (40/2); i++)
-	        stringcast[i] = __le16_to_cpu(stringcast[i]);
-	id->dword_io       = __le16_to_cpu(id->dword_io);
-	id->reserved50     = __le16_to_cpu(id->reserved50);
-	id->field_valid    = __le16_to_cpu(id->field_valid);
-	id->cur_cyls       = __le16_to_cpu(id->cur_cyls);
-	id->cur_heads      = __le16_to_cpu(id->cur_heads);
-	id->cur_sectors    = __le16_to_cpu(id->cur_sectors);
-	id->cur_capacity0  = __le16_to_cpu(id->cur_capacity0);
-	id->cur_capacity1  = __le16_to_cpu(id->cur_capacity1);
-	id->lba_capacity   = __le32_to_cpu(id->lba_capacity);
-	id->dma_1word      = __le16_to_cpu(id->dma_1word);
-	id->dma_mword      = __le16_to_cpu(id->dma_mword);
-	id->eide_pio_modes = __le16_to_cpu(id->eide_pio_modes);
-	id->eide_dma_min   = __le16_to_cpu(id->eide_dma_min);
-	id->eide_dma_time  = __le16_to_cpu(id->eide_dma_time);
-	id->eide_pio       = __le16_to_cpu(id->eide_pio);
-	id->eide_pio_iordy = __le16_to_cpu(id->eide_pio_iordy);
-	for (i = 0; i < 2; i++)
-		id->words69_70[i] = __le16_to_cpu(id->words69_70[i]);
-        for (i = 0; i < 4; i++)
-                id->words71_74[i] = __le16_to_cpu(id->words71_74[i]);
-	id->queue_depth	   = __le16_to_cpu(id->queue_depth);
-	for (i = 0; i < 4; i++)
-		id->words76_79[i] = __le16_to_cpu(id->words76_79[i]);
-	id->major_rev_num  = __le16_to_cpu(id->major_rev_num);
-	id->minor_rev_num  = __le16_to_cpu(id->minor_rev_num);
-	id->command_set_1  = __le16_to_cpu(id->command_set_1);
-	id->command_set_2  = __le16_to_cpu(id->command_set_2);
-	id->cfsse          = __le16_to_cpu(id->cfsse);
-	id->cfs_enable_1   = __le16_to_cpu(id->cfs_enable_1);
-	id->cfs_enable_2   = __le16_to_cpu(id->cfs_enable_2);
-	id->csf_default    = __le16_to_cpu(id->csf_default);
-	id->dma_ultra      = __le16_to_cpu(id->dma_ultra);
-	id->trseuc         = __le16_to_cpu(id->trseuc);
-	id->trsEuc         = __le16_to_cpu(id->trsEuc);
-	id->CurAPMvalues   = __le16_to_cpu(id->CurAPMvalues);
-	id->mprc           = __le16_to_cpu(id->mprc);
-	id->hw_config      = __le16_to_cpu(id->hw_config);
-	id->acoustic       = __le16_to_cpu(id->acoustic);
-	id->msrqs          = __le16_to_cpu(id->msrqs);
-	id->sxfert         = __le16_to_cpu(id->sxfert);
-	id->sal            = __le16_to_cpu(id->sal);
-	id->spg            = __le32_to_cpu(id->spg);
-	id->lba_capacity_2 = __le64_to_cpu(id->lba_capacity_2);
-	for (i = 0; i < 22; i++)
-		id->words104_125[i]   = __le16_to_cpu(id->words104_125[i]);
-	id->last_lun       = __le16_to_cpu(id->last_lun);
-	id->word127        = __le16_to_cpu(id->word127);
-	id->dlf            = __le16_to_cpu(id->dlf);
-	id->csfo           = __le16_to_cpu(id->csfo);
-	for (i = 0; i < 26; i++)
-		id->words130_155[i] = __le16_to_cpu(id->words130_155[i]);
-	id->word156        = __le16_to_cpu(id->word156);
-	for (i = 0; i < 3; i++)
-		id->words157_159[i] = __le16_to_cpu(id->words157_159[i]);
-	id->cfa_power      = __le16_to_cpu(id->cfa_power);
-	for (i = 0; i < 14; i++)
-		id->words161_175[i] = __le16_to_cpu(id->words161_175[i]);
-	for (i = 0; i < 31; i++)
-		id->words176_205[i] = __le16_to_cpu(id->words176_205[i]);
-	for (i = 0; i < 48; i++)
-		id->words206_254[i] = __le16_to_cpu(id->words206_254[i]);
-	id->integrity_word  = __le16_to_cpu(id->integrity_word);
-}
-#endif
diff -urN linux-2.4.21-pre2/arch/ppc/kernel/syscalls.c linux-2.4.21-pre3/arch/ppc/kernel/syscalls.c
--- linux-2.4.21-pre2/arch/ppc/kernel/syscalls.c	Fri Aug  2 17:39:43 2002
+++ linux-2.4.21-pre3/arch/ppc/kernel/syscalls.c	Mon Jan  6 16:32:30 2003
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.syscalls.c 1.13 03/13/02 09:12:22 trini
+ * BK Id: %F% %I% %G% %U% %#%
  */
 /*
  * linux/arch/ppc/kernel/sys_ppc.c
@@ -54,24 +54,6 @@
 	return -EIO;
 }
 
-int sys_iopl(int a1, int a2, int a3, int a4)
-{
-	printk(KERN_ERR "sys_iopl(%x, %x, %x, %x)!\n", a1, a2, a3, a4);
-	return (-ENOSYS);
-}
-
-int sys_vm86(int a1, int a2, int a3, int a4)
-{
-	printk(KERN_ERR "sys_vm86(%x, %x, %x, %x)!\n", a1, a2, a3, a4);
-	return (-ENOSYS);
-}
-
-int sys_modify_ldt(int a1, int a2, int a3, int a4)
-{
-	printk(KERN_ERR "sys_modify_ldt(%x, %x, %x, %x)!\n", a1, a2, a3, a4);
-	return (-ENOSYS);
-}
-
 /*
  * sys_ipc() is the de-multiplexer for the SysV IPC calls..
  *
diff -urN linux-2.4.21-pre2/arch/ppc/kernel/time.c linux-2.4.21-pre3/arch/ppc/kernel/time.c
--- linux-2.4.21-pre2/arch/ppc/kernel/time.c	Thu Nov 28 15:53:11 2002
+++ linux-2.4.21-pre3/arch/ppc/kernel/time.c	Mon Jan  6 16:32:30 2003
@@ -295,13 +295,11 @@
 	write_unlock_irqrestore(&xtime_lock, flags);
 }
 
-
+/* This function is only called on the boot processor */
 void __init time_init(void)
 {
 	time_t sec, old_sec;
 	unsigned old_stamp, stamp, elapsed;
-	/* This function is only called on the boot processor */
-	unsigned long flags;
 
         if (ppc_md.time_init != NULL)
                 time_offset = ppc_md.time_init();
@@ -318,32 +316,33 @@
 	/* Now that the decrementer is calibrated, it can be used in case the 
 	 * clock is stuck, but the fact that we have to handle the 601
 	 * makes things more complex. Repeatedly read the RTC until the
-	 * next second boundary to try to achieve some precision...
+	 * next second boundary to try to achieve some precision.  If there
+	 * is no RTC, we still need to set tb_last_stamp and
+	 * last_jiffy_stamp(cpu 0) to the current stamp.
 	 */
+	stamp = get_native_tbl();
 	if (ppc_md.get_rtc_time) {
-		stamp = get_native_tbl();
 		sec = ppc_md.get_rtc_time();
 		elapsed = 0;
 		do {
 			old_stamp = stamp; 
 			old_sec = sec;
 			stamp = get_native_tbl();
-			if (__USE_RTC() && stamp < old_stamp) old_stamp -= 1000000000;
+			if (__USE_RTC() && stamp < old_stamp)
+				old_stamp -= 1000000000;
 			elapsed += stamp - old_stamp;
 			sec = ppc_md.get_rtc_time();
 		} while ( sec == old_sec && elapsed < 2*HZ*tb_ticks_per_jiffy);
-		if (sec==old_sec) {
+		if (sec == old_sec)
 			printk("Warning: real time clock seems stuck!\n");
-		}
-		write_lock_irqsave(&xtime_lock, flags);
 		xtime.tv_sec = sec;
-		last_jiffy_stamp(0) = tb_last_stamp = stamp;
 		xtime.tv_usec = 0;
 		/* No update now, we just read the time from the RTC ! */
 		last_rtc_update = xtime.tv_sec;
-		write_unlock_irqrestore(&xtime_lock, flags);
 	}
 
+	last_jiffy_stamp(0) = tb_last_stamp = stamp;
+
 	/* Not exact, but the timer interrupt takes care of this */
 	set_dec(tb_ticks_per_jiffy);
 
diff -urN linux-2.4.21-pre2/arch/ppc/platforms/chrp_setup.c linux-2.4.21-pre3/arch/ppc/platforms/chrp_setup.c
--- linux-2.4.21-pre2/arch/ppc/platforms/chrp_setup.c	Mon Jan  6 16:32:27 2003
+++ linux-2.4.21-pre3/arch/ppc/platforms/chrp_setup.c	Mon Jan  6 16:32:30 2003
@@ -411,8 +411,6 @@
 chrp_init_irq_openpic(unsigned long intack)
 {
 	int i;
-	unsigned char* chrp_int_ack_special = 0;
-	int nmi_irq = -1;
 	unsigned char init_senses[NR_IRQS - NUM_8259_INTERRUPTS];
 
 	chrp_find_openpic();
@@ -421,12 +419,14 @@
 	OpenPIC_InitSenses = init_senses;
 	OpenPIC_NumInitSenses = NR_IRQS - NUM_8259_INTERRUPTS;
 
-	if (intack)
-		chrp_int_ack_special = (unsigned char *) ioremap(intack, 1);
-	openpic_init(1, NUM_8259_INTERRUPTS, chrp_int_ack_special, nmi_irq);
+	openpic_init(NUM_8259_INTERRUPTS);
+	/* We have a cascade on OpenPIC IRQ 0, Linux IRQ 16 */
+	openpic_hookup_cascade(NUM_8259_INTERRUPTS, "82c59 cascade",
+			       i8259_irq);
+
 	for (i = 0; i < NUM_8259_INTERRUPTS; i++)
 		irq_desc[i].handler = &i8259_pic;
-	i8259_init(0);
+	i8259_init(intack);
 }
 
 static void __init
diff -urN linux-2.4.21-pre2/arch/ppc/platforms/gemini_setup.c linux-2.4.21-pre3/arch/ppc/platforms/gemini_setup.c
--- linux-2.4.21-pre2/arch/ppc/platforms/gemini_setup.c	Thu Nov 28 15:53:11 2002
+++ linux-2.4.21-pre3/arch/ppc/platforms/gemini_setup.c	Mon Jan  6 16:32:30 2003
@@ -118,13 +118,14 @@
 }
 
 static u_char gemini_openpic_initsenses[] = {
-	1,
-	1,
-	1,
-	1,
-	0,
-	0,
-	1, /* remainder are level-triggered */
+	(IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),	/* IRQ 0 */
+	(IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),	/* IRQ 1 */
+	(IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),	/* IRQ 2 */
+	(IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),	/* IRQ 3 */
+	(IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE),	/* IRQ 4 */
+	(IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE),	/* IRQ 5 */
+	(IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE),	/* IRQ 6 */
+	/* remainder are level-triggered */
 };
 
 #define GEMINI_MPIC_ADDR (0xfcfc0000)
@@ -330,7 +331,7 @@
 void __init gemini_init_IRQ(void)
 {
 	/* gemini has no 8259 */
-	openpic_init(1, 0, 0, -1);
+	openpic_init(0);
 }
 
 #define gemini_rtc_read(x)       (readb(GEMINI_RTC+(x)))
@@ -374,7 +375,7 @@
 		year = gemini_rtc_read(M48T35_RTC_YEAR);
 	} while( sec != gemini_rtc_read(M48T35_RTC_SECONDS));
 #ifdef DEBUG_RTC
-	printk("get rtc: sec=%x, min=%x, hour=%x, day=%x, mon=%x, year=%x\n", 
+	printk("get rtc: sec=%x, min=%x, hour=%x, day=%x, mon=%x, year=%x\n",
 	       sec, min, hour, day, mon, year);
 #endif
 
@@ -390,7 +391,7 @@
 	if ((year += 1900) < 1970)
 		year += 100;
 #ifdef DEBUG_RTC
-	printk("get rtc: sec=%x, min=%x, hour=%x, day=%x, mon=%x, year=%x\n", 
+	printk("get rtc: sec=%x, min=%x, hour=%x, day=%x, mon=%x, year=%x\n",
 	       sec, min, hour, day, mon, year);
 #endif
 
diff -urN linux-2.4.21-pre2/arch/ppc/platforms/pmac_pic.c linux-2.4.21-pre3/arch/ppc/platforms/pmac_pic.c
--- linux-2.4.21-pre2/arch/ppc/platforms/pmac_pic.c	Thu Nov 28 15:53:11 2002
+++ linux-2.4.21-pre3/arch/ppc/platforms/pmac_pic.c	Mon Jan  6 16:32:31 2003
@@ -358,26 +358,28 @@
 		printk("PowerMac using OpenPIC irq controller\n");
 		if (irqctrler->n_addrs > 0)
 		{
-			int nmi_irq = -1;
 			unsigned char senses[NR_IRQS];
-#ifdef CONFIG_XMON
-			struct device_node* pswitch;
 
-			pswitch = find_devices("programmer-switch");
-			if (pswitch && pswitch->n_intrs)
-				nmi_irq = pswitch->intrs[0].line;
-#endif /* CONFIG_XMON */
 			prom_get_irq_senses(senses, 0, NR_IRQS);
 			OpenPIC_InitSenses = senses;
 			OpenPIC_NumInitSenses = NR_IRQS;
 			ppc_md.get_irq = openpic_get_irq;
 			OpenPIC_Addr = ioremap(irqctrler->addrs[0].address,
 					       irqctrler->addrs[0].size);
-			openpic_init(1, 0, 0, nmi_irq);
+			openpic_init(0);
 #ifdef CONFIG_XMON
-			if (nmi_irq >= 0)
-				request_irq(nmi_irq, xmon_irq, 0,
-					    "NMI - XMON", 0);
+			{
+				struct device_node* pswitch;
+				int nmi_irq;
+
+				pswitch = find_devices("programmer-switch");
+				if (pswitch && pswitch->n_intrs) {
+					nmi_irq = pswitch->intrs[0].line;
+					openpic_init_nmi_irq(nmi_irq);
+					request_irq(nmi_irq, xmon_irq, 0,
+							"NMI - XMON", 0);
+				}
+			}
 #endif	/* CONFIG_XMON */
 			return;
 		}
diff -urN linux-2.4.21-pre2/arch/ppc/platforms/prep_pci.c linux-2.4.21-pre3/arch/ppc/platforms/prep_pci.c
--- linux-2.4.21-pre2/arch/ppc/platforms/prep_pci.c	Thu Nov 28 15:53:11 2002
+++ linux-2.4.21-pre3/arch/ppc/platforms/prep_pci.c	Mon Jan  6 16:32:31 2003
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.prep_pci.c 1.33 12/20/01 15:36:12 trini
+ * BK Id: %F% %I% %G% %U% %#%
  */
 /*
  * PReP pci functions.
@@ -665,22 +665,22 @@
 #define	MPIC_HAWK_ID		0x48030000
 #define	MOT_PROC2_BIT		0x800
 
-static u_char mvme2600_openpic_initsenses[] __initdata = {
-    1,	/* MVME2600_INT_SIO */
-    0,	/* MVME2600_INT_FALCN_ECC_ERR */
-    1,	/* MVME2600_INT_PCI_ETHERNET */
-    1,	/* MVME2600_INT_PCI_SCSI */
-    1,	/* MVME2600_INT_PCI_GRAPHICS */
-    1,	/* MVME2600_INT_PCI_VME0 */
-    1,	/* MVME2600_INT_PCI_VME1 */
-    1,	/* MVME2600_INT_PCI_VME2 */
-    1,	/* MVME2600_INT_PCI_VME3 */
-    1,	/* MVME2600_INT_PCI_INTA */
-    1,	/* MVME2600_INT_PCI_INTB */
-    1,	/* MVME2600_INT_PCI_INTC */
-    1,	/* MVME2600_INT_PCI_INTD */
-    1,	/* MVME2600_INT_LM_SIG0 */
-    1,	/* MVME2600_INT_LM_SIG1 */
+static u_char prep_openpic_initsenses[] __initdata = {
+    (IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE), /* MVME2600_INT_SIO */
+    (IRQ_SENSE_EDGE  | IRQ_POLARITY_NEGATIVE), /* MVME2600_INT_FALCN_ECC_ERR */
+    (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* MVME2600_INT_PCI_ETHERNET */
+    (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* MVME2600_INT_PCI_SCSI */
+    (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* MVME2600_INT_PCI_GRAPHICS */
+    (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* MVME2600_INT_PCI_VME0 */
+    (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* MVME2600_INT_PCI_VME1 */
+    (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* MVME2600_INT_PCI_VME2 */
+    (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* MVME2600_INT_PCI_VME3 */
+    (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* MVME2600_INT_PCI_INTA */
+    (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* MVME2600_INT_PCI_INTB */
+    (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* MVME2600_INT_PCI_INTC */
+    (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* MVME2600_INT_PCI_INTD */
+    (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* MVME2600_INT_LM_SIG0 */
+    (IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE), /* MVME2600_INT_LM_SIG1 */
 };
 
 #define MOT_RAVEN_PRESENT	0x1
@@ -737,8 +737,8 @@
 	/* Map the Raven MPIC registers to virtual memory. */
 	OpenPIC_Addr = ioremap(pci_membase+0xC0000000, 0x22000);
 
-	OpenPIC_InitSenses = mvme2600_openpic_initsenses;
-	OpenPIC_NumInitSenses = sizeof(mvme2600_openpic_initsenses);
+	OpenPIC_InitSenses = prep_openpic_initsenses;
+	OpenPIC_NumInitSenses = sizeof(prep_openpic_initsenses);
 
 	ppc_md.get_irq = openpic_get_irq;
 	
diff -urN linux-2.4.21-pre2/arch/ppc/platforms/prep_setup.c linux-2.4.21-pre3/arch/ppc/platforms/prep_setup.c
--- linux-2.4.21-pre2/arch/ppc/platforms/prep_setup.c	Mon Jan  6 16:32:27 2003
+++ linux-2.4.21-pre3/arch/ppc/platforms/prep_setup.c	Mon Jan  6 16:32:31 2003
@@ -2,7 +2,7 @@
  * BK Id: %F% %I% %G% %U% %#%
  */
 /*
- *  linux/arch/ppc/kernel/setup.c
+ *  arch/ppc/platforms/setup.c
  *
  *  Copyright (C) 1995  Linus Torvalds
  *  Adapted from 'alpha' version by Gary Thomas
@@ -59,6 +59,7 @@
 #include <asm/time.h>
 #include <asm/i8259.h>
 #include <asm/open_pic.h>
+#include <asm/pci-bridge.h>
 
 #if defined(CONFIG_SOUND) || defined(CONFIG_SOUND_MODULE)
 #include <../drivers/sound/sound_config.h>
@@ -621,12 +622,28 @@
 prep_init_IRQ(void)
 {
 	int i;
+	unsigned int pci_viddid, pci_did;
 
-	if (OpenPIC_Addr != NULL)
-		openpic_init(1, NUM_8259_INTERRUPTS, 0, -1);
-	for ( i = 0 ; i < NUM_8259_INTERRUPTS ; i++ )
+	if (OpenPIC_Addr != NULL) {
+		openpic_init(NUM_8259_INTERRUPTS);
+		/* We have a cascade on OpenPIC IRQ 0, Linux IRQ 16 */
+		openpic_hookup_cascade(NUM_8259_INTERRUPTS, "82c59 cascade",
+				       i8259_irq);
+	}
+	for (i = 0; i < NUM_8259_INTERRUPTS; i++)
 		irq_desc[i].handler = &i8259_pic;
-	i8259_init(0xbffffff0); /* PCI interrupt ack address for MPC105 and 106 */
+	 /* If we have a Raven PCI bridge or a Hawk PCI bridge / Memory
+	  * controller, we poll (as they have a different int-ack address). */
+	early_read_config_dword(0, 0, 0, PCI_VENDOR_ID, &pci_viddid);
+	pci_did = (pci_viddid & 0xffff0000) >> 16;
+	if (((pci_viddid & 0xffff) == PCI_VENDOR_ID_MOTOROLA)
+			&& ((pci_did == PCI_DEVICE_ID_MOTOROLA_RAVEN)
+				|| (pci_did == PCI_DEVICE_ID_MOTOROLA_HAWK)))
+		i8259_init(0);
+	else
+		/* PCI interrupt ack address given in section 6.1.8 of the
+		 * PReP specification. */
+		i8259_init(0xbffffff0);
 }
 
 #if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE)
diff -urN linux-2.4.21-pre2/arch/sparc64/kernel/ioctl32.c linux-2.4.21-pre3/arch/sparc64/kernel/ioctl32.c
--- linux-2.4.21-pre2/arch/sparc64/kernel/ioctl32.c	Thu Nov 28 15:53:12 2002
+++ linux-2.4.21-pre3/arch/sparc64/kernel/ioctl32.c	Mon Jan  6 16:32:31 2003
@@ -95,6 +95,7 @@
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci.h>
+#include <net/bluetooth/rfcomm.h>
 
 #include <linux/usb.h>
 #include <linux/usbdevice_fs.h>
@@ -4286,6 +4287,15 @@
 	return sys_ioctl(fd, BLKGETSIZE64, arg);
 }
 
+/* Bluetooth ioctls */
+#define HCIUARTSETPROTO	_IOW('U', 200, int)
+#define HCIUARTGETPROTO	_IOR('U', 201, int)
+
+#define BNEPCONNADD	_IOW('B', 200, int)
+#define BNEPCONNDEL	_IOW('B', 201, int)
+#define BNEPGETCONNLIST	_IOR('B', 210, int)
+#define BNEPGETCONNINFO	_IOR('B', 211, int)
+
 struct ioctl_trans {
 	unsigned int cmd;
 	unsigned int handler;
@@ -4987,6 +4997,17 @@
 COMPATIBLE_IOCTL(HCISETACLMTU)
 COMPATIBLE_IOCTL(HCISETSCOMTU)
 COMPATIBLE_IOCTL(HCIINQUIRY)
+COMPATIBLE_IOCTL(HCIUARTSETPROTO)
+COMPATIBLE_IOCTL(HCIUARTGETPROTO)
+COMPATIBLE_IOCTL(RFCOMMCREATEDEV)
+COMPATIBLE_IOCTL(RFCOMMRELEASEDEV)
+COMPATIBLE_IOCTL(RFCOMMGETDEVLIST)
+COMPATIBLE_IOCTL(RFCOMMGETDEVINFO)
+COMPATIBLE_IOCTL(RFCOMMSTEALDLC)
+COMPATIBLE_IOCTL(BNEPCONNADD)
+COMPATIBLE_IOCTL(BNEPCONNDEL)
+COMPATIBLE_IOCTL(BNEPGETCONNLIST)
+COMPATIBLE_IOCTL(BNEPGETCONNINFO)
 /* Misc. */
 COMPATIBLE_IOCTL(0x41545900)		/* ATYIO_CLKR */
 COMPATIBLE_IOCTL(0x41545901)		/* ATYIO_CLKW */
diff -urN linux-2.4.21-pre2/drivers/atm/iphase.c linux-2.4.21-pre3/drivers/atm/iphase.c
--- linux-2.4.21-pre2/drivers/atm/iphase.c	Thu Nov 28 15:53:12 2002
+++ linux-2.4.21-pre3/drivers/atm/iphase.c	Mon Jan  6 16:32:31 2003
@@ -1192,7 +1192,8 @@
 
 	/* Build the DLE structure */  
 	wr_ptr = iadev->rx_dle_q.write;  
-	wr_ptr->sys_pkt_addr = virt_to_bus(skb->data);	  
+	wr_ptr->sys_pkt_addr = pci_map_single(iadev->pci, skb->data,
+		len, PCI_DMA_FROMDEVICE);
 	wr_ptr->local_pkt_addr = buf_addr;  
 	wr_ptr->bytes = len;	/* We don't know this do we ?? */  
 	wr_ptr->mode = DMA_INT_ENABLE;  
@@ -1311,6 +1312,9 @@
           struct cpcs_trailer *trailer;
           u_short length;
           struct ia_vcc *ia_vcc;
+
+	  pci_unmap_single(iadev->pci, iadev->rx_dle_q.write->sys_pkt_addr,
+	  	len, PCI_DMA_FROMDEVICE);
           /* no VCC related housekeeping done as yet. lets see */  
           vcc = ATM_SKB(skb)->vcc;
 	  if (!vcc) {
@@ -1430,7 +1434,7 @@
 	IADEV *iadev;  
 	struct rx_buf_desc *buf_desc_ptr;  
 	unsigned long rx_pkt_start = 0;  
-	u32 *odle_addr, *dle_addr;  
+	void *dle_addr;  
 	struct abr_vc_table  *abr_vc_table; 
 	u16 *vc_table;  
 	u16 *reass_table;  
@@ -1441,25 +1445,14 @@
   
 	iadev = INPH_IA_DEV(dev);  
   //    spin_lock_init(&iadev->rx_lock); 
-	/* I need to initialize the DLEs somewhere. Lets see what I   
-		need to do for this, hmmm...  
-		- allocate memory for 256 DLEs. make sure that it starts  
-		on a 4k byte address boundary. Program the start address   
-		in Receive List address register.  ..... to do for TX also  
-	   To make sure that it is a 4k byte boundary - allocate 8k and find   
-		4k byte boundary within.  
-		( (addr + (4k-1)) & ~(4k-1) )  
-	*/   
   
-	/* allocate 8k bytes */  
-	odle_addr = kmalloc(2*sizeof(struct dle)*DLE_ENTRIES, GFP_KERNEL);  
-	if (!odle_addr)  
-	{  
-		printk(KERN_ERR DEV_LABEL "can't allocate DLEs\n");  
-		return -ENOMEM;
-	}  
-	/* find 4k byte boundary within the 8k allocated */  
-	dle_addr = (u32*)( ((u32)odle_addr+(4096-1)) & ~(4096-1) );  
+	/* Allocate 4k bytes - more aligned than needed (4k boundary) */
+	dle_addr = pci_alloc_consistent(iadev->pci, DLE_TOTAL_SIZE,
+					&iadev->rx_dle_dma);  
+	if (!dle_addr)  {  
+		printk(KERN_ERR DEV_LABEL "can't allocate DLEs\n");
+		goto err_out;
+	}
 	iadev->rx_dle_q.start = (struct dle*)dle_addr;  
 	iadev->rx_dle_q.read = iadev->rx_dle_q.start;  
 	iadev->rx_dle_q.write = iadev->rx_dle_q.start;  
@@ -1468,7 +1461,8 @@
 	DLE that can be used. */  
   
 	/* write the upper 20 bits of the start address to rx list address register */  
-	writel(virt_to_bus(dle_addr) & 0xfffff000, iadev->dma+IPHASE5575_RX_LIST_ADDR);  
+	writel(iadev->rx_dle_dma & 0xfffff000,
+	       iadev->dma + IPHASE5575_RX_LIST_ADDR);  
 	IF_INIT(printk("Tx Dle list addr: 0x%08x value: 0x%0x\n", 
                       (u32)(iadev->dma+IPHASE5575_TX_LIST_ADDR), 
                       *(u32*)(iadev->dma+IPHASE5575_TX_LIST_ADDR));  
@@ -1642,8 +1636,7 @@
 	{  
 		printk(KERN_ERR DEV_LABEL "itf %d couldn't get free page\n",
 		dev->number);  
-		kfree(odle_addr);
-		return -ENOMEM;  
+		goto err_free_dle;
 	}  
 	memset(iadev->rx_open, 0, 4*iadev->num_vc);  
         iadev->rxing = 1;
@@ -1651,6 +1644,12 @@
 	/* Mode Register */  
 	writew(R_ONLINE, iadev->reass_reg+MODE_REG);  
 	return 0;  
+
+err_free_dle:
+	pci_free_consistent(iadev->pci, DLE_TOTAL_SIZE, iadev->rx_dle_q.start,
+			    iadev->rx_dle_dma);  
+err_out:
+	return -ENOMEM;
 }  
   
 
@@ -1715,6 +1714,12 @@
             /* free the DMAed skb */ 
             skb = skb_dequeue(&iadev->tx_dma_q); 
             if (!skb) break;
+
+	    /* Revenge of the 2 dle (skb + trailer) used in ia_pkt_tx() */
+	    if (!((dle - iadev->tx_dle_q.start)%(2*sizeof(struct dle)))) {
+		pci_unmap_single(iadev->pci, dle->sys_pkt_addr, skb->len,
+				 PCI_DMA_TODEVICE);
+	    }
             vcc = ATM_SKB(skb)->vcc;
             if (!vcc) {
                   printk("tx_dle_intr: vcc is null\n");
@@ -1907,7 +1912,7 @@
 	IADEV *iadev;  
 	struct tx_buf_desc *buf_desc_ptr;
 	unsigned int tx_pkt_start;  
-	u32 *dle_addr;  
+	void *dle_addr;  
 	int i;  
 	u_short tcq_st_adr;  
 	u_short *tcq_start;  
@@ -1923,24 +1928,22 @@
  
 	IF_INIT(printk("Tx MASK REG: 0x%0x\n", 
                                 readw(iadev->seg_reg+SEG_MASK_REG));)  
-	/*---------- Initializing Transmit DLEs ----------*/  
-	/* allocating 8k memory for transmit DLEs */  
-	dle_addr = kmalloc(2*sizeof(struct dle)*DLE_ENTRIES, GFP_KERNEL);  
-	if (!dle_addr)  
-	{  
-		printk(KERN_ERR DEV_LABEL "can't allocate TX DLEs\n");  
-		return -ENOMEM;
-	}  
-  
-	/* find 4k byte boundary within the 8k allocated */  
-	dle_addr = (u32*)(((u32)dle_addr+(4096-1)) & ~(4096-1));  
+
+	/* Allocate 4k (boundary aligned) bytes */
+	dle_addr = pci_alloc_consistent(iadev->pci, DLE_TOTAL_SIZE,
+					&iadev->tx_dle_dma);  
+	if (!dle_addr)  {
+		printk(KERN_ERR DEV_LABEL "can't allocate DLEs\n");
+		goto err_out;
+	}
 	iadev->tx_dle_q.start = (struct dle*)dle_addr;  
 	iadev->tx_dle_q.read = iadev->tx_dle_q.start;  
 	iadev->tx_dle_q.write = iadev->tx_dle_q.start;  
 	iadev->tx_dle_q.end = (struct dle*)((u32)dle_addr+sizeof(struct dle)*DLE_ENTRIES);  
 
 	/* write the upper 20 bits of the start address to tx list address register */  
-	writel(virt_to_bus(dle_addr) & 0xfffff000, iadev->dma+IPHASE5575_TX_LIST_ADDR);  
+	writel(iadev->tx_dle_dma & 0xfffff000,
+	       iadev->dma + IPHASE5575_TX_LIST_ADDR);  
 	writew(0xffff, iadev->seg_reg+SEG_MASK_REG);  
 	writew(0, iadev->seg_reg+MODE_REG_0);  
 	writew(RESET_SEG, iadev->seg_reg+SEG_COMMAND_REG);  
@@ -1984,23 +1987,28 @@
 		buf_desc_ptr++;		  
 		tx_pkt_start += iadev->tx_buf_sz;  
 	}  
-        iadev->tx_buf = kmalloc(iadev->num_tx_desc*sizeof(caddr_t), GFP_KERNEL);
+        iadev->tx_buf = kmalloc(iadev->num_tx_desc*sizeof(struct cpcs_trailer_desc), GFP_KERNEL);
         if (!iadev->tx_buf) {
             printk(KERN_ERR DEV_LABEL " couldn't get mem\n");
-            return -EAGAIN;
+	    goto err_free_dle;
         }
        	for (i= 0; i< iadev->num_tx_desc; i++)
        	{
+	    struct cpcs_trailer *cpcs;
  
-       	    iadev->tx_buf[i] = kmalloc(sizeof(struct cpcs_trailer),
-                                                           GFP_KERNEL|GFP_DMA);
-            if(!iadev->tx_buf[i]) {                
+       	    cpcs = kmalloc(sizeof(*cpcs), GFP_KERNEL|GFP_DMA);
+            if(!cpcs) {                
 		printk(KERN_ERR DEV_LABEL " couldn't get freepage\n"); 
-         	return -EAGAIN;
+		goto err_free_tx_bufs;
             }
+	    iadev->tx_buf[i].cpcs = cpcs;
+	    iadev->tx_buf[i].dma_addr = pci_map_single(iadev->pci,
+		cpcs, sizeof(*cpcs), PCI_DMA_TODEVICE);
         }
         iadev->desc_tbl = kmalloc(iadev->num_tx_desc *
                                    sizeof(struct desc_tbl_t), GFP_KERNEL);
+        if(!iadev->desc_tbl)
+		goto err_free_all_tx_bufs;
   
 	/* Communication Queues base address */  
         i = TX_COMP_Q * iadev->memSize;
@@ -2128,7 +2136,7 @@
         iadev->testTable = kmalloc(sizeof(long)*iadev->num_vc, GFP_KERNEL); 
         if (!iadev->testTable) {
            printk("Get freepage  failed\n");
-           return -EAGAIN; 
+	   goto err_free_desc_tbl;
         }
 	for(i=0; i<iadev->num_vc; i++)  
 	{  
@@ -2137,7 +2145,7 @@
                 iadev->testTable[i] = kmalloc(sizeof(struct testTable_t),
 						GFP_KERNEL);
 		if (!iadev->testTable[i])
-			return -ENOMEM;
+			goto err_free_test_tables;
               	iadev->testTable[i]->lastTime = 0;
  		iadev->testTable[i]->fract = 0;
                 iadev->testTable[i]->vc_status = VC_UBR;
@@ -2193,7 +2201,30 @@
         iadev->tx_pkt_cnt = 0;
         iadev->rate_limit = iadev->LineRate / 3;
   
-	return 0;  
+	return 0;
+
+err_free_test_tables:
+	while (--i >= 0)
+		kfree(iadev->testTable[i]);
+	kfree(iadev->testTable);
+err_free_desc_tbl:
+	kfree(iadev->desc_tbl);
+err_free_all_tx_bufs:
+	i = iadev->num_tx_desc;
+err_free_tx_bufs:
+	while (--i >= 0) {
+		struct cpcs_trailer_desc *desc = iadev->tx_buf + i;
+
+		pci_unmap_single(iadev->pci, desc->dma_addr,
+			sizeof(*desc->cpcs), PCI_DMA_TODEVICE);
+		kfree(desc->cpcs);
+	}
+	kfree(iadev->tx_buf);
+err_free_dle:
+	pci_free_consistent(iadev->pci, DLE_TOTAL_SIZE, iadev->tx_dle_q.start,
+			    iadev->tx_dle_dma);  
+err_out:
+	return -ENOMEM;
 }   
    
 static void ia_int(int irq, void *dev_id, struct pt_regs *regs)  
@@ -2455,6 +2486,33 @@
 	return readl(INPH_IA_DEV(dev)->phy+addr);  
 }  
 
+static void ia_free_tx(IADEV *iadev)
+{
+	int i;
+
+	kfree(iadev->desc_tbl);
+	for (i = 0; i < iadev->num_vc; i++)
+		kfree(iadev->testTable[i]);
+	kfree(iadev->testTable);
+	for (i = 0; i < iadev->num_tx_desc; i++) {
+		struct cpcs_trailer_desc *desc = iadev->tx_buf + i;
+
+		pci_unmap_single(iadev->pci, desc->dma_addr,
+			sizeof(*desc->cpcs), PCI_DMA_TODEVICE);
+		kfree(desc->cpcs);
+	}
+	kfree(iadev->tx_buf);
+	pci_free_consistent(iadev->pci, DLE_TOTAL_SIZE, iadev->tx_dle_q.start,
+			    iadev->tx_dle_dma);  
+}
+
+static void ia_free_rx(IADEV *iadev)
+{
+	kfree(iadev->rx_open);
+	pci_free_consistent(iadev->pci, DLE_TOTAL_SIZE, iadev->rx_dle_q.start,
+			    iadev->rx_dle_dma);  
+}
+
 #if LINUX_VERSION_CODE >= 0x20312
 static int __init ia_start(struct atm_dev *dev)
 #else
@@ -2462,7 +2520,7 @@
 #endif  
 {  
 	IADEV *iadev;  
-	int error = 1;  
+	int error;  
 	unsigned char phy;  
 	u32 ctrl_reg;  
 	IF_EVENT(printk(">ia_start\n");)  
@@ -2470,7 +2528,8 @@
         if (request_irq(iadev->irq, &ia_int, SA_SHIRQ, DEV_LABEL, dev)) {  
                 printk(KERN_ERR DEV_LABEL "(itf %d): IRQ%d is already in use\n",  
                     dev->number, iadev->irq);  
-                return -EAGAIN;  
+		error = -EAGAIN;
+		goto err_out;
         }  
         /* @@@ should release IRQ on error */  
 	/* enabling memory + master */  
@@ -2480,8 +2539,8 @@
 	{  
                 printk(KERN_ERR DEV_LABEL "(itf %d): can't enable memory+"  
                     "master (0x%x)\n",dev->number, error);  
-                free_irq(iadev->irq, dev); 
-                return -EIO;  
+		error = -EIO;  
+		goto err_free_irq;
         }  
 	udelay(10);  
   
@@ -2515,15 +2574,11 @@
     
         ia_hw_type(iadev); 
 	error = tx_init(dev);  
-	if (error) {
-           free_irq(iadev->irq, dev);  
-           return error;
-        }  
+	if (error)
+		goto err_free_irq;
 	error = rx_init(dev);  
-	if (error) {
-          free_irq(iadev->irq, dev); 
-          return error;  
-        }
+	if (error)
+		goto err_free_tx;
   
 	ctrl_reg = readl(iadev->reg+IPHASE5575_BUS_CONTROL_REG);  
        	writel(ctrl_reg | CTRL_FE_RST, iadev->reg+IPHASE5575_BUS_CONTROL_REG);   
@@ -2538,33 +2593,36 @@
 
         if (iadev->phy_type &  FE_25MBIT_PHY) {
            ia_mb25_init(iadev);
-           return 0;
-        }
-        if (iadev->phy_type & (FE_DS3_PHY | FE_E3_PHY)) {
+        } else if (iadev->phy_type & (FE_DS3_PHY | FE_E3_PHY)) {
            ia_suni_pm7345_init(iadev);
-           return 0;
-        }
-
-	error = suni_init(dev);  
-	if (error) {
-          free_irq(iadev->irq, dev); 
-          return error;  
-        }
-
-        /* Enable interrupt on loss of signal SUNI_RSOP_CIE 0x10
-           SUNI_RSOP_CIE_LOSE - 0x04
-        */
-        ia_phy_put(dev, ia_phy_get(dev,0x10) | 0x04, 0x10);         
+        } else {
+		error = suni_init(dev);
+		if (error)
+			goto err_free_rx;
+		/* 
+		 * Enable interrupt on loss of signal
+		 * SUNI_RSOP_CIE - 0x10
+		 * SUNI_RSOP_CIE_LOSE - 0x04
+		 */
+		ia_phy_put(dev, ia_phy_get(dev, 0x10) | 0x04, 0x10);
 #ifndef MODULE
-	error = dev->phy->start(dev);  
-	if (error) {
-          free_irq(iadev->irq, dev);
-          return error;
-        }   
+		error = dev->phy->start(dev);
+		if (error)
+			goto err_free_rx;
 #endif
-        /* Get iadev->carrier_detect status */
-        IaFrontEndIntr(iadev);
-	return 0;  
+		/* Get iadev->carrier_detect status */
+		IaFrontEndIntr(iadev);
+	}
+	return 0;
+
+err_free_rx:
+	ia_free_rx(iadev);
+err_free_tx:
+	ia_free_tx(iadev);
+err_free_irq:
+	free_irq(iadev->irq, dev);  
+err_out:
+	return error;
 }  
   
 static void ia_close(struct atm_vcc *vcc)  
@@ -2873,10 +2931,10 @@
         struct tx_buf_desc *buf_desc_ptr;
         int desc;
         int comp_code;
-        unsigned int addr;
         int total_len, pad, last;
         struct cpcs_trailer *trailer;
         struct ia_vcc *iavcc;
+
         iadev = INPH_IA_DEV(vcc->dev);  
         iavcc = INPH_IA_VCC(vcc);
         if (!iavcc->txing) {
@@ -2961,13 +3019,9 @@
 	IF_TX(printk("ia packet len:%d padding:%d\n", total_len, pad);)  
  
 	/* Put the packet in a tx buffer */   
-	if (!iadev->tx_buf[desc-1])  
-		printk("couldn't get free page\n");  
-
+	trailer = iadev->tx_buf[desc-1].cpcs;
         IF_TX(printk("Sent: skb = 0x%x skb->data: 0x%x len: %d, desc: %d\n",
                   (u32)skb, (u32)skb->data, skb->len, desc);)
-        addr = virt_to_bus(skb->data);
-	trailer = (struct cpcs_trailer*)iadev->tx_buf[desc-1];  
 	trailer->control = 0; 
         /*big endian*/ 
 	trailer->length = ((skb->len & 0xff) << 8) | ((skb->len & 0xff00) >> 8);
@@ -2983,6 +3037,7 @@
 	buf_desc_ptr = (struct tx_buf_desc *)(iadev->seg_ram+TX_DESC_BASE);  
 	buf_desc_ptr += desc;	/* points to the corresponding entry */  
 	buf_desc_ptr->desc_mode = AAL5 | EOM_EN | APP_CRC32 | CMPL_INT;   
+	/* Huh ? p.115 of users guide describes this as a read-only register */
         writew(TRANSMIT_DONE, iadev->seg_reg+SEG_INTR_STATUS_REG);
 	buf_desc_ptr->vc_index = vcc->vci;
 	buf_desc_ptr->bytes = total_len;  
@@ -2993,7 +3048,8 @@
 	/* Build the DLE structure */  
 	wr_ptr = iadev->tx_dle_q.write;  
 	memset((caddr_t)wr_ptr, 0, sizeof(*wr_ptr));  
-	wr_ptr->sys_pkt_addr = addr;  
+	wr_ptr->sys_pkt_addr = pci_map_single(iadev->pci, skb->data,
+		skb->len, PCI_DMA_TODEVICE);
 	wr_ptr->local_pkt_addr = (buf_desc_ptr->buf_start_hi << 16) | 
                                                   buf_desc_ptr->buf_start_lo;  
 	/* wr_ptr->bytes = swap(total_len);	didn't seem to affect ?? */  
@@ -3011,7 +3067,7 @@
 		wr_ptr = iadev->tx_dle_q.start;  
         
         /* Build trailer dle */
-        wr_ptr->sys_pkt_addr = virt_to_bus(iadev->tx_buf[desc-1]);
+        wr_ptr->sys_pkt_addr = iadev->tx_buf[desc-1].dma_addr;
         wr_ptr->local_pkt_addr = ((buf_desc_ptr->buf_start_hi << 16) | 
           buf_desc_ptr->buf_start_lo) + total_len - sizeof(struct cpcs_trailer);
 
@@ -3163,135 +3219,130 @@
 };  
 	  
   
-#if LINUX_VERSION_CODE >= 0x20312
-int __init ia_detect(void)
-#else
-__initfunc(int ia_detect(void)) 
-#endif 
+static int __devinit ia_init_one(struct pci_dev *pdev,
+				 const struct pci_device_id *ent)
 {  
 	struct atm_dev *dev;  
 	IADEV *iadev;  
         unsigned long flags;
-        int index = 0;  
-	struct pci_dev *prev_dev;       
-	if (!pci_present()) {  
-		printk(KERN_ERR DEV_LABEL " driver but no PCI BIOS ?\n");  
-		return 0;  
-	}  
+	int ret;
+
 	iadev = kmalloc(sizeof(*iadev), GFP_KERNEL); 
-	if (!iadev) return -ENOMEM;  
-        memset((char*)iadev, 0, sizeof(*iadev));
-	prev_dev = NULL;  
-	while((iadev->pci = pci_find_device(PCI_VENDOR_ID_IPHASE,  
-		PCI_DEVICE_ID_IPHASE_5575,  prev_dev))) {  
-		IF_INIT(printk("ia detected at bus:%d dev: %d function:%d\n",
-                     iadev->pci->bus->number, PCI_SLOT(iadev->pci->devfn), 
-                                                 PCI_FUNC(iadev->pci->devfn));)  
-		if (pci_enable_device(iadev->pci)) break;
-		dev = atm_dev_register(DEV_LABEL, &ops, -1, NULL);
-		if (!dev) break;  
-		IF_INIT(printk(DEV_LABEL "registered at (itf :%d)\n", 
-                                                             dev->number);)  
-		INPH_IA_DEV(dev) = iadev; 
-                // TODO: multi_board using ia_boards logic in cleanup_module
-		ia_dev[index] = iadev;
-		_ia_dev[index] = dev;
-                IF_INIT(printk("dev_id = 0x%x iadev->LineRate = %d \n", 
-                                        (u32)dev, iadev->LineRate);)
-                iadev_count++;
-                spin_lock_init(&iadev->misc_lock);
-                spin_lock_irqsave(&iadev->misc_lock, flags); 
-		if (ia_init(dev) || ia_start(dev)) {  
-			atm_dev_deregister(dev);  
-                        IF_INIT(printk("IA register failed!\n");)
-                        ia_dev[index] = NULL;
-                        _ia_dev[index] = NULL;
-                        iadev_count--;
-                        spin_unlock_irqrestore(&iadev->misc_lock, flags); 
-			return -EINVAL;  
-		}  
-                spin_unlock_irqrestore(&iadev->misc_lock, flags);
-                IF_EVENT(printk("iadev_count = %d\n", iadev_count);)
-		prev_dev = iadev->pci;  
-		iadev->next_board = ia_boards;  
-		ia_boards = dev;  
-		iadev = kmalloc(sizeof(*iadev), GFP_KERNEL);  
-		if (!iadev) break;   
-                memset((char*)iadev, 0, sizeof(*iadev)); 
-		index++;  
-                dev = NULL;
-	}  
-	return index;  
-}  
-  
+	if (!iadev) {
+		ret = -ENOMEM;
+		goto err_out;
+	}
+	memset(iadev, 0, sizeof(*iadev));
+	iadev->pci = pdev;
 
-#ifdef MODULE  
-  
-int init_module(void)  
-{  
-	IF_EVENT(printk(">ia init_module\n");)  
-	if (!ia_detect()) {  
-		printk(KERN_ERR DEV_LABEL ": no adapter found\n");  
-		return -ENXIO;  
-	}  
-   	ia_timer.expires = jiffies + 3*HZ;
-   	add_timer(&ia_timer); 
-   
-	return 0;  
-}  
-  
-  
-void cleanup_module(void)  
-{  
-	struct atm_dev *dev;  
-	IADEV *iadev;  
-	unsigned short command;  
-        int i, j= 0;
- 
-	IF_EVENT(printk(">ia cleanup_module\n");)  
-	if (MOD_IN_USE)  
-		printk("ia: module in use\n");  
-        del_timer(&ia_timer);
-	while(ia_dev[j])  
-	{  
-		dev = ia_boards;  
-		iadev = INPH_IA_DEV(dev);  
-		ia_boards = iadev->next_board;  
-                
-        	/* disable interrupt of lost signal */
-        	ia_phy_put(dev, ia_phy_get(dev,0x10) & ~(0x4), 0x10); 
-        	udelay(1);
-
-      		/* De-register device */  
-      		atm_dev_deregister(dev);  
-		IF_EVENT(printk("iav deregistered at (itf:%d)\n", dev->number);)
-		for (i= 0; i< iadev->num_tx_desc; i++)
-                        kfree(iadev->tx_buf[i]);
-                kfree(iadev->tx_buf);
-      		/* Disable memory mapping and busmastering */  
-		if (pci_read_config_word(iadev->pci,  
-					     PCI_COMMAND, &command) != 0)  
-      		{  
-         		printk("ia: can't read PCI_COMMAND.\n");  
-      		}  
-      		command &= ~(PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);  
-      		if (pci_write_config_word(iadev->pci,  
-					      PCI_COMMAND, command) != 0)  
-      		{  
-         		printk("ia: can't write PCI_COMMAND.\n");  
-      		}  
-      		free_irq(iadev->irq, dev);  
-      		iounmap((void *) iadev->base);  
-      		kfree(iadev);  
-                j++;
-	}  
-	/* and voila whatever we tried seems to work. I don't know if it will  
-		fix suni errors though. Really doubt that. */  
-        for (i = 0; i<8; i++) {
-               ia_dev[i] =  NULL;
-              _ia_dev[i] = NULL;
-        }
-}  
+	IF_INIT(printk("ia detected at bus:%d dev: %d function:%d\n",
+		pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));)
+	if (pci_enable_device(pdev)) {
+		ret = -ENODEV;
+		goto err_out_free_iadev;
+	}
+	dev = atm_dev_register(DEV_LABEL, &ops, -1, NULL);
+	if (!dev) {
+		ret = -ENOMEM;
+		goto err_out_disable_dev;
+	}
+	INPH_IA_DEV(dev) = iadev; 
+	IF_INIT(printk(DEV_LABEL "registered at (itf :%d)\n", dev->number);)
+	IF_INIT(printk("dev_id = 0x%x iadev->LineRate = %d \n", (u32)dev,
+		iadev->LineRate);)
+
+	ia_dev[iadev_count] = iadev;
+	_ia_dev[iadev_count] = dev;
+	iadev_count++;
+	spin_lock_init(&iadev->misc_lock);
+	/* First fixes first. I don't want to think about this now. */
+	spin_lock_irqsave(&iadev->misc_lock, flags); 
+	if (ia_init(dev) || ia_start(dev)) {  
+		IF_INIT(printk("IA register failed!\n");)
+		iadev_count--;
+		ia_dev[iadev_count] = NULL;
+		_ia_dev[iadev_count] = NULL;
+		spin_unlock_irqrestore(&iadev->misc_lock, flags); 
+		ret = -EINVAL;
+		goto err_out_deregister_dev;
+	}
+	spin_unlock_irqrestore(&iadev->misc_lock, flags); 
+	IF_EVENT(printk("iadev_count = %d\n", iadev_count);)
 
-#endif  
+	iadev->next_board = ia_boards;  
+	ia_boards = dev;  
+
+	pci_set_drvdata(pdev, dev);
+
+	return 0;
+
+err_out_deregister_dev:
+	atm_dev_deregister(dev);  
+err_out_disable_dev:
+	pci_disable_device(pdev);
+err_out_free_iadev:
+	kfree(iadev);
+err_out:
+	return ret;
+}
+
+static void __devexit ia_remove_one(struct pci_dev *pdev)
+{
+	struct atm_dev *dev = pci_get_drvdata(pdev);
+	IADEV *iadev = INPH_IA_DEV(dev);
+
+	ia_phy_put(dev, ia_phy_get(dev,0x10) & ~(0x4), 0x10); 
+	udelay(1);
+
+	/* De-register device */  
+      	free_irq(iadev->irq, dev);
+	iadev_count--;
+	ia_dev[iadev_count] = NULL;
+	_ia_dev[iadev_count] = NULL;
+	atm_dev_deregister(dev);
+	IF_EVENT(printk("iav deregistered at (itf:%d)\n", dev->number);)
+
+      	iounmap((void *) iadev->base);  
+	pci_disable_device(pdev);
+
+	ia_free_rx(iadev);
+	ia_free_tx(iadev);
+
+      	kfree(iadev);
+}
+
+static struct pci_device_id ia_pci_tbl[] __devinitdata = {
+	{ PCI_VENDOR_ID_IPHASE, 0x0008, PCI_ANY_ID, PCI_ANY_ID, },
+	{ PCI_VENDOR_ID_IPHASE, 0x0009, PCI_ANY_ID, PCI_ANY_ID, },
+	{ 0,}
+};
+MODULE_DEVICE_TABLE(pci, ia_pci_tbl);
+
+static struct pci_driver ia_driver = {
+	.name =         DEV_LABEL,
+	.id_table =     ia_pci_tbl,
+	.probe =        ia_init_one,
+	.remove =       ia_remove_one,
+};
+
+static int __init ia_init_module(void)
+{
+	int ret;
+
+	ret = pci_module_init(&ia_driver);
+	if (ret >= 0) {
+		ia_timer.expires = jiffies + 3*HZ;
+		add_timer(&ia_timer); 
+	}
+	return ret;
+}
+
+static void __exit ia_cleanup_module(void)
+{
+	pci_unregister_driver(&ia_driver);
+
+        del_timer(&ia_timer);
+}
 
+module_init(ia_init_module);
+module_exit(ia_cleanup_module);
diff -urN linux-2.4.21-pre2/drivers/atm/iphase.h linux-2.4.21-pre3/drivers/atm/iphase.h
--- linux-2.4.21-pre2/drivers/atm/iphase.h	Sun Feb  4 10:05:29 2001
+++ linux-2.4.21-pre3/drivers/atm/iphase.h	Mon Jan  6 16:32:31 2003
@@ -200,7 +200,13 @@
 	u_short length;  
 	u_int	crc32;  
 };  
-  
+
+struct cpcs_trailer_desc
+{
+	struct cpcs_trailer *cpcs;
+	dma_addr_t dma_addr;
+};
+
 struct ia_vcc 
 { 
 	int rxing;	 
@@ -272,6 +278,7 @@
 #define DLE_ENTRIES 256  
 #define DMA_INT_ENABLE 0x0002	/* use for both Tx and Rx */  
 #define TX_DLE_PSI 0x0001  
+#define DLE_TOTAL_SIZE (sizeof(struct dle)*DLE_ENTRIES)
   
 /* Descriptor List Entries (DLE) */  
 struct dle 
@@ -1017,7 +1024,7 @@
         struct wait_queue     *close_wait;
         struct wait_queue     *timeout_wait;
 #endif
-	caddr_t *tx_buf;  
+	struct cpcs_trailer_desc *tx_buf;
         u16 num_tx_desc, tx_buf_sz, rate_limit;
         u32 tx_cell_cnt, tx_pkt_cnt;
         u32 MAIN_VC_TABLE_ADDR, EXT_VC_TABLE_ADDR, ABR_SCHED_TABLE_ADDR;
@@ -1063,7 +1070,9 @@
         struct desc_tbl_t *desc_tbl;
         u_short host_tcq_wr;
         struct testTable_t **testTable;
-} IADEV;  
+	dma_addr_t tx_dle_dma;
+	dma_addr_t rx_dle_dma;
+} IADEV;
   
   
 #define INPH_IA_DEV(d) ((IADEV *) (d)->dev_data)  
diff -urN linux-2.4.21-pre2/drivers/char/Makefile linux-2.4.21-pre3/drivers/char/Makefile
--- linux-2.4.21-pre2/drivers/char/Makefile	Mon Jan  6 16:32:28 2003
+++ linux-2.4.21-pre3/drivers/char/Makefile	Mon Jan  6 16:32:31 2003
@@ -255,7 +255,7 @@
 obj-$(CONFIG_DZ) += dz.o
 obj-$(CONFIG_NWBUTTON) += nwbutton.o
 obj-$(CONFIG_NWFLASH) += nwflash.o
-obj-$(CONFIG_SCx200_GPIO) += scx200_gpio.o
+obj-$(CONFIG_SCx200_GPIO) += scx200_gpio.o scx200.o
 
 # Only one watchdog can succeed. We probe the hardware watchdog
 # drivers first, then the softdog driver.  This means if your hardware
diff -urN linux-2.4.21-pre2/drivers/char/agp/agpgart_be.c linux-2.4.21-pre3/drivers/char/agp/agpgart_be.c
--- linux-2.4.21-pre2/drivers/char/agp/agpgart_be.c	Mon Jan  6 16:32:28 2003
+++ linux-2.4.21-pre3/drivers/char/agp/agpgart_be.c	Mon Jan  6 16:32:31 2003
@@ -4688,6 +4688,12 @@
 		"Via",
 		"Apollo Pro KT400",
 		via_generic_setup },
+	{ PCI_DEVICE_ID_VIA_P4X333,
+		PCI_VENDOR_ID_VIA,
+		VIA_APOLLO_P4X400,
+		"Via",
+		"Apollo P4X400",
+		via_generic_setup },
 	{ 0,
 		PCI_VENDOR_ID_VIA,
 		VIA_GENERIC,
diff -urN linux-2.4.21-pre2/drivers/char/drm/drm_agpsupport.h linux-2.4.21-pre3/drivers/char/drm/drm_agpsupport.h
--- linux-2.4.21-pre2/drivers/char/drm/drm_agpsupport.h	Mon Jan  6 16:32:28 2003
+++ linux-2.4.21-pre3/drivers/char/drm/drm_agpsupport.h	Mon Jan  6 16:32:31 2003
@@ -283,6 +283,8 @@
 			break;
 		case VIA_APOLLO_PRO: 	head->chipset = "VIA Apollo Pro";
 			break;
+		case VIA_APOLLO_P4X400:	head->chipset = "VIA Apollo P4X400";
+			break;
 
 		case SIS_GENERIC:	head->chipset = "SiS";           break;
 		case AMD_GENERIC:	head->chipset = "AMD";           break;
diff -urN linux-2.4.21-pre2/drivers/char/drm/drm_ioctl.h linux-2.4.21-pre3/drivers/char/drm/drm_ioctl.h
--- linux-2.4.21-pre2/drivers/char/drm/drm_ioctl.h	Thu Nov 28 15:53:12 2002
+++ linux-2.4.21-pre3/drivers/char/drm/drm_ioctl.h	Mon Jan  6 16:32:31 2003
@@ -40,8 +40,20 @@
 	if (copy_from_user(&p, (drm_irq_busid_t *)arg, sizeof(p)))
 		return -EFAULT;
 	dev = pci_find_slot(p.busnum, PCI_DEVFN(p.devnum, p.funcnum));
-	if (dev) p.irq = dev->irq;
-	else	 p.irq = 0;
+	if (!dev) {
+		DRM_ERROR("pci_find_slot failed for %d:%d:%d\n",
+			  p.busnum, p.devnum, p.funcnum);
+		p.irq = 0;
+		goto out;
+	}			
+	if (pci_enable_device(dev) != 0) {
+		DRM_ERROR("pci_enable_device failed for %d:%d:%d\n",
+			  p.busnum, p.devnum, p.funcnum);
+		p.irq = 0;
+		goto out;
+	}		
+	p.irq = dev->irq;
+ out:
 	DRM_DEBUG("%d:%d:%d => IRQ %d\n",
 		  p.busnum, p.devnum, p.funcnum, p.irq);
 	if (copy_to_user((drm_irq_busid_t *)arg, &p, sizeof(p)))
diff -urN linux-2.4.21-pre2/drivers/char/drm/drm_lists.h linux-2.4.21-pre3/drivers/char/drm/drm_lists.h
--- linux-2.4.21-pre2/drivers/char/drm/drm_lists.h	Thu Nov 28 15:53:12 2002
+++ linux-2.4.21-pre3/drivers/char/drm/drm_lists.h	Mon Jan  6 16:32:31 2003
@@ -41,7 +41,7 @@
 				    DRM_MEM_BUFLISTS);
 
 	if(!bl->bufs) return -ENOMEM;
-
+	memset(bl->bufs, 0, sizeof(*bl->bufs));
 	bl->count      = count;
 	bl->rp	       = bl->bufs;
 	bl->wp	       = bl->bufs;
diff -urN linux-2.4.21-pre2/drivers/char/drm/drm_scatter.h linux-2.4.21-pre3/drivers/char/drm/drm_scatter.h
--- linux-2.4.21-pre2/drivers/char/drm/drm_scatter.h	Thu Nov 28 15:53:12 2002
+++ linux-2.4.21-pre3/drivers/char/drm/drm_scatter.h	Mon Jan  6 16:32:31 2003
@@ -93,6 +93,8 @@
 		return -ENOMEM;
 	}
 
+	memset(entry->pagelist, 0, pages * sizeof(*entry->pagelist));
+
 	entry->busaddr = DRM(alloc)( pages * sizeof(*entry->busaddr),
 				     DRM_MEM_PAGES );
 	if ( !entry->busaddr ) {
diff -urN linux-2.4.21-pre2/drivers/char/drm/drm_vm.h linux-2.4.21-pre3/drivers/char/drm/drm_vm.h
--- linux-2.4.21-pre2/drivers/char/drm/drm_vm.h	Thu Nov 28 15:53:12 2002
+++ linux-2.4.21-pre3/drivers/char/drm/drm_vm.h	Mon Jan  6 16:32:31 2003
@@ -406,7 +406,7 @@
 
 	if (!capable(CAP_SYS_ADMIN) && (map->flags & _DRM_READ_ONLY)) {
 		vma->vm_flags &= VM_MAYWRITE;
-#if defined(__i386__)
+#if defined(__i386__) || defined(__x86_64__)
 		pgprot_val(vma->vm_page_prot) &= ~_PAGE_RW;
 #else
 				/* Ye gads this is ugly.  With more thought
@@ -433,7 +433,7 @@
 	case _DRM_FRAME_BUFFER:
 	case _DRM_REGISTERS:
 		if (VM_OFFSET(vma) >= __pa(high_memory)) {
-#if defined(__i386__)
+#if defined(__i386__) || defined(__x86_64__)
 			if (boot_cpu_data.x86 > 3 && map->type != _DRM_AGP) {
 				pgprot_val(vma->vm_page_prot) |= _PAGE_PCD;
 				pgprot_val(vma->vm_page_prot) &= ~_PAGE_PWT;
diff -urN linux-2.4.21-pre2/drivers/char/drm/mga_dma.c linux-2.4.21-pre3/drivers/char/drm/mga_dma.c
--- linux-2.4.21-pre2/drivers/char/drm/mga_dma.c	Thu Nov 28 15:53:12 2002
+++ linux-2.4.21-pre3/drivers/char/drm/mga_dma.c	Mon Jan  6 16:32:31 2003
@@ -27,7 +27,7 @@
  * Authors:
  *    Rickard E. (Rik) Faith <faith@valinux.com>
  *    Jeff Hartmann <jhartmann@valinux.com>
- *    Keith Whitwell <keithw@valinux.com>
+ *    Keith Whitwell <keith@tungstengraphics.com>
  *
  * Rewritten by:
  *    Gareth Hughes <gareth@valinux.com>
diff -urN linux-2.4.21-pre2/drivers/char/drm/mga_drm.h linux-2.4.21-pre3/drivers/char/drm/mga_drm.h
--- linux-2.4.21-pre2/drivers/char/drm/mga_drm.h	Wed Aug  8 09:42:15 2001
+++ linux-2.4.21-pre3/drivers/char/drm/mga_drm.h	Mon Jan  6 16:32:31 2003
@@ -26,7 +26,7 @@
  *
  * Authors:
  *    Jeff Hartmann <jhartmann@valinux.com>
- *    Keith Whitwell <keithw@valinux.com>
+ *    Keith Whitwell <keith@tungstengraphics.com>
  *
  * Rewritten by:
  *    Gareth Hughes <gareth@valinux.com>
diff -urN linux-2.4.21-pre2/drivers/char/drm/mga_state.c linux-2.4.21-pre3/drivers/char/drm/mga_state.c
--- linux-2.4.21-pre2/drivers/char/drm/mga_state.c	Thu Nov 28 15:53:12 2002
+++ linux-2.4.21-pre3/drivers/char/drm/mga_state.c	Mon Jan  6 16:32:31 2003
@@ -26,7 +26,7 @@
  *
  * Authors:
  *    Jeff Hartmann <jhartmann@valinux.com>
- *    Keith Whitwell <keithw@valinux.com>
+ *    Keith Whitwell <keith@tungstengraphics.com>
  *
  * Rewritten by:
  *    Gareth Hughes <gareth@valinux.com>
diff -urN linux-2.4.21-pre2/drivers/char/drm/radeon.h linux-2.4.21-pre3/drivers/char/drm/radeon.h
--- linux-2.4.21-pre2/drivers/char/drm/radeon.h	Wed Aug  8 09:42:15 2001
+++ linux-2.4.21-pre3/drivers/char/drm/radeon.h	Mon Jan  6 16:32:31 2003
@@ -25,6 +25,7 @@
  *
  * Authors:
  *    Gareth Hughes <gareth@valinux.com>
+ *    Keith Whitwell <keith@tungstengraphics.com>
  */
 
 #ifndef __RADEON_H__
diff -urN linux-2.4.21-pre2/drivers/char/drm-4.0/agpsupport.c linux-2.4.21-pre3/drivers/char/drm-4.0/agpsupport.c
--- linux-2.4.21-pre2/drivers/char/drm-4.0/agpsupport.c	Mon Jan  6 16:32:28 2003
+++ linux-2.4.21-pre3/drivers/char/drm-4.0/agpsupport.c	Mon Jan  6 16:32:31 2003
@@ -277,6 +277,7 @@
 			break;
 		case VIA_APOLLO_KT400:  head->chipset = "VIA Apollo KT400";
 			break;
+		case VIA_APOLLO_P4X400:	head->chipset = "VIA Apollo P4X400";
 #endif
 
 		case VIA_APOLLO_PRO: 	head->chipset = "VIA Apollo Pro";
diff -urN linux-2.4.21-pre2/drivers/char/n_r3964.c linux-2.4.21-pre3/drivers/char/n_r3964.c
--- linux-2.4.21-pre2/drivers/char/n_r3964.c	Thu Sep 13 15:21:32 2001
+++ linux-2.4.21-pre3/drivers/char/n_r3964.c	Mon Jan  6 16:32:31 2003
@@ -1364,7 +1364,7 @@
       pHeader->owner = pClient;
    }
 
-   copy_from_user (pHeader->data, data, count); /* We already verified this */
+   __copy_from_user(pHeader->data, data, count); /* We already verified this */
 
    if(pInfo->flags & R3964_DEBUG)
    {
diff -urN linux-2.4.21-pre2/drivers/char/pdc_console.c linux-2.4.21-pre3/drivers/char/pdc_console.c
--- linux-2.4.21-pre2/drivers/char/pdc_console.c	Thu Nov 28 15:53:12 2002
+++ linux-2.4.21-pre3/drivers/char/pdc_console.c	Mon Jan  6 16:32:31 2003
@@ -625,7 +625,7 @@
 #else
 	pdc_drv_driver.name = "ttyB";
 #endif
-	pdc_drv_driver.major = PDCCONS_MAJOR;
+	pdc_drv_driver.major = MUX_MAJOR;
 	pdc_drv_driver.minor_start = 0;
 	pdc_drv_driver.num = NR_PORTS;
 	pdc_drv_driver.type = TTY_DRIVER_TYPE_SERIAL;
diff -urN linux-2.4.21-pre2/drivers/char/scx200.c linux-2.4.21-pre3/drivers/char/scx200.c
--- linux-2.4.21-pre2/drivers/char/scx200.c	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre3/drivers/char/scx200.c	Mon Jan  6 16:32:31 2003
@@ -0,0 +1,128 @@
+/* linux/drivers/char/scx200.c 
+
+   Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
+
+   National Semiconductor SCx200 support. */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+
+#include <linux/scx200.h>
+#include <linux/scx200.h>
+
+#define NAME "scx200"
+
+MODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>");
+MODULE_DESCRIPTION("NatSemi SCx200 Driver");
+MODULE_LICENSE("GPL");
+
+unsigned scx200_gpio_base = 0;
+long scx200_gpio_shadow[2];
+
+spinlock_t scx200_gpio_lock = SPIN_LOCK_UNLOCKED;
+static spinlock_t scx200_gpio_config_lock = SPIN_LOCK_UNLOCKED;
+
+u32 scx200_gpio_configure(int index, u32 mask, u32 bits)
+{
+	u32 config, new_config;
+	unsigned long flags;
+
+	spin_lock_irqsave(&scx200_gpio_config_lock, flags);
+
+	outl(index, scx200_gpio_base + 0x20);
+	config = inl(scx200_gpio_base + 0x24);
+
+	new_config = (config & mask) | bits;
+	outl(new_config, scx200_gpio_base + 0x24);
+
+	spin_unlock_irqrestore(&scx200_gpio_config_lock, flags);
+
+	return config;
+}
+
+void scx200_gpio_dump(unsigned index)
+{
+	u32 config = scx200_gpio_configure(index, ~0, 0);
+	printk(KERN_DEBUG "GPIO%02u: 0x%08lx", index, (unsigned long)config);
+	
+	if (config & 1) 
+		printk(" OE"); /* output enabled */
+	else
+		printk(" TS"); /* tristate */
+	if (config & 2) 
+		printk(" PP"); /* push pull */
+	else
+		printk(" OD"); /* open drain */
+	if (config & 4) 
+		printk(" PUE"); /* pull up enabled */
+	else
+		printk(" PUD"); /* pull up disabled */
+	if (config & 8) 
+		printk(" LOCKED"); /* locked */
+	if (config & 16) 
+		printk(" LEVEL"); /* level input */
+	else
+		printk(" EDGE"); /* edge input */
+	if (config & 32) 
+		printk(" HI"); /* trigger on rising edge */
+	else
+		printk(" LO"); /* trigger on falling edge */
+	if (config & 64) 
+		printk(" DEBOUNCE"); /* debounce */
+	printk("\n");
+}
+
+int __init scx200_init(void)
+{
+	struct pci_dev *bridge;
+	int bank;
+	unsigned base;
+
+	printk(KERN_INFO NAME ": NatSemi SCx200 Driver\n");
+
+	if ((bridge = pci_find_device(PCI_VENDOR_ID_NS, 
+				      PCI_DEVICE_ID_NS_SCx200_BRIDGE,
+				      NULL)) == NULL)
+		return -ENODEV;
+
+	base = pci_resource_start(bridge, 0);
+	printk(KERN_INFO NAME ": GPIO base 0x%x\n", base);
+
+	if (request_region(base, SCx200_GPIO_SIZE, "NatSemi SCx200 GPIO") == 0) {
+		printk(KERN_ERR NAME ": can't allocate I/O for GPIOs\n");
+		return -EBUSY;
+	}
+
+	scx200_gpio_base = base;
+
+	/* read the current values driven on the GPIO signals */
+	for (bank = 0; bank < 2; ++bank)
+		scx200_gpio_shadow[bank] = inl(scx200_gpio_base + 0x10 * bank);
+
+	return 0;
+}
+
+void __exit scx200_cleanup(void)
+{
+	release_region(scx200_gpio_base, SCx200_GPIO_SIZE);
+}
+
+module_init(scx200_init);
+module_exit(scx200_cleanup);
+
+EXPORT_SYMBOL(scx200_gpio_base);
+EXPORT_SYMBOL(scx200_gpio_shadow);
+EXPORT_SYMBOL(scx200_gpio_lock);
+EXPORT_SYMBOL(scx200_gpio_configure);
+EXPORT_SYMBOL(scx200_gpio_dump);
+
+/*
+    Local variables:
+        compile-command: "make -k -C ../../.. SUBDIRS=arch/i386/kernel modules"
+        c-basic-offset: 8
+    End:
+*/
diff -urN linux-2.4.21-pre2/drivers/ide/Config.in linux-2.4.21-pre3/drivers/ide/Config.in
--- linux-2.4.21-pre2/drivers/ide/Config.in	Mon Jan  6 16:32:28 2003
+++ linux-2.4.21-pre3/drivers/ide/Config.in	Mon Jan  6 16:32:31 2003
@@ -51,6 +51,7 @@
 	    dep_tristate '    AMD Viper support' CONFIG_BLK_DEV_AMD74XX $CONFIG_BLK_DEV_IDEDMA_PCI
 	    dep_mbool    '      AMD Viper ATA-66 Override' CONFIG_AMD74XX_OVERRIDE $CONFIG_BLK_DEV_AMD74XX
 	    dep_tristate '    CMD64{3|6|8|9} chipset support' CONFIG_BLK_DEV_CMD64X $CONFIG_BLK_DEV_IDEDMA_PCI
+	    dep_tristate '    Compaq Triflex IDE support' CONFIG_BLK_DEV_TRIFLEX $CONFIG_BLK_DEV_IDEDMA_PCI
 	    dep_tristate '    CY82C693 chipset support' CONFIG_BLK_DEV_CY82C693 $CONFIG_BLK_DEV_IDEDMA_PCI
 	    dep_tristate '    Cyrix CS5530 MediaGX chipset support' CONFIG_BLK_DEV_CS5530 $CONFIG_BLK_DEV_IDEDMA_PCI
   	    dep_tristate '    HPT34X chipset support' CONFIG_BLK_DEV_HPT34X $CONFIG_BLK_DEV_IDEDMA_PCI
diff -urN linux-2.4.21-pre2/drivers/ide/ide-cd.c linux-2.4.21-pre3/drivers/ide/ide-cd.c
--- linux-2.4.21-pre2/drivers/ide/ide-cd.c	Mon Jan  6 16:32:28 2003
+++ linux-2.4.21-pre3/drivers/ide/ide-cd.c	Mon Jan  6 16:32:31 2003
@@ -852,6 +852,7 @@
 	}
 
 	/* Set up the controller registers. */
+	/* FIXME: for Virtual DMA we must check harder */
 	HWIF(drive)->OUTB(info->dma, IDE_FEATURE_REG);
 	HWIF(drive)->OUTB(0, IDE_IREASON_REG);
 	HWIF(drive)->OUTB(0, IDE_SECTOR_REG);
diff -urN linux-2.4.21-pre2/drivers/ide/ide-tape.c linux-2.4.21-pre3/drivers/ide/ide-tape.c
--- linux-2.4.21-pre2/drivers/ide/ide-tape.c	Mon Jan  6 16:32:28 2003
+++ linux-2.4.21-pre3/drivers/ide/ide-tape.c	Mon Jan  6 16:32:31 2003
@@ -6597,14 +6597,16 @@
 
 	for (minor = 0; minor < MAX_HWIFS * MAX_DRIVES; minor++) {
 		drive = idetape_chrdevs[minor].drive;
-		if (drive != NULL && idetape_cleanup(drive))
-			printk(KERN_ERR "ide-tape: %s: cleanup_module() "
-				"called while still busy\n", drive->name);
-	}
+		if (drive) {
+			if (idetape_cleanup(drive))
+				printk(KERN_ERR "ide-tape: %s: cleanup_module() "
+					"called while still busy\n", drive->name);
 #ifdef CONFIG_PROC_FS
-	if (drive->proc)
-		ide_remove_proc_entries(drive->proc, idetape_proc);
+			if (drive->proc)
+				ide_remove_proc_entries(drive->proc, idetape_proc);
 #endif
+		}
+	}
 
 	ide_unregister_module(&idetape_module);
 }
diff -urN linux-2.4.21-pre2/drivers/ide/ide-timing.h linux-2.4.21-pre3/drivers/ide/ide-timing.h
--- linux-2.4.21-pre2/drivers/ide/ide-timing.h	Fri Aug  2 17:39:44 2002
+++ linux-2.4.21-pre3/drivers/ide/ide-timing.h	Mon Jan  6 16:32:31 2003
@@ -245,6 +245,14 @@
 	}
 
 /*
+ * If the drive is an ATAPI device it may need slower address setup timing,
+ * so we stay on the safe side.
+ */
+
+	if (drive->media != ide_disk)
+		p.setup = 120;
+
+/*
  * Convert the timing to bus clock counts.
  */
 
diff -urN linux-2.4.21-pre2/drivers/ide/legacy/hd.c linux-2.4.21-pre3/drivers/ide/legacy/hd.c
--- linux-2.4.21-pre2/drivers/ide/legacy/hd.c	Mon Jan  6 16:32:28 2003
+++ linux-2.4.21-pre3/drivers/ide/legacy/hd.c	Mon Jan  6 16:32:31 2003
@@ -45,7 +45,6 @@
 #include <linux/mc146818rtc.h> /* CMOS defines */
 #include <linux/init.h>
 #include <linux/blkpg.h>
-#include <linux/ide.h>
 
 #define REALLY_SLOW_IO
 #include <asm/system.h>
diff -urN linux-2.4.21-pre2/drivers/ide/pci/Makefile linux-2.4.21-pre3/drivers/ide/pci/Makefile
--- linux-2.4.21-pre2/drivers/ide/pci/Makefile	Mon Jan  6 16:32:28 2003
+++ linux-2.4.21-pre3/drivers/ide/pci/Makefile	Mon Jan  6 16:32:31 2003
@@ -33,6 +33,7 @@
 obj-$(CONFIG_BLK_DEV_SLC90E66)		+= slc90e66.o
 obj-$(CONFIG_BLK_DEV_TRM290)		+= trm290.o
 obj-$(CONFIG_BLK_DEV_VIA82CXXX)		+= via82cxxx.o
+obj-$(CONFIG_BLK_DEV_TRIFLEX)		+= triflex.o
 
 # Must appear at the end of the block
 obj-$(CONFIG_BLK_DEV_GENERIC)		+= generic.o
diff -urN linux-2.4.21-pre2/drivers/ide/pci/alim15x3.c linux-2.4.21-pre3/drivers/ide/pci/alim15x3.c
--- linux-2.4.21-pre2/drivers/ide/pci/alim15x3.c	Mon Jan  6 16:32:28 2003
+++ linux-2.4.21-pre3/drivers/ide/pci/alim15x3.c	Mon Jan  6 16:32:31 2003
@@ -782,7 +782,7 @@
 	u8 ideic, inmir;
 	s8 irq_routing_table[] = { -1,  9, 3, 10, 4,  5, 7,  6,
 				      1, 11, 0, 12, 0, 14, 0, 15 };
-	int irq;
+	int irq = -1;
 	
 	hwif->irq = hwif->channel ? 15 : 14;
 
diff -urN linux-2.4.21-pre2/drivers/ide/pci/triflex.c linux-2.4.21-pre3/drivers/ide/pci/triflex.c
--- linux-2.4.21-pre2/drivers/ide/pci/triflex.c	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre3/drivers/ide/pci/triflex.c	Mon Jan  6 16:32:31 2003
@@ -0,0 +1,249 @@
+/*
+ * triflex.c
+ * 
+ * IDE Chipset driver for the Compaq TriFlex IDE controller.
+ * 
+ * Known to work with the Compaq Workstation 5x00 series.
+ *
+ * Copyright (C) 2002 Hewlett-Packard Development Group, L.P.
+ * Author: Torben Mathiasen <torben.mathiasen@hp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * Loosely based on the piix & svwks drivers.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/pci.h>
+#include <linux/ide.h>
+#include <linux/init.h>
+
+#include "ide_modes.h"
+#include "triflex.h"
+
+static struct pci_dev *triflex_dev;
+
+static int triflex_get_info(char *buf, char **addr, off_t offset, int count)
+{
+	char *p = buf;
+
+	struct pci_dev *dev	= triflex_dev;
+	unsigned long bibma = pci_resource_start(dev, 4);
+	u8  c0 = 0, c1 = 0;
+	u32 pri_timing, sec_timing;
+
+	p += sprintf(p, "\n                                Compaq Triflex Chipset\n");
+	
+	pci_read_config_dword(dev, 0x70, &pri_timing);
+	pci_read_config_dword(dev, 0x74, &sec_timing);
+
+	/*
+	 * at that point bibma+0x2 et bibma+0xa are byte registers
+	 * to investigate:
+	 */
+	c0 = inb((unsigned short)bibma + 0x02);
+	c1 = inb((unsigned short)bibma + 0x0a);
+
+	p += sprintf(p, "--------------- Primary Channel "
+			"---------------- Secondary Channel "
+			"-------------\n");
+	p += sprintf(p, "                %sabled "
+			"                        %sabled\n",
+			(c0&0x80) ? "dis" : " en",
+			(c1&0x80) ? "dis" : " en");
+	p += sprintf(p, "--------------- drive0 --------- drive1 "
+			"-------- drive0 ---------- drive1 ------\n");
+	p += sprintf(p, "DMA enabled:    %s              %s "
+			"            %s               %s\n",
+			(c0&0x20) ? "yes" : "no ",
+			(c0&0x40) ? "yes" : "no ",
+			(c1&0x20) ? "yes" : "no ",
+			(c1&0x40) ? "yes" : "no " );
+
+	p += sprintf(p, "DMA\n");
+	p += sprintf(p, "PIO\n");
+	
+	return p-buf;
+}
+
+static int triflex_tune_chipset(ide_drive_t *drive, u8 xferspeed)
+{
+	ide_hwif_t *hwif = HWIF(drive);
+	struct pci_dev *dev = hwif->pci_dev;
+	u8 channel_offset = hwif->channel ? 0x74 : 0x70;
+	u16 timing = 0;
+	u32 triflex_timings = 0;
+	u8 unit = (drive->select.b.unit & 0x01);
+	u8 speed = ide_rate_filter(0, xferspeed);
+	
+	pci_read_config_dword(dev, channel_offset, &triflex_timings);
+	
+	switch(speed) {
+		case XFER_MW_DMA_2:
+			timing = 0x0103; 
+			break;
+		case XFER_MW_DMA_1:
+			timing = 0x0203;
+			break;
+		case XFER_MW_DMA_0:
+			timing = 0x0808;
+			break;
+		case XFER_SW_DMA_2:
+		case XFER_SW_DMA_1:
+		case XFER_SW_DMA_0:
+			timing = 0x0f0f;
+			break;
+		case XFER_PIO_4:
+			timing = 0x0202;
+			break;
+		case XFER_PIO_3:
+			timing = 0x0204;
+			break;
+		case XFER_PIO_2:
+			timing = 0x0404;
+			break;
+		case XFER_PIO_1:
+			timing = 0x0508;
+			break;
+		case XFER_PIO_0:
+			timing = 0x0808;
+			break;
+		default:
+			return -1;
+	}
+
+	triflex_timings &= ~(0xFFFF << (16 * unit));
+	triflex_timings |= (timing << (16 * unit));
+	
+	pci_write_config_dword(dev, channel_offset, triflex_timings);
+	
+	return (ide_config_drive_speed(drive, speed));
+}
+
+static void triflex_tune_drive(ide_drive_t *drive, u8 pio)
+{
+	int use_pio = ide_get_best_pio_mode(drive, pio, 4, NULL);
+	(void) triflex_tune_chipset(drive, (XFER_PIO_0 + use_pio));
+}
+
+static int triflex_config_drive_for_dma(ide_drive_t *drive)
+{
+	int speed = ide_dma_speed(drive, 0); /* No ultra speeds */
+
+	if (!speed) { 
+		u8 pspeed = ide_get_best_pio_mode(drive, 255, 4, NULL);
+		speed = XFER_PIO_0 + pspeed;
+	}
+	
+	(void) triflex_tune_chipset(drive, speed);
+	 return ide_dma_enable(drive);
+}
+
+static int triflex_config_drive_xfer_rate(ide_drive_t *drive)
+{
+	ide_hwif_t *hwif	= HWIF(drive);
+	struct hd_driveid *id	= drive->id;
+	
+	if (id && (id->capability & 1) && drive->autodma) {
+		if (hwif->ide_dma_bad_drive(drive))
+			goto tune_pio;
+		if (id->field_valid & 2) {
+			if ((id->dma_mword & hwif->mwdma_mask) ||
+				(id->dma_1word & hwif->swdma_mask)) {
+				if (!triflex_config_drive_for_dma(drive))
+					goto tune_pio;
+			}
+		} else 
+			goto tune_pio;
+	} else {
+tune_pio:
+		hwif->tuneproc(drive, 255);
+		return hwif->ide_dma_off_quietly(drive);
+	}
+
+	return hwif->ide_dma_on(drive);
+}
+
+static void __init init_hwif_triflex(ide_hwif_t *hwif)
+{
+	hwif->tuneproc = &triflex_tune_drive;
+	hwif->speedproc = &triflex_tune_chipset;
+
+	hwif->atapi_dma  = 1;
+	hwif->mwdma_mask = 0x07;
+	hwif->swdma_mask = 0x07;
+	hwif->ide_dma_check = &triflex_config_drive_xfer_rate;
+	
+	if (!noautodma)
+		hwif->autodma = 1;
+	hwif->drives[0].autodma = hwif->autodma;
+	hwif->drives[1].autodma = hwif->autodma;
+}
+
+static unsigned int __init init_chipset_triflex(struct pci_dev *dev, 
+		const char *name) 
+{
+#ifdef CONFIG_PROC_FS
+	ide_pci_register_host_proc(&triflex_proc);
+#endif
+	return 0;	
+}
+
+static int __devinit triflex_init_one(struct pci_dev *dev, 
+		const struct pci_device_id *id)
+{
+	ide_pci_device_t *d = &triflex_devices[id->driver_data];
+	if (dev->device != d->device)
+		BUG();
+	
+	ide_setup_pci_device(dev, d);
+	triflex_dev = dev;
+	MOD_INC_USE_COUNT;
+	
+	return 0;
+}
+
+static struct pci_driver driver = {
+	.name		= "TRIFLEX IDE",
+	.id_table	= triflex_pci_tbl,
+	.probe		= triflex_init_one,
+};
+
+static int triflex_ide_init(void)
+{
+	return ide_pci_register_driver(&driver);
+}
+
+static void triflex_ide_exit(void)
+{
+	ide_pci_unregister_driver(&driver);
+}
+
+module_init(triflex_ide_init);
+module_exit(triflex_ide_exit);
+
+MODULE_AUTHOR("Torben Mathiasen");
+MODULE_DESCRIPTION("PCI driver module for Compaq Triflex IDE");
+MODULE_LICENSE("GPL");
+
+
diff -urN linux-2.4.21-pre2/drivers/ide/pci/triflex.h linux-2.4.21-pre3/drivers/ide/pci/triflex.h
--- linux-2.4.21-pre2/drivers/ide/pci/triflex.h	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre3/drivers/ide/pci/triflex.h	Mon Jan  6 16:32:31 2003
@@ -0,0 +1,55 @@
+/* 
+ * triflex.h
+ *
+ * Copyright (C) 2002 Hewlett-Packard Development Group, L.P.
+ * Author: Torben Mathiasen <torben.mathiasen@hp.com>
+ *
+ */
+#ifndef TRIFLEX_H
+#define TRIFLEX_H
+
+#include <linux/config.h>
+#include <linux/pci.h>
+#include <linux/ide.h>
+
+static unsigned int __devinit init_chipset_triflex(struct pci_dev *, const char *);
+static void init_hwif_triflex(ide_hwif_t *);
+static int triflex_get_info(char *, char **, off_t, int);
+
+static ide_pci_device_t triflex_devices[] __devinitdata = {
+	{
+		.vendor 	PCI_VENDOR_ID_COMPAQ,
+		.device		PCI_DEVICE_ID_COMPAQ_TRIFLEX_IDE,
+		.name		"TRIFLEX",
+		.init_chipset	init_chipset_triflex,
+		.init_iops	NULL,
+		.init_hwif	init_hwif_triflex,
+		.channels	2,
+		.autodma	AUTODMA,
+		.enablebits	{{0x80, 0x01, 0x01}, {0x80, 0x02, 0x02}},
+		.bootable	ON_BOARD,
+		.extra		0,
+	},{	
+		.vendor		0,
+		.device		0,
+		.channels	0,
+		.bootable	EOL,
+	}
+};
+
+#ifdef CONFIG_PROC_FS
+static ide_pci_host_proc_t triflex_proc __initdata = {
+		name:		"triflex",
+		set:		1,
+		get_info: 	triflex_get_info,
+		parent: 	NULL,
+};
+#endif
+
+static struct pci_device_id triflex_pci_tbl[] __devinitdata = {
+	{ PCI_VENDOR_ID_COMPAQ, PCI_DEVICE_ID_COMPAQ_TRIFLEX_IDE, PCI_ANY_ID, 
+		PCI_ANY_ID, 0, 0, 0 },
+	{ 0, },
+};
+
+#endif /* TRIFLEX_H */
diff -urN linux-2.4.21-pre2/drivers/ieee1394/Makefile linux-2.4.21-pre3/drivers/ieee1394/Makefile
--- linux-2.4.21-pre2/drivers/ieee1394/Makefile	Mon Jan  6 16:32:28 2003
+++ linux-2.4.21-pre3/drivers/ieee1394/Makefile	Fri Aug  2 17:39:44 2002
@@ -2,8 +2,11 @@
 # Makefile for the Linux IEEE 1394 implementation
 #
 
+O_TARGET := ieee1394drv.o
+
 export-objs := ieee1394_core.o ohci1394.o cmp.o
 
+list-multi := ieee1394.o
 ieee1394-objs := ieee1394_core.o ieee1394_transactions.o hosts.o \
 		 highlevel.o csr.o nodemgr.o
 
diff -urN linux-2.4.21-pre2/drivers/md/md.c linux-2.4.21-pre3/drivers/md/md.c
--- linux-2.4.21-pre2/drivers/md/md.c	Mon Jan  6 16:32:28 2003
+++ linux-2.4.21-pre3/drivers/md/md.c	Mon Jan  6 16:32:31 2003
@@ -489,7 +489,7 @@
 	init_buffer(&bh, bh_complete, &event);
 	bh.b_rdev = dev;
 	bh.b_rsector = sector;
-	bh.b_state	= (1 << BH_Req) | (1 << BH_Mapped);
+	bh.b_state	= (1 << BH_Req) | (1 << BH_Mapped) | (1 << BH_Lock);
 	bh.b_size = size;
 	bh.b_page = page;
 	bh.b_reqnext = NULL;
diff -urN linux-2.4.21-pre2/drivers/net/Config.in linux-2.4.21-pre3/drivers/net/Config.in
--- linux-2.4.21-pre2/drivers/net/Config.in	Mon Jan  6 16:32:28 2003
+++ linux-2.4.21-pre3/drivers/net/Config.in	Mon Jan  6 16:32:32 2003
@@ -158,6 +158,7 @@
    fi
    if [ "$CONFIG_NET_PCI" = "y" ]; then
       dep_tristate '    AMD PCnet32 PCI support' CONFIG_PCNET32 $CONFIG_PCI
+      dep_tristate '    AMD 8111 (new PCI lance) support' CONFIG_AMD8111_ETH $CONFIG_PCI
       dep_tristate '    Adaptec Starfire/DuraLAN support' CONFIG_ADAPTEC_STARFIRE $CONFIG_PCI
       if [ "$CONFIG_ISA" = "y" -o "$CONFIG_EISA" = "y" ]; then
 	 dep_tristate '    Ansel Communications EISA 3200 support (EXPERIMENTAL)' CONFIG_AC3200 $CONFIG_EXPERIMENTAL
diff -urN linux-2.4.21-pre2/drivers/net/Makefile linux-2.4.21-pre3/drivers/net/Makefile
--- linux-2.4.21-pre2/drivers/net/Makefile	Mon Jan  6 16:32:28 2003
+++ linux-2.4.21-pre3/drivers/net/Makefile	Mon Jan  6 16:32:32 2003
@@ -226,6 +226,7 @@
 obj-$(CONFIG_TUN) += tun.o
 obj-$(CONFIG_DL2K) += dl2k.o
 obj-$(CONFIG_R8169) += r8169.o
+obj-$(CONFIG_AMD8111_ETH) += amd8111e.o
 
 # non-drivers/net drivers who want mii lib
 obj-$(CONFIG_PCMCIA_SMC91C92) += mii.o
diff -urN linux-2.4.21-pre2/drivers/net/amd8111e.c linux-2.4.21-pre3/drivers/net/amd8111e.c
--- linux-2.4.21-pre2/drivers/net/amd8111e.c	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre3/drivers/net/amd8111e.c	Mon Jan  6 16:32:32 2003
@@ -0,0 +1,1651 @@
+
+/* Advanced  Micro Devices Inc. AMD8111E Linux Network Driver 
+ * Copyright (C) 2002 Advanced Micro Devices 
+ *
+ * 
+ * Copyright 2001,2002 Jeff Garzik <jgarzik@mandrakesoft.com> [ 8139cp.c,tg3.c ]
+ * Copyright (C) 2001, 2002 David S. Miller (davem@redhat.com)[ tg3.c]
+ * Copyright 1996-1999 Thomas Bogendoerfer [ pcnet32.c ]
+ * Derived from the lance driver written 1993,1994,1995 by Donald Becker.
+ * Copyright 1993 United States Government as represented by the
+ *	Director, National Security Agency.[ pcnet32.c ]
+ * Carsten Langgaard, carstenl@mips.com [ pcnet32.c ]
+ * Copyright (C) 2000 MIPS Technologies, Inc.  All rights reserved.
+ *
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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
+  
+Module Name:
+
+	amd8111e.c
+
+Abstract:
+	
+ 	 AMD8111 based 10/100 Ethernet Controller Driver. 
+
+Environment:
+
+	Kernel Mode
+
+Revision History:
+
+*/
+
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/compiler.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <linux/if_vlan.h>
+#include <linux/ctype.h>	
+#include <linux/crc32.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <asm/uaccess.h>
+
+#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
+#define AMD8111E_VLAN_TAG_USED 1
+#else
+#define AMD8111E_VLAN_TAG_USED 0
+#endif
+
+#include "amd8111e.h"
+#define MODULE_NAME	"amd8111e"
+#define MODULE_VERSION	"3.0.0"
+MODULE_AUTHOR("Advanced Micro Devices, Inc.");
+MODULE_DESCRIPTION ("AMD8111 based 10/100 Ethernet Controller. Driver Version 3.0.0"); 
+MODULE_LICENSE("GPL");
+
+MODULE_PARM(speed_duplex, "1-" __MODULE_STRING (MAX_UNITS) "i");
+MODULE_PARM_DESC(speed_duplex, "Set device speed and duplex modes, 0: Auto Negotitate, 1: 10Mbps Half Duplex, 2: 10Mbps Full Duplex, 3: 100Mbps Half Duplex, 4: 100Mbps Full Duplex");
+
+static struct pci_device_id amd8111e_pci_tbl[] __devinitdata = {
+		
+	{ PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD8111E_7462,
+	 PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
+	{ 0, }
+
+};
+
+/*
+This function will set PHY speed. During initialization sets the original speed to 100 full.
+*/
+static void amd8111e_set_ext_phy(struct net_device *dev)
+{
+	struct amd8111e_priv *lp = (struct amd8111e_priv *)dev->priv;
+	unsigned long  reg_val = 0;
+	void * mmio = lp->mmio;
+	struct amd8111e_link_config *link_config = &lp->link_config;
+	
+	if(!lp->opened){
+		/* Initializing SPEED_100 and DUPLEX_FULL as original values */
+		link_config->orig_speed = SPEED_100;
+		link_config->orig_duplex = DUPLEX_FULL;
+		link_config->orig_phy_option = XPHYSP |XPHYFD;
+	}
+	reg_val = lp->ext_phy_option;
+
+	/* Disable port manager */
+	writel((u32) EN_PMGR, mmio + CMD3 );
+
+	/* Reset PHY */
+	writel((u32)XPHYRST | lp->ext_phy_option, mmio + CTRL2);
+
+	/* Enable port manager */
+	writel((u32)VAL1 | EN_PMGR, mmio + CMD3 );
+}
+
+/* 
+This function will unmap skb->data space and will free 
+all transmit and receive skbuffs.
+*/
+static int amd8111e_free_skbs(struct net_device *dev)
+{
+	struct amd8111e_priv *lp = (struct amd8111e_priv *)dev->priv;
+	struct sk_buff* rx_skbuff;
+	int i;
+
+	/* Freeing transmit skbs */
+	for(i = 0; i < NUM_TX_BUFFERS; i++){
+		if(lp->tx_skbuff[i]){
+			pci_unmap_single(lp->pci_dev,lp->tx_dma_addr[i],					lp->tx_skbuff[i]->len,PCI_DMA_TODEVICE);
+			dev_kfree_skb (lp->tx_skbuff[i]);
+			lp->tx_skbuff[i] = NULL;
+			lp->tx_dma_addr[i] = 0;
+		}
+	}
+	/* Freeing previously allocated receive buffers */
+	for (i = 0; i < NUM_RX_BUFFERS; i++){
+		rx_skbuff = lp->rx_skbuff[i];
+		if(rx_skbuff != NULL){
+			pci_unmap_single(lp->pci_dev,lp->rx_dma_addr[i],
+				  lp->rx_buff_len - 2,PCI_DMA_FROMDEVICE);
+			dev_kfree_skb(lp->rx_skbuff[i]);
+			lp->rx_skbuff[i] = NULL;
+			lp->rx_dma_addr[i] = 0;
+		}
+	}
+	
+	return 0;
+}
+
+/*
+ This will set the receive buffer length corresponding to the mtu size of network interface.
+*/
+static inline void amd8111e_set_rx_buff_len(struct net_device* dev)
+{
+	struct amd8111e_priv* lp = dev->priv;
+	unsigned int mtu = dev->mtu;
+	
+	if (mtu > ETH_DATA_LEN){
+		/* MTU + ethernet header + FCS + optional VLAN tag */
+		lp->rx_buff_len = mtu + ETH_HLEN + 8;
+		lp->options |= OPTION_JUMBO_ENABLE;
+	} else{
+		lp->rx_buff_len = PKT_BUFF_SZ;
+		lp->options &= ~OPTION_JUMBO_ENABLE;
+	}
+}
+
+/* 
+This function will free all the previously allocated buffers, determine new receive buffer length  and will allocate new receive buffers. This function also allocates and initializes both the transmitter and receive hardware descriptors.
+ */
+static int amd8111e_init_ring(struct net_device *dev)
+{
+	struct amd8111e_priv *lp = (struct amd8111e_priv *)dev->priv;
+	int i;
+
+	lp->rx_idx = lp->tx_idx = 0;
+	lp->tx_complete_idx = 0;
+	lp->tx_ring_idx = 0;
+	
+
+	if(lp->opened)
+		/* Free previously allocated transmit and receive skbs */
+		amd8111e_free_skbs(dev);	
+
+	else{
+		 /* allocate the tx and rx descriptors */
+	     	if((lp->tx_ring = pci_alloc_consistent(lp->pci_dev, 
+			sizeof(struct amd8111e_tx_dr)*NUM_TX_RING_DR,
+			&lp->tx_ring_dma_addr)) == NULL)
+		
+			goto err_no_mem;
+	
+	     	if((lp->rx_ring = pci_alloc_consistent(lp->pci_dev, 
+			sizeof(struct amd8111e_rx_dr)*NUM_RX_RING_DR,
+			&lp->rx_ring_dma_addr)) == NULL)
+		
+			goto err_free_tx_ring;
+
+	}
+	/* Set new receive buff size */
+	amd8111e_set_rx_buff_len(dev);
+
+	/* Allocating receive  skbs */
+	for (i = 0; i < NUM_RX_BUFFERS; i++) {
+
+		if (!(lp->rx_skbuff[i] = dev_alloc_skb(lp->rx_buff_len))) {
+				/* Release previos allocated skbs */
+				for(--i; i >= 0 ;i--)
+					dev_kfree_skb(lp->rx_skbuff[i]);
+				goto err_free_rx_ring;
+		}
+		skb_reserve(lp->rx_skbuff[i],2);
+	}
+        /* Initilaizing receive descriptors */
+	for (i = 0; i < NUM_RX_BUFFERS; i++) {
+		lp->rx_dma_addr[i] = pci_map_single(lp->pci_dev, 
+			lp->rx_skbuff[i]->data,lp->rx_buff_len-2, PCI_DMA_FROMDEVICE);
+
+		lp->rx_ring[i].buff_phy_addr = cpu_to_le32(lp->rx_dma_addr[i]);
+		lp->rx_ring[i].buff_count = cpu_to_le16(lp->rx_buff_len);
+		lp->rx_ring[i].rx_dr_offset10 = cpu_to_le16(OWN_BIT);
+	}
+
+	/* Initializing transmit descriptors */
+	for (i = 0; i < NUM_TX_RING_DR; i++) {
+		lp->tx_ring[i].buff_phy_addr = 0;
+		lp->tx_ring[i].tx_dr_offset2 = 0;
+		lp->tx_ring[i].buff_count = 0;
+	}
+
+	return 0;
+
+err_free_rx_ring:
+	
+	pci_free_consistent(lp->pci_dev, 
+		sizeof(struct amd8111e_rx_dr)*NUM_RX_RING_DR,lp->rx_ring,
+		lp->rx_ring_dma_addr);
+
+err_free_tx_ring:
+	
+	pci_free_consistent(lp->pci_dev,
+		 sizeof(struct amd8111e_tx_dr)*NUM_TX_RING_DR,lp->tx_ring, 
+		 lp->tx_ring_dma_addr);
+
+err_no_mem:
+	return -ENOMEM;
+}
+/* 
+This function initializes the device registers  and starts the device.  
+*/
+static int amd8111e_restart(struct net_device *dev)
+{
+	struct amd8111e_priv *lp = (struct amd8111e_priv* )dev->priv;
+	void * mmio = lp->mmio;
+	int i,reg_val;
+
+	/* stop the chip */
+	 writel(RUN, mmio + CMD0);
+
+	if(amd8111e_init_ring(dev))
+		return -ENOMEM;
+	
+	amd8111e_set_ext_phy(dev);
+
+	/* set control registers */
+	reg_val = readl(mmio + CTRL1);
+	
+	writel( reg_val| XMTSP_128 | CACHE_ALIGN | B1_MASK, mmio + CTRL1 );
+
+	/* enable interrupt */
+	writel( APINT5EN | APINT4EN | APINT3EN | APINT2EN | APINT1EN | 
+		APINT0EN | MIIPDTINTEN | MCCIINTEN | MCCINTEN | MREINTEN |
+		SPNDINTEN | MPINTEN | SINTEN | STINTEN, mmio + INTEN0);
+
+	writel(VAL3 | LCINTEN | VAL1 | TINTEN0 | VAL0 | RINTEN0, mmio + INTEN0);
+
+	/* initialize tx and rx ring base addresses */
+	writel((u32)lp->tx_ring_dma_addr,mmio + XMT_RING_BASE_ADDR0);
+	writel((u32)lp->rx_ring_dma_addr,mmio+ RCV_RING_BASE_ADDR0);
+
+	writew((u32)NUM_TX_RING_DR, mmio + XMT_RING_LEN0);
+	writew((u16)NUM_RX_RING_DR, mmio + RCV_RING_LEN0);
+
+	if(lp->options & OPTION_JUMBO_ENABLE){
+		writel((u32)VAL2|JUMBO, mmio + CMD3);
+		/* Reset REX_UFLO */
+		writel( REX_UFLO, mmio + CMD2);
+		/* Should not set REX_UFLO for jumbo frames */
+		writel( VAL0 | APAD_XMT | REX_RTRY, mmio + CMD2);
+	}else
+		writel( VAL0 | APAD_XMT | REX_RTRY|REX_UFLO, mmio + CMD2);
+
+#if AMD8111E_VLAN_TAG_USED
+	writel((u32) VAL2|VSIZE|VL_TAG_DEL, mmio + CMD3);
+#endif
+	writel( VAL0 | APAD_XMT | REX_RTRY, mmio + CMD2 );
+	
+	/* Setting the MAC address to the device */
+	for(i = 0; i < ETH_ADDR_LEN; i++)
+		writeb( dev->dev_addr[i], mmio + PADR + i ); 
+	
+	/* set RUN bit to start the chip */
+	writel(VAL2 | RDMD0, mmio + CMD0);
+	writel(VAL0 | INTREN | RUN, mmio + CMD0);
+	
+	return 0;
+}
+/* 
+This function clears necessary the device registers. 
+*/	
+static void amd8111e_init_hw_default( struct amd8111e_priv* lp)
+{
+	unsigned int reg_val;
+	unsigned int logic_filter[2] ={0,};
+	void * mmio = lp->mmio;
+
+
+	/* AUTOPOLL0 Register *//*TBD default value is 8100 in FPS */
+	writew( 0x8101, mmio + AUTOPOLL0);
+
+	/* Clear RCV_RING_BASE_ADDR */
+	writel(0, mmio + RCV_RING_BASE_ADDR0);
+
+	/* Clear XMT_RING_BASE_ADDR */
+	writel(0, mmio + XMT_RING_BASE_ADDR0);
+	writel(0, mmio + XMT_RING_BASE_ADDR1);
+	writel(0, mmio + XMT_RING_BASE_ADDR2);
+	writel(0, mmio + XMT_RING_BASE_ADDR3);
+
+	/* Clear CMD0  */
+	writel(CMD0_CLEAR,mmio + CMD0);
+	
+	/* Clear CMD2 */
+	writel(CMD2_CLEAR, mmio +CMD2);
+
+	/* Clear CMD7 */
+	writel(CMD7_CLEAR , mmio + CMD7);
+
+	/* Clear DLY_INT_A and DLY_INT_B */
+	writel(0x0, mmio + DLY_INT_A);
+	writel(0x0, mmio + DLY_INT_B);
+
+	/* Clear FLOW_CONTROL */
+	writel(0x0, mmio + FLOW_CONTROL);
+
+	/* Clear INT0  write 1 to clear register */
+	reg_val = readl(mmio + INT0);
+	writel(reg_val, mmio + INT0);
+
+	/* Clear STVAL */
+	writel(0x0, mmio + STVAL);
+
+	/* Clear INTEN0 */
+	writel( INTEN0_CLEAR, mmio + INTEN0);
+
+	/* Clear LADRF */
+	writel(0x0 , mmio + LADRF);
+
+	/* Set SRAM_SIZE & SRAM_BOUNDARY registers  */
+	writel( 0x80010,mmio + SRAM_SIZE);
+
+	/* Clear RCV_RING0_LEN */
+	writel(0x0, mmio +  RCV_RING_LEN0);
+
+	/* Clear XMT_RING0/1/2/3_LEN */
+	writel(0x0, mmio +  XMT_RING_LEN0);
+	writel(0x0, mmio +  XMT_RING_LEN1);
+	writel(0x0, mmio +  XMT_RING_LEN2);
+	writel(0x0, mmio +  XMT_RING_LEN3);
+
+	/* Clear XMT_RING_LIMIT */
+	writel(0x0, mmio + XMT_RING_LIMIT);
+
+	/* Clear MIB */
+	writew(MIB_CLEAR, mmio + MIB_ADDR);
+
+	/* Clear LARF */
+	AMD8111E_WRITE_REG64(mmio, LADRF,logic_filter);
+
+	/* SRAM_SIZE register */
+	reg_val = readl(mmio + SRAM_SIZE);
+	
+	if(lp->options & OPTION_JUMBO_ENABLE)
+		writel( VAL2|JUMBO, mmio + CMD3);
+#if AMD8111E_VLAN_TAG_USED
+	writel(VAL2|VSIZE|VL_TAG_DEL, mmio + CMD3 );
+#endif
+	/* CMD2 register */
+	reg_val = readl(mmio + CMD2);
+
+}
+
+/* 
+This function disables the interrupt and clears all the pending 
+interrupts in INT0
+ */
+static void amd8111e_disable_interrupt(struct amd8111e_priv* lp)
+{	
+	u32 intr0;
+
+	/* Disable interrupt */
+	writel(INTREN, lp->mmio + CMD0);
+	
+	/* Clear INT0 */
+	intr0 = readl(lp->mmio + INT0);
+	writel(intr0, lp->mmio + INT0);
+
+}
+
+/*
+This function stops the chip. 
+*/
+static void amd8111e_stop_chip(struct amd8111e_priv* lp)
+{
+	writel(RUN, lp->mmio + CMD0);
+}
+
+/* 
+This function frees the  transmiter and receiver descriptor rings.
+*/
+static void amd8111e_free_ring(struct amd8111e_priv* lp)
+{	
+
+	/* Free transmit and receive skbs */
+	amd8111e_free_skbs(lp->amd8111e_net_dev);
+
+	/* Free transmit and receive descriptor rings */
+	if(lp->rx_ring){
+		pci_free_consistent(lp->pci_dev, 
+			sizeof(struct amd8111e_rx_dr)*NUM_RX_RING_DR,
+			lp->rx_ring, lp->rx_ring_dma_addr);
+		lp->rx_ring = NULL;
+	}
+	
+	if(lp->tx_ring){
+		pci_free_consistent(lp->pci_dev, 
+			sizeof(struct amd8111e_tx_dr)*NUM_TX_RING_DR,
+			lp->tx_ring, lp->tx_ring_dma_addr);
+
+		lp->tx_ring = NULL;
+	}
+
+}
+#if AMD8111E_VLAN_TAG_USED	
+/* 
+This is the receive indication function for packets with vlan tag.
+*/	
+static int amd8111e_vlan_rx(struct amd8111e_priv *lp, struct sk_buff *skb, u16 vlan_tag)
+{
+	return vlan_hwaccel_rx(skb, lp->vlgrp, vlan_tag);
+}
+#endif
+
+/*
+This function will free all the transmit skbs that are actually transmitted by the device. It will check the ownership of the skb before freeing the skb. 
+*/
+static int amd8111e_tx(struct net_device *dev)
+{
+	struct amd8111e_priv* lp = dev->priv;
+	int tx_index = lp->tx_complete_idx & TX_RING_DR_MOD_MASK;
+	int status;
+	
+	/* Complete all the transmit packet */
+	while (lp->tx_complete_idx != lp->tx_idx){
+		tx_index =  lp->tx_complete_idx & TX_RING_DR_MOD_MASK;
+		status = le16_to_cpu(lp->tx_ring[tx_index].tx_dr_offset2);
+
+		if(status & OWN_BIT)
+			break;	/* It still hasn't been Txed */
+
+		lp->tx_ring[tx_index].buff_phy_addr = 0;
+
+		/* We must free the original skb */
+		if (lp->tx_skbuff[tx_index]) {
+			pci_unmap_single(lp->pci_dev, lp->tx_dma_addr[tx_index],
+				  	lp->tx_skbuff[tx_index]->len,
+					PCI_DMA_TODEVICE);
+			dev_kfree_skb_irq (lp->tx_skbuff[tx_index]);
+			lp->tx_skbuff[tx_index] = 0;
+			lp->tx_dma_addr[tx_index] = 0;
+		}
+		lp->tx_complete_idx++;	
+
+		if (netif_queue_stopped(dev) &&
+			lp->tx_complete_idx > lp->tx_idx - NUM_TX_BUFFERS +2){
+			/* The ring is no longer full, clear tbusy. */
+			netif_wake_queue (dev);
+		}
+	}
+	return 0;
+}
+
+/* 
+This function will check the ownership of receive buffers and descriptors. It will indicate to kernel up to half the number of maximum receive buffers in the descriptor ring, in a single receive interrupt. It will also replenish the descriptors with new skbs.
+*/
+static int amd8111e_rx(struct net_device *dev)
+{
+	struct amd8111e_priv *lp = dev->priv;
+	struct sk_buff *skb,*new_skb;
+	int rx_index = lp->rx_idx & RX_RING_DR_MOD_MASK;
+	int min_pkt_len, status;
+	int num_rx_pkt = 0;
+	int max_rx_pkt = NUM_RX_BUFFERS/2;
+	short pkt_len;
+#if AMD8111E_VLAN_TAG_USED		
+	short vtag;
+#endif
+	
+	/* If we own the next entry, it's a new packet. Send it up. */
+	while(++num_rx_pkt <= max_rx_pkt){
+		if(lp->rx_ring[rx_index].rx_dr_offset10 & OWN_BIT)
+			return 0;
+	       
+		/* check if err summary bit is set */ 
+		if(le16_to_cpu(lp->rx_ring[rx_index].rx_dr_offset10) & ERR_BIT){
+			/* 
+			 * There is a tricky error noted by John Murphy,
+			 * <murf@perftech.com> to Russ Nelson: Even with full-sized
+			 * buffers it's possible for a jabber packet to use two
+			 * buffers, with only the last correctly noting the error.			 */
+			/* reseting flags */
+			lp->rx_ring[rx_index].rx_dr_offset10 &= 
+						cpu_to_le16(RESET_RX_FLAGS);
+			goto err_next_pkt;
+		}
+		/* check for STP and ENP */
+		status = le16_to_cpu(lp->rx_ring[rx_index].rx_dr_offset10);
+		if(!((status & STP_BIT) && (status & ENP_BIT))){
+			/* reseting flags */
+			lp->rx_ring[rx_index].rx_dr_offset10 &= 
+						cpu_to_le16(RESET_RX_FLAGS);
+			goto err_next_pkt;
+		}
+		pkt_len = le16_to_cpu(lp->rx_ring[rx_index].msg_count) - 4;
+
+#if AMD8111E_VLAN_TAG_USED		
+		vtag = le16_to_cpu(lp->rx_ring[rx_index].rx_dr_offset10) & TT_MASK;
+		/*MAC will strip vlan tag*/ 
+		if(lp->vlgrp != NULL && vtag !=0)
+			min_pkt_len =MIN_PKT_LEN - 4;
+		else
+#endif
+			min_pkt_len =MIN_PKT_LEN;
+
+		if (pkt_len < min_pkt_len) {
+			lp->rx_ring[rx_index].rx_dr_offset10 &= 
+				cpu_to_le16(RESET_RX_FLAGS);
+			lp->stats.rx_errors++;
+			goto err_next_pkt;
+		}
+		if(!(new_skb = dev_alloc_skb(lp->rx_buff_len))){
+			/* if allocation fail, 
+				ignore that pkt and go to next one */
+			lp->rx_ring[rx_index].rx_dr_offset10 &= 
+				cpu_to_le16(RESET_RX_FLAGS);
+			lp->stats.rx_errors++;
+			goto err_next_pkt;
+		}
+		
+		skb_reserve(new_skb, 2);
+		skb = lp->rx_skbuff[rx_index];
+		pci_unmap_single(lp->pci_dev,lp->rx_dma_addr[rx_index],
+			lp->rx_buff_len-2, PCI_DMA_FROMDEVICE);
+		skb_put(skb, pkt_len);
+		skb->dev = dev;
+		lp->rx_skbuff[rx_index] = new_skb;
+		new_skb->dev = dev;
+		lp->rx_dma_addr[rx_index] = pci_map_single(lp->pci_dev,
+			new_skb->data, lp->rx_buff_len-2,PCI_DMA_FROMDEVICE);
+	
+		skb->protocol = eth_type_trans(skb, dev);
+
+#if AMD8111E_VLAN_TAG_USED		
+		
+		vtag = lp->rx_ring[rx_index].rx_dr_offset10 & TT_MASK;
+		if(lp->vlgrp != NULL && (vtag == TT_VLAN_TAGGED)){
+			amd8111e_vlan_rx(lp, skb,
+				    lp->rx_ring[rx_index].tag_ctrl_info);
+		} else
+#endif
+			
+			dev->last_rx = jiffies;
+			netif_rx (skb);
+	
+err_next_pkt:
+		lp->rx_ring[rx_index].buff_phy_addr
+			 = cpu_to_le32(lp->rx_dma_addr[rx_index]);
+		lp->rx_ring[rx_index].buff_count = 
+				cpu_to_le16(lp->rx_buff_len-2);
+		lp->rx_ring[rx_index].rx_dr_offset10 |= cpu_to_le16(OWN_BIT);
+		rx_index = (++lp->rx_idx) & RX_RING_DR_MOD_MASK;
+	}
+
+	return 0;
+}
+
+/* 
+This function will store the original speed to restore later, if autoneg is turned on. This speed will be set later when the autoneg is turned off. If the link status indicates that link is down, that will be indicated to the kernel */
+
+static int amd8111e_link_change(struct net_device* dev)
+{	
+	struct amd8111e_priv *lp = dev->priv;
+	int status0,speed;
+
+	/* read the link change */
+     	status0 = readl(lp->mmio + STAT0);
+	
+	if(status0 & LINK_STATS){
+		if(status0 & AUTONEG_COMPLETE){
+			/* keeping the original speeds */
+			if((lp->link_config.speed != SPEED_INVALID)&&
+				(lp->link_config.duplex != DUPLEX_INVALID)){
+			lp->link_config.orig_speed = lp->link_config.speed;
+			lp->link_config.orig_duplex = lp->link_config.duplex;
+			lp->link_config.orig_phy_option = lp->ext_phy_option;
+			}
+	
+			lp->link_config.speed = SPEED_INVALID;
+			lp->link_config.duplex = DUPLEX_INVALID;
+			lp->link_config.autoneg = AUTONEG_ENABLE;
+			netif_carrier_on(dev);
+			return 0;
+		}
+		if(status0 & FULL_DPLX)
+			lp->link_config.duplex = DUPLEX_FULL;
+		else 
+			lp->link_config.duplex = DUPLEX_HALF;
+		speed = (status0 & SPEED_MASK) >> 7;
+		if(speed == PHY_SPEED_10)
+			lp->link_config.speed = SPEED_10;
+		else if(speed == PHY_SPEED_100)
+			lp->link_config.speed = SPEED_100;
+		lp->link_config.autoneg = AUTONEG_DISABLE;
+		netif_carrier_on(dev);
+	}
+	else{	
+		lp->link_config.speed = SPEED_INVALID;
+		lp->link_config.duplex = DUPLEX_INVALID;
+		lp->link_config.autoneg = AUTONEG_INVALID;
+		netif_carrier_off(dev);
+	}
+		
+	return 0;
+}
+/*
+This function reads the mib counters. 	 
+*/
+static int amd8111e_read_mib(void* mmio, u8 MIB_COUNTER)
+{
+	unsigned int  status;
+	unsigned  int data;
+	unsigned int repeat = REPEAT_CNT;
+
+	writew( MIB_RD_CMD | MIB_COUNTER, mmio + MIB_ADDR);
+	do {
+		status = readw(mmio + MIB_ADDR);
+		udelay(2);	/* controller takes MAX 2 us to get mib data */
+	}
+	while (--repeat && (status & MIB_CMD_ACTIVE));
+
+	data = readl(mmio + MIB_DATA);
+	return data;
+}
+
+/*
+This function retuurns the reads the mib registers and returns the hardware statistics. It adds the previous statistics with new values.*/ 
+static struct net_device_stats *amd8111e_get_stats(struct net_device * dev)
+{
+	struct amd8111e_priv *lp = dev->priv;
+	void * mmio = lp->mmio;
+	unsigned long flags;
+	struct net_device_stats *prev_stats = &lp->prev_stats;
+	struct net_device_stats* new_stats = &lp->stats;
+	
+	if(!lp->opened)
+		return prev_stats;	
+	spin_lock_irqsave (&lp->lock, flags);
+
+	/* stats.rx_packets */
+	new_stats->rx_packets = prev_stats->rx_packets+
+		amd8111e_read_mib(mmio, rcv_broadcast_pkts)+
+		amd8111e_read_mib(mmio, rcv_multicast_pkts)+
+		amd8111e_read_mib(mmio, rcv_unicast_pkts);
+
+	/* stats.tx_packets */
+	new_stats->tx_packets = prev_stats->tx_packets+
+		amd8111e_read_mib(mmio, xmt_packets);
+
+	/*stats.rx_bytes */
+	new_stats->rx_bytes = prev_stats->rx_bytes+
+		amd8111e_read_mib(mmio, rcv_octets);
+
+	/* stats.tx_bytes */
+	new_stats->tx_bytes = prev_stats->tx_bytes+
+		amd8111e_read_mib(mmio, xmt_octets);
+
+	/* stats.rx_errors */
+	new_stats->rx_errors = prev_stats->rx_errors+
+		amd8111e_read_mib(mmio, rcv_undersize_pkts)+
+		amd8111e_read_mib(mmio, rcv_fragments)+
+		amd8111e_read_mib(mmio, rcv_jabbers)+
+		amd8111e_read_mib(mmio, rcv_alignment_errors)+
+		amd8111e_read_mib(mmio, rcv_fcs_errors)+
+		amd8111e_read_mib(mmio, rcv_miss_pkts);
+
+	/* stats.tx_errors */
+	new_stats->tx_errors = prev_stats->tx_errors+
+		amd8111e_read_mib(mmio, xmt_underrun_pkts);
+
+	/* stats.rx_dropped*/
+	new_stats->rx_dropped = prev_stats->rx_dropped+
+		amd8111e_read_mib(mmio, rcv_miss_pkts);
+
+	/* stats.tx_dropped*/
+	new_stats->tx_dropped = prev_stats->tx_dropped+
+		amd8111e_read_mib(mmio,  xmt_underrun_pkts);
+
+	/* stats.multicast*/
+	new_stats->multicast = prev_stats->multicast+
+		amd8111e_read_mib(mmio, rcv_multicast_pkts);
+
+	/* stats.collisions*/
+	new_stats->collisions = prev_stats->collisions+
+		amd8111e_read_mib(mmio, xmt_collisions);
+
+	/* stats.rx_length_errors*/
+	new_stats->rx_length_errors = prev_stats->rx_length_errors+
+		amd8111e_read_mib(mmio, rcv_undersize_pkts)+
+		amd8111e_read_mib(mmio, rcv_oversize_pkts);
+
+	/* stats.rx_over_errors*/
+	new_stats->rx_over_errors = prev_stats->rx_over_errors+
+		amd8111e_read_mib(mmio, rcv_miss_pkts);
+
+	/* stats.rx_crc_errors*/
+	new_stats->rx_crc_errors = prev_stats->rx_crc_errors+
+		amd8111e_read_mib(mmio, rcv_fcs_errors);
+
+	/* stats.rx_frame_errors*/
+	new_stats->rx_frame_errors = prev_stats->rx_frame_errors+
+		amd8111e_read_mib(mmio, rcv_alignment_errors);
+
+	/* stats.rx_fifo_errors */
+	new_stats->rx_fifo_errors = prev_stats->rx_fifo_errors+
+		amd8111e_read_mib(mmio, rcv_miss_pkts);
+
+	/* stats.rx_missed_errors */
+	new_stats->rx_missed_errors = prev_stats->rx_missed_errors+
+		amd8111e_read_mib(mmio, rcv_miss_pkts);
+
+	/* stats.tx_aborted_errors*/
+	new_stats->tx_aborted_errors = prev_stats->tx_aborted_errors+
+		amd8111e_read_mib(mmio, xmt_excessive_collision);
+
+	/* stats.tx_carrier_errors*/
+	new_stats->tx_carrier_errors = prev_stats->tx_carrier_errors+
+		amd8111e_read_mib(mmio, xmt_loss_carrier);
+
+	/* stats.tx_fifo_errors*/
+	new_stats->tx_fifo_errors = prev_stats->tx_fifo_errors+
+		amd8111e_read_mib(mmio, xmt_underrun_pkts);
+
+	/* stats.tx_window_errors*/
+	new_stats->tx_window_errors = prev_stats->tx_window_errors+
+		amd8111e_read_mib(mmio, xmt_late_collision);
+
+	spin_unlock_irqrestore (&lp->lock, flags);
+
+	return new_stats;
+}
+
+/*
+This is device interrupt function. It handles transmit, receive and link change interrupts.
+*/
+static void amd8111e_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+
+	struct net_device * dev = (struct net_device *) dev_id;
+	struct amd8111e_priv *lp = dev->priv;
+	void * mmio = lp->mmio;
+	unsigned int intr0;
+
+	if(dev == NULL)
+		return;
+
+	spin_lock (&lp->lock);
+	/* disabling interrupt */
+	writel(INTREN, mmio + CMD0);
+
+	/* Read interrupt status */
+	intr0 = readl(mmio + INT0);
+
+	/* Process all the INT event until INTR bit is clear. */
+
+	if (!(intr0 & INTR))
+		goto err_no_interrupt;
+		 
+	/* Current driver processes 3 interrupts : RINT,TINT,LCINT */
+	writel(intr0, mmio + INT0);
+
+	/* Check if Receive Interrupt has occurred. */
+	if(intr0 & RINT0){
+		amd8111e_rx(dev);
+		writel(VAL2 | RDMD0, mmio + CMD0);
+	}
+
+	/* Check if  Transmit Interrupt has occurred. */
+	if(intr0 & TINT0)
+		amd8111e_tx(dev);
+		
+	/* Check if  Link Change Interrupt has occurred. */
+	if (intr0 & LCINT)
+		amd8111e_link_change(dev);
+	
+err_no_interrupt:
+	writel( VAL0 | INTREN,mmio + CMD0);
+	spin_unlock(&lp->lock);
+	return;
+
+}
+/*
+This function closes the network interface and copies the new set of statistics into the previous statistics structure so that most recent statistics will be available after the interface is down.
+*/
+static int amd8111e_close(struct net_device * dev)
+{
+	struct amd8111e_priv *lp = dev->priv;
+	netif_stop_queue(dev);
+	
+	spin_lock_irq(&lp->lock);
+	
+	amd8111e_disable_interrupt(lp);
+	amd8111e_stop_chip(lp);
+	amd8111e_free_ring(lp);
+	
+	netif_carrier_off(lp->amd8111e_net_dev);
+
+	spin_unlock_irq(&lp->lock);
+
+	free_irq(dev->irq, dev);
+	memcpy(&lp->prev_stats,amd8111e_get_stats(dev), sizeof(lp->prev_stats));
+	lp->opened = 0;
+	return 0;
+}
+/* This function opens new interface.It requests irq for the device, initializes the device,buffers and descriptors, and starts the device. 
+*/
+static int amd8111e_open(struct net_device * dev )
+{
+	struct amd8111e_priv *lp = (struct amd8111e_priv *)dev->priv;
+
+	if(dev->irq ==0 || request_irq(dev->irq, amd8111e_interrupt, SA_SHIRQ,
+					 dev->name, dev)) 
+		return -EAGAIN;
+
+	spin_lock_irq(&lp->lock);
+
+	amd8111e_init_hw_default(lp);
+
+	if(amd8111e_restart(dev)){
+		spin_unlock_irq(&lp->lock);
+		return -ENOMEM;
+	}
+	
+	lp->opened = 1;
+
+	spin_unlock_irq(&lp->lock);
+
+	netif_start_queue(dev);
+
+	return 0;		
+}
+/* 
+This function checks if there is any transmit  descriptors available to queue more packet.
+*/
+static int amd8111e_tx_queue_avail(struct amd8111e_priv* lp )
+{	
+	int tx_index = lp->tx_idx & TX_BUFF_MOD_MASK;
+	if(lp->tx_skbuff[tx_index] != 0)
+		return -1;
+	else
+		return 0;
+	
+}
+/* 
+This function will queue the transmit packets to the descriptors and will trigger the send operation. It also initializes the transmit descriptors with buffer physical address, byte count, ownership to hardware etc.
+*/
+
+static int amd8111e_start_xmit(struct sk_buff *skb, struct net_device * dev)
+{
+	struct amd8111e_priv *lp = dev->priv;
+	int tx_index;
+	unsigned long flags;
+
+	spin_lock_irqsave(&lp->lock, flags);
+
+	tx_index = lp->tx_idx & TX_RING_DR_MOD_MASK;
+
+	lp->tx_ring[tx_index].buff_count = cpu_to_le16(skb->len);
+
+	lp->tx_skbuff[tx_index] = skb;
+	lp->tx_ring[tx_index].tx_dr_offset2 = 0;
+
+#if AMD8111E_VLAN_TAG_USED
+	if((lp->vlgrp != NULL) && vlan_tx_tag_present(skb)){
+
+		lp->tx_ring[tx_index].tag_ctrl_cmd |= 
+				cpu_to_le32(TCC_VLAN_INSERT);	
+		lp->tx_ring[tx_index].tag_ctrl_info = 
+				cpu_to_le16(vlan_tx_tag_get(skb));
+
+	}
+#endif
+	lp->tx_dma_addr[tx_index] =
+	    pci_map_single(lp->pci_dev, skb->data, skb->len, PCI_DMA_TODEVICE);
+	lp->tx_ring[tx_index].buff_phy_addr =
+	    (u32) cpu_to_le32(lp->tx_dma_addr[tx_index]);
+
+	/*  Set FCS and LTINT bits */
+	lp->tx_ring[tx_index].tx_dr_offset2 |=
+	    cpu_to_le16(OWN_BIT | STP_BIT | ENP_BIT|ADD_FCS_BIT|LTINT_BIT);
+
+	lp->tx_idx++;
+
+	/* Trigger an immediate send poll. */
+	writel( VAL1 | TDMD0, lp->mmio + CMD0);
+	writel( VAL2 | RDMD0,lp->mmio + CMD0);
+
+	dev->trans_start = jiffies;
+
+	if(amd8111e_tx_queue_avail(lp) < 0){
+		netif_stop_queue(dev);
+	}
+	spin_unlock_irqrestore(&lp->lock, flags);
+	return 0;
+}
+/*
+This function returns all the memory mapped registers of the device.
+*/
+static char* amd8111e_read_regs(struct amd8111e_priv* lp)
+{    	
+	void * mmio = lp->mmio;
+        unsigned char * reg_buff;
+
+     	int i;
+     
+     	reg_buff = kmalloc( AMD8111E_REG_DUMP_LEN,GFP_KERNEL);
+	if(NULL == reg_buff)
+		return NULL;
+    	for( i=0; i< AMD8111E_REG_DUMP_LEN;i+=4);
+		reg_buff[i]= readl(mmio + i);	
+	return reg_buff;
+}
+/*
+This function sets promiscuos mode, all-multi mode or the multicast address 
+list to the device.
+*/
+static void amd8111e_set_multicast_list(struct net_device *dev)
+{
+	struct dev_mc_list* mc_ptr;
+	struct amd8111e_priv *lp = dev->priv;
+	u32 mc_filter[2] ;
+	int i,bit_num;
+
+	if(dev->flags & IFF_PROMISC){
+		printk("%s: Setting  promiscuous mode.\n",dev->name);
+		writel( VAL2 | PROM, lp->mmio + CMD2);
+		return;
+	}
+	else
+		writel( PROM, lp->mmio + CMD2);
+	if(dev->flags & IFF_ALLMULTI || dev->mc_count > MAX_FILTER_SIZE){
+		/* get all multicast packet */
+		mc_filter[1] = mc_filter[0] = 0xffffffff;
+		lp->mc_list = dev->mc_list;
+		lp->options |= OPTION_MULTICAST_ENABLE;
+		AMD8111E_WRITE_REG64(lp->mmio, LADRF,mc_filter);
+		return;
+	}
+	if( dev->mc_count == 0 ){
+		/* get only own packets */
+		mc_filter[1] = mc_filter[0] = 0;
+		lp->mc_list = 0;
+		lp->options &= ~OPTION_MULTICAST_ENABLE;
+		AMD8111E_WRITE_REG64(lp->mmio, LADRF,mc_filter);
+		/* disable promiscous mode */
+		writel(PROM, lp->mmio + CMD2);
+		return;
+	}
+	/* load all the multicast addresses in the logic filter */
+	lp->options |= OPTION_MULTICAST_ENABLE;
+	lp->mc_list = dev->mc_list;
+	mc_filter[1] = mc_filter[0] = 0;
+	for (i = 0, mc_ptr = dev->mc_list; mc_ptr && i < dev->mc_count;
+		     i++, mc_ptr = mc_ptr->next) {
+		bit_num = ether_crc(ETH_ALEN, mc_ptr->dmi_addr) >> 26;
+			
+		mc_filter[bit_num >> 5] |= 1 << (bit_num & 31);
+	}	
+
+	AMD8111E_WRITE_REG64(lp->mmio, LADRF, mc_filter);
+	return;
+}
+/*
+This function handles all the  ethtool ioctls. It gives driver info, gets/sets driver speed, gets memory mapped register values, forces auto negotiation, sets/gets WOL options for ethtool application. 
+*/
+	
+static int amd8111e_ethtool_ioctl(struct net_device* dev, void* useraddr)
+{
+	struct amd8111e_priv *lp = dev->priv;
+	struct pci_dev *pci_dev = lp->pci_dev;
+	u32 ethcmd;
+	
+	if( useraddr == NULL) 
+		return -EINVAL;
+	if(copy_from_user (&ethcmd, useraddr, sizeof (ethcmd)))
+		return -EFAULT;
+	
+	switch(ethcmd){
+	
+	case ETHTOOL_GDRVINFO:{
+		struct ethtool_drvinfo info = { ETHTOOL_GDRVINFO };
+		strcpy (info.driver, MODULE_NAME);
+		strcpy (info.version, MODULE_VERSION);
+		memset(&info.fw_version, 0, sizeof(info.fw_version));
+		strcpy (info.bus_info, pci_dev->slot_name);
+		info.eedump_len = 0;
+		info.regdump_len = AMD8111E_REG_DUMP_LEN;
+		if (copy_to_user (useraddr, &info, sizeof(info)))
+			return -EFAULT;
+		return 0;
+	}
+	case ETHTOOL_GSET:{
+		struct ethtool_cmd cmd = { ETHTOOL_GSET };
+
+		if (!lp->opened) 
+			return -EAGAIN;
+
+		cmd.supported = SUPPORTED_Autoneg |
+				SUPPORTED_100baseT_Half |
+				SUPPORTED_100baseT_Full |
+			  	SUPPORTED_10baseT_Half |
+			        SUPPORTED_10baseT_Full |
+			        SUPPORTED_MII;
+
+		cmd.advertising = ADVERTISED_Autoneg |
+				ADVERTISED_100baseT_Half |
+				ADVERTISED_100baseT_Full |
+			  	ADVERTISED_10baseT_Half |
+			        ADVERTISED_10baseT_Full |
+			        ADVERTISED_MII;
+		cmd.speed = lp->link_config.speed;
+		cmd.duplex = lp->link_config.duplex;
+		cmd.port = 0;
+		cmd.phy_address = PHY_ID;
+		cmd.transceiver = XCVR_EXTERNAL;
+		cmd.autoneg = lp->link_config.autoneg;
+		cmd.maxtxpkt = 0; /* not implemented interrupt coalasing */
+		cmd.maxrxpkt = 0; /* not implemented interrupt coalasing */
+		if (copy_to_user(useraddr, &cmd, sizeof(cmd)))
+			return -EFAULT;
+		return 0;
+		}
+	case ETHTOOL_SSET: {
+	
+		struct ethtool_cmd cmd;
+
+		if (!lp->opened)
+			return -EAGAIN;
+		if (copy_from_user(&cmd, useraddr, sizeof(cmd)))
+			return -EFAULT;
+
+		spin_lock_irq(&lp->lock);
+
+		if(cmd.autoneg == AUTONEG_ENABLE){
+			/* keeping the original speeds */
+			if((lp->link_config.speed != SPEED_INVALID)&&
+				(lp->link_config.duplex != DUPLEX_INVALID)){
+			lp->link_config.orig_speed = lp->link_config.speed;
+			lp->link_config.orig_duplex = lp->link_config.duplex;
+			lp->link_config.orig_phy_option = lp->ext_phy_option;
+			}
+
+			lp->ext_phy_option = XPHYANE;
+		}
+		else if(cmd.speed == SPEED_100 && cmd.duplex ==  DUPLEX_HALF)
+			lp->ext_phy_option = XPHYSP;
+		else if(cmd.speed == SPEED_100 && cmd.duplex ==  DUPLEX_FULL)
+			lp->ext_phy_option = XPHYSP |XPHYFD;
+		else if(cmd.speed == SPEED_10 && cmd.duplex ==  DUPLEX_HALF)
+			lp->ext_phy_option = 0;
+		else if(cmd.speed == SPEED_10 && cmd.duplex ==  DUPLEX_FULL)
+			lp->ext_phy_option = XPHYFD;
+		else {	
+			/* setting the original speed */
+			cmd.speed = lp->link_config.orig_speed;
+			cmd.duplex = lp->link_config.orig_duplex;
+			lp->ext_phy_option = lp->link_config.orig_phy_option;
+		}
+		lp->link_config.autoneg = cmd.autoneg;
+		if (cmd.autoneg == AUTONEG_ENABLE) {
+			
+			lp->link_config.speed = SPEED_INVALID;
+			lp->link_config.duplex = DUPLEX_INVALID;
+		} else {
+			lp->link_config.speed = cmd.speed;
+			lp->link_config.duplex = cmd.duplex;
+		}
+		amd8111e_set_ext_phy(dev);
+		spin_unlock_irq(&lp->lock);
+		return 0;
+	}
+	case ETHTOOL_GREGS: {
+		struct ethtool_regs regs;
+		u8 *regbuf;
+		int ret;
+
+		if (copy_from_user(&regs, useraddr, sizeof(regs)))
+			return -EFAULT;
+		if (regs.len > AMD8111E_REG_DUMP_LEN)
+			regs.len = AMD8111E_REG_DUMP_LEN;
+		regs.version = 0;
+		if (copy_to_user(useraddr, &regs, sizeof(regs)))
+			return -EFAULT;
+
+		regbuf = amd8111e_read_regs(lp);
+		if (!regbuf)
+			return -ENOMEM;
+
+		useraddr += offsetof(struct ethtool_regs, data);
+		ret = 0;
+		if (copy_to_user(useraddr, regbuf, regs.len))
+			ret = -EFAULT;
+		kfree(regbuf);
+		return ret;
+	}
+	case ETHTOOL_NWAY_RST: {
+		int ret;
+		spin_lock_irq(&lp->lock);
+		if(lp->link_config.autoneg == AUTONEG_ENABLE){
+			lp->ext_phy_option = XPHYANE;
+			amd8111e_set_ext_phy(dev);
+			ret = 0;
+		}else
+			ret =  -EINVAL;
+		spin_unlock_irq(&lp->lock);
+		return ret;
+	}
+	case ETHTOOL_GLINK: {
+		struct ethtool_value val = { ETHTOOL_GLINK };
+
+		val.data = netif_carrier_ok(dev) ? 1 : 0;
+		if (copy_to_user(useraddr, &val, sizeof(val)))
+			return -EFAULT;
+	}
+	case ETHTOOL_GWOL: {
+		struct ethtool_wolinfo wol_info = { ETHTOOL_GWOL };
+
+		wol_info.supported = WAKE_MAGIC|WAKE_PHY;
+		wol_info.wolopts = 0;
+		if (lp->options & OPTION_WOL_ENABLE)
+			wol_info.wolopts = WAKE_MAGIC;
+		memset(&wol_info.sopass, 0, sizeof(wol_info.sopass));
+		if (copy_to_user(useraddr, &wol_info, sizeof(wol_info)))
+			return -EFAULT;
+		return 0;
+	}
+	case ETHTOOL_SWOL: {
+		struct ethtool_wolinfo wol_info;
+
+		if (copy_from_user(&wol_info, useraddr, sizeof(wol_info)))
+			return -EFAULT;
+		if (wol_info.wolopts & ~(WAKE_MAGIC |WAKE_PHY))
+			return -EINVAL;
+		spin_lock_irq(&lp->lock);
+		if(wol_info.wolopts & WAKE_MAGIC)
+			lp->options |= 
+				(OPTION_WOL_ENABLE | OPTION_WAKE_MAGIC_ENABLE);
+		else if(wol_info.wolopts & WAKE_PHY)
+			lp->options |= 
+				(OPTION_WOL_ENABLE | OPTION_WAKE_PHY_ENABLE);
+		else
+			lp->options &= ~OPTION_WOL_ENABLE; 
+		spin_unlock_irq(&lp->lock);
+		return 0;
+	}
+	
+	default:
+		break;
+	}
+		return -EOPNOTSUPP;
+}
+static int amd8111e_read_phy(struct amd8111e_priv* lp, int phy_id, int reg, u32* val)
+{
+	void * mmio = lp->mmio;
+	unsigned int reg_val;
+	unsigned int repeat= REPEAT_CNT;
+
+	reg_val = readl(mmio + PHY_ACCESS);
+	while (reg_val & PHY_CMD_ACTIVE)
+		reg_val = readl( mmio + PHY_ACCESS );
+
+	writel( PHY_RD_CMD | ((phy_id & 0x1f) << 21) |
+			   ((reg & 0x1f) << 16),  mmio +PHY_ACCESS);
+	do{
+		reg_val = readl(mmio + PHY_ACCESS);
+		udelay(30);  /* It takes 30 us to read/write data */
+	} while (--repeat && (reg_val & PHY_CMD_ACTIVE));
+	if(reg_val & PHY_RD_ERR)
+		goto err_phy_read;
+	
+	*val = reg_val & 0xffff;
+	return 0;
+err_phy_read:	
+	*val = 0;
+	return -EINVAL;
+	
+}
+static int amd8111e_write_phy(struct amd8111e_priv* lp,int phy_id, int reg, u32 val)
+{
+	unsigned int repeat = REPEAT_CNT
+	void * mmio = lp->mmio;
+	unsigned int reg_val;
+	
+
+	reg_val = readl(mmio + PHY_ACCESS);
+	while (reg_val & PHY_CMD_ACTIVE)
+		reg_val = readl( mmio + PHY_ACCESS );
+
+	writel( PHY_WR_CMD | ((phy_id & 0x1f) << 21) |
+			   ((reg & 0x1f) << 16)|val, mmio + PHY_ACCESS);
+
+	do{
+		reg_val = readl(mmio + PHY_ACCESS);
+		udelay(30);  /* It takes 30 us to read/write the data */
+	} while (--repeat && (reg_val & PHY_CMD_ACTIVE));
+	
+	if(reg_val & PHY_RD_ERR)
+		goto err_phy_write;
+	
+	return 0;
+
+err_phy_write:	
+	return -EINVAL;
+	
+}
+static int amd8111e_ioctl(struct net_device * dev , struct ifreq *ifr, int cmd)
+{
+	struct mii_ioctl_data *data = (struct mii_ioctl_data *)&ifr->ifr_data;
+	struct amd8111e_priv *lp = dev->priv;
+	int err;
+	u32 mii_regval;
+
+	if (!capable(CAP_NET_ADMIN))
+		return -EPERM;
+
+	switch(cmd) {
+	case SIOCETHTOOL:
+		return amd8111e_ethtool_ioctl(dev, (void *) ifr->ifr_data);
+	case SIOCGMIIPHY:
+		data->phy_id = PHY_ID;
+
+	/* fallthru */
+	case SIOCGMIIREG: 
+
+		spin_lock_irq(&lp->lock);
+		err = amd8111e_read_phy(lp, data->phy_id,
+			data->reg_num & PHY_REG_ADDR_MASK, &mii_regval);
+		spin_unlock_irq(&lp->lock);
+
+		data->val_out = mii_regval;
+		return err;
+
+	case SIOCSMIIREG:
+
+		spin_lock_irq(&lp->lock);
+		err = amd8111e_write_phy(lp, data->phy_id,
+			data->reg_num & PHY_REG_ADDR_MASK, data->val_in);
+		spin_unlock_irq(&lp->lock);
+
+		return err;
+
+	default:
+		/* do nothing */
+		break;
+	}
+	return -EOPNOTSUPP;
+}
+/* 
+This function changes the mtu of the device. It restarts the device  to initialize the descriptor with new receive buffers.
+*/  
+int amd8111e_change_mtu(struct net_device *dev, int new_mtu)
+{
+	struct amd8111e_priv *lp = dev->priv;
+	int err;
+
+	if ((new_mtu < AMD8111E_MIN_MTU) || (new_mtu > AMD8111E_MAX_MTU))
+		return -EINVAL;
+
+	if (!netif_running(dev)) {
+		/* new_mtu will be used
+		   when device starts netxt time */ 
+		dev->mtu = new_mtu;
+		return 0;
+	}
+
+	spin_lock_irq(&lp->lock);
+
+        /* stop the chip */
+	writel(RUN, lp->mmio + CMD0);
+
+	dev->mtu = new_mtu;
+
+	/* if (new_mtu > ETH_DATA_LEN)
+		lp->options |= OPTION_JUMBO_ENABLE;
+	else
+		lp->options &= ~OPTION_JUMBO_ENABLE;
+	*/
+	err = amd8111e_restart(dev);
+	spin_unlock_irq(&lp->lock);
+
+	netif_start_queue(dev);
+	return err;
+}
+
+#if AMD8111E_VLAN_TAG_USED
+static void amd8111e_vlan_rx_register(struct net_device *dev, struct vlan_group *grp)
+{
+	struct  amd8111e_priv *lp = dev->priv;
+	spin_lock_irq(&lp->lock);
+	lp->vlgrp = grp;
+	spin_unlock_irq(&lp->lock);
+}
+	
+static void amd8111e_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
+{
+	struct amd8111e_priv *lp = dev->priv;
+	spin_lock_irq(&lp->lock);
+	if (lp->vlgrp)
+		lp->vlgrp->vlan_devices[vid] = NULL;
+	spin_unlock_irq(&lp->lock);
+}
+#endif
+static int amd8111e_enable_magicpkt(struct amd8111e_priv* lp)
+{
+	writel( VAL1|MPPLBA, lp->mmio + CMD3);
+	writel( VAL0|MPEN_SW, lp->mmio + CMD7);
+	return 0;
+}
+
+static int amd8111e_enable_link_change(struct amd8111e_priv* lp)
+{
+	/* Adapter is already stoped/suspended/interrupt-disabled */
+	writel(VAL0|LCMODE_SW,lp->mmio + CMD7);
+	return 0;
+}	
+
+/* 
+This function sets the power state of the device. When the device go to lower power states 1,2, and 3 it enables the wake on lan 
+*/  	
+static int amd8111e_set_power_state(struct amd8111e_priv* lp, u32 state)
+{
+	u16 power_control;
+	int pm = lp->pm_cap;
+
+	pci_read_config_word(lp->pci_dev,
+			     pm + PCI_PM_CTRL,
+			     &power_control);
+
+	power_control |= PCI_PM_CTRL_PME_STATUS;
+	power_control &= ~(PCI_PM_CTRL_STATE_MASK);
+	switch (state) {
+	case 0:
+		power_control |= 0;
+		pci_write_config_word(lp->pci_dev,
+				      pm + PCI_PM_CTRL,
+				      power_control);
+		return 0;
+
+	case 1:
+		power_control |= 1;
+		break;
+
+	case 2:
+		power_control |= 2;
+		break;
+
+	case 3:
+		power_control |= 3;
+		break;
+	default:
+
+		printk(KERN_WARNING "%s: Invalid power state (%d) requested.\n",
+		       lp->amd8111e_net_dev->name, state);
+		return -EINVAL;
+	}
+	
+	if(lp->options & OPTION_WAKE_MAGIC_ENABLE)
+		amd8111e_enable_magicpkt(lp);	
+	if(lp->options & OPTION_WAKE_PHY_ENABLE)
+		amd8111e_enable_link_change(lp);	
+
+	/*  Setting new power state. */
+	pci_write_config_word(lp->pci_dev, pm + PCI_PM_CTRL, power_control);
+
+	return 0;
+
+
+}
+static int amd8111e_suspend(struct pci_dev *pci_dev, u32 state)
+{	
+	struct net_device *dev = pci_get_drvdata(pci_dev);
+	struct amd8111e_priv *lp = dev->priv;
+	int err;
+	
+	if (!netif_running(dev))
+		return 0;
+
+	/* disable the interrupt */
+	spin_lock_irq(&lp->lock);
+	amd8111e_disable_interrupt(lp);
+	spin_unlock_irq(&lp->lock);
+
+	netif_device_detach(dev);
+	
+	/* stop chip */
+	spin_lock_irq(&lp->lock);
+	amd8111e_stop_chip(lp);
+	spin_unlock_irq(&lp->lock);
+
+	err = amd8111e_set_power_state(lp, state);
+	if (err) {
+		
+		spin_lock_irq(&lp->lock);
+		amd8111e_restart(dev);
+		spin_unlock_irq(&lp->lock);
+
+		netif_device_attach(dev);
+	}
+	return err;
+}
+static int amd8111e_resume(struct pci_dev *pci_dev)
+{
+	struct net_device *dev = pci_get_drvdata(pci_dev);
+	struct amd8111e_priv *lp = dev->priv;
+	int err;
+	
+	if (!netif_running(dev))
+		return 0;
+
+	err = amd8111e_set_power_state(lp, 0);
+	if (err)
+		return err;
+
+	netif_device_attach(dev);
+
+	spin_lock_irq(&lp->lock);
+	amd8111e_restart(dev);
+	spin_unlock_irq(&lp->lock);
+
+	return 0;
+}
+
+
+static void __devexit amd8111e_remove_one(struct pci_dev *pdev)
+{
+	struct net_device *dev = pci_get_drvdata(pdev);
+	if (dev) {
+		unregister_netdev(dev);
+		iounmap((void *) ((struct amd8111e_priv *)(dev->priv))->mmio);
+		kfree(dev);
+		pci_release_regions(pdev);
+		pci_disable_device(pdev);
+		pci_set_drvdata(pdev, NULL);
+	}
+}
+
+static int __devinit amd8111e_probe_one(struct pci_dev *pdev,
+				  const struct pci_device_id *ent)
+{
+	int err,i,pm_cap;
+	unsigned long reg_addr,reg_len;
+	struct amd8111e_priv* lp;
+	struct net_device* dev;
+	unsigned int chip_version;
+
+	err = pci_enable_device(pdev);
+	if(err){
+		printk(KERN_ERR "amd8111e: Cannot enable new PCI device,"
+			"exiting.\n");
+		return err;
+	}
+
+	if(!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)){
+		printk(KERN_ERR "amd8111e: Cannot find PCI base address"
+		       "exiting.\n");
+		err = -ENODEV;
+		goto err_disable_pdev;
+	}
+
+	err = pci_request_regions(pdev, MODULE_NAME);
+	if(err){
+		printk(KERN_ERR "amd8111e: Cannot obtain PCI resources, "
+		       "exiting.\n");
+		goto err_disable_pdev;
+	}
+
+	pci_set_master(pdev);
+
+	/* Find power-management capability. */
+	if((pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM))==0){
+		printk(KERN_ERR "amd8111e: No Power Management capability, "
+		       "exiting.\n");
+		goto err_free_reg;
+	}
+
+	/* Initialize DMA */
+	if(!pci_dma_supported(pdev, 0xffffffff)){
+		printk(KERN_ERR "amd8111e: DMA not supported,"
+			"exiting.\n");
+		goto  err_free_reg;
+	} else
+		pdev->dma_mask = 0xffffffff;
+	
+	reg_addr = pci_resource_start(pdev, 0);
+	reg_len = pci_resource_len(pdev, 0);
+
+	dev = alloc_etherdev(sizeof(struct amd8111e_priv));
+	if (!dev) {
+		printk(KERN_ERR "amd8111e: Etherdev alloc failed, exiting.\n");
+		err = -ENOMEM;
+		goto err_free_reg;
+	}
+
+	SET_MODULE_OWNER(dev);
+
+#if AMD8111E_VLAN_TAG_USED
+	dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX ;
+	dev->vlan_rx_register =amd8111e_vlan_rx_register;
+	dev->vlan_rx_kill_vid = amd8111e_vlan_rx_kill_vid;
+#endif	
+	
+	lp = dev->priv;
+	memset (lp, 0, sizeof (*lp));
+	lp->pci_dev = pdev;
+	lp->amd8111e_net_dev = dev;
+	lp->pm_cap = pm_cap;
+
+	spin_lock_init(&lp->lock);
+
+	lp->mmio = ioremap(reg_addr, reg_len);
+	if (lp->mmio == 0) {
+		printk(KERN_ERR "amd8111e: Cannot map device registers, "
+		       "exiting\n");
+		err = -ENOMEM;
+		goto err_free_dev;
+	}
+	
+	/* Initializing MAC address */
+	for(i = 0; i < ETH_ADDR_LEN; i++)
+			dev->dev_addr[i] =readb(lp->mmio + PADR + i);
+	/* Setting user defined speed */
+	if (speed_duplex[card_idx] > sizeof(speed_duplex_mapping))
+		lp->ext_phy_option = XPHYANE;
+	else
+		lp->ext_phy_option = 
+				speed_duplex_mapping[speed_duplex[card_idx]];
+	/* Initialize driver entry points */
+	dev->open = amd8111e_open;
+	dev->hard_start_xmit = amd8111e_start_xmit;
+	dev->stop = amd8111e_close;
+	dev->get_stats = amd8111e_get_stats;
+	dev->set_multicast_list = amd8111e_set_multicast_list;
+	dev->do_ioctl = amd8111e_ioctl;
+	dev->change_mtu = amd8111e_change_mtu;
+	dev->irq =pdev->irq;
+
+#if AMD8111E_VLAN_TAG_USED
+	dev->features |= NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
+	dev->vlan_rx_register =amd8111e_vlan_rx_register;
+	dev->vlan_rx_kill_vid = amd8111e_vlan_rx_kill_vid;
+#endif	
+	
+	/* Set receive buffer length and set jumbo option*/
+	amd8111e_set_rx_buff_len(dev);
+	
+
+	/* dev->tx_timeout = tg3_tx_timeout; */
+	/* dev->watchdog_timeo = TG3_TX_TIMEOUT; */
+
+	err = register_netdev(dev);
+	if (err) {
+		printk(KERN_ERR "amd8111e: Cannot register net device, "
+		       "exiting.\n");
+		goto err_iounmap;
+	}
+
+	pci_set_drvdata(pdev, dev);
+	
+	/*  display driver and device information */
+
+    chip_version = (readl(lp->mmio + CHIPID) & 0xf0000000)>>28;
+    printk("%s: AMD-8111e Driver Version: %s\n",dev->name,MODULE_VERSION);
+    printk("%s: [ Rev %x ] PCI 10/100BaseT Ethernet ", dev->name, chip_version);
+    for (i = 0; i < 6; i++)
+	printk("%2.2x%c", dev->dev_addr[i],i == 5 ? ' ' : ':');
+     printk("\n");	
+	return 0;
+err_iounmap:
+	iounmap((void *) lp->mmio);
+
+err_free_dev:
+	kfree(dev);
+
+err_free_reg:
+	pci_release_regions(pdev);
+
+err_disable_pdev:
+	pci_disable_device(pdev);
+	pci_set_drvdata(pdev, NULL);
+	return err;
+
+}
+
+static struct pci_driver amd8111e_driver = {
+	name:		MODULE_NAME,
+	id_table:	amd8111e_pci_tbl,
+	probe:		amd8111e_probe_one,
+	remove:		__devexit_p(amd8111e_remove_one),
+	suspend:	amd8111e_suspend,
+	resume:		amd8111e_resume
+};
+
+static int __init amd8111e_init(void)
+{
+	return pci_module_init(&amd8111e_driver);
+}
+
+static void __exit amd8111e_cleanup(void)
+{
+	pci_unregister_driver(&amd8111e_driver);
+}
+
+module_init(amd8111e_init);
+module_exit(amd8111e_cleanup);
diff -urN linux-2.4.21-pre2/drivers/net/amd8111e.h linux-2.4.21-pre3/drivers/net/amd8111e.h
--- linux-2.4.21-pre2/drivers/net/amd8111e.h	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre3/drivers/net/amd8111e.h	Mon Jan  6 16:32:32 2003
@@ -0,0 +1,786 @@
+/*
+ * 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
+
+Module Name:
+
+    amd8111e.h
+
+Abstract:
+	
+ 	 AMD8111 based 10/100 Ethernet Controller driver definitions. 
+
+Environment:
+    
+	Kernel Mode
+
+Revision History:
+
+*/
+
+#ifndef _AMD811E_H
+#define _AMD811E_H
+
+/* Hardware definitions */
+
+#define B31_MASK	0x80000000
+#define B30_MASK	0X40000000
+#define B29_MASK	0x20000000
+#define B28_MASK	0x10000000
+#define B27_MASK	0x08000000
+#define B26_MASK	0x04000000
+#define B25_MASK	0x02000000
+#define B24_MASK	0x01000000
+#define B23_MASK	0x00800000
+#define B22_MASK	0x00400000
+#define B21_MASK	0x00200000
+#define B20_MASK	0x00100000
+#define B19_MASK	0x00080000
+#define B18_MASK	0x00040000
+#define B17_MASK	0x00020000
+#define B16_MASK	0x00010000
+
+#define B15_MASK	0x8000
+#define B14_MASK	0x4000
+#define B13_MASK	0x2000
+#define B12_MASK	0x1000
+#define B11_MASK	0x0800
+#define B10_MASK	0x0400
+#define B9_MASK		0x0200
+#define B8_MASK		0x0100
+#define B7_MASK		0x0080
+#define B6_MASK		0x0040
+#define B5_MASK		0x0020
+#define B4_MASK		0x0010
+#define B3_MASK		0x0008
+#define B2_MASK		0x0004
+#define B1_MASK		0x0002
+#define B0_MASK		0x0001
+
+/* PCI register offset */
+#define PCI_ID_REG		0x00
+#define PCI_COMMAND_REG		0x04
+/* #define MEMEN_BIT		B1_MASK */
+/* #define IOEN_BIT		B0_MASK */
+#define PCI_REV_ID_REG		0x08
+#define PCI_MEM_BASE_REG	0x10
+/* #define MEMBASE_MASK		0xFFFFF000 */
+/* #define MEMBASE_SIZE		4096 */
+#define PCI_INTR_REG		0x3C
+#define PCI_STATUS_REG		0x06
+#define PCI_CAP_ID_REG_OFFSET	0x34
+#define PCI_PMC_REG_OFFSET	0x36
+#define PCI_PMCSR_REG_OFFSET	0x38
+
+/* #define NEW_CAP		0x0010  */
+#define PME_EN			0x0100
+
+#define PARTID_MASK		0xFFFFF000
+#define PARTID_START_BIT	12
+
+/* #define LANCE_DWIO_RESET_PORT	0x18
+#define LANCE_WIO_RESET_PORT	0x14 */
+#define MIB_OFFSET		0x28
+
+/* Command style register access
+
+Registers CMD0, CMD2, CMD3,CMD7 and INTEN0 uses a write access technique called command style access. It allows the write to selected bits of this register without altering the bits that are not selected. Command style registers are divided into 4 bytes that can be written independently. Higher order bit of each byte is the  value bit that specifies the value that will be written into the selected bits of register. 
+
+eg., if the value 10011010b is written into the least significant byte of a command style register, bits 1,3 and 4 of the register will be set to 1, and the other bits will not be altered. If the value 00011010b is written into the same byte, bits 1,3 and 4 will be cleared to 0 and the other bits will not be altered.
+
+*/
+
+/*  Offset for Memory Mapped Registers. */
+/* 32 bit registers */
+
+#define  ASF_STAT		0x00	/* ASF status register */
+#define CHIPID			0x04	/* Chip ID regsiter */
+#define	MIB_DATA		0x10	/* MIB data register */
+#define MIB_ADDR		0x14	/* MIB address register */
+#define STAT0			0x30	/* Status0 register */
+#define INT0			0x38	/* Interrupt0 register */
+#define INTEN0			0x40	/* Interrupt0  enable register*/
+#define CMD0			0x48	/* Command0 register */
+#define CMD2			0x50	/* Command2 register */
+#define CMD3			0x54	/* Command3 resiter */
+#define CMD7			0x64	/* Command7 register */
+
+#define CTRL1 			0x6C	/* Control1 register */
+#define CTRL2 			0x70	/* Control2 register */
+
+#define XMT_RING_LIMIT		0x7C	/* Transmit ring limit register */
+
+#define AUTOPOLL0		0x88	/* Auto-poll0 register */
+#define AUTOPOLL1		0x8A	/* Auto-poll1 register */
+#define AUTOPOLL2		0x8C	/* Auto-poll2 register */
+#define AUTOPOLL3		0x8E	/* Auto-poll3 register */
+#define AUTOPOLL4		0x90	/* Auto-poll4 register */
+#define	AUTOPOLL5		0x92	/* Auto-poll5 register */
+
+#define AP_VALUE		0x98	/* Auto-poll value register */
+#define DLY_INT_A		0xA8	/* Group A delayed interrupt register */
+#define DLY_INT_B		0xAC	/* Group B delayed interrupt register */
+
+#define FLOW_CONTROL		0xC8	/* Flow control register */
+#define PHY_ACCESS		0xD0	/* PHY access register */
+
+#define STVAL			0xD8	/* Software timer value register */
+
+#define XMT_RING_BASE_ADDR0	0x100	/* Transmit ring0 base addr register */
+#define XMT_RING_BASE_ADDR1	0x108	/* Transmit ring1 base addr register */
+#define XMT_RING_BASE_ADDR2	0x110	/* Transmit ring2 base addr register */
+#define XMT_RING_BASE_ADDR3	0x118	/* Transmit ring2 base addr register */
+
+#define RCV_RING_BASE_ADDR0	0x120	/* Transmit ring0 base addr register */
+
+#define PMAT0			0x190	/* OnNow pattern register0 */
+#define PMAT1			0x194	/* OnNow pattern register1 */
+
+/* 16bit registers */
+
+#define XMT_RING_LEN0		0x140	/* Transmit Ring0 length register */
+#define XMT_RING_LEN1		0x144	/* Transmit Ring1 length register */
+#define XMT_RING_LEN2		0x148 	/* Transmit Ring2 length register */
+#define XMT_RING_LEN3		0x14C	/* Transmit Ring3 length register */
+
+#define RCV_RING_LEN0		0x150	/* Transmit Ring0 length register */
+
+#define SRAM_SIZE		0x178	/* SRAM size register */
+#define SRAM_BOUNDARY		0x17A	/* SRAM boundary register */
+
+/* 48bit register */
+
+#define PADR			0x160	/* Physical address register */
+
+/* 64bit register */
+
+#define LADRF			0x168	/* Logical address filter register */
+
+/* 8bit regsisters */
+
+#define IFS1			0x18C	/* Inter-frame spacing Part1 register */
+#define IFS			0x18D	/* Inter-frame spacing register */
+
+/* Register Bit Definitions */
+
+/* STAT_ASF			0x00, 32bit register */
+#define ASF_INIT_DONE		B1_MASK
+#define ASF_INIT_PRESENT	B0_MASK
+
+/* MIB_ADDR			0x14, 16bit register */
+#define	MIB_CMD_ACTIVE		B15_MASK
+#define	MIB_RD_CMD		B13_MASK
+#define	MIB_CLEAR		B12_MASK
+#define	MIB_ADDRESS		0x0000003F	/* 5:0 */
+
+/* QOS_ADDR			0x1C, 16bit register */
+#define QOS_CMD_ACTIVE		B15_MASK
+#define QOS_WR_CMD		B14_MASK
+#define QOS_RD_CMD		B13_MASK
+#define QOS_ADDRESS		0x0000001F	/* 4:0 */
+
+/* STAT0			0x30, 32bit register */
+#define PAUSE_PEND		B14_MASK
+#define PAUSING			B13_MASK
+#define PMAT_DET		B12_MASK
+#define MP_DET			B11_MASK
+#define LC_DET			B10_MASK
+#define SPEED_MASK		0x0380	/* 9:7 */
+#define FULL_DPLX		B6_MASK
+#define LINK_STATS		B5_MASK
+#define AUTONEG_COMPLETE	B4_MASK
+#define MIIPD			B3_MASK
+#define RX_SUSPENDED		B2_MASK
+#define TX_SUSPENDED		B1_MASK
+#define RUNNING			B0_MASK
+#define PHY_SPEED_10		0x2
+#define PHY_SPEED_100		0x3
+
+/* INT0				0x38, 32bit register */
+#define INTR			B31_MASK
+#define PCSINT			B28_MASK
+#define LCINT			B27_MASK
+#define APINT5			B26_MASK
+#define APINT4			B25_MASK
+#define APINT3			B24_MASK
+#define TINT_SUM		B23_MASK
+#define APINT2			B22_MASK
+#define APINT1			B21_MASK
+#define APINT0			B20_MASK
+#define MIIPDTINT		B19_MASK
+#define MCCIINT			B18_MASK
+#define MCCINT			B17_MASK
+#define MREINT			B16_MASK
+#define RINT_SUM		B15_MASK
+#define SPNDINT			B14_MASK
+#define MPINT			B13_MASK
+#define SINT			B12_MASK
+#define TINT3			B11_MASK
+#define TINT2			B10_MASK
+#define TINT1			B9_MASK
+#define TINT0			B8_MASK
+#define UINT			B7_MASK
+#define STINT			B4_MASK
+#define RINT3			B3_MASK
+#define RINT2			B2_MASK
+#define RINT1			B1_MASK
+#define RINT0			B0_MASK
+
+/* INTEN0			0x40, 32bit register */
+#define VAL3			B31_MASK   /* VAL bit for byte 3 */
+#define VAL2			B23_MASK   /* VAL bit for byte 2 */
+#define VAL1			B15_MASK   /* VAL bit for byte 1 */
+#define VAL0			B7_MASK    /* VAL bit for byte 0 */
+/* VAL3 */
+#define PSCINTEN		B28_MASK
+#define LCINTEN			B27_MASK
+#define APINT5EN		B26_MASK
+#define APINT4EN		B25_MASK
+#define APINT3EN		B24_MASK
+/* VAL2 */
+#define APINT2EN		B22_MASK
+#define APINT1EN		B21_MASK
+#define APINT0EN		B20_MASK
+#define MIIPDTINTEN		B19_MASK
+#define MCCIINTEN		B18_MASK
+#define MCCINTEN		B17_MASK
+#define MREINTEN		B16_MASK
+/* VAL1 */
+#define SPNDINTEN		B14_MASK
+#define MPINTEN			B13_MASK
+#define SINTEN			B12_MASK
+#define TINTEN3			B11_MASK
+#define TINTEN2			B10_MASK
+#define TINTEN1			B9_MASK
+#define TINTEN0			B8_MASK
+/* VAL0 */
+#define STINTEN			B4_MASK
+#define RINTEN3			B3_MASK
+#define RINTEN2			B2_MASK
+#define RINTEN1			B1_MASK
+#define RINTEN0			B0_MASK
+
+#define INTEN0_CLEAR 		0x1F7F7F1F /* Command style register */		
+
+/* CMD0				0x48, 32bit register */
+/* VAL2 */
+#define RDMD3			B19_MASK
+#define RDMD2			B18_MASK
+#define RDMD1			B17_MASK
+#define RDMD0			B16_MASK
+/* VAL1 */
+#define TDMD3			B11_MASK
+#define TDMD2			B10_MASK
+#define TDMD1			B9_MASK
+#define TDMD0			B8_MASK
+/* VAL0 */
+#define UINTCMD			B6_MASK
+#define RX_FAST_SPND		B5_MASK
+#define TX_FAST_SPND		B4_MASK
+#define RX_SPND			B3_MASK
+#define TX_SPND			B2_MASK
+#define INTREN			B1_MASK
+#define RUN			B0_MASK
+
+#define CMD0_CLEAR 		0x000F0F7F   /* Command style register */	
+
+/* CMD2 			0x50, 32bit register */
+/* VAL3 */
+#define CONDUIT_MODE		B29_MASK
+/* VAL2 */
+#define RPA			B19_MASK
+#define DRCVPA			B18_MASK
+#define DRCVBC			B17_MASK
+#define PROM			B16_MASK
+/* VAL1 */
+#define ASTRP_RCV		B13_MASK
+#define FCOLL			B12_MASK
+#define EMBA			B11_MASK
+#define DXMT2PD			B10_MASK
+#define LTINTEN			B9_MASK
+#define DXMTFCS			B8_MASK
+/* VAL0 */
+#define APAD_XMT		B6_MASK
+#define DRTY			B5_MASK
+#define INLOOP			B4_MASK
+#define EXLOOP			B3_MASK
+#define REX_RTRY		B2_MASK
+#define REX_UFLO		B1_MASK
+#define REX_LCOL		B0_MASK
+
+#define CMD2_CLEAR 		0x3F7F3F7F   /* Command style register */
+
+/* CMD3				0x54, 32bit register */
+/* VAL3 */
+#define ASF_INIT_DONE_ALIAS	B29_MASK
+/* VAL2 */
+#define JUMBO			B21_MASK
+#define VSIZE			B20_MASK
+#define VLONLY			B19_MASK
+#define VL_TAG_DEL		B18_MASK
+/* VAL1 */
+#define EN_PMGR			B14_MASK
+#define INTLEVEL		B13_MASK
+#define FORCE_FULL_DUPLEX	B12_MASK
+#define FORCE_LINK_STATUS	B11_MASK
+#define APEP			B10_MASK
+#define MPPLBA			B9_MASK
+/* VAL0 */
+#define RESET_PHY_PULSE		B2_MASK
+#define RESET_PHY		B1_MASK
+#define PHY_RST_POL		B0_MASK
+/* CMD7				0x64, 32bit register */
+/* VAL0 */
+#define PMAT_SAVE_MATCH		B4_MASK
+#define PMAT_MODE		B3_MASK
+#define MPEN_SW			B1_MASK
+#define LCMODE_SW		B0_MASK
+
+#define CMD7_CLEAR  		0x0000001B		/* Command style register */
+/* CTRL0			0x68, 32bit register */
+#define PHY_SEL			0x03000000	/* 25:24 */
+#define RESET_PHY_WIDTH		0x00FF0000	/* 23:16 */
+#define BSWP_REGS		B10_MASK
+#define BSWP_DESC		B9_MASK
+#define BSWP_DATA		B8_MASK
+#define CACHE_ALIGN		B4_MASK
+#define BURST_LIMIT		0x0000000F	/* 3:0 */
+
+/* CTRL1			0x6C, 32bit register */
+#define SLOTMOD_MASK		0x03000000	/* 25:24 */
+#define XMTSP_MASK		0x300	/* 17:16 */
+#define XMTSP_128		0x200
+#define XMTSP_64		0x100
+#define CRTL1_DEFAULT		0x00000017
+
+/* CTRL2			0x70, 32bit register */
+#define FS_MASK			0x00070000	/* 18:16 */
+#define FMDC_MASK		0x00000300	/* 9:8 */
+#define XPHYRST			B7_MASK
+#define XPHYANE			B6_MASK
+#define XPHYFD			B5_MASK
+#define XPHYSP			B3_MASK	/* 4:3 */
+#define APDW_MASK		0x00000007	/* 2:0 */
+
+/* RCV_RING_CFG			0x78, 16bit register */
+#define RCV_DROP3		B11_MASK
+#define RCV_DROP2		B10_MASK
+#define RCV_DROP1		B9_MASK
+#define RCV_DROP0		B8_MASK
+#define RCV_RING_DEFAULT	0x0030	/* 5:4 */
+#define RCV_RING3_EN		B3_MASK
+#define RCV_RING2_EN		B2_MASK
+#define RCV_RING1_EN		B1_MASK
+#define RCV_RING0_EN		B0_MASK
+
+/* XMT_RING_LIMIT		0x7C, 32bit register */
+#define XMT_RING2_LIMIT		0x00FF0000	/* 23:16 */
+#define XMT_RING1_LIMIT		0x0000FF00	/* 15:8 */
+#define XMT_RING0_LIMIT		0x000000FF	/* 7:0 */
+
+/* AUTOPOLL0			0x88, 16bit register */
+#define AP_REG0_EN		B15_MASK
+#define AP_REG0_ADDR_MASK	0x1F00	/* 12:8 */
+#define AP_PHY0_ADDR_MASK	0x001F	/* 4:0 */
+
+/* AUTOPOLL1			0x8A, 16bit register */
+#define AP_REG1_EN		B15_MASK
+#define AP_REG1_ADDR_MASK	0x1F00	/* 12:8 */
+#define AP_PRE_SUP1		B6_MASK
+#define AP_PHY1_DFLT		B5_MASK
+#define AP_PHY1_ADDR_MASK	0x001F	/* 4:0 */
+
+/* AUTOPOLL2			0x8C, 16bit register */
+#define AP_REG2_EN		B15_MASK
+#define AP_REG2_ADDR_MASK	0x1F00	/* 12:8 */
+#define AP_PRE_SUP2		B6_MASK
+#define AP_PHY2_DFLT		B5_MASK
+#define AP_PHY2_ADDR_MASK	0x001F	/* 4:0 */
+
+/* AUTOPOLL3			0x8E, 16bit register */
+#define AP_REG3_EN		B15_MASK
+#define AP_REG3_ADDR_MASK	0x1F00	/* 12:8 */
+#define AP_PRE_SUP3		B6_MASK
+#define AP_PHY3_DFLT		B5_MASK
+#define AP_PHY3_ADDR_MASK	0x001F	/* 4:0 */
+
+/* AUTOPOLL4			0x90, 16bit register */
+#define AP_REG4_EN		B15_MASK
+#define AP_REG4_ADDR_MASK	0x1F00	/* 12:8 */
+#define AP_PRE_SUP4		B6_MASK
+#define AP_PHY4_DFLT		B5_MASK
+#define AP_PHY4_ADDR_MASK	0x001F	/* 4:0 */
+
+/* AUTOPOLL5			0x92, 16bit register */
+#define AP_REG5_EN		B15_MASK
+#define AP_REG5_ADDR_MASK	0x1F00	/* 12:8 */
+#define AP_PRE_SUP5		B6_MASK
+#define AP_PHY5_DFLT		B5_MASK
+#define AP_PHY5_ADDR_MASK	0x001F	/* 4:0 */
+
+/* AP_VALUE 			0x98, 32bit ragister */
+#define AP_VAL_ACTIVE		B31_MASK
+#define AP_VAL_RD_CMD		B29_MASK
+#define AP_ADDR			0x00070000	/* 18:16 */
+#define AP_VAL			0x0000FFFF	/* 15:0 */
+
+/* PCS_ANEG			0x9C, 32bit register */
+#define SYNC_LOST		B10_MASK
+#define IMATCH			B9_MASK
+#define CMATCH			B8_MASK
+#define PCS_AN_IDLE		B1_MASK
+#define PCS_AN_CFG		B0_MASK
+
+/* DLY_INT_A			0xA8, 32bit register */
+#define DLY_INT_A_R3		B31_MASK
+#define DLY_INT_A_R2		B30_MASK
+#define DLY_INT_A_R1		B29_MASK
+#define DLY_INT_A_R0		B28_MASK
+#define DLY_INT_A_T3		B27_MASK
+#define DLY_INT_A_T2		B26_MASK
+#define DLY_INT_A_T1		B25_MASK
+#define DLY_INT_A_T0		B24_MASK
+#define EVENT_COUNT_A		0x00FF0000	/* 20:16 */
+#define MAX_DELAY_TIME_A	0x000007FF	/* 10:0 */
+
+/* DLY_INT_B			0xAC, 32bit register */
+#define DLY_INT_B_R3		B31_MASK
+#define DLY_INT_B_R2		B30_MASK
+#define DLY_INT_B_R1		B29_MASK
+#define DLY_INT_B_R0		B28_MASK
+#define DLY_INT_B_T3		B27_MASK
+#define DLY_INT_B_T2		B26_MASK
+#define DLY_INT_B_T1		B25_MASK
+#define DLY_INT_B_T0		B24_MASK
+#define EVENT_COUNT_B		0x00FF0000	/* 20:16 */
+#define MAX_DELAY_TIME_B	0x000007FF	/* 10:0 */
+
+/* DFC_THRESH2			0xC0, 16bit register */
+#define DFC_THRESH2_HIGH	0xFF00	/* 15:8 */
+#define DFC_THRESH2_LOW		0x00FF	/* 7:0 */
+
+/* DFC_THRESH3			0xC2, 16bit register */
+#define DFC_THRESH3_HIGH	0xFF00	/* 15:8 */
+#define DFC_THRESH3_LOW		0x00FF	/* 7:0 */
+
+/* DFC_THRESH0			0xC4, 16bit register */
+#define DFC_THRESH0_HIGH	0xFF00	/* 15:8 */
+#define DFC_THRESH0_LOW		0x00FF	/* 7:0 */
+
+/* DFC_THRESH1			0xC6, 16bit register */
+#define DFC_THRESH1_HIGH	0xFF00	/* 15:8 */
+#define DFC_THRESH1_LOW		0x00FF	/* 7:0 */
+
+/* FLOW_CONTROL 		0xC8, 32bit register */
+#define PAUSE_LEN_CHG		B30_MASK
+#define	FFC_EN			B28_MASK
+#define DFC_RING3_EN		B27_MASK
+#define DFC_RING2_EN		B26_MASK
+#define DFC_RING1_EN		B25_MASK
+#define DFC_RING0_EN		B24_MASK
+#define FIXP_CONGEST		B21_MASK
+#define FPA			B20_MASK
+#define NPA			B19_MASK
+#define FIXP			B18_MASK
+#define FCPEN			B17_MASK
+#define FCCMD			B16_MASK
+#define PAUSE_LEN		0x0000FFFF	/* 15:0 */
+
+/* FFC THRESH			0xCC, 32bit register */
+#define FFC_HIGH		0xFFFF0000	/* 31:16 */
+#define FFC_LOW			0x0000FFFF	/* 15:0 */
+
+/* PHY_ ACCESS			0xD0, 32bit register */
+#define	PHY_CMD_ACTIVE		B31_MASK
+#define PHY_WR_CMD		B30_MASK
+#define PHY_RD_CMD		B29_MASK
+#define PHY_RD_ERR		B28_MASK
+#define PHY_PRE_SUP		B27_MASK
+#define PHY_ADDR		0x03E00000	/* 25:21 */
+#define PHY_REG_ADDR		0x001F0000	/* 20:16 */
+#define PHY_DATA		0x0000FFFF	/* 15:0 */
+
+/* LED0..3			0xE0..0xE6, 16bit register */
+#define LEDOUT			B15_MASK
+#define LEDPOL			B14_MASK
+#define LEDDIS			B13_MASK
+#define LEDSTRETCH		B12_MASK
+#define LED1000			B8_MASK
+#define LED100			B7_MASK
+#define LEDMP			B6_MASK
+#define LEDFD			B5_MASK
+#define LEDLINK			B4_MASK
+#define LEDRCVMAT		B3_MASK
+#define LEDXMT			B2_MASK
+#define LEDRCV			B1_MASK
+#define LEDCOLOUT		B0_MASK
+
+/* EEPROM_ACC			0x17C, 16bit register */
+#define PVALID			B15_MASK
+#define PREAD			B14_MASK
+#define EEDET			B13_MASK
+#define	EEN			B4_MASK
+#define ECS			B2_MASK
+#define EESK			B1_MASK
+#define edi_edo			b0_MASK
+
+/* PMAT0			0x190,	 32bit register */
+#define PMR_ACTIVE		B31_MASK
+#define PMR_WR_CMD		B30_MASK
+#define PMR_RD_CMD		B29_MASK
+#define PMR_BANK		B28_MASK
+#define PMR_ADDR		0x007F0000	/* 22:16 */
+#define PMR_B4			0x000000FF	/* 15:0 */
+
+/* PMAT1			0x194,	 32bit register */
+#define PMR_B3			0xFF000000	/* 31:24 */
+#define PMR_B2			0x00FF0000	/* 23:16 */
+#define PMR_B1			0x0000FF00	/* 15:8 */
+#define PMR_B0			0x000000FF	/* 7:0 */
+
+/************************************************************************/
+/*                                                                      */
+/*                      MIB counter definitions                         */
+/*                                                                      */
+/************************************************************************/
+
+#define rcv_miss_pkts				0x00
+#define rcv_octets				0x01
+#define rcv_broadcast_pkts			0x02
+#define rcv_multicast_pkts			0x03
+#define rcv_undersize_pkts			0x04
+#define rcv_oversize_pkts			0x05
+#define rcv_fragments				0x06
+#define rcv_jabbers				0x07
+#define rcv_unicast_pkts			0x08
+#define rcv_alignment_errors			0x09
+#define rcv_fcs_errors				0x0A
+#define rcv_good_octets				0x0B
+#define rcv_mac_ctrl				0x0C
+#define rcv_flow_ctrl				0x0D
+#define rcv_pkts_64_octets			0x0E
+#define rcv_pkts_65to127_octets			0x0F
+#define rcv_pkts_128to255_octets		0x10
+#define rcv_pkts_256to511_octets		0x11
+#define rcv_pkts_512to1023_octets		0x12
+#define rcv_pkts_1024to1518_octets		0x13
+#define rcv_unsupported_opcode			0x14
+#define rcv_symbol_errors			0x15
+#define rcv_drop_pkts_ring1			0x16
+#define rcv_drop_pkts_ring2			0x17
+#define rcv_drop_pkts_ring3			0x18
+#define rcv_drop_pkts_ring4			0x19
+#define rcv_jumbo_pkts				0x1A
+
+#define xmt_underrun_pkts			0x20
+#define xmt_octets				0x21
+#define xmt_packets				0x22
+#define xmt_broadcast_pkts			0x23
+#define xmt_multicast_pkts			0x24
+#define xmt_collisions				0x25
+#define xmt_unicast_pkts			0x26
+#define xmt_one_collision			0x27
+#define xmt_multiple_collision			0x28
+#define xmt_deferred_transmit			0x29
+#define xmt_late_collision			0x2A
+#define xmt_excessive_defer			0x2B
+#define xmt_loss_carrier			0x2C
+#define xmt_excessive_collision			0x2D
+#define xmt_back_pressure			0x2E
+#define xmt_flow_ctrl				0x2F
+#define xmt_pkts_64_octets			0x30
+#define xmt_pkts_65to127_octets			0x31
+#define xmt_pkts_128to255_octets		0x32
+#define xmt_pkts_256to511_octets		0x33
+#define xmt_pkts_512to1023_octets		0x34
+#define xmt_pkts_1024to1518_octet		0x35
+#define xmt_oversize_pkts			0x36
+#define xmt_jumbo_pkts				0x37
+
+
+/* Driver definitions */
+
+#define	 PCI_VENDOR_ID_AMD		0x1022
+#define  PCI_DEVICE_ID_AMD8111E_7462	0x7462
+
+#define MAX_UNITS			16 /* Maximum number of devices possible */
+
+#define NUM_TX_BUFFERS			32 /* Number of transmit buffers */
+#define NUM_RX_BUFFERS			32 /* Number of receive buffers */	
+
+#define TX_BUFF_MOD_MASK         	31 /* (NUM_TX_BUFFERS -1) */
+#define RX_BUFF_MOD_MASK         	31 /* (NUM_RX_BUFFERS -1) */
+
+#define NUM_TX_RING_DR			32  
+#define NUM_RX_RING_DR			32 
+
+#define TX_RING_DR_MOD_MASK         	31 /* (NUM_TX_RING_DR -1) */
+#define RX_RING_DR_MOD_MASK         	31 /* (NUM_RX_RING_DR -1) */
+
+#define MAX_FILTER_SIZE			64 /* Maximum multicast address */ 
+#define AMD8111E_MIN_MTU	 	60 	
+#define AMD8111E_MAX_MTU		9000			
+
+#define PKT_BUFF_SZ			1536
+#define MIN_PKT_LEN			60
+#define ETH_ADDR_LEN			6
+
+#define OPTION_VLAN_ENABLE		0x0001
+#define OPTION_JUMBO_ENABLE		0x0002
+#define OPTION_MULTICAST_ENABLE		0x0004
+#define OPTION_WOL_ENABLE		0x0008
+#define OPTION_WAKE_MAGIC_ENABLE	0x0010
+#define OPTION_WAKE_PHY_ENABLE		0x0020
+
+#define PHY_REG_ADDR_MASK		0x1f
+
+/* Assume contoller gets data 10 times the maximum processing time */
+#define  REPEAT_CNT			10; 
+     
+/* amd8111e decriptor flag definitions */
+
+#define OWN_BIT			B15_MASK
+#define ADD_FCS_BIT		B13_MASK
+#define LTINT_BIT		B12_MASK
+#define STP_BIT			B9_MASK
+#define ENP_BIT			B8_MASK
+#define KILL_BIT		B6_MASK
+#define TCC_MASK		0x0003
+#define TCC_VLAN_INSERT		B1_MASK
+#define TCC_VLAN_REPLACE	0x0003
+#define RESET_RX_FLAGS		0x0000
+
+#define ERR_BIT 		B14_MASK
+#define FRAM_BIT		B13_MASK
+#define OFLO_BIT		B12_MASK
+#define CRC_BIT			B11_MASK
+#define PAM_BIT			B6_MASK
+#define LAFM_BIT		B5_MASK
+#define BAM_BIT			B4_MASK
+#define TT_MASK			0x000c
+#define TT_VLAN_TAGGED		0x000c
+#define TT_PRTY_TAGGED		0x0008
+
+/* driver ioctl parameters */
+#define PHY_ID 			0x01	/* currently it is fixed */
+#define AMD8111E_REG_DUMP_LEN	4096	/* Memory mapped register length */
+
+/* amd8111e desriptor format */
+
+struct amd8111e_tx_dr{
+
+	u16 buff_count; /* Size of the buffer pointed by this descriptor */
+
+	u16 tx_dr_offset2;
+
+	u16 tag_ctrl_info;
+
+	u16 tag_ctrl_cmd;
+
+	u32 buff_phy_addr;
+
+	u32 reserved;
+}; 
+
+struct amd8111e_rx_dr{
+	
+	u32 reserved;
+
+	u16 msg_count; /* Received message len */
+
+	u16 tag_ctrl_info; 
+
+	u16 buff_count;  /* Len of the buffer pointed by descriptor. */
+
+	u16 rx_dr_offset10;
+
+	u32 buff_phy_addr;
+
+};
+struct amd8111e_link_config{
+
+#define SPEED_INVALID		0xffff
+#define DUPLEX_INVALID		0xff
+#define AUTONEG_INVALID		0xff
+	
+	unsigned long			orig_phy_option;
+	u16				speed;
+	u8				duplex;
+	u8				autoneg;
+	u16 				orig_speed;
+	u8				orig_duplex;
+	u8				reserved;  /* 32bit alignment */
+};
+struct amd8111e_priv{
+	
+	struct amd8111e_tx_dr*  tx_ring;
+	struct amd8111e_rx_dr* rx_ring;
+	dma_addr_t tx_ring_dma_addr;	/* tx descriptor ring base address */
+	dma_addr_t rx_ring_dma_addr;	/* rx descriptor ring base address */
+	const char *name;
+	struct pci_dev *pci_dev;	/* Ptr to the associated pci_dev */
+	struct net_device* amd8111e_net_dev; 	/* ptr to associated net_device */
+	/* Transmit and recive skbs */
+	struct sk_buff *tx_skbuff[NUM_TX_BUFFERS];
+	struct sk_buff *rx_skbuff[NUM_RX_BUFFERS];
+	/* Transmit and receive dma mapped addr */
+	dma_addr_t tx_dma_addr[NUM_TX_BUFFERS];
+	dma_addr_t rx_dma_addr[NUM_RX_BUFFERS];
+	/* Reg memory mapped address */
+	void *  mmio;
+	
+	spinlock_t lock;	/* Guard lock */
+	unsigned long  rx_idx, tx_idx;	/* The next free ring entry */
+	unsigned long  tx_complete_idx;
+	unsigned long tx_ring_complete_idx;
+	unsigned long tx_ring_idx;
+	int rx_buff_len;	/* Buffer length of rx buffers */
+	int options;		/* Options enabled/disabled for the device */
+	unsigned long ext_phy_option;
+	struct amd8111e_link_config link_config;
+	int pm_cap;
+
+	struct net_device *next;
+#if AMD8111E_VLAN_TAG_USED
+	struct vlan_group		*vlgrp;
+#endif	
+	char opened;
+	struct net_device_stats stats;
+	struct net_device_stats prev_stats;
+	struct dev_mc_list* mc_list;
+	
+};
+#define AMD8111E_READ_REG64(_memMapBase, _offset, _pUlData)	\
+			*(u32*)(_pUlData) = readl(_memMapBase + (_offset));	\
+			*((u32*)(_pUlData))+1) = readl(_memMapBase + ((_offset)+4))
+
+#define AMD8111E_WRITE_REG64(_memMapBase, _offset, _pUlData)	\
+			writel(*(u32*)(_pUlData), _memMapBase + (_offset));	\
+			writel(*(u32*)((u8*)(_pUlData)+4), _memMapBase + ((_offset)+4))	\
+
+/* maps the external speed options to internal value */
+static unsigned char speed_duplex_mapping[] = {
+
+	XPHYANE,		/* Auto-negotiation, speed_duplex option 0 */
+	0,			/* 10M Half,  speed_duplex option 1 */
+	XPHYFD,			/* 10M Full,  speed_duplex option 2 */
+	XPHYSP,			/* 100M Half, speed_duplex option 3 */
+	XPHYFD | XPHYSP		/* 100M Full, speed_duplex option 4 */
+};
+static int card_idx;
+static int speed_duplex[MAX_UNITS] = { 0, };
+
+#endif /* _AMD8111E_H */
+
diff -urN linux-2.4.21-pre2/drivers/net/arcnet/com20020-pci.c linux-2.4.21-pre3/drivers/net/arcnet/com20020-pci.c
--- linux-2.4.21-pre2/drivers/net/arcnet/com20020-pci.c	Fri Aug  2 17:39:44 2002
+++ linux-2.4.21-pre3/drivers/net/arcnet/com20020-pci.c	Mon Jan  6 16:32:32 2003
@@ -1,5 +1,6 @@
 /*
- * Linux ARCnet driver - COM20020 PCI support (Contemporary Controls PCI20)
+ * Linux ARCnet driver - COM20020 PCI support
+ * for Contemporary Controls PCI20 and SOHARD SH-ARC PCI with com20020 chipset
  * 
  * Written 1994-1999 by Avery Pennarun,
  *    based on an ISA version by David Woodhouse.
@@ -86,7 +87,21 @@
 	memset(lp, 0, sizeof(struct arcnet_local));
 	pci_set_drvdata(pdev, dev);
 
-	ioaddr = pci_resource_start(pdev, 2);
+	// SOHARD needs PCI base addr 4
+	if (pdev->vendor==0x10B5) {
+		BUGMSG(D_NORMAL, "SOHARD\n");
+		ioaddr = pci_resource_start(pdev, 4);
+	}
+	else {
+		BUGMSG(D_NORMAL, "Contemporary Controls\n");
+		ioaddr = pci_resource_start(pdev, 2);
+	}
+
+	// Dummy access after Reset
+	// ARCNET controller needs this access to detect bustype
+	outb(0x00,ioaddr+1);
+	inb(ioaddr+1);
+
 	dev->base_addr = ioaddr;
 	dev->irq = pdev->irq;
 	dev->dev_addr[0] = node;
@@ -152,6 +167,7 @@
 	{ 0x1571, 0xa204, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_CAN_10MBIT },
 	{ 0x1571, 0xa205, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_CAN_10MBIT },
 	{ 0x1571, 0xa206, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_CAN_10MBIT },
+        { 0x10B5, 0x9050, PCI_ANY_ID, PCI_ANY_ID, 0, 0, ARC_CAN_10MBIT },
 	{0,}
 };
 
diff -urN linux-2.4.21-pre2/drivers/net/arcnet/com20020.c linux-2.4.21-pre3/drivers/net/arcnet/com20020.c
--- linux-2.4.21-pre2/drivers/net/arcnet/com20020.c	Fri Aug  2 17:39:44 2002
+++ linux-2.4.21-pre3/drivers/net/arcnet/com20020.c	Mon Jan  6 16:32:32 2003
@@ -1,6 +1,6 @@
 /*
  * Linux ARCnet driver - COM20020 chipset support
- * 
+ *
  * Written 1997 by David Woodhouse.
  * Written 1994-1999 by Avery Pennarun.
  * Written 1999 by Martin Mares <mj@ucw.cz>.
@@ -98,6 +98,9 @@
 	lp->setup = lp->clockm ? 0 : (lp->clockp << 1);
 	lp->setup2 = (lp->clockm << 4) | 8;
 
+	/* Enable P1Mode for backplane mode */
+	lp->setup = lp->setup | P1MODE;
+
 	SET_SUBADR(SUB_SETUP1);
 	outb(lp->setup, _XREG);
 
@@ -105,7 +108,7 @@
 	{
 		SET_SUBADR(SUB_SETUP2);
 		outb(lp->setup2, _XREG);
-	
+
 		/* must now write the magic "restart operation" command */
 		mdelay(1);
 		outb(0x18, _COMMAND);
@@ -183,7 +186,7 @@
 	{
 		SET_SUBADR(SUB_SETUP2);
 		outb(lp->setup2, _XREG);
-	
+
 		/* must now write the magic "restart operation" command */
 		mdelay(1);
 		outb(0x18, _COMMAND);
@@ -202,7 +205,7 @@
 		return -ENODEV;
 	}
 	/* reserve the I/O region */
-	if (request_region(ioaddr, ARCNET_TOTAL_SIZE, "arcnet (COM20020)")) {
+	if (!request_region(ioaddr, ARCNET_TOTAL_SIZE, "arcnet (COM20020)")) {
 		free_irq(dev->irq, dev);
 		return -EBUSY;
 	}
@@ -218,7 +221,7 @@
 		BUGMSG(D_NORMAL, "Using extended timeout value of %d.\n", lp->timeout);
 
 	BUGMSG(D_NORMAL, "Using CKP %d - data rate %s.\n",
-	       lp->setup >> 1, 
+	       lp->setup >> 1,
 	       clockrates[3 - ((lp->setup2 & 0xF0) >> 4) + ((lp->setup & 0x0F) >> 1)]);
 
 	if (!dev->init && register_netdev(dev)) {
@@ -230,9 +233,9 @@
 }
 
 
-/* 
+/*
  * Do a hardware reset on the card, and set up necessary registers.
- * 
+ *
  * This should be called as little as possible, because it disrupts the
  * token on the network (causes a RECON) and requires a significant delay.
  *
diff -urN linux-2.4.21-pre2/drivers/net/bonding.c linux-2.4.21-pre3/drivers/net/bonding.c
--- linux-2.4.21-pre2/drivers/net/bonding.c	Mon Jan  6 16:32:28 2003
+++ linux-2.4.21-pre3/drivers/net/bonding.c	Mon Jan  6 16:32:32 2003
@@ -177,20 +177,91 @@
  *     - Port Gleb Natapov's multicast support patchs from 2.4.12
  *       to 2.4.18 adding support for multicast.
  *
- * 2002/06/17 - Tony Cureington <tony.cureington * hp_com>
+ * 2002/06/10 - Tony Cureington <tony.cureington * hp_com>
  *     - corrected uninitialized pointer (ifr.ifr_data) in bond_check_dev_link;
- *       actually changed function to use ETHTOOL, then MIIPHY, and finally
- *       MIIREG to determine the link status
+ *       actually changed function to use MIIPHY, then MIIREG, and finally
+ *       ETHTOOL to determine the link status
  *     - fixed bad ifr_data pointer assignments in bond_ioctl
  *     - corrected mode 1 being reported as active-backup in bond_get_info;
  *       also added text to distinguish type of load balancing (rr or xor)
  *     - change arp_ip_target module param from "1-12s" (array of 12 ptrs)
  *       to "s" (a single ptr)
  *
+ * 2002/08/30 - Jay Vosburgh <fubar at us dot ibm dot com>
+ *     - Removed acquisition of xmit_lock in set_multicast_list; caused
+ *       deadlock on SMP (lock is held by caller).
+ *     - Revamped SIOCGMIIPHY, SIOCGMIIREG portion of bond_check_dev_link().
+ *
  * 2002/09/18 - Jay Vosburgh <fubar at us dot ibm dot com>
  *     - Fixed up bond_check_dev_link() (and callers): removed some magic
  *	 numbers, banished local MII_ defines, wrapped ioctl calls to
  *	 prevent EFAULT errors
+ *
+ * 2002/9/30 - Jay Vosburgh <fubar at us dot ibm dot com>
+ *     - make sure the ip target matches the arp_target before saving the
+ *	 hw address.
+ *
+ * 2002/9/30 - Dan Eisner <eisner at 2robots dot com>
+ *     - make sure my_ip is set before taking down the link, since
+ *	 not all switches respond if the source ip is not set.
+ *
+ * 2002/10/8 - Janice Girouard <girouard at us dot ibm dot com>
+ *     - read in the local ip address when enslaving a device
+ *     - add primary support
+ *     - make sure 2*arp_interval has passed when a new device
+ *       is brought on-line before taking it down.
+ *
+ * 2002/09/11 - Philippe De Muyter <phdm at macqel dot be>
+ *     - Added bond_xmit_broadcast logic.
+ *     - Added bond_mode() support function.
+ *
+ * 2002/10/26 - Laurent Deniel <laurent.deniel at free.fr>
+ *     - allow to register multicast addresses only on active slave
+ *       (useful in active-backup mode)
+ *     - add multicast module parameter
+ *     - fix deletion of multicast groups after unloading module
+ *
+ * 2002/11/06 - Kameshwara Rayaprolu <kameshwara.rao * wipro_com>
+ *     - Changes to prevent panic from closing the device twice; if we close 
+ *       the device in bond_release, we must set the original_flags to down 
+ *       so it won't be closed again by the network layer.
+ *
+ * 2002/11/07 - Tony Cureington <tony.cureington * hp_com>
+ *     - Fix arp_target_hw_addr memory leak
+ *     - Created activebackup_arp_monitor function to handle arp monitoring 
+ *       in active backup mode - the bond_arp_monitor had several problems... 
+ *       such as allowing slaves to tx arps sequentially without any delay 
+ *       for a response
+ *     - Renamed bond_arp_monitor to loadbalance_arp_monitor and re-wrote
+ *       this function to just handle arp monitoring in load-balancing mode;
+ *       it is a lot more compact now
+ *     - Changes to ensure one and only one slave transmits in active-backup 
+ *       mode
+ *     - Robustesize parameters; warn users about bad combinations of 
+ *       parameters; also if miimon is specified and a network driver does 
+ *       not support MII or ETHTOOL, inform the user of this
+ *     - Changes to support link_failure_count when in arp monitoring mode
+ *     - Fix up/down delay reported in /proc
+ *     - Added version; log version; make version available from "modinfo -d"
+ *     - Fixed problem in bond_check_dev_link - if the first IOCTL (SIOCGMIIPH)
+ *	 failed, the ETHTOOL ioctl never got a chance
+ *
+ * 2002/11/16 - Laurent Deniel <laurent.deniel at free.fr>
+ *     - fix multicast handling in activebackup_arp_monitor
+ *     - remove one unnecessary and confusing current_slave == slave test 
+ *	 in activebackup_arp_monitor
+ *
+ *  2002/11/17 - Laurent Deniel <laurent.deniel at free.fr>
+ *     - fix bond_slave_info_query when slave_id = num_slaves
+ *
+ *  2002/11/19 - Janice Girouard <girouard at us dot ibm dot com>
+ *     - correct ifr_data reference.  Update ifr_data reference
+ *       to mii_ioctl_data struct values to avoid confusion.
+ *
+ *
+ *  2002/11/22 - Bert Barbe <bert.barbe at oracle dot com>
+ *      - Add support for multiple arp_ip_target
+ *
  */
 
 #include <linux/config.h>
@@ -203,6 +274,7 @@
 #include <linux/ptrace.h>
 #include <linux/ioport.h>
 #include <linux/in.h>
+#include <linux/ip.h>
 #include <linux/slab.h>
 #include <linux/string.h>
 #include <linux/init.h>
@@ -216,6 +288,7 @@
 #include <linux/errno.h>
 
 #include <linux/netdevice.h>
+#include <linux/inetdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 #include <net/sock.h>
@@ -228,6 +301,13 @@
 #include <linux/mii.h>
 #include <linux/ethtool.h>
 
+#define DRV_VERSION		"2.4.20-20021210"
+#define DRV_RELDATE		"December 10, 2002"
+#define DRV_NAME		"bonding"
+#define DRV_DESCRIPTION		"Ethernet Channel Bonding Driver"
+
+static const char *version =
+DRV_NAME ".c:v" DRV_VERSION " (" DRV_RELDATE ")\n";
 
 /* monitor all links that often (in milliseconds). <=0 disables monitoring */
 #ifndef BOND_LINK_MON_INTERV
@@ -238,20 +318,31 @@
 #define BOND_LINK_ARP_INTERV	0
 #endif
 
+#ifndef MAX_ARP_IP_TARGETS
+#define MAX_ARP_IP_TARGETS 16
+#endif
+
 static int arp_interval = BOND_LINK_ARP_INTERV;
-static char *arp_ip_target = NULL;
-static unsigned long arp_target = 0;
+static char *arp_ip_target[MAX_ARP_IP_TARGETS] = { NULL, };
+static unsigned long arp_target[MAX_ARP_IP_TARGETS] = { 0, } ;
+static int arp_ip_count = 0;
 static u32 my_ip = 0;
 char *arp_target_hw_addr = NULL;
 
+static char *primary= NULL;
+
 static int max_bonds	= BOND_DEFAULT_MAX_BONDS;
 static int miimon	= BOND_LINK_MON_INTERV;
 static int mode		= BOND_MODE_ROUNDROBIN;
 static int updelay	= 0;
 static int downdelay	= 0;
 
+#define BOND_MULTICAST_DISABLED 0
+#define BOND_MULTICAST_ACTIVE   1
+#define BOND_MULTICAST_ALL      2
+static int multicast    = BOND_MULTICAST_ALL;
+
 static int first_pass	= 1;
-int bond_cnt;
 static struct bonding *these_bonds =  NULL;
 static struct net_device *dev_bonds = NULL;
 
@@ -262,13 +353,17 @@
 MODULE_PARM(mode, "i");
 MODULE_PARM(arp_interval, "i");
 MODULE_PARM_DESC(arp_interval, "arp interval in milliseconds");
-MODULE_PARM(arp_ip_target, "s");
-MODULE_PARM_DESC(arp_ip_target, "arp target in n.n.n.n form");
+MODULE_PARM(arp_ip_target, "1-" __MODULE_STRING(MAX_ARP_IP_TARGETS) "s");
+MODULE_PARM_DESC(arp_ip_target, "arp targets in n.n.n.n form");
 MODULE_PARM_DESC(mode, "Mode of operation : 0 for round robin, 1 for active-backup, 2 for xor");
 MODULE_PARM(updelay, "i");
 MODULE_PARM_DESC(updelay, "Delay before considering link up, in milliseconds");
 MODULE_PARM(downdelay, "i");
 MODULE_PARM_DESC(downdelay, "Delay before considering link down, in milliseconds");
+MODULE_PARM(primary, "s");
+MODULE_PARM_DESC(primary, "Primary network device to use");
+MODULE_PARM(multicast, "i");
+MODULE_PARM_DESC(multicast, "Mode for multicast support : 0 for none, 1 for active slave, 2 for all slaves (default)");
 
 extern void arp_send( int type, int ptype, u32 dest_ip, struct net_device *dev,
 	u32 src_ip, unsigned char *dest_hw, unsigned char *src_hw, 
@@ -279,7 +374,8 @@
 static int bond_xmit_activebackup(struct sk_buff *skb, struct net_device *dev);
 static struct net_device_stats *bond_get_stats(struct net_device *dev);
 static void bond_mii_monitor(struct net_device *dev);
-static void bond_arp_monitor(struct net_device *dev);
+static void loadbalance_arp_monitor(struct net_device *dev);
+static void activebackup_arp_monitor(struct net_device *dev);
 static int bond_event(struct notifier_block *this, unsigned long event, void *ptr);
 static void bond_restore_slave_flags(slave_t *slave);
 static void bond_mc_list_destroy(struct bonding *bond);
@@ -290,6 +386,7 @@
 static void bond_set_promiscuity(bonding_t *bond, int inc);
 static void bond_set_allmulti(bonding_t *bond, int inc);
 static struct dev_mc_list* bond_mc_list_find_dmi(struct dev_mc_list *dmi, struct dev_mc_list *mc_list);
+static void bond_mc_update(bonding_t *bond, slave_t *new, slave_t *old);
 static void bond_set_slave_inactive_flags(slave_t *slave);
 static void bond_set_slave_active_flags(slave_t *slave);
 static int bond_enslave(struct net_device *master, struct net_device *slave);
@@ -311,6 +408,47 @@
 
 #define IS_UP(dev)	((((dev)->flags & (IFF_UP)) == (IFF_UP)) && \
 			(netif_running(dev) && netif_carrier_ok(dev)))
+  
+static void arp_send_all(slave_t *slave)
+{	
+  int i; 
+  for ( i=0; (i<MAX_ARP_IP_TARGETS) && arp_target[i]; i++) { 
+    arp_send(ARPOP_REQUEST, ETH_P_ARP, arp_target[i], slave->dev, 
+	     my_ip, arp_target_hw_addr, slave->dev->dev_addr,
+	     arp_target_hw_addr); 
+  } 
+}
+ 
+
+static const char *bond_mode(void)
+{
+	switch (mode) {
+	case BOND_MODE_ROUNDROBIN :
+		return "load balancing (round-robin)";
+	case BOND_MODE_ACTIVEBACKUP :
+		return "fault-tolerance (active-backup)";
+	case BOND_MODE_XOR :
+		return "load balancing (xor)";
+	case BOND_MODE_BROADCAST :
+		return "fault-tolerance (broadcast)";
+	default :
+		return "unknown";
+	}
+}
+
+static const char *multicast_mode(void)
+{
+	switch(multicast) {
+	case BOND_MULTICAST_DISABLED :
+		return "disabled";
+	case BOND_MULTICAST_ACTIVE :
+		return "active slave only";
+	case BOND_MULTICAST_ALL :
+		return "all slaves";
+	default :
+		return "unknown";
+	}
+}
 
 static void bond_restore_slave_flags(slave_t *slave)
 {
@@ -418,11 +556,24 @@
 		/*       call it and not the others for that team          */
 		/*       member.                                           */
 
-		/* try SOICETHTOOL ioctl, some drivers cache ETHTOOL_GLINK */
-		/* for a period of time; we need to encourage link status  */
-		/* be reported by network drivers in real time; if the     */
-		/* value is cached, the mmimon module parm may have no     */
-		/* effect...                                               */
+		/*
+		 * We cannot assume that SIOCGMIIPHY will also read a
+		 * register; not all network drivers (e.g., e100)
+		 * support that.
+		 */
+
+		/* Yes, the mii is overlaid on the ifreq.ifr_ifru */
+		mii = (struct mii_ioctl_data *)&ifr.ifr_data;
+		if (IOCTL(dev, &ifr, SIOCGMIIPHY) == 0) {
+			mii->reg_num = MII_BMSR;
+			if (IOCTL(dev, &ifr, SIOCGMIIREG) == 0) {
+				return mii->val_out & BMSR_LSTATUS;
+			}
+		}
+
+		/* try SIOCETHTOOL ioctl, some drivers cache ETHTOOL_GLINK */
+		/* for a period of time so we attempt to get link status   */
+		/* from it last if the above MII ioctls fail...            */
 	        etool.cmd = ETHTOOL_GLINK;
 	        ifr.ifr_data = (char*)&etool;
 		if (IOCTL(dev, &ifr, SIOCETHTOOL) == 0) {
@@ -430,26 +581,14 @@
 				return BMSR_LSTATUS;
 			} 
 			else { 
+#ifdef BONDING_DEBUG
+				printk(KERN_INFO 
+					":: SIOCETHTOOL shows failure \n");
+#endif
 				return(0);
 			} 
 		}
 
-		/*
-		 * We cannot assume that SIOCGMIIPHY will also read a
-		 * register; not all network drivers support that.
-		 */
-
-		/* Yes, the mii is overlaid on the ifreq.ifr_ifru */
-		mii = (struct mii_ioctl_data *)&ifr.ifr_data;
-		if (IOCTL(dev, &ifr, SIOCGMIIPHY) != 0) {
-			return BMSR_LSTATUS;	 /* can't tell */
-		}
-
-		mii->reg_num = MII_BMSR;
-		if (IOCTL(dev, &ifr, SIOCGMIIREG) == 0) {
-			return mii->val_out & BMSR_LSTATUS;
-		}
-
 	}
 	return BMSR_LSTATUS;  /* spoof link up ( we can't check it) */
 }
@@ -486,7 +625,11 @@
 		init_timer(arp_timer);
 		arp_timer->expires  = jiffies + (arp_interval * HZ / 1000);
 		arp_timer->data     = (unsigned long)dev;
-		arp_timer->function = (void *)&bond_arp_monitor;
+		if (mode == BOND_MODE_ACTIVEBACKUP) {
+			arp_timer->function = (void *)&activebackup_arp_monitor;
+		} else {
+			arp_timer->function = (void *)&loadbalance_arp_monitor;
+		}
 		add_timer(arp_timer);
 	}
 	return 0;
@@ -504,6 +647,10 @@
 	}
 	if (arp_interval> 0) {  /* arp interval, in milliseconds. */
 		del_timer(&bond->arp_timer);
+                if (arp_target_hw_addr != NULL) {
+			kfree(arp_target_hw_addr); 
+			arp_target_hw_addr = NULL;
+		}
 	}
 
 	/* Release the bonded slaves */
@@ -548,9 +695,18 @@
 static void bond_mc_add(bonding_t *bond, void *addr, int alen)
 { 
 	slave_t *slave;
-
-	for (slave = bond->prev; slave != (slave_t*)bond; slave = slave->prev) {
-		dev_mc_add(slave->dev, addr, alen, 0);
+	switch (multicast) {
+	case BOND_MULTICAST_ACTIVE :
+		/* write lock already acquired */
+		if (bond->current_slave != NULL)
+			dev_mc_add(bond->current_slave->dev, addr, alen, 0);
+		break;
+	case BOND_MULTICAST_ALL :
+		for (slave = bond->prev; slave != (slave_t*)bond; slave = slave->prev)
+			dev_mc_add(slave->dev, addr, alen, 0);
+		break;
+	case BOND_MULTICAST_DISABLED :
+		break;
 	}
 } 
 
@@ -560,9 +716,19 @@
 static void bond_mc_delete(bonding_t *bond, void *addr, int alen)
 { 
 	slave_t *slave; 
-
-	for (slave = bond->prev; slave != (slave_t*)bond; slave = slave->prev)
-		dev_mc_delete(slave->dev, addr, alen, 0);
+	switch (multicast) {
+	case BOND_MULTICAST_ACTIVE :
+		/* write lock already acquired */
+		if (bond->current_slave != NULL)
+			dev_mc_delete(bond->current_slave->dev, addr, alen, 0);
+		break;
+	case BOND_MULTICAST_ALL :
+		for (slave = bond->prev; slave != (slave_t*)bond; slave = slave->prev)
+			dev_mc_delete(slave->dev, addr, alen, 0);
+		break;
+	case BOND_MULTICAST_DISABLED :
+		break;
+	}
 } 
 
 /*
@@ -606,9 +772,19 @@
 static void bond_set_promiscuity(bonding_t *bond, int inc)
 { 
 	slave_t *slave; 
- 
-	for (slave = bond->prev; slave != (slave_t*)bond; slave = slave->prev)
-		dev_set_promiscuity(slave->dev, inc);
+	switch (multicast) {
+	case BOND_MULTICAST_ACTIVE :
+		/* write lock already acquired */
+		if (bond->current_slave != NULL)
+			dev_set_promiscuity(bond->current_slave->dev, inc);
+		break;
+	case BOND_MULTICAST_ALL :
+		for (slave = bond->prev; slave != (slave_t*)bond; slave = slave->prev)
+			dev_set_promiscuity(slave->dev, inc);
+		break;
+	case BOND_MULTICAST_DISABLED :
+		break;
+	}
 } 
 
 /*
@@ -617,9 +793,19 @@
 static void bond_set_allmulti(bonding_t *bond, int inc)
 { 
 	slave_t *slave; 
- 
-	for (slave = bond->prev; slave != (slave_t*)bond; slave = slave->prev)
-		dev_set_allmulti(slave->dev, inc);
+	switch (multicast) {
+	case BOND_MULTICAST_ACTIVE : 
+		/* write lock already acquired */
+		if (bond->current_slave != NULL)
+			dev_set_allmulti(bond->current_slave->dev, inc);
+		break;
+	case BOND_MULTICAST_ALL :
+		for (slave = bond->prev; slave != (slave_t*)bond; slave = slave->prev)
+			dev_set_allmulti(slave->dev, inc);
+		break;
+	case BOND_MULTICAST_DISABLED :
+		break;
+	}
 } 
 
 /* 
@@ -644,6 +830,8 @@
 	struct dev_mc_list *dmi;
 	unsigned long flags = 0;
 
+	if (multicast == BOND_MULTICAST_DISABLED)
+		return;
 	/*
 	 * Lock the private data for the master
 	 */
@@ -686,6 +874,43 @@
 }
 
 /*
+ * Update the mc list and multicast-related flags for the new and 
+ * old active slaves (if any) according to the multicast mode
+ */
+static void bond_mc_update(bonding_t *bond, slave_t *new, slave_t *old)
+{
+	struct dev_mc_list *dmi;
+
+	switch(multicast) {
+	case BOND_MULTICAST_ACTIVE :		
+		if (bond->device->flags & IFF_PROMISC) {
+			if (old != NULL && new != old)
+				dev_set_promiscuity(old->dev, -1);
+			dev_set_promiscuity(new->dev, 1);
+		}
+		if (bond->device->flags & IFF_ALLMULTI) {
+			if (old != NULL && new != old)
+				dev_set_allmulti(old->dev, -1);
+			dev_set_allmulti(new->dev, 1);
+		}
+		/* first remove all mc addresses from old slave if any,
+		   and _then_ add them to new active slave */
+		if (old != NULL && new != old) {
+			for (dmi = bond->device->mc_list; dmi != NULL; dmi = dmi->next)
+				dev_mc_delete(old->dev, dmi->dmi_addr, dmi->dmi_addrlen, 0);
+		}
+		for (dmi = bond->device->mc_list; dmi != NULL; dmi = dmi->next)
+			dev_mc_add(new->dev, dmi->dmi_addr, dmi->dmi_addrlen, 0);
+		break;
+	case BOND_MULTICAST_ALL :
+		/* nothing to do: mc list is already up-to-date on all slaves */
+		break;
+	case BOND_MULTICAST_DISABLED :
+		break;
+	}
+}
+
+/*
  * This function counts the number of attached 
  * slaves for use by bond_xmit_xor.
  */
@@ -706,9 +931,16 @@
 	bonding_t *bond = NULL;
 	slave_t *new_slave = NULL;
 	unsigned long flags = 0;
+	unsigned long rflags = 0;
 	int ndx = 0;
 	int err = 0;
 	struct dev_mc_list *dmi;
+	struct in_ifaddr **ifap;
+	struct in_ifaddr *ifa;
+	static int (* ioctl)(struct net_device *, struct ifreq *, int);
+	struct ifreq ifr;
+	struct ethtool_value etool; 
+	int link_reporting = 0;
 
 	if (master_dev == NULL || slave_dev == NULL) {
 		return -ENODEV;
@@ -761,17 +993,19 @@
 
 	new_slave->dev = slave_dev;
 
-	/* set promiscuity level to new slave */ 
-	if (master_dev->flags & IFF_PROMISC)
-		dev_set_promiscuity(slave_dev, 1); 
+	if (multicast == BOND_MULTICAST_ALL) {
+		/* set promiscuity level to new slave */ 
+		if (master_dev->flags & IFF_PROMISC)
+			dev_set_promiscuity(slave_dev, 1); 
  
-	/* set allmulti level to new slave */
-	if (master_dev->flags & IFF_ALLMULTI) 
-		dev_set_allmulti(slave_dev, 1); 
- 
-	/* upload master's mc_list to new slave */ 
-	for (dmi = master_dev->mc_list; dmi != NULL; dmi = dmi->next) 
-		dev_mc_add (slave_dev, dmi->dmi_addr, dmi->dmi_addrlen, 0);
+		/* set allmulti level to new slave */
+		if (master_dev->flags & IFF_ALLMULTI) 
+			dev_set_allmulti(slave_dev, 1); 
+		
+		/* upload master's mc_list to new slave */ 
+		for (dmi = master_dev->mc_list; dmi != NULL; dmi = dmi->next) 
+			dev_mc_add (slave_dev, dmi->dmi_addr, dmi->dmi_addrlen, 0);
+	}
 
 	/* 
 	 * queue to the end of the slaves list, make the first element its
@@ -802,6 +1036,56 @@
 	new_slave->delay = 0;
 	new_slave->link_failure_count = 0;
 
+	if (miimon > 0) {
+		/* if the network driver for the slave does not support
+		 * ETHTOOL/MII link status reporting, warn the user of this
+		 */
+		if ((ioctl = slave_dev->do_ioctl) != NULL) {
+			etool.cmd = ETHTOOL_GLINK;
+	        	ifr.ifr_data = (char*)&etool;
+			if (IOCTL(slave_dev, &ifr, SIOCETHTOOL) == 0) {
+				link_reporting = 1;
+			}
+			else {
+				if (IOCTL(slave_dev, &ifr, SIOCGMIIPHY) == 0) {
+		                        /* Yes, the mii is overlaid on the
+					 * ifreq.ifr_ifru
+					 */
+					((struct mii_ioctl_data*)
+					 (&ifr.ifr_data))->reg_num = 1;
+					if (IOCTL(slave_dev, &ifr, SIOCGMIIREG)
+						       			== 0) {
+	                                	link_reporting = 1;
+					}
+				}
+			}
+		}
+
+		if ((link_reporting == 0) && (arp_interval == 0)) {
+			/* miimon is set but a bonded network driver does
+			 * not support ETHTOOL/MII and arp_interval is
+			 * not set
+			 */ 
+			printk(KERN_ERR
+				"bond_enslave(): MII and ETHTOOL support not "
+				"available for interface %s, and "
+				"arp_interval/arp_ip_target module parameters "
+		       		"not specified, thus bonding will not detect "
+				"link failures! see bonding.txt for details.\n",
+		       		slave_dev->name);
+		}
+		else if (link_reporting == 0) {
+			/* unable  get link status using mii/ethtool */
+			printk(KERN_WARNING 
+			       "bond_enslave: can't get link status from "
+			       "interface %s; the network driver associated "
+			       "with this interface does not support "
+			       "MII or ETHTOOL link status reporting, thus "
+			       "miimon has no effect on this interface.\n", 
+			       slave_dev->name);
+		}
+	}
+
 	/* check for initial state */
 	if ((miimon <= 0) ||
 	    (bond_check_dev_link(slave_dev) == BMSR_LSTATUS)) {
@@ -809,6 +1093,7 @@
 		printk(KERN_CRIT "Initial state of slave_dev is BOND_LINK_UP\n");
 #endif
 		new_slave->link  = BOND_LINK_UP;
+		new_slave->jiffies = jiffies;
 	}
 	else {
 #ifdef BONDING_DEBUG
@@ -835,6 +1120,7 @@
 			   is OK, so make this interface the active one */
 			bond->current_slave = new_slave;
 			bond_set_slave_active_flags(new_slave);
+			bond_mc_update(bond, new_slave, NULL);
 		}
 		else {
 #ifdef BONDING_DEBUG
@@ -842,15 +1128,24 @@
 #endif
 			bond_set_slave_inactive_flags(new_slave);
 		}
+		read_lock_irqsave(&(((struct in_device *)slave_dev->ip_ptr)->lock), rflags);
+		ifap= &(((struct in_device *)slave_dev->ip_ptr)->ifa_list);
+		ifa = *ifap;
+		my_ip = ifa->ifa_address;
+		read_unlock_irqrestore(&(((struct in_device *)slave_dev->ip_ptr)->lock), rflags);
+
+		/* if there is a primary slave, remember it */
+		if (primary != NULL)
+		  if( strcmp(primary, new_slave->dev->name) == 0) 
+			bond->primary_slave = new_slave;
 	} else {
 #ifdef BONDING_DEBUG
 		printk(KERN_CRIT "This slave is always active in trunk mode\n");
 #endif
 		/* always active in trunk mode */
 		new_slave->state = BOND_STATE_ACTIVE;
-		if (bond->current_slave == NULL) {
+		if (bond->current_slave == NULL) 
 			bond->current_slave = new_slave;
-		}
 	}
 
 	update_slave_cnt(bond);
@@ -941,6 +1236,7 @@
 	    IS_UP(newactive->dev)) {
 		bond_set_slave_inactive_flags(oldactive);
 		bond_set_slave_active_flags(newactive);
+		bond_mc_update(bond, newactive, oldactive);
 		bond->current_slave = newactive;
 		printk("%s : activate %s(old : %s)\n",
 			master_dev->name, newactive->dev->name, 
@@ -981,6 +1277,7 @@
 			newslave = bond->current_slave = bond->next;
 			write_unlock(&bond->ptrlock);
 		} else {
+
 			printk (" but could not find any %s interface.\n",
 				(mode == BOND_MODE_ACTIVEBACKUP) ? "backup":"other");
 			write_lock(&bond->ptrlock);
@@ -988,16 +1285,38 @@
 			write_unlock(&bond->ptrlock);
 			return NULL; /* still no slave, return NULL */
 		}
+	} else if (mode == BOND_MODE_ACTIVEBACKUP) {
+		/* make sure oldslave doesn't send arps - this could
+		 * cause a ping-pong effect between interfaces since they
+		 * would be able to tx arps - in active backup only one
+		 * slave should be able to tx arps, and that should be
+		 * the current_slave; the only exception is when all
+		 * slaves have gone down, then only one non-current slave can
+		 * send arps at a time; clearing oldslaves' mc list is handled
+		 * later in this function.
+		 */
+		bond_set_slave_inactive_flags(oldslave);
 	}
 
 	mintime = updelay;
 
+	/* first try the primary link; if arping, a link must tx/rx traffic 
+	 * before it can be considered the current_slave - also, we would skip 
+	 * slaves between the current_slave and primary_slave that may be up 
+	 * and able to arp
+	 */
+	if ((bond->primary_slave != NULL) && (arp_interval == 0)) {
+		if (IS_UP(bond->primary_slave->dev)) 
+			newslave = bond->primary_slave;
+	}
+
 	do {
 		if (IS_UP(newslave->dev)) {
 			if (newslave->link == BOND_LINK_UP) {
 				/* this one is immediately usable */
 				if (mode == BOND_MODE_ACTIVEBACKUP) {
 					bond_set_slave_active_flags(newslave);
+					bond_mc_update(bond, newslave, oldslave);
 					printk (" and making interface %s the active one.\n",
 						newslave->dev->name);
 				}
@@ -1033,14 +1352,30 @@
 
 		bestslave->delay = 0;
 		bestslave->link = BOND_LINK_UP;
+		bestslave->jiffies = jiffies;
 		bond_set_slave_active_flags(bestslave);
-
+		bond_mc_update(bond, bestslave, oldslave);
 		write_lock(&bond->ptrlock);
 		bond->current_slave = bestslave;
 		write_unlock(&bond->ptrlock);
 		return bestslave;
 	}
 
+	if ((mode == BOND_MODE_ACTIVEBACKUP) &&
+	    (multicast == BOND_MULTICAST_ACTIVE) &&
+	    (oldslave != NULL)) {
+		/* flush bonds (master's) mc_list from oldslave since it wasn't
+		 * updated (and deleted) above
+		 */ 
+		bond_mc_list_flush(oldslave->dev, bond->device); 
+		if (bond->device->flags & IFF_PROMISC) {
+			dev_set_promiscuity(oldslave->dev, -1);
+		}
+		if (bond->device->flags & IFF_ALLMULTI) {
+			dev_set_allmulti(oldslave->dev, -1);
+		}
+	}
+
 	printk (" but could not find any %s interface.\n",
 		(mode == BOND_MODE_ACTIVEBACKUP) ? "backup":"other");
 	
@@ -1084,6 +1419,7 @@
 		return -EINVAL;
 	}
 
+	bond->current_arp_slave = NULL;
 	our_slave = (slave_t *)bond;
 	old_current = bond->current_slave;
 	while ((our_slave = our_slave->prev) != (slave_t *)bond) {
@@ -1104,16 +1440,18 @@
 
 			/* release the slave from its bond */
 
-			/* flush master's mc_list from slave */ 
-			bond_mc_list_flush (slave, master); 
-       
-			/* unset promiscuity level from slave */
-			if (master->flags & IFF_PROMISC) 
-				dev_set_promiscuity(slave, -1); 
+			if (multicast == BOND_MULTICAST_ALL) {
+				/* flush master's mc_list from slave */ 
+				bond_mc_list_flush (slave, master); 
+				
+				/* unset promiscuity level from slave */
+				if (master->flags & IFF_PROMISC) 
+					dev_set_promiscuity(slave, -1); 
        
-			/* unset allmulti level from slave */ 
-			if (master->flags & IFF_ALLMULTI)
-				dev_set_allmulti(slave, -1); 
+				/* unset allmulti level from slave */ 
+				if (master->flags & IFF_ALLMULTI)
+					dev_set_allmulti(slave, -1); 
+			}
 
 			netdev_set_master(slave, NULL);
 
@@ -1125,6 +1463,7 @@
 			if (slave->flags & IFF_NOARP || 
 				bond->current_slave != NULL) {
 					dev_close(slave);
+					our_slave->original_flags &= ~IFF_UP;
 			}
 
 			bond_restore_slave_flags(our_slave);
@@ -1138,6 +1477,10 @@
 
 			update_slave_cnt(bond);
 
+			if (bond->primary_slave == our_slave) {
+				bond->primary_slave = NULL;
+			}
+
 			write_unlock_irqrestore(&bond->lock, flags);
 			return 0;  /* deletion OK */
 		}
@@ -1169,12 +1512,28 @@
 	}
 
 	bond = (struct bonding *) master->priv;
-	bond->current_slave = NULL;
+	bond->current_arp_slave = NULL;
 
 	while ((our_slave = bond->prev) != (slave_t *)bond) {
 		slave_dev = our_slave->dev;
 		bond->prev = our_slave->prev;
 
+		if (multicast == BOND_MULTICAST_ALL 
+		    || (multicast == BOND_MULTICAST_ACTIVE 
+			&& bond->current_slave == our_slave)) {
+
+			/* flush master's mc_list from slave */ 
+			bond_mc_list_flush (slave_dev, master); 
+			
+			/* unset promiscuity level from slave */
+			if (master->flags & IFF_PROMISC) 
+				dev_set_promiscuity(slave_dev, -1); 
+			
+			/* unset allmulti level from slave */ 
+			if (master->flags & IFF_ALLMULTI)
+				dev_set_allmulti(slave_dev, -1); 
+		}
+
 		kfree(our_slave);
 
 		netdev_set_master(slave_dev, NULL);
@@ -1186,9 +1545,12 @@
 		if (slave_dev->flags & IFF_NOARP)
 			dev_close(slave_dev);
 	}
+
+	bond->current_slave = NULL;
 	bond->next = (slave_t *)bond;
 	bond->slave_cnt = 0;
-	printk (KERN_INFO "%s: releases all slaves\n", master->name);
+	bond->primary_slave = NULL;
+	printk (KERN_INFO "%s: released all slaves\n", master->name);
 
 	return 0;
 }
@@ -1294,6 +1656,7 @@
 			} else {
 				/* link up again */
 				slave->link  = BOND_LINK_UP;
+				slave->jiffies = jiffies;
 				printk(KERN_INFO
 					"%s: link status up again after %d ms "
 					"for interface %s.\n",
@@ -1346,8 +1709,10 @@
 				if (slave->delay == 0) {
 					/* now the link has been up for long time enough */
 					slave->link = BOND_LINK_UP;
+					slave->jiffies = jiffies;
 
-					if (mode == BOND_MODE_ACTIVEBACKUP) {
+					if ( (mode == BOND_MODE_ACTIVEBACKUP) 
+					 || (slave != bond->primary_slave) ) {
 						/* prevent it from being the active one */
 						slave->state = BOND_STATE_BACKUP;
 					}
@@ -1361,15 +1726,26 @@
 						"for interface %s.\n",
 						master->name,
 						dev->name);
+	
+					if ( (bond->primary_slave != NULL)	 
+					  && (slave == bond->primary_slave) )
+						change_active_interface(bond); 
 				}
 				else
 					slave->delay--;
 				
 				/* we'll also look for the mostly eligible slave */
-				if (IS_UP(dev) && (slave->delay < mindelay)) {
+				if (bond->primary_slave == NULL)  {
+				    if (IS_UP(dev) && (slave->delay < mindelay)) {
+					mindelay = slave->delay;
+					bestslave = slave;
+				    } 
+				} else if ( (IS_UP(bond->primary_slave->dev))  || 
+				          ( (!IS_UP(bond->primary_slave->dev))  && 
+				          (IS_UP(dev) && (slave->delay < mindelay)) ) ) {
 					mindelay = slave->delay;
 					bestslave = slave;
-				} 
+				}
 			}
 			break;
 		} /* end of switch */
@@ -1383,7 +1759,8 @@
 	oldcurrent = bond->current_slave;
 	read_unlock(&bond->ptrlock);
 
-	if (oldcurrent == NULL) {  /* no active interface at the moment */
+	/* no active interface at the moment or need to bring up the primary */
+	if (oldcurrent == NULL)  { /* no active interface at the moment */
 		if (bestslave != NULL) { /* last chance to find one ? */
 			if (bestslave->link == BOND_LINK_UP) {
 				printk (KERN_INFO
@@ -1398,10 +1775,12 @@
 
 				bestslave->delay = 0;
 				bestslave->link  = BOND_LINK_UP;
+				bestslave->jiffies = jiffies;
 			}
 
 			if (mode == BOND_MODE_ACTIVEBACKUP) {
 				bond_set_slave_active_flags(bestslave);
+				bond_mc_update(bond, bestslave, NULL);
 			} else {
 				bestslave->state = BOND_STATE_ACTIVE;
 			}
@@ -1423,10 +1802,12 @@
 
 /* 
  * this function is called regularly to monitor each slave's link 
- * insuring that traffic is being sent and received.  If the adapter
- * has been dormant, then an arp is transmitted to generate traffic 
+ * ensuring that traffic is being sent and received when arp monitoring
+ * is used in load-balancing mode. if the adapter has been dormant, then an 
+ * arp is transmitted to generate traffic. see activebackup_arp_monitor for 
+ * arp monitoring in active backup mode. 
  */
-static void bond_arp_monitor(struct net_device *master)
+static void loadbalance_arp_monitor(struct net_device *master)
 {
 	bonding_t *bond;
 	unsigned long flags;
@@ -1442,147 +1823,358 @@
 
 	read_lock_irqsave(&bond->lock, flags);
 
-	if (!IS_UP(master)) {
+	/* TODO: investigate why rtnl_shlock_nowait and rtnl_exlock_nowait
+	 * are called below and add comment why they are required... 
+	 */
+	if ((!IS_UP(master)) || rtnl_shlock_nowait()) {
 		mod_timer(&bond->arp_timer, next_timer);
-		goto arp_monitor_out;
-	}
-
-
-	if (rtnl_shlock_nowait()) {
-		goto arp_monitor_out;
+		read_unlock_irqrestore(&bond->lock, flags);
+		return;
 	}
 
 	if (rtnl_exlock_nowait()) {
 		rtnl_shunlock();
-		goto arp_monitor_out;
+		mod_timer(&bond->arp_timer, next_timer);
+		read_unlock_irqrestore(&bond->lock, flags);
+		return;
 	}
 
-	/* see if any of the previous devices are up now (i.e. they have seen a 
-	 * response from an arp request sent by another adapter, since they 
-	 * have the same hardware address).
+	/* see if any of the previous devices are up now (i.e. they have
+	 * xmt and rcv traffic). the current_slave does not come into
+	 * the picture unless it is null. also, slave->jiffies is not needed
+	 * here because we send an arp on each slave and give a slave as
+	 * long as it needs to get the tx/rx within the delta.
+	 * TODO: what about up/down delay in arp mode? it wasn't here before
+	 *       so it can wait 
 	 */
-
 	slave = (slave_t *)bond;
 	while ((slave = slave->prev) != (slave_t *)bond)  {
 
-		read_lock(&bond->ptrlock);
-	  	if ( (!(slave->link == BOND_LINK_UP))  
-				&& (slave != bond->current_slave) ) {
-
-			read_unlock(&bond->ptrlock);
+	  	if (slave->link != BOND_LINK_UP) {
 
-	  		if ( ((jiffies - slave->dev->trans_start) <= 
+	  		if (((jiffies - slave->dev->trans_start) <= 
 						the_delta_in_ticks) &&  
 			     ((jiffies - slave->dev->last_rx) <= 
-						the_delta_in_ticks) ) {
+						the_delta_in_ticks)) {
 
 				slave->link  = BOND_LINK_UP;
-				write_lock(&bond->ptrlock);
+				slave->state = BOND_STATE_ACTIVE;
+
+				/* primary_slave has no meaning in round-robin
+				 * mode. the window of a slave being up and 
+				 * current_slave being null after enslaving
+				 * is closed.
+				 */
+				read_lock(&bond->ptrlock);
 				if (bond->current_slave == NULL) {
-					slave->state = BOND_STATE_ACTIVE;
+					read_unlock(&bond->ptrlock);
+					printk(KERN_INFO
+						"%s: link status definitely up "
+						"for interface %s, ",
+						master->name,
+						slave->dev->name);
+					change_active_interface(bond); 
+				} else {
+					read_unlock(&bond->ptrlock);
+					printk(KERN_INFO
+						"%s: interface %s is now up\n",
+						master->name,
+						slave->dev->name);
+				}
+			} 
+		} else {
+			/* slave->link == BOND_LINK_UP */
+
+			/* not all switches will respond to an arp request
+			 * when the source ip is 0, so don't take the link down
+			 * if we don't know our ip yet
+			 */
+			if (((jiffies - slave->dev->trans_start) >= 
+		              (2*the_delta_in_ticks)) ||
+		             (((jiffies - slave->dev->last_rx) >= 
+		               (2*the_delta_in_ticks)) && my_ip !=0)) {
+				slave->link  = BOND_LINK_DOWN;
+				slave->state = BOND_STATE_BACKUP;
+				if (slave->link_failure_count < UINT_MAX) {
+					slave->link_failure_count++;
+				}
+				printk(KERN_INFO
+				       "%s: interface %s is now down.\n",
+				       master->name,
+				       slave->dev->name);
+
+				read_lock(&bond->ptrlock);
+				if (slave == bond->current_slave) {
+					read_unlock(&bond->ptrlock);
+					change_active_interface(bond);
+				} else {
+					read_unlock(&bond->ptrlock);
+				}
+			}
+		} 
+
+		/* note: if switch is in round-robin mode, all links 
+		 * must tx arp to ensure all links rx an arp - otherwise
+		 * links may oscillate or not come up at all; if switch is 
+		 * in something like xor mode, there is nothing we can 
+		 * do - all replies will be rx'ed on same link causing slaves 
+		 * to be unstable during low/no traffic periods
+		 */
+		if (IS_UP(slave->dev)) {
+			arp_send_all(slave);
+		}
+	}
+
+	rtnl_exunlock();
+	rtnl_shunlock();
+	read_unlock_irqrestore(&bond->lock, flags);
+
+	/* re-arm the timer */
+	mod_timer(&bond->arp_timer, next_timer);
+}
+
+/* 
+ * When using arp monitoring in active-backup mode, this function is
+ * called to determine if any backup slaves have went down or a new
+ * current slave needs to be found.
+ * The backup slaves never generate traffic, they are considered up by merely 
+ * receiving traffic. If the current slave goes down, each backup slave will 
+ * be given the opportunity to tx/rx an arp before being taken down - this 
+ * prevents all slaves from being taken down due to the current slave not 
+ * sending any traffic for the backups to receive. The arps are not necessarily
+ * necessary, any tx and rx traffic will keep the current slave up. While any 
+ * rx traffic will keep the backup slaves up, the current slave is responsible 
+ * for generating traffic to keep them up regardless of any other traffic they 
+ * may have received.
+ * see loadbalance_arp_monitor for arp monitoring in load balancing mode
+ */
+static void activebackup_arp_monitor(struct net_device *master)
+{
+	bonding_t *bond;
+	unsigned long flags;
+	slave_t *slave;
+	int the_delta_in_ticks =  arp_interval * HZ / 1000;
+	int next_timer = jiffies + (arp_interval * HZ / 1000);
+
+	bond = (struct bonding *) master->priv; 
+	if (master->priv == NULL) {
+		mod_timer(&bond->arp_timer, next_timer);
+		return;
+	}
+
+	read_lock_irqsave(&bond->lock, flags);
+
+	if (!IS_UP(master)) {
+		mod_timer(&bond->arp_timer, next_timer);
+	        read_unlock_irqrestore(&bond->lock, flags);
+		return;
+	}
+
+	/* determine if any slave has come up or any backup slave has 
+	 * gone down 
+	 * TODO: what about up/down delay in arp mode? it wasn't here before
+	 *       so it can wait 
+	 */
+	slave = (slave_t *)bond;
+	while ((slave = slave->prev) != (slave_t *)bond)  {
+
+	  	if (slave->link != BOND_LINK_UP) {
+			if ((jiffies - slave->dev->last_rx) <=
+			    the_delta_in_ticks) {
+
+				slave->link = BOND_LINK_UP;
+				write_lock(&bond->ptrlock);
+				if ((bond->current_slave == NULL) &&
+				    ((jiffies - slave->dev->trans_start) <=
+				     the_delta_in_ticks)) {
 					bond->current_slave = slave;
+					bond_set_slave_active_flags(slave);
+			                bond_mc_update(bond, slave, NULL);
+					bond->current_arp_slave = NULL;
+				} else if (bond->current_slave != slave) {
+					/* this slave has just come up but we 
+					 * already have a current slave; this
+					 * can also happen if bond_enslave adds
+					 * a new slave that is up while we are 
+					 * searching for a new slave
+					 */
+					bond_set_slave_inactive_flags(slave);
+					bond->current_arp_slave = NULL;
 				}
-				if (slave != bond->current_slave) {
-					slave->dev->flags |= IFF_NOARP;
+
+				if (slave == bond->current_slave) {
+					printk(KERN_INFO
+						"%s: %s is up and now the "
+						"active interface\n",
+						master->name,
+						slave->dev->name);
+				} else {
+					printk(KERN_INFO
+						"%s: backup interface %s is "
+						"now up\n",
+						master->name,
+						slave->dev->name);
 				}
+
 				write_unlock(&bond->ptrlock);
-			} else {
-				if ((jiffies - slave->dev->last_rx) <= 
-						the_delta_in_ticks)  {
-					arp_send(ARPOP_REQUEST, ETH_P_ARP, 
-						arp_target, slave->dev, 
-						my_ip, arp_target_hw_addr, 
-						slave->dev->dev_addr, 
-		  				arp_target_hw_addr); 
+			}
+		} else {
+			read_lock(&bond->ptrlock);
+			if ((slave != bond->current_slave) &&
+			    (bond->current_arp_slave == NULL) &&
+			    (((jiffies - slave->dev->last_rx) >=
+			     3*the_delta_in_ticks) && (my_ip != 0))) {
+				/* a backup slave has gone down; three times 
+				 * the delta allows the current slave to be 
+				 * taken out before the backup slave.
+				 * note: a non-null current_arp_slave indicates
+				 * the current_slave went down and we are 
+				 * searching for a new one; under this 
+				 * condition we only take the current_slave 
+				 * down - this gives each slave a chance to 
+				 * tx/rx traffic before being taken out
+				 */
+				read_unlock(&bond->ptrlock);
+				slave->link  = BOND_LINK_DOWN;
+				if (slave->link_failure_count < UINT_MAX) {
+					slave->link_failure_count++;
 				}
+				bond_set_slave_inactive_flags(slave);
+				printk(KERN_INFO
+					"%s: backup interface %s is now down\n",
+					master->name,
+					slave->dev->name);
+			} else {
+				read_unlock(&bond->ptrlock);
 			}
-		} else 
-			read_unlock(&bond->ptrlock);
+		}
 	}
 
 	read_lock(&bond->ptrlock);
 	slave = bond->current_slave;
 	read_unlock(&bond->ptrlock);
 
-	if (slave != 0) {
-	
-	  /* see if you need to take down the current_slave, since
-	   * you haven't seen an arp in 2*arp_intervals
-	   */
-
-		if ( ((jiffies - slave->dev->trans_start) >= 
-		      (2*the_delta_in_ticks)) ||
-		     ((jiffies - slave->dev->last_rx) >= 
-		      (2*the_delta_in_ticks)) ) {
+	if (slave != NULL) {
 
-			if (slave->link == BOND_LINK_UP) {
-				slave->link  = BOND_LINK_DOWN;
-				slave->state = BOND_STATE_BACKUP;
-				/* 
-				 * we want to see arps, otherwise we couldn't 
-				 * bring the adapter back online...  
-				 */
-				printk(KERN_INFO "%s: link status definitely "
-						 "down for interface %s, "
-						 "disabling it",
-				       slave->dev->master->name,
-				       slave->dev->name);
-				/* find a new interface and be verbose */
-				change_active_interface(bond);
-				read_lock(&bond->ptrlock);
-				slave = bond->current_slave;
-				read_unlock(&bond->ptrlock);
-			}
-		} 
+		/* if we have sent traffic in the past 2*arp_intervals but
+		 * haven't xmit and rx traffic in that time interval, select 
+		 * a different slave. slave->jiffies is only updated when
+		 * a slave first becomes the current_slave - not necessarily
+		 * after every arp; this ensures the slave has a full 2*delta 
+		 * before being taken out. if a primary is being used, check 
+		 * if it is up and needs to take over as the current_slave
+		 */
+		if ((((jiffies - slave->dev->trans_start) >= 
+		       (2*the_delta_in_ticks)) ||
+		     (((jiffies - slave->dev->last_rx) >= 
+		       (2*the_delta_in_ticks)) && (my_ip != 0))) &&
+		    ((jiffies - slave->jiffies) >= 2*the_delta_in_ticks)) {
+
+			slave->link  = BOND_LINK_DOWN;
+			if (slave->link_failure_count < UINT_MAX) {
+				slave->link_failure_count++;
+			}
+			printk(KERN_INFO "%s: link status down for "
+					 "active interface %s, disabling it",
+			       master->name,
+			       slave->dev->name);
+			slave = change_active_interface(bond);
+			bond->current_arp_slave = slave;
+			if (slave != NULL) {
+				slave->jiffies = jiffies;
+			}
+
+		} else if ((bond->primary_slave != NULL) && 
+			   (bond->primary_slave != slave) && 
+			   (bond->primary_slave->link == BOND_LINK_UP)) {
+			/* at this point, slave is the current_slave */
+			printk(KERN_INFO 
+			       "%s: changing from interface %s to primary "
+			       "interface %s\n",
+			       master->name, 
+			       slave->dev->name, 
+			       bond->primary_slave->dev->name);
+			       
+			/* primary is up so switch to it */
+			bond_set_slave_inactive_flags(slave);
+			bond_mc_update(bond, bond->primary_slave, slave);
+			write_lock(&bond->ptrlock);
+			bond->current_slave = bond->primary_slave;
+			write_unlock(&bond->ptrlock);
+			slave = bond->primary_slave;
+			bond_set_slave_active_flags(slave);
+			slave->jiffies = jiffies;
+		} else {
+			bond->current_arp_slave = NULL;
+		}
 
-		/* 
-		 * ok, we know up/down, so just send a arp out if there has
-		 * been no activity for a while 
+		/* the current slave must tx an arp to ensure backup slaves
+		 * rx traffic
 		 */
+		if ((slave != NULL) &&
+		    (((jiffies - slave->dev->last_rx) >= the_delta_in_ticks) &&
+		     (my_ip != 0))) {
+		  arp_send_all(slave);
+		}
+	}
 
-		if (slave != NULL ) {
-			if ( ((jiffies - slave->dev->trans_start) >= 
-			       the_delta_in_ticks) || 
-			     ((jiffies - slave->dev->last_rx) >= 
-			       the_delta_in_ticks) ) {
-				arp_send(ARPOP_REQUEST, ETH_P_ARP, 
-					 arp_target, slave->dev,
-					 my_ip, arp_target_hw_addr, 
-					 slave->dev->dev_addr, 
-					 arp_target_hw_addr); 
-			}
+	/* if we don't have a current_slave, search for the next available 
+	 * backup slave from the current_arp_slave and make it the candidate 
+	 * for becoming the current_slave
+	 */
+	if (slave == NULL) { 
+
+		if ((bond->current_arp_slave == NULL) ||
+		    (bond->current_arp_slave == (slave_t *)bond)) {
+			bond->current_arp_slave = bond->prev;
 		} 
 
-	}
+		if (bond->current_arp_slave != (slave_t *)bond) {
+			bond_set_slave_inactive_flags(bond->current_arp_slave);
+			slave = bond->current_arp_slave->next;
+
+			/* search for next candidate */
+			do {
+				if (IS_UP(slave->dev)) {
+					slave->link = BOND_LINK_BACK;
+					bond_set_slave_active_flags(slave);
+					arp_send_all(slave);
+					slave->jiffies = jiffies;
+					bond->current_arp_slave = slave;
+					break;
+				}
 
-	/* if we have no current slave.. try sending 
-	 * an arp on all of the interfaces 
-	 */
+				/* if the link state is up at this point, we 
+				 * mark it down - this can happen if we have 
+				 * simultaneous link failures and 
+				 * change_active_interface doesn't make this 
+				 * one the current slave so it is still marked 
+				 * up when it is actually down
+				 */
+				if (slave->link == BOND_LINK_UP) {
+					slave->link  = BOND_LINK_DOWN;
+					if (slave->link_failure_count < 
+							UINT_MAX) {
+						slave->link_failure_count++;
+					}
 
-	read_lock(&bond->ptrlock);
-	if (bond->current_slave == NULL) { 
-		read_unlock(&bond->ptrlock);
-		slave = (slave_t *)bond;
-		while ((slave = slave->prev) != (slave_t *)bond)   {
-			arp_send(ARPOP_REQUEST, ETH_P_ARP, arp_target, 
-				 slave->dev, my_ip, arp_target_hw_addr, 
-				 slave->dev->dev_addr, arp_target_hw_addr); 
+					bond_set_slave_inactive_flags(slave);
+					printk(KERN_INFO
+						"%s: backup interface "
+						"%s is now down.\n",
+						master->name,
+						slave->dev->name);
+				}
+			} while ((slave = slave->next) != 
+					bond->current_arp_slave->next);
 		}
 	}
-	else {
-		read_unlock(&bond->ptrlock);
-	}
-
-	rtnl_exunlock();
-	rtnl_shunlock();
-
-arp_monitor_out:
-	read_unlock_irqrestore(&bond->lock, flags);
 
-	/* re-arm the timer */
 	mod_timer(&bond->arp_timer, next_timer);
+	read_unlock_irqrestore(&bond->lock, flags);
 }
 
+
 #define isdigit(c) (c >= '0' && c <= '9')
 __inline static int atoi( char **s) 
 {
@@ -1723,7 +2315,7 @@
 	}
 	read_unlock_irqrestore(&bond->lock, flags);
 
-	if (cur_ndx == info->slave_id) {
+	if (slave != (slave_t *)bond) {
 		strcpy(info->slave_name, slave->dev->name);
 		info->link = slave->link;
 		info->state = slave->state;
@@ -1740,7 +2332,7 @@
 	struct net_device *slave_dev = NULL;
 	struct ifbond *u_binfo = NULL, k_binfo;
 	struct ifslave *u_sinfo = NULL, k_sinfo;
-	u16 *data = NULL;
+	struct mii_ioctl_data *mii = NULL;
 	int ret = 0;
 
 #ifdef BONDING_DEBUG
@@ -1750,23 +2342,23 @@
 
 	switch (cmd) {
 	case SIOCGMIIPHY:
-		data = (u16 *)ifr->ifr_data;
-		if (data == NULL) {
+		mii = (struct mii_ioctl_data *)&ifr->ifr_data;
+		if (mii == NULL) {
 			return -EINVAL;
 		}
-		data[0] = 0;
+		mii->phy_id = 0;
 		/* Fall Through */
 	case SIOCGMIIREG:
 		/* 
 		 * We do this again just in case we were called by SIOCGMIIREG
 		 * instead of SIOCGMIIPHY.
 		 */
-		data = (u16 *)ifr->ifr_data;
-		if (data == NULL) {
+		mii = (struct mii_ioctl_data *)&ifr->ifr_data;
+		if (mii == NULL) {
 			return -EINVAL;
 		}
-		if (data[1] == 1) {
-			data[3] = bond_check_mii_link(
+		if (mii->reg_num == 1) {
+			mii->val_out = bond_check_mii_link(
 				(struct bonding *)master_dev->priv);
 		}
 		return 0;
@@ -1849,6 +2441,65 @@
 }
 #endif
 
+/* 
+ * in broadcast mode, we send everything to all usable interfaces.
+ */
+static int bond_xmit_broadcast(struct sk_buff *skb, struct net_device *dev)
+{
+	slave_t *slave, *start_at;
+	struct bonding *bond = (struct bonding *) dev->priv;
+	unsigned long flags;
+	struct net_device *device_we_should_send_to = 0;
+
+	if (!IS_UP(dev)) { /* bond down */
+		dev_kfree_skb(skb);
+		return 0;
+	}
+
+	read_lock_irqsave(&bond->lock, flags);
+
+	read_lock(&bond->ptrlock);
+	slave = start_at = bond->current_slave;
+	read_unlock(&bond->ptrlock);
+
+	if (slave == NULL) { /* we're at the root, get the first slave */
+		/* no suitable interface, frame not sent */
+		read_unlock_irqrestore(&bond->lock, flags);
+		dev_kfree_skb(skb);
+		return 0;
+	}
+
+	do {
+		if (IS_UP(slave->dev)
+		    && (slave->link == BOND_LINK_UP)
+		    && (slave->state == BOND_STATE_ACTIVE)) {
+			if (device_we_should_send_to) {
+				struct sk_buff *skb2;
+				if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL) {
+					printk(KERN_ERR "bond_xmit_broadcast: skb_clone() failed\n");
+					continue;
+				}
+
+				skb2->dev = device_we_should_send_to;
+				skb2->priority = 1;
+				dev_queue_xmit(skb2);
+			}
+			device_we_should_send_to = slave->dev;
+		}
+	} while ((slave = slave->next) != start_at);
+
+	if (device_we_should_send_to) {
+		skb->dev = device_we_should_send_to;
+		skb->priority = 1;
+		dev_queue_xmit(skb);
+	} else
+		dev_kfree_skb(skb);
+
+	/* frame sent to all suitable interfaces */
+	read_unlock_irqrestore(&bond->lock, flags);
+	return 0;
+}
+
 static int bond_xmit_roundrobin(struct sk_buff *skb, struct net_device *dev)
 {
 	slave_t *slave, *start_at;
@@ -1981,14 +2632,25 @@
 	}
 
 	/* if we are sending arp packets and don't know 
-	   the target hw address, save it so we don't need 
-	   to use a broadcast address */
-	if ( (arp_interval > 0) && (arp_target_hw_addr == NULL) &&
+	 * the target hw address, save it so we don't need 
+	 * to use a broadcast address.
+	 * don't do this if in active backup mode because the slaves must 
+	 * receive packets to stay up, and the only ones they receive are 
+	 * broadcasts. 
+	 */
+	if ( (mode != BOND_MODE_ACTIVEBACKUP) && 
+             (arp_ip_count == 1) &&
+	     (arp_interval > 0) && (arp_target_hw_addr == NULL) &&
 	     (skb->protocol == __constant_htons(ETH_P_IP) ) ) {
 		struct ethhdr *eth_hdr = 
 			(struct ethhdr *) (((char *)skb->data));
-		arp_target_hw_addr = kmalloc(ETH_ALEN, GFP_KERNEL);
-		memcpy(arp_target_hw_addr, eth_hdr->h_dest, ETH_ALEN);
+		struct iphdr *ip_hdr = (struct iphdr *)(eth_hdr + 1);
+
+		if (arp_target[0] == ip_hdr->daddr) {
+			arp_target_hw_addr = kmalloc(ETH_ALEN, GFP_KERNEL);
+			if (arp_target_hw_addr != NULL)
+				memcpy(arp_target_hw_addr, eth_hdr->h_dest, ETH_ALEN);
+		}
 	}
 
 	read_lock_irqsave(&bond->lock, flags);
@@ -2077,29 +2739,7 @@
 		 */
 		link = bond_check_mii_link(bond);
 
-		len += sprintf(buf + len, "Bonding Mode: ");
-
-		switch (mode) {
-			case BOND_MODE_ACTIVEBACKUP:
-				len += sprintf(buf + len, "%s\n", 
-						"active-backup");
-			break;
-
-			case BOND_MODE_ROUNDROBIN:
-				len += sprintf(buf + len, "%s\n", 
-						"load balancing (round-robin)");
-			break;
-
-			case BOND_MODE_XOR:
-				len += sprintf(buf + len, "%s\n", 
-						"load balancing (xor)");
-			break;
-
-			default:
-				len += sprintf(buf + len, "%s\n", 
-						"unknown");
-			break;
-		}
+		len += sprintf(buf + len, "Bonding Mode: %s\n", bond_mode());
 
 		if (mode == BOND_MODE_ACTIVEBACKUP) {
 			read_lock_irqsave(&bond->lock, flags);
@@ -2118,8 +2758,11 @@
 				link == BMSR_LSTATUS ? "up\n" : "down\n");
 		len += sprintf(buf + len, "MII Polling Interval (ms): %d\n", 
 				miimon);
-		len += sprintf(buf + len, "Up Delay (ms): %d\n", updelay);
-		len += sprintf(buf + len, "Down Delay (ms): %d\n", downdelay);
+		len += sprintf(buf + len, "Up Delay (ms): %d\n", 
+				updelay * miimon);
+		len += sprintf(buf + len, "Down Delay (ms): %d\n", 
+				downdelay * miimon);
+		len += sprintf(buf + len, "Multicast Mode: %s\n", multicast_mode());
 
 		read_lock_irqsave(&bond->lock, flags);
 		for (slave = bond->prev; slave != (slave_t *)bond; 
@@ -2208,6 +2851,7 @@
 static int __init bond_init(struct net_device *dev)
 {
 	bonding_t *bond, *this_bond, *last_bond;
+	int count;
 
 #ifdef BONDING_DEBUG
 	printk (KERN_INFO "Begin bond_init for %s\n", dev->name);
@@ -2231,6 +2875,7 @@
 
 	bond->next = bond->prev = (slave_t *)bond;
 	bond->current_slave = NULL;
+	bond->current_arp_slave = NULL;
 	bond->device = dev;
 	dev->priv = bond;
 
@@ -2241,6 +2886,8 @@
 		dev->hard_start_xmit = bond_xmit_roundrobin;
 	} else if (mode == BOND_MODE_XOR) {
 		dev->hard_start_xmit = bond_xmit_xor;
+	} else if (mode == BOND_MODE_BROADCAST) {
+		dev->hard_start_xmit = bond_xmit_broadcast;
 	} else {
 		printk(KERN_ERR "Unknown bonding mode %d\n", mode);
 		kfree(bond->stats);
@@ -2275,7 +2922,18 @@
 	} else {
 		printk("out MII link monitoring");
 	}
-	printk(", in %s mode.\n",mode?"active-backup":"bonding");
+	printk(", in %s mode.\n", bond_mode());
+
+	printk(KERN_INFO "%s registered with", dev->name);
+	if (arp_interval > 0) {
+		printk(" ARP monitoring set to %d ms with %d target(s):", 
+			arp_interval, arp_ip_count);
+		for (count=0 ; count<arp_ip_count ; count++)
+                        printk (" %s", arp_ip_target[count]);
+		printk("\n");
+	} else {
+		printk("out ARP monitoring\n");
+	}
 
 #ifdef CONFIG_PROC_FS
 	bond->bond_proc_dir = proc_mkdir(dev->name, proc_net);
@@ -2332,6 +2990,8 @@
 	/* Find a name for this unit */
 	static struct net_device *dev_bond = NULL;
 
+	printk(KERN_INFO "%s", version);
+
 	if (max_bonds < 1 || max_bonds > INT_MAX) {
 		printk(KERN_WARNING 
 		       "bonding_init(): max_bonds (%d) not in range %d-%d, "
@@ -2346,6 +3006,14 @@
 	}
 	memset(dev_bonds, 0, max_bonds*sizeof(struct net_device));
 
+	if (miimon < 0) {
+		printk(KERN_WARNING 
+		       "bonding_init(): miimon module parameter (%d), "
+		       "not in range 0-%d, so it was reset to %d\n",
+		       miimon, INT_MAX, BOND_LINK_MON_INTERV);
+		miimon = BOND_LINK_MON_INTERV;
+	}
+
 	if (updelay < 0) {
 		printk(KERN_WARNING 
 		       "bonding_init(): updelay module parameter (%d), "
@@ -2362,6 +3030,52 @@
 		downdelay = 0;
 	}
 
+	if (miimon == 0) {
+		if ((updelay != 0) || (downdelay != 0)) {
+			/* just warn the user the up/down delay will have
+			 * no effect since miimon is zero...
+			 */
+			printk(KERN_WARNING 
+		               "bonding_init(): miimon module parameter not "
+			       "set and updelay (%d) or downdelay (%d) module "
+			       "parameter is set; updelay and downdelay have "
+			       "no effect unless miimon is set\n",
+		               updelay, downdelay);
+		}
+	} else {
+		/* don't allow arp monitoring */
+		if (arp_interval != 0) {
+			printk(KERN_WARNING 
+		               "bonding_init(): miimon (%d) and arp_interval "
+			       "(%d) can't be used simultaneously, "
+			       "disabling ARP monitoring\n",
+		               miimon, arp_interval);
+			arp_interval = 0;
+		}
+
+		if ((updelay % miimon) != 0) {
+			/* updelay will be rounded in bond_init() when it
+			 * is divided by miimon, we just inform user here
+			 */
+			printk(KERN_WARNING 
+		               "bonding_init(): updelay (%d) is not a multiple "
+			       "of miimon (%d), updelay rounded to %d ms\n",
+		               updelay, miimon, (updelay / miimon) * miimon);
+		}
+
+		if ((downdelay % miimon) != 0) {
+			/* downdelay will be rounded in bond_init() when it
+			 * is divided by miimon, we just inform user here
+			 */
+			printk(KERN_WARNING 
+		               "bonding_init(): downdelay (%d) is not a "
+			       "multiple of miimon (%d), downdelay rounded "
+			       "to %d ms\n",
+		               downdelay, miimon, 
+			       (downdelay / miimon) * miimon);
+		}
+	}
+
 	if (arp_interval < 0) {
 		printk(KERN_WARNING 
 		       "bonding_init(): arp_interval module parameter (%d), "
@@ -2370,11 +3084,63 @@
 		arp_interval = BOND_LINK_ARP_INTERV;
 	}
 
-	if (arp_ip_target) {
-		/* TODO: check and log bad ip address */
-		if (my_inet_aton(arp_ip_target, &arp_target) == 0)  {
-			arp_interval = 0;
+        for (arp_ip_count=0 ;
+             (arp_ip_count < MAX_ARP_IP_TARGETS) && arp_ip_target[arp_ip_count];
+              arp_ip_count++ ) {
+                /* TODO: check and log bad ip address */
+                if (my_inet_aton(arp_ip_target[arp_ip_count],
+                                 &arp_target[arp_ip_count]) == 0) {
+                        printk(KERN_WARNING
+                               "bonding_init(): bad arp_ip_target module "
+                               "parameter (%s), ARP monitoring will not be "
+                               "performed\n",
+                               arp_ip_target[arp_ip_count]);
+                        arp_interval = 0;
 		}
+        }
+
+
+	if ( (arp_interval > 0) && (arp_ip_count==0)) {
+		/* don't allow arping if no arp_ip_target given... */
+		printk(KERN_WARNING 
+		       "bonding_init(): arp_interval module parameter "
+		       "(%d) specified without providing an arp_ip_target "
+		       "parameter, arp_interval was reset to 0\n",
+		       arp_interval);
+		arp_interval = 0;
+	}
+
+	if ((miimon == 0) && (arp_interval == 0)) {
+		/* miimon and arp_interval not set, we need one so things
+		 * work as expected, see bonding.txt for details
+		 */
+		printk(KERN_ERR 
+		       "bonding_init(): either miimon or "
+		       "arp_interval and arp_ip_target module parameters "
+		       "must be specified, otherwise bonding will not detect "
+		       "link failures! see bonding.txt for details.\n");
+	}
+
+	if ((primary != NULL) && (mode != BOND_MODE_ACTIVEBACKUP)){
+		/* currently, using a primary only makes sence 
+		 * in active backup mode 
+		 */
+		printk(KERN_WARNING 
+		       "bonding_init(): %s primary device specified but has "
+		       " no effect in %s mode\n",
+		       primary, bond_mode());
+		primary = NULL;
+	}
+
+
+        if (multicast != BOND_MULTICAST_DISABLED &&
+	    multicast != BOND_MULTICAST_ACTIVE &&
+	    multicast != BOND_MULTICAST_ALL) {
+		printk(KERN_WARNING 
+		       "bonding_init(): unknown multicast module "
+		       "parameter (%d), multicast reset to %d\n", 
+		       multicast, BOND_MULTICAST_ALL);
+		multicast = BOND_MULTICAST_ALL;
 	}
 
 	for (no = 0; no < max_bonds; no++) {
@@ -2423,6 +3189,7 @@
 module_init(bonding_init);
 module_exit(bonding_exit);
 MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(DRV_DESCRIPTION ", v" DRV_VERSION);
 
 /*
  * Local variables:
diff -urN linux-2.4.21-pre2/drivers/net/de4x5.c linux-2.4.21-pre3/drivers/net/de4x5.c
--- linux-2.4.21-pre2/drivers/net/de4x5.c	Fri Aug  2 17:39:44 2002
+++ linux-2.4.21-pre3/drivers/net/de4x5.c	Mon Jan  6 16:32:32 2003
@@ -1229,6 +1229,7 @@
 	lp->pdev = pdev;
 	memcpy((char *)&lp->srom,(char *)&bus.srom,sizeof(struct de4x5_srom));
 	lp->lock = (spinlock_t) SPIN_LOCK_UNLOCKED;
+	init_timer(&lp->timer);
 	de4x5_parse_params(dev);
 
 	/*
diff -urN linux-2.4.21-pre2/drivers/net/e100/e100.h linux-2.4.21-pre3/drivers/net/e100/e100.h
--- linux-2.4.21-pre2/drivers/net/e100/e100.h	Mon Jan  6 16:32:28 2003
+++ linux-2.4.21-pre3/drivers/net/e100/e100.h	Mon Jan  6 16:32:32 2003
@@ -268,6 +268,7 @@
 #define SCB_CUC_NOOP            0
 #define SCB_CUC_START           BIT_4	/* CU Start */
 #define SCB_CUC_RESUME          BIT_5	/* CU Resume */
+#define SCB_CUC_UNKNOWN         BIT_7	/* CU unknown command */
 /* Changed for 82558 enhancements */
 #define SCB_CUC_STATIC_RESUME   (BIT_5 | BIT_7)	/* 82558/9 Static Resume */
 #define SCB_CUC_DUMP_ADDR       BIT_6	/* CU Dump Counters Address */
@@ -953,6 +954,10 @@
 	u32 pci_state[16];
 #endif
 	char ifname[IFNAMSIZ];
+#ifdef E100_CU_DEBUG	
+	u8 last_cmd;
+	u8 last_sub_cmd;
+#endif	
 };
 
 #define E100_AUTONEG        0
@@ -964,7 +969,7 @@
 /********* function prototypes *************/
 extern void e100_isolate_driver(struct e100_private *bdp);
 extern void e100_sw_reset(struct e100_private *bdp, u32 reset_cmd);
-extern void e100_start_cu(struct e100_private *bdp, tcb_t *tcb);
+extern u8 e100_start_cu(struct e100_private *bdp, tcb_t *tcb);
 extern void e100_free_non_tx_cmd(struct e100_private *bdp,
 				 nxmit_cb_entry_t *non_tx_cmd);
 extern nxmit_cb_entry_t *e100_alloc_non_tx_cmd(struct e100_private *bdp);
@@ -976,8 +981,10 @@
 extern unsigned char e100_wait_scb(struct e100_private *bdp);
 
 extern void e100_deisolate_driver(struct e100_private *bdp, u8 full_reset);
-extern unsigned char e100_hw_reset_recover(struct e100_private *bdp,
-					   u32 reset_cmd);
+extern unsigned char e100_configure_device(struct e100_private *bdp);
+#ifdef E100_CU_DEBUG
+extern unsigned char e100_cu_unknown_state(struct e100_private *bdp);
+#endif
 
 #define ROM_TEST_FAIL		0x01
 #define REGISTER_TEST_FAIL	0x02
diff -urN linux-2.4.21-pre2/drivers/net/e100/e100_config.c linux-2.4.21-pre3/drivers/net/e100/e100_config.c
--- linux-2.4.21-pre2/drivers/net/e100/e100_config.c	Thu Nov 28 15:53:13 2002
+++ linux-2.4.21-pre3/drivers/net/e100/e100_config.c	Mon Jan  6 16:32:32 2003
@@ -494,8 +494,7 @@
  * e100_config_wol
  * @bdp: atapter's private data struct
  *
- * This sets configuration options for Wake On LAN functionality (WOL) in the
- * config record. WOL options are retrieved from wolinfo_wolopts in @bdp
+ * This sets configuration options for PHY and Magic Packet WoL 
  */
 void
 e100_config_wol(struct e100_private *bdp)
@@ -504,14 +503,21 @@
 
 	if (bdp->wolopts & WAKE_PHY) {
 		bdp->config[9] |= CB_LINK_STATUS_WOL;
-		E100_CONFIG(bdp, 9);
+	}
+	else {
+		/* Disable PHY WoL */
+		bdp->config[9] &= ~CB_LINK_STATUS_WOL;
 	}
 
-	if (!(bdp->wolopts & WAKE_MAGIC)) {
+	if (bdp->wolopts & WAKE_MAGIC) {
+		bdp->config[19] &= ~CB_DISABLE_MAGPAK_WAKE;
+	}
+	else {
+		/* Disable Magic Packet WoL */
 		bdp->config[19] |= CB_DISABLE_MAGPAK_WAKE;
-		E100_CONFIG(bdp, 19);
 	}
 
+	E100_CONFIG(bdp, 19);
 	spin_unlock_bh(&(bdp->config_lock));
 }
 
diff -urN linux-2.4.21-pre2/drivers/net/e100/e100_main.c linux-2.4.21-pre3/drivers/net/e100/e100_main.c
--- linux-2.4.21-pre2/drivers/net/e100/e100_main.c	Mon Jan  6 16:32:28 2003
+++ linux-2.4.21-pre3/drivers/net/e100/e100_main.c	Mon Jan  6 16:32:32 2003
@@ -46,6 +46,21 @@
 
 /* Change Log
  *
+ * 2.1.29	12/20/02
+ *   o Bug fix: Device command timeout due to SMBus processing during init
+ *   o Bug fix: Not setting/clearing I (Interrupt) bit in tcb correctly
+ *   o Bug fix: Not using EEPROM WoL setting as default in ethtool
+ *   o Bug fix: Not able to set autoneg on using ethtool when interface down
+ *   o Bug fix: Not able to change speed/duplex using ethtool/mii
+ *     when interface up
+ *   o Bug fix: Ethtool shows autoneg on when forced to 100/Full
+ *   o Bug fix: Compiler error when CONFIG_PROC_FS not defined
+ *   o Bug fix: 2.5.44 e100 doesn't load with preemptive kernel enabled
+ *     (sleep while holding spinlock)
+ *   o Bug fix: 2.1.24-k1 doesn't display complete statistics
+ *   o Bug fix: System panic due to NULL watchdog timer dereference during
+ *     ifconfig down, rmmod and insmod
+ *
  * 2.1.24       10/7/02
  *   o Bug fix: Wrong files under /proc/net/PRO_LAN_Adapters/ when interface
  *     name is changed
@@ -56,21 +71,6 @@
  *   o Removed misleading printks
  *
  * 2.1.12       8/2/02
- *   o Feature: ethtool register dump
- *   o Bug fix: Driver passes wrong name to /proc/interrupts
- *   o Bug fix: Ethernet bridging not working 
- *   o Bug fix: Promiscuous mode is not working
- *   o Bug fix: Checked return value from copy_from_user (William Stinson,
- *     wstinson@infonie.fr)
- *   o Bug fix: ARP wake on LAN fails
- *   o Bug fix: mii-diag does not update driver level's speed, duplex and
- *     re-configure flow control
- *   o Bug fix: Ethtool shows wrong speed/duplex when not connected
- *   o Bug fix: Ethtool shows wrong speed/duplex when reconnected if forced 
- *     speed/duplex
- *   o Bug fix: PHY loopback diagnostic fails
- *
- * 2.1.6        7/5/02
  */
  
 #include <linux/config.h>
@@ -87,8 +87,8 @@
 extern int e100_create_proc_subdir(struct e100_private *, char *);
 extern void e100_remove_proc_subdir(struct e100_private *, char *);
 #else
-#define e100_create_proc_subdir(X) 0
-#define e100_remove_proc_subdir(X) do {} while(0)
+#define e100_create_proc_subdir(X, Y) 0
+#define e100_remove_proc_subdir(X, Y) do {} while(0)
 #endif
 
 static int e100_do_ethtool_ioctl(struct net_device *, struct ifreq *);
@@ -135,7 +135,7 @@
 
 /* Global Data structures and variables */
 char e100_copyright[] __devinitdata = "Copyright (c) 2002 Intel Corporation";
-char e100_driver_version[]="2.1.24-k2";
+char e100_driver_version[]="2.1.29-k1";
 const char *e100_full_driver_name = "Intel(R) PRO/100 Network Driver";
 char e100_short_driver_name[] = "e100";
 static int e100nics = 0;
@@ -145,17 +145,17 @@
 static int e100_suspend(struct pci_dev *pcid, u32 state);
 static int e100_resume(struct pci_dev *pcid);
 struct notifier_block e100_notifier_reboot = {
-        notifier_call:  e100_notify_reboot,
-        next:           NULL,
-        priority:       0
+        .notifier_call  = e100_notify_reboot,
+        .next           = NULL,
+        .priority       = 0
 };
 #endif
 static int e100_notify_netdev(struct notifier_block *, unsigned long event, void *ptr);
  
 struct notifier_block e100_notifier_netdev = {
-	notifier_call:  e100_notify_netdev,
-	next:           NULL,
-	priority:       0
+	.notifier_call  = e100_notify_netdev,
+	.next           = NULL,
+	.priority       = 0
 };
 
 static void e100_get_mdix_status(struct e100_private *bdp);
@@ -224,7 +224,7 @@
 static void e100_set_int_option(int *, int, int, int, int, char *);
 static void e100_set_bool_option(struct e100_private *bdp, int, u32, int,
 				 char *);
-unsigned char e100_wait_exec_cmplx(struct e100_private *, u32, u8);
+unsigned char e100_wait_exec_cmplx(struct e100_private *, u32, u8, u8);
 void e100_exec_cmplx(struct e100_private *, u32, u8);
 
 /**
@@ -443,9 +443,21 @@
 	if (!e100_wait_scb(bdp)) {
 		printk(KERN_DEBUG "e100: %s: e100_wait_exec_simple: failed\n",
 		       bdp->device->name);
+#ifdef E100_CU_DEBUG		
+		printk(KERN_ERR "e100: %s: Last command (%x/%x) "
+			"timeout\n", bdp->device->name, 
+			bdp->last_cmd, bdp->last_sub_cmd);
+		printk(KERN_ERR "e100: %s: Current simple command (%x) "
+			"can't be executed\n", 
+			bdp->device->name, scb_cmd_low);
+#endif		
 		return false;
 	}
 	e100_exec_cmd(bdp, scb_cmd_low);
+#ifdef E100_CU_DEBUG	
+	bdp->last_cmd = scb_cmd_low;
+	bdp->last_sub_cmd = 0;
+#endif	
 	return true;
 }
 
@@ -458,12 +470,24 @@
 }
 
 unsigned char
-e100_wait_exec_cmplx(struct e100_private *bdp, u32 phys_addr, u8 cmd)
+e100_wait_exec_cmplx(struct e100_private *bdp, u32 phys_addr, u8 cmd, u8 sub_cmd)
 {
 	if (!e100_wait_scb(bdp)) {
+#ifdef E100_CU_DEBUG		
+		printk(KERN_ERR "e100: %s: Last command (%x/%x) "
+			"timeout\n", bdp->device->name, 
+			bdp->last_cmd, bdp->last_sub_cmd);
+		printk(KERN_ERR "e100: %s: Current complex command "
+			"(%x/%x) can't be executed\n", 
+			bdp->device->name, cmd, sub_cmd);
+#endif		
 		return false;
 	}
 	e100_exec_cmplx(bdp, phys_addr, cmd);
+#ifdef E100_CU_DEBUG	
+	bdp->last_cmd = cmd;
+	bdp->last_sub_cmd = sub_cmd;
+#endif	
 	return true;
 }
 
@@ -494,18 +518,23 @@
 }
 
 /**
- * e100_dis_intr - disable interrupts
+ * e100_disable_clear_intr - disable and clear/ack interrupts
  * @bdp: atapter's private data struct
  *
  * This routine disables interrupts at the hardware, by setting
  * the M (mask) bit in the adapter's CSR SCB command word.
+ * It also clear/ack interrupts.
  */
 static inline void
-e100_dis_intr(struct e100_private *bdp)
+e100_disable_clear_intr(struct e100_private *bdp)
 {
+	u16 intr_status;
 	/* Disable interrupts on our PCI board by setting the mask bit */
 	writeb(SCB_INT_MASK, &bdp->scb->scb_cmd_hi);
-	readw(&(bdp->scb->scb_status));	/* flushes last write, read-safe */
+	intr_status = readw(&bdp->scb->scb_status);
+	/* ack and clear intrs */
+	writew(intr_status, &bdp->scb->scb_status);
+	readw(&bdp->scb->scb_status);
 }
 
 /**
@@ -623,7 +652,7 @@
 	cal_checksum = e100_eeprom_calculate_chksum(bdp);
 	read_checksum = e100_eeprom_read(bdp, (bdp->eeprom_size - 1));
 	if (cal_checksum != read_checksum) {
-                printk(KERN_ERR "e100: Corrupted EERPROM on instance #%d\n",
+                printk(KERN_ERR "e100: Corrupted EEPROM on instance #%d\n",
 		       e100nics);
                 rc = -ENODEV;
                 goto err_pci;
@@ -673,12 +702,17 @@
 		       bdp->device->name);
 	}
 
-	/* Disabling all WOLs as initialization */
-	bdp->wolsupported = bdp->wolopts = 0;
-	if (bdp->rev_id >= D101A4_REV_ID) {
-		bdp->wolsupported = WAKE_PHY | WAKE_MAGIC;
+	bdp->wolsupported = 0;
+	bdp->wolopts = 0;
+	
+	/* Check if WoL is enabled on EEPROM */
+	if (e100_eeprom_read(bdp, EEPROM_ID_WORD) & BIT_5) {
+		if (bdp->rev_id >= D101A4_REV_ID)
+			bdp->wolsupported = WAKE_PHY | WAKE_MAGIC;
 		if (bdp->rev_id >= D101MA_REV_ID)
 			bdp->wolsupported |= WAKE_UCAST | WAKE_ARP;
+		/* Magic Packet WoL is enabled on device by default */
+		/* if EEPROM WoL bit is TRUE                        */
 		bdp->wolopts = WAKE_MAGIC;
 	}
 
@@ -953,12 +987,12 @@
 		goto err_exit;
 	}
 
-	if (!e100_wait_exec_cmplx(bdp, 0, SCB_CUC_LOAD_BASE)) {
+	if (!e100_wait_exec_cmplx(bdp, 0, SCB_CUC_LOAD_BASE, 0)) {
 		rc = -EAGAIN;
 		goto err_exit;
 	}
 
-	if (!e100_wait_exec_cmplx(bdp, 0, SCB_RUC_LOAD_BASE)) {
+	if (!e100_wait_exec_cmplx(bdp, 0, SCB_RUC_LOAD_BASE, 0)) {
 		rc = -EAGAIN;
 		goto err_exit;
 	}
@@ -1235,7 +1269,8 @@
 		printk(KERN_ERR "e100: hw init failed\n");
 		return false;
 	}
-	e100_dis_intr(bdp);
+	/* Interrupts are enabled after device reset */
+	e100_disable_clear_intr(bdp);
 
 	return true;
 }
@@ -1303,10 +1338,10 @@
 	e100_sw_reset(bdp, reset_cmd);
 
 	/* Load the CU BASE (set to 0, because we use linear mode) */
-	if (!e100_wait_exec_cmplx(bdp, 0, SCB_CUC_LOAD_BASE))
+	if (!e100_wait_exec_cmplx(bdp, 0, SCB_CUC_LOAD_BASE, 0))
 		return false;
 
-	if (!e100_wait_exec_cmplx(bdp, 0, SCB_RUC_LOAD_BASE))
+	if (!e100_wait_exec_cmplx(bdp, 0, SCB_RUC_LOAD_BASE, 0))
 		return false;
 
 	/* Load interrupt microcode  */
@@ -1590,6 +1625,12 @@
 {
 	struct e100_private *bdp = dev->priv;
 
+#ifdef E100_CU_DEBUG
+	if (e100_cu_unknown_state(bdp)) {
+		printk(KERN_ERR "e100: %s: CU unknown state in e100_watchdog\n",
+			dev->name);
+	}
+#endif	
 	if (!netif_running(dev)) {
 		return;
 	}
@@ -1646,7 +1687,8 @@
 		if (netif_running(dev))
 			netif_wake_queue(dev);
 	} else {
-		netif_stop_queue(dev);
+		if (netif_running(dev))
+			netif_stop_queue(dev);
 	}
 
 	rmb();
@@ -1774,11 +1816,8 @@
 		return;
 	}
 
-	/* disable intr before we ack & after identifying the intr as ours */
-	e100_dis_intr(bdp);
-
-	writew(intr_status, &bdp->scb->scb_status);	/* ack intrs */
-	readw(&bdp->scb->scb_status);
+	/* disable and ack intr */
+	e100_disable_clear_intr(bdp);
 
 	/* the device is closed, don't continue or else bad things may happen. */
 	if (!netif_running(dev)) {
@@ -2208,10 +2247,11 @@
  *
  * e100_start_cu must be called while holding the tx_lock ! 
  */
-void
+u8
 e100_start_cu(struct e100_private *bdp, tcb_t *tcb)
 {
 	unsigned long lock_flag;
+	u8 ret = true;
 
 	spin_lock_irqsave(&(bdp->bd_lock), lock_flag);
 	switch (bdp->next_cu_cmd) {
@@ -2242,12 +2282,13 @@
 			       "e100: %s: cu_start: timeout waiting for cu\n",
 			       bdp->device->name);
 		if (!e100_wait_exec_cmplx(bdp, (u32) (tcb->tcb_phys),
-					  SCB_CUC_START)) {
+					  SCB_CUC_START, CB_TRANSMIT)) {
 			printk(KERN_DEBUG
 			       "e100: %s: cu_start: timeout waiting for scb\n",
 			       bdp->device->name);
 			e100_exec_cmplx(bdp, (u32) (tcb->tcb_phys),
 					SCB_CUC_START);
+			ret = false;
 		}
 
 		bdp->next_cu_cmd = RESUME_WAIT;
@@ -2259,6 +2300,7 @@
 	bdp->last_tcb = tcb;
 
 	spin_unlock_irqrestore(&(bdp->bd_lock), lock_flag);
+	return ret;
 }
 
 /* ====================================================================== */
@@ -2306,8 +2348,9 @@
 	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout(HZ / 100 + 1);
 
-	/* disable interrupts since the're now enabled */
-	e100_dis_intr(bdp);
+	/* disable interrupts since they are enabled */
+	/* after device reset during selftest        */
+	e100_disable_clear_intr(bdp);
 
 	/* if The First Self Test DWORD Still Zero, We've timed out. If the
 	 * second DWORD is not zero then we have an error. */
@@ -2405,7 +2448,7 @@
 
 	spin_lock(&bdp->bd_lock);
 
-	if (!e100_wait_exec_cmplx(bdp, rx_struct->dma_addr, SCB_RUC_START)) {
+	if (!e100_wait_exec_cmplx(bdp, rx_struct->dma_addr, SCB_RUC_START, 0)) {
 		printk(KERN_DEBUG
 		       "e100: %s: start_ru: wait_scb failed\n", 
 		       bdp->device->name);
@@ -2473,7 +2516,7 @@
 	*pcmd_complete = 0;
 	wmb();
 
-	if (!e100_wait_exec_cmplx(bdp, bdp->stat_cnt_phys, SCB_CUC_DUMP_ADDR))
+	if (!e100_wait_exec_cmplx(bdp, bdp->stat_cnt_phys, SCB_CUC_DUMP_ADDR, 0))
 		return false;
 
 	/* wait 10 microseconds for the command to complete */
@@ -2595,8 +2638,10 @@
 	unsigned long lock_flag;
 	unsigned long expiration_time;
 	unsigned char rc = true;
+	u8 sub_cmd;
 
 	ntcb_hdr = (cb_header_t *) command->non_tx_cmd;	/* get hdr of non tcb cmd */
+	sub_cmd = cpu_to_le16(ntcb_hdr->cb_cmd);
 
 	/* Set the Command Block to be the last command block */
 	ntcb_hdr->cb_cmd |= __constant_cpu_to_le16(CB_EL_BIT);
@@ -2629,7 +2674,7 @@
 
 	spin_lock_irqsave(&bdp->bd_lock, lock_flag);
 
-	if (!e100_wait_exec_cmplx(bdp, command->dma_addr, SCB_CUC_START)) {
+	if (!e100_wait_exec_cmplx(bdp, command->dma_addr, SCB_CUC_START, sub_cmd)) {
 		spin_unlock_irqrestore(&(bdp->bd_lock), lock_flag);
 		rc = false;
 		goto exit;
@@ -2649,6 +2694,10 @@
 			yield();
 			spin_lock_bh(&(bdp->bd_non_tx_lock));
 		} else {
+#ifdef E100_CU_DEBUG			
+			printk(KERN_ERR "e100: %s: non-TX command (%x) "
+				"timeout\n", bdp->device->name, sub_cmd);
+#endif			
 			rc = false;
 			goto exit;
 		}
@@ -2694,7 +2743,12 @@
 	}
 
 	/* Mask off our interrupt line -- its unmasked after reset */
-	e100_dis_intr(bdp);
+	e100_disable_clear_intr(bdp);
+#ifdef E100_CU_DEBUG	
+	bdp->last_cmd = 0;
+	bdp->last_sub_cmd = 0;
+#endif	
+
 }
 
 /**
@@ -2894,19 +2948,6 @@
 void __devinit
 e100_print_brd_conf(struct e100_private *bdp)
 {
-	if (netif_carrier_ok(bdp->device)) {
-		printk(KERN_NOTICE
-		       "  Mem:0x%08lx  IRQ:%d  Speed:%d Mbps  Dx:%s\n",
-		       (unsigned long) bdp->device->mem_start,
-		       bdp->device->irq, bdp->cur_line_speed,
-		       (bdp->cur_dplx_mode == FULL_DUPLEX) ? "Full" : "Half");
-	} else {
-		printk(KERN_NOTICE
-		       "  Mem:0x%08lx  IRQ:%d  Speed:%d Mbps  Dx:%s\n",
-		       (unsigned long) bdp->device->mem_start,
-		       bdp->device->irq, 0, "N/A");
-	}
-
 	/* Print the string if checksum Offloading was enabled */
 	if (bdp->flags & DF_CSUM_OFFLOAD)
 		printk(KERN_NOTICE "  Hardware receive checksums enabled\n");
@@ -3005,8 +3046,13 @@
 void
 e100_isolate_driver(struct e100_private *bdp)
 {
-	if (netif_running(bdp->device)) {
-		e100_dis_intr(bdp);
+
+	/* Check if interface is up                              */
+	/* NOTE: Can't use netif_running(bdp->device) because    */
+	/* dev_close clears __LINK_STATE_START before calling    */
+	/* e100_close (aka dev->stop)                            */
+	if (bdp->device->flags & IFF_UP) {
+		e100_disable_clear_intr(bdp);
 		del_timer_sync(&bdp->watchdog_timer);
 		del_timer_sync(&bdp->hwi_timer);
 		/* If in middle of cable diag, */
@@ -3045,63 +3091,51 @@
 }
 
 /* 
- * Procedure:   e100_hw_reset_recover
+ * Procedure:   e100_configure_device
  *
- * Description: This routine will recover the hw after reset.
+ * Description: This routine will configure device
  *
  * Arguments:
  *      bdp - Ptr to this card's e100_bdconfig structure
- *        reset_cmd - s/w reset or selective reset. 
  *
  * Returns:
  *        true upon success
  *        false upon failure
  */
 unsigned char
-e100_hw_reset_recover(struct e100_private *bdp, u32 reset_cmd)
+e100_configure_device(struct e100_private *bdp)
 {
-	bdp->last_tcb = NULL;
-	if (reset_cmd == PORT_SOFTWARE_RESET) {
+	/*load CU & RU base */
+	if (!e100_wait_exec_cmplx(bdp, 0, SCB_CUC_LOAD_BASE, 0))
+		return false;
 
-		/*load CU & RU base */
-		if (!e100_wait_exec_cmplx(bdp, 0, SCB_CUC_LOAD_BASE)) {
-			return false;
-		}
+	if (e100_load_microcode(bdp))
+		bdp->flags |= DF_UCODE_LOADED;
 
-		if (e100_load_microcode(bdp)) {
-			bdp->flags |= DF_UCODE_LOADED;
-		}
+	if (!e100_wait_exec_cmplx(bdp, 0, SCB_RUC_LOAD_BASE, 0))
+		return false;
 
-		if (!e100_wait_exec_cmplx(bdp, 0, SCB_RUC_LOAD_BASE)) {
-			return false;
-		}
+	/* Issue the load dump counters address command */
+	if (!e100_wait_exec_cmplx(bdp, bdp->stat_cnt_phys, SCB_CUC_DUMP_ADDR, 0))
+		return false;
 
-		/* Issue the load dump counters address command */
-		if (!e100_wait_exec_cmplx(bdp, bdp->stat_cnt_phys,
-					  SCB_CUC_DUMP_ADDR)) {
-			return false;
-		}
+	if (!e100_setup_iaaddr(bdp, bdp->device->dev_addr)) {
+		printk(KERN_ERR "e100: e100_configure_device: "
+			"setup iaaddr failed\n");
+		return false;
+	}
 
-		if (!e100_setup_iaaddr(bdp, bdp->device->dev_addr)) {
-			printk(KERN_ERR
-			       "e100: e100_hw_reset_recover: "
-			       "setup iaaddr failed\n");
-			return false;
-		}
-
-		e100_set_multi_exec(bdp->device);
-
-		/* Change for 82558 enhancement */
-		/* If 82558/9 and if the user has enabled flow control, set up * the
-		 * Flow Control Reg. in the CSR */
-		if ((bdp->flags & IS_BACHELOR)
-		    && (bdp->params.b_params & PRM_FC)) {
-			writeb(DFLT_FC_THLD,
-			       &bdp->scb->scb_ext.d101_scb.scb_fc_thld);
-			writeb(DFLT_FC_CMD,
-			       &bdp->scb->scb_ext.d101_scb.scb_fc_xon_xoff);
-		}
+	e100_set_multi_exec(bdp->device);
 
+	/* Change for 82558 enhancement                                */
+	/* If 82558/9 and if the user has enabled flow control, set up */
+	/* flow Control Reg. in the CSR                                */
+	if ((bdp->flags & IS_BACHELOR)
+	    && (bdp->params.b_params & PRM_FC)) {
+		writeb(DFLT_FC_THLD,
+			&bdp->scb->scb_ext.d101_scb.scb_fc_thld);
+		writeb(DFLT_FC_CMD,
+			&bdp->scb->scb_ext.d101_scb.scb_fc_xon_xoff);
 	}
 
 	e100_force_config(bdp);
@@ -3115,7 +3149,7 @@
 	u32 cmd = full_reset ? PORT_SOFTWARE_RESET : PORT_SELECTIVE_RESET;
 	e100_sw_reset(bdp, cmd);
 	if (cmd == PORT_SOFTWARE_RESET) {
-		if (!e100_hw_reset_recover(bdp, cmd))
+		if (!e100_configure_device(bdp))
 			printk(KERN_ERR "e100: e100_deisolate_driver:" 
 		       		" device configuration failed\n");
 	} 
@@ -3253,10 +3287,8 @@
 e100_ethtool_set_settings(struct net_device *dev, struct ifreq *ifr)
 {
 	struct e100_private *bdp;
-	int current_duplex;
 	int e100_new_speed_duplex;
 	int ethtool_new_speed_duplex;
-	int speed_duplex_change_required;
 	struct ethtool_cmd ecmd;
 
 	if (!capable(CAP_NET_ADMIN)) {
@@ -3264,64 +3296,48 @@
 	}
 
 	bdp = dev->priv;
-	if (netif_running(dev)) {
-		return -EBUSY;
-	}
 	if (copy_from_user(&ecmd, ifr->ifr_data, sizeof (ecmd))) {
 		return -EFAULT;
 	}
-	current_duplex =
-		(bdp->cur_dplx_mode == HALF_DUPLEX) ? DUPLEX_HALF : DUPLEX_FULL;
-	speed_duplex_change_required = (ecmd.speed != bdp->cur_line_speed)
-		|| (ecmd.duplex != current_duplex);
-
-	if ((ecmd.autoneg == AUTONEG_ENABLE) && speed_duplex_change_required) {
-		return -EINVAL;
-	}
 
 	if ((ecmd.autoneg == AUTONEG_ENABLE)
 	    && (bdp->speed_duplex_caps & SUPPORTED_Autoneg)) {
 		bdp->params.e100_speed_duplex = E100_AUTONEG;
 		e100_set_speed_duplex(bdp);
 	} else {
-		if (speed_duplex_change_required) {
-			if (ecmd.speed == SPEED_10) {
-				if (ecmd.duplex == DUPLEX_HALF) {
-					e100_new_speed_duplex =
-						E100_SPEED_10_HALF;
-					ethtool_new_speed_duplex =
-						SUPPORTED_10baseT_Half;
-
-				} else {
-					e100_new_speed_duplex =
-						E100_SPEED_10_FULL;
-					ethtool_new_speed_duplex =
-						SUPPORTED_10baseT_Full;
-				}
-
-			} else {
-				if (ecmd.duplex == DUPLEX_HALF) {
-					e100_new_speed_duplex =
-						E100_SPEED_100_HALF;
-					ethtool_new_speed_duplex =
-						SUPPORTED_100baseT_Half;
-
-				} else {
-					e100_new_speed_duplex =
-						E100_SPEED_100_FULL;
-					ethtool_new_speed_duplex =
-						SUPPORTED_100baseT_Full;
-				}
-			}
-
-			if (bdp->speed_duplex_caps & ethtool_new_speed_duplex) {
-				bdp->params.e100_speed_duplex =
-					e100_new_speed_duplex;
-				e100_set_speed_duplex(bdp);
-			} else {
-				return -EOPNOTSUPP;
-			}
-		}
+		if (ecmd.speed == SPEED_10) {
+			if (ecmd.duplex == DUPLEX_HALF) {
+				e100_new_speed_duplex =
+					E100_SPEED_10_HALF;
+				ethtool_new_speed_duplex =
+					SUPPORTED_10baseT_Half;
+			} else { 
+				e100_new_speed_duplex =
+					E100_SPEED_10_FULL;
+				ethtool_new_speed_duplex =
+					SUPPORTED_10baseT_Full;
+			} 
+		} else { 
+			if (ecmd.duplex == DUPLEX_HALF) {
+				e100_new_speed_duplex =
+					E100_SPEED_100_HALF;
+				ethtool_new_speed_duplex =
+					SUPPORTED_100baseT_Half;
+			} else { 
+				e100_new_speed_duplex =
+					E100_SPEED_100_FULL;
+				ethtool_new_speed_duplex =
+					SUPPORTED_100baseT_Full;
+			} 
+		}
+
+		if (bdp->speed_duplex_caps & ethtool_new_speed_duplex) {
+			bdp->params.e100_speed_duplex =
+				e100_new_speed_duplex;
+			e100_set_speed_duplex(bdp);
+		} else {
+			return -EOPNOTSUPP;
+		} 
 	}
 
 	return 0;
@@ -3838,9 +3854,6 @@
 	case SIOCSMIIREG:
 		if (!capable(CAP_NET_ADMIN))
 			return -EPERM;
-		if (netif_running(dev)) {
-			return -EBUSY;
-		}
 		/* If reg = 0 && change speed/duplex */
 		if (data_ptr->reg_num == 0 && 
 			(data_ptr->val_in == (BMCR_ANENABLE | BMCR_ANRESTART) /* restart cmd */
@@ -3860,10 +3873,9 @@
 					bdp->params.e100_speed_duplex = E100_SPEED_10_HALF;
 				e100_set_speed_duplex(bdp);
 		}
-		else {
-			e100_mdi_write(bdp, data_ptr->reg_num, bdp->phy_addr,
-			       data_ptr->val_in);
-		}
+		else 
+			/* Only allows changing speed/duplex */
+			return -EINVAL;
 		
 		break;
 
@@ -3960,6 +3972,8 @@
 	struct e100_private *bdp = (struct e100_private *) ptr;
 	nxmit_cb_entry_t *active_command;
 	int restart = true;
+	cb_header_t *non_tx_cmd;
+	u8 sub_cmd;
 
 	spin_lock_bh(&(bdp->bd_non_tx_lock));
 
@@ -3987,6 +4001,15 @@
 		    && time_before(jiffies, active_command->expiration_time)) {
 			goto exit;
 		} else {
+			non_tx_cmd = (cb_header_t *) active_command->non_tx_cmd;
+			sub_cmd = CB_CMD_MASK & le16_to_cpu(non_tx_cmd->cb_cmd);
+#ifdef E100_CU_DEBUG			
+			if (!(non_tx_cmd->cb_status 
+			    & __constant_cpu_to_le16(CB_STATUS_COMPLETE)))
+				printk(KERN_ERR "e100: %s: Queued "
+					"command (%x) timeout\n", 
+					bdp->device->name, sub_cmd);
+#endif			
 			list_del(&(active_command->list_elem));
 			e100_free_non_tx_cmd(bdp, active_command);
 		}
@@ -4009,9 +4032,10 @@
 		bdp->non_tx_command_state = E100_WAIT_NON_TX_FINISH;
 		active_command = list_entry(bdp->non_tx_cmd_list.next,
 					    nxmit_cb_entry_t, list_elem);
+		sub_cmd = ((cb_header_t *) active_command->non_tx_cmd)->cb_cmd;
 		spin_lock_irq(&(bdp->bd_lock));
 		e100_wait_exec_cmplx(bdp, active_command->dma_addr,
-				     SCB_CUC_START);
+				     SCB_CUC_START, sub_cmd);
 		spin_unlock_irq(&(bdp->bd_lock));
 		active_command->expiration_time = jiffies + HZ;
 		cmd_type = CB_CMD_MASK &
@@ -4084,9 +4108,11 @@
 	e100_isolate_driver(bdp);
 	pci_save_state(pcid, bdp->pci_state);
 
+	/* Enable or disable WoL */
+	e100_do_wol(pcid, bdp);
+	
 	/* If wol is enabled */
 	if (bdp->wolopts) {
-		e100_do_wol(pcid, bdp);
 		pci_enable_wake(pcid, 3, 1);	/* Enable PME for power state D3 */
 		pci_set_power_state(pcid, 3);	/* Set power state to D3.        */
 	} else {
@@ -4102,17 +4128,13 @@
 {
 	struct net_device *netdev = pci_get_drvdata(pcid);
 	struct e100_private *bdp = netdev->priv;
-	u8 full_reset = false;
 
 	pci_set_power_state(pcid, 0);
 	pci_enable_wake(pcid, 0, 0);	/* Clear PME status and disable PME */
 	pci_restore_state(pcid, bdp->pci_state);
 
-	if (bdp->wolopts & (WAKE_UCAST | WAKE_ARP)) {
-		full_reset = true;
-	}
-
-	e100_deisolate_driver(bdp, full_reset);
+	/* Also do device full reset because device was in D3 state */
+	e100_deisolate_driver(bdp, true);
 
 	return 0;
 }
@@ -4239,3 +4261,20 @@
 	e100_mdi_write(bdp, MII_BMCR, bdp->phy_addr, control);
 	return;
 }
+
+#ifdef E100_CU_DEBUG
+unsigned char
+e100_cu_unknown_state(struct e100_private *bdp)
+{
+	u8 scb_cmd_low;
+	u16 scb_status;
+	scb_cmd_low = bdp->scb->scb_cmd_low;
+	scb_status = le16_to_cpu(bdp->scb->scb_status);
+	/* If CU is active and executing unknown cmd */
+	if (scb_status & SCB_CUS_ACTIVE && scb_cmd_low & SCB_CUC_UNKNOWN)
+		return true;
+	else
+		return false;
+}
+#endif
+
diff -urN linux-2.4.21-pre2/drivers/net/e100/e100_test.c linux-2.4.21-pre3/drivers/net/e100/e100_test.c
--- linux-2.4.21-pre2/drivers/net/e100/e100_test.c	Mon Jan  6 16:32:28 2003
+++ linux-2.4.21-pre3/drivers/net/e100/e100_test.c	Mon Jan  6 16:32:32 2003
@@ -29,7 +29,7 @@
 #include "e100_config.h"
 
 extern u16 e100_eeprom_read(struct e100_private *, u16);
-extern int e100_wait_exec_cmplx(struct e100_private *, u32,u8);
+extern int e100_wait_exec_cmplx(struct e100_private *, u32,u8, u8);
 extern void e100_phy_reset(struct e100_private *bdp);
 extern void e100_phy_autoneg(struct e100_private *bdp);
 extern void e100_phy_set_loopback(struct e100_private *bdp);
@@ -95,11 +95,9 @@
 		test_info [E100_EEPROM_TEST_FAIL] = true;
 	}
 
-	e100_deisolate_driver(bdp, false);
-
-	/*Let card recover from the test*/
 	set_current_state(TASK_UNINTERRUPTIBLE);
 	schedule_timeout(HZ * 2);
+    	e100_deisolate_driver(bdp, false);
 
 	return flags | (test_result ? 0 : ETH_TEST_FL_FAILED);
 }
@@ -128,7 +126,7 @@
 		}
 	}
 
-	e100_hw_reset_recover(bdp,PORT_SOFTWARE_RESET);
+	e100_configure_device(bdp);
 
 	return retval;
 }
@@ -166,13 +164,19 @@
 {
 	u8 rc = 0;
 
+	printk(KERN_DEBUG "%s: PHY loopback test starts\n", dev->name);
+	e100_sw_reset(dev->priv, PORT_SELECTIVE_RESET);
 	if (!e100_diag_one_loopback(dev, PHY_LOOPBACK)) {
 		rc |= PHY_LOOPBACK;
 	}
+	printk(KERN_DEBUG "%s: PHY loopback test ends\n", dev->name);
 
+	printk(KERN_DEBUG "%s: MAC loopback test starts\n", dev->name);
+	e100_sw_reset(dev->priv, PORT_SELECTIVE_RESET);
 	if (!e100_diag_one_loopback(dev, MAC_LOOPBACK)) {
 		rc |= MAC_LOOPBACK;
 	}
+	printk(KERN_DEBUG "%s: MAC loopback test ends\n", dev->name);
 
 	return rc;
 }
@@ -341,12 +345,12 @@
 e100_diag_loopback_cu_ru_exec(struct e100_private *bdp)
 {
 	/*load CU & RU base */ 
-	if (!e100_wait_exec_cmplx(bdp, 0, SCB_CUC_LOAD_BASE))
-		printk("e100: SCB_CUC_LOAD_BASE failed\n");
-	if(!e100_wait_exec_cmplx(bdp, 0, SCB_RUC_LOAD_BASE))
-		printk("e100: SCB_RUC_LOAD_BASE failed!\n");
-	if(!e100_wait_exec_cmplx(bdp, bdp->loopback.dma_handle, SCB_RUC_START))
-		printk("e100: SCB_RUC_START failed!\n");
+	if (!e100_wait_exec_cmplx(bdp, 0, SCB_CUC_LOAD_BASE, 0))
+		printk(KERN_ERR "e100: SCB_CUC_LOAD_BASE failed\n");
+	if(!e100_wait_exec_cmplx(bdp, 0, SCB_RUC_LOAD_BASE, 0))
+		printk(KERN_ERR "e100: SCB_RUC_LOAD_BASE failed!\n");
+	if(!e100_wait_exec_cmplx(bdp, bdp->loopback.dma_handle, SCB_RUC_START, 0))
+		printk(KERN_ERR "e100: SCB_RUC_START failed!\n");
 
 	bdp->next_cu_cmd = START_WAIT;
 	e100_start_cu(bdp, bdp->loopback.tcb);
diff -urN linux-2.4.21-pre2/drivers/net/e1000/e1000.h linux-2.4.21-pre3/drivers/net/e1000/e1000.h
--- linux-2.4.21-pre2/drivers/net/e1000/e1000.h	Thu Nov 28 15:53:13 2002
+++ linux-2.4.21-pre3/drivers/net/e1000/e1000.h	Mon Jan  6 16:32:32 2003
@@ -112,6 +112,7 @@
 #define E1000_DEFAULT_PBA    0x00000030
 
 #define AUTO_ALL_MODES       0
+#define E1000_EEPROM_APME    4
 
 /* only works for sizes that are powers of 2 */
 #define E1000_ROUNDUP(i, size) ((i) = (((i) + (size) - 1) & ~((size) - 1)))
diff -urN linux-2.4.21-pre2/drivers/net/e1000/e1000_ethtool.c linux-2.4.21-pre3/drivers/net/e1000/e1000_ethtool.c
--- linux-2.4.21-pre2/drivers/net/e1000/e1000_ethtool.c	Thu Nov 28 15:53:13 2002
+++ linux-2.4.21-pre3/drivers/net/e1000/e1000_ethtool.c	Mon Jan  6 16:32:32 2003
@@ -194,7 +194,7 @@
 	regs_buff[4]  = E1000_READ_REG(hw, RDH);
 	regs_buff[5]  = E1000_READ_REG(hw, RDT);
 	regs_buff[6]  = E1000_READ_REG(hw, RDTR);
-	
+
 	regs_buff[7]  = E1000_READ_REG(hw, TCTL);
 	regs_buff[8]  = E1000_READ_REG(hw, TDLEN);
 	regs_buff[9]  = E1000_READ_REG(hw, TDH);
@@ -233,7 +233,7 @@
 	return 0;
 }
 
-static int 
+static int
 e1000_ethtool_seeprom(struct e1000_adapter *adapter,
                       struct ethtool_eeprom *eeprom, void *user_data)
 {
@@ -306,12 +306,10 @@
 		/* Fall Through */
 
 	default:
-		wol->supported = WAKE_PHY | WAKE_UCAST | 
-				 WAKE_MCAST | WAKE_BCAST | WAKE_MAGIC;
-		
+		wol->supported = WAKE_UCAST | WAKE_MCAST
+			         | WAKE_BCAST | WAKE_MAGIC;
+
 		wol->wolopts = 0;
-		if(adapter->wol & E1000_WUFC_LNKC)
-			wol->wolopts |= WAKE_PHY;
 		if(adapter->wol & E1000_WUFC_EX)
 			wol->wolopts |= WAKE_UCAST;
 		if(adapter->wol & E1000_WUFC_MC)
@@ -343,13 +341,11 @@
 		/* Fall Through */
 
 	default:
-		if(wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE))
+		if(wol->wolopts & (WAKE_ARP | WAKE_MAGICSECURE | WAKE_PHY))
 			return -EOPNOTSUPP;
 
 		adapter->wol = 0;
 
-		if(wol->wolopts & WAKE_PHY)
-			adapter->wol |= E1000_WUFC_LNKC;
 		if(wol->wolopts & WAKE_UCAST)
 			adapter->wol |= E1000_WUFC_EX;
 		if(wol->wolopts & WAKE_MCAST)
@@ -374,7 +370,7 @@
 e1000_led_blink_callback(unsigned long data)
 {
 	struct e1000_adapter *adapter = (struct e1000_adapter *) data;
-	
+
 	if(test_and_change_bit(E1000_LED_ON, &adapter->led_status))
 		e1000_led_off(&adapter->hw);
 	else
@@ -394,13 +390,13 @@
 
 	e1000_setup_led(&adapter->hw);
 	mod_timer(&adapter->blink_timer, jiffies);
-	
+
 	set_current_state(TASK_INTERRUPTIBLE);
 	if(id->data)
 		schedule_timeout(id->data * HZ);
 	else
 		schedule_timeout(MAX_SCHEDULE_TIMEOUT);
-	
+
 	del_timer_sync(&adapter->blink_timer);
 	e1000_led_off(&adapter->hw);
 	clear_bit(E1000_LED_ON, &adapter->led_status);
diff -urN linux-2.4.21-pre2/drivers/net/e1000/e1000_main.c linux-2.4.21-pre3/drivers/net/e1000/e1000_main.c
--- linux-2.4.21-pre2/drivers/net/e1000/e1000_main.c	Thu Nov 28 15:53:13 2002
+++ linux-2.4.21-pre3/drivers/net/e1000/e1000_main.c	Mon Jan  6 16:32:32 2003
@@ -26,11 +26,21 @@
 
 *******************************************************************************/
 
-#define __E1000_MAIN__
 #include "e1000.h"
 
 /* Change Log
  *
+ * 4.4.19       11/27/02
+ *   o Feature: Added user-settable knob for interrupt throttle rate (ITR).
+ *   o Cleanup: removed large static array allocations.
+ *   o Cleanup: C99 struct initializer format.
+ *   o Bug fix: restore VLAN settings when interface is brought up.
+ *   o Bug fix: return cleanly in probe if error in detecting MAC type.
+ *   o Bug fix: Wake up on magic packet by default only if enabled in eeprom.
+ *   o Bug fix: Validate MAC address in set_mac.
+ *   o Bug fix: Throw away zero-length Tx skbs.
+ *   o Bug fix: Make ethtool EEPROM acceses work on older versions of ethtool.
+ * 
  * 4.4.12       10/15/02
  *   o Clean up: use members of pci_device rather than direct calls to
  *     pci_read_config_word.
@@ -43,27 +53,13 @@
  *   o Now setting netdev->mem_end in e1000_probe.
  *   o Clean up: Moved tx_timeout from interrupt context to process context
  *     using schedule_task.
- *
- * 4.3.15      8/9/02
- *   o Converted from Dual BSD/GPL license to GPL license.
- *   o Clean up: use pci_[clear|set]_mwi rather than direct calls to
- *     pci_write_config_word.
- *   o Bug fix: added read-behind-write calls to post writes before delays.
- *   o Bug fix: removed mdelay busy-waits in interrupt context.
- *   o Clean up: direct clear of descriptor bits rather than using memset.
- *   o Bug fix: added wmb() for ia-64 between descritor writes and advancing
- *     descriptor tail.
- *   o Feature: added locking mechanism for asf functionality.
- *   o Feature: exposed two Tx and one Rx interrupt delay knobs for finer
- *     control over interurpt rate tuning.
- *   o Misc ethtool bug fixes.
- *
- * 4.3.2       7/5/02
+ * 
+ * 4.3.15       8/9/02
  */
- 
+
 char e1000_driver_name[] = "e1000";
 char e1000_driver_string[] = "Intel(R) PRO/1000 Network Driver";
-char e1000_driver_version[] = "4.4.12-k1";
+char e1000_driver_version[] = "4.4.19-k1";
 char e1000_copyright[] = "Copyright (c) 1999-2002 Intel Corporation.";
 
 /* e1000_pci_tbl - PCI Device ID Table
@@ -168,6 +164,7 @@
 static void e1000_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp);
 static void e1000_vlan_rx_add_vid(struct net_device *netdev, uint16_t vid);
 static void e1000_vlan_rx_kill_vid(struct net_device *netdev, uint16_t vid);
+static void e1000_restore_vlan(struct e1000_adapter *adapter);
 
 static int e1000_notify_reboot(struct notifier_block *, unsigned long event, void *ptr);
 static int e1000_notify_netdev(struct notifier_block *, unsigned long event, void *ptr);
@@ -267,6 +264,7 @@
 	/* hardware has been reset, we need to reload some things */
 
 	e1000_set_multi(netdev);
+	e1000_restore_vlan(adapter);
 
 	e1000_configure_tx(adapter);
 	e1000_setup_rctl(adapter);
@@ -342,6 +340,7 @@
 	int mmio_len;
 	int pci_using_dac;
 	int i;
+	uint16_t eeprom_data;
 
 	if((i = pci_enable_device(pdev)))
 		return i;
@@ -464,7 +463,7 @@
 	adapter->phy_info_timer.function = &e1000_update_phy_info;
 	adapter->phy_info_timer.data = (unsigned long) adapter;
 
-	INIT_TQUEUE(&adapter->tx_timeout_task, 
+	INIT_TQUEUE(&adapter->tx_timeout_task,
 		(void (*)(void *))e1000_tx_timeout_task, netdev);
 
 	register_netdev(netdev);
@@ -485,8 +484,9 @@
 	 * enable the ACPI Magic Packet filter
 	 */
 
+	e1000_read_eeprom(&adapter->hw, EEPROM_INIT_CONTROL2_REG, &eeprom_data);
 	if((adapter->hw.mac_type >= e1000_82544) &&
-	   (E1000_READ_REG(&adapter->hw, WUC) & E1000_WUC_APME))
+	   (eeprom_data & E1000_EEPROM_APME))
 		adapter->wol |= E1000_WUFC_MAG;
 
 	/* reset the hardware with the new settings */
@@ -567,6 +567,7 @@
 	hw->subsystem_id = pdev->subsystem_device;
 
 	pci_read_config_byte(pdev, PCI_REVISION_ID, &hw->revision_id);
+
 	pci_read_config_word(pdev, PCI_COMMAND, &hw->pci_cmd_word);
 
 	adapter->rx_buffer_len = E1000_RXBUFFER_2048;
@@ -611,7 +612,7 @@
 	hw->adaptive_ifs = TRUE;
 
 	/* Copper options */
-	
+
 	if(hw->media_type == e1000_media_type_copper) {
 		hw->mdix = AUTO_ALL_MODES;
 		hw->disable_polarity_correction = FALSE;
@@ -1128,6 +1129,9 @@
 	struct e1000_adapter *adapter = netdev->priv;
 	struct sockaddr *addr = p;
 
+	if(!is_valid_ether_addr(addr->sa_data))
+		return -EADDRNOTAVAIL;
+
 	/* 82542 2.0 needs to be in reset to write receive address registers */
 
 	if(adapter->hw.mac_type == e1000_82542_rev2_0)
@@ -1331,7 +1335,6 @@
 
 	int f;
 	len = skb->len - skb->data_len;
-
 	i = (tx_ring->next_to_use + tx_ring->count - 1) % tx_ring->count;
 	count = 0;
 
@@ -1432,11 +1435,16 @@
 {
 	struct e1000_adapter *adapter = netdev->priv;
 	int tx_flags = 0, count;
-
 	int f;
 
 	count = TXD_USE_COUNT(skb->len - skb->data_len,
 	                      adapter->max_data_per_txd);
+
+	if(count == 0) {
+		dev_kfree_skb_any(skb);
+		return 0;
+	}
+
 	for(f = 0; f < skb_shinfo(skb)->nr_frags; f++)
 		count += TXD_USE_COUNT(skb_shinfo(skb)->frags[f].size,
 		                       adapter->max_data_per_txd);
@@ -2143,6 +2151,21 @@
 	e1000_write_vfta(&adapter->hw, index, vfta);
 }
 
+static void
+e1000_restore_vlan(struct e1000_adapter *adapter)
+{
+	e1000_vlan_rx_register(adapter->netdev, adapter->vlgrp);
+
+	if(adapter->vlgrp) {
+		uint16_t vid;
+		for(vid = 0; vid < VLAN_GROUP_ARRAY_LEN; vid++) {
+			if(!adapter->vlgrp->vlan_devices[vid])
+				continue;
+			e1000_vlan_rx_add_vid(adapter->netdev, vid);
+		}
+	}
+}
+
 static int
 e1000_notify_reboot(struct notifier_block *nb, unsigned long event, void *p)
 {
@@ -2188,14 +2211,19 @@
 {
 	struct net_device *netdev = pci_get_drvdata(pdev);
 	struct e1000_adapter *adapter = netdev->priv;
-	uint32_t ctrl, ctrl_ext, rctl, manc;
+	uint32_t ctrl, ctrl_ext, rctl, manc, status;
+	uint32_t wufc = adapter->wol;
 
 	netif_device_detach(netdev);
 
 	if(netif_running(netdev))
 		e1000_down(adapter);
 
-	if(adapter->wol) {
+	status = E1000_READ_REG(&adapter->hw, STATUS);
+	if(status & E1000_STATUS_LU)
+		wufc &= ~E1000_WUFC_LNKC;
+
+	if(wufc) {
 		e1000_setup_rctl(adapter);
 		e1000_set_multi(netdev);
 
@@ -2225,7 +2253,7 @@
 		}
 
 		E1000_WRITE_REG(&adapter->hw, WUC, E1000_WUC_PME_EN);
-		E1000_WRITE_REG(&adapter->hw, WUFC, adapter->wol);
+		E1000_WRITE_REG(&adapter->hw, WUFC, wufc);
 		pci_enable_wake(pdev, 3, 1);
 		pci_enable_wake(pdev, 4, 1); /* 4 == D3 cold */
 	} else {
diff -urN linux-2.4.21-pre2/drivers/net/e1000/e1000_osdep.h linux-2.4.21-pre3/drivers/net/e1000/e1000_osdep.h
--- linux-2.4.21-pre2/drivers/net/e1000/e1000_osdep.h	Thu Nov 28 15:53:13 2002
+++ linux-2.4.21-pre3/drivers/net/e1000/e1000_osdep.h	Mon Jan  6 16:32:32 2003
@@ -27,7 +27,7 @@
 *******************************************************************************/
 
 
-/* glue for the OS independant part of e1000 
+/* glue for the OS independant part of e1000
  * includes register access macros
  */
 
diff -urN linux-2.4.21-pre2/drivers/net/e1000/e1000_param.c linux-2.4.21-pre3/drivers/net/e1000/e1000_param.c
--- linux-2.4.21-pre2/drivers/net/e1000/e1000_param.c	Thu Nov 28 15:53:13 2002
+++ linux-2.4.21-pre3/drivers/net/e1000/e1000_param.c	Mon Jan  6 16:32:32 2003
@@ -230,7 +230,6 @@
 	} arg;
 };
 
-
 static int __devinit
 e1000_validate_option(int *value, struct e1000_option *opt)
 {
@@ -273,7 +272,7 @@
 	default:
 		BUG();
 	}
-		
+
 	printk(KERN_INFO "Invalid %s specified (%i) %s\n",
 	       opt->name, *value, opt->err);
 	*value = opt->def;
@@ -298,7 +297,7 @@
 {
 	int bd = adapter->bd_number;
 	if(bd >= E1000_MAX_NIC) {
-		printk(KERN_NOTICE 
+		printk(KERN_NOTICE
 		       "Warning: no configuration for board #%i\n", bd);
 		printk(KERN_NOTICE "Using defaults for all values\n");
 		bd = E1000_MAX_NIC;
@@ -310,11 +309,11 @@
 			.name = "Transmit Descriptors",
 			.err  = "using default of " __MODULE_STRING(DEFAULT_TXD),
 			.def  = DEFAULT_TXD,
-			.arg  = { r: { min: MIN_TXD }}
+			.arg  = { .r = { .min = MIN_TXD }}
 		};
 		struct e1000_desc_ring *tx_ring = &adapter->tx_ring;
 		e1000_mac_type mac_type = adapter->hw.mac_type;
-		opt.arg.r.max = mac_type < e1000_82544 ? 
+		opt.arg.r.max = mac_type < e1000_82544 ?
 			MAX_TXD : MAX_82544_TXD;
 
 		tx_ring->count = TxDescriptors[bd];
@@ -327,7 +326,7 @@
 			.name = "Receive Descriptors",
 			.err  = "using default of " __MODULE_STRING(DEFAULT_RXD),
 			.def  = DEFAULT_RXD,
-			.arg  = { r: { min: MIN_RXD }}
+			.arg  = { .r = { .min = MIN_RXD }}
 		};
 		struct e1000_desc_ring *rx_ring = &adapter->rx_ring;
 		e1000_mac_type mac_type = adapter->hw.mac_type;
@@ -344,13 +343,13 @@
 			.err  = "defaulting to Enabled",
 			.def  = OPTION_ENABLED
 		};
-		
+
 		int rx_csum = XsumRX[bd];
 		e1000_validate_option(&rx_csum, &opt);
 		adapter->rx_csum = rx_csum;
 	}
 	{ /* Flow Control */
-		
+
 		struct e1000_opt_list fc_list[] =
 			{{ e1000_fc_none,    "Flow Control Disabled" },
 			 { e1000_fc_rx_pause,"Flow Control Receive Only" },
@@ -363,7 +362,7 @@
 			.name = "Flow Control",
 			.err  = "reading default settings from EEPROM",
 			.def  = e1000_fc_default,
-			.arg  = { l: { nr: ARRAY_SIZE(fc_list), p: fc_list }}
+			.arg  = { .l = { .nr = ARRAY_SIZE(fc_list), .p = fc_list }}
 		};
 
 		int fc = FlowControl[bd];
@@ -375,7 +374,7 @@
 		struct e1000_option opt = {
 			.type = range_option,
 			.name = "Transmit Interrupt Delay",
-			.arg  = { r: { min: MIN_TXDELAY, max: MAX_TXDELAY }}
+			.arg  = { .r = { .min = MIN_TXDELAY, .max = MAX_TXDELAY }}
 		};
 		opt.def = DEFAULT_TIDV;
 		opt.err = tidv;
@@ -388,7 +387,7 @@
 		struct e1000_option opt = {
 			.type = range_option,
 			.name = "Transmit Absolute Interrupt Delay",
-			.arg  = { r: { min: MIN_TXABSDELAY, max: MAX_TXABSDELAY }}
+			.arg  = { .r = { .min = MIN_TXABSDELAY, .max = MAX_TXABSDELAY }}
 		};
 		opt.def = DEFAULT_TADV;
 		opt.err = tadv;
@@ -401,7 +400,7 @@
 		struct e1000_option opt = {
 			.type = range_option,
 			.name = "Receive Interrupt Delay",
-			.arg  = { r: { min: MIN_RXDELAY, max: MAX_RXDELAY }}
+			.arg  = { .r = { .min = MIN_RXDELAY, .max = MAX_RXDELAY }}
 		};
 		opt.def = DEFAULT_RDTR;
 		opt.err = rdtr;
@@ -414,7 +413,7 @@
 		struct e1000_option opt = {
 			.type = range_option,
 			.name = "Receive Absolute Interrupt Delay",
-			.arg  = { r: { min: MIN_RXABSDELAY, max: MAX_RXABSDELAY }}
+			.arg  = { .r = { .min = MIN_RXABSDELAY, .max = MAX_RXABSDELAY }}
 		};
 		opt.def = DEFAULT_RADV;
 		opt.err = radv;
@@ -478,15 +477,16 @@
 
 	{ /* Speed */
 		struct e1000_opt_list speed_list[] = {{          0, "" },
-		                                      {   SPEED_10, "" },
-		                                      {  SPEED_100, "" },
-		                                      { SPEED_1000, "" }};
+						      {   SPEED_10, "" },
+						      {  SPEED_100, "" },
+						      { SPEED_1000, "" }};
+
 		struct e1000_option opt = {
 			.type = list_option,
 			.name = "Speed",
 			.err  = "parameter ignored",
 			.def  = 0,
-			.arg  = { l: { nr: ARRAY_SIZE(speed_list), p: speed_list }}
+			.arg  = { .l = { .nr = ARRAY_SIZE(speed_list), .p = speed_list }}
 		};
 
 		speed = Speed[bd];
@@ -494,14 +494,15 @@
 	}
 	{ /* Duplex */
 		struct e1000_opt_list dplx_list[] = {{           0, "" },
-		                                     { HALF_DUPLEX, "" },
-		                                     { FULL_DUPLEX, "" }};
+						     { HALF_DUPLEX, "" },
+						     { FULL_DUPLEX, "" }};
+
 		struct e1000_option opt = {
 			.type = list_option,
 			.name = "Duplex",
 			.err  = "parameter ignored",
 			.def  = 0,
-			.arg  = { l: { nr: ARRAY_SIZE(dplx_list), p: dplx_list }}
+			.arg  = { .l = { .nr = ARRAY_SIZE(dplx_list), .p = dplx_list }}
 		};
 
 		dplx = Duplex[bd];
@@ -553,7 +554,7 @@
 			.name = "AutoNeg",
 			.err  = "parameter ignored",
 			.def  = AUTONEG_ADV_DEFAULT,
-			.arg  = { l: { nr: ARRAY_SIZE(an_list), p: an_list }}
+			.arg  = { .l = { .nr = ARRAY_SIZE(an_list), .p = an_list }}
 		};
 
 		int an = AutoNeg[bd];
@@ -572,7 +573,7 @@
 		printk(KERN_INFO "Half Duplex specified without Speed\n");
 		printk(KERN_INFO "Using Autonegotiation at Half Duplex only\n");
 		adapter->hw.autoneg = 1;
-		adapter->hw.autoneg_advertised = ADVERTISE_10_HALF | 
+		adapter->hw.autoneg_advertised = ADVERTISE_10_HALF |
 		                                 ADVERTISE_100_HALF;
 		break;
 	case FULL_DUPLEX:
diff -urN linux-2.4.21-pre2/drivers/net/e1000/e1000_proc.c linux-2.4.21-pre3/drivers/net/e1000/e1000_proc.c
--- linux-2.4.21-pre2/drivers/net/e1000/e1000_proc.c	Thu Nov 28 15:53:13 2002
+++ linux-2.4.21-pre3/drivers/net/e1000/e1000_proc.c	Mon Jan  6 16:32:32 2003
@@ -109,7 +109,7 @@
 		if(!strlen(elem->tag))
 			p += sprintf(p, "\n");
 		else
-			p += sprintf(p, "%-*.*s %.*s\n", 
+			p += sprintf(p, "%-*.*s %.*s\n",
 				TAG_MAX_LENGTH, TAG_MAX_LENGTH,
 				elem->tag, FIELD_MAX_LENGTH,
 				elem->func(elem->data, elem->len, buf));
@@ -126,7 +126,7 @@
 {
 	struct proc_list *elem = data;
 
-	sprintf(page, "%.*s", FIELD_MAX_LENGTH, elem->func(elem->data, 
+	sprintf(page, "%.*s", FIELD_MAX_LENGTH, elem->func(elem->data,
 	        elem->len, page));
 
 	return e1000_proc_read(page, start, off, count, eof);
@@ -216,7 +216,7 @@
 }
 
 static void
-e1000_proc_dirs_create(void *data, char *name, 
+e1000_proc_dirs_create(void *data, char *name,
                        struct list_head *proc_list_head)
 {
 	struct proc_dir_entry *intel_proc_dir, *proc_dir, *info_entry;
@@ -257,7 +257,7 @@
 
 static void
 e1000_proc_list_add(struct list_head *proc_list_head, char *tag,
-                    void *data, size_t len, 
+                    void *data, size_t len,
 		    char *(*func)(void *, size_t, char *))
 {
 	struct proc_list *new = (struct proc_list *)
@@ -509,8 +509,8 @@
 {
 	struct e1000_adapter *adapter = data;
 	e1000_auto_x_mode mdix_mode = adapter->phy_info.mdix_mode;
-	sprintf(buf, 
-		mdix_mode == e1000_auto_x_mode_manual_mdi  ? "MDI"   : 
+	sprintf(buf,
+		mdix_mode == e1000_auto_x_mode_manual_mdi  ? "MDI"   :
 		mdix_mode == e1000_auto_x_mode_manual_mdix ? "MDI-X" :
 		"Unknown");
 	return buf;
@@ -531,7 +531,7 @@
  * e1000_proc_list_setup - build link list of proc praramters
  * @adapter: board private structure
  *
- * Order matters - ethx.info entries are ordered in the order links 
+ * Order matters - ethx.info entries are ordered in the order links
  * are added to list.
  */
 
@@ -610,7 +610,7 @@
 	LIST_ADD_U("Tx_Aborted_Errors", &adapter->net_stats.tx_aborted_errors);
 	LIST_ADD_U("Tx_Carrier_Errors", &adapter->net_stats.tx_carrier_errors);
 	LIST_ADD_U("Tx_FIFO_Errors", &adapter->net_stats.tx_fifo_errors);
-	LIST_ADD_U("Tx_Heartbeat_Errors", 
+	LIST_ADD_U("Tx_Heartbeat_Errors",
 	           &adapter->net_stats.tx_heartbeat_errors);
 	LIST_ADD_U("Tx_Window_Errors", &adapter->net_stats.tx_window_errors);
 
@@ -646,17 +646,17 @@
 		           adapter, e1000_proc_cable_polarity);
 		LIST_ADD_F("PHY_Disable_Polarity_Correction",
 		           adapter, e1000_proc_polarity_correction);
-		LIST_ADD_U("PHY_Idle_Errors", 
+		LIST_ADD_U("PHY_Idle_Errors",
 		           &adapter->phy_stats.idle_errors);
 		LIST_ADD_U("PHY_Receive_Errors",
 		           &adapter->phy_stats.receive_errors);
 		LIST_ADD_F("PHY_MDI_X_Enabled",
 		           adapter, e1000_proc_mdi_x_enabled);
 		LIST_ADD_F("PHY_Local_Receiver_Status",
-		           &adapter->phy_info.local_rx, 
+		           &adapter->phy_info.local_rx,
 			   e1000_proc_rx_status);
 		LIST_ADD_F("PHY_Remote_Receiver_Status",
-		           &adapter->phy_info.remote_rx, 
+		           &adapter->phy_info.remote_rx,
 			   e1000_proc_rx_status);
 	}
 
@@ -672,7 +672,7 @@
 {
 	e1000_proc_list_setup(adapter);
 
-	e1000_proc_dirs_create(adapter, 
+	e1000_proc_dirs_create(adapter,
 	                       adapter->ifname,
 	                       &adapter->proc_list_head);
 }
diff -urN linux-2.4.21-pre2/drivers/net/eepro100.c linux-2.4.21-pre3/drivers/net/eepro100.c
--- linux-2.4.21-pre2/drivers/net/eepro100.c	Mon Jan  6 16:32:28 2003
+++ linux-2.4.21-pre3/drivers/net/eepro100.c	Mon Jan  6 16:32:32 2003
@@ -41,8 +41,8 @@
 static int txdmacount = 128;
 static int rxdmacount /* = 0 */;
 
-#if defined(__ia64__) || defined(__alpha__) || defined(__sparc__) || defined(__mips__) || \
-	defined(__arm__)
+#if defined(__ia64__) || defined(__alpha__) || defined(__sparc__) || \
+	defined(__mips__) || defined(__arm__) || defined(__hppa__)
   /* align rx buffers to 2 bytes so that IP header is aligned */
 # define rx_align(skb)		skb_reserve((skb), 2)
 # define RxFD_ALIGNMENT		__attribute__ ((aligned (2), packed))
@@ -2387,6 +2387,7 @@
 	{ PCI_VENDOR_ID_INTEL, 0x103C, PCI_ANY_ID, PCI_ANY_ID, },
 	{ PCI_VENDOR_ID_INTEL, 0x103D, PCI_ANY_ID, PCI_ANY_ID, },
 	{ PCI_VENDOR_ID_INTEL, 0x103E, PCI_ANY_ID, PCI_ANY_ID, },
+	{ PCI_VENDOR_ID_INTEL, 0x1059, PCI_ANY_ID, PCI_ANY_ID, },
 	{ PCI_VENDOR_ID_INTEL, 0x1227, PCI_ANY_ID, PCI_ANY_ID, },
 	{ PCI_VENDOR_ID_INTEL, 0x1228, PCI_ANY_ID, PCI_ANY_ID, },
 	{ PCI_VENDOR_ID_INTEL, 0x2449, PCI_ANY_ID, PCI_ANY_ID, },
diff -urN linux-2.4.21-pre2/drivers/net/irda/Config.in linux-2.4.21-pre3/drivers/net/irda/Config.in
--- linux-2.4.21-pre2/drivers/net/irda/Config.in	Thu Nov 28 15:53:13 2002
+++ linux-2.4.21-pre3/drivers/net/irda/Config.in	Mon Jan  6 16:32:32 2003
@@ -28,6 +28,7 @@
 dep_tristate 'IrDA USB dongles (Experimental)' CONFIG_USB_IRDA $CONFIG_IRDA $CONFIG_USB $CONFIG_EXPERIMENTAL
 dep_tristate 'NSC PC87108/PC87338' CONFIG_NSC_FIR  $CONFIG_IRDA
 dep_tristate 'Winbond W83977AF (IR)' CONFIG_WINBOND_FIR $CONFIG_IRDA
+dep_tristate 'Toshiba Type-O IR Port (old driver)' CONFIG_TOSHIBA_OLD $CONFIG_IRDA
 dep_tristate 'Toshiba Type-O IR Port' CONFIG_TOSHIBA_FIR $CONFIG_IRDA
 if [ "$CONFIG_MIPS" = "y" ]; then
    dep_tristate 'Alchemy Au1000 SIR/FIR' CONFIG_AU1000_FIR $CONFIG_IRDA
diff -urN linux-2.4.21-pre2/drivers/net/irda/Makefile linux-2.4.21-pre3/drivers/net/irda/Makefile
--- linux-2.4.21-pre2/drivers/net/irda/Makefile	Thu Nov 28 15:53:13 2002
+++ linux-2.4.21-pre3/drivers/net/irda/Makefile	Mon Jan  6 16:32:32 2003
@@ -17,7 +17,8 @@
 obj-$(CONFIG_NSC_FIR)		+= nsc-ircc.o
 obj-$(CONFIG_WINBOND_FIR)	+= w83977af_ir.o
 obj-$(CONFIG_SA1100_FIR)	+= sa1100_ir.o
-obj-$(CONFIG_TOSHIBA_FIR)	+= toshoboe.o
+obj-$(CONFIG_TOSHIBA_OLD)	+= toshoboe.o
+obj-$(CONFIG_TOSHIBA_FIR)	+= donauboe.o
 obj-$(CONFIG_SMC_IRCC_FIR)	+= smc-ircc.o	irport.o
 obj-$(CONFIG_ALI_FIR)		+= ali-ircc.o
 obj-$(CONFIG_VLSI_FIR)		+= vlsi_ir.o
diff -urN linux-2.4.21-pre2/drivers/net/irda/donauboe.c linux-2.4.21-pre3/drivers/net/irda/donauboe.c
--- linux-2.4.21-pre2/drivers/net/irda/donauboe.c	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre3/drivers/net/irda/donauboe.c	Mon Jan  6 16:32:32 2003
@@ -0,0 +1,1850 @@
+/*****************************************************************
+ *
+ * Filename:		donauboe.c
+ * Version: 		2.17
+ * Description:   Driver for the Toshiba OBOE (or type-O or 701)
+ *                FIR Chipset, also supports the DONAUOBOE (type-DO
+ *                or d01) FIR chipset which as far as I know is
+ *                register compatible.
+ * Status:        Experimental.
+ * Author:        James McKenzie <james@fishsoup.dhs.org>
+ * Created at:    Sat May 8  12:35:27 1999
+ * Modified:      Paul Bristow <paul.bristow@technologist.com>
+ * Modified:      Mon Nov 11 19:10:05 1999
+ * Modified:      James McKenzie <james@fishsoup.dhs.org>
+ * Modified:      Thu Mar 16 12:49:00 2000 (Substantial rewrite)
+ * Modified:      Sat Apr 29 00:23:03 2000 (Added DONAUOBOE support)
+ * Modified:      Wed May 24 23:45:02 2000 (Fixed chipio_t structure)
+ * Modified: 2.13 Christian Gennerat <christian.gennerat@polytechnique.org>
+ * Modified: 2.13 dim jan 07 21:57:39 2001 (tested with kernel 2.4 & irnet/ppp)
+ * Modified: 2.14 Christian Gennerat <christian.gennerat@polytechnique.org>
+ * Modified: 2.14 lun fev 05 17:55:59 2001 (adapted to patch-2.4.1-pre8-irda1)
+ * Modified: 2.15 Martin Lucina <mato@kotelna.sk>
+ * Modified: 2.15 Fri Jun 21 20:40:59 2002 (sync with 2.4.18, substantial fixes)
+ * Modified: 2.16 Martin Lucina <mato@kotelna.sk>
+ * Modified: 2.16 Sat Jun 22 18:54:29 2002 (fix freeregion, default to verbose)
+ * Modified: 2.17 Christian Gennerat <christian.gennerat@polytechnique.org>
+ * Modified: 2.17 jeu sep 12 08:50:20 2002 (save_flags();cli(); replaced by spinlocks)
+ *
+ *     Copyright (c) 1999 James McKenzie, All Rights Reserved.
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License as
+ *     published by the Free Software Foundation; either version 2 of
+ *     the License, or (at your option) any later version.
+ *
+ *     Neither James McKenzie nor Cambridge University admit liability nor
+ *     provide warranty for any of this software. This material is
+ *     provided "AS-IS" and at no charge.
+ *
+ *     Applicable Models : Libretto 100/110CT and many more.
+ *     Toshiba refers to this chip as the type-O IR port,
+ *     or the type-DO IR port.
+ *
+ ********************************************************************/
+
+/* Look at toshoboe.h (currently in include/net/irda) for details of */
+/* Where to get documentation on the chip         */
+
+
+static char *rcsid =
+  "$Id: donauboe.c V2.17 jeu sep 12 08:50:20 2002 $";
+
+/* See below for a description of the logic in this driver */
+
+/* Is irda_crc16_table[] exported? not yet */
+/* define this if you get errors about multiple defns of irda_crc16_table */
+#undef CRC_EXPORTED
+
+/* User servicable parts */
+/* Enable the code which probes the chip and does a few tests */
+/* Probe code is very useful for understanding how the hardware works */
+/* Use it with various combinations of TT_LEN, RX_LEN */
+/* Strongly recomended, disable if the probe fails on your machine */
+/* and send me <james@fishsoup.dhs.org> the output of dmesg */
+#define DO_PROBE 1
+
+/* Trace Transmit ring, interrupts, Receive ring or not ? */
+#define PROBE_VERBOSE 1
+
+/* Debug option, examine sent and received raw data */
+/* Irdadump is better, but does not see all packets. enable it if you want. */
+#undef DUMP_PACKETS
+
+/* MIR mode has not been tested. Some behaviour is different */
+/* Seems to work against an Ericsson R520 for me. -Martin */
+#define USE_MIR
+
+/* Schedule back to back hardware transmits wherever possible, otherwise */
+/* we need an interrupt for every frame, unset if oboe works for a bit and */
+/* then hangs */
+#define OPTIMIZE_TX
+
+/* Set the number of slots in the rings */
+/* If you get rx/tx fifo overflows at high bitrates, you can try increasing */
+/* these */
+
+#define RING_SIZE (OBOE_RING_SIZE_RX8 | OBOE_RING_SIZE_TX8)
+#define TX_SLOTS    8
+#define RX_SLOTS    8
+
+
+/* Less user servicable parts below here */
+
+/* Test, Transmit and receive buffer sizes, adjust at your peril */
+/* remarks: nfs usually needs 1k blocks */
+/* remarks: in SIR mode, CRC is received, -> RX_LEN=TX_LEN+2 */
+/* remarks: test accepts large blocks. Standard is 0x80 */
+/* When TT_LEN > RX_LEN (SIR mode) data is stored in successive slots. */
+/* When 3 or more slots are needed for each test packet, */
+/* data received in the first slots is overwritten, even */
+/* if OBOE_CTL_RX_HW_OWNS is not set, without any error! */
+#define TT_LEN      0x80
+#define TX_LEN      0xc00
+#define RX_LEN      0xc04
+/* Real transmitted length (SIR mode) is about 14+(2%*TX_LEN) more */
+/* long than user-defined length (see async_wrap_skb) and is less then 4K */
+/* Real received length is (max RX_LEN) differs from user-defined */
+/* length only b the CRC (2 or 4 bytes) */
+#define BUF_SAFETY  0x7a
+#define RX_BUF_SZ   (RX_LEN)
+#define TX_BUF_SZ   (TX_LEN+BUF_SAFETY)
+
+
+/* Logic of the netdev part of this driver                             */
+
+/* The RX ring is filled with buffers, when a packet arrives           */
+/* it is DMA'd into the buffer which is marked used and RxDone called  */
+/* RxDone forms an skb (and checks the CRC if in SIR mode) and ships   */
+/* the packet off upstairs */
+
+/* The transmitter on the oboe chip can work in one of two modes       */
+/* for each ring->tx[] the transmitter can either                      */
+/* a) transmit the packet, leave the trasmitter enabled and proceed to */
+/*    the next ring                                                    */
+/* OR                                                                  */
+/* b) transmit the packet, switch off the transmitter and issue TxDone */
+
+/* All packets are entered into the ring in mode b), if the ring was   */
+/* empty the transmitter is started.                                   */
+
+/* If OPTIMIZE_TX is defined then in TxDone if the ring contains       */
+/* more than one packet, all but the last are set to mode a) [HOWEVER  */
+/* the hardware may not notice this, this is why we start in mode b) ] */
+/* then restart the transmitter                                        */
+
+/* If OPTIMIZE_TX is not defined then we just restart the transmitter  */
+/* if the ring isn't empty */
+
+/* Speed changes are delayed until the TxRing is empty                 */
+/* mtt is handled by generating packets with bad CRCs, before the data */
+
+/* TODO: */
+/* check the mtt works ok      */
+/* finish the watchdog         */
+
+/* No user servicable parts below here */
+
+#define STATIC static
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/rtnetlink.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include <net/irda/wrapper.h>
+#include <net/irda/irda.h>
+//#include <net/irda/irmod.h>
+//#include <net/irda/irlap_frame.h>
+#include <net/irda/irda_device.h>
+#include <net/irda/crc.h>
+
+#include "donauboe.h"
+
+#define INB(port)       inb_p(port)
+#define OUTB(val,port)  outb_p(val,port)
+#define OUTBP(val,port) outb_p(val,port)
+
+#define PROMPT  OUTB(OBOE_PROMPT_BIT,OBOE_PROMPT);
+
+#if PROBE_VERBOSE
+#define PROBE_DEBUG(args...) (printk (args))
+#else
+#define PROBE_DEBUG(args...) ;
+#endif
+
+/* Set the DMA to be byte at a time */
+#define CONFIG0H_DMA_OFF OBOE_CONFIG0H_RCVANY
+#define CONFIG0H_DMA_ON_NORX CONFIG0H_DMA_OFF| OBOE_CONFIG0H_ENDMAC
+#define CONFIG0H_DMA_ON CONFIG0H_DMA_ON_NORX | OBOE_CONFIG0H_ENRX
+
+static struct pci_device_id toshoboe_pci_tbl[] __initdata = {
+	{ PCI_VENDOR_ID_TOSHIBA, PCI_DEVICE_ID_FIR701, PCI_ANY_ID, PCI_ANY_ID, },
+	{ PCI_VENDOR_ID_TOSHIBA, PCI_DEVICE_ID_FIRD01, PCI_ANY_ID, PCI_ANY_ID, },
+	{ }			/* Terminating entry */
+};
+MODULE_DEVICE_TABLE(pci, toshoboe_pci_tbl);
+
+#define DRIVER_NAME "toshoboe"
+static char *driver_name = DRIVER_NAME;
+
+static int max_baud = 4000000;
+static int do_probe = DO_PROBE;
+
+
+/**********************************************************************/
+/* Fcs code */
+
+#ifdef CRC_EXPORTED
+extern __u16 const irda_crc16_table[];
+#else
+static __u16 const irda_crc16_table[256] = {
+  0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+  0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+  0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+  0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+  0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+  0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+  0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+  0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+  0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+  0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+  0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+  0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+  0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+  0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+  0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+  0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+  0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+  0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+  0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+  0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+  0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+  0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+  0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+  0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+  0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+  0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+  0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+  0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+  0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+  0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+  0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+  0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+#endif
+
+STATIC int
+toshoboe_checkfcs (unsigned char *buf, int len)
+{
+  int i;
+  union
+  {
+    __u16 value;
+    __u8 bytes[2];
+  }
+  fcs;
+
+  fcs.value = INIT_FCS;
+
+  for (i = 0; i < len; ++i)
+    fcs.value = irda_fcs (fcs.value, *(buf++));
+
+  return (fcs.value == GOOD_FCS);
+}
+
+/***********************************************************************/
+/* Generic chip handling code */
+#ifdef DUMP_PACKETS
+static unsigned char dump[50];
+STATIC void 
+_dumpbufs (unsigned char *data, int len, char tete)
+{
+int i,j;
+char head=tete;
+for (i=0;i<len;i+=16) {
+    for (j=0;j<16 && i+j<len;j++) { sprintf(&dump[3*j],"%02x.",data[i+j]); }
+    dump [3*j]=0;
+    IRDA_DEBUG (2, "%c%s\n",head , dump);
+    head='+'; 
+    }
+}
+#endif
+
+/* Dump the registers */
+STATIC void
+toshoboe_dumpregs (struct toshoboe_cb *self)
+{
+  __u32 ringbase;
+
+  IRDA_DEBUG (4, "%s()\n", __FUNCTION__);
+
+  ringbase = INB (OBOE_RING_BASE0) << 10;
+  ringbase |= INB (OBOE_RING_BASE1) << 18;
+  ringbase |= INB (OBOE_RING_BASE2) << 26;
+
+  printk (KERN_ERR DRIVER_NAME ": Register dump:\n");
+  printk (KERN_ERR "Interrupts: Tx:%d Rx:%d TxUnder:%d RxOver:%d Sip:%d\n",
+          self->int_tx, self->int_rx, self->int_txunder, self->int_rxover,
+          self->int_sip);
+  printk (KERN_ERR "RX %02x TX %02x RingBase %08x\n",
+          INB (OBOE_RXSLOT), INB (OBOE_TXSLOT), ringbase);
+  printk (KERN_ERR "RING_SIZE %02x IER %02x ISR %02x\n",
+          INB (OBOE_RING_SIZE), INB (OBOE_IER), INB (OBOE_ISR));
+  printk (KERN_ERR "CONFIG1 %02x STATUS %02x\n",
+          INB (OBOE_CONFIG1), INB (OBOE_STATUS));
+  printk (KERN_ERR "CONFIG0 %02x%02x ENABLE %02x%02x\n",
+          INB (OBOE_CONFIG0H), INB (OBOE_CONFIG0L),
+          INB (OBOE_ENABLEH), INB (OBOE_ENABLEL));
+  printk (KERN_ERR "NEW_PCONFIG %02x%02x CURR_PCONFIG %02x%02x\n",
+          INB (OBOE_NEW_PCONFIGH), INB (OBOE_NEW_PCONFIGL),
+          INB (OBOE_CURR_PCONFIGH), INB (OBOE_CURR_PCONFIGL));
+  printk (KERN_ERR "MAXLEN %02x%02x RXCOUNT %02x%02x\n",
+          INB (OBOE_MAXLENH), INB (OBOE_MAXLENL),
+          INB (OBOE_RXCOUNTL), INB (OBOE_RXCOUNTH));
+
+  if (self->ring)
+    {
+      int i;
+      ringbase = virt_to_bus (self->ring);
+      printk (KERN_ERR "Ring at %08x:\n", ringbase);
+      printk (KERN_ERR "RX:");
+      for (i = 0; i < RX_SLOTS; ++i)
+        printk (" (%d,%02x)",self->ring->rx[i].len,self->ring->rx[i].control);
+      printk ("\n");
+      printk (KERN_ERR "TX:");
+      for (i = 0; i < RX_SLOTS; ++i)
+        printk (" (%d,%02x)",self->ring->tx[i].len,self->ring->tx[i].control);
+      printk ("\n");
+    }
+}
+
+/*Don't let the chip look at memory */
+STATIC void
+toshoboe_disablebm (struct toshoboe_cb *self)
+{
+  __u8 command;
+  IRDA_DEBUG (4, "%s()\n", __FUNCTION__);
+
+  pci_read_config_byte (self->pdev, PCI_COMMAND, &command);
+  command &= ~PCI_COMMAND_MASTER;
+  pci_write_config_byte (self->pdev, PCI_COMMAND, command);
+
+}
+
+/* Shutdown the chip and point the taskfile reg somewhere else */
+STATIC void
+toshoboe_stopchip (struct toshoboe_cb *self)
+{
+  IRDA_DEBUG (4, "%s()\n", __FUNCTION__);
+
+  /*Disable interrupts */
+  OUTB (0x0, OBOE_IER);
+  /*Disable DMA, Disable Rx, Disable Tx */
+  OUTB (CONFIG0H_DMA_OFF, OBOE_CONFIG0H);
+  /*Disable SIR MIR FIR, Tx and Rx */
+  OUTB (0x00, OBOE_ENABLEH);
+  /*Point the ring somewhere safe */
+  OUTB (0x3f, OBOE_RING_BASE2);
+  OUTB (0xff, OBOE_RING_BASE1);
+  OUTB (0xff, OBOE_RING_BASE0);
+
+  OUTB (RX_LEN >> 8, OBOE_MAXLENH);
+  OUTB (RX_LEN & 0xff, OBOE_MAXLENL);
+
+  /*Acknoledge any pending interrupts */
+  OUTB (0xff, OBOE_ISR);
+
+  /*Why */
+  OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH);
+
+  /*switch it off */
+  OUTB (OBOE_CONFIG1_OFF, OBOE_CONFIG1);
+
+  toshoboe_disablebm (self);
+}
+
+/* Transmitter initialization */
+STATIC void
+toshoboe_start_DMA (struct toshoboe_cb *self, int opts)
+{
+  OUTB (0x0, OBOE_ENABLEH);
+  OUTB (CONFIG0H_DMA_ON | opts,  OBOE_CONFIG0H);
+  OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH);
+  PROMPT;
+}
+
+/*Set the baud rate */
+STATIC void
+toshoboe_setbaud (struct toshoboe_cb *self)
+{
+  __u16 pconfig = 0;
+  __u8 config0l = 0;
+
+  IRDA_DEBUG (2, "%s(%d/%d)\n", __FUNCTION__, self->speed, self->io.speed);
+
+  switch (self->speed)
+    {
+    case 2400:
+    case 4800:
+    case 9600:
+    case 19200:
+    case 38400:
+    case 57600:
+    case 115200:
+#ifdef USE_MIR
+    case 1152000:
+#endif
+    case 4000000:
+      break;
+    default:
+
+      printk (KERN_ERR DRIVER_NAME ": switch to unsupported baudrate %d\n",
+              self->speed);
+      return;
+    }
+
+  switch (self->speed)
+    {
+      /* For SIR the preamble is done by adding XBOFs */
+      /* to the packet */
+      /* set to filtered SIR mode, filter looks for BOF and EOF */
+    case 2400:
+      pconfig |= 47 << OBOE_PCONFIG_BAUDSHIFT;
+      pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT;
+      break;
+    case 4800:
+      pconfig |= 23 << OBOE_PCONFIG_BAUDSHIFT;
+      pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT;
+      break;
+    case 9600:
+      pconfig |= 11 << OBOE_PCONFIG_BAUDSHIFT;
+      pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT;
+      break;
+    case 19200:
+      pconfig |= 5 << OBOE_PCONFIG_BAUDSHIFT;
+      pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT;
+      break;
+    case 38400:
+      pconfig |= 2 << OBOE_PCONFIG_BAUDSHIFT;
+      pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT;
+      break;
+    case 57600:
+      pconfig |= 1 << OBOE_PCONFIG_BAUDSHIFT;
+      pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT;
+      break;
+    case 115200:
+      pconfig |= 0 << OBOE_PCONFIG_BAUDSHIFT;
+      pconfig |= 25 << OBOE_PCONFIG_WIDTHSHIFT;
+      break;
+    default:
+      /*Set to packet based reception */
+      OUTB (RX_LEN >> 8, OBOE_MAXLENH);
+      OUTB (RX_LEN & 0xff, OBOE_MAXLENL);
+      break;
+    }
+
+  switch (self->speed)
+    {
+    case 2400:
+    case 4800:
+    case 9600:
+    case 19200:
+    case 38400:
+    case 57600:
+    case 115200:
+      config0l = OBOE_CONFIG0L_ENSIR;
+      if (self->async)
+        {
+          /*Set to character based reception */
+          /*System will lock if MAXLEN=0 */
+          /*so have to be careful */
+          OUTB (0x01, OBOE_MAXLENH);
+          OUTB (0x01, OBOE_MAXLENL);
+          OUTB (0x00, OBOE_MAXLENH);
+        }
+      else
+        {
+          /*Set to packet based reception */
+          config0l |= OBOE_CONFIG0L_ENSIRF;
+          OUTB (RX_LEN >> 8, OBOE_MAXLENH);
+          OUTB (RX_LEN & 0xff, OBOE_MAXLENL);
+        }
+      break;
+
+#ifdef USE_MIR
+      /* MIR mode */
+      /* Set for 16 bit CRC and enable MIR */
+      /* Preamble now handled by the chip */
+    case 1152000:
+      pconfig |= 0 << OBOE_PCONFIG_BAUDSHIFT;
+      pconfig |= 8 << OBOE_PCONFIG_WIDTHSHIFT;
+      pconfig |= 1 << OBOE_PCONFIG_PREAMBLESHIFT;
+      config0l = OBOE_CONFIG0L_CRC16 | OBOE_CONFIG0L_ENMIR;
+      break;
+#endif
+      /* FIR mode */
+      /* Set for 32 bit CRC and enable FIR */
+      /* Preamble handled by the chip */
+    case 4000000:
+      pconfig |= 0 << OBOE_PCONFIG_BAUDSHIFT;
+      /* Documentation says 14, but toshiba use 15 in their drivers */
+      pconfig |= 15 << OBOE_PCONFIG_PREAMBLESHIFT;
+      config0l = OBOE_CONFIG0L_ENFIR;
+      break;
+    }
+
+  /* Copy into new PHY config buffer */
+  OUTBP (pconfig >> 8, OBOE_NEW_PCONFIGH);
+  OUTB (pconfig & 0xff, OBOE_NEW_PCONFIGL);
+  OUTB (config0l, OBOE_CONFIG0L);
+
+  /* Now make OBOE copy from new PHY to current PHY */
+  OUTB (0x0, OBOE_ENABLEH);
+  OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH);
+  PROMPT;
+
+  /* speed change executed */
+  self->new_speed = 0;
+  self->io.speed = self->speed;
+}
+
+/*Let the chip look at memory */
+STATIC void
+toshoboe_enablebm (struct toshoboe_cb *self)
+{
+  IRDA_DEBUG (4, "%s()\n", __FUNCTION__);
+  pci_set_master (self->pdev);
+}
+
+/*setup the ring */
+STATIC void
+toshoboe_initring (struct toshoboe_cb *self)
+{
+  int i;
+
+  IRDA_DEBUG (4, "%s()\n", __FUNCTION__);
+
+  for (i = 0; i < TX_SLOTS; ++i)
+    {
+      self->ring->tx[i].len = 0;
+      self->ring->tx[i].control = 0x00;
+      self->ring->tx[i].address = virt_to_bus (self->tx_bufs[i]);
+    }
+
+  for (i = 0; i < RX_SLOTS; ++i)
+    {
+      self->ring->rx[i].len = RX_LEN;
+      self->ring->rx[i].len = 0;
+      self->ring->rx[i].address = virt_to_bus (self->rx_bufs[i]);
+      self->ring->rx[i].control = OBOE_CTL_RX_HW_OWNS;
+    }
+}
+
+STATIC void
+toshoboe_resetptrs (struct toshoboe_cb *self)
+{
+  /* Can reset pointers by twidling DMA */
+  OUTB (0x0, OBOE_ENABLEH);
+  OUTBP (CONFIG0H_DMA_OFF, OBOE_CONFIG0H);
+  OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH);
+
+  self->rxs = inb_p (OBOE_RXSLOT) & OBOE_SLOT_MASK;
+  self->txs = inb_p (OBOE_TXSLOT) & OBOE_SLOT_MASK;
+}
+
+/* Called in locked state */
+STATIC void
+toshoboe_initptrs (struct toshoboe_cb *self)
+{
+
+  /* spin_lock_irqsave(self->spinlock, flags); */
+  /* save_flags (flags); */
+
+  /* Can reset pointers by twidling DMA */
+  toshoboe_resetptrs (self);
+
+  OUTB (0x0, OBOE_ENABLEH);
+  OUTB (CONFIG0H_DMA_ON, OBOE_CONFIG0H);
+  OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH);
+
+  self->txpending = 0;
+
+  /* spin_unlock_irqrestore(self->spinlock, flags); */
+  /* restore_flags (flags); */
+}
+
+/* Wake the chip up and get it looking at the rings */
+/* Called in locked state */
+STATIC void
+toshoboe_startchip (struct toshoboe_cb *self)
+{
+  __u32 physaddr;
+
+  IRDA_DEBUG (4, "%s()\n", __FUNCTION__);
+
+  toshoboe_initring (self);
+  toshoboe_enablebm (self);
+  OUTBP (OBOE_CONFIG1_RESET, OBOE_CONFIG1);
+  OUTBP (OBOE_CONFIG1_ON, OBOE_CONFIG1);
+
+  /* Stop the clocks */
+  OUTB (0, OBOE_ENABLEH);
+
+  /*Set size of rings */
+  OUTB (RING_SIZE, OBOE_RING_SIZE);
+
+  /*Acknoledge any pending interrupts */
+  OUTB (0xff, OBOE_ISR);
+
+  /*Enable ints */
+  OUTB (OBOE_INT_TXDONE  | OBOE_INT_RXDONE |
+        OBOE_INT_TXUNDER | OBOE_INT_RXOVER | OBOE_INT_SIP , OBOE_IER);
+
+  /*Acknoledge any pending interrupts */
+  OUTB (0xff, OBOE_ISR);
+
+  /*Set the maximum packet length to 0xfff (4095) */
+  OUTB (RX_LEN >> 8, OBOE_MAXLENH);
+  OUTB (RX_LEN & 0xff, OBOE_MAXLENL);
+
+  /*Shutdown DMA */
+  OUTB (CONFIG0H_DMA_OFF, OBOE_CONFIG0H);
+
+  /*Find out where the rings live */
+  physaddr = virt_to_bus (self->ring);
+
+  ASSERT ((physaddr & 0x3ff) == 0,
+          printk (KERN_ERR DRIVER_NAME "ring not correctly aligned\n");
+          return;);
+
+  OUTB ((physaddr >> 10) & 0xff, OBOE_RING_BASE0);
+  OUTB ((physaddr >> 18) & 0xff, OBOE_RING_BASE1);
+  OUTB ((physaddr >> 26) & 0x3f, OBOE_RING_BASE2);
+
+  /*Enable DMA controler in byte mode and RX */
+  OUTB (CONFIG0H_DMA_ON, OBOE_CONFIG0H);
+
+  /* Start up the clocks */
+  OUTB (OBOE_ENABLEH_PHYANDCLOCK, OBOE_ENABLEH);
+
+  /*set to sensible speed */
+  self->speed = 9600;
+  toshoboe_setbaud (self);
+  toshoboe_initptrs (self);
+}
+
+STATIC void
+toshoboe_isntstuck (struct toshoboe_cb *self)
+{
+}
+
+STATIC void
+toshoboe_checkstuck (struct toshoboe_cb *self)
+{
+  unsigned long flags;
+
+  if (0)
+    {
+      spin_lock_irqsave(&self->spinlock, flags);
+
+      /* This will reset the chip completely */
+      printk (KERN_ERR DRIVER_NAME ": Resetting chip\n");
+
+      toshoboe_stopchip (self);
+      toshoboe_startchip (self);
+      spin_unlock_irqrestore(&self->spinlock, flags);
+    }
+}
+
+/*Generate packet of about mtt us long */
+STATIC int
+toshoboe_makemttpacket (struct toshoboe_cb *self, void *buf, int mtt)
+{
+  int xbofs;
+
+  xbofs = ((int) (mtt/100)) * (int) (self->speed);
+  xbofs=xbofs/80000; /*Eight bits per byte, and mtt is in us*/
+  xbofs++;
+
+  IRDA_DEBUG (2, DRIVER_NAME 
+      ": generated mtt of %d bytes for %d us at %d baud\n"
+	  , xbofs,mtt,self->speed);
+
+  if (xbofs > TX_LEN)
+    {
+      printk (KERN_ERR DRIVER_NAME ": wanted %d bytes MTT but TX_LEN is %d\n",
+              xbofs, TX_LEN);
+      xbofs = TX_LEN;
+    }
+
+  /*xbofs will do for SIR, MIR and FIR,SIR mode doesn't generate a checksum anyway */
+  memset (buf, XBOF, xbofs);
+
+  return xbofs;
+}
+
+/***********************************************************************/
+/* Probe code */
+
+STATIC void
+toshoboe_dumptx (struct toshoboe_cb *self)
+{
+  int i;
+  PROBE_DEBUG(KERN_WARNING "TX:");
+  for (i = 0; i < RX_SLOTS; ++i)
+    PROBE_DEBUG(" (%d,%02x)",self->ring->tx[i].len,self->ring->tx[i].control);
+  PROBE_DEBUG(" [%d]\n",self->speed);
+}
+
+STATIC void
+toshoboe_dumprx (struct toshoboe_cb *self, int score)
+{
+  int i;
+  PROBE_DEBUG(" %d\nRX:",score);
+  for (i = 0; i < RX_SLOTS; ++i)
+    PROBE_DEBUG(" (%d,%02x)",self->ring->rx[i].len,self->ring->rx[i].control);
+  PROBE_DEBUG("\n");
+}
+
+static inline int
+stuff_byte (__u8 byte, __u8 * buf)
+{
+  switch (byte)
+    {
+    case BOF:                  /* FALLTHROUGH */
+    case EOF:                  /* FALLTHROUGH */
+    case CE:
+      /* Insert transparently coded */
+      buf[0] = CE;              /* Send link escape */
+      buf[1] = byte ^ IRDA_TRANS; /* Complement bit 5 */
+      return 2;
+      /* break; */
+    default:
+      /* Non-special value, no transparency required */
+      buf[0] = byte;
+      return 1;
+      /* break; */
+    }
+}
+
+STATIC int toshoboe_invalid_dev(int irq) 
+{
+  printk (KERN_WARNING DRIVER_NAME ": irq %d for unknown device.\n", irq);
+  return 1;
+}
+
+STATIC void
+toshoboe_probeinterrupt (int irq, void *dev_id, struct pt_regs *regs)
+{
+  struct toshoboe_cb *self = (struct toshoboe_cb *) dev_id;
+  __u8 irqstat;
+
+  if (self == NULL && toshoboe_invalid_dev(irq)) 
+    return;
+
+  irqstat = INB (OBOE_ISR);
+
+/* was it us */
+  if (!(irqstat & OBOE_INT_MASK))
+    return;
+
+/* Ack all the interrupts */
+  OUTB (irqstat, OBOE_ISR);
+
+  if (irqstat & OBOE_INT_TXDONE)
+    {
+      int txp;
+
+      self->int_tx++;
+      PROBE_DEBUG("T");
+
+      txp = INB (OBOE_TXSLOT) & OBOE_SLOT_MASK;
+      if (self->ring->tx[txp].control & OBOE_CTL_TX_HW_OWNS)
+        {
+          self->int_tx+=100;
+          PROBE_DEBUG("S");
+          toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX | OBOE_CONFIG0H_LOOP);
+        }
+    }
+
+  if (irqstat & OBOE_INT_RXDONE) {
+    self->int_rx++;
+    PROBE_DEBUG("R"); }
+  if (irqstat & OBOE_INT_TXUNDER) {
+    self->int_txunder++;
+    PROBE_DEBUG("U"); }
+  if (irqstat & OBOE_INT_RXOVER) {
+    self->int_rxover++;
+    PROBE_DEBUG("O"); }
+  if (irqstat & OBOE_INT_SIP) {
+    self->int_sip++;
+    PROBE_DEBUG("I"); }
+}
+
+STATIC int
+toshoboe_maketestpacket (unsigned char *buf, int badcrc, int fir)
+{
+  int i;
+  int len = 0;
+  union
+  {
+    __u16 value;
+    __u8 bytes[2];
+  }
+  fcs;
+
+  if (fir)
+    {
+      memset (buf, 0, TT_LEN);
+      return (TT_LEN);
+    }
+
+  fcs.value = INIT_FCS;
+
+  memset (buf, XBOF, 10);
+  len += 10;
+  buf[len++] = BOF;
+
+  for (i = 0; i < TT_LEN; ++i)
+    {
+      len += stuff_byte (i, buf + len);
+      fcs.value = irda_fcs (fcs.value, i);
+    }
+
+  len += stuff_byte (fcs.bytes[0] ^ badcrc, buf + len);
+  len += stuff_byte (fcs.bytes[1] ^ badcrc, buf + len);
+  buf[len++] = EOF;
+  len++;
+  return len;
+}
+
+STATIC int
+toshoboe_probefail (struct toshoboe_cb *self, char *msg)
+{
+  printk (KERN_ERR DRIVER_NAME "probe(%d) failed %s\n",self-> speed, msg);
+  toshoboe_dumpregs (self);
+  toshoboe_stopchip (self);
+  free_irq (self->io.irq, (void *) self);
+  return 0;
+}
+
+STATIC int
+toshoboe_numvalidrcvs (struct toshoboe_cb *self)
+{
+  int i, ret = 0;
+  for (i = 0; i < RX_SLOTS; ++i)
+    if ((self->ring->rx[i].control & 0xe0) == 0)
+      ret++;
+
+  return ret;
+}
+
+STATIC int
+toshoboe_numrcvs (struct toshoboe_cb *self)
+{
+  int i, ret = 0;
+  for (i = 0; i < RX_SLOTS; ++i)
+    if (!(self->ring->rx[i].control & OBOE_CTL_RX_HW_OWNS))
+      ret++;
+
+  return ret;
+}
+
+STATIC int
+toshoboe_probe (struct toshoboe_cb *self)
+{
+  int i, j, n;
+#ifdef USE_MIR
+  int bauds[] = { 9600, 115200, 4000000, 1152000 };
+#else
+  int bauds[] = { 9600, 115200, 4000000 };
+#endif
+  unsigned long flags;
+
+  IRDA_DEBUG (4, "%s()\n", __FUNCTION__);
+
+  if (request_irq (self->io.irq, toshoboe_probeinterrupt,
+                   self->io.irqflags, "toshoboe", (void *) self))
+    {
+      printk (KERN_ERR DRIVER_NAME ": probe failed to allocate irq %d\n",
+              self->io.irq);
+      return 0;
+    }
+
+  /* test 1: SIR filter and back to back */
+
+  for (j = 0; j < (sizeof (bauds) / sizeof (int)); ++j)
+    {
+      int fir = (j > 1);
+      toshoboe_stopchip (self);
+
+
+      spin_lock_irqsave(&self->spinlock, flags);
+      /*Address is already setup */
+      toshoboe_startchip (self);
+      self->int_rx = self->int_tx = 0;
+      self->speed = bauds[j];
+      toshoboe_setbaud (self);
+      toshoboe_initptrs (self);
+      spin_unlock_irqrestore(&self->spinlock, flags); 
+
+      self->ring->tx[self->txs].control =
+/*   (FIR only) OBOE_CTL_TX_SIP needed for switching to next slot */
+/*    MIR: all received data is stored in one slot */
+        (fir) ? OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX
+              : OBOE_CTL_TX_HW_OWNS ;
+      self->ring->tx[self->txs].len =
+        toshoboe_maketestpacket (self->tx_bufs[self->txs], 0, fir);
+      self->txs++;
+      self->txs %= TX_SLOTS;
+
+      self->ring->tx[self->txs].control =
+        (fir) ? OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_SIP
+              : OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX ;
+      self->ring->tx[self->txs].len =
+        toshoboe_maketestpacket (self->tx_bufs[self->txs], 0, fir);
+      self->txs++;
+      self->txs %= TX_SLOTS;
+
+      self->ring->tx[self->txs].control = 
+        (fir) ? OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX
+              : OBOE_CTL_TX_HW_OWNS ;
+      self->ring->tx[self->txs].len =
+        toshoboe_maketestpacket (self->tx_bufs[self->txs], 0, fir);
+      self->txs++;
+      self->txs %= TX_SLOTS;
+
+      self->ring->tx[self->txs].control =
+        (fir) ? OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX
+              | OBOE_CTL_TX_SIP     | OBOE_CTL_TX_BAD_CRC
+              : OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX ;
+      self->ring->tx[self->txs].len =
+        toshoboe_maketestpacket (self->tx_bufs[self->txs], 0, fir);
+      self->txs++;
+      self->txs %= TX_SLOTS;
+
+      toshoboe_dumptx (self);
+      /* Turn on TX and RX and loopback */
+      toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX | OBOE_CONFIG0H_LOOP);
+
+      i = 0;
+      n = fir ? 1 : 4;
+      while (toshoboe_numvalidrcvs (self) != n)
+        {
+          if (i > 4800)
+              return toshoboe_probefail (self, "filter test");
+          udelay ((9600*(TT_LEN+16))/self->speed);
+          i++;
+        }
+
+      n = fir ? 203 : 102;
+      while ((toshoboe_numrcvs(self) != self->int_rx) || (self->int_tx != n))
+        {
+          if (i > 4800)
+              return toshoboe_probefail (self, "interrupt test");
+          udelay ((9600*(TT_LEN+16))/self->speed);
+          i++;
+        }
+     toshoboe_dumprx (self,i);
+
+     }
+
+  /* test 2: SIR in char at a time */
+
+  toshoboe_stopchip (self);
+  self->int_rx = self->int_tx = 0;
+
+  spin_lock_irqsave(&self->spinlock, flags);
+  toshoboe_startchip (self);
+  spin_unlock_irqrestore(&self->spinlock, flags);
+
+  self->async = 1;
+  self->speed = 115200;
+  toshoboe_setbaud (self);
+  self->ring->tx[self->txs].control =
+    OBOE_CTL_TX_RTCENTX | OBOE_CTL_TX_HW_OWNS;
+  self->ring->tx[self->txs].len = 4;
+
+  ((unsigned char *) self->tx_bufs[self->txs])[0] = 'f';
+  ((unsigned char *) self->tx_bufs[self->txs])[1] = 'i';
+  ((unsigned char *) self->tx_bufs[self->txs])[2] = 's';
+  ((unsigned char *) self->tx_bufs[self->txs])[3] = 'h';
+  toshoboe_dumptx (self);
+  toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX | OBOE_CONFIG0H_LOOP);
+
+  i = 0;
+  while (toshoboe_numvalidrcvs (self) != 4)
+    {
+      if (i > 100)
+          return toshoboe_probefail (self, "Async test");
+      udelay (100);
+      i++;
+    }
+
+  while ((toshoboe_numrcvs (self) != self->int_rx) || (self->int_tx != 1))
+    {
+      if (i > 100)
+          return toshoboe_probefail (self, "Async interrupt test");
+      udelay (100);
+      i++;
+    }
+  toshoboe_dumprx (self,i);
+
+  self->async = 0;
+  self->speed = 9600;
+  toshoboe_setbaud (self);
+  toshoboe_stopchip (self);
+
+  free_irq (self->io.irq, (void *) self);
+
+  printk (KERN_WARNING DRIVER_NAME ": Self test passed ok\n");
+
+  return 1;
+}
+
+/******************************************************************/
+/* Netdev style code */
+
+/* Transmit something */
+STATIC int
+toshoboe_hard_xmit (struct sk_buff *skb, struct net_device *dev)
+{
+  struct toshoboe_cb *self;
+  __s32 speed;
+  int mtt, len, ctl;
+  unsigned long flags;
+  struct irda_skb_cb *cb = (struct irda_skb_cb *) skb->cb;
+
+  self = (struct toshoboe_cb *) dev->priv;
+
+  ASSERT (self != NULL, return 0; );
+
+  IRDA_DEBUG (1, "%s.tx:%x(%x)%x\n", __FUNCTION__ 
+      ,skb->len,self->txpending,INB (OBOE_ENABLEH));
+  if (!cb->magic) {
+      IRDA_DEBUG (2, "%s.Not IrLAP:%x\n", __FUNCTION__, cb->magic);
+#ifdef DUMP_PACKETS
+      _dumpbufs(skb->data,skb->len,'>'); 
+#endif
+    }
+
+  /* change speed pending, wait for its execution */
+  if (self->new_speed)
+      return -EBUSY;
+
+  /* device stopped (apm) wait for restart */
+  if (self->stopped)
+      return -EBUSY;
+
+  toshoboe_checkstuck (self);
+
+  /* Check if we need to change the speed */
+  /* But not now. Wait after transmission if mtt not required */
+  speed=irda_get_next_speed(skb);
+  if ((speed != self->io.speed) && (speed != -1))
+    {
+      spin_lock_irqsave(&self->spinlock, flags);
+
+      if (self->txpending || skb->len)
+        {
+          self->new_speed = speed;
+          IRDA_DEBUG (1, "%s: Queued TxDone scheduled speed change %d\n" ,
+		      __FUNCTION__, speed);
+          /* if no data, that's all! */
+          if (!skb->len) 
+            {
+	      spin_unlock_irqrestore(&self->spinlock, flags);
+              dev_kfree_skb (skb);
+              return 0;
+            }
+          /* True packet, go on, but */
+          /* do not accept anything before change speed execution */
+          netif_stop_queue(dev); 
+          /* ready to process TxDone interrupt */
+	  spin_unlock_irqrestore(&self->spinlock, flags);
+        }
+      else
+        { 
+          /* idle and no data, change speed now */
+          self->speed = speed;
+          toshoboe_setbaud (self);
+	  spin_unlock_irqrestore(&self->spinlock, flags);
+          dev_kfree_skb (skb);
+          return 0;
+        }
+
+    }
+
+  if ((mtt = irda_get_mtt(skb)))
+    {
+      /* This is fair since the queue should be empty anyway */
+      spin_lock_irqsave(&self->spinlock, flags);
+
+      if (self->txpending)
+        {
+	  spin_unlock_irqrestore(&self->spinlock, flags);
+          return -EBUSY;
+        }
+
+      /* If in SIR mode we need to generate a string of XBOFs */
+      /* In MIR and FIR we need to generate a string of data */
+      /* which we will add a wrong checksum to */
+
+      mtt = toshoboe_makemttpacket (self, self->tx_bufs[self->txs], mtt);
+      IRDA_DEBUG (1, "%s.mtt:%x(%x)%d\n", __FUNCTION__ 
+          ,skb->len,mtt,self->txpending);
+      if (mtt)
+        {
+          self->ring->tx[self->txs].len = mtt & 0xfff;
+
+          ctl = OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX;
+          if (INB (OBOE_ENABLEH) & OBOE_ENABLEH_FIRON)
+            {
+              ctl |= OBOE_CTL_TX_BAD_CRC | OBOE_CTL_TX_SIP ;
+            }
+#ifdef USE_MIR
+          else if (INB (OBOE_ENABLEH) & OBOE_ENABLEH_MIRON)
+            {
+              ctl |= OBOE_CTL_TX_BAD_CRC;
+            }
+#endif
+          self->ring->tx[self->txs].control = ctl;
+
+          OUTB (0x0, OBOE_ENABLEH);
+          /* It is only a timer. Do not send mtt packet outside! */
+          toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX | OBOE_CONFIG0H_LOOP);
+
+          self->txpending++;
+
+          self->txs++;
+          self->txs %= TX_SLOTS;
+
+        }
+      else
+        {
+          printk(KERN_ERR DRIVER_NAME ": problem with mtt packet - ignored\n");
+        }
+      spin_unlock_irqrestore(&self->spinlock, flags);
+    }
+
+#ifdef DUMP_PACKETS
+dumpbufs(skb->data,skb->len,'>'); 
+#endif
+
+  spin_lock_irqsave(&self->spinlock, flags);
+
+  if (self->ring->tx[self->txs].control & OBOE_CTL_TX_HW_OWNS)
+    {
+      IRDA_DEBUG (0, "%s.ful:%x(%x)%x\n", __FUNCTION__
+          ,skb->len, self->ring->tx[self->txs].control, self->txpending);
+      toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX);
+      spin_unlock_irqrestore(&self->spinlock, flags);
+      return -EBUSY;
+    }
+
+  if (INB (OBOE_ENABLEH) & OBOE_ENABLEH_SIRON)
+    {
+      len = async_wrap_skb (skb, self->tx_bufs[self->txs], TX_BUF_SZ);
+    }
+  else
+    {
+      len = skb->len;
+      memcpy (self->tx_bufs[self->txs], skb->data, len);
+    }
+  self->ring->tx[self->txs].len = len & 0x0fff;
+
+  /*Sometimes the HW doesn't see us assert RTCENTX in the interrupt code */
+  /*later this plays safe, we garuntee the last packet to be transmitted */
+  /*has RTCENTX set */
+
+  ctl = OBOE_CTL_TX_HW_OWNS | OBOE_CTL_TX_RTCENTX;
+  if (INB (OBOE_ENABLEH) & OBOE_ENABLEH_FIRON)
+    {
+      ctl |= OBOE_CTL_TX_SIP ;
+    }
+  self->ring->tx[self->txs].control = ctl;
+
+  /* If transmitter is idle start in one-shot mode */
+
+  if (!self->txpending)
+      toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX);
+
+  self->txpending++;
+
+  self->txs++;
+  self->txs %= TX_SLOTS;
+
+  spin_unlock_irqrestore(&self->spinlock, flags);
+  dev_kfree_skb (skb);
+
+  return 0;
+}
+
+/*interrupt handler */
+STATIC void
+toshoboe_interrupt (int irq, void *dev_id, struct pt_regs *regs)
+{
+  struct toshoboe_cb *self = (struct toshoboe_cb *) dev_id;
+  __u8 irqstat;
+  struct sk_buff *skb = NULL;
+
+  if (self == NULL && toshoboe_invalid_dev(irq)) 
+    return;
+
+  irqstat = INB (OBOE_ISR);
+
+/* was it us */
+  if (!(irqstat & OBOE_INT_MASK))
+      return;
+
+/* Ack all the interrupts */
+  OUTB (irqstat, OBOE_ISR);
+
+  toshoboe_isntstuck (self);
+
+/* Txdone */
+  if (irqstat & OBOE_INT_TXDONE)
+    {
+      int txp, txpc;
+      int i;
+
+      txp = self->txpending;
+      self->txpending = 0;
+
+      for (i = 0; i < TX_SLOTS; ++i)
+        {
+          if (self->ring->tx[i].control & OBOE_CTL_TX_HW_OWNS)
+              self->txpending++;
+        }
+      IRDA_DEBUG (1, "%s.txd(%x)%x/%x\n", __FUNCTION__
+          ,irqstat,txp,self->txpending);
+
+      txp = INB (OBOE_TXSLOT) & OBOE_SLOT_MASK;
+
+      /* Got anything queued ? start it together */
+      if (self->ring->tx[txp].control & OBOE_CTL_TX_HW_OWNS)
+        {
+          txpc = txp;
+#ifdef OPTIMIZE_TX
+          while (self->ring->tx[txpc].control & OBOE_CTL_TX_HW_OWNS)
+            {
+              txp = txpc;
+              txpc++;
+              txpc %= TX_SLOTS;
+              self->stats.tx_packets++;
+              if (self->ring->tx[txpc].control & OBOE_CTL_TX_HW_OWNS)
+                  self->ring->tx[txp].control &= ~OBOE_CTL_TX_RTCENTX;
+            }
+          self->stats.tx_packets--;
+#else
+          self->stats.tx_packets++;
+#endif
+          toshoboe_start_DMA(self, OBOE_CONFIG0H_ENTX);
+        }
+
+      if ((!self->txpending) && (self->new_speed))
+        {
+          self->speed = self->new_speed;
+          IRDA_DEBUG (1, "%s: Executed TxDone scheduled speed change %d\n",
+		      __FUNCTION__, self->speed);
+          toshoboe_setbaud (self);
+        }
+
+      /* Tell network layer that we want more frames */
+      if (!self->new_speed)
+          netif_wake_queue(self->netdev);
+    }
+
+  if (irqstat & OBOE_INT_RXDONE)
+    {
+      while (!(self->ring->rx[self->rxs].control & OBOE_CTL_RX_HW_OWNS))
+        {
+          int len = self->ring->rx[self->rxs].len;
+          skb = NULL;
+          IRDA_DEBUG (3, "%s.rcv:%x(%x)\n", __FUNCTION__ 
+		      ,len,self->ring->rx[self->rxs].control);
+
+#ifdef DUMP_PACKETS
+dumpbufs(self->rx_bufs[self->rxs],len,'<');
+#endif
+
+          if (self->ring->rx[self->rxs].control == 0)
+            {
+              __u8 enable = INB (OBOE_ENABLEH);
+
+              /* In SIR mode we need to check the CRC as this */
+              /* hasn't been done by the hardware */
+              if (enable & OBOE_ENABLEH_SIRON)
+                {
+                  if (!toshoboe_checkfcs (self->rx_bufs[self->rxs], len))
+                      len = 0;
+                  /*Trim off the CRC */
+                  if (len > 1)
+                      len -= 2;
+                  else
+                      len = 0;
+                  IRDA_DEBUG (1, "%s.SIR:%x(%x)\n", __FUNCTION__, len,enable);
+                }
+
+#ifdef USE_MIR
+              else if (enable & OBOE_ENABLEH_MIRON)
+                {
+                  if (len > 1)
+                      len -= 2;
+                  else
+                      len = 0;
+                  IRDA_DEBUG (2, "%s.MIR:%x(%x)\n", __FUNCTION__, len,enable);
+                }
+#endif
+              else if (enable & OBOE_ENABLEH_FIRON)
+                {
+                  if (len > 3)
+                      len -= 4;   /*FIXME: check this */
+                  else
+                      len = 0;
+                  IRDA_DEBUG (1, "%s.FIR:%x(%x)\n", __FUNCTION__, len,enable);
+                }
+              else 
+                  IRDA_DEBUG (0, "%s.?IR:%x(%x)\n", __FUNCTION__, len,enable);
+
+              if (len)
+                {
+                  skb = dev_alloc_skb (len + 1);
+                  if (skb)
+                    {
+                      skb_reserve (skb, 1);
+
+                      skb_put (skb, len);
+                      memcpy (skb->data, self->rx_bufs[self->rxs], len);
+
+                      self->stats.rx_packets++;
+                      skb->dev = self->netdev;
+                      skb->mac.raw = skb->data;
+                      skb->protocol = htons (ETH_P_IRDA);
+                    }
+                  else
+                    {
+                      printk (KERN_INFO 
+                              "%s(), memory squeeze, dropping frame.\n",
+			      __FUNCTION__);
+                    }
+                }
+            }
+          else 
+            {
+            /* TODO: =========================================== */
+            /*  if OBOE_CTL_RX_LENGTH, our buffers are too small */
+            /* (MIR or FIR) data is lost. */
+            /* (SIR) data is splitted in several slots. */
+            /* we have to join all the received buffers received */
+            /*in a large buffer before checking CRC. */
+            IRDA_DEBUG (0, "%s.err:%x(%x)\n", __FUNCTION__
+                ,len,self->ring->rx[self->rxs].control);
+            }
+
+          self->ring->rx[self->rxs].len = 0x0;
+          self->ring->rx[self->rxs].control = OBOE_CTL_RX_HW_OWNS;
+
+          self->rxs++;
+          self->rxs %= RX_SLOTS;
+
+          if (skb)
+              netif_rx (skb);
+
+        }
+    }
+
+  if (irqstat & OBOE_INT_TXUNDER)
+    {
+      printk (KERN_WARNING DRIVER_NAME ": tx fifo underflow\n");
+    }
+  if (irqstat & OBOE_INT_RXOVER)
+    {
+      printk (KERN_WARNING DRIVER_NAME ": rx fifo overflow\n");
+    }
+/* This must be useful for something... */
+  if (irqstat & OBOE_INT_SIP)
+    {
+      self->int_sip++;
+      IRDA_DEBUG (1, "%s.sip:%x(%x)%x\n", __FUNCTION__
+	      ,self->int_sip,irqstat,self->txpending);
+    }
+}
+
+STATIC int
+toshoboe_net_init (struct net_device *dev)
+{
+  IRDA_DEBUG (4, "%s()\n", __FUNCTION__);
+
+  /* Setup to be a normal IrDA network device driver */
+  irda_device_setup (dev);
+
+  /* Insert overrides below this line! */
+  return 0;
+}
+
+STATIC int
+toshoboe_net_open (struct net_device *dev)
+{
+  struct toshoboe_cb *self;
+  unsigned long flags;
+
+  IRDA_DEBUG (4, "%s()\n", __FUNCTION__);
+
+  ASSERT (dev != NULL, return -1; );
+  self = (struct toshoboe_cb *) dev->priv;
+
+  ASSERT (self != NULL, return 0; );
+
+  if (self->async)
+    return -EBUSY;
+
+  if (self->stopped)
+    return 0;
+
+  if (request_irq (self->io.irq, toshoboe_interrupt,
+                   SA_SHIRQ | SA_INTERRUPT, dev->name, (void *) self))
+    {
+      return -EAGAIN;
+    }
+
+  spin_lock_irqsave(&self->spinlock, flags);
+  toshoboe_startchip (self);
+  spin_unlock_irqrestore(&self->spinlock, flags);
+
+  /* Ready to play! */
+  netif_start_queue(dev);
+
+  /*
+   * Open new IrLAP layer instance, now that everything should be
+   * initialized properly
+   */
+  self->irlap = irlap_open (dev, &self->qos, driver_name);
+
+  self->irdad = 1;
+
+  MOD_INC_USE_COUNT;
+
+  return 0;
+}
+
+STATIC int
+toshoboe_net_close (struct net_device *dev)
+{
+  struct toshoboe_cb *self;
+
+  IRDA_DEBUG (4, "%s()\n", __FUNCTION__);
+
+  ASSERT (dev != NULL, return -1; );
+  self = (struct toshoboe_cb *) dev->priv;
+
+  /* Stop device */
+  netif_stop_queue(dev);
+
+  /* Stop and remove instance of IrLAP */
+  if (self->irlap)
+    irlap_close (self->irlap);
+  self->irlap = NULL;
+
+  self->irdad = 0;
+
+  free_irq (self->io.irq, (void *) self);
+
+  if (!self->stopped)
+    {
+      toshoboe_stopchip (self);
+    }
+
+  MOD_DEC_USE_COUNT;
+
+  return 0;
+}
+
+/*
+ * Function toshoboe_net_ioctl (dev, rq, cmd)
+ *
+ *    Process IOCTL commands for this device
+ *
+ */
+STATIC int
+toshoboe_net_ioctl (struct net_device *dev, struct ifreq *rq, int cmd)
+{
+  struct if_irda_req *irq = (struct if_irda_req *) rq;
+  struct toshoboe_cb *self;
+  unsigned long flags;
+  int ret = 0;
+
+  ASSERT (dev != NULL, return -1; );
+
+  self = dev->priv;
+
+  ASSERT (self != NULL, return -1; );
+
+  IRDA_DEBUG (5, "%s(), %s, (cmd=0x%X)\n", __FUNCTION__, dev->name, cmd);
+
+  /* Disable interrupts & save flags */
+  spin_lock_irqsave(&self->spinlock, flags);
+
+  switch (cmd)
+    {
+    case SIOCSBANDWIDTH:       /* Set bandwidth */
+      /* This function will also be used by IrLAP to change the
+       * speed, so we still must allow for speed change within
+       * interrupt context.
+       */
+      IRDA_DEBUG (1, "%s(BANDWIDTH), %s, (%X/%ld\n", __FUNCTION__
+          ,dev->name, INB (OBOE_STATUS), irq->ifr_baudrate );
+      if (!in_interrupt () && !capable (CAP_NET_ADMIN))
+        return -EPERM;
+
+      /* self->speed=irq->ifr_baudrate; */
+      /* toshoboe_setbaud(self); */
+      /* Just change speed once - inserted by Paul Bristow */
+      self->new_speed = irq->ifr_baudrate;
+      break;
+    case SIOCSMEDIABUSY:       /* Set media busy */
+      IRDA_DEBUG (1, "%s(MEDIABUSY), %s, (%X/%x)\n", __FUNCTION__
+          ,dev->name, INB (OBOE_STATUS), capable (CAP_NET_ADMIN) );
+      if (!capable (CAP_NET_ADMIN))
+        return -EPERM;
+      irda_device_set_media_busy (self->netdev, TRUE);
+      break;
+    case SIOCGRECEIVING:       /* Check if we are receiving right now */
+      irq->ifr_receiving = (INB (OBOE_STATUS) & OBOE_STATUS_RXBUSY) ? 1 : 0;
+      IRDA_DEBUG (3, "%s(RECEIVING), %s, (%X/%x)\n", __FUNCTION__
+          ,dev->name, INB (OBOE_STATUS), irq->ifr_receiving );
+      break;
+    default:
+      IRDA_DEBUG (1, "%s(?), %s, (cmd=0x%X)\n", __FUNCTION__, dev->name, cmd);
+      ret = -EOPNOTSUPP;
+    }
+
+  spin_unlock_irqrestore(&self->spinlock, flags);
+  return ret;
+
+}
+
+MODULE_DESCRIPTION("Toshiba OBOE IrDA Device Driver");
+MODULE_AUTHOR("James McKenzie <james@fishsoup.dhs.org>");
+MODULE_LICENSE("GPL");
+
+MODULE_PARM (max_baud, "i");
+MODULE_PARM_DESC(max_baud, "Maximum baud rate");
+
+MODULE_PARM (do_probe, "i");
+MODULE_PARM_DESC(do_probe, "Enable/disable chip probing and self-test");
+
+STATIC void
+toshoboe_close (struct pci_dev *pci_dev)
+{
+  int i;
+  struct toshoboe_cb *self = (struct toshoboe_cb*)pci_get_drvdata(pci_dev);
+
+  IRDA_DEBUG (4, "%s()\n", __FUNCTION__);
+
+  ASSERT (self != NULL, return; );
+
+  if (!self->stopped)
+    {
+      toshoboe_stopchip (self);
+    }
+
+  release_region (self->io.fir_base, self->io.fir_ext);
+
+  for (i = 0; i < TX_SLOTS; ++i)
+    {
+      kfree (self->tx_bufs[i]);
+      self->tx_bufs[i] = NULL;
+    }
+
+  for (i = 0; i < RX_SLOTS; ++i)
+    {
+      kfree (self->rx_bufs[i]);
+      self->rx_bufs[i] = NULL;
+    }
+
+  if (self->netdev)
+    {
+      /* Remove netdevice */
+      rtnl_lock ();
+      unregister_netdevice (self->netdev);
+      rtnl_unlock ();
+    }
+
+  kfree (self->ringbuf);
+  self->ringbuf = NULL;
+  self->ring = NULL;
+
+  return;
+}
+
+STATIC int
+toshoboe_open (struct pci_dev *pci_dev, const struct pci_device_id *pdid)
+{
+  struct toshoboe_cb *self;
+  struct net_device *dev;
+  int i = 0;
+  int ok = 0;
+  int err;
+
+  IRDA_DEBUG (4, "%s()\n", __FUNCTION__);
+
+  if ((err=pci_enable_device(pci_dev)))
+    return err;
+
+  self = kmalloc (sizeof (struct toshoboe_cb), GFP_KERNEL);
+
+  if (self == NULL)
+    {
+      printk (KERN_ERR DRIVER_NAME ": can't allocate memory for "
+              "IrDA control block\n");
+      return -ENOMEM;
+    }
+
+  memset (self, 0, sizeof (struct toshoboe_cb));
+
+  self->pdev = pci_dev;
+  self->base = pci_resource_start(pci_dev,0);
+
+  self->io.fir_base = self->base;
+  self->io.fir_ext = OBOE_IO_EXTENT;
+  self->io.irq = pci_dev->irq;
+  self->io.irqflags = SA_SHIRQ | SA_INTERRUPT;
+
+  self->speed = self->io.speed = 9600;
+  self->async = 0;
+
+  /* Lock the port that we need */
+  if (NULL==request_region (self->io.fir_base, self->io.fir_ext, driver_name))
+    {
+      printk (KERN_ERR DRIVER_NAME ": can't get iobase of 0x%03x\n"
+	      ,self->io.fir_base);
+      err = -EBUSY;
+      goto freeself;
+    }
+
+  spin_lock_init(&self->spinlock);
+  
+  irda_init_max_qos_capabilies (&self->qos);
+  self->qos.baud_rate.bits = 0;
+
+  if (max_baud >= 2400)
+    self->qos.baud_rate.bits |= IR_2400;
+  /*if (max_baud>=4800) idev->qos.baud_rate.bits|=IR_4800; */
+  if (max_baud >= 9600)
+    self->qos.baud_rate.bits |= IR_9600;
+  if (max_baud >= 19200)
+    self->qos.baud_rate.bits |= IR_19200;
+  if (max_baud >= 115200)
+    self->qos.baud_rate.bits |= IR_115200;
+#ifdef USE_MIR
+  if (max_baud >= 1152000)
+    {
+      self->qos.baud_rate.bits |= IR_1152000;
+      self->flags |= IFF_MIR;
+    }
+#endif
+  if (max_baud >= 4000000)
+    {
+      self->qos.baud_rate.bits |= (IR_4000000 << 8);
+      self->flags |= IFF_FIR;
+    }
+
+  /*FIXME: work this out... */
+  self->qos.min_turn_time.bits = 0xff;
+
+  irda_qos_bits_to_value (&self->qos);
+
+  self->flags = IFF_SIR | IFF_DMA | IFF_PIO;
+
+  /* Allocate twice the size to guarantee alignment */
+  self->ringbuf = (void *) kmalloc (OBOE_RING_LEN << 1, GFP_KERNEL);
+  if (!self->ringbuf)
+    {
+      printk (KERN_ERR DRIVER_NAME ": can't allocate DMA buffers\n");
+      err = -ENOMEM;
+      goto freeregion;
+    }
+
+  /*We need to align the taskfile on a taskfile size boundary */
+  {
+    __u32 addr;
+
+    addr = (__u32) self->ringbuf;
+    addr &= ~(OBOE_RING_LEN - 1);
+    addr += OBOE_RING_LEN;
+    self->ring = (struct OboeRing *) addr;
+  }
+
+  memset (self->ring, 0, OBOE_RING_LEN);
+  self->io.mem_base = (__u32) self->ring;
+
+  ok = 1;
+  for (i = 0; i < TX_SLOTS; ++i)
+    {
+      self->tx_bufs[i] = kmalloc (TX_BUF_SZ, GFP_KERNEL);
+      if (!self->tx_bufs[i])
+        ok = 0;
+    }
+
+  for (i = 0; i < RX_SLOTS; ++i)
+    {
+      self->rx_bufs[i] = kmalloc (RX_BUF_SZ, GFP_KERNEL);
+      if (!self->rx_bufs[i])
+        ok = 0;
+    }
+
+  if (!ok)
+    {
+      printk (KERN_ERR DRIVER_NAME ": can't allocate rx/tx buffers\n");
+      err = -ENOMEM;
+      goto freebufs;
+    }
+
+  if (do_probe)
+    if (!toshoboe_probe (self))
+      {
+        err = -ENODEV;
+        goto freebufs;
+      }
+
+  if (!(dev = dev_alloc ("irda%d", &err)))
+    {
+      printk (KERN_ERR DRIVER_NAME ": dev_alloc() failed\n");
+      err = -ENOMEM;
+      goto freebufs;
+    }
+
+  dev->priv = (void *) self;
+  self->netdev = dev;
+
+  printk (KERN_INFO "IrDA: Registered device %s\n", dev->name);
+
+  dev->init = toshoboe_net_init;
+  dev->hard_start_xmit = toshoboe_hard_xmit;
+  dev->open = toshoboe_net_open;
+  dev->stop = toshoboe_net_close;
+  dev->do_ioctl = toshoboe_net_ioctl;
+
+  rtnl_lock ();
+  err = register_netdevice (dev);
+  rtnl_unlock ();
+  if (err)
+    {
+      printk (KERN_ERR DRIVER_NAME ": register_netdev() failed\n");
+      err = -ENOMEM;
+      goto freebufs;
+    }
+
+  pci_set_drvdata(pci_dev,self);
+
+  printk (KERN_INFO DRIVER_NAME ": Using multiple tasks, version %s\n", rcsid);
+
+  return 0;
+
+freebufs:
+  for (i = 0; i < TX_SLOTS; ++i)
+    if (self->tx_bufs[i])
+      kfree (self->tx_bufs[i]);
+  for (i = 0; i < RX_SLOTS; ++i)
+    if (self->rx_bufs[i])
+      kfree (self->rx_bufs[i]);
+  kfree(self->ringbuf);
+
+freeregion:
+  release_region (self->io.fir_base, self->io.fir_ext);
+
+freeself:
+  kfree (self);
+
+  return err;
+}
+
+STATIC int
+toshoboe_gotosleep (struct pci_dev *pci_dev, u32 crap)
+{
+  struct toshoboe_cb *self = (struct toshoboe_cb*)pci_get_drvdata(pci_dev);
+  unsigned long flags;
+  int i = 10;
+
+  IRDA_DEBUG (4, "%s()\n", __FUNCTION__);
+
+  if (!self || self->stopped)
+    return 0;
+
+  if ((!self->irdad) && (!self->async))
+    return 0;
+
+/* Flush all packets */
+  while ((i--) && (self->txpending))
+    udelay (10000);
+
+  spin_lock_irqsave(&self->spinlock, flags);
+
+  toshoboe_stopchip (self);
+  self->stopped = 1;
+  self->txpending = 0;
+
+  spin_unlock_irqrestore(&self->spinlock, flags);
+  return 0;
+}
+
+STATIC int
+toshoboe_wakeup (struct pci_dev *pci_dev)
+{
+  struct toshoboe_cb *self = (struct toshoboe_cb*)pci_get_drvdata(pci_dev);
+  unsigned long flags;
+
+  IRDA_DEBUG (4, "%s()\n", __FUNCTION__);
+
+  if (!self || !self->stopped)
+    return 0;
+
+  if ((!self->irdad) && (!self->async))
+    return 0;
+
+  spin_lock_irqsave(&self->spinlock, flags);
+
+  toshoboe_startchip (self);
+  self->stopped = 0;
+
+  netif_wake_queue(self->netdev);
+  spin_unlock_irqrestore(&self->spinlock, flags);
+  return 0;
+}
+
+static struct pci_driver toshoboe_pci_driver = {
+  name		: "toshoboe",
+  id_table	: toshoboe_pci_tbl,
+  probe		: toshoboe_open,
+  remove	: toshoboe_close,
+  suspend	: toshoboe_gotosleep,
+  resume	: toshoboe_wakeup 
+};
+
+int __init
+toshoboe_init (void)
+{
+  return pci_module_init(&toshoboe_pci_driver);
+}
+
+STATIC void __exit
+toshoboe_cleanup (void)
+{
+  pci_unregister_driver(&toshoboe_pci_driver);
+}
+
+module_init(toshoboe_init);
+module_exit(toshoboe_cleanup);
diff -urN linux-2.4.21-pre2/drivers/net/irda/donauboe.h linux-2.4.21-pre3/drivers/net/irda/donauboe.h
--- linux-2.4.21-pre2/drivers/net/irda/donauboe.h	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre3/drivers/net/irda/donauboe.h	Mon Jan  6 16:32:32 2003
@@ -0,0 +1,363 @@
+/*********************************************************************
+ *                
+ * Filename:      toshoboe.h
+ * Version:       2.16
+ * Description:   Driver for the Toshiba OBOE (or type-O or 701)
+ *                FIR Chipset, also supports the DONAUOBOE (type-DO
+ *                or d01) FIR chipset which as far as I know is
+ *                register compatible.
+ * Status:        Experimental.
+ * Author:        James McKenzie <james@fishsoup.dhs.org>
+ * Created at:    Sat May 8  12:35:27 1999
+ * Modified: 2.16 Martin Lucina <mato@kotelna.sk>
+ * Modified: 2.16 Sat Jun 22 18:54:29 2002 (sync headers)
+ * Modified: 2.17 Christian Gennerat <christian.gennerat@polytechnique.org>
+ * Modified: 2.17 jeu sep 12 08:50:20 2002 (add lock to be used by spinlocks)
+ * 
+ *     Copyright (c) 1999 James McKenzie, All Rights Reserved.
+ *      
+ *     This program is free software; you can redistribute it and/or 
+ *     modify it under the terms of the GNU General Public License as 
+ *     published by the Free Software Foundation; either version 2 of 
+ *     the License, or (at your option) any later version.
+ *  
+ *     Neither James McKenzie nor Cambridge University admit liability nor
+ *     provide warranty for any of this software. This material is 
+ *     provided "AS-IS" and at no charge.
+ * 
+ *     Applicable Models : Libretto 100/110CT and many more.
+ *     Toshiba refers to this chip as the type-O IR port,
+ *     or the type-DO IR port.
+ *
+ * IrDA chip set list from Toshiba Computer Engineering Corp.
+ * model			method	maker	controler		Version 
+ * Portege 320CT	FIR,SIR Toshiba Oboe(Triangle) 
+ * Portege 3010CT	FIR,SIR Toshiba Oboe(Sydney) 
+ * Portege 3015CT	FIR,SIR Toshiba Oboe(Sydney) 
+ * Portege 3020CT	FIR,SIR Toshiba Oboe(Sydney) 
+ * Portege 7020CT	FIR,SIR ?		?
+ * 
+ * Satell. 4090XCDT	FIR,SIR ?		?
+ * 
+ * Libretto 100CT	FIR,SIR Toshiba Oboe 
+ * Libretto 1000CT	FIR,SIR Toshiba Oboe 
+ * 
+ * TECRA750DVD		FIR,SIR Toshiba Oboe(Triangle)	REV ID=14h 
+ * TECRA780			FIR,SIR Toshiba Oboe(Sandlot)	REV ID=32h,33h 
+ * TECRA750CDT		FIR,SIR Toshiba Oboe(Triangle)	REV ID=13h,14h 
+ * TECRA8000		FIR,SIR Toshiba Oboe(ISKUR)		REV ID=23h 
+ * 
+ ********************************************************************/
+
+/* The documentation for this chip is allegedly released         */
+/* However I have not seen it, not have I managed to contact     */
+/* anyone who has. HOWEVER the chip bears a striking resemblence */
+/* to the IrDA controller in the Toshiba RISC TMPR3922 chip      */
+/* the documentation for this is freely available at             */
+/* http://www.toshiba.com/taec/components/Generic/TMPR3922.shtml */
+/* The mapping between the registers in that document and the    */
+/* Registers in the 701 oboe chip are as follows    */
+
+
+/* 3922 reg     701 regs, by bit numbers                        */
+/*               7- 0  15- 8  24-16  31-25                      */
+/* $28            0x0    0x1                                    */
+/* $2c                                     SEE NOTE 1           */
+/* $30            0x6    0x7                                    */
+/* $34            0x8    0x9               SEE NOTE 2           */
+/* $38           0x10   0x11                                    */
+/* $3C                   0xe               SEE NOTE 3           */
+/* $40           0x12   0x13                                    */
+/* $44           0x14   0x15                                    */
+/* $48           0x16   0x17                                    */
+/* $4c           0x18   0x19                                    */
+/* $50           0x1a   0x1b                                    */
+
+/* FIXME: could be 0x1b 0x1a here */
+
+/* $54           0x1d   0x1c                                    */
+/* $5C           0xf                       SEE NOTE 4           */
+/* $130                                    SEE NOTE 5           */
+/* $134                                    SEE NOTE 6           */
+/*                                                              */
+/* NOTES:                                                       */
+/* 1. The pointer to ring is packed in most unceremoniusly      */
+/*    701 Register      Address bits    (A9-A0 must be zero)    */
+/*            0x4:      A17 A16 A15 A14 A13 A12 A11 A10         */
+/*            0x5:      A25 A24 A23 A22 A21 A20 A19 A18         */
+/*            0x2:        0   0 A31 A30 A29 A28 A27 A26         */
+/*                                                              */
+/* 2. The M$ drivers do a write 0x1 to 0x9, however the 3922    */
+/*    documentation would suggest that a write of 0x1 to 0x8    */
+/*    would be more appropriate.                                */
+/*                                                              */
+/* 3. This assignment is tenuous at best, register 0xe seems to */
+/*    have bits arranged 0 0 0 R/W R/W R/W R/W R/W              */
+/*    if either of the lower two bits are set the chip seems to */
+/*    switch off                                                */
+/*                                                              */
+/* 4. Bits 7-4 seem to be different 4 seems just to be generic  */
+/*    receiver busy flag                                        */
+/*                                                              */
+/* 5. and 6. The IER and ISR have a different bit assignment    */
+/*    The lower three bits of both read back as ones            */
+/* ISR is register 0xc, IER is register 0xd                     */
+/*           7      6      5      4      3      2      1      0 */
+/* 0xc: TxDone RxDone TxUndr RxOver SipRcv      1      1      1 */
+/* 0xd: TxDone RxDone TxUndr RxOver SipRcv      1      1      1 */
+/* TxDone xmitt done (generated only if generate interrupt bit  */
+/*   is set in the ring)                                        */
+/* RxDone recv completed (or other recv condition if you set it */
+/*   up                                                         */
+/* TxUnder underflow in Transmit FIFO                           */
+/* RxOver  overflow in Recv FIFO                                */
+/* SipRcv  received serial gap  (or other condition you set)    */
+/* Interrupts are enabled by writing a one to the IER register  */
+/* Interrupts are cleared by writting a one to the ISR register */
+/*                                                              */
+/* 6. The remaining registers: 0x6 and 0x3 appear to be         */
+/*    reserved parts of 16 or 32 bit registersthe remainder     */
+/*    0xa 0xb 0x1e 0x1f could possibly be (by their behaviour)  */
+/*    the Unicast Filter register at $58.                       */
+/*                                                              */
+/* 7. While the core obviously expects 32 bit accesses all the  */
+/*    M$ drivers do 8 bit accesses, infact the Miniport ones    */
+/*    write and read back the byte serveral times (why?)        */
+
+
+#ifndef TOSHOBOE_H
+#define TOSHOBOE_H
+
+/* Registers */
+
+#define OBOE_IO_EXTENT	0x1f
+
+/*Receive and transmit slot pointers */
+#define OBOE_REG(i)	(i+(self->base))
+#define OBOE_RXSLOT	OBOE_REG(0x0)
+#define OBOE_TXSLOT	OBOE_REG(0x1)
+#define OBOE_SLOT_MASK	0x3f
+
+#define OBOE_TXRING_OFFSET		0x200
+#define OBOE_TXRING_OFFSET_IN_SLOTS	0x40
+
+/*pointer to the ring */
+#define OBOE_RING_BASE0	OBOE_REG(0x4)
+#define OBOE_RING_BASE1	OBOE_REG(0x5)
+#define OBOE_RING_BASE2	OBOE_REG(0x2)
+#define OBOE_RING_BASE3	OBOE_REG(0x3)
+
+/*Number of slots in the ring */
+#define OBOE_RING_SIZE  OBOE_REG(0x7)
+#define OBOE_RING_SIZE_RX4	0x00
+#define OBOE_RING_SIZE_RX8	0x01
+#define OBOE_RING_SIZE_RX16	0x03
+#define OBOE_RING_SIZE_RX32	0x07
+#define OBOE_RING_SIZE_RX64	0x0f
+#define OBOE_RING_SIZE_TX4	0x00
+#define OBOE_RING_SIZE_TX8	0x10
+#define OBOE_RING_SIZE_TX16	0x30
+#define OBOE_RING_SIZE_TX32	0x70
+#define OBOE_RING_SIZE_TX64	0xf0
+
+#define OBOE_RING_MAX_SIZE	64
+
+/*Causes the gubbins to re-examine the ring */
+#define OBOE_PROMPT	OBOE_REG(0x9)
+#define OBOE_PROMPT_BIT		0x1
+
+/* Interrupt Status Register */
+#define OBOE_ISR	OBOE_REG(0xc)
+/* Interrupt Enable Register */
+#define OBOE_IER	OBOE_REG(0xd)
+/* Interrupt bits for IER and ISR */
+#define OBOE_INT_TXDONE		0x80
+#define OBOE_INT_RXDONE		0x40
+#define OBOE_INT_TXUNDER	0x20
+#define OBOE_INT_RXOVER		0x10
+#define OBOE_INT_SIP		0x08
+#define OBOE_INT_MASK		0xf8
+
+/*Reset Register */
+#define OBOE_CONFIG1	OBOE_REG(0xe)
+#define OBOE_CONFIG1_RST	0x01
+#define OBOE_CONFIG1_DISABLE	0x02
+#define OBOE_CONFIG1_4		0x08
+#define OBOE_CONFIG1_8		0x08
+
+#define OBOE_CONFIG1_ON		0x8
+#define OBOE_CONFIG1_RESET	0xf
+#define OBOE_CONFIG1_OFF	0xe
+
+#define OBOE_STATUS	OBOE_REG(0xf)
+#define OBOE_STATUS_RXBUSY	0x10
+#define OBOE_STATUS_FIRRX	0x04
+#define OBOE_STATUS_MIRRX	0x02
+#define OBOE_STATUS_SIRRX	0x01
+
+
+/*Speed control registers */
+#define OBOE_CONFIG0L	OBOE_REG(0x10)
+#define OBOE_CONFIG0H	OBOE_REG(0x11)
+
+#define OBOE_CONFIG0H_TXONLOOP  0x80 /*Transmit when looping (dangerous) */
+#define OBOE_CONFIG0H_LOOP	0x40 /*Loopback Tx->Rx */
+#define OBOE_CONFIG0H_ENTX	0x10 /*Enable Tx */
+#define OBOE_CONFIG0H_ENRX	0x08 /*Enable Rx */
+#define OBOE_CONFIG0H_ENDMAC	0x04 /*Enable/reset* the DMA controller */
+#define OBOE_CONFIG0H_RCVANY	0x02 /*DMA mode 1=bytes, 0=dwords */
+
+#define OBOE_CONFIG0L_CRC16	0x80 /*CRC 1=16 bit 0=32 bit */
+#define OBOE_CONFIG0L_ENFIR	0x40 /*Enable FIR */
+#define OBOE_CONFIG0L_ENMIR	0x20 /*Enable MIR */
+#define OBOE_CONFIG0L_ENSIR	0x10 /*Enable SIR */
+#define OBOE_CONFIG0L_ENSIRF	0x08 /*Enable SIR framer */
+#define OBOE_CONFIG0L_SIRTEST	0x04 /*Enable SIR framer in MIR and FIR */
+#define OBOE_CONFIG0L_INVERTTX  0x02 /*Invert Tx Line */
+#define OBOE_CONFIG0L_INVERTRX  0x01 /*Invert Rx Line */
+
+#define OBOE_BOF	OBOE_REG(0x12)
+#define OBOE_EOF	OBOE_REG(0x13)
+
+#define OBOE_ENABLEL	OBOE_REG(0x14)
+#define OBOE_ENABLEH	OBOE_REG(0x15)
+
+#define OBOE_ENABLEH_PHYANDCLOCK	0x80 /*Toggle low to copy config in */
+#define OBOE_ENABLEH_CONFIGERR		0x40
+#define OBOE_ENABLEH_FIRON		0x20
+#define OBOE_ENABLEH_MIRON		0x10
+#define OBOE_ENABLEH_SIRON		0x08
+#define OBOE_ENABLEH_ENTX		0x04
+#define OBOE_ENABLEH_ENRX		0x02
+#define OBOE_ENABLEH_CRC16		0x01
+
+#define OBOE_ENABLEL_BROADCAST		0x01
+
+#define OBOE_CURR_PCONFIGL		OBOE_REG(0x16) /*Current config */
+#define OBOE_CURR_PCONFIGH		OBOE_REG(0x17)
+
+#define OBOE_NEW_PCONFIGL		OBOE_REG(0x18)
+#define OBOE_NEW_PCONFIGH		OBOE_REG(0x19)
+
+#define OBOE_PCONFIGH_BAUDMASK		0xfc
+#define OBOE_PCONFIGH_WIDTHMASK		0x04
+#define OBOE_PCONFIGL_WIDTHMASK		0xe0
+#define OBOE_PCONFIGL_PREAMBLEMASK	0x1f
+
+#define OBOE_PCONFIG_BAUDMASK		0xfc00
+#define OBOE_PCONFIG_BAUDSHIFT		10
+#define OBOE_PCONFIG_WIDTHMASK		0x04e0
+#define OBOE_PCONFIG_WIDTHSHIFT		5
+#define OBOE_PCONFIG_PREAMBLEMASK	0x001f
+#define OBOE_PCONFIG_PREAMBLESHIFT	0
+
+#define OBOE_MAXLENL			OBOE_REG(0x1a)
+#define OBOE_MAXLENH			OBOE_REG(0x1b)
+
+#define OBOE_RXCOUNTH			OBOE_REG(0x1c) /*Reset on recipt */
+#define OBOE_RXCOUNTL			OBOE_REG(0x1d) /*of whole packet */
+
+/* The PCI ID of the OBOE chip */
+#ifndef PCI_DEVICE_ID_FIR701
+#define PCI_DEVICE_ID_FIR701 	0x0701
+#endif
+
+#ifndef PCI_DEVICE_ID_FIRD01
+#define PCI_DEVICE_ID_FIRD01 	0x0d01
+#endif
+
+struct OboeSlot
+{
+  __u16 len;                    /*Tweleve bits of packet length */
+  __u8 unused;
+  __u8 control;                 /*Slot control/status see below */
+  __u32 address;                /*Slot buffer address */
+}
+__attribute__ ((packed));
+
+#define OBOE_NTASKS OBOE_TXRING_OFFSET_IN_SLOTS
+
+struct OboeRing
+{
+  struct OboeSlot rx[OBOE_NTASKS];
+  struct OboeSlot tx[OBOE_NTASKS];
+};
+
+#define OBOE_RING_LEN (sizeof(struct OboeRing))
+
+
+#define OBOE_CTL_TX_HW_OWNS	0x80 /*W/R This slot owned by the hardware */
+#define OBOE_CTL_TX_DISTX_CRC	0x40 /*W Disable CRC generation for [FM]IR */
+#define OBOE_CTL_TX_BAD_CRC     0x20 /*W Generate bad CRC */
+#define OBOE_CTL_TX_SIP		0x10   /*W Generate an SIP after xmittion */
+#define OBOE_CTL_TX_MKUNDER	0x08 /*W Generate an underrun error */
+#define OBOE_CTL_TX_RTCENTX	0x04 /*W Enable receiver and generate TXdone */
+     /*  After this slot is processed        */
+#define OBOE_CTL_TX_UNDER	0x01  /*R Set by hardware to indicate underrun */
+
+
+#define OBOE_CTL_RX_HW_OWNS	0x80 /*W/R This slot owned by hardware */
+#define OBOE_CTL_RX_PHYERR	0x40 /*R Decoder error on receiption */
+#define OBOE_CTL_RX_CRCERR	0x20 /*R CRC error only set for [FM]IR */
+#define OBOE_CTL_RX_LENGTH	0x10 /*R Packet > max Rx length  */
+#define OBOE_CTL_RX_OVER	0x08   /*R set to indicate an overflow */
+#define OBOE_CTL_RX_SIRBAD	0x04 /*R SIR had BOF in packet or ABORT sequence */
+#define OBOE_CTL_RX_RXEOF	0x02  /*R Finished receiving on this slot */
+
+
+struct toshoboe_cb
+{
+  struct net_device *netdev;    /* Yes! we are some kind of netdevice */
+  struct net_device_stats stats;
+  struct tty_driver ttydev;
+
+  struct irlap_cb *irlap;       /* The link layer we are binded to */
+
+  chipio_t io;                  /* IrDA controller information */
+  struct qos_info qos;          /* QoS capabilities for this device */
+
+  __u32 flags;                  /* Interface flags */
+
+  struct pci_dev *pdev;         /*PCI device */
+  int base;                     /*IO base */
+
+
+  int txpending;                /*how many tx's are pending */
+  int txs, rxs;                 /*Which slots are we at  */
+
+  int irdad;                    /*Driver under control of netdev end  */
+  int async;                    /*Driver under control of async end   */
+
+
+  int stopped;                  /*Stopped by some or other APM stuff */
+
+  int filter;                   /*In SIR mode do we want to receive
+                                   frames or byte ranges */
+
+  void *ringbuf;                /*The ring buffer */
+  struct OboeRing *ring;        /*The ring */
+
+  void *tx_bufs[OBOE_RING_MAX_SIZE]; /*The buffers   */
+  void *rx_bufs[OBOE_RING_MAX_SIZE];
+
+
+  int speed;                    /*Current setting of the speed */
+  int new_speed;                /*Set to request a speed change */
+
+/* The spinlock protect critical parts of the driver.
+ *	Locking is done like this :
+ *		spin_lock_irqsave(&self->spinlock, flags);
+ *	Releasing the lock :
+ *		spin_unlock_irqrestore(&self->spinlock, flags);
+ */
+  spinlock_t spinlock;		
+  /* Used for the probe and diagnostics code */
+  int int_rx;
+  int int_tx;
+  int int_txunder;
+  int int_rxover;
+  int int_sip;
+};
+
+
+#endif
diff -urN linux-2.4.21-pre2/drivers/net/mii.c linux-2.4.21-pre3/drivers/net/mii.c
--- linux-2.4.21-pre2/drivers/net/mii.c	Mon Jan  6 16:32:28 2003
+++ linux-2.4.21-pre3/drivers/net/mii.c	Mon Jan  6 16:32:32 2003
@@ -124,13 +124,13 @@
 		/* advertise only what has been requested */
 		advert = mii->mdio_read(dev, mii->phy_id, MII_ADVERTISE);
 		tmp = advert & ~(ADVERTISE_ALL | ADVERTISE_100BASE4);
-		if (ADVERTISED_10baseT_Half)
+		if (ecmd->advertising & ADVERTISED_10baseT_Half)
 			tmp |= ADVERTISE_10HALF;
-		if (ADVERTISED_10baseT_Full)
+		if (ecmd->advertising & ADVERTISED_10baseT_Full)
 			tmp |= ADVERTISE_10FULL;
-		if (ADVERTISED_100baseT_Half)
+		if (ecmd->advertising & ADVERTISED_100baseT_Half)
 			tmp |= ADVERTISE_100HALF;
-		if (ADVERTISED_100baseT_Full)
+		if (ecmd->advertising & ADVERTISED_100baseT_Full)
 			tmp |= ADVERTISE_100FULL;
 		if (advert != tmp) {
 			mii->mdio_write(dev, mii->phy_id, MII_ADVERTISE, tmp);
diff -urN linux-2.4.21-pre2/drivers/net/wan/Config.in linux-2.4.21-pre3/drivers/net/wan/Config.in
--- linux-2.4.21-pre2/drivers/net/wan/Config.in	Thu Nov 28 15:53:14 2002
+++ linux-2.4.21-pre3/drivers/net/wan/Config.in	Mon Jan  6 16:32:32 2003
@@ -44,9 +44,6 @@
 
    dep_tristate '  Etinc PCISYNC serial board support (EXPERIMENTAL)' CONFIG_DSCC4 m
 
-# FarSite Communications' cards
-
-   tristate '  FarSync T-Series X.21 (and V.35/V.24) cards' CONFIG_FARSYNC
 
 #
 # Lan Media's board. Currently 1000, 1200, 5200, 5245
@@ -66,8 +63,13 @@
 
    tristate '  SyncLink HDLC/SYNCPPP support' CONFIG_SYNCLINK_SYNCPPP
 
-   tristate '  Generic HDLC driver' CONFIG_HDLC
+# Generic HDLC
+
+   tristate '  Generic HDLC layer' CONFIG_HDLC
    if [ "$CONFIG_HDLC" != "n" ]; then
+      bool '    Raw HDLC support' CONFIG_HDLC_RAW
+      bool '    Cisco HDLC support' CONFIG_HDLC_CISCO
+      bool '    Frame Relay support' CONFIG_HDLC_FR
       bool '    Synchronous Point-to-Point Protocol (PPP) support' CONFIG_HDLC_PPP
       if [ "$CONFIG_LAPB" = "m" -a "$CONFIG_HDLC" = "m" -o "$CONFIG_LAPB" = "y" ]; then
 	 bool '    X.25 protocol support' CONFIG_HDLC_X25
@@ -76,6 +78,11 @@
       fi
       dep_tristate '    SDL RISCom/N2 support' CONFIG_N2 $CONFIG_HDLC
       dep_tristate '    Moxa C101 support' CONFIG_C101 $CONFIG_HDLC
+      dep_tristate '    FarSync T-Series support' CONFIG_FARSYNC $CONFIG_HDLC
+      bool '    Debug received/transmitted packets' CONFIG_HDLC_DEBUG_PKT
+      bool '    Debug hard_header routines' CONFIG_HDLC_DEBUG_HARD_HEADER
+      bool '    Debug FECN/BECN conditions' CONFIG_HDLC_DEBUG_ECN
+      bool '    Debug RX/TX packet rings' CONFIG_HDLC_DEBUG_RINGS
    fi
 
    tristate '  Frame relay DLCI support' CONFIG_DLCI
diff -urN linux-2.4.21-pre2/drivers/net/wan/Makefile linux-2.4.21-pre3/drivers/net/wan/Makefile
--- linux-2.4.21-pre2/drivers/net/wan/Makefile	Fri Aug  2 17:39:44 2002
+++ linux-2.4.21-pre3/drivers/net/wan/Makefile	Mon Jan  6 16:32:32 2003
@@ -9,7 +9,7 @@
 
 O_TARGET := wan.o
 
-export-objs =	z85230.o syncppp.o comx.o sdladrv.o cycx_drv.o hdlc.o
+export-objs =	z85230.o syncppp.o comx.o sdladrv.o cycx_drv.o hdlc_generic.o
 list-multi =	wanpipe.o cyclomx.o
 
 wanpipe-objs = sdlamain.o sdla_ft1.o $(wanpipe-y)
@@ -22,6 +22,13 @@
 cyclomx-objs = cycx_main.o $(cyclomx-y)  
 cyclomx-$(CONFIG_CYCLOMX_X25)	+= cycx_x25.o
 
+hdlc-y				:= hdlc_generic.o
+hdlc-$(CONFIG_HDLC_RAW)		+= hdlc_raw.o
+hdlc-$(CONFIG_HDLC_CISCO)	+= hdlc_cisco.o
+hdlc-$(CONFIG_HDLC_FR)		+= hdlc_fr.o
+hdlc-$(CONFIG_HDLC_PPP)		+= hdlc_ppp.o
+hdlc-$(CONFIG_HDLC_X25)		+= hdlc_x25.o
+hdlc-objs			:= $(hdlc-y)
 
 obj-$(CONFIG_HOSTESS_SV11)	+= z85230.o	syncppp.o	hostess_sv11.o
 obj-$(CONFIG_SEALEVEL_4021)	+= z85230.o	syncppp.o	sealevel.o
@@ -64,12 +71,17 @@
 obj-$(CONFIG_LAPBETHER)		+= lapbether.o
 obj-$(CONFIG_SBNI)		+= sbni.o
 obj-$(CONFIG_HDLC)		+= hdlc.o
-obj-$(CONFIG_HDLC_PPP)		+= syncppp.o
+ifeq ($(CONFIG_HDLC_PPP),y)
+  obj-$(CONFIG_HDLC)		+= syncppp.o
+endif
 obj-$(CONFIG_N2)		+= n2.o
 obj-$(CONFIG_C101)		+= c101.o
 
 include $(TOPDIR)/Rules.make
 
+hdlc.o: $(hdlc-objs)
+	$(LD) -r -o $@ $(hdlc-objs)
+
 wanpipe.o: $(wanpipe-objs)
 	$(LD) -r -o $@ $(wanpipe-objs)
 
diff -urN linux-2.4.21-pre2/drivers/net/wan/c101.c linux-2.4.21-pre3/drivers/net/wan/c101.c
--- linux-2.4.21-pre2/drivers/net/wan/c101.c	Thu Sep 13 16:04:43 2001
+++ linux-2.4.21-pre3/drivers/net/wan/c101.c	Mon Jan  6 16:32:32 2003
@@ -1,7 +1,7 @@
 /*
  * Moxa C101 synchronous serial card driver for Linux
  *
- * Copyright (C) 2000 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 2000-2002 Krzysztof Halasa <khc@pm.waw.pl>
  *
  * 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
@@ -15,6 +15,7 @@
  *    Moxa C101 User's Manual
  */
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/slab.h>
@@ -29,10 +30,8 @@
 
 #include "hd64570.h"
 
-#define DEBUG_RINGS
-/* #define DEBUG_PKT */
 
-static const char* version = "Moxa C101 driver revision: 1.02 for Linux 2.4";
+static const char* version = "Moxa C101 driver version: 1.10";
 static const char* devname = "C101";
 
 #define C101_PAGE 0x1D00
@@ -51,12 +50,12 @@
 typedef struct card_s {
 	hdlc_device hdlc;	/* HDLC device struct - must be first */
 	spinlock_t lock;	/* TX lock */
-	int clkmode;		/* clock mode */
-	int clkrate;		/* clock speed */
-	int line;		/* loopback only */
 	u8 *win0base;		/* ISA window base address */
 	u32 phy_winbase;	/* ISA physical base address */
 	u16 buff_offset;	/* offset of first buffer of first channel */
+	sync_serial_settings settings;
+	unsigned short encoding;
+	unsigned short parity;
 	u8 rxs, txs, tmc;	/* SCA registers */
 	u8 irq;			/* IRQ (3-15) */
 	u8 ring_buffers;	/* number of buffers in a ring */
@@ -72,6 +71,9 @@
 
 typedef card_t port_t;
 
+static card_t *first_card;
+static card_t **new_card = &first_card;
+
 
 #define sca_in(reg, card)	   readb((card)->win0base + C101_SCA + (reg))
 #define sca_out(value, reg, card)  writeb(value, (card)->win0base + C101_SCA + (reg))
@@ -105,18 +107,13 @@
 #include "hd6457x.c"
 
 
-static int c101_set_clock(port_t *port, int value)
+static void c101_set_iface(port_t *port)
 {
 	u8 msci = get_msci(port);
 	u8 rxs = port->rxs & CLK_BRG_MASK;
 	u8 txs = port->txs & CLK_BRG_MASK;
 
-	switch(value) {
-	case CLOCK_EXT:
-		rxs |= CLK_LINE_RX; /* RXC input */
-		txs |= CLK_LINE_TX; /* TXC input */
-		break;
-
+	switch(port->settings.clock_type) {
 	case CLOCK_INT:
 		rxs |= CLK_BRG_RX; /* TX clock */
 		txs |= CLK_RXCLK_TX; /* BRG output */
@@ -132,84 +129,100 @@
 		txs |= CLK_RXCLK_TX; /* RX clock */
 		break;
 
-	default:
-		return -EINVAL;
+	default:	/* EXTernal clock */
+		rxs |= CLK_LINE_RX; /* RXC input */
+		txs |= CLK_LINE_TX; /* TXC input */
 	}
 
 	port->rxs = rxs;
 	port->txs = txs;
 	sca_out(rxs, msci + RXS, port);
 	sca_out(txs, msci + TXS, port);
-	port->clkmode = value;
-	return 0;
+	sca_set_port(port);
 }
 
 
-static int c101_open(hdlc_device *hdlc)
+static int c101_open(struct net_device *dev)
 {
+	hdlc_device *hdlc = dev_to_hdlc(dev);
 	port_t *port = hdlc_to_port(hdlc);
+	int result = hdlc_open(hdlc);
+	if (result)
+		return result;
 
 	MOD_INC_USE_COUNT;
 	writeb(1, port->win0base + C101_DTR);
 	sca_out(0, MSCI1_OFFSET + CTL, port); /* RTS uses ch#2 output */
 	sca_open(hdlc);
-	c101_set_clock(port, port->clkmode);
+	c101_set_iface(port);
 	return 0;
 }
 
 
-static void c101_close(hdlc_device *hdlc)
+static int c101_close(struct net_device *dev)
 {
+	hdlc_device *hdlc = dev_to_hdlc(dev);
 	port_t *port = hdlc_to_port(hdlc);
 
 	sca_close(hdlc);
 	writeb(0, port->win0base + C101_DTR);
 	sca_out(CTL_NORTS, MSCI1_OFFSET + CTL, port);
+	hdlc_close(hdlc);
 	MOD_DEC_USE_COUNT;
+	return 0;
 }
 
 
-static int c101_ioctl(hdlc_device *hdlc, struct ifreq *ifr, int cmd)
+static int c101_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 {
-	int value = ifr->ifr_ifru.ifru_ivalue;
-	int result = 0;
+	const size_t size = sizeof(sync_serial_settings);
+	sync_serial_settings new_line, *line = ifr->ifr_settings.ifs_ifsu.sync;
+	hdlc_device *hdlc = dev_to_hdlc(dev);
 	port_t *port = hdlc_to_port(hdlc);
 
-	if(!capable(CAP_NET_ADMIN))
-		return -EPERM;
+#ifdef CONFIG_HDLC_DEBUG_RINGS
+	if (cmd == SIOCDEVPRIVATE) {
+		sca_dump_rings(hdlc);
+		return 0;
+	}
+#endif
+	if (cmd != SIOCWANDEV)
+		return hdlc_ioctl(dev, ifr, cmd);
 
-	switch(cmd) {
-	case HDLCSCLOCK:
-		result = c101_set_clock(port, value);
-	case HDLCGCLOCK:
-		value = port->clkmode;
-		break;
+	switch(ifr->ifr_settings.type) {
+	case IF_GET_IFACE:
+		ifr->ifr_settings.type = IF_IFACE_SYNC_SERIAL;
+		if (ifr->ifr_settings.size < size) {
+			ifr->ifr_settings.size = size; /* data size wanted */
+			return -ENOBUFS;
+		}
+		if (copy_to_user(line, &port->settings, size))
+			return -EFAULT;
+		return 0;
 
-	case HDLCSCLOCKRATE:
-		port->clkrate = value;
-		sca_set_clock(port);
-	case HDLCGCLOCKRATE:
-		value = port->clkrate;
-		break;
+	case IF_IFACE_SYNC_SERIAL:
+		if(!capable(CAP_NET_ADMIN))
+			return -EPERM;
+
+		if (copy_from_user(&new_line, line, size))
+			return -EFAULT;
+
+		if (new_line.clock_type != CLOCK_EXT &&
+		    new_line.clock_type != CLOCK_TXFROMRX &&
+		    new_line.clock_type != CLOCK_INT &&
+		    new_line.clock_type != CLOCK_TXINT)
+		return -EINVAL;	/* No such clock setting */
 
-	case HDLCSLINE:
-		result = sca_set_loopback(port, value);
-	case HDLCGLINE:
-		value = port->line;
-		break;
+		if (new_line.loopback != 0 && new_line.loopback != 1)
+			return -EINVAL;
 
-#ifdef DEBUG_RINGS
-	case HDLCRUN:
-		sca_dump_rings(hdlc);
+		memcpy(&port->settings, &new_line, size); /* Update settings */
+		c101_set_iface(port);
 		return 0;
-#endif /* DEBUG_RINGS */
 
 	default:
-		return -EINVAL;
+		return hdlc_ioctl(dev, ifr, cmd);
 	}
-
-	ifr->ifr_ifru.ifru_ivalue = value;
-	return result;
 }
 
 
@@ -231,6 +244,7 @@
 
 static int c101_run(unsigned long irq, unsigned long winbase)
 {
+	struct net_device *dev;
 	card_t *card;
 	int result;
 
@@ -284,15 +298,19 @@
 
 	sca_init(card, 0);
 
+	dev = hdlc_to_dev(&card->hdlc);
+
 	spin_lock_init(&card->lock);
-	hdlc_to_dev(&card->hdlc)->irq = irq;
-	hdlc_to_dev(&card->hdlc)->mem_start = winbase;
-	hdlc_to_dev(&card->hdlc)->mem_end = winbase + C101_MAPPED_RAM_SIZE - 1;
-	hdlc_to_dev(&card->hdlc)->tx_queue_len = 50;
-	card->hdlc.ioctl = c101_ioctl;
-	card->hdlc.open = c101_open;
-	card->hdlc.close = c101_close;
+	dev->irq = irq;
+	dev->mem_start = winbase;
+	dev->mem_end = winbase + C101_MAPPED_RAM_SIZE - 1;
+	dev->tx_queue_len = 50;
+	dev->do_ioctl = c101_ioctl;
+	dev->open = c101_open;
+	dev->stop = c101_close;
+	card->hdlc.attach = sca_attach;
 	card->hdlc.xmit = sca_xmit;
+	card->settings.clock_type = CLOCK_EXT;
 
 	result = register_hdlc_device(&card->hdlc);
 	if (result) {
@@ -319,7 +337,7 @@
 		return -ENOSYS;	/* no parameters specified, abort */
 	}
 
-	printk(KERN_INFO "%s\n", version);
+	printk(KERN_INFO "%s (SCA-%s)\n", version, sca_version);
 
 	do {
 		unsigned long irq, ram;
diff -urN linux-2.4.21-pre2/drivers/net/wan/dscc4.c linux-2.4.21-pre3/drivers/net/wan/dscc4.c
--- linux-2.4.21-pre2/drivers/net/wan/dscc4.c	Fri Nov  9 13:41:42 2001
+++ linux-2.4.21-pre3/drivers/net/wan/dscc4.c	Mon Jan  6 16:32:32 2003
@@ -1,8 +1,8 @@
 /*
- * drivers/net/wan/dscc4/dscc4_main.c: a DSCC4 HDLC driver for Linux
+ * drivers/net/wan/dscc4/dscc4.c: a DSCC4 HDLC driver for Linux
  *
- * This software may be used and distributed according to the terms of the 
- * GNU General Public License. 
+ * This software may be used and distributed according to the terms of the
+ * GNU General Public License.
  *
  * The author may be reached as romieu@cogenit.fr.
  * Specific bug reports/asian food will be welcome.
@@ -16,11 +16,12 @@
  * I. Board Compatibility
  *
  * This device driver is designed for the Siemens PEB20534 4 ports serial
- * controller as found on Etinc PCISYNC cards. The documentation for the 
+ * controller as found on Etinc PCISYNC cards. The documentation for the
  * chipset is available at http://www.infineon.com:
  * - Data Sheet "DSCC4, DMA Supported Serial Communication Controller with
  * 4 Channels, PEB 20534 Version 2.1, PEF 20534 Version 2.1";
  * - Application Hint "Management of DSCC4 on-chip FIFO resources".
+ * - Errata sheet DS5 (courtesy of Michael Skerritt).
  * Jens David has built an adapter based on the same chipset. Take a look
  * at http://www.afthd.tu-darmstadt.de/~dg1kjd/pciscc4 for a specific
  * driver.
@@ -37,15 +38,13 @@
  *
  * III. Driver operation
  *
- * The rx/tx operations are based on a linked list of descriptor. I haven't
- * tried the start/stop descriptor method as this one looks like the cheapest
- * in terms of PCI manipulation.
+ * The rx/tx operations are based on a linked list of descriptors. The driver
+ * doesn't use HOLD mode any more. HOLD mode is definitely buggy and the more
+ * I tried to fix it, the more it started to look like (convoluted) software
+ * mutation of LxDA method. Errata sheet DS5 suggests to use LxDA: consider
+ * this a rfc2119 MUST.
  *
  * Tx direction
- * Once the data section of the current descriptor processed, the next linked
- * descriptor is loaded if the HOLD bit isn't set in the current descriptor.
- * If HOLD is met, the transmission is stopped until the host unsets it and
- * signals the change via TxPOLL.
  * When the tx ring is full, the xmit routine issues a call to netdev_stop.
  * The device is supposed to be enabled again during an ALLS irq (we could
  * use HI but as it's easy to loose events, it's fscked).
@@ -55,12 +54,6 @@
  * I may implement it some day but it isn't the highest ranked item.
  *
  * IV. Notes
- * The chipset is buggy. Typically, under some specific load patterns (I
- * wouldn't call them "high"), the irq queues and the descriptors look like
- * some event has been lost. Even assuming some fancy PCI feature, it won't 
- * explain the reproductible missing "C" bit in the descriptors. Faking an 
- * irq in the periodic timer isn't really elegant but at least it seems 
- * reliable.
  * The current error (XDU, RFO) recovery code is untested.
  * So far, RDO takes his RX channel down and the right sequence to enable it
  * again is still a mistery. If RDO happens, plan a reboot. More details
@@ -69,21 +62,29 @@
  * suggest it for DCE either but at least one can get some messages instead
  * of a complete instant freeze.
  * Tests are done on Rev. 20 of the silicium. The RDO handling changes with
- * the documentation/chipset releases. An on-line errata would be welcome.
+ * the documentation/chipset releases.
  *
  * TODO:
- * - some trivial error lurk,
- * - the stats are fscked,
+ * - test X25.
  * - use polling at high irq/s,
  * - performance analysis,
  * - endianness.
  *
+ * 2001/12/10	Daniela Squassoni  <daniela@cyclades.com>
+ * - Contribution to support the new generic HDLC layer.
+ *
+ * 2002/01	Ueimor
+ * - old style interface removal
+ * - dscc4_release_ring fix (related to DMA mapping)
+ * - hard_start_xmit fix (hint: TxSizeMax)
+ * - misc crapectomy.
  */
 
 #include <linux/version.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/errno.h>
+#include <linux/list.h>
 #include <linux/ioport.h>
 #include <linux/pci.h>
 #include <linux/kernel.h>
@@ -107,23 +108,37 @@
 #include <linux/hdlc.h>
 
 /* Version */
-static const char version[] = "$Id: dscc4.c,v 1.130 2001/02/25 15:27:34 romieu Exp $\n";
+static const char version[] = "$Id: dscc4.c,v 1.159 2002/04/10 22:05:17 romieu Exp $ for Linux\n";
 static int debug;
+static int quartz;
+
+#define	DRV_NAME	"dscc4"
 
+#undef DSCC4_POLLING
 
 /* Module parameters */
+
 MODULE_AUTHOR("Maintainer: Francois Romieu <romieu@cogenit.fr>");
-MODULE_DESCRIPTION("Siemens PEB20534 PCI Controller");
+MODULE_DESCRIPTION("Siemens PEB20534 PCI Controler");
 MODULE_LICENSE("GPL");
 MODULE_PARM(debug,"i");
+MODULE_PARM_DESC(debug,"Enable/disable extra messages");
+MODULE_PARM(quartz,"i");
+MODULE_PARM_DESC(quartz,"If present, on-board quartz frequency (Hz)");
 
 /* Structures */
+
+struct thingie {
+	int define;
+	u32 bits;
+};
+
 struct TxFD {
 	u32 state;
 	u32 next;
 	u32 data;
 	u32 complete;
-	u32 jiffies; /* more hack to come :o) */
+	u32 jiffies; /* Allows sizeof(TxFD) == sizeof(RxFD) + extra hack */
 };
 
 struct RxFD {
@@ -134,22 +149,24 @@
 	u32 end;
 };
 
-#define DEBUG
-#define DEBUG_PARANOID
-#define TX_RING_SIZE    32
-#define RX_RING_SIZE    32
-#define IRQ_RING_SIZE   64 /* Keep it A multiple of 32 */
-#define TX_TIMEOUT      (HZ/10)
-#define BRR_DIVIDER_MAX 64*0x00008000
-#define dev_per_card	4
-
-#define SOURCE_ID(flags) ((flags >> 28 ) & 0x03)
-#define TO_SIZE(state) ((state >> 16) & 0x1fff)
-#define TO_STATE(len) cpu_to_le32((len & TxSizeMax) << 16)
-#define RX_MAX(len) ((((len) >> 5) + 1) << 5)
-#define SCC_REG_START(id) SCC_START+(id)*SCC_OFFSET
-
-#undef DEBUG
+#define DUMMY_SKB_SIZE		64
+#define TX_LOW			8
+#define TX_RING_SIZE		32
+#define RX_RING_SIZE		32
+#define TX_TOTAL_SIZE		TX_RING_SIZE*sizeof(struct TxFD)
+#define RX_TOTAL_SIZE		RX_RING_SIZE*sizeof(struct RxFD)
+#define IRQ_RING_SIZE		64		/* Keep it a multiple of 32 */
+#define TX_TIMEOUT		(HZ/10)
+#define DSCC4_HZ_MAX		33000000
+#define BRR_DIVIDER_MAX		64*0x00004000	/* Cf errata DS5 p.10 */
+#define dev_per_card		4
+#define SCC_REGISTERS_MAX	23		/* Cf errata DS5 p.4 */
+
+#define SOURCE_ID(flags)	(((flags) >> 28) & 0x03)
+#define TO_SIZE(state)		(((state) >> 16) & 0x1fff)
+#define TO_STATE(len)		cpu_to_le32(((len) & TxSizeMax) << 16)
+#define RX_MAX(len)		((((len) >> 5) + 1) << 5)
+#define SCC_REG_START(dpriv)	(SCC_START+(dpriv->dev_id)*SCC_OFFSET)
 
 struct dscc4_pci_priv {
         u32 *iqcfg;
@@ -157,7 +174,7 @@
         spinlock_t lock;
         struct pci_dev *pdev;
 
-        struct net_device *root;
+        struct dscc4_dev_priv *root;
         dma_addr_t iqcfg_dma;
 	u32 xtal_hz;
 };
@@ -171,34 +188,38 @@
         u32 *iqrx;
         u32 *iqtx;
 
+	/* FIXME: check all the volatile are required */
+        volatile u32 tx_current;
         u32 rx_current;
-        u32 tx_current;
-        u32 iqrx_current;
         u32 iqtx_current;
+        u32 iqrx_current;
 
-        u32 tx_dirty;
-	int bad_tx_frame;
-	int bad_rx_frame;
-	int rx_needs_refill;
+        volatile u32 tx_dirty;
+        volatile u32 ltda;
+        u32 rx_dirty;
+        u32 lrda;
 
         dma_addr_t tx_fd_dma;
         dma_addr_t rx_fd_dma;
         dma_addr_t iqtx_dma;
         dma_addr_t iqrx_dma;
 
-        struct net_device_stats stats;
+	u32 scc_regs[SCC_REGISTERS_MAX]; /* Cf errata DS5 p.4 */
+
 	struct timer_list timer;
 
         struct dscc4_pci_priv *pci_priv;
         spinlock_t lock;
 
         int dev_id;
-	u32 flags;
+	volatile u32 flags;
 	u32 timer_help;
-	u32 hi_expected;
 
-	struct hdlc_device_struct hdlc;
-	int usecount;
+	unsigned short encoding;
+	unsigned short parity;
+	hdlc_device hdlc;
+	sync_serial_settings settings;
+	u32 __pad __attribute__ ((aligned (4)));
 };
 
 /* GLOBAL registers definitions */
@@ -217,6 +238,10 @@
 #define CH0CFG  0x50
 #define CH0BRDA 0x54
 #define CH0BTDA 0x58
+#define CH0FRDA 0x98
+#define CH0FTDA 0xb0
+#define CH0LRDA 0xc8
+#define CH0LTDA 0xe0
 
 /* SCC registers definitions */
 #define SCC_START	0x0100
@@ -232,8 +257,11 @@
 #define ISR     0x58
 
 /* Bit masks */
-#define IntRxScc0       0x10000000
-#define IntTxScc0       0x01000000
+#define EncodingMask	0x00700000
+#define CrcMask		0x00000003
+
+#define IntRxScc0	0x10000000
+#define IntTxScc0	0x01000000
 
 #define TxPollCmd	0x00000400
 #define RxActivate	0x08000000
@@ -242,22 +270,24 @@
 #define Rdt		0x00200000
 #define Idr		0x00100000
 #define Idt		0x00080000
-#define TxSccRes       0x01000000
-#define RxSccRes       0x00010000
-#define TxSizeMax	0x1ffc
-#define RxSizeMax	0x1ffc
+#define TxSccRes	0x01000000
+#define RxSccRes	0x00010000
+#define TxSizeMax	0x1fff
 
 #define Ccr0ClockMask	0x0000003f
 #define Ccr1LoopMask	0x00000200
+#define IsrMask		0x000fffff
 #define BrrExpMask	0x00000f00
 #define BrrMultMask	0x0000003f
 #define EncodingMask	0x00700000
 #define Hold		0x40000000
 #define SccBusy		0x10000000
+#define PowerUp		0x80000000
 #define FrameOk		(FrameVfr | FrameCrc)
 #define FrameVfr	0x80
 #define FrameRdo	0x40
 #define FrameCrc	0x20
+#define FrameRab	0x10
 #define FrameAborted	0x00000200
 #define FrameEnd	0x80000000
 #define DataComplete	0x40000000
@@ -272,10 +302,12 @@
 #define TxEvt		0x0f000000
 #define Alls		0x00040000
 #define Xdu		0x00010000
+#define Cts		0x00004000
 #define Xmr		0x00002000
 #define Xpr		0x00001000
 #define Rdo		0x00000080
 #define Rfs		0x00000040
+#define Cd		0x00000004
 #define Rfo		0x00000002
 #define Flex		0x00000001
 
@@ -293,184 +325,352 @@
 #define RdoSet		0x00000004
 
 /* Functions prototypes */
-static __inline__ void dscc4_rx_irq(struct dscc4_pci_priv *, struct net_device *);
-static __inline__ void dscc4_tx_irq(struct dscc4_pci_priv *, struct net_device *);
+static inline void dscc4_rx_irq(struct dscc4_pci_priv *, struct dscc4_dev_priv *);
+static inline void dscc4_tx_irq(struct dscc4_pci_priv *, struct dscc4_dev_priv *);
 static int dscc4_found1(struct pci_dev *, unsigned long ioaddr);
 static int dscc4_init_one(struct pci_dev *, const struct pci_device_id *ent);
 static int dscc4_open(struct net_device *);
 static int dscc4_start_xmit(struct sk_buff *, struct net_device *);
 static int dscc4_close(struct net_device *);
 static int dscc4_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
-static int dscc4_change_mtu(struct net_device *dev, int mtu);
 static int dscc4_init_ring(struct net_device *);
 static void dscc4_release_ring(struct dscc4_dev_priv *);
 static void dscc4_timer(unsigned long);
 static void dscc4_tx_timeout(struct net_device *);
 static void dscc4_irq(int irq, void *dev_id, struct pt_regs *ptregs);
-static struct net_device_stats *dscc4_get_stats(struct net_device *);
-static int dscc4_attach_hdlc_device(struct net_device *);
-static void dscc4_unattach_hdlc_device(struct net_device *);
-static int dscc4_hdlc_open(struct hdlc_device_struct *);
-static void dscc4_hdlc_close(struct hdlc_device_struct *);
-static int dscc4_hdlc_ioctl(struct hdlc_device_struct *, struct ifreq *, int);
-static int dscc4_hdlc_xmit(hdlc_device *, struct sk_buff *);
-#ifdef EXPERIMENTAL_POLLING
+static int dscc4_hdlc_attach(hdlc_device *, unsigned short, unsigned short);
+static int dscc4_set_iface(struct dscc4_dev_priv *, struct net_device *);
+static inline int dscc4_set_quartz(struct dscc4_dev_priv *, int);
+#ifdef DSCC4_POLLING
 static int dscc4_tx_poll(struct dscc4_dev_priv *, struct net_device *);
 #endif
 
-void inline reset_TxFD(struct TxFD *tx_fd) {
-	/* FIXME: test with the last arg (size specification) = 0 */
-	tx_fd->state = FrameEnd | Hold | 0x00100000;
-	tx_fd->complete = 0x00000000;
+static inline struct dscc4_dev_priv *dscc4_priv(struct net_device *dev)
+{
+	return list_entry(dev, struct dscc4_dev_priv, hdlc.netdev);
 }
 
-void inline dscc4_release_ring_skbuff(struct sk_buff **p, int n)
+static void scc_patchl(u32 mask, u32 value, struct dscc4_dev_priv *dpriv,
+			struct net_device *dev, int offset)
 {
-	for(; n > 0; n--) {
-		if (*p)
-			dev_kfree_skb(*p);
-		p++;
+	u32 state;
+
+	/* Cf scc_writel for concern regarding thread-safety */
+	state = dpriv->scc_regs[offset >> 2];
+	state &= ~mask;
+	state |= value;
+	dpriv->scc_regs[offset >> 2] = state;
+	writel(state, dev->base_addr + SCC_REG_START(dpriv) + offset);
+}
+
+static void scc_writel(u32 bits, struct dscc4_dev_priv *dpriv,
+		       struct net_device *dev, int offset)
+{
+	/*
+	 * Thread-UNsafe.
+	 * As of 2002/02/16, there are no thread racing for access.
+	 */
+	dpriv->scc_regs[offset >> 2] = bits;
+	writel(bits, dev->base_addr + SCC_REG_START(dpriv) + offset);
+}
+
+static inline u32 scc_readl(struct dscc4_dev_priv *dpriv, int offset)
+{
+	return dpriv->scc_regs[offset >> 2];
+}
+
+static u32 scc_readl_star(struct dscc4_dev_priv *dpriv, struct net_device *dev)
+{
+	/* Cf errata DS5 p.4 */
+	readl(dev->base_addr + SCC_REG_START(dpriv) + STAR);
+	return readl(dev->base_addr + SCC_REG_START(dpriv) + STAR);
+}
+
+static inline void dscc4_do_tx(struct dscc4_dev_priv *dpriv,
+			       struct net_device *dev)
+{
+	dpriv->ltda = dpriv->tx_fd_dma +
+                      ((dpriv->tx_current-1)%TX_RING_SIZE)*sizeof(struct TxFD);
+	writel(dpriv->ltda, dev->base_addr + CH0LTDA + dpriv->dev_id*4);
+	/* Flush posted writes *NOW* */
+	readl(dev->base_addr + CH0LTDA + dpriv->dev_id*4);
+}
+
+static inline void dscc4_rx_update(struct dscc4_dev_priv *dpriv,
+				   struct net_device *dev)
+{
+	dpriv->lrda = dpriv->rx_fd_dma +
+		      ((dpriv->rx_dirty - 1)%RX_RING_SIZE)*sizeof(struct RxFD);
+	writel(dpriv->lrda, dev->base_addr + CH0LRDA + dpriv->dev_id*4);
+}
+
+static inline unsigned int dscc4_tx_done(struct dscc4_dev_priv *dpriv)
+{
+	return dpriv->tx_current == dpriv->tx_dirty;
+}
+
+static inline unsigned int dscc4_tx_quiescent(struct dscc4_dev_priv *dpriv,
+					      struct net_device *dev)
+{
+	return readl(dev->base_addr + CH0FTDA + dpriv->dev_id*4) == dpriv->ltda;
+}
+
+int state_check(u32 state, struct dscc4_dev_priv *dpriv, struct net_device *dev,
+		const char *msg)
+{
+	int ret = 0;
+
+	if (debug > 1) {
+	if (SOURCE_ID(state) != dpriv->dev_id) {
+		printk(KERN_DEBUG "%s (%s): Source Id=%d, state=%08x\n",
+		       dev->name, msg, SOURCE_ID(state), state );
+			ret = -1;
+	}
+	if (state & 0x0df80c00) {
+		printk(KERN_DEBUG "%s (%s): state=%08x (UFO alert)\n",
+		       dev->name, msg, state);
+			ret = -1;
 	}
+	}
+	return ret;
+}
+
+void dscc4_tx_print(struct net_device *dev, struct dscc4_dev_priv *dpriv,
+		    char *msg)
+{
+	printk(KERN_DEBUG "%s: tx_current=%02d tx_dirty=%02d (%s)\n",
+	       dev->name, dpriv->tx_current, dpriv->tx_dirty, msg);
 }
 
 static void dscc4_release_ring(struct dscc4_dev_priv *dpriv)
 {
 	struct pci_dev *pdev = dpriv->pci_priv->pdev;
+	struct TxFD *tx_fd = dpriv->tx_fd;
+	struct RxFD *rx_fd = dpriv->rx_fd;
+	struct sk_buff **skbuff;
+	int i;
+
+	pci_free_consistent(pdev, TX_TOTAL_SIZE, tx_fd, dpriv->tx_fd_dma);
+	pci_free_consistent(pdev, RX_TOTAL_SIZE, rx_fd, dpriv->rx_fd_dma);
+
+	skbuff = dpriv->tx_skbuff;
+	for (i = 0; i < TX_RING_SIZE; i++) {
+		if (*skbuff) {
+			pci_unmap_single(pdev, tx_fd->data, (*skbuff)->len,
+				PCI_DMA_TODEVICE);
+			dev_kfree_skb(*skbuff);
+		}
+		skbuff++;
+		tx_fd++;
+	}
 
-	pci_free_consistent(pdev, TX_RING_SIZE*sizeof(struct TxFD),
-			    dpriv->tx_fd, dpriv->tx_fd_dma);
-	pci_free_consistent(pdev, RX_RING_SIZE*sizeof(struct RxFD),
-			    dpriv->rx_fd, dpriv->rx_fd_dma);
-	dscc4_release_ring_skbuff(dpriv->tx_skbuff, TX_RING_SIZE);
-	dscc4_release_ring_skbuff(dpriv->rx_skbuff, RX_RING_SIZE);
+	skbuff = dpriv->rx_skbuff;
+	for (i = 0; i < RX_RING_SIZE; i++) {
+		if (*skbuff) {
+			pci_unmap_single(pdev, rx_fd->data, (*skbuff)->len,
+				PCI_DMA_FROMDEVICE);
+			dev_kfree_skb(*skbuff);
+		}
+		skbuff++;
+		rx_fd++;
+	}
 }
 
-void inline try_get_rx_skb(struct dscc4_dev_priv *priv, int cur, struct net_device *dev)
+inline int try_get_rx_skb(struct dscc4_dev_priv *dpriv, struct net_device *dev)
 {
+	unsigned int dirty = dpriv->rx_dirty%RX_RING_SIZE;
+	struct RxFD *rx_fd = dpriv->rx_fd + dirty;
 	struct sk_buff *skb;
+	int ret = 0;
 
-	skb = dev_alloc_skb(RX_MAX(HDLC_MAX_MRU+2));
-	priv->rx_skbuff[cur] = skb;
-	if (!skb) {
-		priv->rx_fd[cur--].data = (u32) NULL;
-		priv->rx_fd[cur%RX_RING_SIZE].state1 |= Hold;
-		priv->rx_needs_refill++;
-		return;
-	}
+	skb = dev_alloc_skb(RX_MAX(HDLC_MAX_MRU));
+	dpriv->rx_skbuff[dirty] = skb;
+	if (skb) {
 	skb->dev = dev;
 	skb->protocol = htons(ETH_P_IP);
 	skb->mac.raw = skb->data;
-	priv->rx_fd[cur].data = pci_map_single(priv->pci_priv->pdev, skb->data,
+		rx_fd->data = pci_map_single(dpriv->pci_priv->pdev, skb->data,
 					       skb->len, PCI_DMA_FROMDEVICE);
+	} else {
+		rx_fd->data = (u32) NULL;
+		ret = -1;
+	}
+	return ret;
 }
 
 /*
  * IRQ/thread/whatever safe
  */
-static int dscc4_wait_ack_cec(u32 ioaddr, struct net_device *dev, char *msg)
+static int dscc4_wait_ack_cec(struct dscc4_dev_priv *dpriv,
+			      struct net_device *dev, char *msg)
 {
-	s16 i = 0;
+	s8 i = 0;
 
-	while (readl(ioaddr + STAR) & SccBusy) {
-		if (i++ < 0)  {
-			printk(KERN_ERR "%s: %s timeout\n", dev->name, msg);
-			return -1;
-		}
-	}
-	printk(KERN_DEBUG "%s: %s ack (%d try)\n", dev->name, msg, i);
-	return 0;
+	do {
+		if (!(scc_readl_star(dpriv, dev) & SccBusy)) {
+			printk(KERN_DEBUG "%s: %s ack (%d try)\n", dev->name,
+			       msg, i);
+			goto done;
+		}
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(10);
+		rmb();
+	} while (++i > 0);
+	printk(KERN_ERR "%s: %s timeout\n", dev->name, msg);
+done:
+	return (i >= 0) ? i : -EAGAIN;
 }
 
 static int dscc4_do_action(struct net_device *dev, char *msg)
 {
 	unsigned long ioaddr = dev->base_addr;
-	u32 state;
-	s16 i;
+	s16 i = 0;
 
 	writel(Action, ioaddr + GCMDR);
 	ioaddr += GSTAR;
-	for (i = 0; i >= 0; i++) {
-		state = readl(ioaddr);
-		if (state & Arf) {
+	do {
+		u32 state = readl(ioaddr);
+
+		if (state & ArAck) {
+			printk(KERN_DEBUG "%s: %s ack\n", dev->name, msg);
+			writel(ArAck, ioaddr);
+			goto done;
+		} else if (state & Arf) {
 			printk(KERN_ERR "%s: %s failed\n", dev->name, msg);
 			writel(Arf, ioaddr);
-			return -1;
-		} else if (state & ArAck) {
-			printk(KERN_DEBUG "%s: %s ack (%d try)\n",
-			       dev->name, msg, i);
-			writel(ArAck, ioaddr);
-			return 0;
-		}
+			i = -1;
+			goto done;
 	}
+		rmb();
+	} while (++i > 0);
 	printk(KERN_ERR "%s: %s timeout\n", dev->name, msg);
-	return -1;
+done:
+	return i;
 }
 
-static __inline__ int dscc4_xpr_ack(struct dscc4_dev_priv *dpriv)
+static inline int dscc4_xpr_ack(struct dscc4_dev_priv *dpriv)
 {
-	int cur;
-	s16 i;
+	int cur = dpriv->iqtx_current%IRQ_RING_SIZE;
+	s8 i = 0;
 
-	cur = dpriv->iqtx_current%IRQ_RING_SIZE;
-	for (i = 0; i >= 0; i++) {
+	do {
 		if (!(dpriv->flags & (NeedIDR | NeedIDT)) ||
 		    (dpriv->iqtx[cur] & Xpr))
-			return 0;
+			break;
+		smp_rmb();
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(10);
+	} while (++i > 0);
+
+	return (i >= 0 ) ? i : -EAGAIN;
+}
+
+/* Requires protection against interrupt */
+static void dscc4_rx_reset(struct dscc4_dev_priv *dpriv, struct net_device *dev)
+{
+	/* Cf errata DS5 p.6 */
+	writel(0x00000000, dev->base_addr + CH0LRDA + dpriv->dev_id*4);
+	scc_writel(~PowerUp & scc_readl(dpriv, CCR0), dpriv, dev, CCR0);
+	readl(dev->base_addr + CH0LRDA + dpriv->dev_id*4);
+	writel(MTFi|Rdr, dev->base_addr + dpriv->dev_id*0x0c + CH0CFG);
+	writel(Action, dev->base_addr + GCMDR);
+}
+
+static void dscc4_tx_reset(struct dscc4_dev_priv *dpriv, struct net_device *dev)
+{
+	u16 i = 0;
+
+	/* Cf errata DS5 p.7 */
+	scc_writel(~PowerUp & scc_readl(dpriv, CCR0), dpriv, dev, CCR0);
+	scc_writel(0x00050000, dpriv, dev, CCR2);
+	/*
+	 * Must be longer than the time required to fill the fifo.
+	 */
+	while (!dscc4_tx_quiescent(dpriv, dev) && ++i) {
+		udelay(1);
+		wmb();
 	}
-	printk(KERN_ERR "%s: %s timeout\n", "dscc4", "XPR");
-	return -1;
+
+	writel(MTFi|Rdt, dev->base_addr + dpriv->dev_id*0x0c + CH0CFG);
+	if (dscc4_do_action(dev, "Rdt") < 0)
+		printk(KERN_ERR "%s: Tx reset failed\n", dev->name);
 }
 
-static __inline__ void dscc4_rx_skb(struct dscc4_dev_priv *dpriv, int cur,
-	struct RxFD *rx_fd, struct net_device *dev)
+/* TODO: (ab)use this function to refill a completely depleted RX ring. */
+static inline void dscc4_rx_skb(struct dscc4_dev_priv *dpriv,
+				struct net_device *dev)
 {
+	struct RxFD *rx_fd = dpriv->rx_fd + dpriv->rx_current%RX_RING_SIZE;
+	struct net_device_stats *stats = &dpriv->hdlc.stats;
 	struct pci_dev *pdev = dpriv->pci_priv->pdev;
 	struct sk_buff *skb;
 	int pkt_len;
 
-	skb = dpriv->rx_skbuff[cur];
-	pkt_len = TO_SIZE(rx_fd->state2) - 1;
-	pci_dma_sync_single(pdev, rx_fd->data, pkt_len + 1, PCI_DMA_FROMDEVICE);
-	if((skb->data[pkt_len] & FrameOk) == FrameOk) {
-		pci_unmap_single(pdev, rx_fd->data, skb->len, PCI_DMA_FROMDEVICE);
-		dpriv->stats.rx_packets++;
-		dpriv->stats.rx_bytes += pkt_len;
+	skb = dpriv->rx_skbuff[dpriv->rx_current++%RX_RING_SIZE];
+	if (!skb) {
+		printk(KERN_DEBUG "%s: skb=0 (%s)\n", dev->name, __FUNCTION__);
+		goto refill;
+	}
+	pkt_len = TO_SIZE(rx_fd->state2);
+	pci_dma_sync_single(pdev, rx_fd->data, pkt_len, PCI_DMA_FROMDEVICE);
+	pci_unmap_single(pdev, rx_fd->data, pkt_len, PCI_DMA_FROMDEVICE);
+	if ((skb->data[--pkt_len] & FrameOk) == FrameOk) {
+		stats->rx_packets++;
+		stats->rx_bytes += pkt_len;
 		skb->tail += pkt_len;
 		skb->len = pkt_len;
-       		if (netif_running(hdlc_to_dev(&dpriv->hdlc)))
-			hdlc_netif_rx(&dpriv->hdlc, skb);
-		else
-			netif_rx(skb);
-		try_get_rx_skb(dpriv, cur, dev);
+       	if (netif_running(dev))
+			skb->protocol = htons(ETH_P_HDLC);
+		skb->dev->last_rx = jiffies;
+		netif_rx(skb);
 	} else {
-		if(skb->data[pkt_len] & FrameRdo)
-			dpriv->stats.rx_fifo_errors++;
-		else if(!(skb->data[pkt_len] | ~FrameCrc))
-			dpriv->stats.rx_crc_errors++;
-		else if(!(skb->data[pkt_len] | ~FrameVfr))
-			dpriv->stats.rx_length_errors++;
+		if (skb->data[pkt_len] & FrameRdo)
+			stats->rx_fifo_errors++;
+		else if (!(skb->data[pkt_len] | ~FrameCrc))
+			stats->rx_crc_errors++;
+		else if (!(skb->data[pkt_len] | ~(FrameVfr | FrameRab)))
+			stats->rx_length_errors++;
 		else
-			dpriv->stats.rx_errors++;
+			stats->rx_errors++;
+		dev_kfree_skb_irq(skb);
+	}
+refill:
+	while ((dpriv->rx_dirty - dpriv->rx_current) % RX_RING_SIZE) {
+		if (try_get_rx_skb(dpriv, dev) < 0)
+			break;
+		dpriv->rx_dirty++;
 	}
-	rx_fd->state1 |= Hold;
+	dscc4_rx_update(dpriv, dev);
 	rx_fd->state2 = 0x00000000;
 	rx_fd->end = 0xbabeface;
-	if (!rx_fd->data)
-		return;
-	rx_fd--;
-	if (!cur)
-		rx_fd += RX_RING_SIZE;
-	rx_fd->state1 &= ~Hold;
 }
 
-static int __init dscc4_init_one (struct pci_dev *pdev,
+static void dscc4_free1(struct pci_dev *pdev)
+{
+	struct dscc4_pci_priv *ppriv;
+	struct dscc4_dev_priv *root;
+	int i;
+
+	ppriv = pci_get_drvdata(pdev);
+	root = ppriv->root;
+
+	for (i = 0; i < dev_per_card; i++)
+		unregister_hdlc_device(&root[i].hdlc);
+
+	pci_set_drvdata(pdev, NULL);
+
+	kfree(root);
+	kfree(ppriv);
+}
+
+static int __init dscc4_init_one(struct pci_dev *pdev,
 				  const struct pci_device_id *ent)
 {
 	struct dscc4_pci_priv *priv;
 	struct dscc4_dev_priv *dpriv;
-	int i;
 	static int cards_found = 0;
 	unsigned long ioaddr;
+	int i;
 
 	printk(KERN_DEBUG "%s", version);
 
@@ -478,27 +678,30 @@
 		goto err_out;
 	if (!request_mem_region(pci_resource_start(pdev, 0),
 	                	pci_resource_len(pdev, 0), "registers")) {
-	        printk (KERN_ERR "dscc4: can't reserve MMIO region (regs)\n");
+	        printk(KERN_ERR "%s: can't reserve MMIO region (regs)\n",
+			DRV_NAME);
 	        goto err_out;
 	}
 	if (!request_mem_region(pci_resource_start(pdev, 1),
 	                        pci_resource_len(pdev, 1), "LBI interface")) {
-	        printk (KERN_ERR "dscc4: can't reserve MMIO region (lbi)\n");
+	        printk(KERN_ERR "%s: can't reserve MMIO region (lbi)\n",
+			DRV_NAME);
 	        goto err_out_free_mmio_region0;
 	}
 	ioaddr = (unsigned long)ioremap(pci_resource_start(pdev, 0),
 					pci_resource_len(pdev, 0));
 	if (!ioaddr) {
-		printk(KERN_ERR "dscc4: cannot remap MMIO region %lx @ %lx\n",
-	                pci_resource_len(pdev, 0), pci_resource_start(pdev, 0));
+		printk(KERN_ERR "%s: cannot remap MMIO region %lx @ %lx\n",
+			DRV_NAME, pci_resource_len(pdev, 0),
+			pci_resource_start(pdev, 0));
 		goto err_out_free_mmio_region;
 	}
-	printk(KERN_DEBUG "Siemens DSCC4, MMIO at %#lx (regs), %#lx (lbi), IRQ %d.\n",
+	printk(KERN_DEBUG "Siemens DSCC4, MMIO at %#lx (regs), %#lx (lbi), IRQ %d\n",
 	        pci_resource_start(pdev, 0),
 	        pci_resource_start(pdev, 1), pdev->irq);
 
-	/* High PCI latency useless. Cf app. note. */
-	pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0x10);
+	/* Cf errata DS5 p.2 */
+	pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xf8);
 	pci_set_master(pdev);
 
 	if (dscc4_found1(pdev, ioaddr))
@@ -506,14 +709,13 @@
 
 	priv = (struct dscc4_pci_priv *)pci_get_drvdata(pdev);
 
-	if (request_irq(pdev->irq, &dscc4_irq, SA_SHIRQ, "dscc4", priv->root)) {
-		printk(KERN_WARNING "dscc4: IRQ %d is busy\n", pdev->irq);
-		goto err_out_iounmap;
+	if (request_irq(pdev->irq, &dscc4_irq, SA_SHIRQ, DRV_NAME, priv->root)){
+		printk(KERN_WARNING "%s: IRQ %d busy\n", DRV_NAME, pdev->irq);
+		goto err_out_free1;
 	}
-	priv->pdev = pdev;
 
-	/* power up/little endian/dma core controlled via hold bit */
-	writel(0x00000000, ioaddr + GMODE);
+	/* power up/little endian/dma core controlled via lrda/ltda */
+	writel(0x00000001, ioaddr + GMODE);
 	/* Shared interrupt queue */
 	{
 		u32 bits;
@@ -532,20 +734,20 @@
 		goto err_out_free_irq;
 	writel(priv->iqcfg_dma, ioaddr + IQCFG);
 
-	/* 
-	 * SCC 0-3 private rx/tx irq structures 
+	/*
+	 * SCC 0-3 private rx/tx irq structures
 	 * IQRX/TXi needs to be set soon. Learned it the hard way...
 	 */
-	for(i = 0; i < dev_per_card; i++) {
-		dpriv = (struct dscc4_dev_priv *)(priv->root + i)->priv;
+	for (i = 0; i < dev_per_card; i++) {
+		dpriv = priv->root + i;
 		dpriv->iqtx = (u32 *) pci_alloc_consistent(pdev,
 			IRQ_RING_SIZE*sizeof(u32), &dpriv->iqtx_dma);
 		if (!dpriv->iqtx)
 			goto err_out_free_iqtx;
 		writel(dpriv->iqtx_dma, ioaddr + IQTX0 + i*4);
 	}
-	for(i = 0; i < dev_per_card; i++) {
-		dpriv = (struct dscc4_dev_priv *)(priv->root + i)->priv;
+	for (i = 0; i < dev_per_card; i++) {
+		dpriv = priv->root + i;
 		dpriv->iqrx = (u32 *) pci_alloc_consistent(pdev,
 			IRQ_RING_SIZE*sizeof(u32), &dpriv->iqrx_dma);
 		if (!dpriv->iqrx)
@@ -553,10 +755,7 @@
 		writel(dpriv->iqrx_dma, ioaddr + IQRX0 + i*4);
 	}
 
-	/* 
-	 * Cf application hint. Beware of hard-lock condition on 
-	 * threshold .
-	 */
+	/* Cf application hint. Beware of hard-lock condition on threshold. */
 	writel(0x42104000, ioaddr + FIFOCR1);
 	//writel(0x9ce69800, ioaddr + FIFOCR2);
 	writel(0xdef6d800, ioaddr + FIFOCR2);
@@ -572,21 +771,23 @@
 
 err_out_free_iqrx:
 	while (--i >= 0) {
-		dpriv = (struct dscc4_dev_priv *)(priv->root + i)->priv;
-		pci_free_consistent(pdev, IRQ_RING_SIZE*sizeof(u32), 
+		dpriv = priv->root + i;
+		pci_free_consistent(pdev, IRQ_RING_SIZE*sizeof(u32),
 				    dpriv->iqrx, dpriv->iqrx_dma);
 	}
 	i = dev_per_card;
 err_out_free_iqtx:
 	while (--i >= 0) {
-		dpriv = (struct dscc4_dev_priv *)(priv->root + i)->priv;
-		pci_free_consistent(pdev, IRQ_RING_SIZE*sizeof(u32), 
+		dpriv = priv->root + i;
+		pci_free_consistent(pdev, IRQ_RING_SIZE*sizeof(u32),
 				    dpriv->iqtx, dpriv->iqtx_dma);
 	}
-	pci_free_consistent(pdev, IRQ_RING_SIZE*sizeof(u32), priv->iqcfg, 
+	pci_free_consistent(pdev, IRQ_RING_SIZE*sizeof(u32), priv->iqcfg,
 			    priv->iqcfg_dma);
 err_out_free_irq:
 	free_irq(pdev->irq, priv->root);
+err_out_free1:
+	dscc4_free1(pdev);
 err_out_iounmap:
 	iounmap ((void *)ioaddr);
 err_out_free_mmio_region:
@@ -599,140 +800,118 @@
 	return -ENODEV;
 };
 
+/*
+ * Let's hope the default values are decent enough to protect my
+ * feet from the user's gun - Ueimor
+ */
+static void dscc4_init_registers(struct dscc4_dev_priv *dpriv,
+				 struct net_device *dev)
+{
+	scc_writel(0x80001000, dpriv, dev, CCR0);
+
+	scc_writel(LengthCheck | (HDLC_MAX_MRU >> 5), dpriv, dev, RLCR);
+
+	/*
+	 * No address recognition/crc-CCITT/cts enabled
+	 * Shared flags transmission disabled - cf errata DS5 p.11
+	 * Carrier detect disabled - cf errata p.14
+	 */
+	scc_writel(0x021c8000, dpriv, dev, CCR1);
+
+	/* crc not forwarded - Cf errata DS5 p.11 */
+	scc_writel(0x00050008 & ~RxActivate, dpriv, dev, CCR2);
+	// crc forwarded
+	//scc_writel(0x00250008 & ~RxActivate, dpriv, dev, CCR2);
+
+	/* Don't mask RDO. Ever. */
+#ifdef DSCC4_POLLING
+	scc_writel(0xfffeef7f, dpriv, dev, IMR); /* Interrupt mask */
+#else
+	//scc_writel(0xfffaef7f, dpriv, dev, IMR); /* Interrupt mask */
+	//scc_writel(0xfffaef7e, dpriv, dev, IMR); /* Interrupt mask */
+	scc_writel(0xfffa8f7a, dpriv, dev, IMR); /* Interrupt mask */
+#endif
+}
+
 static int dscc4_found1(struct pci_dev *pdev, unsigned long ioaddr)
 {
 	struct dscc4_pci_priv *ppriv;
-	struct dscc4_dev_priv *dpriv;
-	struct net_device *dev;
+	struct dscc4_dev_priv *root;
 	int i = 0;
 
-	dpriv = (struct dscc4_dev_priv *)
-		kmalloc(dev_per_card*sizeof(struct dscc4_dev_priv), GFP_KERNEL);
-	if (!dpriv) {
-		printk(KERN_ERR "dscc4: can't allocate data\n");
+	root = (struct dscc4_dev_priv *)
+		kmalloc(dev_per_card*sizeof(*root), GFP_KERNEL);
+	if (!root) {
+		printk(KERN_ERR "%s: can't allocate data\n", DRV_NAME);
 		goto err_out;
 	}
-	memset(dpriv, 0, dev_per_card*sizeof(struct dscc4_dev_priv));
-
-	dev = (struct net_device *)
-	      kmalloc(dev_per_card*sizeof(struct net_device), GFP_KERNEL);
-	if (!dev) {
-		printk(KERN_ERR "dscc4: can't allocate net_device\n");
-		goto err_dealloc_priv;
-	}
-	memset(dev, 0, dev_per_card*sizeof(struct net_device));
+	memset(root, 0, dev_per_card*sizeof(*root));
 
-	ppriv = (struct dscc4_pci_priv *)
-		kmalloc(sizeof(struct dscc4_pci_priv), GFP_KERNEL);
+	ppriv = (struct dscc4_pci_priv *) kmalloc(sizeof(*ppriv), GFP_KERNEL);
 	if (!ppriv) {
-		printk(KERN_ERR "dscc4: can't allocate pci private data.\n");
-		goto err_dealloc_dev;
+		printk(KERN_ERR "%s: can't allocate private data\n", DRV_NAME);
+		goto err_free_dev;
 	}
 	memset(ppriv, 0, sizeof(struct dscc4_pci_priv));
 
 	for (i = 0; i < dev_per_card; i++) {
-		struct dscc4_dev_priv *p;
-		struct net_device *d;
+		struct dscc4_dev_priv *dpriv = root + i;
+		hdlc_device *hdlc = &dpriv->hdlc;
+		struct net_device *d = hdlc_to_dev(hdlc);
 
-	        d = dev + i;
 	        d->base_addr = ioaddr;
 		d->init = NULL;
 	        d->irq = pdev->irq;
-		/* The card adds the crc */
-		d->type = ARPHRD_RAWHDLC;
 	        d->open = dscc4_open;
 	        d->stop = dscc4_close;
-	        d->hard_start_xmit = dscc4_start_xmit;
 		d->set_multicast_list = NULL;
 	        d->do_ioctl = dscc4_ioctl;
-		d->get_stats = dscc4_get_stats;
-		d->change_mtu = dscc4_change_mtu;
-	        d->mtu = HDLC_MAX_MTU;
-	        d->flags = IFF_MULTICAST|IFF_POINTOPOINT|IFF_NOARP;
 		d->tx_timeout = dscc4_tx_timeout;
 		d->watchdog_timeo = TX_TIMEOUT;
 
-		p = dpriv + i;
-		p->dev_id = i;
-		p->pci_priv = ppriv;
-		spin_lock_init(&p->lock);
-		d->priv = p;
-
-		if (dev_alloc_name(d, "scc%d")<0) {
-			printk(KERN_ERR "dev_alloc_name failed for scc.\n");
-			goto err_dealloc_dev;
-		}
-	        if (register_netdev(d)) {
-			printk(KERN_ERR "%s: register_netdev != 0.\n", d->name);
-			goto err_dealloc_dev;
+		dpriv->dev_id = i;
+		dpriv->pci_priv = ppriv;
+		spin_lock_init(&dpriv->lock);
+
+		hdlc->xmit = dscc4_start_xmit;
+		hdlc->attach = dscc4_hdlc_attach;
+
+	        if (register_hdlc_device(hdlc)) {
+			printk(KERN_ERR "%s: unable to register\n", DRV_NAME);
+			goto err_unregister;
 	        }
-		dscc4_attach_hdlc_device(d);
+		hdlc->proto = IF_PROTO_HDLC;
 		SET_MODULE_OWNER(d);
-	}
-	ppriv->root = dev;
-	ppriv->pdev = pdev;
+		dscc4_init_registers(dpriv, d);
+		dpriv->parity = PARITY_CRC16_PR0_CCITT;
+		dpriv->encoding = ENCODING_NRZ;
+	}
+	if (dscc4_set_quartz(root, quartz) < 0)
+		goto err_unregister;
+	ppriv->root = root;
 	spin_lock_init(&ppriv->lock);
 	pci_set_drvdata(pdev, ppriv);
 	return 0;
 
-err_dealloc_dev:
+err_unregister:
 	while (--i >= 0)
-		unregister_netdev(dev + i);
-	kfree(dev);
-err_dealloc_priv:
-	kfree(dpriv);
+		unregister_hdlc_device(&root[i].hdlc);
+	kfree(ppriv);
+err_free_dev:
+	kfree(root);
 err_out:
 	return -1;
 };
 
+/* FIXME: get rid of the unneeded code */
 static void dscc4_timer(unsigned long data)
 {
 	struct net_device *dev = (struct net_device *)data;
-	struct dscc4_dev_priv *dpriv;
-	struct dscc4_pci_priv *ppriv;
-
-	dpriv = dev->priv;
-	if (netif_queue_stopped(dev) && 
-	   ((jiffies - dev->trans_start) > TX_TIMEOUT)) {
-		ppriv = dpriv->pci_priv;
-		if (dpriv->iqtx[dpriv->iqtx_current%IRQ_RING_SIZE]) {
-			u32 flags;
-
-			printk(KERN_DEBUG "%s: pending events\n", dev->name);
-			dev->trans_start = jiffies;
-			spin_lock_irqsave(&ppriv->lock, flags);
-			dscc4_tx_irq(ppriv, dev);
-			spin_unlock_irqrestore(&ppriv->lock, flags);
-		} else {
-			struct TxFD *tx_fd;
-			struct sk_buff *skb;
-			int i,j;
+	struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
+//	struct dscc4_pci_priv *ppriv;
 
-			printk(KERN_DEBUG "%s: missing events\n", dev->name);
-			i = dpriv->tx_dirty%TX_RING_SIZE; 
-			j = dpriv->tx_current - dpriv->tx_dirty;
-			dpriv->stats.tx_dropped += j;
-			while(j--) {
-				skb = dpriv->tx_skbuff[i];
-				tx_fd = dpriv->tx_fd + i;
-				if (skb) {
-					dpriv->tx_skbuff[i] = NULL;
-					pci_unmap_single(ppriv->pdev, tx_fd->data, skb->len,
-						 	 PCI_DMA_TODEVICE);
-					dev_kfree_skb_irq(skb);
-				} else 
-					printk(KERN_INFO "%s: hardware on drugs!\n", dev->name);
-				tx_fd->data = 0; /* DEBUG */
-				tx_fd->complete &= ~DataComplete;
-				i++;	
-				i %= TX_RING_SIZE; 
-			}
-			dpriv->tx_dirty = dpriv->tx_current;
-			dev->trans_start = jiffies;
-			netif_wake_queue(dev);
-			printk(KERN_DEBUG "%s: re-enabled\n", dev->name);	
-		}
-	}
+	goto done;
+done:
         dpriv->timer.expires = jiffies + TX_TIMEOUT;
         add_timer(&dpriv->timer);
 }
@@ -740,41 +919,41 @@
 static void dscc4_tx_timeout(struct net_device *dev)
 {
 	/* FIXME: something is missing there */
-};
+}
 
-static int dscc4_open(struct net_device *dev)
+static int dscc4_loopback_check(struct dscc4_dev_priv *dpriv)
 {
-	struct dscc4_dev_priv *dpriv = (struct dscc4_dev_priv *)dev->priv;
-	struct dscc4_pci_priv *ppriv;
-	u32 ioaddr = 0;
+	sync_serial_settings *settings = &dpriv->settings;
 
-	MOD_INC_USE_COUNT;
+	if (settings->loopback && (settings->clock_type != CLOCK_INT)) {
+		struct net_device *dev = hdlc_to_dev(&dpriv->hdlc);
 
-	ppriv = dpriv->pci_priv;
+		printk(KERN_INFO "%s: loopback requires clock\n", dev->name);
+		return -1;
+	}
+	return 0;
+}
 
-	if (dscc4_init_ring(dev))
-		goto err_out;
+static int dscc4_open(struct net_device *dev)
+{
+	struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
+	hdlc_device *hdlc = &dpriv->hdlc;
+	struct dscc4_pci_priv *ppriv;
+	int ret = -EAGAIN;
 
-	ioaddr = dev->base_addr + SCC_REG_START(dpriv->dev_id);
+	if ((dscc4_loopback_check(dpriv) < 0) || !dev->hard_start_xmit)
+		goto err;
 
-	/* FIXME: VIS */
-	writel(readl(ioaddr + CCR0) | 0x80001000, ioaddr + CCR0);
+	if ((ret = hdlc_open(hdlc)))
+		goto err;
 
-	writel(LengthCheck | (HDLC_MAX_MRU >> 5), ioaddr + RLCR);
+	MOD_INC_USE_COUNT;
 
-	/* no address recognition/crc-CCITT/cts enabled */
-	writel(readl(ioaddr + CCR1) | 0x021c8000, ioaddr + CCR1);
+	ppriv = dpriv->pci_priv;
 
-	/* Ccr2.Rac = 0 */
-	writel(0x00050008 & ~RxActivate, ioaddr + CCR2);
+	if ((ret = dscc4_init_ring(dev)))
+		goto err_out;
 
-#ifdef EXPERIMENTAL_POLLING
-	writel(0xfffeef7f, ioaddr + IMR); /* Interrupt mask */
-#else
-	/* Don't mask RDO. Ever. */
-	//writel(0xfffaef7f, ioaddr + IMR); /* Interrupt mask */
-	writel(0xfffaef7e, ioaddr + IMR); /* Interrupt mask */
-#endif
 	/* IDT+IDR during XPR */
 	dpriv->flags = NeedIDR | NeedIDT;
 
@@ -785,26 +964,34 @@
 	 * power-down mode or..." and CCR2.RAC = 1 are two different
 	 * situations.
 	 */
-	if (readl(ioaddr + STAR) & SccBusy) {
+	if (scc_readl_star(dpriv, dev) & SccBusy) {
 		printk(KERN_ERR "%s busy. Try later\n", dev->name);
+		ret = -EAGAIN;
 		goto err_free_ring;
-	}
-	writel(TxSccRes | RxSccRes, ioaddr + CMDR);
+	} else
+		printk(KERN_INFO "%s: available. Good\n", dev->name);
+
+	/* Posted write is flushed in the wait_ack loop */
+	scc_writel(TxSccRes | RxSccRes, dpriv, dev, CMDR);
 
-	/* ... the following isn't */
-	if (dscc4_wait_ack_cec(ioaddr, dev, "Cec"))
+	if ((ret = dscc4_wait_ack_cec(dpriv, dev, "Cec")) < 0)
 		goto err_free_ring;
 
-	/* 
+	/*
 	 * I would expect XPR near CE completion (before ? after ?).
 	 * At worst, this code won't see a late XPR and people
-	 * will have to re-issue an ifconfig (this is harmless). 
-	 * WARNING, a really missing XPR usually means a hardware 
+	 * will have to re-issue an ifconfig (this is harmless).
+	 * WARNING, a really missing XPR usually means a hardware
 	 * reset is needed. Suggestions anyone ?
 	 */
-	if (dscc4_xpr_ack(dpriv))
+	if ((ret = dscc4_xpr_ack(dpriv)) < 0) {
+		printk(KERN_ERR "%s: %s timeout\n", DRV_NAME, "XPR");
 		goto err_free_ring;
+	}
 	
+	if (debug > 2)
+		dscc4_tx_print(dev, dpriv, "Open");
+
 	netif_start_queue(dev);
 
         init_timer(&dpriv->timer);
@@ -819,97 +1006,105 @@
 err_free_ring:
 	dscc4_release_ring(dpriv);
 err_out:
+	hdlc_close(hdlc);
 	MOD_DEC_USE_COUNT;
-	return -EAGAIN;
+err:
+	return ret;
 }
 
-#ifdef EXPERIMENTAL_POLLING
+#ifdef DSCC4_POLLING
 static int dscc4_tx_poll(struct dscc4_dev_priv *dpriv, struct net_device *dev)
 {
 	/* FIXME: it's gonna be easy (TM), for sure */
 }
-#endif /* EXPERIMENTAL_POLLING */
+#endif /* DSCC4_POLLING */
 
 static int dscc4_start_xmit(struct sk_buff *skb, struct net_device *dev)
 {
-	struct dscc4_dev_priv *dpriv = dev->priv;
-	struct dscc4_pci_priv *ppriv;
+	struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
+	struct dscc4_pci_priv *ppriv = dpriv->pci_priv;
 	struct TxFD *tx_fd;
-	int cur, next;
+	int next;
 
-	ppriv = dpriv->pci_priv;
-	cur = dpriv->tx_current++%TX_RING_SIZE;
 	next = dpriv->tx_current%TX_RING_SIZE;
 	dpriv->tx_skbuff[next] = skb;
 	tx_fd = dpriv->tx_fd + next;
-	tx_fd->state = FrameEnd | Hold | TO_STATE(skb->len & TxSizeMax);
+	tx_fd->state = FrameEnd | TO_STATE(skb->len);
 	tx_fd->data = pci_map_single(ppriv->pdev, skb->data, skb->len,
 				     PCI_DMA_TODEVICE);
 	tx_fd->complete = 0x00000000;
-	mb(); // FIXME: suppress ?
+	tx_fd->jiffies = jiffies;
+	mb();
 
-#ifdef EXPERIMENTAL_POLLING
+#ifdef DSCC4_POLLING
 	spin_lock(&dpriv->lock);
-	while(dscc4_tx_poll(dpriv, dev));
+	while (dscc4_tx_poll(dpriv, dev));
 	spin_unlock(&dpriv->lock);
 #endif
-	/*
-	 * I know there's a window for a race in the following lines but
-	 * dscc4_timer will take good care of it. The chipset eats events
-	 * (especially the net_dev re-enabling ones) thus there is no
-	 * reason to try and be smart.
-	 */
-	if ((dpriv->tx_dirty + 16) < dpriv->tx_current) {
-			netif_stop_queue(dev);
-			dpriv->hi_expected = 2;
-	}
-	tx_fd = dpriv->tx_fd + cur;
-	tx_fd->state &= ~Hold;
-	mb(); // FIXME: suppress ?
-
-	/* 
-	 * One may avoid some pci transactions during intense TX periods.
-	 * Not sure it's worth the pain...
-	 */
-	writel((TxPollCmd << dpriv->dev_id) | NoAck, dev->base_addr + GCMDR);
+
 	dev->trans_start = jiffies;
+
+	if (debug > 2)
+		dscc4_tx_print(dev, dpriv, "Xmit");
+	/* To be cleaned(unsigned int)/optimized. Later, ok ? */
+	if (!((++dpriv->tx_current - dpriv->tx_dirty)%TX_RING_SIZE))
+		netif_stop_queue(dev);
+
+	if (dscc4_tx_quiescent(dpriv, dev))
+		dscc4_do_tx(dpriv, dev);
+
 	return 0;
 }
 
 static int dscc4_close(struct net_device *dev)
 {
-	struct dscc4_dev_priv *dpriv = (struct dscc4_dev_priv *)dev->priv;
-	u32 ioaddr = dev->base_addr;
-	int dev_id;
+	struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
+	hdlc_device *hdlc = dev_to_hdlc(dev);
+	unsigned long flags;
 
 	del_timer_sync(&dpriv->timer);
 	netif_stop_queue(dev);
 
-	dev_id = dpriv->dev_id;
+	spin_lock_irqsave(&dpriv->pci_priv->lock, flags);
+	dscc4_rx_reset(dpriv, dev);
+	spin_unlock_irqrestore(&dpriv->pci_priv->lock, flags);
 
-	writel(0x00050000, ioaddr + SCC_REG_START(dev_id) + CCR2);
-	writel(MTFi|Rdr|Rdt, ioaddr + CH0CFG + dev_id*0x0c); /* Reset Rx/Tx */
-	writel(0x00000001, ioaddr + GCMDR);
+	dscc4_tx_reset(dpriv, dev);
 
+	hdlc_close(hdlc);
 	dscc4_release_ring(dpriv);
 
 	MOD_DEC_USE_COUNT;
 	return 0;
 }
 
+static inline int dscc4_check_clock_ability(int port)
+{
+	int ret = 0;
+
+#ifdef CONFIG_DSCC4_CLOCK_ON_TWO_PORTS_ONLY
+	if (port >= 2)
+		ret = -1;
+#endif
+	return ret;
+}
+
 static int dscc4_set_clock(struct net_device *dev, u32 *bps, u32 *state)
 {
-	struct dscc4_dev_priv *dpriv = (struct dscc4_dev_priv *)dev->priv;
+	struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
+	int ret = -1;
 	u32 brr;
 
 	*state &= ~Ccr0ClockMask;
-	if (*bps) { /* DCE */
+	if (*bps) { /* Clock generated - required for DCE */
 		u32 n = 0, m = 0, divider;
 		int xtal;
 
 		xtal = dpriv->pci_priv->xtal_hz;
 		if (!xtal)
-			return -1;
+			goto done;
+		if (dscc4_check_clock_ability(dpriv->dev_id) < 0)
+			goto done;
 		divider = xtal / *bps;
 		if (divider > BRR_DIVIDER_MAX) {
 			divider >>= 4;
@@ -933,226 +1128,205 @@
 		if (!(*state & 0x00000001)) /* Clock mode 6b */
 			divider <<= 4;
 		*bps = xtal / divider;
-	} else { /* DTE */
-		/* 
-		 * "state" already reflects Clock mode 0a. 
-		 * Nothing more to be done 
+	} else {
+		/*
+		 * External clock - DTE
+		 * "state" already reflects Clock mode 0a.
+		 * Nothing more to be done
 		 */
 		brr = 0;
 	}
-	writel(brr, dev->base_addr + BRR + SCC_REG_START(dpriv->dev_id));
-
-	return 0;
+	scc_writel(brr, dpriv, dev, BRR);
+	ret = 0;
+done:
+	return ret;
 }
 
-#ifdef LATER_PLEASE
-/*
- * -*- [RFC] Configuring Synchronous Interfaces in Linux -*-
- */
+static int dscc4_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	sync_serial_settings *line = ifr->ifr_settings.ifs_ifsu.sync;
+	struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
+	const size_t size = sizeof(dpriv->settings);
+	int ret = 0;
 
-// FIXME: MEDIA already defined in linux/hdlc.h
-#define HDLC_MEDIA_V35		0
-#define HDLC_MEDIA_RS232	1
-#define HDLC_MEDIA_X21		2
-#define HDLC_MEDIA_E1		3
-#define HDLC_MEDIA_HSSI		4
-
-#define HDLC_CODING_NRZ		0
-#define HDLC_CODING_NRZI	1
-#define HDLC_CODING_FM0		2
-#define HDLC_CODING_FM1		3
-#define HDLC_CODING_MANCHESTER	4
-
-#define HDLC_CRC_NONE		0
-#define HDLC_CRC_16		1
-#define HDLC_CRC_32		2
-#define HDLC_CRC_CCITT		3
-
-/* RFC: add the crc reset value ? */
-struct hdlc_physical {
-	u8 media;
-	u8 coding;
-	u32 rate;
-	u8 crc;
-	u8 crc_siz;		/* 2 or 4 bytes */
-	u8 shared_flags;	/* Discouraged on the DSCC4 */
-};
+        if (dev->flags & IFF_UP)
+                return -EBUSY;
 
-// FIXME: PROTO already defined in linux/hdlc.h
-#define HDLC_PROTO_RAW		0
-#define HDLC_PROTO_FR		1
-#define HDLC_PROTO_X25		2
-#define HDLC_PROTO_PPP		3
-#define HDLC_PROTO_CHDLC	4
+	if (cmd != SIOCWANDEV)
+		return -EOPNOTSUPP;
 
-struct hdlc_protocol {
-	u8 proto; 
-	
-	union {
-	} u;
-};
+	switch(ifr->ifr_settings.type) {
+	case IF_GET_IFACE:
+		ifr->ifr_settings.type = IF_IFACE_SYNC_SERIAL;
+		if (ifr->ifr_settings.size < size) {
+			ifr->ifr_settings.size = size; /* data size wanted */
+			return -ENOBUFS;
+		}
+		if (copy_to_user(line, &dpriv->settings, size))
+			return -EFAULT;
+		break;
 
-struct screq {
-	u16 media_group;
+	case IF_IFACE_SYNC_SERIAL:
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
 
-	union {
-		struct hdlc_physical hdlc_phy;
-		struct hdlc_protocol hdlc_proto;
-	} u;
-};
+		if (copy_from_user(&dpriv->settings, line, size))
+			return -EFAULT;
+		ret = dscc4_set_iface(dpriv, dev);
+		break;
 
-// FIXME: go sub-module 
-static struct {
-	u16 coding;
-	u16 bits;
-} map[] = {
-	{HDLC_CODING_NRZ,		0x00},
-	{HDLC_CODING_NRZI,		0x20},
-	{HDLC_CODING_FM0,		0x40},
-	{HDLC_CODING_FM1,		0x50},
-	{HDLC_CODING_MANCHESTER,	0x60},
-	{65535,	0x00}
-};
-#endif /* LATER_PLEASE */
+	default:
+		ret = hdlc_ioctl(dev, ifr, cmd);
+		break;
+	}
 
-static int dscc4_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+	return ret;
+}
+
+static inline int dscc4_set_quartz(struct dscc4_dev_priv *dpriv, int hz)
 {
-	struct dscc4_dev_priv *dpriv = dev->priv;
-	u32 state, ioaddr;
+	int ret = 0;
 
-        if (dev->flags & IFF_UP)
-                return -EBUSY;
+	if ((hz < 0) || (hz > DSCC4_HZ_MAX))
+		ret = -EOPNOTSUPP;
+	else
+		dpriv->pci_priv->xtal_hz = hz;
 
-	switch (cmd) {
-		/* Set built-in quartz frequency */
-		case SIOCDEVPRIVATE: {
-			u32 hz;
-
-			hz = ifr->ifr_ifru.ifru_ivalue;
-			if (hz >= 33000000) /* 33 MHz */
-				return -EOPNOTSUPP;
-			dpriv->pci_priv->xtal_hz = hz;
-			return 0;
-		}
-		/* Set/unset loopback */
-		case SIOCDEVPRIVATE+1: {
-			u32 flags;
-	
-			ioaddr = dev->base_addr + CCR1 +
-				 SCC_REG_START(dpriv->dev_id);
-			state = readl(ioaddr);
-			flags = ifr->ifr_ifru.ifru_ivalue;
-			if (flags & 0x00000001) {
-				printk(KERN_DEBUG "%s: loopback\n", dev->name);
-				state |= 0x00000100;
-			} else {
-				printk(KERN_DEBUG "%s: normal\n", dev->name);
-				state &= ~0x00000100;
-			}
-			writel(state, ioaddr);
-			return 0;
-		}
+	return ret;
+}
 
-#ifdef LATER_PLEASE
-		case SIOCDEVPRIVATE+2: {
-		{
-			struct screq scr;
-
-			err = copy_from_user(&scr, ifr->ifr_ifru.ifru_data, sizeof(struct screq));
-			if (err)
-				return err;
-			do {
-				if (scr.u.hdlc_phy.coding == map[i].coding)
-					break;
-			} while (map[++i].coding != 65535);
-			if (!map[i].coding)
-				return -EOPNOTSUPP;
-			
-			ioaddr = dev->base_addr + CCR0 +
-				 SCC_REG_START(dpriv->dev_id);
-			state = readl(ioaddr) & ~EncodingMask;
-			state |= (u32)map[i].bits << 16;
-			writel(state, ioaddr);	
-			printk("state: %08x\n", state); /* DEBUG */
-			return 0;
-		}
-		case SIOCDEVPRIVATE+3: {
-			struct screq *scr = (struct screq *)ifr->ifr_ifru.ifru_data;
-
-			ioaddr = dev->base_addr + CCR0 +
-				 SCC_REG_START(dpriv->dev_id);
-			state = (readl(ioaddr) & EncodingMask) >> 16;
-			do {
-				if (state == map[i].bits)
-					break;
-			} while (map[++i].coding);
-			return put_user(map[i].coding, (u16 *)scr->u.hdlc_phy.coding);
-		}
-#endif /* LATER_PLEASE */
+static int dscc4_match(struct thingie *p, int value)
+{
+	int i;
 
-		case HDLCSCLOCKRATE:
-		{
-			u32 state, bps;
- 
-			bps = ifr->ifr_ifru.ifru_ivalue;
-			ioaddr = dev->base_addr + CCR0 +
-				 SCC_REG_START(dpriv->dev_id);
-			state = readl(ioaddr);
-			if(dscc4_set_clock(dev, &bps, &state) < 0)
-				return -EOPNOTSUPP;
-			if (bps) { /* DCE */
-				printk(KERN_DEBUG "%s: generated RxClk (DCE)\n",
-				       dev->name);
-				ifr->ifr_ifru.ifru_ivalue = bps;
-			} else { /* DTE */
-				state = 0x80001000;
-				printk(KERN_DEBUG "%s: external RxClk (DTE)\n",
-				       dev->name);
-			}
-			writel(state, ioaddr);	
-			return 0;
+	for (i = 0; p[i].define != -1; i++) {
+		if (value == p[i].define)
+			break;
+	}
+	if (p[i].define == -1)
+		return -1;
+	else
+		return i;
+}
+
+static int dscc4_clock_setting(struct dscc4_dev_priv *dpriv,
+			       struct net_device *dev)
+{
+	sync_serial_settings *settings = &dpriv->settings;
+	int ret = -EOPNOTSUPP;
+	u32 bps, state;
+
+	bps = settings->clock_rate;
+	state = scc_readl(dpriv, CCR0);
+	if (dscc4_set_clock(dev, &bps, &state) < 0)
+		goto done;
+	if (bps) { /* DCE */
+		printk(KERN_DEBUG "%s: generated RxClk (DCE)\n", dev->name);
+		if (settings->clock_rate != bps) {
+			settings->clock_rate = bps;
+			printk(KERN_DEBUG "%s: clock adjusted (%08d -> %08d)\n",
+				dev->name, dpriv->settings.clock_rate, bps);
 		}
-		case HDLCGCLOCKRATE: {
-			u32 brr;
-			int bps;
+	} else { /* DTE */
+		state = 0x80001000;
+		printk(KERN_DEBUG "%s: external RxClk (DTE)\n", dev->name);
+	}
+	scc_writel(state, dpriv, dev, CCR0);
+	ret = 0;
+done:
+	return ret;
+}
+
+static int dscc4_encoding_setting(struct dscc4_dev_priv *dpriv,
+				  struct net_device *dev)
+{
+	struct thingie encoding[] = {
+		{ ENCODING_NRZ,		0x00000000 },
+		{ ENCODING_NRZI,	0x00200000 },
+		{ ENCODING_FM_MARK,	0x00400000 },
+		{ ENCODING_FM_SPACE,	0x00500000 },
+		{ ENCODING_MANCHESTER,	0x00600000 },
+		{ -1,			0}
+	};
+	int i, ret = 0;
+
+	i = dscc4_match(encoding, dpriv->encoding);
+	if (i >= 0)
+		scc_patchl(EncodingMask, encoding[i].bits, dpriv, dev, CCR0);
+	else
+		ret = -EOPNOTSUPP;
+	return ret;
+}
 
-			brr = readl(dev->base_addr + BRR + 
-				    SCC_REG_START(dpriv->dev_id));
-			bps = dpriv->pci_priv->xtal_hz >> (brr >> 8);
-			bps /= (brr & 0x3f) + 1;
-			ifr->ifr_ifru.ifru_ivalue = bps;
-			return 0;
-		}
+static int dscc4_loopback_setting(struct dscc4_dev_priv *dpriv,
+				  struct net_device *dev)
+{
+	sync_serial_settings *settings = &dpriv->settings;
+	u32 state;
 
-		default:
-			return -EOPNOTSUPP;
+	state = scc_readl(dpriv, CCR1);
+	if (settings->loopback) {
+		printk(KERN_DEBUG "%s: loopback\n", dev->name);
+		state |= 0x00000100;
+	} else {
+		printk(KERN_DEBUG "%s: normal\n", dev->name);
+		state &= ~0x00000100;
 	}
+	scc_writel(state, dpriv, dev, CCR1);
+	return 0;
 }
 
-static int dscc4_change_mtu(struct net_device *dev, int mtu)
+static int dscc4_crc_setting(struct dscc4_dev_priv *dpriv,
+			     struct net_device *dev)
 {
-	/* FIXME: chainsaw coded... */
-	if ((mtu <= 3) || (mtu > 65531))
-		return -EINVAL;
-        if(dev->flags & IFF_UP)
-                return -EBUSY;
-	dev->mtu = mtu;
-        return(0);
+	struct thingie crc[] = {
+		{ PARITY_CRC16_PR0_CCITT,	0x00000010 },
+		{ PARITY_CRC16_PR1_CCITT,	0x00000000 },
+		{ PARITY_CRC32_PR0_CCITT,	0x00000011 },
+		{ PARITY_CRC32_PR1_CCITT,	0x00000001 }
+	};
+	int i, ret = 0;
+
+	i = dscc4_match(crc, dpriv->parity);
+	if (i >= 0)
+		scc_patchl(CrcMask, crc[i].bits, dpriv, dev, CCR1);
+	else
+		ret = -EOPNOTSUPP;
+	return ret;
+}
+
+static int dscc4_set_iface(struct dscc4_dev_priv *dpriv, struct net_device *dev)
+{
+	struct {
+		int (*action)(struct dscc4_dev_priv *, struct net_device *);
+	} *p, do_setting[] = {
+		{ dscc4_encoding_setting },
+		{ dscc4_clock_setting },
+		{ dscc4_loopback_setting },
+		{ dscc4_crc_setting },
+		{ NULL }
+	};
+	int ret = 0;
+
+	for (p = do_setting; p->action; p++) {
+		if ((ret = p->action(dpriv, dev)) < 0)
+			break;
+	}
+	return ret;
 }
 
-static void dscc4_irq(int irq, void *dev_instance, struct pt_regs *ptregs)
+static void dscc4_irq(int irq, void *token, struct pt_regs *ptregs)
 {
-	struct net_device *dev = dev_instance;
+	struct dscc4_dev_priv *root = token;
 	struct dscc4_pci_priv *priv;
+	struct net_device *dev;
 	u32 ioaddr, state;
 	unsigned long flags;
 	int i;
 
-	priv = ((struct dscc4_dev_priv *)dev->priv)->pci_priv;
-	/* 
-	 * FIXME: shorten the protected area (set some bit telling we're
-	 * in an interrupt or increment some work-to-do counter etc...)
-	 */
+	priv = root->pci_priv;
+	dev = hdlc_to_dev(&root->hdlc);
+
 	spin_lock_irqsave(&priv->lock, flags);
 
 	ioaddr = dev->base_addr;
@@ -1162,15 +1336,15 @@
 		goto out;
 	writel(state, ioaddr + GSTAR);
 
-	if (state & Arf) { 
+	if (state & Arf) {
 		printk(KERN_ERR "%s: failure (Arf). Harass the maintener\n",
 		       dev->name);
 		goto out;
 	}
 	state &= ~ArAck;
 	if (state & Cfg) {
-		if (debug)
-			printk(KERN_DEBUG "CfgIV\n");
+		if (debug > 0)
+			printk(KERN_DEBUG "%s: CfgIV\n", DRV_NAME);
 		if (priv->iqcfg[priv->cfg_cur++%IRQ_RING_SIZE] & Arf)
 			printk(KERN_ERR "%s: %s failed\n", dev->name, "CFG");
 		if (!(state &= ~Cfg))
@@ -1179,14 +1353,14 @@
 	if (state & RxEvt) {
 		i = dev_per_card - 1;
 		do {
-			dscc4_rx_irq(priv, dev + i);
+			dscc4_rx_irq(priv, root + i);
 		} while (--i >= 0);
 		state &= ~RxEvt;
 	}
 	if (state & TxEvt) {
 		i = dev_per_card - 1;
 		do {
-			dscc4_tx_irq(priv, dev + i);
+			dscc4_tx_irq(priv, root + i);
 		} while (--i >= 0);
 		state &= ~TxEvt;
 	}
@@ -1194,10 +1368,10 @@
 	spin_unlock_irqrestore(&priv->lock, flags);
 }
 
-static __inline__ void dscc4_tx_irq(struct dscc4_pci_priv *ppriv, 
-				    struct net_device *dev)
+static inline void dscc4_tx_irq(struct dscc4_pci_priv *ppriv,
+				struct dscc4_dev_priv *dpriv)
 {
-	struct dscc4_dev_priv *dpriv = dev->priv;
+	struct net_device *dev = hdlc_to_dev(&dpriv->hdlc);
 	u32 state;
 	int cur, loop = 0;
 
@@ -1205,161 +1379,154 @@
 	cur = dpriv->iqtx_current%IRQ_RING_SIZE;
 	state = dpriv->iqtx[cur];
 	if (!state) {
-#ifdef DEBUG
-		if (loop > 1)
+		if ((debug > 1) && (loop > 1))
 			printk(KERN_DEBUG "%s: Tx irq loop=%d\n", dev->name, loop);
-#endif
 		if (loop && netif_queue_stopped(dev))
-			if ((dpriv->tx_dirty + 8) >= dpriv->tx_current)
+			if ((dpriv->tx_current - dpriv->tx_dirty)%TX_RING_SIZE)
 				netif_wake_queue(dev);
+
+		if (netif_running(dev) && dscc4_tx_quiescent(dpriv, dev) &&
+		    !dscc4_tx_done(dpriv))
+				dscc4_do_tx(dpriv, dev);
 		return;
 	}
 	loop++;
 	dpriv->iqtx[cur] = 0;
 	dpriv->iqtx_current++;
 
-#ifdef DEBUG_PARANOID
-	if (SOURCE_ID(state) != dpriv->dev_id) {
-		printk(KERN_DEBUG "%s (Tx): Source Id=%d, state=%08x\n",
-		       dev->name, SOURCE_ID(state), state );
+	if (state_check(state, dpriv, dev, "Tx") < 0)
 		return;
-	}
-	if (state & 0x0df80c00) {
-		printk(KERN_DEBUG "%s (Tx): state=%08x (UFO alert)\n",
-		       dev->name, state);
-		return;
-	}
-#endif
-	// state &= 0x0fffffff; /* Tracking the analyzed bits */
+
 	if (state & SccEvt) {
 		if (state & Alls) {
-			struct TxFD *tx_fd;
+			struct net_device_stats *stats = &dpriv->hdlc.stats;
 			struct sk_buff *skb;
+			struct TxFD *tx_fd;
 
+			if (debug > 2)
+				dscc4_tx_print(dev, dpriv, "Alls");
+			/*
+			 * DataComplete can't be trusted for Tx completion.
+			 * Cf errata DS5 p.8
+			 */
 			cur = dpriv->tx_dirty%TX_RING_SIZE;
 			tx_fd = dpriv->tx_fd + cur;
-
 			skb = dpriv->tx_skbuff[cur];
-
-			/* XXX: hideous kludge - to be removed "later" */
-			if (!skb) {
-				printk(KERN_ERR "%s: NULL skb in tx_irq at index %d\n", dev->name, cur);
-				goto try;
-			}
-			dpriv->tx_dirty++; // MUST be after skb test
-
-			/* Happens sometime. Don't know what triggers it */
-			if (!(tx_fd->complete & DataComplete)) {
-				u32 ioaddr, isr;
-
-				ioaddr = dev->base_addr + 
-					 SCC_REG_START(dpriv->dev_id) + ISR;
-				isr = readl(ioaddr);
-				printk(KERN_DEBUG 
-				       "%s: DataComplete=0 cur=%d isr=%08x state=%08x\n",
-				       dev->name, cur, isr, state);
-				writel(isr, ioaddr);
-				dpriv->stats.tx_dropped++;
-			} else {
-				tx_fd->complete &= ~DataComplete;
+			if (skb) {
+				pci_unmap_single(ppriv->pdev, tx_fd->data,
+						 skb->len, PCI_DMA_TODEVICE);
 				if (tx_fd->state & FrameEnd) {
-					dpriv->stats.tx_packets++;
-					dpriv->stats.tx_bytes += skb->len;
+					stats->tx_packets++;
+					stats->tx_bytes += skb->len;
 				}
+				dev_kfree_skb_irq(skb);
+				dpriv->tx_skbuff[cur] = NULL;
+				++dpriv->tx_dirty;
+			} else {
+				if (debug > 1)
+					printk(KERN_ERR "%s Tx: NULL skb %d\n",
+						dev->name, cur);
 			}
+			/*
+			 * If the driver ends sending crap on the wire, it
+			 * will be way easier to diagnose than the (not so)
+			 * random freeze induced by null sized tx frames.
+			 */
+			tx_fd->data = tx_fd->next;
+			tx_fd->state = FrameEnd | TO_STATE(2*DUMMY_SKB_SIZE);
+			tx_fd->complete = 0x00000000;
+			tx_fd->jiffies = 0;
 
-			dpriv->tx_skbuff[cur] = NULL;
-			pci_unmap_single(ppriv->pdev, tx_fd->data, skb->len,
-					 PCI_DMA_TODEVICE);
-			tx_fd->data = 0; /* DEBUG */
-			dev_kfree_skb_irq(skb);
-{ // DEBUG
-			cur = (dpriv->tx_dirty-1)%TX_RING_SIZE;
-			tx_fd = dpriv->tx_fd + cur;
-			tx_fd->state |= Hold;
-}
 			if (!(state &= ~Alls))
 				goto try;
 		}
-		/* 
+		/*
 		 * Transmit Data Underrun
 		 */
 		if (state & Xdu) {
-			printk(KERN_ERR "dscc4: XDU. Contact maintainer\n");
-			dpriv->flags = NeedIDT; 
+			printk(KERN_ERR "%s: XDU. Ask maintainer\n", DRV_NAME);
+			dpriv->flags = NeedIDT;
 			/* Tx reset */
-			writel(MTFi | Rdt, 
+			writel(MTFi | Rdt,
 			       dev->base_addr + 0x0c*dpriv->dev_id + CH0CFG);
-			writel(0x00000001, dev->base_addr + GCMDR);
+			writel(Action, dev->base_addr + GCMDR);
 			return;
 		}
+		if (state & Cts) {
+			printk(KERN_INFO "%s: CTS transition\n", dev->name);
+			if (!(state &= ~Cts)) /* DEBUG */
+				goto try;
+		}
 		if (state & Xmr) {
 			/* Frame needs to be sent again - FIXME */
-			//dscc4_start_xmit(dpriv->tx_skbuff[dpriv->tx_dirty], dev);
-			if (!(state &= ~0x00002000)) /* DEBUG */
+			printk(KERN_ERR "%s: Xmr. Ask maintainer\n", DRV_NAME);
+			if (!(state &= ~Xmr)) /* DEBUG */
 				goto try;
 		}
 		if (state & Xpr) {
-			unsigned long ioaddr = dev->base_addr;
-			unsigned long scc_offset;
-			u32 scc_addr;
+			u32 scc_addr, ring;
 
-			scc_offset = ioaddr + SCC_REG_START(dpriv->dev_id);
-			scc_addr = ioaddr + 0x0c*dpriv->dev_id;
-			if (readl(scc_offset + STAR) & SccBusy)
-				printk(KERN_DEBUG "%s busy. Fatal\n", 
-				       dev->name);
-			/*
-			 * Keep this order: IDT before IDR
-			 */
+			if (scc_readl_star(dpriv, dev) & SccBusy)
+				printk(KERN_ERR "%s busy. Fatal\n", dev->name);
+			scc_addr = dev->base_addr + 0x0c*dpriv->dev_id;
+			/* Keep this order: IDT before IDR */
 			if (dpriv->flags & NeedIDT) {
-				writel(MTFi | Idt, scc_addr + CH0CFG);
-				writel(dpriv->tx_fd_dma + 
+				if (debug > 2)
+					dscc4_tx_print(dev, dpriv, "Xpr");
+				ring = dpriv->tx_fd_dma +
 				       (dpriv->tx_dirty%TX_RING_SIZE)*
-				       sizeof(struct TxFD), scc_addr + CH0BTDA);
-				if(dscc4_do_action(dev, "IDT"))
+				       sizeof(struct TxFD);
+				writel(ring, scc_addr + CH0BTDA);
+				dscc4_do_tx(dpriv, dev);
+				writel(MTFi | Idt, scc_addr + CH0CFG);
+				if (dscc4_do_action(dev, "IDT") < 0)
 					goto err_xpr;
 				dpriv->flags &= ~NeedIDT;
-				mb();
 			}
 			if (dpriv->flags & NeedIDR) {
-				writel(MTFi | Idr, scc_addr + CH0CFG);
-				writel(dpriv->rx_fd_dma + 
+				ring = dpriv->rx_fd_dma +
 				       (dpriv->rx_current%RX_RING_SIZE)*
-				       sizeof(struct RxFD), scc_addr + CH0BRDA);
-				if(dscc4_do_action(dev, "IDR"))
+				       sizeof(struct RxFD);
+				writel(ring, scc_addr + CH0BRDA);
+				dscc4_rx_update(dpriv, dev);
+				writel(MTFi | Idr, scc_addr + CH0CFG);
+				if (dscc4_do_action(dev, "IDR") < 0)
 					goto err_xpr;
 				dpriv->flags &= ~NeedIDR;
-				mb();
+				smp_wmb();
 				/* Activate receiver and misc */
-				writel(0x08050008, scc_offset + CCR2);
+				scc_writel(0x08050008, dpriv, dev, CCR2);
 			}
 		err_xpr:
 			if (!(state &= ~Xpr))
 				goto try;
 		}
+		if (state & Cd) {
+			printk(KERN_INFO "%s: CD transition\n", dev->name);
+			if (!(state &= ~Cd)) /* DEBUG */
+				goto try;
+		}
 	} else { /* ! SccEvt */
 		if (state & Hi) {
-#ifdef EXPERIMENTAL_POLLING
-			while(!dscc4_tx_poll(dpriv, dev));
+#ifdef DSCC4_POLLING
+			while (!dscc4_tx_poll(dpriv, dev));
 #endif
+			printk(KERN_INFO "%s: Tx Hi\n", dev->name);
 			state &= ~Hi;
 		}
-		/*
-		 * FIXME: it may be avoided. Re-re-re-read the manual.
-		 */
 		if (state & Err) {
-			printk(KERN_ERR "%s: Tx ERR\n", dev->name);
-			dpriv->stats.tx_errors++;
+			printk(KERN_INFO "%s: Tx ERR\n", dev->name);
+			dev_to_hdlc(dev)->stats.tx_errors++;
 			state &= ~Err;
 		}
 	}
 	goto try;
 }
 
-static __inline__ void dscc4_rx_irq(struct dscc4_pci_priv *priv, struct net_device *dev)
+static inline void dscc4_rx_irq(struct dscc4_pci_priv *priv,
+				    struct dscc4_dev_priv *dpriv)
 {
-	struct dscc4_dev_priv *dpriv = dev->priv;
+	struct net_device *dev = hdlc_to_dev(&dpriv->hdlc);
 	u32 state;
 	int cur;
 
@@ -1371,117 +1538,107 @@
 	dpriv->iqrx[cur] = 0;
 	dpriv->iqrx_current++;
 
-#ifdef DEBUG_PARANOID
-	if (SOURCE_ID(state) != dpriv->dev_id) {
-		printk(KERN_DEBUG "%s (Rx): Source Id=%d, state=%08x\n",
-		       dev->name, SOURCE_ID(state), state);
-		goto try;
-	}
-	if (state & 0x0df80c00) {
-		printk(KERN_DEBUG "%s (Rx): state=%08x (UFO alert)\n",
-		       dev->name, state);
-		goto try;
-	}
-#endif
+	if (state_check(state, dpriv, dev, "Rx") < 0)
+		return;
+
 	if (!(state & SccEvt)){
 		struct RxFD *rx_fd;
 
 		state &= 0x00ffffff;
 		if (state & Err) { /* Hold or reset */
-			printk(KERN_DEBUG "%s (Rx): ERR\n", dev->name);
-			cur = dpriv->rx_current;
+			printk(KERN_DEBUG "%s: Rx ERR\n", dev->name);
+			cur = dpriv->rx_current%RX_RING_SIZE;
 			rx_fd = dpriv->rx_fd + cur;
 			/*
-			 * Presume we're not facing a DMAC receiver reset. 
-			 * As We use the rx size-filtering feature of the 
-			 * DSCC4, the beginning of a new frame is waiting in 
-			 * the rx fifo. I bet a Receive Data Overflow will 
+			 * Presume we're not facing a DMAC receiver reset.
+			 * As We use the rx size-filtering feature of the
+			 * DSCC4, the beginning of a new frame is waiting in
+			 * the rx fifo. I bet a Receive Data Overflow will
 			 * happen most of time but let's try and avoid it.
 			 * Btw (as for RDO) if one experiences ERR whereas
-			 * the system looks rather idle, there may be a 
+			 * the system looks rather idle, there may be a
 			 * problem with latency. In this case, increasing
 			 * RX_RING_SIZE may help.
 			 */
-			while (dpriv->rx_needs_refill) {
-				while(!(rx_fd->state1 & Hold)) {
+			//while (dpriv->rx_needs_refill) {
+				while (!(rx_fd->state1 & Hold)) {
 					rx_fd++;
 					cur++;
 					if (!(cur = cur%RX_RING_SIZE))
 						rx_fd = dpriv->rx_fd;
 				}
-				dpriv->rx_needs_refill--;
-				try_get_rx_skb(dpriv, cur, dev);
+				//dpriv->rx_needs_refill--;
+				try_get_rx_skb(dpriv, dev);
 				if (!rx_fd->data)
 					goto try;
 				rx_fd->state1 &= ~Hold;
 				rx_fd->state2 = 0x00000000;
 				rx_fd->end = 0xbabeface;
-			}
+			//}
 			goto try;
 		}
 		if (state & Fi) {
-			cur = dpriv->rx_current%RX_RING_SIZE;
-			rx_fd = dpriv->rx_fd + cur;
-			dscc4_rx_skb(dpriv, cur, rx_fd, dev);
-			dpriv->rx_current++;
+			dscc4_rx_skb(dpriv, dev);
 			goto try;
 		}
 		if (state & Hi ) { /* HI bit */
+			printk(KERN_INFO "%s: Rx Hi\n", dev->name);
 			state &= ~Hi;
 			goto try;
 		}
-	} else { /* ! SccEvt */
-#ifdef DEBUG_PARANOIA
-		int i;
+	} else { /* SccEvt */
+		if (debug > 1) {
+			//FIXME: verifier la presence de tous les evenements
 		static struct {
 			u32 mask;
 			const char *irq_name;
 		} evts[] = {
 			{ 0x00008000, "TIN"},
-			{ 0x00004000, "CSC"},
 			{ 0x00000020, "RSC"},
 			{ 0x00000010, "PCE"},
 			{ 0x00000008, "PLLA"},
-			{ 0x00000004, "CDSC"},
 			{ 0, NULL}
-		};
-#endif /* DEBUG_PARANOIA */
-		state &= 0x00ffffff;
-#ifdef DEBUG_PARANOIA
-		for (i = 0; evts[i].irq_name; i++) {
-			if (state & evts[i].mask) {
-				printk(KERN_DEBUG "dscc4(%s): %s\n",
-					dev->name, evts[i].irq_name);
-				if (!(state &= ~evts[i].mask))
+		}, *evt;
+
+		for (evt = evts; evt->irq_name; evt++) {
+			if (state & evt->mask) {
+					printk(KERN_DEBUG "%s: %s\n",
+						dev->name, evt->irq_name);
+				if (!(state &= ~evt->mask))
 					goto try;
 			}
 		}
-#endif /* DEBUG_PARANOIA */
+		} else {
+			if (!(state &= ~0x0000c03c))
+				goto try;
+		}
+		if (state & Cts) {
+			printk(KERN_INFO "%s: CTS transition\n", dev->name);
+			if (!(state &= ~Cts)) /* DEBUG */
+				goto try;
+		}
 		/*
-		 * Receive Data Overflow (FIXME: untested)
+		 * Receive Data Overflow (FIXME: fscked)
 		 */
 		if (state & Rdo) {
-			u32 ioaddr, scc_offset, scc_addr;
 			struct RxFD *rx_fd;
+			u32 scc_addr;
 			int cur;
 
 			//if (debug)
 			//	dscc4_rx_dump(dpriv);
-			ioaddr = dev->base_addr;
-			scc_addr = ioaddr + 0x0c*dpriv->dev_id;
-			scc_offset = ioaddr + SCC_REG_START(dpriv->dev_id);
+			scc_addr = dev->base_addr + 0x0c*dpriv->dev_id;
 
-			writel(readl(scc_offset + CCR2) & ~RxActivate, 
-			       scc_offset + CCR2);
+			scc_patchl(RxActivate, 0, dpriv, dev, CCR2);
 			/*
 			 * This has no effect. Why ?
 			 * ORed with TxSccRes, one sees the CFG ack (for
 			 * the TX part only).
-			 */	
-			writel(RxSccRes, scc_offset + CMDR);
+			 */
+			scc_writel(RxSccRes, dpriv, dev, CMDR);
 			dpriv->flags |= RdoSet;
 
-			/* 
+			/*
 			 * Let's try and save something in the received data.
 			 * rx_current must be incremented at least once to
 			 * avoid HOLD in the BRDA-to-be-pointed desc.
@@ -1492,180 +1649,170 @@
 				if (!(rx_fd->state2 & DataComplete))
 					break;
 				if (rx_fd->state2 & FrameAborted) {
-					dpriv->stats.rx_over_errors++;
+					dev_to_hdlc(dev)->stats.rx_over_errors++;
 					rx_fd->state1 |= Hold;
 					rx_fd->state2 = 0x00000000;
 					rx_fd->end = 0xbabeface;
-				} else 
-					dscc4_rx_skb(dpriv, cur, rx_fd, dev);
+				} else
+					dscc4_rx_skb(dpriv, dev);
 			} while (1);
 
-			if (debug) {
+			if (debug > 0) {
 				if (dpriv->flags & RdoSet)
-					printk(KERN_DEBUG 
-					       "dscc4: no RDO in Rx data\n");
+					printk(KERN_DEBUG
+					       "%s: no RDO in Rx data\n", DRV_NAME);
 			}
 #ifdef DSCC4_RDO_EXPERIMENTAL_RECOVERY
 			/*
 			 * FIXME: must the reset be this violent ?
 			 */
-			writel(dpriv->rx_fd_dma + 
+#warning "FIXME: CH0BRDA"
+			writel(dpriv->rx_fd_dma +
 			       (dpriv->rx_current%RX_RING_SIZE)*
 			       sizeof(struct RxFD), scc_addr + CH0BRDA);
 			writel(MTFi|Rdr|Idr, scc_addr + CH0CFG);
-			if(dscc4_do_action(dev, "RDR")) {
+			if (dscc4_do_action(dev, "RDR") < 0) {
 				printk(KERN_ERR "%s: RDO recovery failed(%s)\n",
 				       dev->name, "RDR");
 				goto rdo_end;
 			}
 			writel(MTFi|Idr, scc_addr + CH0CFG);
-			if(dscc4_do_action(dev, "IDR")) {
+			if (dscc4_do_action(dev, "IDR") < 0) {
 				printk(KERN_ERR "%s: RDO recovery failed(%s)\n",
 				       dev->name, "IDR");
 				goto rdo_end;
 			}
 		rdo_end:
 #endif
-			writel(readl(scc_offset + CCR2) | RxActivate, 
-			       scc_offset + CCR2);
+			scc_patchl(0, RxActivate, dpriv, dev, CCR2);
 			goto try;
 		}
-		/* These will be used later */
-		if (state & Rfs) {
-			if (!(state &= ~Rfs))
-				goto try;
-		}
-		if (state & Rfo) {
-			if (!(state &= ~Rfo))
+		if (state & Cd) {
+			printk(KERN_INFO "%s: CD transition\n", dev->name);
+			if (!(state &= ~Cd)) /* DEBUG */
 				goto try;
 		}
 		if (state & Flex) {
+			printk(KERN_DEBUG "%s: Flex. Ttttt...\n", DRV_NAME);
 			if (!(state &= ~Flex))
 				goto try;
 		}
 	}
 }
 
+/*
+ * I had expected the following to work for the first descriptor
+ * (tx_fd->state = 0xc0000000)
+ * - Hold=1 (don't try and branch to the next descripto);
+ * - No=0 (I want an empty data section, i.e. size=0);
+ * - Fe=1 (required by No=0 or we got an Err irq and must reset).
+ * It failed and locked solid. Thus the introduction of a dummy skb.
+ * Problem is acknowledged in errata sheet DS5. Joy :o/
+ */
+struct sk_buff *dscc4_init_dummy_skb(struct dscc4_dev_priv *dpriv)
+{
+	struct sk_buff *skb;
+
+	skb = dev_alloc_skb(DUMMY_SKB_SIZE);
+	if (skb) {
+		int last = dpriv->tx_dirty%TX_RING_SIZE;
+		struct TxFD *tx_fd = dpriv->tx_fd + last;
+
+		skb->len = DUMMY_SKB_SIZE;
+		memcpy(skb->data, version, strlen(version)%DUMMY_SKB_SIZE);
+		tx_fd->state = FrameEnd | TO_STATE(DUMMY_SKB_SIZE);
+		tx_fd->data = pci_map_single(dpriv->pci_priv->pdev, skb->data,
+					     DUMMY_SKB_SIZE, PCI_DMA_TODEVICE);
+		dpriv->tx_skbuff[last] = skb;
+	}
+	return skb;
+}
+
 static int dscc4_init_ring(struct net_device *dev)
 {
-	struct dscc4_dev_priv *dpriv = (struct dscc4_dev_priv *)dev->priv;
+	struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
+	struct pci_dev *pdev = dpriv->pci_priv->pdev;
 	struct TxFD *tx_fd;
 	struct RxFD *rx_fd;
+	void *ring;
 	int i;
 
-	tx_fd = (struct TxFD *) pci_alloc_consistent(dpriv->pci_priv->pdev,
-		TX_RING_SIZE*sizeof(struct TxFD), &dpriv->tx_fd_dma);
-	if (!tx_fd)
+	ring = pci_alloc_consistent(pdev, RX_TOTAL_SIZE, &dpriv->rx_fd_dma);
+	if (!ring)
 		goto err_out;
-	rx_fd = (struct RxFD *) pci_alloc_consistent(dpriv->pci_priv->pdev,
-		RX_RING_SIZE*sizeof(struct RxFD), &dpriv->rx_fd_dma);
-	if (!rx_fd)
-		goto err_free_dma_tx;
+	dpriv->rx_fd = rx_fd = (struct RxFD *) ring;
 
-	dpriv->tx_fd = tx_fd;
-	dpriv->rx_fd = rx_fd;
-	dpriv->rx_current = 0;
-	dpriv->tx_current = 0;
-	dpriv->tx_dirty = 0;
-
-	/* the dma core of the dscc4 will be locked on the first desc */
-	for(i = 0; i < TX_RING_SIZE; ) {
-		reset_TxFD(tx_fd);
+	ring = pci_alloc_consistent(pdev, TX_TOTAL_SIZE, &dpriv->tx_fd_dma);
+	if (!ring)
+		goto err_free_dma_rx;
+	dpriv->tx_fd = tx_fd = (struct TxFD *) ring;
+
+	memset(dpriv->tx_skbuff, 0, sizeof(struct sk_buff *)*TX_RING_SIZE);
+	dpriv->tx_dirty = 0xffffffff;
+	i = dpriv->tx_current = 0;
+	do {
+		tx_fd->state = FrameEnd | TO_STATE(2*DUMMY_SKB_SIZE);
+		tx_fd->complete = 0x00000000;
 	        /* FIXME: NULL should be ok - to be tried */
 	        tx_fd->data = dpriv->tx_fd_dma;
-	        dpriv->tx_skbuff[i] = NULL;
-		i++;
-		tx_fd->next = (u32)(dpriv->tx_fd_dma + i*sizeof(struct TxFD));
-		tx_fd++;
-	}
-	(--tx_fd)->next = (u32)dpriv->tx_fd_dma;
-{
-	/*
-	 * XXX: I would expect the following to work for the first descriptor
-	 * (tx_fd->state = 0xc0000000)
-	 * - Hold=1 (don't try and branch to the next descripto);
-	 * - No=0 (I want an empty data section, i.e. size=0);
-	 * - Fe=1 (required by No=0 or we got an Err irq and must reset).
-	 * Alas, it fails (and locks solid). Thus the introduction of a dummy
-	 * skb to avoid No=0 (choose one: Ugly [ ] Tasteless [ ] VMS [ ]).
-	 * TODO: fiddle the tx threshold when time permits.
-	 */
-	struct sk_buff *skb;
+		(tx_fd++)->next = (u32)(dpriv->tx_fd_dma +
+					(++i%TX_RING_SIZE)*sizeof(*tx_fd));
+	} while (i < TX_RING_SIZE);
 
-	skb = dev_alloc_skb(32);
-	if (!skb)
+	if (dscc4_init_dummy_skb(dpriv) < 0)
 		goto err_free_dma_tx;
-	skb->len = 32;
-	memset(skb->data, 0xaa, 16);
-	tx_fd -= (TX_RING_SIZE - 1);
-	tx_fd->state = 0xc0000000;
-	tx_fd->state |= ((u32)(skb->len & TxSizeMax)) << 16;
-	tx_fd->data = pci_map_single(dpriv->pci_priv->pdev, skb->data, 
-				     skb->len, PCI_DMA_TODEVICE);
-	dpriv->tx_skbuff[0] = skb;
-}
-	for (i = 0; i < RX_RING_SIZE;) {
+
+	memset(dpriv->rx_skbuff, 0, sizeof(struct sk_buff *)*RX_RING_SIZE);
+	i = dpriv->rx_dirty = dpriv->rx_current = 0;
+	do {
 		/* size set by the host. Multiple of 4 bytes please */
-	        rx_fd->state1 = HiDesc; /* Hi, no Hold */
+	        rx_fd->state1 = HiDesc;
 	        rx_fd->state2 = 0x00000000;
 	        rx_fd->end = 0xbabeface;
-	        rx_fd->state1 |= ((u32)(HDLC_MAX_MRU & RxSizeMax)) << 16;
-		try_get_rx_skb(dpriv, i, dev);
-		i++;
-		rx_fd->next = (u32)(dpriv->rx_fd_dma + i*sizeof(struct RxFD));
-		rx_fd++;
-	}
-	(--rx_fd)->next = (u32)dpriv->rx_fd_dma;
-	rx_fd->state1 |= 0x40000000; /* Hold */
+	        rx_fd->state1 |= (RX_MAX(HDLC_MAX_MRU) << 16);
+		// FIXME: return value verifiee mais traitement suspect
+		if (try_get_rx_skb(dpriv, dev) >= 0)
+			dpriv->rx_dirty++;
+		(rx_fd++)->next = (u32)(dpriv->rx_fd_dma +
+					(++i%RX_RING_SIZE)*sizeof(*rx_fd));
+	} while (i < RX_RING_SIZE);
 
 	return 0;
 
 err_free_dma_tx:
-	pci_free_consistent(dpriv->pci_priv->pdev, TX_RING_SIZE*sizeof(*tx_fd),
-			    tx_fd, dpriv->tx_fd_dma);
+	pci_free_consistent(pdev, TX_TOTAL_SIZE, ring, dpriv->tx_fd_dma);
+err_free_dma_rx:
+	pci_free_consistent(pdev, RX_TOTAL_SIZE, rx_fd, dpriv->rx_fd_dma);
 err_out:
-	return -1;
-}
-
-static struct net_device_stats *dscc4_get_stats(struct net_device *dev)
-{
-	struct dscc4_dev_priv *priv = (struct dscc4_dev_priv *)dev->priv;
-
-	return &priv->stats;
+	return -ENOMEM;
 }
 
 static void __exit dscc4_remove_one(struct pci_dev *pdev)
 {
 	struct dscc4_pci_priv *ppriv;
-	struct net_device *root;
+	struct dscc4_dev_priv *root;
+	u32 ioaddr;
 	int i;
 
 	ppriv = pci_get_drvdata(pdev);
 	root = ppriv->root;
 
+	ioaddr = hdlc_to_dev(&root->hdlc)->base_addr;
 	free_irq(pdev->irq, root);
-	pci_free_consistent(pdev, IRQ_RING_SIZE*sizeof(u32), ppriv->iqcfg, 
+	pci_free_consistent(pdev, IRQ_RING_SIZE*sizeof(u32), ppriv->iqcfg,
 			    ppriv->iqcfg_dma);
-	for (i=0; i < dev_per_card; i++) {
-		struct dscc4_dev_priv *dpriv;
-		struct net_device *dev;
-
-		dev = ppriv->root + i;
-		dscc4_unattach_hdlc_device(dev);
+	for (i = 0; i < dev_per_card; i++) {
+		struct dscc4_dev_priv *dpriv = root + i;
 
-		dpriv = (struct dscc4_dev_priv *)dev->priv;
-		pci_free_consistent(pdev, IRQ_RING_SIZE*sizeof(u32), 
+		pci_free_consistent(pdev, IRQ_RING_SIZE*sizeof(u32),
 				    dpriv->iqrx, dpriv->iqrx_dma);
-		pci_free_consistent(pdev, IRQ_RING_SIZE*sizeof(u32), 
+		pci_free_consistent(pdev, IRQ_RING_SIZE*sizeof(u32),
 				    dpriv->iqtx, dpriv->iqtx_dma);
-		unregister_netdev(dev);
 	}
-	kfree(root->priv);
 
-	iounmap((void *)root->base_addr);
-	kfree(root);
+	dscc4_free1(pdev);
 
-	kfree(ppriv);
+	iounmap((void *)ioaddr);
 
 	release_mem_region(pci_resource_start(pdev, 1),
 			   pci_resource_len(pdev, 1));
@@ -1673,73 +1820,41 @@
 			   pci_resource_len(pdev, 0));
 }
 
-static int dscc4_hdlc_ioctl(struct hdlc_device_struct *hdlc, struct ifreq *ifr, int cmd)
-{
-	struct net_device *dev = (struct net_device *)hdlc->netdev.base_addr;
-	int result;
-
-	/* FIXME: locking ? */
-	result = dscc4_ioctl(dev, ifr, cmd);
-	return result;
-}
-
-static int dscc4_hdlc_open(struct hdlc_device_struct *hdlc)
+static int dscc4_hdlc_attach(hdlc_device *hdlc, unsigned short encoding,
+	unsigned short parity)
 {
-	struct net_device *dev = (struct net_device *)(hdlc->netdev.base_addr);
+	struct net_device *dev = hdlc_to_dev(hdlc);
+	struct dscc4_dev_priv *dpriv = dscc4_priv(dev);
 
-        if (netif_running(dev)) {
-		printk(KERN_DEBUG "%s: already running\n", dev->name); // DEBUG
-		return 0;
-	}
-	return dscc4_open(dev);
-}
-
-static int dscc4_hdlc_xmit(hdlc_device *hdlc, struct sk_buff *skb)
-{
-	struct net_device *dev = (struct net_device *)hdlc->netdev.base_addr;
-
-	return dscc4_start_xmit(skb, dev);
-}
+	if (encoding != ENCODING_NRZ &&
+	    encoding != ENCODING_NRZI &&
+	    encoding != ENCODING_FM_MARK &&
+	    encoding != ENCODING_FM_SPACE &&
+	    encoding != ENCODING_MANCHESTER)
+		return -EINVAL;
 
-static void dscc4_hdlc_close(struct hdlc_device_struct *hdlc)
-{
-	struct net_device *dev = (struct net_device *)hdlc->netdev.base_addr;
-	struct dscc4_dev_priv *dpriv;
+	if (parity != PARITY_NONE &&
+	    parity != PARITY_CRC16_PR0_CCITT &&
+	    parity != PARITY_CRC16_PR1_CCITT &&
+	    parity != PARITY_CRC32_PR0_CCITT &&
+	    parity != PARITY_CRC32_PR1_CCITT)
+		return -EINVAL;
 
-	dpriv = dev->priv;
-	--dpriv->usecount;
+        dpriv->encoding = encoding;
+        dpriv->parity = parity;
+	return 0;
 }
 
-/* Operated under dev lock */
-static int dscc4_attach_hdlc_device(struct net_device *dev) 
+static int __init dscc4_setup(char *str)
 {
-	struct dscc4_dev_priv *dpriv = dev->priv;
-	struct hdlc_device_struct *hdlc;
-	int result;
+	int *args[] = { &debug, &quartz, NULL }, **p = args;
 
-	hdlc = &dpriv->hdlc;
-	/* XXX: Don't look at the next line */
-	hdlc->netdev.base_addr = (unsigned long)dev;
-	hdlc->set_mode = NULL;
-	hdlc->open = dscc4_hdlc_open;
-	hdlc->close = dscc4_hdlc_close;
-	hdlc->ioctl = dscc4_hdlc_ioctl;
-	hdlc->xmit = dscc4_hdlc_xmit;
-
-	result = register_hdlc_device(hdlc);
-	if (!result)
-		dpriv->usecount++;
-	return result;
+	while (*p && (get_option(&str, *p) == 2))
+		p++;
+	return 1;
 }
 
-/* Operated under dev lock */
-static void dscc4_unattach_hdlc_device(struct net_device *dev) 
-{
-	struct dscc4_dev_priv *dpriv = dev->priv;
-
-	unregister_hdlc_device(&dpriv->hdlc);
-	dpriv->usecount--;
-}
+__setup("dscc4.setup=", dscc4_setup);
 
 static struct pci_device_id dscc4_pci_tbl[] __devinitdata = {
 	{ PCI_VENDOR_ID_SIEMENS, PCI_DEVICE_ID_SIEMENS_DSCC4,
@@ -1749,10 +1864,10 @@
 MODULE_DEVICE_TABLE(pci, dscc4_pci_tbl);
 
 static struct pci_driver dscc4_driver = {
-	name:           "dscc4",
-	id_table:       dscc4_pci_tbl,
-	probe:          dscc4_init_one,
-	remove:         dscc4_remove_one,
+	.name		= DRV_NAME,
+	.id_table	= dscc4_pci_tbl,
+	.probe		= dscc4_init_one,
+	.remove		= dscc4_remove_one,
 };
 
 static int __init dscc4_init_module(void)
diff -urN linux-2.4.21-pre2/drivers/net/wan/farsync.c linux-2.4.21-pre3/drivers/net/wan/farsync.c
--- linux-2.4.21-pre2/drivers/net/wan/farsync.c	Fri Dec 21 09:41:55 2001
+++ linux-2.4.21-pre3/drivers/net/wan/farsync.c	Mon Jan  6 16:32:32 2003
@@ -1,5 +1,5 @@
 /*
- *      FarSync X21 driver for Linux (2.4.x kernel version)
+ *      FarSync X21 driver for Linux (generic HDLC version)
  *
  *      Actually sync driver for X.21, V.35 and V.24 on FarSync T-series cards
  *
@@ -18,11 +18,10 @@
 #include <linux/kernel.h>
 #include <linux/pci.h>
 #include <linux/ioport.h>
-#include <linux/netdevice.h>
 #include <linux/init.h>
-#include <linux/if_arp.h>
 #include <asm/uaccess.h>
-#include <net/syncppp.h>
+#include <linux/if.h>
+#include <linux/hdlc.h>
 
 #include "farsync.h"
 
@@ -32,6 +31,7 @@
  */
 MODULE_AUTHOR("R.J.Dunlop <bob.dunlop@farsite.co.uk>");
 MODULE_DESCRIPTION("FarSync T-Series X21 driver. FarSite Communications Ltd.");
+MODULE_LICENSE("GPL");
 
 EXPORT_NO_SYMBOLS;
 
@@ -331,26 +331,15 @@
 /*      Per port (line or channel) information
  */
 struct fst_port_info {
-        void                   *if_ptr; /* Some drivers describe this as a
-                                         * general purpose pointer. However if
-                                         * using syncPPP it has a very specific
-                                         * purpose: it must be the first item in
-                                         * the structure pointed to by dev->priv
-                                         * and must in turn point to the
-                                         * associated ppp_device structure.
-                                         */
+        hdlc_device             hdlc;   /* HDLC device struct - must be first */
         struct fst_card_info   *card;   /* Card we're associated with */
         int                     index;  /* Port index on the card */
-        int                     proto;  /* Protocol we are running */
         int                     hwif;   /* Line hardware (lineInterface copy) */
         int                     run;    /* Port is running */
         int                     rxpos;  /* Next Rx buffer to use */
         int                     txpos;  /* Next Tx buffer to use */
         int                     txipos; /* Next Tx buffer to check for free */
         int                     txcnt;  /* Count of Tx buffers in use */
-        struct net_device      *dev;    /* Kernel network device entry */
-        struct net_device_stats stats;  /* Standard statistics */
-        struct ppp_device       pppdev; /* Link to syncPPP */
 };
 
 /*      Per card information
@@ -370,6 +359,11 @@
         struct fst_port_info ports[ FST_MAX_PORTS ];
 };
 
+/* Convert an HDLC device pointer into a port info pointer and similar */
+#define hdlc_to_port(H) ((struct fst_port_info *)(H))
+#define dev_to_port(D)  hdlc_to_port(dev_to_hdlc(D))
+#define port_to_dev(P)  hdlc_to_dev(&(P)->hdlc)
+
 
 /*
  *      Shared memory window access macros
@@ -632,25 +626,18 @@
 
         if ( signals & (( port->hwif == X21 ) ? IPSTS_INDICATE : IPSTS_DCD ))
         {
-                if ( ! netif_carrier_ok ( port->dev ))
+                if ( ! netif_carrier_ok ( port_to_dev ( port )))
                 {
                         dbg ( DBG_INTR,"DCD active\n");
-
-                        /* Poke sPPP to renegotiate */
-                        if ( port->proto == FST_HDLC || port->proto == FST_PPP )
-                        {
-                                sppp_reopen ( port->dev );
-                        }
-
-                        netif_carrier_on ( port->dev );
+                        netif_carrier_on ( port_to_dev ( port ));
                 }
         }
         else
         {
-                if ( netif_carrier_ok ( port->dev ))
+                if ( netif_carrier_ok ( port_to_dev ( port )))
                 {
                         dbg ( DBG_INTR,"DCD lost\n");
-                        netif_carrier_off ( port->dev );
+                        netif_carrier_off ( port_to_dev ( port ));
                 }
         }
 }
@@ -693,24 +680,24 @@
                                         len );
         if ( dmabits != ( RX_STP | RX_ENP ) || len > LEN_RX_BUFFER - 2 )
         {
-                port->stats.rx_errors++;
+                port->hdlc.stats.rx_errors++;
 
                 /* Update error stats and discard buffer */
                 if ( dmabits & RX_OFLO )
                 {
-                        port->stats.rx_fifo_errors++;
+                        port->hdlc.stats.rx_fifo_errors++;
                 }
                 if ( dmabits & RX_CRC )
                 {
-                        port->stats.rx_crc_errors++;
+                        port->hdlc.stats.rx_crc_errors++;
                 }
                 if ( dmabits & RX_FRAM )
                 {
-                        port->stats.rx_frame_errors++;
+                        port->hdlc.stats.rx_frame_errors++;
                 }
                 if ( dmabits == ( RX_STP | RX_ENP ))
                 {
-                        port->stats.rx_length_errors++;
+                        port->hdlc.stats.rx_length_errors++;
                 }
 
                 /* Discard buffer descriptors until we see the end of packet
@@ -747,7 +734,7 @@
         {
                 dbg ( DBG_RX,"intr_rx: can't allocate buffer\n");
 
-                port->stats.rx_dropped++;
+                port->hdlc.stats.rx_dropped++;
 
                 /* Return descriptor to card */
                 FST_WRB ( card, rxDescrRing[pi][rxp].bits, DMA_OWN );
@@ -771,29 +758,16 @@
                 port->rxpos = rxp;
 
         /* Update stats */
-        port->stats.rx_packets++;
-        port->stats.rx_bytes += len;
+        port->hdlc.stats.rx_packets++;
+        port->hdlc.stats.rx_bytes += len;
 
         /* Push upstream */
-        if ( port->proto == FST_HDLC || port->proto == FST_PPP )
-        {
-                /* Mark for further processing by sPPP module */
-                skb->protocol = htons ( ETH_P_WAN_PPP );
-        }
-        else
-        {
-                /* DEC customer specific protocol (since nothing defined for
-                 * marking raw data), at least one other driver uses this value
-                 * for this purpose.
-                 */
-                skb->protocol = htons ( ETH_P_CUST );
-                skb->pkt_type = PACKET_HOST;
-        }
         skb->mac.raw = skb->data;
-        skb->dev = port->dev;
+        skb->dev = hdlc_to_dev ( &port->hdlc );
+        skb->protocol = htons ( ETH_P_HDLC );
         netif_rx ( skb );
 
-        port->dev->last_rx = jiffies;
+        port_to_dev ( port )->last_rx = jiffies;
 }
 
 
@@ -844,7 +818,7 @@
                 case CTLB_CHG:
                 case CTLC_CHG:
                 case CTLD_CHG:
-                        if ( port->run && port->dev != NULL )
+                        if ( port->run )
                                 fst_intr_ctlchg ( card, port );
                         break;
 
@@ -863,9 +837,8 @@
                          * always load up the entire packet for DMA.
                          */
                         dbg ( DBG_TX,"Tx underflow port %d\n", event & 0x03 );
-
-                        port->stats.tx_errors++;
-                        port->stats.tx_fifo_errors++;
+                        port->hdlc.stats.tx_errors++;
+                        port->hdlc.stats.tx_fifo_errors++;
                         break;
 
                 case INIT_CPLT:
@@ -890,7 +863,7 @@
 
         for ( pi = 0, port = card->ports ; pi < card->nports ; pi++, port++ )
         {
-                if ( port->dev == NULL || ! port->run )
+                if ( ! port->run )
                         continue;
 
                 /* Check for rx completions */
@@ -907,7 +880,7 @@
                         --port->txcnt;
                         if ( ++port->txipos >= NUM_TX_BUFFER )
                                 port->txipos = 0;
-                        netif_wake_queue ( port->dev );
+                        netif_wake_queue ( port_to_dev ( port ));
                 }
         }
 
@@ -969,145 +942,28 @@
 
 
 static int
-fst_change_mtu ( struct net_device *dev, int new_mtu )
-{
-        if ( new_mtu < 128 || new_mtu > FST_MAX_MTU )
-                return -EINVAL;
-        dev->mtu = new_mtu;
-        return 0;
-}
-
-
-/* Sooner or later you can't avoid a forward declaration */
-static int fst_ioctl ( struct net_device *dev, struct ifreq *ifr, int cmd );
-
-static int
-switch_proto ( struct fst_port_info *port, int new_proto )
-{
-        int err;
-        int orig_mtu;
-        struct net_device *dev;
-
-        dev = port->dev;
-
-        /* Turn off sPPP module ? */
-        if (( new_proto != FST_HDLC && new_proto != FST_PPP )
-                && ( port->proto == FST_HDLC || port->proto == FST_PPP ))
-        {
-                sppp_close ( port->pppdev.dev );
-                sppp_detach ( port->pppdev.dev );
-
-                /* Reset some fields overwritten by sPPP */
-                dev->hard_header     = NULL;
-                dev->rebuild_header  = NULL;
-                dev->tx_queue_len    = FST_TX_QUEUE_LEN;
-                dev->type            = ARPHRD_MYTYPE;
-                dev->addr_len        = 0;
-                dev->hard_header_len = 0;
-                dev->do_ioctl        = fst_ioctl;
-                dev->change_mtu      = fst_change_mtu;
-                dev->flags           = IFF_POINTOPOINT|IFF_NOARP;
-
-                return 0;
-        }
-
-        /* Turn on sPPP ? */
-        if (( new_proto == FST_HDLC || new_proto == FST_PPP )
-                && ( port->proto != FST_HDLC && port->proto != FST_PPP ))
-        {
-                orig_mtu = dev->mtu;
-
-                /* Attach to sync PPP module */
-                port->pppdev.dev = dev;
-                sppp_attach ( &port->pppdev );
-
-                if ( orig_mtu < dev->mtu )
-                        dev->change_mtu ( dev, orig_mtu );
-
-                /* Claw back the ioctl routine. We promise to be good and call
-                 * the sync PPP routines once we've eliminated our functions.
-                 */
-                dev->do_ioctl = fst_ioctl;
-
-                /* Set the mode */
-                if ( new_proto == FST_HDLC )
-                {
-                        err = sppp_do_ioctl ( port->pppdev.dev, NULL,
-                                                                SPPPIOCCISCO );
-                }
-                else
-                {
-                        err = sppp_do_ioctl ( port->pppdev.dev, NULL,
-                                                                SPPPIOCPPP );
-                }
-
-                /* Open the device */
-                if ( err == 0 )
-                {
-                        (void) sppp_open ( port->dev );
-                }
-
-                return err;
-        }
-
-        /* Switch sPPP mode to that desired */
-        err = 0;
-        if ( new_proto == FST_HDLC && port->pppdev.dev != NULL )
-        {
-                err = sppp_do_ioctl ( port->pppdev.dev, NULL, SPPPIOCCISCO );
-        }
-        else if ( new_proto == FST_PPP && port->pppdev.dev != NULL )
-        {
-                err = sppp_do_ioctl ( port->pppdev.dev, NULL, SPPPIOCPPP );
-        }
-
-        /* Anything else is switching from one raw mode to another which is
-         * basically a NOP
-         */
-
-        return err;
-}
-
-
-static int
 set_conf_from_info ( struct fst_card_info *card, struct fst_port_info *port,
                 struct fstioc_info *info )
 {
         int err;
 
-        /* Set things according to the user set valid flags */
+        /* Set things according to the user set valid flags.
+         * Several of the old options have been invalidated/replaced by the
+         * generic HDLC package.
+         */
         err = 0;
         if ( info->valid & FSTVAL_PROTO )
-        {
-                if ( port->proto != info->proto )
-                {
-                        err = switch_proto ( port, info->proto );
-                        if ( err == 0 )
-                                port->proto = info->proto;
-                }
-        }
+                err = -EINVAL;
         if ( info->valid & FSTVAL_CABLE )
-        {
-                FST_WRB ( card, portConfig[port->index].lineInterface,
-                                        info->lineInterface );
-                port->hwif = info->lineInterface;
-        }
+                err = -EINVAL;
         if ( info->valid & FSTVAL_SPEED )
-        {
-                FST_WRL ( card, portConfig[port->index].lineSpeed,
-                                        info->lineSpeed );
-                FST_WRB ( card, portConfig[port->index].internalClock,
-                                        info->internalClock );
-        }
+                err = -EINVAL;
+
         if ( info->valid & FSTVAL_MODE )
-        {
                 FST_WRW ( card, cardMode, info->cardMode );
-        }
 #if FST_DEBUG
         if ( info->valid & FSTVAL_DEBUG )
-        {
                 fst_debug_mask = info->debug;
-        }
 #endif
 
         return err;
@@ -1125,7 +981,7 @@
         info->nports = card->nports;
         info->type   = card->type;
         info->state  = card->state;
-        info->proto  = port->proto;
+        info->proto  = FST_GEN_HDLC;
         info->index  = i;
 #if FST_DEBUG
         info->debug  = fst_debug_mask;
@@ -1154,6 +1010,107 @@
 
 
 static int
+fst_set_iface ( struct fst_card_info *card, struct fst_port_info *port,
+                struct ifreq *ifr )
+{
+        sync_serial_settings sync;
+        int i;
+
+        if (copy_from_user (&sync, ifr->ifr_settings.ifs_ifsu.sync,
+			    sizeof (sync)))
+                return -EFAULT;
+
+        if ( sync.loopback )
+                return -EINVAL;
+
+        i = port->index;
+
+        switch (ifr->ifr_settings.type)
+        {
+        case IF_IFACE_V35:
+                FST_WRW ( card, portConfig[i].lineInterface, V35 );
+                port->hwif = V35;
+                break;
+
+        case IF_IFACE_V24:
+                FST_WRW ( card, portConfig[i].lineInterface, V24 );
+                port->hwif = V24;
+                break;
+
+        case IF_IFACE_X21:
+                FST_WRW ( card, portConfig[i].lineInterface, X21 );
+                port->hwif = X21;
+                break;
+
+        case IF_IFACE_SYNC_SERIAL:
+                break;
+
+        default:
+                return -EINVAL;
+        }
+
+        switch ( sync.clock_type )
+        {
+        case CLOCK_EXT:
+                FST_WRB ( card, portConfig[i].internalClock, EXTCLK );
+                break;
+
+        case CLOCK_INT:
+                FST_WRB ( card, portConfig[i].internalClock, INTCLK );
+                break;
+
+        default:
+                return -EINVAL;
+        }
+        FST_WRL ( card, portConfig[i].lineSpeed, sync.clock_rate );
+        return 0;
+}
+
+static int
+fst_get_iface ( struct fst_card_info *card, struct fst_port_info *port,
+                struct ifreq *ifr )
+{
+        sync_serial_settings sync;
+        int i;
+
+        /* First check what line type is set, we'll default to reporting X.21
+         * if nothing is set as IF_IFACE_SYNC_SERIAL implies it can't be
+         * changed
+         */
+        switch ( port->hwif )
+        {
+        case V35:
+                ifr->ifr_settings.type = IF_IFACE_V35;
+                break;
+        case V24:
+                ifr->ifr_settings.type = IF_IFACE_V24;
+                break;
+        case X21:
+        default:
+                ifr->ifr_settings.type = IF_IFACE_X21;
+                break;
+        }
+
+	if (ifr->ifr_settings.size < sizeof(sync)) {
+		ifr->ifr_settings.size = sizeof(sync); /* data size wanted */
+		return -ENOBUFS;
+	}
+
+        i = port->index;
+        sync.clock_rate = FST_RDL ( card, portConfig[i].lineSpeed );
+        /* Lucky card and linux use same encoding here */
+        sync.clock_type = FST_RDB ( card, portConfig[i].internalClock );
+        sync.loopback = 0;
+
+        if (copy_to_user (ifr->ifr_settings.ifs_ifsu.sync, &sync,
+			  sizeof(sync)))
+                return -EFAULT;
+
+        return 0;
+}
+
+
+static int
 fst_ioctl ( struct net_device *dev, struct ifreq *ifr, int cmd )
 {
         struct fst_card_info *card;
@@ -1164,7 +1121,7 @@
 
         dbg ( DBG_IOCTL,"ioctl: %x, %p\n", cmd, ifr->ifr_data );
 
-        port = dev->priv;
+        port = dev_to_port ( dev );
         card = port->card;
 
         if ( !capable ( CAP_NET_ADMIN ))
@@ -1201,7 +1158,7 @@
                  * when going over the top
                  */
                 if ( wrthdr.size > FST_MEMSIZE || wrthdr.offset > FST_MEMSIZE
-                                || wrthdr.size + wrthdr.offset > FST_MEMSIZE )
+				|| wrthdr.size + wrthdr.offset > FST_MEMSIZE )
                 {
                         return -ENXIO;
                 }
@@ -1261,6 +1218,9 @@
 
         case FSTSETCONF:
 
+                /* Most of the setting have been moved to the generic ioctls
+                 * this just covers debug and board ident mode now
+                 */
                 if ( copy_from_user ( &info,  ifr->ifr_data, sizeof ( info )))
                 {
                         return -EFAULT;
@@ -1268,12 +1228,25 @@
 
                 return set_conf_from_info ( card, port, &info );
 
+        case SIOCWANDEV:
+                switch (ifr->ifr_settings.type)
+                {
+                case IF_GET_IFACE:
+                        return fst_get_iface ( card, port, ifr );
+
+                case IF_IFACE_SYNC_SERIAL:
+                case IF_IFACE_V35:
+                case IF_IFACE_V24:
+                case IF_IFACE_X21:
+                        return fst_set_iface ( card, port, ifr );
+
+                default:
+                        return hdlc_ioctl ( dev, ifr, cmd );
+                }
+
         default:
-                /* Not one of ours. Pass it through to sPPP package */
-                if ( port->proto == FST_HDLC || port->proto == FST_PPP )
-                        return sppp_do_ioctl ( dev, ifr, cmd );
-                else
-                        return -EINVAL;
+                /* Not one of ours. Pass through to HDLC package */
+                return hdlc_ioctl ( dev, ifr, cmd );
         }
 }
 
@@ -1306,9 +1279,9 @@
                 signals = FST_RDL ( port->card, v24DebouncedSts[port->index]);
                 if ( signals & (( port->hwif == X21 ) ? IPSTS_INDICATE
                                                       : IPSTS_DCD ))
-                        netif_carrier_on ( port->dev );
+                        netif_carrier_on ( port_to_dev ( port ));
                 else
-                        netif_carrier_off ( port->dev );
+                        netif_carrier_off ( port_to_dev ( port ));
         }
 }
 
@@ -1335,64 +1308,15 @@
 static int
 fst_open ( struct net_device *dev )
 {
-        struct fst_card_info *card;
-        struct fst_port_info *port;
-        int orig_mtu;
         int err;
 
-        MOD_INC_USE_COUNT;
-
-        port = dev->priv;
-        card = port->card;
-
-        switch ( port->proto )
-        {
-        case FST_HDLC:
-        case FST_PPP:
-
-                orig_mtu = dev->mtu;
-
-                /* Attach to sync PPP module */
-                port->pppdev.dev = dev;
-                sppp_attach ( &port->pppdev );
-
-                if ( orig_mtu < dev->mtu )
-                        dev->change_mtu ( dev, orig_mtu );
-
-                /* Claw back the ioctl routine. We promise to be good and call
-                 * the sync PPP routines once we've eliminated our functions.
-                 */
-                dev->do_ioctl = fst_ioctl;
-
-                err = sppp_do_ioctl ( dev, NULL, port->proto == FST_HDLC
-                                                ? SPPPIOCCISCO : SPPPIOCPPP );
-                if ( err )
-		{
-			sppp_detach ( dev );
-			MOD_DEC_USE_COUNT;
-			return err;
-		}
-
-                err = sppp_open ( dev );
-                if ( err )
-		{
-			sppp_detach ( dev );
-			MOD_DEC_USE_COUNT;
-			return err;
-		}
-                break;
-
-        case FST_MONITOR:
-        case FST_RAW:
-                break;
-
-        default:
-                dbg ( DBG_OPEN,"open: Unknown proto %d\n", port->proto );
-                break;
-        }
+        err = hdlc_open ( dev_to_hdlc ( dev ));
+        if ( err )
+                return err;
 
-        fst_openport ( port );
+        MOD_INC_USE_COUNT;
 
+        fst_openport ( dev_to_port ( dev ));
         netif_wake_queue ( dev );
         return 0;
 }
@@ -1400,34 +1324,22 @@
 static int
 fst_close ( struct net_device *dev )
 {
-        struct fst_port_info *port;
-
-        port = dev->priv;
-
         netif_stop_queue ( dev );
-        switch ( port->proto )
-        {
-        case FST_HDLC:
-        case FST_PPP:
-                sppp_close ( dev );
-                sppp_detach ( dev );
-                break;
-
-        case FST_MONITOR:
-        case FST_RAW:
-                break;
-
-        default:
-                dbg ( DBG_OPEN,"close: Unknown proto %d\n", port->proto );
-                break;
-        }
-
-        fst_closeport ( port );
-
+        fst_closeport ( dev_to_port ( dev ));
+        hdlc_close ( dev_to_hdlc  ( dev ));
         MOD_DEC_USE_COUNT;
         return 0;
 }
 
+static int
+fst_attach ( hdlc_device *hdlc, unsigned short encoding, unsigned short parity )
+{
+        /* Setting currently fixed in FarSync card so we check and forget */
+        if ( encoding != ENCODING_NRZ || parity != PARITY_CRC16_PR1_CCITT )
+                return -EINVAL;
+        return 0;
+}
+
 
 static void
 fst_tx_timeout ( struct net_device *dev )
@@ -1436,10 +1348,10 @@
 
         dbg ( DBG_INTR | DBG_TX,"tx_timeout\n");
 
-        port = dev->priv;
+        port = dev_to_port ( dev );
 
-        port->stats.tx_errors++;
-        port->stats.tx_aborted_errors++;
+        port->hdlc.stats.tx_errors++;
+        port->hdlc.stats.tx_aborted_errors++;
 
         if ( port->txcnt > 0 )
                 fst_issue_cmd ( port, ABORTTX );
@@ -1459,22 +1371,15 @@
         int pi;
         int txp;
 
-        port = dev->priv;
+        port = dev_to_port ( dev );
         card = port->card;
 
-        /* Drop packet if in monitor (rx only) mode */
-        if ( port->proto == FST_MONITOR )
-        {
-                dev_kfree_skb ( skb );
-                return 0;
-        }
-
         /* Drop packet with error if we don't have carrier */
         if ( ! netif_carrier_ok ( dev ))
         {
                 dev_kfree_skb ( skb );
-                port->stats.tx_errors++;
-                port->stats.tx_carrier_errors++;
+                port->hdlc.stats.tx_errors++;
+                port->hdlc.stats.tx_carrier_errors++;
                 return 0;
         }
 
@@ -1484,7 +1389,7 @@
                 dbg ( DBG_TX,"Packet too large %d vs %d\n", skb->len,
                                                 LEN_TX_BUFFER );
                 dev_kfree_skb ( skb );
-                port->stats.tx_errors++;
+                port->hdlc.stats.tx_errors++;
                 return 0;
         }
 
@@ -1498,7 +1403,7 @@
                 spin_unlock_irqrestore ( &card->card_lock, flags );
                 dbg ( DBG_TX,"Out of Tx buffers\n");
                 dev_kfree_skb ( skb );
-                port->stats.tx_errors++;
+                port->hdlc.stats.tx_errors++;
                 return 0;
         }
         if ( ++port->txpos >= NUM_TX_BUFFER )
@@ -1518,8 +1423,8 @@
         FST_WRW ( card, txDescrRing[pi][txp].bcnt, cnv_bcnt ( skb->len ));
         FST_WRB ( card, txDescrRing[pi][txp].bits, DMA_OWN | TX_STP | TX_ENP );
 
-        port->stats.tx_packets++;
-        port->stats.tx_bytes += skb->len;
+        port->hdlc.stats.tx_packets++;
+        port->hdlc.stats.tx_bytes += skb->len;
 
         dev_kfree_skb ( skb );
 
@@ -1528,18 +1433,6 @@
 }
 
 
-static struct net_device_stats *
-fst_get_stats ( struct net_device *dev )
-{
-        struct fst_port_info *port;
-
-        if (( port = dev->priv ) != NULL )
-                return &port->stats;
-        else
-                return NULL;
-}
-
-
 /*
  *      Card setup having checked hardware resources.
  *      Should be pretty bizarre if we get an error here (kernel memory
@@ -1566,34 +1459,13 @@
          */
         for ( i = 0 ; i < card->nports ; i++ )
         {
-                card->ports[i].if_ptr = &card->ports[i].pppdev;
                 card->ports[i].card   = card;
                 card->ports[i].index  = i;
-                card->ports[i].proto  = FST_HDLC;
                 card->ports[i].run    = 0;
 
-                dev = kmalloc ( sizeof ( struct net_device ), GFP_KERNEL );
-                if ( dev == NULL )
-                {
-                        printk_err ("Cannot allocate net_device for port %d\n",
-                                        i );
-                        /* No point going any further */
-                        card->nports = i;
-                        break;
-                }
-                memset ( dev, 0, sizeof ( struct net_device ));
-                card->ports[i].dev = dev;
-
-                if ( dev_alloc_name ( dev, FST_NDEV_NAME "%d") < 0 )
-                {
-                        printk_err ("Cannot allocate i/f name for port %d\n",
-                                        i );
-                        kfree ( dev );
-                        card->nports = i;
-                        break;
-                }
+                dev = hdlc_to_dev ( &card->ports[i].hdlc );
 
-                /* Fill in remainder of the net device info */
+                /* Fill in the net device info */
                                 /* Since this is a PCI setup this is purely
                                  * informational. Give them the buffer addresses
                                  * and basic card I/O.
@@ -1609,26 +1481,19 @@
                 dev->base_addr   = card->pci_conf;
                 dev->irq         = card->irq;
 
-                dev->get_stats          = fst_get_stats;
-                dev->mtu                = FST_DEF_MTU;
-                dev->change_mtu         = fst_change_mtu;
-                dev->priv               = &card->ports[i];
-                dev->tx_queue_len       = FST_TX_QUEUE_LEN;
-                dev->type               = ARPHRD_MYTYPE;
-                dev->addr_len           = 0;
-                dev->open               = fst_open;
-                dev->stop               = fst_close;
-                dev->hard_start_xmit    = fst_start_xmit;
-                dev->do_ioctl           = fst_ioctl;
-                dev->watchdog_timeo     = FST_TX_TIMEOUT;
-                dev->tx_timeout         = fst_tx_timeout;
-                dev->flags              = IFF_POINTOPOINT|IFF_NOARP;
-
-                if (( err = register_netdev ( dev )) < 0 )
-                {
-                        printk_err ("Cannot register %s (errno %d)\n",
-                                        dev->name, -err );
-                        kfree ( dev );
+                dev->tx_queue_len          = FST_TX_QUEUE_LEN;
+                dev->open                  = fst_open;
+                dev->stop                  = fst_close;
+                dev->do_ioctl              = fst_ioctl;
+                dev->watchdog_timeo        = FST_TX_TIMEOUT;
+                dev->tx_timeout            = fst_tx_timeout;
+                card->ports[i].hdlc.attach = fst_attach;
+                card->ports[i].hdlc.xmit   = fst_start_xmit;
+
+                if (( err = register_hdlc_device ( &card->ports[i].hdlc )) < 0 )
+                {
+                        printk_err ("Cannot register HDLC device for port %d"
+                                    " (errno %d)\n", i, -err );
                         card->nports = i;
                         break;
                 }
@@ -1637,8 +1502,8 @@
         spin_lock_init ( &card->card_lock );
 
         printk ( KERN_INFO "%s-%s: %s IRQ%d, %d ports\n",
-                        card->ports[0].dev->name,
-                        card->ports[card->nports-1].dev->name,
+                        hdlc_to_dev(&card->ports[0].hdlc)->name,
+                        hdlc_to_dev(&card->ports[card->nports-1].hdlc)->name,
                         type_strings[card->type], card->irq, card->nports );
 }
 
@@ -1789,8 +1654,7 @@
 
         for ( i = 0 ; i < card->nports ; i++ )
         {
-                unregister_netdev ( card->ports[i].dev );
-                kfree ( card->ports[i].dev );
+                unregister_hdlc_device ( &card->ports[i].hdlc );
         }
 
         fst_disable_intr ( card );
@@ -1830,4 +1694,3 @@
 module_init ( fst_init );
 module_exit ( fst_cleanup_module );
 
-MODULE_LICENSE("GPL");
diff -urN linux-2.4.21-pre2/drivers/net/wan/farsync.h linux-2.4.21-pre3/drivers/net/wan/farsync.h
--- linux-2.4.21-pre2/drivers/net/wan/farsync.h	Sun Aug 12 10:38:48 2001
+++ linux-2.4.21-pre3/drivers/net/wan/farsync.h	Mon Jan  6 16:32:32 2003
@@ -32,13 +32,8 @@
  *      A short common prefix is useful for routines within the driver to avoid
  *      conflict with other similar drivers and I chosen to use "fst_" for this
  *      purpose (FarSite T-series).
- *
- *      Finally the device driver needs a short network interface name. Since
- *      "hdlc" is already in use I've chosen the even less informative "sync"
- *      for the present.
  */
 #define FST_NAME                "fst"           /* In debug/info etc */
-#define FST_NDEV_NAME           "sync"          /* For net interface */
 #define FST_DEV_NAME            "farsync"       /* For misc interfaces */
 
 
diff -urN linux-2.4.21-pre2/drivers/net/wan/hd64570.h linux-2.4.21-pre3/drivers/net/wan/hd64570.h
--- linux-2.4.21-pre2/drivers/net/wan/hd64570.h	Tue Mar  6 19:44:36 2001
+++ linux-2.4.21-pre3/drivers/net/wan/hd64570.h	Mon Jan  6 16:32:32 2003
@@ -152,7 +152,7 @@
 	u32 bp;			/* Buffer Pointer (24 bits) */
 	u16 len;		/* Data Length */
 	u8 stat;		/* Status */
-	u8 unused2;
+	u8 unused;		/* pads to 2-byte boundary */
 }__attribute__ ((packed)) pkt_desc;
 
 
@@ -202,7 +202,11 @@
 #define MD0_CRC_ITU_0 0x06
 #define MD0_CRC_ITU   0x07
 
-#define MD2_NRZI      0x20	/* NRZI mode */
+#define MD2_NRZ	      0x00
+#define MD2_NRZI      0x20
+#define MD2_MANCHESTER 0x80
+#define MD2_FM_MARK   0xA0
+#define MD2_FM_SPACE  0xC0
 #define MD2_LOOPBACK  0x03      /* Local data Loopback */
 
 #define CTL_NORTS     0x01
diff -urN linux-2.4.21-pre2/drivers/net/wan/hd6457x.c linux-2.4.21-pre3/drivers/net/wan/hd6457x.c
--- linux-2.4.21-pre2/drivers/net/wan/hd6457x.c	Tue Mar  6 19:44:36 2001
+++ linux-2.4.21-pre3/drivers/net/wan/hd6457x.c	Mon Jan  6 16:32:32 2003
@@ -42,13 +42,7 @@
 #error Either hd64570.h or hd64572.h must be included
 #endif
 
-
-static card_t *first_card;
-static card_t **new_card = &first_card;
-
-
-/* Maximum events to handle at each interrupt - should I increase it? */
-#define INTR_WORK 4
+static char sca_version[]="1.09";
 
 #define get_msci(port)	  (phy_node(port) ?   MSCI1_OFFSET :   MSCI0_OFFSET)
 #define get_dmac_rx(port) (phy_node(port) ? DMAC1RX_OFFSET : DMAC0RX_OFFSET)
@@ -63,11 +57,19 @@
 #define sca_ina(reg, card)		sca_inw(reg, card)
 #define writea(value, ptr)		writew(value, ptr)
 
+#else /* HD64572 */
+#define sca_outa(value, reg, card)	sca_outl(value, reg, card)
+#define sca_ina(reg, card)		sca_inl(reg, card)
+#define writea(value, ptr)		writel(value, ptr)
+#endif
+
 static inline int sca_intr_status(card_t *card)
 {
+	u8 result = 0;
+
+#ifdef __HD64570_H /* HD64570 */
 	u8 isr0 = sca_in(ISR0, card);
 	u8 isr1 = sca_in(ISR1, card);
-	u8 result = 0;
 
 	if (isr1 & 0x03) result |= SCA_INTR_DMAC_RX(0);
 	if (isr1 & 0x0C) result |= SCA_INTR_DMAC_TX(0);
@@ -76,19 +78,8 @@
 	if (isr0 & 0x0F) result |= SCA_INTR_MSCI(0);
 	if (isr0 & 0xF0) result |= SCA_INTR_MSCI(1);
 
-	return result;
-}
-
 #else /* HD64572 */
-#define sca_outa(value, reg, card)	sca_outl(value, reg, card)
-#define sca_ina(reg, card)		sca_inl(reg, card)
-#define writea(value, ptr)		writel(value, ptr)
-
-
-static inline int sca_intr_status(card_t *card)
-{
 	u32 isr0 = sca_inl(ISR0, card);
-	u8 result = 0;
 
 	if (isr0 & 0x0000000F) result |= SCA_INTR_DMAC_RX(0);
 	if (isr0 & 0x000000F0) result |= SCA_INTR_DMAC_TX(0);
@@ -97,11 +88,17 @@
 	if (isr0 & 0x003E0000) result |= SCA_INTR_MSCI(0);
 	if (isr0 & 0x3E000000) result |= SCA_INTR_MSCI(1);
 
-	return result;
-}
-
 #endif /* HD64570 vs HD64572 */
 
+	if (!(result & SCA_INTR_DMAC_TX(0)))
+		if (sca_in(DSR_TX(0), card) & DSR_EOM)
+			result |= SCA_INTR_DMAC_TX(0);
+	if (!(result & SCA_INTR_DMAC_TX(1)))
+		if (sca_in(DSR_TX(1), card) & DSR_EOM)
+			result |= SCA_INTR_DMAC_TX(1);
+
+	return result;
+}
 
 
 
@@ -250,8 +247,7 @@
 
 
 
-static inline void sca_rx(card_t *card, port_t *port, pkt_desc *desc,
-			      u8 rxin)
+static inline void sca_rx(card_t *card, port_t *port, pkt_desc *desc, u8 rxin)
 {
 	struct sk_buff *skb;
 	u16 len;
@@ -289,13 +285,17 @@
 	openwin(card, 0);
 #endif
 	skb_put(skb, len);
-#ifdef DEBUG_PKT
+#ifdef CONFIG_HDLC_DEBUG_PKT
 	printk(KERN_DEBUG "%s RX(%i):", hdlc_to_name(&port->hdlc), skb->len);
 	debug_frame(skb);
 #endif
 	port->hdlc.stats.rx_packets++;
 	port->hdlc.stats.rx_bytes += skb->len;
-	hdlc_netif_rx(&port->hdlc, skb);
+	skb->mac.raw = skb->data;
+	skb->dev = hdlc_to_dev(&port->hdlc);
+	skb->dev->last_rx = jiffies;
+	skb->protocol = htons(ETH_P_HDLC);
+	netif_rx(skb);
 }
 
 
@@ -320,14 +320,9 @@
 		pkt_desc *desc;
 		u32 cda = sca_ina(dmac + CDAL, card);
 
-		if (cda == desc_off)
+		if ((cda >= desc_off) && (cda < desc_off + sizeof(pkt_desc)))
 			break;	/* No frame received */
 
-#ifdef __HD64572_H
-		if (cda == desc_off + 8)
-			break;	/* SCA-II updates CDA in 2 steps */
-#endif
-
 		desc = desc_address(port, port->rxin, 0);
 		stat = readb(&desc->stat);
 		if (!(stat & ST_RX_EOM))
@@ -371,20 +366,17 @@
 		DSR_TX(phy_node(port)), card);
 
 	while (1) {
-		u32 desc_off = desc_offset(port, port->txlast, 1);
 		pkt_desc *desc;
-		u16 len;
 
-		if (sca_ina(dmac + CDAL, card) == desc_off)
+		u32 desc_off = desc_offset(port, port->txlast, 1);
+		u32 cda = sca_ina(dmac + CDAL, card);
+		if ((cda >= desc_off) && (cda < desc_off + sizeof(pkt_desc)))
 			break;	/* Transmitter is/will_be sending this frame */
 
 		desc = desc_address(port, port->txlast, 1);
-		len = readw(&desc->len);
-
 		port->hdlc.stats.tx_packets++;
-		port->hdlc.stats.tx_bytes += len;
+		port->hdlc.stats.tx_bytes += readw(&desc->len);
 		writeb(0, &desc->stat);	/* Free descriptor */
-
 		port->txlast = (port->txlast + 1) %
 			port_to_card(port)->ring_buffers;
 	}
@@ -398,7 +390,8 @@
 static void sca_intr(int irq, void* dev_id, struct pt_regs *regs)
 {
 	card_t *card = dev_id;
-	int boguscnt = INTR_WORK;
+/* Maximum events to handle at each interrupt - should I increase it? */
+	int boguscnt = 4;
 	int i;
 	u8 stat;
 
@@ -421,9 +414,11 @@
 			}
 
 			if (--boguscnt < 0) {
+#if 0
 				printk(KERN_ERR "%s: too much work at "
 				       "interrupt\n",
 				       hdlc_to_name(&port->hdlc));
+#endif
 				goto exit;
 			}
 		}
@@ -433,51 +428,27 @@
 #ifndef ALL_PAGES_ALWAYS_MAPPED
 	openwin(card, page);		/* Restore original page */
 #endif
+	return;
 }
 
 
 
-static inline int sca_set_loopback(port_t *port, int line)
+static void sca_set_port(port_t *port)
 {
 	card_t* card = port_to_card(port);
 	u8 msci = get_msci(port);
 	u8 md2 = sca_in(msci + MD2, card);
-
-	switch(line) {
-	case LINE_DEFAULT:
-		md2 &= ~MD2_LOOPBACK;
-		port->line &= ~LINE_LOOPBACK;
-		break;
-
-	case LINE_LOOPBACK:
-		md2 |= MD2_LOOPBACK;
-		port->line |= LINE_LOOPBACK;
-		break;
-
-	default:
-		return -EINVAL;
-	}
-
-	sca_out(md2, msci + MD2, card);
-	return 0;
-}
-
-
-
-static void sca_set_clock(port_t *port)
-{
-	card_t *card = port_to_card(port);
-	u8 msci = get_msci(port);
 	unsigned int tmc, br = 10, brv = 1024;
 
-	if (port->clkrate > 0) {
+
+	if (port->settings.clock_rate > 0) {
 		/* Try lower br for better accuracy*/
 		do {
 			br--;
 			brv >>= 1; /* brv = 2^9 = 512 max in specs */
 
 			/* Baud Rate = CLOCK_BASE / TMC / 2^BR */
-			tmc = CLOCK_BASE / (brv * port->clkrate);
+			tmc = CLOCK_BASE / (brv * port->settings.clock_rate);
 		}while(br > 1 && tmc <= 128);
 
 		if (tmc < 1) {
@@ -487,11 +458,11 @@
 		} else if (tmc > 255)
 			tmc = 256; /* tmc=0 means 256 - low baud rates */
 
-		port->clkrate = CLOCK_BASE / (brv * tmc);
+		port->settings.clock_rate = CLOCK_BASE / (brv * tmc);
 	} else {
 		br = 9; /* Minimum clock rate */
 		tmc = 256;	/* 8bit = 0 */
-		port->clkrate = CLOCK_BASE / (256 * 512);
+		port->settings.clock_rate = CLOCK_BASE / (256 * 512);
 	}
 
 	port->rxs = (port->rxs & ~CLK_BRG_MASK) | br;
@@ -509,27 +480,59 @@
 	/* Set BRG bits */
 	sca_out(port->rxs, msci + RXS, card);
 	sca_out(port->txs, msci + TXS, card);
+
+	if (port->settings.loopback)
+		md2 |= MD2_LOOPBACK;
+	else
+		md2 &= ~MD2_LOOPBACK;
+
+	sca_out(md2, msci + MD2, card);
+
 }
 
 
 
-static void sca_set_hdlc_mode(port_t *port, u8 idle, u8 crc, u8 nrzi)
+static void sca_open(hdlc_device *hdlc)
 {
+	port_t *port = hdlc_to_port(hdlc);
 	card_t* card = port_to_card(port);
 	u8 msci = get_msci(port);
-	u8 md2 = (nrzi ? MD2_NRZI : 0) |
-		((port->line & LINE_LOOPBACK) ? MD2_LOOPBACK : 0);
-	u8 ctl = (idle ? CTL_IDLE : 0);
-#ifdef __HD64572_H
-	ctl |= CTL_URCT | CTL_URSKP; /* Skip the rest of underrun frame */
-#endif
+	u8 md0, md2;
+
+	switch(port->encoding) {
+	case ENCODING_NRZ:	md2 = MD2_NRZ;		break;
+	case ENCODING_NRZI:	md2 = MD2_NRZI;		break;
+	case ENCODING_FM_MARK:	md2 = MD2_FM_MARK;	break;
+	case ENCODING_FM_SPACE:	md2 = MD2_FM_SPACE;	break;
+	default:		md2 = MD2_MANCHESTER;
+	}
+
+	if (port->settings.loopback)
+		md2 |= MD2_LOOPBACK;
+
+	switch(port->parity) {
+	case PARITY_CRC16_PR0:	     md0 = MD0_HDLC | MD0_CRC_16_0;  break;
+	case PARITY_CRC16_PR1:	     md0 = MD0_HDLC | MD0_CRC_16;    break;
+#ifdef __HD64570_H
+	case PARITY_CRC16_PR0_CCITT: md0 = MD0_HDLC | MD0_CRC_ITU_0; break;
+#else
+	case PARITY_CRC32_PR1_CCITT: md0 = MD0_HDLC | MD0_CRC_ITU32; break;
+#endif	
+	case PARITY_CRC16_PR1_CCITT: md0 = MD0_HDLC | MD0_CRC_ITU;   break;
+	default:		     md0 = MD0_HDLC | MD0_CRC_NONE;
+	}
 
 	sca_out(CMD_RESET, msci + CMD, card);
-	sca_out(MD0_HDLC | crc, msci + MD0, card);
+	sca_out(md0, msci + MD0, card);
 	sca_out(0x00, msci + MD1, card); /* no address field check */
 	sca_out(md2, msci + MD2, card);
 	sca_out(0x7E, msci + IDL, card); /* flag character 0x7E */
-	sca_out(ctl, msci + CTL, card);
+#ifdef __HD64570_H
+	sca_out(CTL_IDLE, msci + CTL, card);
+#else
+	/* Skip the rest of underrun frame */
+	sca_out(CTL_IDLE | CTL_URCT | CTL_URSKP, msci + CTL, card);
+#endif
 
 #ifdef __HD64570_H
 	/* Allow at least 8 bytes before requesting RX DMA operation */
@@ -539,24 +542,28 @@
 	sca_out(0x14, msci + TRC1, card); /* +1=TXRDY/DMA deactiv condition */
 #else
 	sca_out(0x0F, msci + RNR, card); /* +1=RX DMA activation condition */
-	/* Setting than to larger value may cause Illegal Access */
-	sca_out(0x20, msci + TNR0, card); /* =TX DMA activation condition */
-	sca_out(0x30, msci + TNR1, card); /* +1=TX DMA deactivation condition*/
-	sca_out(0x04, msci + TCR, card); /* =Critical TX DMA activ condition */
+	sca_out(0x3C, msci + TFS, card); /* +1 = TX start */
+	sca_out(0x38, msci + TCR, card); /* =Critical TX DMA activ condition */
+	sca_out(0x38, msci + TNR0, card); /* =TX DMA activation condition */
+	sca_out(0x3F, msci + TNR1, card); /* +1=TX DMA deactivation condition*/
 #endif
 
+/* We're using the following interrupts:
+   - TXINT (DMAC completed all transmisions, underflow or CTS change)
+   - all DMA interrupts
+*/
 
 #ifdef __HD64570_H
 	/* MSCI TX INT IRQ enable */
 	sca_out(IE0_TXINT, msci + IE0, card);
-	sca_out(IE1_UDRN, msci + IE1, card); /* TX underrun IRQ */
+	sca_out(IE1_UDRN, msci + IE1, card); /* TX underrun -> TXINT */
 	sca_out(sca_in(IER0, card) | (phy_node(port) ? 0x80 : 0x08),
 		IER0, card);
 	/* DMA IRQ enable */
 	sca_out(sca_in(IER1, card) | (phy_node(port) ? 0xF0 : 0x0F),
 		IER1, card);
 #else
-	/* MSCI TX INT and underrrun IRQ enable */
+	/* MSCI TX INT IRQ enable */
 	sca_outl(IE0_TXINT | IE0_UDRN, msci + IE0, card);
 	/* DMA & MSCI IRQ enable */
 	sca_outl(sca_in(IER0, card) |
@@ -573,11 +580,52 @@
 	sca_out(port->txs, msci + TXS, card);
 	sca_out(CMD_TX_ENABLE, msci + CMD, card);
 	sca_out(CMD_RX_ENABLE, msci + CMD, card);
+
+	netif_start_queue(hdlc_to_dev(hdlc));
+}
+
+
+
+static void sca_close(hdlc_device *hdlc)
+{
+	port_t *port = hdlc_to_port(hdlc);
+
+	/* reset channel */
+	netif_stop_queue(hdlc_to_dev(hdlc));
+	sca_out(CMD_RESET, get_msci(port) + CMD, port_to_card(port));
 }
 
 
 
-#ifdef DEBUG_RINGS
+static int sca_attach(hdlc_device *hdlc, unsigned short encoding,
+		      unsigned short parity)
+{
+	if (encoding != ENCODING_NRZ &&
+	    encoding != ENCODING_NRZI &&
+	    encoding != ENCODING_FM_MARK &&
+	    encoding != ENCODING_FM_SPACE &&
+	    encoding != ENCODING_MANCHESTER)
+		return -EINVAL;
+
+	if (parity != PARITY_NONE &&
+	    parity != PARITY_CRC16_PR0 &&
+	    parity != PARITY_CRC16_PR1 &&
+#ifdef __HD64570_H
+	    parity != PARITY_CRC16_PR0_CCITT &&
+#else
+	    parity != PARITY_CRC32_PR1_CCITT &&
+#endif	
+	    parity != PARITY_CRC16_PR1_CCITT)
+		return -EINVAL;
+
+	hdlc_to_port(hdlc)->encoding = encoding;
+	hdlc_to_port(hdlc)->parity = parity;
+	return 0;
+}
+
+
+
+#ifdef CONFIG_HDLC_DEBUG_RINGS
 static void sca_dump_rings(hdlc_device *hdlc)
 {
 	port_t *port = hdlc_to_port(hdlc);
@@ -644,34 +692,14 @@
 	openwin(card, page); /* Restore original page */
 #endif
 }
-#endif /* DEBUG_RINGS */
-
-
-
-static void sca_open(hdlc_device *hdlc)
-{
-	port_t *port = hdlc_to_port(hdlc);
-
-	sca_set_hdlc_mode(port, 1, MD0_CRC_ITU, 0);
-	netif_start_queue(hdlc_to_dev(hdlc));
-}
-
-
-static void sca_close(hdlc_device *hdlc)
-{
-	port_t *port = hdlc_to_port(hdlc);
-
-	/* reset channel */
-	netif_stop_queue(hdlc_to_dev(hdlc));
-	sca_out(CMD_RESET, get_msci(port) + CMD, port_to_card(port));
-}
+#endif /* CONFIG_HDLC_DEBUG_RINGS */
 
 
 
-static int sca_xmit(hdlc_device *hdlc, struct sk_buff *skb)
+static int sca_xmit(struct sk_buff *skb, struct net_device *dev)
 {
+	hdlc_device *hdlc = dev_to_hdlc(dev);
 	port_t *port = hdlc_to_port(hdlc);
-	struct net_device *dev = hdlc_to_dev(hdlc);
 	card_t *card = port_to_card(port);
 	pkt_desc *desc;
 	u32 buff, len;
@@ -685,7 +713,7 @@
 	desc = desc_address(port, port->txin + 1, 1);
 	if (readb(&desc->stat)) { /* allow 1 packet gap */
 		/* should never happen - previous xmit should stop queue */
-#ifdef DEBUG_PKT
+#ifdef CONFIG_HDLC_DEBUG_PKT
 		printk(KERN_DEBUG "%s: transmitter buffer full\n", dev->name);
 #endif
 		netif_stop_queue(dev);
@@ -693,7 +721,7 @@
 		return 1;	/* request packet to be queued */
 	}
 
-#ifdef DEBUG_PKT
+#ifdef CONFIG_HDLC_DEBUG_PKT
 	printk(KERN_DEBUG "%s TX(%i):", hdlc_to_name(hdlc), skb->len);
 	debug_frame(skb);
 #endif
diff -urN linux-2.4.21-pre2/drivers/net/wan/hdlc.c linux-2.4.21-pre3/drivers/net/wan/hdlc.c
--- linux-2.4.21-pre2/drivers/net/wan/hdlc.c	Thu Sep 13 16:04:43 2001
+++ linux-2.4.21-pre3/drivers/net/wan/hdlc.c	Wed Dec 31 16:00:00 1969
@@ -1,1453 +0,0 @@
-/*
- * Generic HDLC support routines for Linux
- *
- * Copyright (C) 1999, 2000 Krzysztof Halasa <khc@pm.waw.pl>
- *
- * 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.
- *
- * Current status:
- *    - this is work in progress
- *    - not heavily tested on SMP
- *    - currently supported:
- *	* raw IP-in-HDLC
- *	* Cisco HDLC
- *	* Frame Relay with ANSI or CCITT LMI (both user and network side)
- *	* PPP (using syncppp.c)
- *	* X.25
- *
- * Use sethdlc utility to set line parameters, protocol and PVCs
- */
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/poll.h>
-#include <linux/sched.h>
-#include <linux/errno.h>
-#include <linux/if_arp.h>
-#include <linux/init.h>
-#include <linux/skbuff.h>
-#include <linux/pkt_sched.h>
-#include <linux/inetdevice.h>
-#include <linux/lapb.h>
-#include <linux/rtnetlink.h>
-#include <linux/hdlc.h>
-
-/* #define DEBUG_PKT */
-/* #define DEBUG_HARD_HEADER */
-/* #define DEBUG_FECN */
-/* #define DEBUG_BECN */
-
-static const char* version = "HDLC support module revision 1.02 for Linux 2.4";
-
-
-#define CISCO_MULTICAST		0x8F	/* Cisco multicast address */
-#define CISCO_UNICAST		0x0F	/* Cisco unicast address */
-#define CISCO_KEEPALIVE		0x8035	/* Cisco keepalive protocol */
-#define CISCO_SYS_INFO		0x2000	/* Cisco interface/system info */
-#define CISCO_ADDR_REQ		0	/* Cisco address request */
-#define CISCO_ADDR_REPLY	1	/* Cisco address reply */
-#define CISCO_KEEPALIVE_REQ	2	/* Cisco keepalive request */
-
-static int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
-
-/********************************************************
- *
- * Cisco HDLC support
- *
- *******************************************************/
-
-static int cisco_hard_header(struct sk_buff *skb, struct net_device *dev,
-			     u16 type, void *daddr, void *saddr,
-			     unsigned int len)
-{
-	hdlc_header *data;
-#ifdef DEBUG_HARD_HEADER
-	printk(KERN_DEBUG "%s: cisco_hard_header called\n", dev->name);
-#endif
-
-	skb_push(skb, sizeof(hdlc_header));
-	data = (hdlc_header*)skb->data;
-	if (type == CISCO_KEEPALIVE)
-		data->address = CISCO_MULTICAST;
-	else
-		data->address = CISCO_UNICAST;
-	data->control = 0;
-	data->protocol = htons(type);
-
-	return sizeof(hdlc_header);
-}
-
-
-
-static void cisco_keepalive_send(hdlc_device *hdlc, u32 type,
-				 u32 par1, u32 par2)
-{
-	struct sk_buff *skb;
-	cisco_packet *data;
-
-	skb = dev_alloc_skb(sizeof(hdlc_header)+sizeof(cisco_packet));
-	if (!skb) {
-		printk(KERN_WARNING "%s: Memory squeeze on cisco_keepalive_send()\n",
-			       hdlc_to_name(hdlc));
-		return;
-	}
-	skb_reserve(skb, 4);
-	cisco_hard_header(skb, hdlc_to_dev(hdlc), CISCO_KEEPALIVE,
-			  NULL, NULL, 0);
-	data = (cisco_packet*)skb->tail;
-
-	data->type = htonl(type);
-	data->par1 = htonl(par1);
-	data->par2 = htonl(par2);
-	data->rel = 0xFFFF;
-	data->time = htonl(jiffies * 1000 / HZ);
-
-	skb_put(skb, sizeof(cisco_packet));
-	skb->priority = TC_PRIO_CONTROL;
-	skb->dev = hdlc_to_dev(hdlc);
-
-	dev_queue_xmit(skb);
-}
-
-
-
-static void cisco_netif(hdlc_device *hdlc, struct sk_buff *skb)
-{
-	hdlc_header *data = (hdlc_header*)skb->data;
-	cisco_packet *cisco_data;
-	struct in_device *in_dev;
-	u32 addr, mask;
-
-	if (skb->len<sizeof(hdlc_header))
-		goto rx_error;
-
-	if (data->address != CISCO_MULTICAST &&
-	    data->address != CISCO_UNICAST)
-		goto rx_error;
-
-	skb_pull(skb, sizeof(hdlc_header));
-
-	switch(ntohs(data->protocol)) {
-	case ETH_P_IP:
-	case ETH_P_IPX:
-	case ETH_P_IPV6:
-		skb->protocol = data->protocol;
-		skb->dev = hdlc_to_dev(hdlc);
-		netif_rx(skb);
-		return;
-
-	case CISCO_SYS_INFO:
-		/* Packet is not needed, drop it. */
-		dev_kfree_skb_any(skb);
-		return;
-
-	case CISCO_KEEPALIVE:
-		if (skb->len != CISCO_PACKET_LEN &&
-		    skb->len != CISCO_BIG_PACKET_LEN) {
-			printk(KERN_INFO "%s: Invalid length of Cisco "
-			       "control packet (%d bytes)\n",
-			       hdlc_to_name(hdlc), skb->len);
-			goto rx_error;
-		}
-
-		cisco_data = (cisco_packet*)skb->data;
-
-		switch(ntohl (cisco_data->type)) {
-		case CISCO_ADDR_REQ: /* Stolen from syncppp.c :-) */
-			in_dev = hdlc_to_dev(hdlc)->ip_ptr;
-			addr = 0;
-			mask = ~0; /* is the mask correct? */
-
-			if (in_dev != NULL) {
-				struct in_ifaddr **ifap = &in_dev->ifa_list;
-
-				while (*ifap != NULL) {
-					if (strcmp(hdlc_to_name(hdlc),
-						   (*ifap)->ifa_label) == 0) {
-						addr = (*ifap)->ifa_local;
-						mask = (*ifap)->ifa_mask;
-						break;
-					}
-					ifap = &(*ifap)->ifa_next;
-				}
-
-				cisco_keepalive_send(hdlc, CISCO_ADDR_REPLY,
-						     addr, mask);
-			}
-			dev_kfree_skb_any(skb);
-			return;
-
-		case CISCO_ADDR_REPLY:
-			printk(KERN_INFO "%s: Unexpected Cisco IP address "
-			       "reply\n", hdlc_to_name(hdlc));
-			goto rx_error;
-
-		case CISCO_KEEPALIVE_REQ:
-			hdlc->lmi.rxseq = ntohl(cisco_data->par1);
-			if (ntohl(cisco_data->par2) == hdlc->lmi.txseq) {
-				hdlc->lmi.last_poll = jiffies;
-				if (!(hdlc->lmi.state & LINK_STATE_RELIABLE)) {
-					u32 sec, min, hrs, days;
-					sec = ntohl(cisco_data->time) / 1000;
-					min = sec / 60; sec -= min * 60;
-					hrs = min / 60; min -= hrs * 60;
-					days = hrs / 24; hrs -= days * 24;
-					printk(KERN_INFO "%s: Link up (peer "
-					       "uptime %ud%uh%um%us)\n",
-					       hdlc_to_name(hdlc), days, hrs,
-					       min, sec);
-				}
-				hdlc->lmi.state |= LINK_STATE_RELIABLE;
-			}
-
-			dev_kfree_skb_any(skb);
-			return;
-		} /* switch(keepalive type) */
-	} /* switch(protocol) */
-
-	printk(KERN_INFO "%s: Unsupported protocol %x\n", hdlc_to_name(hdlc),
-	       data->protocol);
-	dev_kfree_skb_any(skb);
-	return;
-
- rx_error:
-	hdlc->stats.rx_errors++; /* Mark error */
-	dev_kfree_skb_any(skb);
-}
-
-
-
-static void cisco_timer(unsigned long arg)
-{
-	hdlc_device *hdlc = (hdlc_device*)arg;
-
-	if ((hdlc->lmi.state & LINK_STATE_RELIABLE) &&
-	    (jiffies - hdlc->lmi.last_poll >= hdlc->lmi.T392 * HZ)) {
-		hdlc->lmi.state &= ~LINK_STATE_RELIABLE;
-		printk(KERN_INFO "%s: Link down\n", hdlc_to_name(hdlc));
-	}
-
-	cisco_keepalive_send(hdlc, CISCO_KEEPALIVE_REQ, ++hdlc->lmi.txseq,
-			     hdlc->lmi.rxseq);
-	hdlc->timer.expires = jiffies + hdlc->lmi.T391*HZ;
-
-	hdlc->timer.function = cisco_timer;
-	hdlc->timer.data = arg;
-	add_timer(&hdlc->timer);
-}
-
-
-
-/******************************************************************
- *
- *     generic Frame Relay routines
- *
- *****************************************************************/
-
-
-static int fr_hard_header(struct sk_buff *skb, struct net_device *dev,
-			  u16 type, void *daddr, void *saddr, unsigned int len)
-{
-	u16 head_len;
-
-	if (!daddr)
-		daddr = dev->broadcast;
-
-#ifdef DEBUG_HARD_HEADER
-	printk(KERN_DEBUG "%s: fr_hard_header called\n", dev->name);
-#endif
-
-	switch(type) {
-	case ETH_P_IP:
-		head_len = 4;
-		skb_push(skb, head_len);
-		skb->data[3] = NLPID_IP;
-		break;
-
-	case ETH_P_IPV6:
-		head_len = 4;
-		skb_push(skb, head_len);
-		skb->data[3] = NLPID_IPV6;
-		break;
-
-	case LMI_PROTO:
-		head_len = 4;
-		skb_push(skb, head_len);
-		skb->data[3] = LMI_PROTO;
-		break;
-
-	default:
-		head_len = 10;
-		skb_push(skb, head_len);
-		skb->data[3] = FR_PAD;
-		skb->data[4] = NLPID_SNAP;
-		skb->data[5] = FR_PAD;
-		skb->data[6] = FR_PAD;
-		skb->data[7] = FR_PAD;
-		skb->data[8] = type>>8;
-		skb->data[9] = (u8)type;
-	}
-
-	memcpy(skb->data, daddr, 2);
-	skb->data[2] = FR_UI;
-
-	return head_len;
-}
-
-
-
-static inline void fr_log_dlci_active(pvc_device *pvc)
-{
-	printk(KERN_INFO "%s: %sactive%s\n", pvc_to_name(pvc),
-	       pvc->state & PVC_STATE_ACTIVE ? "" : "in",
-	       pvc->state & PVC_STATE_NEW ? " new" : "");
-}
-
-
-
-static inline u8 fr_lmi_nextseq(u8 x)
-{
-	x++;
-	return x ? x : 1;
-}
-
-
-
-static void fr_lmi_send(hdlc_device *hdlc, int fullrep)
-{
-	struct sk_buff *skb;
-	pvc_device *pvc = hdlc->first_pvc;
-	int len = mode_is(hdlc, MODE_FR_ANSI) ? LMI_ANSI_LENGTH : LMI_LENGTH;
-	int stat_len = 3;
-	u8 *data;
-	int i = 0;
-
-	if (mode_is(hdlc, MODE_DCE) && fullrep) {
-		len += hdlc->pvc_count * (2 + stat_len);
-		if (len > HDLC_MAX_MTU) {
-			printk(KERN_WARNING "%s: Too many PVCs while sending "
-			       "LMI full report\n", hdlc_to_name(hdlc));
-			return;
-		}
-	}
-
-	skb = dev_alloc_skb(len);
-	if (!skb) {
-		printk(KERN_WARNING "%s: Memory squeeze on fr_lmi_send()\n",
-			       hdlc_to_name(hdlc));
-		return;
-	}
-	memset(skb->data, 0, len);
-	skb_reserve(skb, 4);
-	fr_hard_header(skb, hdlc_to_dev(hdlc), LMI_PROTO, NULL, NULL, 0);
-	data = skb->tail;
-	data[i++] = LMI_CALLREF;
-	data[i++] = mode_is(hdlc, MODE_DCE) ? LMI_STATUS : LMI_STATUS_ENQUIRY;
-	if (mode_is(hdlc, MODE_FR_ANSI))
-		data[i++] = LMI_ANSI_LOCKSHIFT;
-	data[i++] = mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_REPTYPE :
-		LMI_REPTYPE;
-	data[i++] = LMI_REPT_LEN;
-	data[i++] = fullrep ? LMI_FULLREP : LMI_INTEGRITY;
-
-	data[i++] = mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_ALIVE : LMI_ALIVE;
-	data[i++] = LMI_INTEG_LEN;
-	data[i++] = hdlc->lmi.txseq = fr_lmi_nextseq(hdlc->lmi.txseq);
-	data[i++] = hdlc->lmi.rxseq;
-
-	if (mode_is(hdlc, MODE_DCE) && fullrep) {
-		while (pvc) {
-			data[i++] = mode_is(hdlc, MODE_FR_CCITT) ?
-				LMI_CCITT_PVCSTAT:LMI_PVCSTAT;
-			data[i++] = stat_len;
-
-			if ((hdlc->lmi.state & LINK_STATE_RELIABLE) &&
-			    (pvc->netdev.flags & IFF_UP) &&
-			    !(pvc->state & (PVC_STATE_ACTIVE|PVC_STATE_NEW))) {
-				pvc->state |= PVC_STATE_NEW;
-				fr_log_dlci_active(pvc);
-			}
-
-			dlci_to_status(hdlc, netdev_dlci(&pvc->netdev),
-				       data+i, pvc->state);
-			i += stat_len;
-			pvc = pvc->next;
-		}
-	}
-
-	skb_put(skb, i);
-	skb->priority = TC_PRIO_CONTROL;
-	skb->dev = hdlc_to_dev(hdlc);
-
-	dev_queue_xmit(skb);
-}
-
-
-
-static void fr_timer(unsigned long arg)
-{
-	hdlc_device *hdlc = (hdlc_device*)arg;
-	int i, cnt = 0, reliable;
-	u32 list;
-
-	if (mode_is(hdlc, MODE_DCE))
-		reliable = (jiffies - hdlc->lmi.last_poll < hdlc->lmi.T392*HZ);
-	else {
-		hdlc->lmi.last_errors <<= 1; /* Shift the list */
-		if (hdlc->lmi.state & LINK_STATE_REQUEST) {
-			printk(KERN_INFO "%s: No LMI status reply received\n",
-			       hdlc_to_name(hdlc));
-			hdlc->lmi.last_errors |= 1;
-		}
-
-		for (i = 0, list = hdlc->lmi.last_errors; i < hdlc->lmi.N393;
-		     i++, list >>= 1)
-			cnt += (list & 1);	/* errors count */
-
-		reliable = (cnt < hdlc->lmi.N392);
-	}
-
-	if ((hdlc->lmi.state & LINK_STATE_RELIABLE) !=
-	    (reliable ? LINK_STATE_RELIABLE : 0)) {
-		pvc_device *pvc = hdlc->first_pvc;
-
-		while (pvc) {/* Deactivate all PVCs */
-			pvc->state &= ~(PVC_STATE_NEW | PVC_STATE_ACTIVE);
-			pvc = pvc->next;
-		}
-
-		hdlc->lmi.state ^= LINK_STATE_RELIABLE;
-		printk(KERN_INFO "%s: Link %sreliable\n", hdlc_to_name(hdlc),
-		       reliable ? "" : "un");
-
-		if (reliable) {
-			hdlc->lmi.N391cnt = 0; /* Request full status */
-			hdlc->lmi.state |= LINK_STATE_CHANGED;
-		}
-	}
-
-	if (mode_is(hdlc, MODE_DCE))
-		hdlc->timer.expires = jiffies + hdlc->lmi.T392*HZ;
-	else {
-		if (hdlc->lmi.N391cnt)
-			hdlc->lmi.N391cnt--;
-
-		fr_lmi_send(hdlc, hdlc->lmi.N391cnt == 0);
-
-		hdlc->lmi.state |= LINK_STATE_REQUEST;
-		hdlc->timer.expires = jiffies + hdlc->lmi.T391*HZ;
-	}
-
-	hdlc->timer.function = fr_timer;
-	hdlc->timer.data = arg;
-	add_timer(&hdlc->timer);
-}
-
-
-
-static int fr_lmi_recv(hdlc_device *hdlc, struct sk_buff *skb)
-{
-	int stat_len;
-	pvc_device *pvc;
-	int reptype = -1, error;
-	u8 rxseq, txseq;
-	int i;
-
-	if (skb->len < (mode_is(hdlc, MODE_FR_ANSI) ?
-			LMI_ANSI_LENGTH : LMI_LENGTH)) {
-		printk(KERN_INFO "%s: Short LMI frame\n", hdlc_to_name(hdlc));
-		return 1;
-	}
-
-	if (skb->data[5] != (!mode_is(hdlc, MODE_DCE) ?
-			     LMI_STATUS : LMI_STATUS_ENQUIRY)) {
-		printk(KERN_INFO "%s: LMI msgtype=%x, Not LMI status %s\n",
-		       hdlc_to_name(hdlc), skb->data[2],
-		       mode_is(hdlc, MODE_DCE) ? "enquiry" : "reply");
-		return 1;
-	}
-
-	i = mode_is(hdlc, MODE_FR_ANSI) ? 7 : 6;
-
-	if (skb->data[i] !=
-	    (mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_REPTYPE : LMI_REPTYPE)) {
-		printk(KERN_INFO "%s: Not a report type=%x\n",
-		       hdlc_to_name(hdlc), skb->data[i]);
-		return 1;
-	}
-	i++;
-
-	i++;				/* Skip length field */
-
-	reptype = skb->data[i++];
-
-	if (skb->data[i]!=
-	    (mode_is(hdlc, MODE_FR_CCITT) ? LMI_CCITT_ALIVE : LMI_ALIVE)) {
-		printk(KERN_INFO "%s: Unsupported status element=%x\n",
-		       hdlc_to_name(hdlc), skb->data[i]);
-		return 1;
-	}
-	i++;
-
-	i++;			/* Skip length field */
-
-	hdlc->lmi.rxseq = skb->data[i++]; /* TX sequence from peer */
-	rxseq = skb->data[i++];	/* Should confirm our sequence */
-
-	txseq = hdlc->lmi.txseq;
-
-	if (mode_is(hdlc, MODE_DCE)) {
-		if (reptype != LMI_FULLREP && reptype != LMI_INTEGRITY) {
-			printk(KERN_INFO "%s: Unsupported report type=%x\n",
-			       hdlc_to_name(hdlc), reptype);
-			return 1;
-		}
-	}
-
-	error = 0;
-	if (!(hdlc->lmi.state & LINK_STATE_RELIABLE))
-		error = 1;
-
-	if (rxseq == 0 || rxseq != txseq) {
-		hdlc->lmi.N391cnt = 0; /* Ask for full report next time */
-		error = 1;
-	}
-
-	if (mode_is(hdlc, MODE_DCE)) {
-		if ((hdlc->lmi.state & LINK_STATE_FULLREP_SENT) && !error) {
-/* Stop sending full report - the last one has been confirmed by DTE */
-			hdlc->lmi.state &= ~LINK_STATE_FULLREP_SENT;
-			pvc = hdlc->first_pvc;
-			while (pvc) {
-				if (pvc->state & PVC_STATE_NEW) {
-					pvc->state &= ~PVC_STATE_NEW;
-					pvc->state |= PVC_STATE_ACTIVE;
-					fr_log_dlci_active(pvc);
-
-/* Tell DTE that new PVC is now active */
-					hdlc->lmi.state |= LINK_STATE_CHANGED;
-				}
-				pvc = pvc->next;
-			}
-		}
-
-		if (hdlc->lmi.state & LINK_STATE_CHANGED) {
-			reptype = LMI_FULLREP;
-			hdlc->lmi.state |= LINK_STATE_FULLREP_SENT;
-			hdlc->lmi.state &= ~LINK_STATE_CHANGED;
-		}
-
-		fr_lmi_send(hdlc, reptype == LMI_FULLREP ? 1 : 0);
-		return 0;
-	}
-
-	/* DTE */
-
-	if (reptype != LMI_FULLREP || error)
-		return 0;
-
-	stat_len = 3;
-	pvc = hdlc->first_pvc;
-
-	while (pvc) {
-		pvc->newstate = 0;
-		pvc = pvc->next;
-	}
-
-	while (skb->len >= i + 2 + stat_len) {
-		u16 dlci;
-		u8 state = 0;
-
-		if (skb->data[i] != (mode_is(hdlc, MODE_FR_CCITT) ?
-				     LMI_CCITT_PVCSTAT : LMI_PVCSTAT)) {
-			printk(KERN_WARNING "%s: Invalid PVCSTAT ID: %x\n",
-			       hdlc_to_name(hdlc), skb->data[i]);
-			return 1;
-		}
-		i++;
-
-		if (skb->data[i] != stat_len) {
-			printk(KERN_WARNING "%s: Invalid PVCSTAT length: %x\n",
-			       hdlc_to_name(hdlc), skb->data[i]);
-			return 1;
-		}
-		i++;
-
-		dlci = status_to_dlci(hdlc, skb->data+i, &state);
-		pvc = find_pvc(hdlc, dlci);
-
-		if (pvc)
-			pvc->newstate = state;
-		else if (state == PVC_STATE_NEW)
-			printk(KERN_INFO "%s: new PVC available, DLCI=%u\n",
-			       hdlc_to_name(hdlc), dlci);
-
-		i += stat_len;
-	}
-
-	pvc = hdlc->first_pvc;
-
-	while (pvc) {
-		if (pvc->newstate == PVC_STATE_NEW)
-			pvc->newstate = PVC_STATE_ACTIVE;
-
-		pvc->newstate |= (pvc->state &
-				  ~(PVC_STATE_NEW|PVC_STATE_ACTIVE));
-		if (pvc->state != pvc->newstate) {
-			pvc->state = pvc->newstate;
-			fr_log_dlci_active(pvc);
-		}
-		pvc = pvc->next;
-	}
-
-	/* Next full report after N391 polls */
-	hdlc->lmi.N391cnt = hdlc->lmi.N391;
-
-	return 0;
-}
-
-
-
-static void fr_netif(hdlc_device *hdlc, struct sk_buff *skb)
-{
-	fr_hdr *fh = (fr_hdr*)skb->data;
-	u8 *data = skb->data;
-	u16 dlci;
-	pvc_device *pvc;
-
-	if (skb->len<4 || fh->ea1 || data[2] != FR_UI)
-		goto rx_error;
-
-	dlci = q922_to_dlci(skb->data);
-
-	if (dlci == LMI_DLCI) {
-		if (data[3] == LMI_PROTO) {
-			if (fr_lmi_recv(hdlc, skb))
-				goto rx_error;
-			else {
-				/* No request pending */
-				hdlc->lmi.state &= ~LINK_STATE_REQUEST;
-				hdlc->lmi.last_poll = jiffies;
-				dev_kfree_skb_any(skb);
-				return;
-			}
-		}
-
-		printk(KERN_INFO "%s: Received non-LMI frame with LMI DLCI\n",
-		       hdlc_to_name(hdlc));
-		goto rx_error;
-	}
-
-	pvc = find_pvc(hdlc, dlci);
-	if (!pvc) {
-#ifdef DEBUG_PKT
-		printk(KERN_INFO "%s: No PVC for received frame's DLCI %d\n",
-		       hdlc_to_name(hdlc), dlci);
-#endif
-		goto rx_error;
-	}
-
-	if ((pvc->netdev.flags & IFF_UP) == 0) {
-#ifdef DEBUG_PKT
-		printk(KERN_INFO "%s: PVC for received frame's DLCI %d is down\n",
-		       hdlc_to_name(hdlc), dlci);
-#endif
-		goto rx_error;
-	}
-
-	pvc->stats.rx_packets++; /* PVC traffic */
-	pvc->stats.rx_bytes += skb->len;
-
-	if ((pvc->state & PVC_STATE_FECN) != (fh->fecn ? PVC_STATE_FECN : 0)) {
-#ifdef DEBUG_FECN
-		printk(KERN_DEBUG "%s: FECN O%s\n", pvc_to_name(pvc),
-		       fh->fecn ? "N" : "FF");
-#endif
-		pvc->state ^= PVC_STATE_FECN;
-	}
-
-	if ((pvc->state & PVC_STATE_BECN) != (fh->becn ? PVC_STATE_BECN : 0)) {
-#ifdef DEBUG_FECN
-		printk(KERN_DEBUG "%s: BECN O%s\n", pvc_to_name(pvc),
-		       fh->becn ? "N" : "FF");
-#endif
-		pvc->state ^= PVC_STATE_BECN;
-	}
-
-	if (pvc->state & PVC_STATE_BECN)
-		pvc->stats.rx_compressed++;
-
-	if (data[3] == NLPID_IP) {
-		skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */
-		skb->protocol = htons(ETH_P_IP);
-		skb->dev = &pvc->netdev;
-		netif_rx(skb);
-		return;
-	}
-
-
-	if (data[3] == NLPID_IPV6) {
-		skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */
-		skb->protocol = htons(ETH_P_IPV6);
-		skb->dev = &pvc->netdev;
-		netif_rx(skb);
-		return;
-	}
-
-	if (data[3] == FR_PAD && data[4] == NLPID_SNAP && data[5] == FR_PAD &&
-	    data[6] == FR_PAD && data[7] == FR_PAD &&
-	    ((data[8]<<8) | data[9]) == ETH_P_ARP) {
-		skb_pull(skb, 10);
-		skb->protocol = htons(ETH_P_ARP);
-		skb->dev = &pvc->netdev;
-		netif_rx(skb);
-		return;
-	}
-
-	printk(KERN_INFO "%s: Unusupported protocol %x\n",
-	       hdlc_to_name(hdlc), data[3]);
-	dev_kfree_skb_any(skb);
-	return;
-
- rx_error:
-	hdlc->stats.rx_errors++; /* Mark error */
-	dev_kfree_skb_any(skb);
-}
-
-
-
-static void fr_cisco_open(hdlc_device *hdlc)
-{
-	hdlc->lmi.state = LINK_STATE_CHANGED;
-	hdlc->lmi.txseq = hdlc->lmi.rxseq = 0;
-	hdlc->lmi.last_errors = 0xFFFFFFFF;
-	hdlc->lmi.N391cnt = 0;
-
-	init_timer(&hdlc->timer);
-	hdlc->timer.expires = jiffies + HZ; /* First poll after 1 second */
-	hdlc->timer.function = mode_is(hdlc, MODE_FR) ? fr_timer : cisco_timer;
-	hdlc->timer.data = (unsigned long)hdlc;
-	add_timer(&hdlc->timer);
-}
-
-
-
-static void fr_cisco_close(hdlc_device *hdlc)
-{
-	pvc_device *pvc = hdlc->first_pvc;
-
-	del_timer_sync(&hdlc->timer);
-
-	while(pvc) {		/* NULL in Cisco mode */
-		dev_close(&pvc->netdev); /* Shutdown all PVCs for this FRAD */
-		pvc = pvc->next;
-	}
-}
-
-
-
-/******************************************************************
- *
- *     generic HDLC routines
- *
- *****************************************************************/
-
-
-
-static int hdlc_change_mtu(struct net_device *dev, int new_mtu)
-{
-	if ((new_mtu < 68) || (new_mtu > HDLC_MAX_MTU))
-		return -EINVAL;
-	dev->mtu = new_mtu;
-	return 0;
-}
-
-
-
-/********************************************************
- *
- * PVC device routines
- *
- *******************************************************/
-
-static int pvc_open(struct net_device *dev)
-{
-	pvc_device *pvc = dev_to_pvc(dev);
-	int result = 0;
-
-	if ((hdlc_to_dev(pvc->master)->flags & IFF_UP) == 0)
-		return -EIO;  /* Master must be UP in order to activate PVC */
-
-	memset(&(pvc->stats), 0, sizeof(struct net_device_stats));
-	pvc->state = 0;
-
-	if (!mode_is(pvc->master, MODE_SOFT) && pvc->master->open_pvc)
-		result = pvc->master->open_pvc(pvc);
-	if (result)
-		return result;
-
-	pvc->master->lmi.state |= LINK_STATE_CHANGED;
-	return 0;
-}
-
-
-
-static int pvc_close(struct net_device *dev)
-{
-	pvc_device *pvc = dev_to_pvc(dev);
-	pvc->state = 0;
-
-	if (!mode_is(pvc->master, MODE_SOFT) && pvc->master->close_pvc)
-		pvc->master->close_pvc(pvc);
-
-	pvc->master->lmi.state |= LINK_STATE_CHANGED;
-	return 0;
-}
-
-
-
-static int pvc_xmit(struct sk_buff *skb, struct net_device *dev)
-{
-	pvc_device *pvc = dev_to_pvc(dev);
-
-	if (pvc->state & PVC_STATE_ACTIVE) {
-		skb->dev = hdlc_to_dev(pvc->master);
-		pvc->stats.tx_bytes += skb->len;
-		pvc->stats.tx_packets++;
-		if (pvc->state & PVC_STATE_FECN)
-			pvc->stats.tx_compressed++; /* TX Congestion counter */
-		dev_queue_xmit(skb);
-	} else {
-		pvc->stats.tx_dropped++;
-		dev_kfree_skb(skb);
-	}
-
-	return 0;
-}
-
-
-
-static struct net_device_stats *pvc_get_stats(struct net_device *dev)
-{
-	pvc_device *pvc = dev_to_pvc(dev);
-	return &pvc->stats;
-}
-
-
-
-static int pvc_change_mtu(struct net_device *dev, int new_mtu)
-{
-	if ((new_mtu < 68) || (new_mtu > HDLC_MAX_MTU))
-		return -EINVAL;
-	dev->mtu = new_mtu;
-	return 0;
-}
-
-
-
-static void destroy_pvc_list(hdlc_device *hdlc)
-{
-	pvc_device *pvc = hdlc->first_pvc;
-	while(pvc) {
-		pvc_device *next = pvc->next;
-		unregister_netdevice(&pvc->netdev);
-		kfree(pvc);
-		pvc = next;
-	}
-
-	hdlc->first_pvc = NULL;	/* All PVCs destroyed */
-	hdlc->pvc_count = 0;
-	hdlc->lmi.state |= LINK_STATE_CHANGED;
-}
-
-
-
-/********************************************************
- *
- * X.25 protocol support routines
- *
- *******************************************************/
-
-#ifdef CONFIG_HDLC_X25
-/* These functions are callbacks called by LAPB layer */
-
-void x25_connect_disconnect(void *token, int reason, int code)
-{
-	hdlc_device *hdlc = token;
-	struct sk_buff *skb;
-	unsigned char *ptr;
-
-	if ((skb = dev_alloc_skb(1)) == NULL) {
-		printk(KERN_ERR "%s: out of memory\n", hdlc_to_name(hdlc));
-		return;
-	}
-
-	ptr = skb_put(skb, 1);
-	*ptr = code;
-
-	skb->dev = hdlc_to_dev(hdlc);
-	skb->protocol = htons(ETH_P_X25);
-	skb->mac.raw = skb->data;
-	skb->pkt_type = PACKET_HOST;
-
-	netif_rx(skb);
-}
-
-void x25_connected(void *token, int reason)
-{
-	x25_connect_disconnect(token, reason, 1);
-}
-
-void x25_disconnected(void *token, int reason)
-{
-	x25_connect_disconnect(token, reason, 2);
-}
-
-
-int x25_data_indication(void *token, struct sk_buff *skb)
-{
-	hdlc_device *hdlc = token;
-	unsigned char *ptr;
-
-	ptr = skb_push(skb, 1);
-	*ptr = 0;
-
-	skb->dev = hdlc_to_dev(hdlc);
-	skb->protocol = htons(ETH_P_X25);
-	skb->mac.raw = skb->data;
-	skb->pkt_type = PACKET_HOST;
-
-	return netif_rx(skb);
-}
-
-
-void x25_data_transmit(void *token, struct sk_buff *skb)
-{
-	hdlc_device *hdlc = token;
-	hdlc->xmit(hdlc, skb);	/* Ignore return value :-( */
-}
-#endif /* CONFIG_HDLC_X25 */
-
-
-/********************************************************
- *
- * HDLC device routines
- *
- *******************************************************/
-
-static int hdlc_open(struct net_device *dev)
-{
-	hdlc_device *hdlc = dev_to_hdlc(dev);
-	int result;
-
-	if (hdlc->mode == MODE_NONE)
-		return -ENOSYS;
-
-	memset(&(hdlc->stats), 0, sizeof(struct net_device_stats));
-
-	if (mode_is(hdlc, MODE_FR | MODE_SOFT) ||
-	    mode_is(hdlc, MODE_CISCO | MODE_SOFT))
-		fr_cisco_open(hdlc);
-#ifdef CONFIG_HDLC_PPP
-	else if (mode_is(hdlc, MODE_PPP | MODE_SOFT)) {
-		sppp_attach(&hdlc->pppdev);
-		/* sppp_attach nukes them. We don't need syncppp's ioctl */
-		dev->do_ioctl = hdlc_ioctl;
-		hdlc->pppdev.sppp.pp_flags &= ~PP_CISCO;
-		dev->type = ARPHRD_PPP;
-		result = sppp_open(dev);
-		if (result) {
-			sppp_detach(dev);
-			return result;
-		}
-	}
-#endif
-#ifdef CONFIG_HDLC_X25
-	else if (mode_is(hdlc, MODE_X25)) {
-		struct lapb_register_struct cb;
-
-		cb.connect_confirmation = x25_connected;
-		cb.connect_indication = x25_connected;
-		cb.disconnect_confirmation = x25_disconnected;
-		cb.disconnect_indication = x25_disconnected;
-		cb.data_indication = x25_data_indication;
-		cb.data_transmit = x25_data_transmit;
-
-		result = lapb_register(hdlc, &cb);
-		if (result != LAPB_OK)
-			return result;
-	}
-#endif
-	result = hdlc->open(hdlc);
-	if (result) {
-		if (mode_is(hdlc, MODE_FR | MODE_SOFT) ||
-		    mode_is(hdlc, MODE_CISCO | MODE_SOFT))
-			fr_cisco_close(hdlc);
-#ifdef CONFIG_HDLC_PPP
-		else if (mode_is(hdlc, MODE_PPP | MODE_SOFT)) {
-			sppp_close(dev);
-			sppp_detach(dev);
-			dev->rebuild_header = NULL;
-			dev->change_mtu = hdlc_change_mtu;
-			dev->mtu = HDLC_MAX_MTU;
-			dev->hard_header_len = 16;
-		}
-#endif
-#ifdef CONFIG_HDLC_X25
-		else if (mode_is(hdlc, MODE_X25))
-			lapb_unregister(hdlc);
-#endif
-	}
-
-	return result;
-}
-
-
-
-static int hdlc_close(struct net_device *dev)
-{
-	hdlc_device *hdlc = dev_to_hdlc(dev);
-
-	hdlc->close(hdlc);
-
-	if (mode_is(hdlc, MODE_FR | MODE_SOFT) ||
-	    mode_is(hdlc, MODE_CISCO | MODE_SOFT))
-		fr_cisco_close(hdlc);
-#ifdef CONFIG_HDLC_PPP
-	else if (mode_is(hdlc, MODE_PPP | MODE_SOFT)) {
-		sppp_close(dev);
-		sppp_detach(dev);
-		dev->rebuild_header = NULL;
-		dev->change_mtu = hdlc_change_mtu;
-		dev->mtu = HDLC_MAX_MTU;
-		dev->hard_header_len = 16;
-	}
-#endif
-#ifdef CONFIG_HDLC_X25
-	else if (mode_is(hdlc, MODE_X25))
-		lapb_unregister(hdlc);
-#endif
-	return 0;
-}
-
-
-
-static int hdlc_xmit(struct sk_buff *skb, struct net_device *dev)
-{
-	hdlc_device *hdlc = dev_to_hdlc(dev);
-
-#ifdef CONFIG_HDLC_X25
-	if (mode_is(hdlc, MODE_X25 | MODE_SOFT)) {
-		int result;
-
-
-		/* X.25 to LAPB */
-		switch (skb->data[0]) {
-		case 0:		/* Data to be transmitted */
-			skb_pull(skb, 1);
-			if ((result = lapb_data_request(hdlc, skb)) != LAPB_OK)
-				dev_kfree_skb(skb);
-			return 0;
-
-		case 1:
-			if ((result = lapb_connect_request(hdlc))!= LAPB_OK) {
-				if (result == LAPB_CONNECTED) {
-				/* Send connect confirm. msg to level 3 */
-					x25_connected(hdlc, 0);
-				} else {
-					printk(KERN_ERR "%s: LAPB connect "
-					       "request failed, error code = "
-					       "%i\n", hdlc_to_name(hdlc),
-					       result);
-				}
-			}
-			break;
-
-		case 2:
-			if ((result=lapb_disconnect_request(hdlc))!=LAPB_OK) {
-				if (result == LAPB_NOTCONNECTED) {
-				/* Send disconnect confirm. msg to level 3 */
-					x25_disconnected(hdlc, 0);
-				} else {
-					printk(KERN_ERR "%s: LAPB disconnect "
-					       "request failed, error code = "
-					       "%i\n", hdlc_to_name(hdlc),
-					       result);
-				}
-			}
-			break;
-
-		default:
-			/* to be defined */
-			break;
-		}
-
-		dev_kfree_skb(skb);
-		return 0;
-	} /* MODE_X25 */
-#endif /* CONFIG_HDLC_X25 */
-
-	return hdlc->xmit(hdlc, skb);
-}
-
-
-
-void hdlc_netif_rx(hdlc_device *hdlc, struct sk_buff *skb)
-{
-/* skb contains raw HDLC frame, in both hard- and software modes */
-	skb->mac.raw = skb->data;
-
-	switch(hdlc->mode & MODE_MASK) {
-	case MODE_HDLC:
-		skb->protocol = htons(ETH_P_IP);
-		skb->dev = hdlc_to_dev(hdlc);
-		netif_rx(skb);
-		return;
-
-	case MODE_FR:
-		fr_netif(hdlc, skb);
-		return;
-
-	case MODE_CISCO:
-		cisco_netif(hdlc, skb);
-		return;
-
-#ifdef CONFIG_HDLC_PPP
-	case MODE_PPP:
-#if 0
-		sppp_input(hdlc_to_dev(hdlc), skb);
-#else
-		skb->protocol = htons(ETH_P_WAN_PPP);
-		skb->dev = hdlc_to_dev(hdlc);
-		netif_rx(skb);
-#endif
-		return;
-#endif
-#ifdef CONFIG_HDLC_X25
-	case MODE_X25:
-		skb->dev = hdlc_to_dev(hdlc);
-		if (lapb_data_received(hdlc, skb) == LAPB_OK)
-			return;
-		break;
-#endif
-	}
-
-	hdlc->stats.rx_errors++;
-	dev_kfree_skb_any(skb);
-}
-
-
-
-static struct net_device_stats *hdlc_get_stats(struct net_device *dev)
-{
-	return &dev_to_hdlc(dev)->stats;
-}
-
-
-
-static int hdlc_set_mode(hdlc_device *hdlc, int mode)
-{
-	int result = -1;	/* Default to soft modes */
-	struct net_device *dev = hdlc_to_dev(hdlc);
-
-	if(!capable(CAP_NET_ADMIN))
-		return -EPERM;
-
-	if(dev->flags & IFF_UP)
-		return -EBUSY;
-
-	dev->addr_len = 0;
-	dev->hard_header = NULL;
-	hdlc->mode = MODE_NONE;
-
-	if (!(mode & MODE_SOFT))
-		switch(mode & MODE_MASK) {
-		case MODE_HDLC:
-			result = hdlc->set_mode ?
-				hdlc->set_mode(hdlc, MODE_HDLC) : 0;
-			break;
-
-		case MODE_CISCO: /* By card */
-#ifdef CONFIG_HDLC_PPP
-		case MODE_PPP:
-#endif
-#ifdef CONFIG_HDLC_X25
-		case MODE_X25:
-#endif
-		case MODE_FR:
-			result = hdlc->set_mode ?
-				hdlc->set_mode(hdlc, mode) : -ENOSYS;
-			break;
-
-		default:
-			return -EINVAL;
-		}
-
-	if (result) {
-		mode |= MODE_SOFT; /* Try "host software" protocol */
-
-		switch(mode & MODE_MASK) {
-		case MODE_CISCO:
-			dev->hard_header = cisco_hard_header;
-			break;
-
-#ifdef CONFIG_HDLC_PPP
-		case MODE_PPP:
-			break;
-#endif
-#ifdef CONFIG_HDLC_X25
-		case MODE_X25:
-			break;
-#endif
-
-		case MODE_FR:
-			dev->hard_header = fr_hard_header;
-			dev->addr_len = 2;
-			*(u16*)dev->dev_addr = htons(LMI_DLCI);
-			dlci_to_q922(dev->broadcast, LMI_DLCI);
-			break;
-
-		default:
-			return -EINVAL;
-		}
-
-		result = hdlc->set_mode ?
-			hdlc->set_mode(hdlc, MODE_HDLC) : 0;
-	}
-
-	if (result)
-		return result;
-
-	hdlc->mode = mode;
-	switch(mode & MODE_MASK) {
-#ifdef CONFIG_HDLC_PPP
-	case MODE_PPP:   dev->type = ARPHRD_PPP;   break;
-#endif
-#ifdef CONFIG_HDLC_X25
-	case MODE_X25:   dev->type = ARPHRD_X25;   break;
-#endif
-	case MODE_FR:    dev->type = ARPHRD_FRAD;  break;
-	case MODE_CISCO: dev->type = ARPHRD_CISCO; break;
-	default:         dev->type = ARPHRD_RAWHDLC;
-	}
-
-	memset(&(hdlc->stats), 0, sizeof(struct net_device_stats));
-	destroy_pvc_list(hdlc);
-	return 0;
-}
-
-
-
-static int hdlc_fr_pvc(hdlc_device *hdlc, int dlci)
-{
-	pvc_device **pvc_p = &hdlc->first_pvc;
-	pvc_device *pvc;
-	int result, create = 1;	/* Create or delete PVC */
-
-	if(!capable(CAP_NET_ADMIN))
-		return -EPERM;
-
-	if(dlci<0) {
-		dlci = -dlci;
-		create = 0;
-	}
-
-	if(dlci <= 0 || dlci >= 1024)
-		return -EINVAL;	/* Only 10 bits for DLCI, DLCI=0 is reserved */
-
-	if(!mode_is(hdlc, MODE_FR))
-		return -EINVAL;	/* Only meaningfull on FR */
-
-	while(*pvc_p) {
-		if (netdev_dlci(&(*pvc_p)->netdev) == dlci)
-			break;
-		pvc_p = &(*pvc_p)->next;
-	}
-
-	if (create) {		/* Create PVC */
-		if (*pvc_p != NULL)
-			return -EEXIST;
-
-		pvc = *pvc_p = kmalloc(sizeof(pvc_device), GFP_KERNEL);
-		if (!pvc) {
-			printk(KERN_WARNING "%s: Memory squeeze on "
-			       "hdlc_fr_pvc()\n", hdlc_to_name(hdlc));
-			return -ENOBUFS;
-		}
-		memset(pvc, 0, sizeof(pvc_device));
-
-		pvc->netdev.hard_start_xmit = pvc_xmit;
-		pvc->netdev.get_stats = pvc_get_stats;
-		pvc->netdev.open = pvc_open;
-		pvc->netdev.stop = pvc_close;
-		pvc->netdev.change_mtu = pvc_change_mtu;
-		pvc->netdev.mtu = HDLC_MAX_MTU;
-
-		pvc->netdev.type = ARPHRD_DLCI;
-		pvc->netdev.hard_header_len = 16;
-		pvc->netdev.hard_header = fr_hard_header;
-		pvc->netdev.tx_queue_len = 0;
-		pvc->netdev.flags = IFF_POINTOPOINT;
-
-		pvc->master = hdlc;
-		*(u16*)pvc->netdev.dev_addr = htons(dlci);
-		dlci_to_q922(pvc->netdev.broadcast, dlci);
-		pvc->netdev.addr_len = 2;
-		pvc->netdev.irq = hdlc_to_dev(hdlc)->irq;
-
-		result = dev_alloc_name(&pvc->netdev, "pvc%d");
-		if (result < 0) {
-			kfree(pvc);
-			*pvc_p = NULL;
-			return result;
-		}
-
-		if (register_netdevice(&pvc->netdev) != 0) {
-			kfree(pvc);
-			*pvc_p = NULL;
-			return -EIO;
-		}
-
-		if (!mode_is(hdlc, MODE_SOFT) && hdlc->create_pvc) {
-			result = hdlc->create_pvc(pvc);
-			if (result) {
-				unregister_netdevice(&pvc->netdev);
-				kfree(pvc);
-				*pvc_p = NULL;
-				return result;
-			}
-		}
-
-		hdlc->lmi.state |= LINK_STATE_CHANGED;
-		hdlc->pvc_count++;
-		return 0;
-	}
-
-	if (*pvc_p == NULL)		/* Delete PVC */
-		return -ENOENT;
-
-	pvc = *pvc_p;
-
-	if (pvc->netdev.flags & IFF_UP)
-		return -EBUSY;		/* PVC in use */
-
-	if (!mode_is(hdlc, MODE_SOFT) && hdlc->destroy_pvc)
-		hdlc->destroy_pvc(pvc);
-
-	hdlc->lmi.state |= LINK_STATE_CHANGED;
-	hdlc->pvc_count--;
-	*pvc_p = pvc->next;
-	unregister_netdevice(&pvc->netdev);
-	kfree(pvc);
-	return 0;
-}
-
-
-
-static int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
-{
-	hdlc_device *hdlc = dev_to_hdlc(dev);
-
-	switch(cmd) {
-	case HDLCGMODE:
-		ifr->ifr_ifru.ifru_ivalue = hdlc->mode;
-		return 0;
-
-	case HDLCSMODE:
-		return hdlc_set_mode(hdlc, ifr->ifr_ifru.ifru_ivalue);
-
-	case HDLCPVC:
-		return hdlc_fr_pvc(hdlc, ifr->ifr_ifru.ifru_ivalue);
-
-	default:
-		if (hdlc->ioctl != NULL)
-			return hdlc->ioctl(hdlc, ifr, cmd);
-	}
-
-	return -EINVAL;
-}
-
-
-
-static int hdlc_init(struct net_device *dev)
-{
-	hdlc_device *hdlc = dev_to_hdlc(dev);
-
-	memset(&(hdlc->stats), 0, sizeof(struct net_device_stats));
-
-	dev->get_stats = hdlc_get_stats;
-	dev->open = hdlc_open;
-	dev->stop = hdlc_close;
-	dev->hard_start_xmit = hdlc_xmit;
-	dev->do_ioctl = hdlc_ioctl;
-	dev->change_mtu = hdlc_change_mtu;
-	dev->mtu = HDLC_MAX_MTU;
-
-	dev->type = ARPHRD_RAWHDLC;
-	dev->hard_header_len = 16;
-
-	dev->flags = IFF_POINTOPOINT | IFF_NOARP;
-
-	return 0;
-}
-
-
-
-int register_hdlc_device(hdlc_device *hdlc)
-{
-	int result;
-	struct net_device *dev = hdlc_to_dev(hdlc);
-
-	dev->init = hdlc_init;
-	dev->priv = &hdlc->syncppp_ptr;
-	hdlc->syncppp_ptr = &hdlc->pppdev;
-	hdlc->pppdev.dev = dev;
-	hdlc->mode = MODE_NONE;
-	hdlc->lmi.T391 = 10;	/* polling verification timer */
-	hdlc->lmi.T392 = 15;	/* link integrity verification polling timer */
-	hdlc->lmi.N391 = 6;	/* full status polling counter */
-	hdlc->lmi.N392 = 3;	/* error threshold */
-	hdlc->lmi.N393 = 4;	/* monitored events count */
-
-	result = dev_alloc_name(dev, "hdlc%d");
-	if (result<0)
-		return result;
-
-	result = register_netdev(dev);
-	if (result != 0)
-		return -EIO;
-
-	MOD_INC_USE_COUNT;
-	return 0;
-}
-
-
-
-void unregister_hdlc_device(hdlc_device *hdlc)
-{
-	destroy_pvc_list(hdlc);
-	unregister_netdev(hdlc_to_dev(hdlc));
-	MOD_DEC_USE_COUNT;
-}
-
-MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
-MODULE_DESCRIPTION("HDLC support module");
-MODULE_LICENSE("GPL");
-
-EXPORT_SYMBOL(hdlc_netif_rx);
-EXPORT_SYMBOL(register_hdlc_device);
-EXPORT_SYMBOL(unregister_hdlc_device);
-
-static int __init hdlc_module_init(void)
-{
-	printk(KERN_INFO "%s\n", version);
-	return 0;
-}
-
-
-module_init(hdlc_module_init);
diff -urN linux-2.4.21-pre2/drivers/net/wan/hdlc_cisco.c linux-2.4.21-pre3/drivers/net/wan/hdlc_cisco.c
--- linux-2.4.21-pre2/drivers/net/wan/hdlc_cisco.c	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre3/drivers/net/wan/hdlc_cisco.c	Mon Jan  6 16:32:32 2003
@@ -0,0 +1,300 @@
+/*
+ * Generic HDLC support routines for Linux
+ * Cisco HDLC support
+ *
+ * Copyright (C) 2000 - 2001 Krzysztof Halasa <khc@pm.waw.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/errno.h>
+#include <linux/if_arp.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/pkt_sched.h>
+#include <linux/inetdevice.h>
+#include <linux/lapb.h>
+#include <linux/rtnetlink.h>
+#include <linux/hdlc.h>
+
+
+#define CISCO_MULTICAST		0x8F	/* Cisco multicast address */
+#define CISCO_UNICAST		0x0F	/* Cisco unicast address */
+#define CISCO_KEEPALIVE		0x8035	/* Cisco keepalive protocol */
+#define CISCO_SYS_INFO		0x2000	/* Cisco interface/system info */
+#define CISCO_ADDR_REQ		0	/* Cisco address request */
+#define CISCO_ADDR_REPLY	1	/* Cisco address reply */
+#define CISCO_KEEPALIVE_REQ	2	/* Cisco keepalive request */
+
+
+static int cisco_hard_header(struct sk_buff *skb, struct net_device *dev,
+			     u16 type, void *daddr, void *saddr,
+			     unsigned int len)
+{
+	hdlc_header *data;
+#ifdef CONFIG_HDLC_DEBUG_HARD_HEADER
+	printk(KERN_DEBUG "%s: cisco_hard_header called\n", dev->name);
+#endif
+
+	skb_push(skb, sizeof(hdlc_header));
+	data = (hdlc_header*)skb->data;
+	if (type == CISCO_KEEPALIVE)
+		data->address = CISCO_MULTICAST;
+	else
+		data->address = CISCO_UNICAST;
+	data->control = 0;
+	data->protocol = htons(type);
+
+	return sizeof(hdlc_header);
+}
+
+
+
+static void cisco_keepalive_send(hdlc_device *hdlc, u32 type,
+				 u32 par1, u32 par2)
+{
+	struct sk_buff *skb;
+	cisco_packet *data;
+
+	skb = dev_alloc_skb(sizeof(hdlc_header) + sizeof(cisco_packet));
+	if (!skb) {
+		printk(KERN_WARNING
+		       "%s: Memory squeeze on cisco_keepalive_send()\n",
+		       hdlc_to_name(hdlc));
+		return;
+	}
+	skb_reserve(skb, 4);
+	cisco_hard_header(skb, hdlc_to_dev(hdlc), CISCO_KEEPALIVE,
+			  NULL, NULL, 0);
+	data = (cisco_packet*)skb->tail;
+
+	data->type = htonl(type);
+	data->par1 = htonl(par1);
+	data->par2 = htonl(par2);
+	data->rel = 0xFFFF;
+	data->time = htonl(jiffies * 1000 / HZ);
+
+	skb_put(skb, sizeof(cisco_packet));
+	skb->priority = TC_PRIO_CONTROL;
+	skb->dev = hdlc_to_dev(hdlc);
+
+	dev_queue_xmit(skb);
+}
+
+
+
+static void cisco_rx(struct sk_buff *skb)
+{
+	hdlc_device *hdlc = dev_to_hdlc(skb->dev);
+	hdlc_header *data = (hdlc_header*)skb->data;
+	cisco_packet *cisco_data;
+	struct in_device *in_dev;
+	u32 addr, mask;
+
+	if (skb->len < sizeof(hdlc_header))
+		goto rx_error;
+
+	if (data->address != CISCO_MULTICAST &&
+	    data->address != CISCO_UNICAST)
+		goto rx_error;
+
+	skb_pull(skb, sizeof(hdlc_header));
+
+	switch(ntohs(data->protocol)) {
+	case ETH_P_IP:
+	case ETH_P_IPX:
+	case ETH_P_IPV6:
+		skb->protocol = data->protocol;
+		skb->dev = hdlc_to_dev(hdlc);
+		netif_rx(skb);
+		return;
+
+	case CISCO_SYS_INFO:
+		/* Packet is not needed, drop it. */
+		dev_kfree_skb_any(skb);
+		return;
+
+	case CISCO_KEEPALIVE:
+		if (skb->len != CISCO_PACKET_LEN &&
+		    skb->len != CISCO_BIG_PACKET_LEN) {
+			printk(KERN_INFO "%s: Invalid length of Cisco "
+			       "control packet (%d bytes)\n",
+			       hdlc_to_name(hdlc), skb->len);
+			goto rx_error;
+		}
+
+		cisco_data = (cisco_packet*)skb->data;
+
+		switch(ntohl (cisco_data->type)) {
+		case CISCO_ADDR_REQ: /* Stolen from syncppp.c :-) */
+			in_dev = hdlc_to_dev(hdlc)->ip_ptr;
+			addr = 0;
+			mask = ~0; /* is the mask correct? */
+
+			if (in_dev != NULL) {
+				struct in_ifaddr **ifap = &in_dev->ifa_list;
+
+				while (*ifap != NULL) {
+					if (strcmp(hdlc_to_name(hdlc),
+						   (*ifap)->ifa_label) == 0) {
+						addr = (*ifap)->ifa_local;
+						mask = (*ifap)->ifa_mask;
+						break;
+					}
+					ifap = &(*ifap)->ifa_next;
+				}
+
+				cisco_keepalive_send(hdlc, CISCO_ADDR_REPLY,
+						     addr, mask);
+			}
+			dev_kfree_skb_any(skb);
+			return;
+
+		case CISCO_ADDR_REPLY:
+			printk(KERN_INFO "%s: Unexpected Cisco IP address "
+			       "reply\n", hdlc_to_name(hdlc));
+			goto rx_error;
+
+		case CISCO_KEEPALIVE_REQ:
+			hdlc->state.cisco.rxseq = ntohl(cisco_data->par1);
+			if (ntohl(cisco_data->par2) == hdlc->state.cisco.txseq) {
+				hdlc->state.cisco.last_poll = jiffies;
+				if (!hdlc->state.cisco.up) {
+					u32 sec, min, hrs, days;
+					sec = ntohl(cisco_data->time) / 1000;
+					min = sec / 60; sec -= min * 60;
+					hrs = min / 60; min -= hrs * 60;
+					days = hrs / 24; hrs -= days * 24;
+					printk(KERN_INFO "%s: Link up (peer "
+					       "uptime %ud%uh%um%us)\n",
+					       hdlc_to_name(hdlc), days, hrs,
+					       min, sec);
+				}
+				hdlc->state.cisco.up = 1;
+			}
+
+			dev_kfree_skb_any(skb);
+			return;
+		} /* switch(keepalive type) */
+	} /* switch(protocol) */
+
+	printk(KERN_INFO "%s: Unsupported protocol %x\n", hdlc_to_name(hdlc),
+	       data->protocol);
+	dev_kfree_skb_any(skb);
+	return;
+
+ rx_error:
+	hdlc->stats.rx_errors++; /* Mark error */
+	dev_kfree_skb_any(skb);
+}
+
+
+
+static void cisco_timer(unsigned long arg)
+{
+	hdlc_device *hdlc = (hdlc_device*)arg;
+
+	if (hdlc->state.cisco.up &&
+	    jiffies - hdlc->state.cisco.last_poll >=
+	    hdlc->state.cisco.settings.timeout * HZ) {
+		hdlc->state.cisco.up = 0;
+		printk(KERN_INFO "%s: Link down\n", hdlc_to_name(hdlc));
+	}
+
+	cisco_keepalive_send(hdlc, CISCO_KEEPALIVE_REQ,
+			     ++hdlc->state.cisco.txseq,
+			     hdlc->state.cisco.rxseq);
+	hdlc->state.cisco.timer.expires = jiffies +
+		hdlc->state.cisco.settings.interval * HZ;
+	hdlc->state.cisco.timer.function = cisco_timer;
+	hdlc->state.cisco.timer.data = arg;
+	add_timer(&hdlc->state.cisco.timer);
+}
+
+
+
+static int cisco_open(hdlc_device *hdlc)
+{
+	hdlc->state.cisco.last_poll = 0;
+	hdlc->state.cisco.up = 0;
+	hdlc->state.cisco.txseq = hdlc->state.cisco.rxseq = 0;
+
+	init_timer(&hdlc->state.cisco.timer);
+	hdlc->state.cisco.timer.expires = jiffies + HZ; /*First poll after 1s*/
+	hdlc->state.cisco.timer.function = cisco_timer;
+	hdlc->state.cisco.timer.data = (unsigned long)hdlc;
+	add_timer(&hdlc->state.cisco.timer);
+	return 0;
+}
+
+
+
+static void cisco_close(hdlc_device *hdlc)
+{
+	del_timer_sync(&hdlc->state.cisco.timer);
+}
+
+
+
+int hdlc_cisco_ioctl(hdlc_device *hdlc, struct ifreq *ifr)
+{
+	cisco_proto *cisco_s = ifr->ifr_settings.ifs_ifsu.cisco;
+	const size_t size = sizeof(cisco_proto);
+	cisco_proto new_settings;
+	struct net_device *dev = hdlc_to_dev(hdlc);
+	int result;
+
+	switch (ifr->ifr_settings.type) {
+	case IF_GET_PROTO:
+		ifr->ifr_settings.type = IF_PROTO_CISCO;
+		if (ifr->ifr_settings.size < size) {
+			ifr->ifr_settings.size = size; /* data size wanted */
+			return -ENOBUFS;
+		}
+		if (copy_to_user(cisco_s, &hdlc->state.cisco.settings, size))
+			return -EFAULT;
+		return 0;
+
+	case IF_PROTO_CISCO:
+		if(!capable(CAP_NET_ADMIN))
+			return -EPERM;
+
+		if(dev->flags & IFF_UP)
+			return -EBUSY;
+
+		if (copy_from_user(&new_settings, cisco_s, size))
+			return -EFAULT;
+
+		if (new_settings.interval < 1 ||
+		    new_settings.timeout < 2)
+			return -EINVAL;
+
+		result=hdlc->attach(hdlc, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT);
+
+		if (result)
+			return result;
+
+		hdlc_proto_detach(hdlc);
+		memcpy(&hdlc->state.cisco.settings, &new_settings, size);
+
+		hdlc->open = cisco_open;
+		hdlc->stop = cisco_close;
+		hdlc->netif_rx = cisco_rx;
+		hdlc->proto = IF_PROTO_CISCO;
+		dev->hard_start_xmit = hdlc->xmit;
+		dev->hard_header = cisco_hard_header;
+		dev->type = ARPHRD_CISCO;
+		dev->addr_len = 0;
+		return 0;
+	}
+
+	return -EINVAL;
+}
diff -urN linux-2.4.21-pre2/drivers/net/wan/hdlc_fr.c linux-2.4.21-pre3/drivers/net/wan/hdlc_fr.c
--- linux-2.4.21-pre2/drivers/net/wan/hdlc_fr.c	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre3/drivers/net/wan/hdlc_fr.c	Mon Jan  6 16:32:32 2003
@@ -0,0 +1,862 @@
+/*
+ * Generic HDLC support routines for Linux
+ * Frame Relay support
+ *
+ * Copyright (C) 1999 - 2001 Krzysztof Halasa <khc@pm.waw.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/errno.h>
+#include <linux/if_arp.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/pkt_sched.h>
+#include <linux/inetdevice.h>
+#include <linux/lapb.h>
+#include <linux/rtnetlink.h>
+#include <linux/hdlc.h>
+
+
+__inline__ pvc_device* find_pvc(hdlc_device *hdlc, u16 dlci)
+{
+	pvc_device *pvc=hdlc->state.fr.first_pvc;
+	
+	while (pvc) {
+		if (netdev_dlci(&pvc->netdev) == dlci)
+			return pvc;
+		pvc = pvc->next;
+	}
+
+	return NULL;
+}
+
+
+
+__inline__ u16 status_to_dlci(hdlc_device *hdlc, u8 *status,
+			      int *active, int *new)
+{
+	*new = (status[2] & 0x08);
+	*active = (!*new && (status[2] & 0x02));
+
+	return ((status[0] & 0x3F)<<4) | ((status[1] & 0x78)>>3);
+}
+
+
+__inline__ void dlci_to_status(hdlc_device *hdlc, u16 dlci, u8 *status,
+			       int active, int new)
+{
+	status[0] = (dlci>>4) & 0x3F;
+	status[1] = ((dlci<<3) & 0x78) | 0x80;
+	status[2] = 0x80;
+
+	if (new)
+		status[2] |= 0x08;
+	else if (active)
+		status[2] |= 0x02;
+}
+
+
+
+static int fr_hard_header(struct sk_buff *skb, struct net_device *dev,
+			  u16 type, void *daddr, void *saddr, unsigned int len)
+{
+	u16 head_len;
+
+	if (!daddr)
+		daddr = dev->broadcast;
+
+#ifdef CONFIG_HDLC_DEBUG_HARD_HEADER
+	printk(KERN_DEBUG "%s: fr_hard_header called\n", dev->name);
+#endif
+
+	switch(type) {
+	case ETH_P_IP:
+		head_len = 4;
+		skb_push(skb, head_len);
+		skb->data[3] = NLPID_IP;
+		break;
+
+	case ETH_P_IPV6:
+		head_len = 4;
+		skb_push(skb, head_len);
+		skb->data[3] = NLPID_IPV6;
+		break;
+
+	case LMI_PROTO:
+		head_len = 4;
+		skb_push(skb, head_len);
+		skb->data[3] = LMI_PROTO;
+		break;
+
+	default:
+		head_len = 10;
+		skb_push(skb, head_len);
+		skb->data[3] = FR_PAD;
+		skb->data[4] = NLPID_SNAP;
+		skb->data[5] = FR_PAD;
+		skb->data[6] = FR_PAD;
+		skb->data[7] = FR_PAD;
+		skb->data[8] = type>>8;
+		skb->data[9] = (u8)type;
+	}
+
+	memcpy(skb->data, daddr, 2);
+	skb->data[2] = FR_UI;
+
+	return head_len;
+}
+
+
+
+static int pvc_open(struct net_device *dev)
+{
+	pvc_device *pvc = dev_to_pvc(dev);
+
+	if ((hdlc_to_dev(pvc->master)->flags & IFF_UP) == 0)
+		return -EIO;  /* Master must be UP in order to activate PVC */
+
+	if (pvc->master->state.fr.settings.lmi != LMI_NONE)
+		pvc->state.active = 0;
+	else
+		pvc->state.active = 1;
+
+	pvc->state.new = 0;
+	pvc->master->state.fr.changed = 1;
+	return 0;
+}
+
+
+
+static int pvc_close(struct net_device *dev)
+{
+	pvc_device *pvc = dev_to_pvc(dev);
+	pvc->state.active = pvc->state.new = 0;
+	pvc->master->state.fr.changed = 1;
+	return 0;
+}
+
+
+
+static int pvc_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	pvc_device *pvc = dev_to_pvc(dev);
+
+	if (pvc->state.active) {
+		skb->dev = hdlc_to_dev(pvc->master);
+		pvc->stats.tx_bytes += skb->len;
+		pvc->stats.tx_packets++;
+		if (pvc->state.fecn)
+			pvc->stats.tx_compressed++; /* TX Congestion counter */
+		dev_queue_xmit(skb);
+	} else {
+		pvc->stats.tx_dropped++;
+		dev_kfree_skb(skb);
+	}
+
+	return 0;
+}
+
+
+
+static struct net_device_stats *pvc_get_stats(struct net_device *dev)
+{
+	pvc_device *pvc = dev_to_pvc(dev);
+	return &pvc->stats;
+}
+
+
+
+static int pvc_change_mtu(struct net_device *dev, int new_mtu)
+{
+	if ((new_mtu < 68) || (new_mtu > HDLC_MAX_MTU))
+		return -EINVAL;
+	dev->mtu = new_mtu;
+	return 0;
+}
+
+
+
+static inline void fr_log_dlci_active(pvc_device *pvc)
+{
+	printk(KERN_INFO "%s: %sactive%s\n", pvc_to_name(pvc),
+	       pvc->state.active ? "" : "in",
+	       pvc->state.new ? " new" : "");
+}
+
+
+
+static inline u8 fr_lmi_nextseq(u8 x)
+{
+	x++;
+	return x ? x : 1;
+}
+
+
+
+static void fr_lmi_send(hdlc_device *hdlc, int fullrep)
+{
+	struct sk_buff *skb;
+	pvc_device *pvc = hdlc->state.fr.first_pvc;
+	int len = (hdlc->state.fr.settings.lmi == LMI_ANSI) ? LMI_ANSI_LENGTH
+		: LMI_LENGTH;
+	int stat_len = 3;
+	u8 *data;
+	int i = 0;
+
+	if (hdlc->state.fr.settings.dce && fullrep) {
+		len += hdlc->state.fr.pvc_count * (2 + stat_len);
+		if (len > HDLC_MAX_MTU) {
+			printk(KERN_WARNING "%s: Too many PVCs while sending "
+			       "LMI full report\n", hdlc_to_name(hdlc));
+			return;
+		}
+	}
+
+	skb = dev_alloc_skb(len);
+	if (!skb) {
+		printk(KERN_WARNING "%s: Memory squeeze on fr_lmi_send()\n",
+			       hdlc_to_name(hdlc));
+		return;
+	}
+	memset(skb->data, 0, len);
+	skb_reserve(skb, 4);
+	fr_hard_header(skb, hdlc_to_dev(hdlc), LMI_PROTO, NULL, NULL, 0);
+	data = skb->tail;
+	data[i++] = LMI_CALLREF;
+	data[i++] = hdlc->state.fr.settings.dce
+		? LMI_STATUS : LMI_STATUS_ENQUIRY;
+	if (hdlc->state.fr.settings.lmi == LMI_ANSI)
+		data[i++] = LMI_ANSI_LOCKSHIFT;
+	data[i++] = (hdlc->state.fr.settings.lmi == LMI_CCITT)
+		? LMI_CCITT_REPTYPE : LMI_REPTYPE;
+	data[i++] = LMI_REPT_LEN;
+	data[i++] = fullrep ? LMI_FULLREP : LMI_INTEGRITY;
+
+	data[i++] = (hdlc->state.fr.settings.lmi == LMI_CCITT)
+		? LMI_CCITT_ALIVE : LMI_ALIVE;
+	data[i++] = LMI_INTEG_LEN;
+	data[i++] = hdlc->state.fr.txseq =fr_lmi_nextseq(hdlc->state.fr.txseq);
+	data[i++] = hdlc->state.fr.rxseq;
+
+	if (hdlc->state.fr.settings.dce && fullrep) {
+		while (pvc) {
+			data[i++] = (hdlc->state.fr.settings.lmi == LMI_CCITT)
+				? LMI_CCITT_PVCSTAT : LMI_PVCSTAT;
+			data[i++] = stat_len;
+
+			if (hdlc->state.fr.reliable &&
+			    (pvc->netdev.flags & IFF_UP) &&
+			    !pvc->state.active &&
+			    !pvc->state.new) {
+				pvc->state.new = 1;
+				fr_log_dlci_active(pvc);
+			}
+
+			dlci_to_status(hdlc, netdev_dlci(&pvc->netdev),
+				       data + i,
+				       pvc->state.active, pvc->state.new);
+			i += stat_len;
+			pvc = pvc->next;
+		}
+	}
+
+	skb_put(skb, i);
+	skb->priority = TC_PRIO_CONTROL;
+	skb->dev = hdlc_to_dev(hdlc);
+
+	dev_queue_xmit(skb);
+}
+
+
+
+static void fr_timer(unsigned long arg)
+{
+	hdlc_device *hdlc = (hdlc_device*)arg;
+	int i, cnt = 0, reliable;
+	u32 list;
+
+	if (hdlc->state.fr.settings.dce)
+		reliable = (jiffies - hdlc->state.fr.last_poll <
+			    hdlc->state.fr.settings.t392 * HZ);
+	else {
+		hdlc->state.fr.last_errors <<= 1; /* Shift the list */
+		if (hdlc->state.fr.request) {
+			if (hdlc->state.fr.reliable)
+				printk(KERN_INFO "%s: No LMI status reply "
+				       "received\n", hdlc_to_name(hdlc));
+			hdlc->state.fr.last_errors |= 1;
+		}
+
+		list = hdlc->state.fr.last_errors;
+		for (i = 0; i < hdlc->state.fr.settings.n393; i++, list >>= 1)
+			cnt += (list & 1);	/* errors count */
+
+		reliable = (cnt < hdlc->state.fr.settings.n392);
+	}
+
+	if (hdlc->state.fr.reliable != reliable) {
+		pvc_device *pvc = hdlc->state.fr.first_pvc;
+
+		hdlc->state.fr.reliable = reliable;
+		printk(KERN_INFO "%s: Link %sreliable\n", hdlc_to_name(hdlc),
+		       reliable ? "" : "un");
+
+		if (reliable) {
+			hdlc->state.fr.n391cnt = 0; /* Request full status */
+			hdlc->state.fr.changed = 1;
+		} else {
+			while (pvc) {	/* Deactivate all PVCs */
+				pvc->state.new = pvc->state.active = 0;
+				pvc = pvc->next;
+			}
+		}
+	}
+
+	if (hdlc->state.fr.settings.dce)
+		hdlc->state.fr.timer.expires = jiffies +
+			hdlc->state.fr.settings.t392 * HZ;
+	else {
+		if (hdlc->state.fr.n391cnt)
+			hdlc->state.fr.n391cnt--;
+
+		fr_lmi_send(hdlc, hdlc->state.fr.n391cnt == 0);
+
+		hdlc->state.fr.request = 1;
+		hdlc->state.fr.timer.expires = jiffies +
+			hdlc->state.fr.settings.t391 * HZ;
+	}
+
+	hdlc->state.fr.timer.function = fr_timer;
+	hdlc->state.fr.timer.data = arg;
+	add_timer(&hdlc->state.fr.timer);
+}
+
+
+
+static int fr_lmi_recv(hdlc_device *hdlc, struct sk_buff *skb)
+{
+	int stat_len;
+	pvc_device *pvc;
+	int reptype = -1, error;
+	u8 rxseq, txseq;
+	int i;
+
+	if (skb->len < ((hdlc->state.fr.settings.lmi == LMI_ANSI)
+			? LMI_ANSI_LENGTH : LMI_LENGTH)) {
+		printk(KERN_INFO "%s: Short LMI frame\n", hdlc_to_name(hdlc));
+		return 1;
+	}
+
+	if (skb->data[5] != (!hdlc->state.fr.settings.dce ?
+			     LMI_STATUS : LMI_STATUS_ENQUIRY)) {
+		printk(KERN_INFO "%s: LMI msgtype=%x, Not LMI status %s\n",
+		       hdlc_to_name(hdlc), skb->data[2],
+		       hdlc->state.fr.settings.dce ? "enquiry" : "reply");
+		return 1;
+	}
+
+	i = (hdlc->state.fr.settings.lmi == LMI_ANSI) ? 7 : 6;
+
+	if (skb->data[i] !=
+	    ((hdlc->state.fr.settings.lmi == LMI_CCITT)
+	     ? LMI_CCITT_REPTYPE : LMI_REPTYPE)) {
+		printk(KERN_INFO "%s: Not a report type=%x\n",
+		       hdlc_to_name(hdlc), skb->data[i]);
+		return 1;
+	}
+	i++;
+
+	i++;				/* Skip length field */
+
+	reptype = skb->data[i++];
+
+	if (skb->data[i]!=
+	    ((hdlc->state.fr.settings.lmi == LMI_CCITT)
+	     ? LMI_CCITT_ALIVE : LMI_ALIVE)) {
+		printk(KERN_INFO "%s: Unsupported status element=%x\n",
+		       hdlc_to_name(hdlc), skb->data[i]);
+		return 1;
+	}
+	i++;
+
+	i++;			/* Skip length field */
+
+	hdlc->state.fr.rxseq = skb->data[i++]; /* TX sequence from peer */
+	rxseq = skb->data[i++];	/* Should confirm our sequence */
+
+	txseq = hdlc->state.fr.txseq;
+
+	if (hdlc->state.fr.settings.dce) {
+		if (reptype != LMI_FULLREP && reptype != LMI_INTEGRITY) {
+			printk(KERN_INFO "%s: Unsupported report type=%x\n",
+			       hdlc_to_name(hdlc), reptype);
+			return 1;
+		}
+	}
+
+	error = 0;
+	if (!hdlc->state.fr.reliable)
+		error = 1;
+
+	if (rxseq == 0 || rxseq != txseq) {
+		hdlc->state.fr.n391cnt = 0; /* Ask for full report next time */
+		error = 1;
+	}
+
+	if (hdlc->state.fr.settings.dce) {
+		if (hdlc->state.fr.fullrep_sent && !error) {
+/* Stop sending full report - the last one has been confirmed by DTE */
+			hdlc->state.fr.fullrep_sent = 0;
+			pvc = hdlc->state.fr.first_pvc;
+			while (pvc) {
+				if (pvc->state.new) {
+					pvc->state.new = 0;
+					pvc->state.active = 1;
+					fr_log_dlci_active(pvc);
+
+/* Tell DTE that new PVC is now active */
+					hdlc->state.fr.changed = 1;
+				}
+				pvc = pvc->next;
+			}
+		}
+
+		if (hdlc->state.fr.changed) {
+			reptype = LMI_FULLREP;
+			hdlc->state.fr.fullrep_sent = 1;
+			hdlc->state.fr.changed = 0;
+		}
+
+		fr_lmi_send(hdlc, reptype == LMI_FULLREP ? 1 : 0);
+		return 0;
+	}
+
+	/* DTE */
+
+	if (reptype != LMI_FULLREP || error)
+		return 0;
+
+	stat_len = 3;
+	pvc = hdlc->state.fr.first_pvc;
+
+	while (pvc) {
+		pvc->state.deleted = pvc->state.active; /* mark active PVCs */
+		pvc = pvc->next;
+	}
+
+	while (skb->len >= i + 2 + stat_len) {
+		u16 dlci;
+		int active, new;
+
+		if (skb->data[i] != ((hdlc->state.fr.settings.lmi == LMI_CCITT)
+				     ? LMI_CCITT_PVCSTAT : LMI_PVCSTAT)) {
+			printk(KERN_WARNING "%s: Invalid PVCSTAT ID: %x\n",
+			       hdlc_to_name(hdlc), skb->data[i]);
+			return 1;
+		}
+		i++;
+
+		if (skb->data[i] != stat_len) {
+			printk(KERN_WARNING "%s: Invalid PVCSTAT length: %x\n",
+			       hdlc_to_name(hdlc), skb->data[i]);
+			return 1;
+		}
+		i++;
+
+		dlci = status_to_dlci(hdlc, skb->data + i, &active, &new);
+		pvc = find_pvc(hdlc, dlci);
+
+		active |= new;
+		if (pvc) {
+			if (active && !pvc->state.active &&
+			    (pvc->netdev.flags & IFF_UP)) {
+				pvc->state.active = active;
+				fr_log_dlci_active(pvc);
+			}
+			pvc->state.deleted = 0;
+		}
+		else if (new)
+			printk(KERN_INFO "%s: new PVC available, DLCI=%u\n",
+			       hdlc_to_name(hdlc), dlci);
+
+		i += stat_len;
+	}
+
+	pvc = hdlc->state.fr.first_pvc;
+
+	while (pvc) {
+		if (pvc->state.deleted) {
+			pvc->state.active = pvc->state.new = 0;
+			fr_log_dlci_active(pvc);
+			pvc->state.deleted = 0;
+		}
+		pvc = pvc->next;
+	}
+
+	/* Next full report after N391 polls */
+	hdlc->state.fr.n391cnt = hdlc->state.fr.settings.n391;
+
+	return 0;
+}
+
+
+
+static void fr_rx(struct sk_buff *skb)
+{
+	hdlc_device *hdlc = dev_to_hdlc(skb->dev);
+	fr_hdr *fh = (fr_hdr*)skb->data;
+	u8 *data = skb->data;
+	u16 dlci;
+	pvc_device *pvc;
+
+	if (skb->len<4 || fh->ea1 || data[2] != FR_UI)
+		goto rx_error;
+
+	dlci = q922_to_dlci(skb->data);
+
+	if (dlci == LMI_DLCI) {
+		if (hdlc->state.fr.settings.lmi == LMI_NONE)
+			goto rx_error; /* LMI packet with no LMI? */
+
+		if (data[3] == LMI_PROTO) {
+			if (fr_lmi_recv(hdlc, skb))
+				goto rx_error;
+			else {
+				/* No request pending */
+				hdlc->state.fr.request = 0;
+				hdlc->state.fr.last_poll = jiffies;
+				dev_kfree_skb_any(skb);
+				return;
+			}
+		}
+
+		printk(KERN_INFO "%s: Received non-LMI frame with LMI DLCI\n",
+		       hdlc_to_name(hdlc));
+		goto rx_error;
+	}
+
+	pvc = find_pvc(hdlc, dlci);
+	if (!pvc) {
+#ifdef CONFIG_HDLC_DEBUG_PKT
+		printk(KERN_INFO "%s: No PVC for received frame's DLCI %d\n",
+		       hdlc_to_name(hdlc), dlci);
+#endif
+		goto rx_error;
+	}
+
+	if ((pvc->netdev.flags & IFF_UP) == 0) {
+#ifdef CONFIG_HDLC_DEBUG_PKT
+		printk(KERN_INFO "%s: PVC for received frame's DLCI %d is down\n",
+		       hdlc_to_name(hdlc), dlci);
+#endif
+		goto rx_error;
+	}
+
+	pvc->stats.rx_packets++; /* PVC traffic */
+	pvc->stats.rx_bytes += skb->len;
+
+	if (pvc->state.fecn != (fh->fecn ? PVC_STATE_FECN : 0)) {
+#ifdef CONFIG_HDLC_DEBUG_ECN
+		printk(KERN_DEBUG "%s: FECN O%s\n", pvc_to_name(pvc),
+		       fh->fecn ? "N" : "FF");
+#endif
+		pvc->state.fecn ^= 1;
+	}
+
+	if (pvc->state.becn != (fh->becn ? PVC_STATE_BECN : 0)) {
+#ifdef CONFIG_HDLC_DEBUG_ECN
+		printk(KERN_DEBUG "%s: BECN O%s\n", pvc_to_name(pvc),
+		       fh->becn ? "N" : "FF");
+#endif
+		pvc->state.becn ^= 1;
+	}
+
+	if (pvc->state.becn)
+		pvc->stats.rx_compressed++;
+
+	skb->dev = &pvc->netdev;
+
+	if (data[3] == NLPID_IP) {
+		skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */
+		skb->protocol = htons(ETH_P_IP);
+		netif_rx(skb);
+		return;
+	}
+
+
+	if (data[3] == NLPID_IPV6) {
+		skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */
+		skb->protocol = htons(ETH_P_IPV6);
+		netif_rx(skb);
+		return;
+	}
+
+	if (data[3] == FR_PAD && data[4] == NLPID_SNAP && data[5] == FR_PAD) {
+		u16 oui = ntohs(*(u16*)(data + 6));
+		u16 pid = ntohs(*(u16*)(data + 8));
+		skb_pull(skb, 10);
+
+		switch ((((u32)oui) << 16) | pid) {
+		case ETH_P_ARP: /* routed frame with SNAP */
+		case ETH_P_IPX:
+		case ETH_P_IP:	/* a long variant */
+		case ETH_P_IPV6:
+			skb->protocol = htons(pid);
+			break;
+
+		default:
+			printk(KERN_INFO "%s: Unsupported protocol, OUI=%x "
+			       "PID=%x\n", hdlc_to_name(hdlc), oui, pid);
+			dev_kfree_skb_any(skb);
+			return;
+		}
+
+		netif_rx(skb);
+		return;
+	}
+
+	printk(KERN_INFO "%s: Unsupported protocol, NLPID=%x\n",
+	       hdlc_to_name(hdlc), data[3]);
+	dev_kfree_skb_any(skb);
+	return;
+
+ rx_error:
+	hdlc->stats.rx_errors++; /* Mark error */
+	dev_kfree_skb_any(skb);
+}
+
+
+
+static int fr_open(hdlc_device *hdlc)
+{
+	if (hdlc->state.fr.settings.lmi != LMI_NONE) {
+		hdlc->state.fr.last_poll = 0;
+		hdlc->state.fr.reliable = 0;
+		hdlc->state.fr.changed = 1;
+		hdlc->state.fr.request = 0;
+		hdlc->state.fr.fullrep_sent = 0;
+		hdlc->state.fr.last_errors = 0xFFFFFFFF;
+		hdlc->state.fr.n391cnt = 0;
+		hdlc->state.fr.txseq = hdlc->state.fr.rxseq = 0;
+
+		init_timer(&hdlc->state.fr.timer);
+		/* First poll after 1 s */
+		hdlc->state.fr.timer.expires = jiffies + HZ;
+		hdlc->state.fr.timer.function = fr_timer;
+		hdlc->state.fr.timer.data = (unsigned long)hdlc;
+		add_timer(&hdlc->state.fr.timer);
+	} else
+		hdlc->state.fr.reliable = 1;
+
+	return 0;
+}
+
+
+
+static void fr_close(hdlc_device *hdlc)
+{
+	pvc_device *pvc = hdlc->state.fr.first_pvc;
+
+	if (hdlc->state.fr.settings.lmi != LMI_NONE)
+		del_timer_sync(&hdlc->state.fr.timer);
+
+	while(pvc) {
+		dev_close(&pvc->netdev); /* Shutdown all PVCs for this FRAD */
+		pvc = pvc->next;
+	}
+}
+
+
+
+static int fr_pvc(hdlc_device *hdlc, unsigned int dlci, int create)
+{
+	pvc_device **pvc_p = &hdlc->state.fr.first_pvc;
+	pvc_device *pvc;
+	int result;
+
+	if(dlci <= 0 || dlci >= 1024)
+		return -EINVAL;	/* Only 10 bits for DLCI, DLCI 0 reserved */
+
+	while(*pvc_p) {
+		if (netdev_dlci(&(*pvc_p)->netdev) == dlci)
+			break;
+		pvc_p = &(*pvc_p)->next;
+	}
+
+	if (create) {		/* Create PVC */
+		if (*pvc_p != NULL)
+			return -EEXIST;
+
+		pvc = *pvc_p = kmalloc(sizeof(pvc_device), GFP_KERNEL);
+		if (!pvc) {
+			printk(KERN_WARNING "%s: Memory squeeze on fr_pvc()\n",
+			       hdlc_to_name(hdlc));
+			return -ENOBUFS;
+		}
+		memset(pvc, 0, sizeof(pvc_device));
+
+		pvc->netdev.hard_start_xmit = pvc_xmit;
+		pvc->netdev.get_stats = pvc_get_stats;
+		pvc->netdev.open = pvc_open;
+		pvc->netdev.stop = pvc_close;
+		pvc->netdev.change_mtu = pvc_change_mtu;
+		pvc->netdev.mtu = HDLC_MAX_MTU;
+
+		pvc->netdev.type = ARPHRD_DLCI;
+		pvc->netdev.hard_header_len = 16;
+		pvc->netdev.hard_header = fr_hard_header;
+		pvc->netdev.tx_queue_len = 0;
+		pvc->netdev.flags = IFF_POINTOPOINT;
+
+		pvc->master = hdlc;
+		*(u16*)pvc->netdev.dev_addr = htons(dlci);
+		dlci_to_q922(pvc->netdev.broadcast, dlci);
+		pvc->netdev.addr_len = 2;
+
+		result = dev_alloc_name(&pvc->netdev, "pvc%d");
+		if (result < 0) {
+			kfree(pvc);
+			*pvc_p = NULL;
+			return result;
+		}
+
+		if (register_netdevice(&pvc->netdev) != 0) {
+			kfree(pvc);
+			*pvc_p = NULL;
+			return -EIO;
+		}
+
+		hdlc->state.fr.changed = 1;
+		hdlc->state.fr.pvc_count++;
+		return 0;
+	}
+
+	if (*pvc_p == NULL)		/* Delete PVC */
+		return -ENOENT;
+
+	pvc = *pvc_p;
+
+	if (pvc->netdev.flags & IFF_UP)
+		return -EBUSY;		/* PVC in use */
+
+	hdlc->state.fr.changed = 1;
+	hdlc->state.fr.pvc_count--;
+	*pvc_p = pvc->next;
+	unregister_netdevice(&pvc->netdev);
+	kfree(pvc);
+	return 0;
+}
+
+
+
+static void fr_destroy(hdlc_device *hdlc)
+{
+	pvc_device *pvc = hdlc->state.fr.first_pvc;
+	while(pvc) {
+		pvc_device *next = pvc->next;
+		unregister_netdev(&pvc->netdev);
+		kfree(pvc);
+		pvc = next;
+	}
+
+	hdlc->state.fr.first_pvc = NULL; /* All PVCs destroyed */
+	hdlc->state.fr.pvc_count = 0;
+	hdlc->state.fr.changed = 1;
+}
+
+
+
+int hdlc_fr_ioctl(hdlc_device *hdlc, struct ifreq *ifr)
+{
+	fr_proto *fr_s = ifr->ifr_settings.ifs_ifsu.fr;
+	const size_t size = sizeof(fr_proto);
+	fr_proto new_settings;
+	struct net_device *dev = hdlc_to_dev(hdlc);
+	fr_proto_pvc pvc;
+	int result;
+
+	switch (ifr->ifr_settings.type) {
+	case IF_GET_PROTO:
+		ifr->ifr_settings.type = IF_PROTO_FR;
+		if (ifr->ifr_settings.size < size) {
+			ifr->ifr_settings.size = size; /* data size wanted */
+			return -ENOBUFS;
+		}
+		if (copy_to_user(fr_s, &hdlc->state.fr.settings, size))
+			return -EFAULT;
+		return 0;
+
+	case IF_PROTO_FR:
+		if(!capable(CAP_NET_ADMIN))
+			return -EPERM;
+
+		if(dev->flags & IFF_UP)
+			return -EBUSY;
+
+		if (copy_from_user(&new_settings, fr_s, size))
+			return -EFAULT;
+
+		if (new_settings.lmi == LMI_DEFAULT)
+			new_settings.lmi = LMI_ANSI;
+
+		if ((new_settings.lmi != LMI_NONE &&
+		     new_settings.lmi != LMI_ANSI &&
+		     new_settings.lmi != LMI_CCITT) ||
+		    new_settings.t391 < 1 ||
+		    new_settings.t392 < 2 ||
+		    new_settings.n391 < 1 ||
+		    new_settings.n392 < 1 ||
+		    new_settings.n393 < new_settings.n392 ||
+		    new_settings.n393 > 32 ||
+		    (new_settings.dce != 0 &&
+		     new_settings.dce != 1))
+			return -EINVAL;
+
+		result=hdlc->attach(hdlc, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT);
+		if (result)
+			return result;
+
+		if (hdlc->proto != IF_PROTO_FR) {
+			hdlc_proto_detach(hdlc);
+			hdlc->state.fr.first_pvc = NULL;
+			hdlc->state.fr.pvc_count = 0;
+		}
+		memcpy(&hdlc->state.fr.settings, &new_settings, size);
+
+		hdlc->open = fr_open;
+		hdlc->stop = fr_close;
+		hdlc->netif_rx = fr_rx;
+		hdlc->proto_detach = fr_destroy;
+		hdlc->proto = IF_PROTO_FR;
+		dev->hard_start_xmit = hdlc->xmit;
+		dev->hard_header = fr_hard_header;
+		dev->type = ARPHRD_FRAD;
+		dev->addr_len = 2;
+		*(u16*)dev->dev_addr = htons(LMI_DLCI);
+		dlci_to_q922(dev->broadcast, LMI_DLCI);
+		return 0;
+
+	case IF_PROTO_FR_ADD_PVC:
+	case IF_PROTO_FR_DEL_PVC:
+		if(!capable(CAP_NET_ADMIN))
+			return -EPERM;
+
+		if (copy_from_user(&pvc, ifr->ifr_settings.ifs_ifsu.fr_pvc,
+				   sizeof(fr_proto_pvc)))
+			return -EFAULT;
+
+		return fr_pvc(hdlc, pvc.dlci,
+			      ifr->ifr_settings.type == IF_PROTO_FR_ADD_PVC);
+	}
+
+	return -EINVAL;
+}
diff -urN linux-2.4.21-pre2/drivers/net/wan/hdlc_generic.c linux-2.4.21-pre3/drivers/net/wan/hdlc_generic.c
--- linux-2.4.21-pre2/drivers/net/wan/hdlc_generic.c	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre3/drivers/net/wan/hdlc_generic.c	Mon Jan  6 16:32:32 2003
@@ -0,0 +1,197 @@
+/*
+ * Generic HDLC support routines for Linux
+ *
+ * Copyright (C) 1999 - 2001 Krzysztof Halasa <khc@pm.waw.pl>
+ *
+ * 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.
+ *
+ * Current status:
+ *    - this is work in progress
+ *    - not heavily tested on SMP
+ *    - currently supported:
+ *	* raw IP-in-HDLC
+ *	* Cisco HDLC
+ *	* Frame Relay with ANSI or CCITT LMI (both user and network side)
+ *	* PPP
+ *	* X.25
+ *
+ * Use sethdlc utility to set line parameters, protocol and PVCs
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/errno.h>
+#include <linux/if_arp.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/pkt_sched.h>
+#include <linux/inetdevice.h>
+#include <linux/lapb.h>
+#include <linux/rtnetlink.h>
+#include <linux/hdlc.h>
+
+
+static const char* version = "HDLC support module revision 1.11";
+
+
+static int hdlc_change_mtu(struct net_device *dev, int new_mtu)
+{
+	if ((new_mtu < 68) || (new_mtu > HDLC_MAX_MTU))
+		return -EINVAL;
+	dev->mtu = new_mtu;
+	return 0;
+}
+
+
+
+static struct net_device_stats *hdlc_get_stats(struct net_device *dev)
+{
+	return &dev_to_hdlc(dev)->stats;
+}
+
+
+
+static int hdlc_rcv(struct sk_buff *skb, struct net_device *dev,
+		    struct packet_type *p)
+{
+	dev_to_hdlc(dev)->netif_rx(skb);
+	return 0;
+}
+
+
+#ifndef CONFIG_HDLC_RAW
+#define hdlc_raw_ioctl(hdlc, ifr)	-ENOSYS
+#endif
+
+#ifndef CONFIG_HDLC_PPP
+#define hdlc_ppp_ioctl(hdlc, ifr)	-ENOSYS
+#endif
+
+#ifndef CONFIG_HDLC_CISCO
+#define hdlc_cisco_ioctl(hdlc, ifr)	-ENOSYS
+#endif
+
+#ifndef CONFIG_HDLC_FR
+#define hdlc_fr_ioctl(hdlc, ifr)	-ENOSYS
+#endif
+
+#ifndef CONFIG_HDLC_X25
+#define hdlc_x25_ioctl(hdlc, ifr)	-ENOSYS
+#endif
+
+
+int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+	hdlc_device *hdlc = dev_to_hdlc(dev);
+	unsigned int proto;
+
+	if (cmd != SIOCWANDEV)
+		return -EINVAL;
+
+	switch(ifr->ifr_settings.type) {
+	case IF_PROTO_HDLC:
+	case IF_PROTO_PPP:
+	case IF_PROTO_CISCO:
+	case IF_PROTO_FR:
+	case IF_PROTO_X25:
+		proto = ifr->ifr_settings.type;
+		break;
+
+	default:
+		proto = hdlc->proto;
+	}
+
+	switch(proto) {
+	case IF_PROTO_HDLC:	return hdlc_raw_ioctl(hdlc, ifr);
+	case IF_PROTO_PPP:	return hdlc_ppp_ioctl(hdlc, ifr);
+	case IF_PROTO_CISCO:	return hdlc_cisco_ioctl(hdlc, ifr);
+	case IF_PROTO_FR:	return hdlc_fr_ioctl(hdlc, ifr);
+	case IF_PROTO_X25:	return hdlc_x25_ioctl(hdlc, ifr);
+	default:		return -EINVAL;
+	}
+}
+
+
+
+int register_hdlc_device(hdlc_device *hdlc)
+{
+	int result;
+	struct net_device *dev = hdlc_to_dev(hdlc);
+
+	dev->get_stats = hdlc_get_stats;
+	dev->change_mtu = hdlc_change_mtu;
+	dev->mtu = HDLC_MAX_MTU;
+
+	dev->type = ARPHRD_RAWHDLC;
+	dev->hard_header_len = 16;
+
+	dev->flags = IFF_POINTOPOINT | IFF_NOARP;
+
+	hdlc->proto = -1;
+	hdlc->proto_detach = NULL;
+
+	result = dev_alloc_name(dev, "hdlc%d");
+	if (result<0)
+		return result;
+
+	result = register_netdev(dev);
+	if (result != 0)
+		return -EIO;
+
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+
+
+void unregister_hdlc_device(hdlc_device *hdlc)
+{
+	hdlc_proto_detach(hdlc);
+
+	unregister_netdev(hdlc_to_dev(hdlc));
+	MOD_DEC_USE_COUNT;
+}
+
+
+
+MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
+MODULE_DESCRIPTION("HDLC support module");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(hdlc_ioctl);
+EXPORT_SYMBOL(register_hdlc_device);
+EXPORT_SYMBOL(unregister_hdlc_device);
+
+struct packet_type hdlc_packet_type=
+{
+        __constant_htons(ETH_P_HDLC),
+        NULL,
+        hdlc_rcv,
+        NULL,
+        NULL
+};
+
+
+static int __init hdlc_module_init(void)
+{
+	printk(KERN_INFO "%s\n", version);
+        dev_add_pack(&hdlc_packet_type);
+	return 0;
+}
+
+
+
+static void __exit hdlc_module_exit(void)
+{
+	dev_remove_pack(&hdlc_packet_type);
+}
+
+
+module_init(hdlc_module_init);
+module_exit(hdlc_module_exit);
diff -urN linux-2.4.21-pre2/drivers/net/wan/hdlc_ppp.c linux-2.4.21-pre3/drivers/net/wan/hdlc_ppp.c
--- linux-2.4.21-pre2/drivers/net/wan/hdlc_ppp.c	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre3/drivers/net/wan/hdlc_ppp.c	Mon Jan  6 16:32:32 2003
@@ -0,0 +1,116 @@
+/*
+ * Generic HDLC support routines for Linux
+ * Point-to-point protocol support
+ *
+ * Copyright (C) 1999 - 2001 Krzysztof Halasa <khc@pm.waw.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/errno.h>
+#include <linux/if_arp.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/pkt_sched.h>
+#include <linux/inetdevice.h>
+#include <linux/lapb.h>
+#include <linux/rtnetlink.h>
+#include <linux/hdlc.h>
+
+
+static int ppp_open(hdlc_device *hdlc)
+{
+	struct net_device *dev = hdlc_to_dev(hdlc);
+	void *old_ioctl;
+	int result;
+
+	dev->priv = &hdlc->state.ppp.syncppp_ptr;
+	hdlc->state.ppp.syncppp_ptr = &hdlc->state.ppp.pppdev;
+	hdlc->state.ppp.pppdev.dev = dev;
+
+	old_ioctl = dev->do_ioctl;
+	hdlc->state.ppp.old_change_mtu = dev->change_mtu;
+	sppp_attach(&hdlc->state.ppp.pppdev);
+	/* sppp_attach nukes them. We don't need syncppp's ioctl */
+	dev->do_ioctl = old_ioctl;
+	hdlc->state.ppp.pppdev.sppp.pp_flags &= ~PP_CISCO;
+	dev->type = ARPHRD_PPP;
+	result = sppp_open(dev);
+	if (result) {
+		sppp_detach(dev);
+		return result;
+	}
+
+	return 0;
+}
+
+
+
+static void ppp_close(hdlc_device *hdlc)
+{
+	struct net_device *dev = hdlc_to_dev(hdlc);
+
+	sppp_close(dev);
+	sppp_detach(dev);
+	dev->rebuild_header = NULL;
+	dev->change_mtu = hdlc->state.ppp.old_change_mtu;
+	dev->mtu = HDLC_MAX_MTU;
+	dev->hard_header_len = 16;
+}
+
+
+
+static void ppp_rx(struct sk_buff *skb)
+{
+	skb->protocol = htons(ETH_P_WAN_PPP);
+	netif_rx(skb);
+}
+
+
+
+int hdlc_ppp_ioctl(hdlc_device *hdlc, struct ifreq *ifr)
+{
+	struct net_device *dev = hdlc_to_dev(hdlc);
+	int result;
+
+	switch (ifr->ifr_settings.type) {
+	case IF_GET_PROTO:
+		ifr->ifr_settings.type = IF_PROTO_PPP;
+		return 0; /* return protocol only, no settable parameters */
+
+	case IF_PROTO_PPP:
+		if(!capable(CAP_NET_ADMIN))
+			return -EPERM;
+
+		if(dev->flags & IFF_UP)
+			return -EBUSY;
+
+		/* no settable parameters */
+
+		result=hdlc->attach(hdlc, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT);
+		if (result)
+			return result;
+
+		hdlc_proto_detach(hdlc);
+
+		hdlc->open = ppp_open;
+		hdlc->stop = ppp_close;
+		hdlc->netif_rx = ppp_rx;
+		hdlc->proto = IF_PROTO_PPP;
+		dev->hard_start_xmit = hdlc->xmit;
+		dev->hard_header = NULL;
+		dev->type = ARPHRD_PPP;
+		dev->addr_len = 0;
+		return 0;
+	}
+
+	return -EINVAL;
+}
diff -urN linux-2.4.21-pre2/drivers/net/wan/hdlc_raw.c linux-2.4.21-pre3/drivers/net/wan/hdlc_raw.c
--- linux-2.4.21-pre2/drivers/net/wan/hdlc_raw.c	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre3/drivers/net/wan/hdlc_raw.c	Mon Jan  6 16:32:32 2003
@@ -0,0 +1,92 @@
+/*
+ * Generic HDLC support routines for Linux
+ * HDLC support
+ *
+ * Copyright (C) 1999 - 2001 Krzysztof Halasa <khc@pm.waw.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/errno.h>
+#include <linux/if_arp.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/pkt_sched.h>
+#include <linux/inetdevice.h>
+#include <linux/lapb.h>
+#include <linux/rtnetlink.h>
+#include <linux/hdlc.h>
+
+
+static void raw_rx(struct sk_buff *skb)
+{
+	skb->protocol = htons(ETH_P_IP);
+	netif_rx(skb);
+}
+
+
+
+int hdlc_raw_ioctl(hdlc_device *hdlc, struct ifreq *ifr)
+{
+	raw_hdlc_proto *raw_s = ifr->ifr_settings.ifs_ifsu.raw_hdlc;
+	const size_t size = sizeof(raw_hdlc_proto);
+	raw_hdlc_proto new_settings;
+	struct net_device *dev = hdlc_to_dev(hdlc);
+	int result;
+
+	switch (ifr->ifr_settings.type) {
+	case IF_GET_PROTO:
+		ifr->ifr_settings.type = IF_PROTO_HDLC;
+		if (ifr->ifr_settings.size < size) {
+			ifr->ifr_settings.size = size; /* data size wanted */
+			return -ENOBUFS;
+		}
+		if (copy_to_user(raw_s, &hdlc->state.raw_hdlc.settings, size))
+			return -EFAULT;
+		return 0;
+
+	case IF_PROTO_HDLC:
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
+
+		if (dev->flags & IFF_UP)
+			return -EBUSY;
+
+		if (copy_from_user(&new_settings, raw_s, size))
+			return -EFAULT;
+
+		if (new_settings.encoding == ENCODING_DEFAULT)
+			new_settings.encoding = ENCODING_NRZ;
+
+		if (new_settings.parity == PARITY_DEFAULT)
+			new_settings.parity = PARITY_NONE;
+
+		result = hdlc->attach(hdlc, new_settings.encoding,
+				      new_settings.parity);
+		if (result)
+			return result;
+
+		hdlc_proto_detach(hdlc);
+		memcpy(&hdlc->state.raw_hdlc.settings, &new_settings, size);
+
+		hdlc->open = NULL;
+		hdlc->stop = NULL;
+		hdlc->netif_rx = raw_rx;
+		hdlc->proto = IF_PROTO_HDLC;
+		dev->hard_start_xmit = hdlc->xmit;
+		dev->hard_header = NULL;
+		dev->type = ARPHRD_RAWHDLC;
+		dev->addr_len = 0;
+		return 0;
+	}
+
+	return -EINVAL;
+}
diff -urN linux-2.4.21-pre2/drivers/net/wan/hdlc_x25.c linux-2.4.21-pre3/drivers/net/wan/hdlc_x25.c
--- linux-2.4.21-pre2/drivers/net/wan/hdlc_x25.c	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre3/drivers/net/wan/hdlc_x25.c	Mon Jan  6 16:32:32 2003
@@ -0,0 +1,216 @@
+/*
+ * Generic HDLC support routines for Linux
+ * X.25 support
+ *
+ * Copyright (C) 1999 - 2001 Krzysztof Halasa <khc@pm.waw.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/errno.h>
+#include <linux/if_arp.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/pkt_sched.h>
+#include <linux/inetdevice.h>
+#include <linux/lapb.h>
+#include <linux/rtnetlink.h>
+#include <linux/hdlc.h>
+
+/* These functions are callbacks called by LAPB layer */
+
+static void x25_connect_disconnect(void *token, int reason, int code)
+{
+	hdlc_device *hdlc = token;
+	struct sk_buff *skb;
+	unsigned char *ptr;
+
+	if ((skb = dev_alloc_skb(1)) == NULL) {
+		printk(KERN_ERR "%s: out of memory\n", hdlc_to_name(hdlc));
+		return;
+	}
+
+	ptr = skb_put(skb, 1);
+	*ptr = code;
+
+	skb->dev = hdlc_to_dev(hdlc);
+	skb->protocol = htons(ETH_P_X25);
+	skb->mac.raw = skb->data;
+	skb->pkt_type = PACKET_HOST;
+
+	netif_rx(skb);
+}
+
+
+
+static void x25_connected(void *token, int reason)
+{
+	x25_connect_disconnect(token, reason, 1);
+}
+
+
+
+static void x25_disconnected(void *token, int reason)
+{
+	x25_connect_disconnect(token, reason, 2);
+}
+
+
+
+static int x25_data_indication(void *token, struct sk_buff *skb)
+{
+	hdlc_device *hdlc = token;
+	unsigned char *ptr;
+
+	ptr = skb_push(skb, 1);
+	*ptr = 0;
+
+	skb->dev = hdlc_to_dev(hdlc);
+	skb->protocol = htons(ETH_P_X25);
+	skb->mac.raw = skb->data;
+	skb->pkt_type = PACKET_HOST;
+
+	return netif_rx(skb);
+}
+
+
+
+static void x25_data_transmit(void *token, struct sk_buff *skb)
+{
+	hdlc_device *hdlc = token;
+	hdlc->xmit(skb, hdlc_to_dev(hdlc)); /* Ignore return value :-( */
+}
+
+
+
+static int x25_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	hdlc_device *hdlc = dev_to_hdlc(dev);
+	int result;
+
+
+	/* X.25 to LAPB */
+	switch (skb->data[0]) {
+	case 0:		/* Data to be transmitted */
+		skb_pull(skb, 1);
+		if ((result = lapb_data_request(hdlc, skb)) != LAPB_OK)
+			dev_kfree_skb(skb);
+		return 0;
+
+	case 1:
+		if ((result = lapb_connect_request(hdlc))!= LAPB_OK) {
+			if (result == LAPB_CONNECTED)
+				/* Send connect confirm. msg to level 3 */
+				x25_connected(hdlc, 0);
+			else
+				printk(KERN_ERR "%s: LAPB connect request "
+				       "failed, error code = %i\n",
+				       hdlc_to_name(hdlc), result);
+		}
+		break;
+
+	case 2:
+		if ((result = lapb_disconnect_request(hdlc)) != LAPB_OK) {
+			if (result == LAPB_NOTCONNECTED)
+				/* Send disconnect confirm. msg to level 3 */
+				x25_disconnected(hdlc, 0);
+			else
+				printk(KERN_ERR "%s: LAPB disconnect request "
+				       "failed, error code = %i\n",
+				       hdlc_to_name(hdlc), result);
+		}
+		break;
+
+	default:		/* to be defined */
+		break;
+	}
+
+	dev_kfree_skb(skb);
+	return 0;
+}
+
+
+
+static int x25_open(hdlc_device *hdlc)
+{
+	struct lapb_register_struct cb;
+	int result;
+
+	cb.connect_confirmation = x25_connected;
+	cb.connect_indication = x25_connected;
+	cb.disconnect_confirmation = x25_disconnected;
+	cb.disconnect_indication = x25_disconnected;
+	cb.data_indication = x25_data_indication;
+	cb.data_transmit = x25_data_transmit;
+
+	result = lapb_register(hdlc, &cb);
+	if (result != LAPB_OK)
+		return result;
+	return 0;
+}
+
+
+
+static void x25_close(hdlc_device *hdlc)
+{
+	lapb_unregister(hdlc);
+}
+
+
+
+static void x25_rx(struct sk_buff *skb)
+{
+	hdlc_device *hdlc = dev_to_hdlc(skb->dev);
+
+	if (lapb_data_received(hdlc, skb) == LAPB_OK)
+		return;
+	hdlc->stats.rx_errors++;
+	dev_kfree_skb_any(skb);
+}
+
+
+
+int hdlc_x25_ioctl(hdlc_device *hdlc, struct ifreq *ifr)
+{
+	struct net_device *dev = hdlc_to_dev(hdlc);
+	int result;
+
+	switch (ifr->ifr_settings.type) {
+	case IF_GET_PROTO:
+		ifr->ifr_settings.type = IF_PROTO_X25;
+		return 0; /* return protocol only, no settable parameters */
+
+	case IF_PROTO_X25:
+		if(!capable(CAP_NET_ADMIN))
+			return -EPERM;
+
+		if(dev->flags & IFF_UP)
+			return -EBUSY;
+
+		result=hdlc->attach(hdlc, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT);
+		if (result)
+			return result;
+
+		hdlc_proto_detach(hdlc);
+
+		hdlc->open = x25_open;
+		hdlc->stop = x25_close;
+		hdlc->netif_rx = x25_rx;
+		hdlc->proto = IF_PROTO_X25;
+		dev->hard_start_xmit = x25_xmit;
+		dev->hard_header = NULL;
+		dev->type = ARPHRD_X25;
+		dev->addr_len = 0;
+		return 0;
+	}
+
+	return -EINVAL;
+}
diff -urN linux-2.4.21-pre2/drivers/net/wan/n2.c linux-2.4.21-pre3/drivers/net/wan/n2.c
--- linux-2.4.21-pre2/drivers/net/wan/n2.c	Thu Sep 13 16:04:43 2001
+++ linux-2.4.21-pre3/drivers/net/wan/n2.c	Mon Jan  6 16:32:32 2003
@@ -1,7 +1,7 @@
 /*
  * SDL Inc. RISCom/N2 synchronous serial card driver for Linux
  *
- * Copyright (C) 1998-2000 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 1998-2002 Krzysztof Halasa <khc@pm.waw.pl>
  *
  * 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
@@ -17,10 +17,10 @@
  *    SDL Inc. PPP/HDLC/CISCO driver
  */
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/slab.h>
-#include <linux/sched.h>
 #include <linux/types.h>
 #include <linux/fcntl.h>
 #include <linux/in.h>
@@ -33,10 +33,8 @@
 #include <asm/io.h>
 #include "hd64570.h"
 
-#define DEBUG_RINGS
-/* #define DEBUG_PKT */
 
-static const char* version = "SDL RISCom/N2 driver revision: 1.02 for Linux 2.4";
+static const char* version = "SDL RISCom/N2 driver version: 1.10";
 static const char* devname = "RISCom/N2";
 
 #define USE_WINDOWSIZE 16384
@@ -87,9 +85,9 @@
 	hdlc_device hdlc;	/* HDLC device struct - must be first */
 	struct card_s *card;
 	spinlock_t lock;	/* TX lock */
-	int clkmode;		/* clock mode */
-	int clkrate;		/* clock rate */
-	int line;		/* loopback only */
+	sync_serial_settings settings;
+	unsigned short encoding;
+	unsigned short parity;
 	u8 rxs, txs, tmc;	/* SCA registers */
 	u8 valid;		/* port enabled */
 	u8 phy_node;		/* physical port # - 0 or 1 */
@@ -116,6 +114,9 @@
 }card_t;
 
 
+static card_t *first_card;
+static card_t **new_card = &first_card;
+
 
 #define sca_reg(reg, card) (0x8000 | (card)->io | \
 			    ((reg) & 0x0F) | (((reg) & 0xF0) << 6))
@@ -157,7 +158,7 @@
 
 
 
-static int n2_set_clock(port_t *port, int value)
+static void n2_set_iface(port_t *port)
 {
 	card_t *card = port->card;
 	int io = card->io;
@@ -166,13 +167,7 @@
 	u8 rxs = port->rxs & CLK_BRG_MASK;
 	u8 txs = port->txs & CLK_BRG_MASK;
 
-	switch(value) {
-	case CLOCK_EXT:
-		mcr &= port->phy_node ? ~CLOCK_OUT_PORT1 : ~CLOCK_OUT_PORT0;
-		rxs |= CLK_LINE_RX; /* RXC input */
-		txs |= CLK_LINE_TX; /* TXC input */
-		break;
-
+	switch(port->settings.clock_type) {
 	case CLOCK_INT:
 		mcr |= port->phy_node ? CLOCK_OUT_PORT1 : CLOCK_OUT_PORT0;
 		rxs |= CLK_BRG_RX; /* BRG output */
@@ -191,8 +186,10 @@
 		txs |= CLK_RXCLK_TX; /* RX clock */
 		break;
 
-	default:
-		return -EINVAL;
+	default:		/* Clock EXTernal */
+		mcr &= port->phy_node ? ~CLOCK_OUT_PORT1 : ~CLOCK_OUT_PORT0;
+		rxs |= CLK_LINE_RX; /* RXC input */
+		txs |= CLK_LINE_TX; /* TXC input */
 	}
 
 	outb(mcr, io + N2_MCR);
@@ -200,17 +197,21 @@
 	port->txs = txs;
 	sca_out(rxs, msci + RXS, card);
 	sca_out(txs, msci + TXS, card);
-	port->clkmode = value;
-	return 0;
+	sca_set_port(port);
 }
 
 
 
-static int n2_open(hdlc_device *hdlc)
+static int n2_open(struct net_device *dev)
 {
+	hdlc_device *hdlc = dev_to_hdlc(dev);
 	port_t *port = hdlc_to_port(hdlc);
 	int io = port->card->io;
-	u8 mcr = inb(io + N2_MCR) | (port->phy_node ? TX422_PORT1 : TX422_PORT0);
+	u8 mcr = inb(io + N2_MCR) | (port->phy_node ? TX422_PORT1:TX422_PORT0);
+
+	int result = hdlc_open(hdlc);
+	if (result)
+		return result;
 
 	MOD_INC_USE_COUNT;
 	mcr &= port->phy_node ? ~DTR_PORT1 : ~DTR_PORT0; /* set DTR ON */
@@ -219,14 +220,15 @@
 	outb(inb(io + N2_PCR) | PCR_ENWIN, io + N2_PCR); /* open window */
 	outb(inb(io + N2_PSR) | PSR_DMAEN, io + N2_PSR); /* enable dma */
 	sca_open(hdlc);
-	n2_set_clock(port, port->clkmode);
+	n2_set_iface(port);
 	return 0;
 }
 
 
 
-static void n2_close(hdlc_device *hdlc)
+static int n2_close(struct net_device *dev)
 {
+	hdlc_device *hdlc = dev_to_hdlc(dev);
 	port_t *port = hdlc_to_port(hdlc);
 	int io = port->card->io;
 	u8 mcr = inb(io+N2_MCR) | (port->phy_node ? TX422_PORT1 : TX422_PORT0);
@@ -234,52 +236,63 @@
 	sca_close(hdlc);
 	mcr |= port->phy_node ? DTR_PORT1 : DTR_PORT0; /* set DTR OFF */
 	outb(mcr, io + N2_MCR);
+	hdlc_close(hdlc);
 	MOD_DEC_USE_COUNT;
+	return 0;
 }
 
 
 
-static int n2_ioctl(hdlc_device *hdlc, struct ifreq *ifr, int cmd)
+static int n2_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 {
-	int value = ifr->ifr_ifru.ifru_ivalue;
-	int result = 0;
+	const size_t size = sizeof(sync_serial_settings);
+	sync_serial_settings new_line, *line = ifr->ifr_settings.ifs_ifsu.sync;
+	hdlc_device *hdlc = dev_to_hdlc(dev);
 	port_t *port = hdlc_to_port(hdlc);
 
-	if(!capable(CAP_NET_ADMIN))
-		return -EPERM;
+#ifdef CONFIG_HDLC_DEBUG_RINGS
+	if (cmd == SIOCDEVPRIVATE) {
+		sca_dump_rings(hdlc);
+		return 0;
+	}
+#endif
+	if (cmd != SIOCWANDEV)
+		return hdlc_ioctl(dev, ifr, cmd);
 
-	switch(cmd) {
-	case HDLCSCLOCK:
-		result = n2_set_clock(port, value);
-	case HDLCGCLOCK:
-		value = port->clkmode;
-		break;
+	switch(ifr->ifr_settings.type) {
+	case IF_GET_IFACE:
+		ifr->ifr_settings.type = IF_IFACE_SYNC_SERIAL;
+		if (ifr->ifr_settings.size < size) {
+			ifr->ifr_settings.size = size; /* data size wanted */
+			return -ENOBUFS;
+		}
+		if (copy_to_user(line, &port->settings, size))
+			return -EFAULT;
+		return 0;
 
-	case HDLCSCLOCKRATE:
-		port->clkrate = value;
-		sca_set_clock(port);
-	case HDLCGCLOCKRATE:
-		value = port->clkrate;
-		break;
+	case IF_IFACE_SYNC_SERIAL:
+		if(!capable(CAP_NET_ADMIN))
+			return -EPERM;
+
+		if (copy_from_user(&new_line, line, size))
+			return -EFAULT;
+
+		if (new_line.clock_type != CLOCK_EXT &&
+		    new_line.clock_type != CLOCK_TXFROMRX &&
+		    new_line.clock_type != CLOCK_INT &&
+		    new_line.clock_type != CLOCK_TXINT)
+		return -EINVAL;	/* No such clock setting */
 
-	case HDLCSLINE:
-		result = sca_set_loopback(port, value);
-	case HDLCGLINE:
-		value = port->line;
-		break;
+		if (new_line.loopback != 0 && new_line.loopback != 1)
+			return -EINVAL;
 
-#ifdef DEBUG_RINGS
-	case HDLCRUN:
-		sca_dump_rings(hdlc);
+		memcpy(&port->settings, &new_line, size); /* Update settings */
+		n2_set_iface(port);
 		return 0;
-#endif /* DEBUG_RINGS */
 
 	default:
-		return -EINVAL;
+		return hdlc_ioctl(dev, ifr, cmd);
 	}
-
-	ifr->ifr_ifru.ifru_ivalue = value;
-	return result;
 }
 
 
@@ -465,6 +478,7 @@
 	sca_init(card, 0);
 	for (cnt = 0; cnt < 2; cnt++) {
 		port_t *port = &card->ports[cnt];
+		struct net_device *dev = hdlc_to_dev(&port->hdlc);
 
 		if ((cnt == 0 && !valid0) || (cnt == 1 && !valid1))
 			continue;
@@ -476,14 +490,16 @@
 			port->log_node = 1;
 
 		spin_lock_init(&port->lock);
-		hdlc_to_dev(&port->hdlc)->irq = irq;
-		hdlc_to_dev(&port->hdlc)->mem_start = winbase;
-		hdlc_to_dev(&port->hdlc)->mem_end = winbase + USE_WINDOWSIZE-1;
-		hdlc_to_dev(&port->hdlc)->tx_queue_len = 50;
-		port->hdlc.ioctl = n2_ioctl;
-		port->hdlc.open = n2_open;
-		port->hdlc.close = n2_close;
+		dev->irq = irq;
+		dev->mem_start = winbase;
+		dev->mem_end = winbase + USE_WINDOWSIZE-1;
+		dev->tx_queue_len = 50;
+		dev->do_ioctl = n2_ioctl;
+		dev->open = n2_open;
+		dev->stop = n2_close;
+		port->hdlc.attach = sca_attach;
 		port->hdlc.xmit = sca_xmit;
+		port->settings.clock_type = CLOCK_EXT;
 
 		if (register_hdlc_device(&port->hdlc)) {
 			printk(KERN_WARNING "n2: unable to register hdlc "
@@ -515,7 +531,7 @@
 		return -ENOSYS;	/* no parameters specified, abort */
 	}
 
-	printk(KERN_INFO "%s\n", version);
+	printk(KERN_INFO "%s (SCA-%s)\n", version, sca_version);
 
 	do {
 		unsigned long io, irq, ram;
diff -urN linux-2.4.21-pre2/drivers/pci/quirks.c linux-2.4.21-pre3/drivers/pci/quirks.c
--- linux-2.4.21-pre2/drivers/pci/quirks.c	Mon Jan  6 16:32:28 2003
+++ linux-2.4.21-pre3/drivers/pci/quirks.c	Mon Jan  6 16:32:32 2003
@@ -523,6 +523,56 @@
 }
 
 /*
+ * As per PCI spec, ignore base address registers 0-3 of the IDE controllers
+ * running in Compatible mode (bits 0 and 2 in the ProgIf for primary and
+ * secondary channels respectively). If the device reports Compatible mode
+ * but does use BAR0-3 for address decoding, we assume that firmware has
+ * programmed these BARs with standard values (0x1f0,0x3f4 and 0x170,0x374).
+ * Exceptions (if they exist) must be handled in chip/architecture specific
+ * fixups.
+ *
+ * Note: for non x86 people. You may need an arch specific quirk to handle
+ * moving IDE devices to native mode as well. Some plug in card devices power
+ * up in compatible mode and assume the BIOS will adjust them.
+ *
+ * Q: should we load the 0x1f0,0x3f4 into the registers or zap them as
+ * we do now ? We don't want is pci_enable_device to come along
+ * and assign new resources. Both approaches work for that.
+ */ 
+
+static void __devinit quirk_ide_bases(struct pci_dev *dev)
+{
+       struct resource *res;
+       int first_bar = 2, last_bar = 0;
+
+       if ((dev->class >> 8) != PCI_CLASS_STORAGE_IDE)
+               return;
+
+       res = &dev->resource[0];
+
+       /* primary channel: ProgIf bit 0, BAR0, BAR1 */
+       if (!(dev->class & 1) && (res[0].flags || res[1].flags)) { 
+               res[0].start = res[0].end = res[0].flags = 0;
+               res[1].start = res[1].end = res[1].flags = 0;
+               first_bar = 0;
+               last_bar = 1;
+       }
+
+       /* secondary channel: ProgIf bit 2, BAR2, BAR3 */
+       if (!(dev->class & 4) && (res[2].flags || res[3].flags)) { 
+               res[2].start = res[2].end = res[2].flags = 0;
+               res[3].start = res[3].end = res[3].flags = 0;
+               last_bar = 3;
+       }
+
+       if (!last_bar)
+               return;
+
+       printk(KERN_INFO "PCI: Ignoring BAR%d-%d of IDE controller %s\n",
+              first_bar, last_bar, dev->slot_name);
+}
+
+/*
  *  The main table of quirks.
  */
 
@@ -565,6 +615,7 @@
 	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_AL,	PCI_DEVICE_ID_AL_M7101,		quirk_ali7101_acpi },
  	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_82371SB_2,	quirk_piix3_usb },
 	{ PCI_FIXUP_HEADER,	PCI_VENDOR_ID_INTEL,	PCI_DEVICE_ID_INTEL_82371AB_2,	quirk_piix3_usb },
+	{ PCI_FIXUP_HEADER,     PCI_ANY_ID,             PCI_ANY_ID,                     quirk_ide_bases },
 	{ PCI_FIXUP_FINAL,	PCI_ANY_ID,		PCI_ANY_ID,			quirk_cardbus_legacy },
 
 #ifdef CONFIG_X86_IO_APIC 
diff -urN linux-2.4.21-pre2/drivers/scsi/hosts.c linux-2.4.21-pre3/drivers/scsi/hosts.c
--- linux-2.4.21-pre2/drivers/scsi/hosts.c	Thu Nov 28 15:53:14 2002
+++ linux-2.4.21-pre3/drivers/scsi/hosts.c	Mon Jan  6 16:32:32 2003
@@ -81,8 +81,8 @@
 struct Scsi_Host * scsi_hostlist;
 struct Scsi_Device_Template * scsi_devicelist;
 
-int max_scsi_hosts;
-int next_scsi_host;
+int max_scsi_hosts;	/* host_no for next new host */
+int next_scsi_host;	/* count of registered scsi hosts */
 
 void
 scsi_unregister(struct Scsi_Host * sh){
@@ -107,21 +107,8 @@
     if (shn) shn->host_registered = 0;
     /* else {} : This should not happen, we should panic here... */
     
-    /* If we are removing the last host registered, it is safe to reuse
-     * its host number (this avoids "holes" at boot time) (DB) 
-     * It is also safe to reuse those of numbers directly below which have
-     * been released earlier (to avoid some holes in numbering).
-     */
-    if(sh->host_no == max_scsi_hosts - 1) {
-	while(--max_scsi_hosts >= next_scsi_host) {
-	    shpnt = scsi_hostlist;
-	    while(shpnt && shpnt->host_no != max_scsi_hosts - 1)
-		shpnt = shpnt->next;
-	    if(shpnt)
-		break;
-	}
-    }
     next_scsi_host--;
+
     kfree((char *) sh);
 }
 
diff -urN linux-2.4.21-pre2/drivers/scsi/ide-scsi.c linux-2.4.21-pre3/drivers/scsi/ide-scsi.c
--- linux-2.4.21-pre2/drivers/scsi/ide-scsi.c	Mon Jan  6 16:32:29 2003
+++ linux-2.4.21-pre3/drivers/scsi/ide-scsi.c	Mon Jan  6 16:32:32 2003
@@ -900,7 +900,7 @@
 	for (id = 0; id < MAX_HWIFS * MAX_DRIVES; id++) {
 		drive = idescsi_drives[id];
 		if (drive)
-			DRIVER(drive)->busy--;
+			DRIVER(drive)->busy = 0;
 	}
 	return 0;
 }
diff -urN linux-2.4.21-pre2/drivers/sound/cmpci.c linux-2.4.21-pre3/drivers/sound/cmpci.c
--- linux-2.4.21-pre2/drivers/sound/cmpci.c	Fri Aug  2 17:39:44 2002
+++ linux-2.4.21-pre3/drivers/sound/cmpci.c	Mon Jan  6 16:32:32 2003
@@ -82,6 +82,7 @@
  *	- speaker mixer support 
  *	Mon Aug 13 2001
  *	- optimizations and cleanups
+ *    03/01/2003 - open_mode fixes from Georg Acher <acher@in.tum.de>
  *
  */
 /*****************************************************************************/
@@ -2266,7 +2267,7 @@
 		stop_adc(s);
 		dealloc_dmabuf(&s->dma_adc);
 	}
-	s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE);
+	s->open_mode &= ~(file->f_mode & (FMODE_READ|FMODE_WRITE));
 	up(&s->open_sem);
 	wake_up(&s->open_wait);
 	unlock_kernel();
@@ -2535,7 +2536,7 @@
 		set_current_state(TASK_RUNNING);
 	}
 	down(&s->open_sem);
-	s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE);
+	s->open_mode &= ~((file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE));
 	spin_lock_irqsave(&s->lock, flags);
 	if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) {
 		del_timer(&s->midi.timer);		
diff -urN linux-2.4.21-pre2/drivers/sound/es1370.c linux-2.4.21-pre3/drivers/sound/es1370.c
--- linux-2.4.21-pre2/drivers/sound/es1370.c	Thu Nov 28 15:53:14 2002
+++ linux-2.4.21-pre3/drivers/sound/es1370.c	Mon Jan  6 16:32:32 2003
@@ -3,7 +3,7 @@
 /*
  *      es1370.c  --  Ensoniq ES1370/Asahi Kasei AK4531 audio driver.
  *
- *      Copyright (C) 1998-2001  Thomas Sailer (t.sailer@alumni.ethz.ch)
+ *      Copyright (C) 1998-2001, 2003  Thomas Sailer (t.sailer@alumni.ethz.ch)
  *
  *      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
@@ -122,6 +122,7 @@
  *    07.01.2001   0.36  Timeout change in wrcodec as requested by Frank Klemm <pfk@fuchs.offl.uni-jena.de>
  *    31.01.2001   0.37  Register/Unregister gameport
  *                       Fix SETTRIGGER non OSS API conformity
+ *    03.01.2003   0.38  open_mode fixes from Georg Acher <acher@in.tum.de>
  *
  * some important things missing in Ensoniq documentation:
  *
@@ -1809,7 +1810,7 @@
 		stop_adc(s);
 		dealloc_dmabuf(s, &s->dma_adc);
 	}
-	s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE);
+	s->open_mode &= ~(file->f_mode & (FMODE_READ|FMODE_WRITE));
 	wake_up(&s->open_wait);
 	up(&s->open_sem);
 	unlock_kernel();
@@ -2494,7 +2495,7 @@
 		set_current_state(TASK_RUNNING);
 	}
 	down(&s->open_sem);
-	s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE);
+	s->open_mode &= ~((file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE));
 	spin_lock_irqsave(&s->lock, flags);
 	if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) {
 		s->ctrl &= ~CTRL_UART_EN;
@@ -2745,7 +2746,7 @@
 {
 	if (!pci_present())   /* No PCI bus in this machine! */
 		return -ENODEV;
-	printk(KERN_INFO "es1370: version v0.37 time " __TIME__ " " __DATE__ "\n");
+	printk(KERN_INFO "es1370: version v0.38 time " __TIME__ " " __DATE__ "\n");
 	return pci_module_init(&es1370_driver);
 }
 
diff -urN linux-2.4.21-pre2/drivers/sound/es1371.c linux-2.4.21-pre3/drivers/sound/es1371.c
--- linux-2.4.21-pre2/drivers/sound/es1371.c	Thu Nov 28 15:53:14 2002
+++ linux-2.4.21-pre3/drivers/sound/es1371.c	Mon Jan  6 16:32:32 2003
@@ -3,7 +3,7 @@
 /*
  *      es1371.c  --  Creative Ensoniq ES1371.
  *
- *      Copyright (C) 1998-2001  Thomas Sailer (t.sailer@alumni.ethz.ch)
+ *      Copyright (C) 1998-2001, 2003  Thomas Sailer (t.sailer@alumni.ethz.ch)
  *
  *      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
@@ -110,6 +110,7 @@
  *    31.01.2001   0.30  Register/Unregister gameport
  *                       Fix SETTRIGGER non OSS API conformity
  *    14.07.2001   0.31  Add list of laptops needing amplifier control
+ *    03.01.2003   0.32  open_mode fixes from Georg Acher <acher@in.tum.de>
  */
 
 /*****************************************************************************/
@@ -1995,7 +1996,7 @@
 		stop_adc(s);
 		dealloc_dmabuf(s, &s->dma_adc);
 	}
-	s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE);
+	s->open_mode &= ~(file->f_mode & (FMODE_READ|FMODE_WRITE));
 	up(&s->open_sem);
 	wake_up(&s->open_wait);
 	unlock_kernel();
@@ -2670,7 +2671,7 @@
 		set_current_state(TASK_RUNNING);
 	}
 	down(&s->open_sem);
-	s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE);
+	s->open_mode &= ~((file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE));
 	spin_lock_irqsave(&s->lock, flags);
 	if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) {
 		s->ctrl &= ~CTRL_UART_EN;
@@ -3046,7 +3047,7 @@
 {
 	if (!pci_present())   /* No PCI bus in this machine! */
 		return -ENODEV;
-	printk(KERN_INFO PFX "version v0.30 time " __TIME__ " " __DATE__ "\n");
+	printk(KERN_INFO PFX "version v0.32 time " __TIME__ " " __DATE__ "\n");
 	return pci_module_init(&es1371_driver);
 }
 
diff -urN linux-2.4.21-pre2/drivers/sound/esssolo1.c linux-2.4.21-pre3/drivers/sound/esssolo1.c
--- linux-2.4.21-pre2/drivers/sound/esssolo1.c	Fri Aug  2 17:39:44 2002
+++ linux-2.4.21-pre3/drivers/sound/esssolo1.c	Mon Jan  6 16:32:32 2003
@@ -3,7 +3,7 @@
 /*
  *      esssolo1.c  --  ESS Technology Solo1 (ES1946) audio driver.
  *
- *      Copyright (C) 1998-2001  Thomas Sailer (t.sailer@alumni.ethz.ch)
+ *      Copyright (C) 1998-2001, 2003  Thomas Sailer (t.sailer@alumni.ethz.ch)
  *
  *      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
@@ -82,6 +82,7 @@
  *    22.05.2001   0.19  more cleanups, changed PM to PCI 2.4 style, got rid
  *                       of global list of devices, using pci device data.
  *                       Marcus Meissner <mm@caldera.de>
+ *    03.01.2003   0.20  open_mode fixes from Georg Acher <acher@in.tum.de>
  */
 
 /*****************************************************************************/
@@ -1977,7 +1978,7 @@
 		set_current_state(TASK_RUNNING);
 	}
 	down(&s->open_sem);
-	s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE);
+	s->open_mode &= ~((file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE));
 	spin_lock_irqsave(&s->lock, flags);
 	if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) {
 		outb(0x30, s->iobase + 7); /* enable A1, A2 irq's */
@@ -2452,7 +2453,7 @@
 {
 	if (!pci_present())   /* No PCI bus in this machine! */
 		return -ENODEV;
-	printk(KERN_INFO "solo1: version v0.19 time " __TIME__ " " __DATE__ "\n");
+	printk(KERN_INFO "solo1: version v0.20 time " __TIME__ " " __DATE__ "\n");
 	if (!pci_register_driver(&solo1_driver)) {
 		pci_unregister_driver(&solo1_driver);
                 return -ENODEV;
diff -urN linux-2.4.21-pre2/drivers/sound/i810_audio.c linux-2.4.21-pre3/drivers/sound/i810_audio.c
--- linux-2.4.21-pre2/drivers/sound/i810_audio.c	Mon Jan  6 16:32:29 2003
+++ linux-2.4.21-pre3/drivers/sound/i810_audio.c	Mon Jan  6 16:32:32 2003
@@ -640,6 +640,11 @@
 {
 	int	aud_reg;
 	struct ac97_codec *codec = state->card->ac97_codec[0];
+	
+	/* No codec, no setup */
+	
+	if(codec == NULL)
+		return;
 
 	aud_reg = i810_ac97_get(codec, AC97_EXTENDED_STATUS);
 	aud_reg |= AC97_EA_PRI | AC97_EA_PRJ | AC97_EA_PRK;
diff -urN linux-2.4.21-pre2/drivers/sound/sonicvibes.c linux-2.4.21-pre3/drivers/sound/sonicvibes.c
--- linux-2.4.21-pre2/drivers/sound/sonicvibes.c	Fri Aug  2 17:39:44 2002
+++ linux-2.4.21-pre3/drivers/sound/sonicvibes.c	Mon Jan  6 16:32:32 2003
@@ -3,7 +3,7 @@
 /*
  *      sonicvibes.c  --  S3 Sonic Vibes audio driver.
  *
- *      Copyright (C) 1998-2001  Thomas Sailer (t.sailer@alumni.ethz.ch)
+ *      Copyright (C) 1998-2001, 2003  Thomas Sailer (t.sailer@alumni.ethz.ch)
  *
  *      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
@@ -94,6 +94,7 @@
  *                       Fix SETTRIGGER non OSS API conformity
  *    18.05.2001   0.30  PCI probing and error values cleaned up by Marcus
  *                       Meissner <mm@caldera.de>
+ *    03.01.2003   0.31  open_mode fixes from Georg Acher <acher@in.tum.de>
  *
  */
 
@@ -1964,7 +1965,7 @@
 		stop_adc(s);
 		dealloc_dmabuf(s, &s->dma_adc);
 	}
-	s->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE);
+	s->open_mode &= ~(file->f_mode & (FMODE_READ|FMODE_WRITE));
 	wake_up(&s->open_wait);
 	up(&s->open_sem);
 	unlock_kernel();
@@ -2236,7 +2237,7 @@
 		set_current_state(TASK_RUNNING);
 	}
 	down(&s->open_sem);
-	s->open_mode &= (~(file->f_mode << FMODE_MIDI_SHIFT)) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE);
+	s->open_mode &= ~((file->f_mode << FMODE_MIDI_SHIFT) & (FMODE_MIDI_READ|FMODE_MIDI_WRITE));
 	spin_lock_irqsave(&s->lock, flags);
 	if (!(s->open_mode & (FMODE_MIDI_READ | FMODE_MIDI_WRITE))) {
 		outb(inb(s->ioenh + SV_CODEC_INTMASK) & ~SV_CINTMASK_MIDI, s->ioenh + SV_CODEC_INTMASK);
@@ -2716,7 +2717,7 @@
 {
 	if (!pci_present())   /* No PCI bus in this machine! */
 		return -ENODEV;
-	printk(KERN_INFO "sv: version v0.30 time " __TIME__ " " __DATE__ "\n");
+	printk(KERN_INFO "sv: version v0.31 time " __TIME__ " " __DATE__ "\n");
 #if 0
 	if (!(wavetable_mem = __get_free_pages(GFP_KERNEL, 20-PAGE_SHIFT)))
 		printk(KERN_INFO "sv: cannot allocate 1MB of contiguous nonpageable memory for wavetable data\n");
diff -urN linux-2.4.21-pre2/drivers/sound/trident.c linux-2.4.21-pre3/drivers/sound/trident.c
--- linux-2.4.21-pre2/drivers/sound/trident.c	Mon Jan  6 16:32:29 2003
+++ linux-2.4.21-pre3/drivers/sound/trident.c	Mon Jan  6 16:32:32 2003
@@ -3368,15 +3368,17 @@
         pci_dev = pci_find_device(PCI_VENDOR_ID_AL,PCI_DEVICE_ID_AL_M1533, pci_dev);
         if (pci_dev == NULL)
                 return -1;
-	temp = 0x80;
-	pci_write_config_byte(pci_dev, 0x59, ~temp);
+	pci_read_config_byte(pci_dev, 0x59, &temp);
+	temp &= ~0x80;
+	pci_write_config_byte(pci_dev, 0x59, temp);
 	
 	pci_dev = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101, pci_dev);
 	if (pci_dev == NULL)
                 return -1;
 
-	temp = 0x20;
-	pci_write_config_byte(pci_dev, 0xB8, ~temp);
+	pci_read_config_byte(pci_dev, 0xB8, &temp);
+	temp &= ~0x20;
+	pci_write_config_byte(pci_dev, 0xB8, temp);
 
 	return 0;
 }
@@ -3390,13 +3392,15 @@
 	pci_dev = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, pci_dev);
 	if (pci_dev == NULL)
                 return -1;
-	temp = 0x80;
+	pci_read_config_byte(pci_dev, 0x59, &temp);
+	temp |= 0x80;
 	pci_write_config_byte(pci_dev, 0x59, temp);
 	
 	pci_dev = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101, pci_dev);
  	if (pci_dev == NULL)
                 return -1;
-	temp = 0x20;
+	pci_read_config_byte(pci_dev, (int)0xB8, &temp);
+	temp |= 0x20;
 	pci_write_config_byte(pci_dev, (int)0xB8,(u8) temp);
 	if (chan_nums == 6) {
 		dwValue = inl(TRID_REG(card, ALI_SCTRL)) | 0x000f0000;
diff -urN linux-2.4.21-pre2/drivers/sound/via82cxxx_audio.c linux-2.4.21-pre3/drivers/sound/via82cxxx_audio.c
--- linux-2.4.21-pre2/drivers/sound/via82cxxx_audio.c	Fri Aug  2 17:39:44 2002
+++ linux-2.4.21-pre3/drivers/sound/via82cxxx_audio.c	Mon Jan  6 16:32:32 2003
@@ -2,17 +2,20 @@
  * Support for VIA 82Cxxx Audio Codecs
  * Copyright 1999,2000 Jeff Garzik
  *
+ * Updated to support the VIA 8233/8235 audio subsystem
+ * Alan Cox <alan@redhat.com> (C) Copyright 2002 Red Hat Inc
+ *
  * Distributed under the GNU GENERAL PUBLIC LICENSE (GPL) Version 2.
  * See the "COPYING" file distributed with this software for more info.
+ * NO WARRANTY
  *
  * For a list of known bugs (errata) and documentation,
  * see via-audio.pdf in linux/Documentation/DocBook.
  * If this documentation does not exist, run "make pdfdocs".
- *
  */
 
 
-#define VIA_VERSION	"1.9.1"
+#define VIA_VERSION	"1.9.1-ac2"
 
 
 #include <linux/config.h>
@@ -117,7 +120,10 @@
 #define VIA_PCM_STATUS			0x00
 #define VIA_PCM_CONTROL			0x01
 #define VIA_PCM_TYPE			0x02
+#define VIA_PCM_LEFTVOL			0x02
+#define VIA_PCM_RIGHTVOL		0x03
 #define VIA_PCM_TABLE_ADDR		0x04
+#define VIA_PCM_STOPRATE		0x08	/* 8233+ */
 #define VIA_PCM_BLOCK_COUNT		0x0C
 
 /* XXX unused DMA channel for FM PCM data */
@@ -126,6 +132,12 @@
 #define VIA_BASE0_FM_OUT_CHAN_CTRL	0x21
 #define VIA_BASE0_FM_OUT_CHAN_TYPE	0x22
 
+/* Six channel audio output on 8233 */
+#define VIA_BASE0_MULTI_OUT_CHAN		0x40
+#define VIA_BASE0_MULTI_OUT_CHAN_STATUS		0x40
+#define VIA_BASE0_MULTI_OUT_CHAN_CTRL		0x41
+#define VIA_BASE0_MULTI_OUT_CHAN_TYPE		0x42
+
 #define VIA_BASE0_AC97_CTRL		0x80
 #define VIA_BASE0_SGD_STATUS_SHADOW	0x84
 #define VIA_BASE0_GPI_INT_ENABLE	0x8C
@@ -251,11 +263,15 @@
 	unsigned is_record : 1;
 	unsigned is_mapped : 1;
 	unsigned is_enabled : 1;
+	unsigned is_multi: 1;	/* 8233 6 channel */
 	u8 pcm_fmt;		/* VIA_PCM_FMT_xxx */
+	u8 channels;		/* Channel count */
 
 	unsigned rate;		/* sample rate */
 	unsigned int frag_size;
 	unsigned int frag_number;
+	
+	unsigned char intmask;
 
 	volatile struct via_sgd_table *sgtable;
 	dma_addr_t sgt_handle;
@@ -281,6 +297,9 @@
 	int dev_dsp;		/* /dev/dsp index from register_sound_dsp() */
 
 	unsigned rev_h : 1;
+	unsigned legacy: 1;	/* Has legacy ports */
+	unsigned intmask: 1;	/* Needs int bits */
+	unsigned sixchannel: 1;	/* 8233/35 with 6 channel support */
 
 	int locked_rate : 1;
 
@@ -353,7 +372,9 @@
 
 static struct pci_device_id via_pci_tbl[] __initdata = {
 	{ PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_5,
-	  PCI_ANY_ID, PCI_ANY_ID, },
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{ PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8233_5,
+	  PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1},
 	{ 0, }
 };
 MODULE_DEVICE_TABLE(pci,via_pci_tbl);
@@ -427,7 +448,13 @@
 
 static inline void sg_begin (struct via_channel *chan)
 {
-	outb (VIA_SGD_START, chan->iobase + VIA_PCM_CONTROL);
+	DPRINTK("Start with intmask %d\n", chan->intmask);
+	DPRINTK("About to start from %d to %d\n", 
+		inl(chan->iobase + VIA_PCM_BLOCK_COUNT),
+		inb(chan->iobase + VIA_PCM_STOPRATE + 3));
+	outb (VIA_SGD_START|chan->intmask, chan->iobase + VIA_PCM_CONTROL);
+	DPRINTK("Status is now %02X\n", inb(chan->iobase + VIA_PCM_STATUS));
+	DPRINTK("Control is now %02X\n", inb(chan->iobase + VIA_PCM_CONTROL));
 }
 
 
@@ -443,6 +470,10 @@
 	return 0;
 }
 
+static int via_sg_offset(struct via_channel *chan)
+{
+	return inl (chan->iobase + VIA_PCM_BLOCK_COUNT) & 0x00FFFFFF;
+}
 
 /****************************************************************
  *
@@ -506,6 +537,8 @@
 	via_chan_stop (card->baseaddr + VIA_BASE0_PCM_OUT_CHAN);
 	via_chan_stop (card->baseaddr + VIA_BASE0_PCM_IN_CHAN);
 	via_chan_stop (card->baseaddr + VIA_BASE0_FM_OUT_CHAN);
+	if(card->sixchannel)
+		via_chan_stop (card->baseaddr + VIA_BASE0_MULTI_OUT_CHAN);
 
 	/*
 	 * clear any existing stops / flags (sanity check mainly)
@@ -513,6 +546,8 @@
 	via_chan_status_clear (card->baseaddr + VIA_BASE0_PCM_OUT_CHAN);
 	via_chan_status_clear (card->baseaddr + VIA_BASE0_PCM_IN_CHAN);
 	via_chan_status_clear (card->baseaddr + VIA_BASE0_FM_OUT_CHAN);
+	if(card->sixchannel)
+		via_chan_status_clear (card->baseaddr + VIA_BASE0_MULTI_OUT_CHAN);
 
 	/*
 	 * clear any enabled interrupt bits
@@ -532,6 +567,14 @@
 	if (tmp != new_tmp)
 		outb (0, card->baseaddr + VIA_BASE0_FM_OUT_CHAN_TYPE);
 
+	if(card->sixchannel)
+	{
+		tmp = inb (card->baseaddr + VIA_BASE0_MULTI_OUT_CHAN_TYPE);
+		new_tmp = tmp & ~(VIA_IRQ_ON_FLAG|VIA_IRQ_ON_EOL|VIA_RESTART_SGD_ON_EOL);
+		if (tmp != new_tmp)
+			outb (0, card->baseaddr + VIA_BASE0_MULTI_OUT_CHAN_TYPE);
+	}
+
 	udelay(10);
 
 	/*
@@ -627,9 +670,19 @@
 {
 	memset (chan, 0, sizeof (*chan));
 
+	if(card->intmask)
+		chan->intmask = 0x23;	/* Turn on the IRQ bits */
+		
 	if (chan == &card->ch_out) {
 		chan->name = "PCM-OUT";
-		chan->iobase = card->baseaddr + VIA_BASE0_PCM_OUT_CHAN;
+		if(card->sixchannel)
+		{
+			chan->iobase = card->baseaddr + VIA_BASE0_MULTI_OUT_CHAN;
+			chan->is_multi = 1;
+			DPRINTK("Using multichannel for pcm out\n");
+		}
+		else
+			chan->iobase = card->baseaddr + VIA_BASE0_PCM_OUT_CHAN;
 	} else if (chan == &card->ch_in) {
 		chan->name = "PCM-IN";
 		chan->iobase = card->baseaddr + VIA_BASE0_PCM_IN_CHAN;
@@ -708,6 +761,11 @@
 
 	DPRINTK ("ENTER\n");
 
+
+	chan->intmask = 0;
+	if(card->intmask)
+		chan->intmask = 0x23;	/* Turn on the IRQ bits */
+		
 	if (chan->sgtable != NULL) {
 		DPRINTK ("EXIT\n");
 		return 0;
@@ -778,6 +836,16 @@
 	outl (chan->sgt_handle, chan->iobase + VIA_PCM_TABLE_ADDR);
 	udelay (20);
 	via_ac97_wait_idle (card);
+	/* load no rate adaption, stereo 16bit, set up ring slots */
+	if(card->sixchannel)
+	{
+		if(!chan->is_multi)
+		{
+			outl (0xFFFFF | (0x3 << 20) | (chan->frag_number << 24), chan->iobase + VIA_PCM_STOPRATE);
+			udelay (20);
+			via_ac97_wait_idle (card);
+		}
+	}
 
 	DPRINTK ("inl (0x%lX) = %x\n",
 		chan->iobase + VIA_PCM_TABLE_ADDR,
@@ -881,8 +949,11 @@
 	assert (chan != NULL);
 
 	if (reset)
+	{
 		/* reset to 8-bit mono mode */
 		chan->pcm_fmt = 0;
+		chan->channels = 1;
+	}
 
 	/* enable interrupts on FLAG and EOL */
 	chan->pcm_fmt |= VIA_CHAN_TYPE_MASK;
@@ -893,8 +964,83 @@
 	/* set interrupt select bits where applicable (PCM in & out channels) */
 	if (!chan->is_record)
 		chan->pcm_fmt |= VIA_CHAN_TYPE_INT_SELECT;
+	
+	DPRINTK("SET FMT - %02x %02x\n", chan->intmask , chan->is_multi);
+	
+	if(chan->intmask)
+	{
+		u32 m;
+
+		/*
+		 *	Channel 0x4 is up to 6 x 16bit and has to be
+		 *	programmed differently 
+		 */
+		 		
+		if(chan->is_multi)
+		{
+			u8 c = 0;
+			
+			/*
+			 *	Load the type bit for num channels
+			 *	and 8/16bit
+			 */
+			 
+			if(chan->pcm_fmt & VIA_PCM_FMT_16BIT)
+				c = 1 << 7;
+			if(chan->pcm_fmt & VIA_PCM_FMT_STEREO)
+				c |= (2<<4);
+			else
+				c |= (1<<4);
+				
+			outb(c, chan->iobase + VIA_PCM_TYPE);
+			
+			/*
+			 *	Set the channel steering
+			 *	Mono
+			 *		Channel 0 to slot 3
+			 *		Channel 0 to slot 4
+			 *	Stereo
+			 *		Channel 0 to slot 3
+			 *		Channel 1 to slot 4
+			 */
+			 
+			switch(chan->channels)
+			{
+				case 1:
+					outl(0xFF000000 | (1<<0) | (1<<4) , chan->iobase + VIA_PCM_STOPRATE);
+					break;
+				case 2:
+					outl(0xFF000000 | (1<<0) | (2<<4) , chan->iobase + VIA_PCM_STOPRATE);
+					break;
+				case 4:
+					outl(0xFF000000 | (1<<0) | (2<<4) | (3<<8) | (4<<12), chan->iobase + VIA_PCM_STOPRATE);
+					break;
+				case 6:
+					outl(0xFF000000 | (1<<0) | (2<<4) | (5<<8) | (6<<12) | (3<<16) | (4<<20), chan->iobase + VIA_PCM_STOPRATE);
+					break;
+			}				
+		}
+		else
+		{
+			/*
+			 *	New style, turn off channel volume
+			 *	control, set bits in the right register
+			 */	
+			outb(0x0, chan->iobase + VIA_PCM_LEFTVOL);
+			outb(0x0, chan->iobase + VIA_PCM_RIGHTVOL);
+
+			m = inl(chan->iobase + VIA_PCM_STOPRATE);
+			m &= ~(3<<20);
+			if(chan->pcm_fmt & VIA_PCM_FMT_STEREO)
+				m |= (1 << 20);
+			if(chan->pcm_fmt & VIA_PCM_FMT_16BIT)
+				m |= (1 << 21);
+			outl(m, chan->iobase + VIA_PCM_STOPRATE);
+		}		
+	}
+	else
+		outb (chan->pcm_fmt, chan->iobase + VIA_PCM_TYPE);
 
-	outb (chan->pcm_fmt, chan->iobase + VIA_PCM_TYPE);
 
 	DPRINTK ("EXIT, pcm_fmt = 0x%02X, reg = 0x%02X\n",
 		 chan->pcm_fmt,
@@ -1035,15 +1181,25 @@
 	/* mono */
 	case 1:
 		chan->pcm_fmt &= ~VIA_PCM_FMT_STEREO;
+		chan->channels = 1;
 		via_chan_pcm_fmt (chan, 0);
 		break;
 
 	/* stereo */
 	case 2:
 		chan->pcm_fmt |= VIA_PCM_FMT_STEREO;
+		chan->channels = 2;
 		via_chan_pcm_fmt (chan, 0);
 		break;
 
+	case 4:
+	case 6:
+		if(chan->is_multi)
+		{
+			chan->pcm_fmt |= VIA_PCM_FMT_STEREO;
+			chan->channels = val;
+			break;
+		}
 	/* unknown */
 	default:
 		printk (KERN_WARNING PFX "unknown number of channels\n");
@@ -1077,9 +1233,8 @@
 
 		DPRINTK ("\n");
 
-		chan->frag_size = (VIA_DEFAULT_FRAG_TIME * chan->rate *
-				   ((chan->pcm_fmt & VIA_PCM_FMT_STEREO) ? 2 : 1) *
-				   ((chan->pcm_fmt & VIA_PCM_FMT_16BIT) ? 2 : 1)) / 1000 - 1;
+		chan->frag_size = (VIA_DEFAULT_FRAG_TIME * chan->rate * chan->channels
+				   * ((chan->pcm_fmt & VIA_PCM_FMT_16BIT) ? 2 : 1)) / 1000 - 1;
 
 		shift = 0;
 		while (chan->frag_size) {
@@ -1494,16 +1649,19 @@
         	udelay (100);
 	}
 
+	if(card->legacy)
+	{
 #if 0 /* this breaks on K7M */
-	/* disable legacy stuff */
-	pci_write_config_byte (pdev, 0x42, 0x00);
-	udelay(10);
+		/* disable legacy stuff */
+		pci_write_config_byte (pdev, 0x42, 0x00);
+		udelay(10);
 #endif
 
-	/* route FM trap to IRQ, disable FM trap */
-	pci_write_config_byte (pdev, 0x48, 0x05);
-	udelay(10);
-
+		/* route FM trap to IRQ, disable FM trap */
+		pci_write_config_byte (pdev, 0x48, 0x05);
+		udelay(10);
+	}
+	
 	/* disable all codec GPI interrupts */
 	outl (0, pci_resource_start (pdev, 0) + 0x8C);
 
@@ -1553,7 +1711,9 @@
 		printk (KERN_ERR PFX "unable to reset AC97 codec, aborting\n");
 		goto err_out;
 	}
-
+	
+	mdelay(10);
+	
 	if (ac97_probe_codec (&card->ac97) == 0) {
 		printk (KERN_ERR PFX "unable to probe AC97 codec, aborting\n");
 		rc = -EIO;
@@ -1730,6 +1890,22 @@
 	spin_unlock (&card->lock);
 }
 
+static void via_new_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct via_info *card = dev_id;
+
+	/*
+	 * goes away completely on UP
+	 */
+	spin_lock (&card->lock);
+
+	via_intr_channel (&card->ch_out);
+	via_intr_channel (&card->ch_in);
+	via_intr_channel (&card->ch_fm);
+
+	spin_unlock (&card->lock);
+}
+
 
 /**
  *	via_interrupt_init - Initialize interrupt handling
@@ -1757,18 +1933,32 @@
 		return -EIO;
 	}
 
-	/* make sure FM irq is not routed to us */
-	pci_read_config_byte (card->pdev, VIA_FM_NMI_CTRL, &tmp8);
-	if ((tmp8 & VIA_CR48_FM_TRAP_TO_NMI) == 0) {
-		tmp8 |= VIA_CR48_FM_TRAP_TO_NMI;
-		pci_write_config_byte (card->pdev, VIA_FM_NMI_CTRL, tmp8);
+	/* VIA requires this is done */
+	pci_write_config_byte(card->pdev, PCI_INTERRUPT_LINE, card->pdev->irq);
+	
+	if(card->legacy)
+	{
+		/* make sure FM irq is not routed to us */
+		pci_read_config_byte (card->pdev, VIA_FM_NMI_CTRL, &tmp8);
+		if ((tmp8 & VIA_CR48_FM_TRAP_TO_NMI) == 0) {
+			tmp8 |= VIA_CR48_FM_TRAP_TO_NMI;
+			pci_write_config_byte (card->pdev, VIA_FM_NMI_CTRL, tmp8);
+		}
+		if (request_irq (card->pdev->irq, via_interrupt, SA_SHIRQ, VIA_MODULE_NAME, card)) {
+			printk (KERN_ERR PFX "unable to obtain IRQ %d, aborting\n",
+				card->pdev->irq);
+			DPRINTK ("EXIT, returning -EBUSY\n");
+			return -EBUSY;
+		}
 	}
-
-	if (request_irq (card->pdev->irq, via_interrupt, SA_SHIRQ, VIA_MODULE_NAME, card)) {
-		printk (KERN_ERR PFX "unable to obtain IRQ %d, aborting\n",
-			card->pdev->irq);
-		DPRINTK ("EXIT, returning -EBUSY\n");
-		return -EBUSY;
+	else 
+	{
+		if (request_irq (card->pdev->irq, via_new_interrupt, SA_SHIRQ, VIA_MODULE_NAME, card)) {
+			printk (KERN_ERR PFX "unable to obtain IRQ %d, aborting\n",
+				card->pdev->irq);
+			DPRINTK ("EXIT, returning -EBUSY\n");
+			return -EBUSY;
+		}
 	}
 
 	DPRINTK ("EXIT, returning 0\n");
@@ -1803,11 +1993,14 @@
 
 	assert (card != NULL);
 
-	/* turn off legacy features, if not already */
-	pci_read_config_byte (card->pdev, VIA_FUNC_ENABLE, &tmp8);
-	if (tmp8 & (VIA_CR42_SB_ENABLE |  VIA_CR42_FM_ENABLE)) {
-		tmp8 &= ~(VIA_CR42_SB_ENABLE | VIA_CR42_FM_ENABLE);
-		pci_write_config_byte (card->pdev, VIA_FUNC_ENABLE, tmp8);
+	if(card->legacy)
+	{
+		/* turn off legacy features, if not already */
+		pci_read_config_byte (card->pdev, VIA_FUNC_ENABLE, &tmp8);
+		if (tmp8 & (VIA_CR42_SB_ENABLE |  VIA_CR42_FM_ENABLE)) {
+			tmp8 &= ~(VIA_CR42_SB_ENABLE | VIA_CR42_FM_ENABLE);
+			pci_write_config_byte (card->pdev, VIA_FUNC_ENABLE, tmp8);
+		}
 	}
 
 	via_stop_everything (card);
@@ -2120,7 +2313,6 @@
 		 file, buffer, count, ppos ? ((unsigned long)*ppos) : 0);
 
 	assert (file != NULL);
-	assert (buffer != NULL);
 	card = file->private_data;
 	assert (card != NULL);
 
@@ -2278,11 +2470,12 @@
 	DPRINTK ("Flushed block %u, sw_ptr now %u, n_frags now %d\n",
 		n, chan->sw_ptr, atomic_read (&chan->n_frags));
 
-	DPRINTK ("regs==%02X %02X %02X %08X %08X %08X %08X\n",
+	DPRINTK ("regs==S=%02X C=%02X TP=%02X BP=%08X RT=%08X SG=%08X CC=%08X SS=%08X\n",
 		 inb (card->baseaddr + 0x00),
 		 inb (card->baseaddr + 0x01),
 		 inb (card->baseaddr + 0x02),
 		 inl (card->baseaddr + 0x04),
+		 inl (card->baseaddr + 0x08),
 		 inl (card->baseaddr + 0x0C),
 		 inl (card->baseaddr + 0x80),
 		 inl (card->baseaddr + 0x84));
@@ -2305,7 +2498,6 @@
 		 file, buffer, count, ppos ? ((unsigned long)*ppos) : 0);
 
 	assert (file != NULL);
-	assert (buffer != NULL);
 	card = file->private_data;
 	assert (card != NULL);
 
@@ -2558,7 +2750,7 @@
 	if (chan->is_active) {
 		unsigned long extra;
 		info.ptr = atomic_read (&chan->hw_ptr) * chan->frag_size;
-		extra = chan->frag_size - inl (chan->iobase + VIA_PCM_BLOCK_COUNT);
+		extra = chan->frag_size - via_sg_offset(chan);
 		info.ptr += extra;
 		info.bytes += extra;
 	} else {
@@ -2679,7 +2871,7 @@
                 rc = put_user (val, (int *)arg);
 		break;
 
-	/* query or set number of channels (1=mono, 2=stereo) */
+	/* query or set number of channels (1=mono, 2=stereo, 4/6 for multichannel) */
         case SNDCTL_DSP_CHANNELS:
 		if (get_user(val, (int *)arg)) {
 			rc = -EFAULT;
@@ -2700,11 +2892,10 @@
 
 			val = rc;
 		} else {
-			if ((rd && (card->ch_in.pcm_fmt & VIA_PCM_FMT_STEREO)) ||
-			    (wr && (card->ch_out.pcm_fmt & VIA_PCM_FMT_STEREO)))
-				val = 2;
+			if (rd)
+				val = card->ch_in.channels;
 			else
-				val = 1;
+				val = card->ch_out.channels;
 		}
 		DPRINTK ("CHANNELS EXIT, returning %d\n", val);
                 rc = put_user (val, (int *)arg);
@@ -2864,10 +3055,11 @@
 
 			val = chan->frag_number - atomic_read (&chan->n_frags);
 
+			assert(val >= 0);
+				
 			if (val > 0) {
 				val *= chan->frag_size;
-				val -= chan->frag_size -
-				       inl (chan->iobase + VIA_PCM_BLOCK_COUNT);
+				val -= chan->frag_size - via_sg_offset(chan);
 			}
 			val += chan->slop_len % chan->frag_size;
 		} else
@@ -3029,7 +3221,9 @@
 
 		/* why is this forced to 16-bit stereo in all drivers? */
 		chan->pcm_fmt = VIA_PCM_FMT_16BIT | VIA_PCM_FMT_STEREO;
+		chan->channels = 2;
 
+		// TO DO - use FIFO: via_capture_fifo(card, 1);
 		via_chan_pcm_fmt (chan, 0);
 		via_set_rate (&card->ac97, chan, 44100);
 	}
@@ -3044,6 +3238,7 @@
 			/* if in duplex mode make the recording and playback channels
 			   have the same settings */
 			chan->pcm_fmt = VIA_PCM_FMT_16BIT | VIA_PCM_FMT_STEREO;
+			chan->channels = 2;
 			via_chan_pcm_fmt (chan, 0);
                         via_set_rate (&card->ac97, chan, 44100);
 		} else {
@@ -3083,7 +3278,7 @@
 
 	if (file->f_mode & FMODE_WRITE) {
 		rc = via_dsp_drain_playback (card, &card->ch_out, nonblock);
-		if (rc)
+		if (rc && rc != ERESTARTSYS)	/* Nobody needs to know about ^C */
 			printk (KERN_DEBUG "via_audio: ignoring drain playback error %d\n", rc);
 
 		via_chan_free (card, &card->ch_out);
@@ -3122,11 +3317,12 @@
 	DPRINTK ("ENTER\n");
 
 	if (printed_version++ == 0)
-		printk (KERN_INFO "Via 686a audio driver " VIA_VERSION "\n");
+		printk (KERN_INFO "Via 686a/8233/8235 audio driver " VIA_VERSION "\n");
 
 	rc = pci_enable_device (pdev);
 	if (rc)
 		goto err_out;
+		
 
 	rc = pci_request_regions (pdev, "via82cxxx_audio");
 	if (rc)
@@ -3158,7 +3354,15 @@
 	 * which means it has a few extra features */
 	if (pci_resource_start (pdev, 2) > 0)
 		card->rev_h = 1;
-
+		
+	/* Overkill for now, but more flexible done right */
+	
+	card->intmask = id->driver_data;
+	card->legacy = !card->intmask;
+	card->sixchannel = id->driver_data;
+	
+	if(card->sixchannel)
+		printk(KERN_INFO PFX "Six channel audio available\n");
 	if (pdev->irq < 1) {
 		printk (KERN_ERR PFX "invalid PCI IRQ %d, aborting\n", pdev->irq);
 		rc = -ENODEV;
@@ -3171,6 +3375,8 @@
 		goto err_out_kfree;
 	}
 
+	pci_set_master(pdev);
+	
 	/*
 	 * init AC97 mixer and codec
 	 */
@@ -3214,26 +3420,29 @@
 	/* Disable by default */
 	card->midi_info.io_base = 0;
 
-	pci_read_config_byte (pdev, 0x42, &r42);
-	/* Disable MIDI interrupt */
-	pci_write_config_byte (pdev, 0x42, r42 | VIA_CR42_MIDI_IRQMASK);
-	if (r42 & VIA_CR42_MIDI_ENABLE)
+	if(card->legacy)
 	{
-		if (r42 & VIA_CR42_MIDI_PNP) /* Address selected by iobase 2 - not tested */
-			card->midi_info.io_base = pci_resource_start (pdev, 2);
-		else /* Address selected by byte 0x43 */
+		pci_read_config_byte (pdev, 0x42, &r42);
+		/* Disable MIDI interrupt */
+		pci_write_config_byte (pdev, 0x42, r42 | VIA_CR42_MIDI_IRQMASK);
+		if (r42 & VIA_CR42_MIDI_ENABLE)
 		{
-			u8 r43;
-			pci_read_config_byte (pdev, 0x43, &r43);
-			card->midi_info.io_base = 0x300 + ((r43 & 0x0c) << 2);
-		}
+			if (r42 & VIA_CR42_MIDI_PNP) /* Address selected by iobase 2 - not tested */
+				card->midi_info.io_base = pci_resource_start (pdev, 2);
+			else /* Address selected by byte 0x43 */
+			{
+				u8 r43;
+				pci_read_config_byte (pdev, 0x43, &r43);
+				card->midi_info.io_base = 0x300 + ((r43 & 0x0c) << 2);
+			}
 
-		card->midi_info.irq = -pdev->irq;
-		if (probe_uart401(& card->midi_info, THIS_MODULE))
-		{
-			card->midi_devc=midi_devs[card->midi_info.slots[4]]->devc;
-			pci_write_config_byte(pdev, 0x42, r42 & ~VIA_CR42_MIDI_IRQMASK);
-			printk("Enabled Via MIDI\n");
+			card->midi_info.irq = -pdev->irq;
+			if (probe_uart401(& card->midi_info, THIS_MODULE))
+			{
+				card->midi_devc=midi_devs[card->midi_info.slots[4]]->devc;
+				pci_write_config_byte(pdev, 0x42, r42 & ~VIA_CR42_MIDI_IRQMASK);
+				printk("Enabled Via MIDI\n");
+			}
 		}
 	}
 #endif
diff -urN linux-2.4.21-pre2/drivers/usb/acm.c linux-2.4.21-pre3/drivers/usb/acm.c
--- linux-2.4.21-pre2/drivers/usb/acm.c	Thu Nov 28 15:53:14 2002
+++ linux-2.4.21-pre3/drivers/usb/acm.c	Mon Jan  6 16:32:32 2003
@@ -649,6 +649,7 @@
  */
 
 static struct usb_device_id acm_ids[] = {
+	{ USB_DEVICE(0x22B8, 0x1005) },		/* Motorola TimePort 280 */
 	{ USB_DEVICE_INFO(USB_CLASS_COMM, 0, 0) },
 	{ }
 };
diff -urN linux-2.4.21-pre2/drivers/usb/audio.c linux-2.4.21-pre3/drivers/usb/audio.c
--- linux-2.4.21-pre2/drivers/usb/audio.c	Thu Nov 28 15:53:14 2002
+++ linux-2.4.21-pre3/drivers/usb/audio.c	Mon Jan  6 16:32:32 2003
@@ -99,6 +99,12 @@
  *              for abs. Bug report by Andrew Morton <andrewm@uow.edu.au>
  * 2001-06-16:  Bryce Nesbitt <bryce@obviously.com>
  *              Fix SNDCTL_DSP_STEREO API violation
+ * 2002-10-16:  Monty <monty@xiph.org>
+ *              Expand device support from a maximum of 8/16bit,mono/stereo to 
+ *              8/16/24/32bit,N channels.  Add AFMT_?24_?? and AFMT_?32_?? to OSS
+ *              functionality. Tested and used in production with the emagic emi 2|6 
+ *              on PPC and Intel. Also fixed a few logic 'crash and burn' corner 
+ *              cases.
  */
 
 /*
@@ -230,6 +236,11 @@
 #define DMABUFSHIFT       17  /* 128k worth of DMA buffer */
 #define NRSGBUF           (1U<<(DMABUFSHIFT-PAGE_SHIFT))
 
+#define MAXCHANNELS       32
+#define MAXWIDTH          4
+#define MAXSAMPLEWIDTH    (MAXCHANNELS*MAXWIDTH)
+#define TMPCOPYWIDTH      MAXSAMPLEWIDTH /* max (128,MAXSAMPLEWIDTH) */
+
 /*
  * This influences:
  * - Latency
@@ -387,13 +398,62 @@
 	unsigned count;  /* usage counter; NOTE: the usb stack is also considered a user */
 };
 
+/* in the event we don't have the extended soundcard.h, we still need
+   to compile successfully.  Supply definitions */
+
+#ifndef AFMT_S24_LE
+#	define AFMT_S24_LE	        0x00000800	
+#endif
+#ifndef AFMT_S24_BE
+#	define AFMT_S24_BE	        0x00001000	
+#endif
+#ifndef AFMT_U24_LE
+#	define AFMT_U24_LE	        0x00002000	
+#endif
+#ifndef AFMT_U24_BE
+#	define AFMT_U24_BE	        0x00004000	
+#endif
+#ifndef AFMT_S32_LE
+#	define AFMT_S32_LE	        0x00008000	
+#endif
+#ifndef AFMT_S32_BE
+#	define AFMT_S32_BE	        0x00010000	
+#endif
+#ifndef AFMT_U32_LE
+#	define AFMT_U32_LE	        0x00020000	
+#endif
+#ifndef AFMT_U32_BE
+#	define AFMT_U32_BE	        0x00040000	
+#endif
+
 /* private audio format extensions */
-#define AFMT_STEREO        0x80000000
-#define AFMT_ISSTEREO(x)   ((x) & AFMT_STEREO)
-#define AFMT_IS16BIT(x)    ((x) & (AFMT_S16_LE|AFMT_S16_BE|AFMT_U16_LE|AFMT_U16_BE))
-#define AFMT_ISUNSIGNED(x) ((x) & (AFMT_U8|AFMT_U16_LE|AFMT_U16_BE))
-#define AFMT_BYTESSHIFT(x) ((AFMT_ISSTEREO(x) ? 1 : 0) + (AFMT_IS16BIT(x) ? 1 : 0))
-#define AFMT_BYTES(x)      (1<<AFMT_BYTESSHFIT(x))
+#define AFMT_STEREO         0x01000000
+#define AFMT_CHMASK         0xff000000
+#define AFMT_8MASK          (AFMT_U8 | AFMT_S8)
+#define AFMT_16MASK         (AFMT_U16_LE | AFMT_S16_LE | AFMT_U16_BE | AFMT_S16_BE)
+#define AFMT_24MASK         (AFMT_U24_LE | AFMT_S24_LE | AFMT_U24_BE | AFMT_S24_BE)
+#define AFMT_32MASK         (AFMT_U32_LE | AFMT_S32_LE | AFMT_U32_BE | AFMT_S32_BE)
+
+#define AFMT_SIGNMASK       (AFMT_S8 | AFMT_S16_LE | AFMT_S16_BE |\
+                                       AFMT_S24_LE | AFMT_S24_BE |\
+                                       AFMT_S32_LE | AFMT_S32_BE)
+
+/* a little odd, but the code counts on byte formats being identified as 'big endian' */
+#define AFMT_ENDIANMASK     (AFMT_S8 | AFMT_U8 |\
+			               AFMT_S16_BE | AFMT_U16_BE |\
+                                       AFMT_S24_BE | AFMT_U24_BE |\
+                                       AFMT_S32_BE | AFMT_U32_BE)
+
+#define AFMT_ISSTEREO(x)    (((x) & 0xff000000) == AFMT_STEREO)
+#define AFMT_CHANNELS(x)    (((unsigned)(x) >> 24) + 1)
+#define AFMT_BYTES(x)       ( (((x)&AFMT_8MASK)!=0)+\
+                              (((x)&AFMT_16MASK)!=0)*2+\
+                              (((x)&AFMT_24MASK)!=0)*3+\
+                              (((x)&AFMT_32MASK)!=0)*4 )
+#define AFMT_SAMPLEBYTES(x) (AFMT_BYTES(x)*AFMT_CHANNELS(x))
+#define AFMT_SIGN(x)        ((x)&AFMT_SIGNMASK)
+#define AFMT_ENDIAN(x)      ((x)&AFMT_ENDIANMASK)
+
 
 /* --------------------------------------------------------------------- */
 
@@ -468,7 +528,7 @@
 	/* initialize some fields */
 	db->rdptr = db->wrptr = db->total_bytes = db->count = db->error = 0;
 	/* calculate required buffer size */
-	bytepersec = db->srate << AFMT_BYTESSHIFT(db->format);
+	bytepersec = db->srate * AFMT_SAMPLEBYTES(db->format);
 	bufs = 1U << DMABUFSHIFT;
 	if (db->ossfragshift) {
 		if ((1000 << db->ossfragshift) < bytepersec)
@@ -497,7 +557,7 @@
 			db->sgbuf[nr] = p;
 			mem_map_reserve(virt_to_page(p));
 		}
-		memset(db->sgbuf[nr], AFMT_ISUNSIGNED(db->format) ? 0x80 : 0, PAGE_SIZE);
+		memset(db->sgbuf[nr], AFMT_SIGN(db->format) ? 0 : 0x80, PAGE_SIZE);
 		if ((nr << PAGE_SHIFT) >= db->dmasize)
 			break;
 	}
@@ -688,147 +748,168 @@
 	usbin_stop(as);
 }
 
-static void conversion(const void *ibuf, unsigned int ifmt, void *obuf, unsigned int ofmt, void *tmp, unsigned int scnt)
+static inline int iconvert(unsigned char **xx,int jump)
 {
-	unsigned int cnt, i;
-	__s16 *sp, *sp2, s;
-	unsigned char *bp;
-
-	cnt = scnt;
-	if (AFMT_ISSTEREO(ifmt))
-		cnt <<= 1;
-	sp = ((__s16 *)tmp) + cnt;
-	switch (ifmt & ~AFMT_STEREO) {
-	case AFMT_U8:
-		for (bp = ((unsigned char *)ibuf)+cnt, i = 0; i < cnt; i++) {
-			bp--;
-			sp--;
-			*sp = (*bp ^ 0x80) << 8;
-		}
-		break;
+  int value=0;
+  unsigned char *x=*xx;
 			
-	case AFMT_S8:
-		for (bp = ((unsigned char *)ibuf)+cnt, i = 0; i < cnt; i++) {
-			bp--;
-			sp--;
-			*sp = *bp << 8;
+  /* conversion fall-through cascade compiles to a jump table */
+  switch(jump){
+  case 0:
+    /* 32 bit BE */
+    value  = x[3];
+  case 1:
+    /* 24 bit BE */
+    value |= x[2] << 8;
+  case 2:
+    /* 16 bit BE */
+    value |= x[1] << 16;
+  case 3:
+    /* 8 bit */
+    value |= x[0] << 24;
+    x+=(4-jump);
+    break;
+
+  case 4:
+    /* 32 bit LE */
+    value  = *x++;
+  case 5:
+    /* 24 bit LE */
+    value |= *x++ << 8;
+  case 6:
+    /* 16 bit LE */
+    value |= *x++ << 16;
+    value |= *x++ << 24;
+    break;
+  }
+  *xx=x;
+  return(value);
+}
+
+static inline void oconvert(unsigned char **yy,int jump,int value)
+{
+  unsigned char *y=*yy;
+
+  /* conversion fall-through cascade compiles to a jump table */
+  switch(jump){
+  case 0:
+    /* 32 bit BE */
+    y[3] = value;
+  case 1:
+    /* 24 bit BE */
+    y[2] = value >> 8;
+  case 2:
+    /* 16 bit BE */
+    y[1] = value >> 16;
+  case 3:
+    /* 8 bit */
+    y[0] = value >> 24;
+    y+=(4-jump);
+    break;
+
+  case 4:
+    /* 32 bit LE */
+    *y++ = value;
+  case 5:
+    /* 24 bit LE */
+    *y++ = value >> 8;
+  case 6:
+    /* 16 bit LE */
+    *y++ = value >> 16;
+    *y++ = value >> 24;
+    break;
+  }
+  *yy=y;
+}
+
+/* capable of any-to-any conversion */
+static void conversion(const void *ibuf, unsigned int ifmt, 
+		       void *obuf, unsigned int ofmt, unsigned int scnt)
+{
+
+  /* some conversion is indeed needed */
+  unsigned int i,j;
+  unsigned char *x=(unsigned char *)ibuf;
+  unsigned char *y=(unsigned char *)obuf;
+
+  int ichannels = AFMT_CHANNELS(ifmt);
+  int ochannels = AFMT_CHANNELS(ofmt);
+  int ibytes    = AFMT_BYTES(ifmt);
+  int obytes    = AFMT_BYTES(ofmt);
+  int iendian   = AFMT_ENDIAN(ifmt);
+  int oendian   = AFMT_ENDIAN(ofmt);
+  int isign     = AFMT_SIGN(ifmt)?0:0x80000000UL;
+  int osign     = AFMT_SIGN(ofmt)?0:0x80000000UL;
+  int sign      = (isign==osign?0:0x80000000UL);
+  
+  /* build the byte/endian jump table offsets */
+  int ijump = (iendian ? 4-ibytes : 8-ibytes);
+  int ojump = (oendian ? 4-obytes : 8-obytes);
+  
+  if(ichannels == 2 && ochannels == 1){
+    /* Stereo -> mono is a special case loop; we downmix */
+    for(i=0;i<scnt;i++){
+      int valueL = iconvert(&x,ijump) ^ isign; /* side effect; increments x */
+      int valueR = iconvert(&x,ijump) ^ isign; /* side effect; increments x */
+      int value  = (valueL>>1) + (valueR>>1);
+      oconvert(&y,ojump,value^osign);  /* side effect; increments y */
+		}
+    return;
+		}
+  if(ichannels == 1 && ochannels == 2){
+    /* mono->stereo is a special case loop; we replicate */
+    for(i=0;i<scnt;i++){
+      int value = iconvert(&x,ijump) ^ sign; /* side effect; increments x */
+      oconvert(&y,ojump,value);  /* side effect; increments y */
+      oconvert(&y,ojump,value);  /* side effect; increments y */
+	}
+    return;
+		}
+  if(ichannels<ochannels){
+    /* zero out extra output channels */
+    for(i=0;i<scnt;i++){
+      for(j=0;j<ichannels;j++){
+	int value = iconvert(&x,ijump) ^ sign; /* side effect; increments x */
+	oconvert(&y,ojump,value);  /* side effect; increments y */
+	
+	}
+      for(;j<ochannels;j++){
+	oconvert(&y,ojump,osign);  /* side effect; increments y */
+	}
 		}
-		break;
-		
-	case AFMT_U16_LE:
-		for (bp = ((unsigned char *)ibuf)+2*cnt, i = 0; i < cnt; i++) {
-			bp -= 2;
-			sp--;
-			*sp = (bp[0] | (bp[1] << 8)) ^ 0x8000;
-		}
-		break;
-
-	case AFMT_U16_BE:
-		for (bp = ((unsigned char *)ibuf)+2*cnt, i = 0; i < cnt; i++) {
-			bp -= 2;
-			sp--;
-			*sp = (bp[1] | (bp[0] << 8)) ^ 0x8000;
-		}
-		break;
-
-	case AFMT_S16_LE:
-		for (bp = ((unsigned char *)ibuf)+2*cnt, i = 0; i < cnt; i++) {
-			bp -= 2;
-			sp--;
-			*sp = bp[0] | (bp[1] << 8);
-		}
-		break;
-
-	case AFMT_S16_BE:
-		for (bp = ((unsigned char *)ibuf)+2*cnt, i = 0; i < cnt; i++) {
-			bp -= 2;
-			sp--;
-			*sp = bp[1] | (bp[0] << 8);
-		}
-		break;
-	}
-	if (!AFMT_ISSTEREO(ifmt) && AFMT_ISSTEREO(ofmt)) {
-		/* expand from mono to stereo */
-		for (sp = ((__s16 *)tmp)+scnt, sp2 = ((__s16 *)tmp)+2*scnt, i = 0; i < scnt; i++) {
-			sp--;
-			sp2 -= 2;
-			sp2[0] = sp2[1] = sp[0];
-		}
-	}
-	if (AFMT_ISSTEREO(ifmt) && !AFMT_ISSTEREO(ofmt)) {
-		/* contract from stereo to mono */
-		for (sp = sp2 = ((__s16 *)tmp), i = 0; i < scnt; i++, sp++, sp2 += 2)
-			sp[0] = (sp2[0] + sp2[1]) >> 1;
-	}
-	cnt = scnt;
-	if (AFMT_ISSTEREO(ofmt))
-		cnt <<= 1;
-	sp = ((__s16 *)tmp);
-	bp = ((unsigned char *)obuf);
-	switch (ofmt & ~AFMT_STEREO) {
-	case AFMT_U8:
-		for (i = 0; i < cnt; i++, sp++, bp++)
-			*bp = (*sp >> 8) ^ 0x80;
-		break;
-
-	case AFMT_S8:
-		for (i = 0; i < cnt; i++, sp++, bp++)
-			*bp = *sp >> 8;
-		break;
-
-	case AFMT_U16_LE:
-		for (i = 0; i < cnt; i++, sp++, bp += 2) {
-			s = *sp;
-			bp[0] = s;
-			bp[1] = (s >> 8) ^ 0x80;
-		}
-		break;
-
-	case AFMT_U16_BE:
-		for (i = 0; i < cnt; i++, sp++, bp += 2) {
-			s = *sp;
-			bp[1] = s;
-			bp[0] = (s >> 8) ^ 0x80;
-		}
-		break;
-
-	case AFMT_S16_LE:
-		for (i = 0; i < cnt; i++, sp++, bp += 2) {
-			s = *sp;
-			bp[0] = s;
-			bp[1] = s >> 8;
-		}
-		break;
-
-	case AFMT_S16_BE:
-		for (i = 0; i < cnt; i++, sp++, bp += 2) {
-			s = *sp;
-			bp[1] = s;
-			bp[0] = s >> 8;
+    return;
+		}
+  if(ichannels>=ochannels){
+    /* discard extra input channels */
+    int xincrement=ibytes*(ichannels-ochannels);
+    for(i=0;i<scnt;i++){
+      for(j=0;j<ichannels;j++){
+	int value = iconvert(&x,ijump) ^ sign; /* side effect; increments x */
+	oconvert(&y,ojump,value);  /* side effect; increments y */
+
+		}
+      x+=xincrement;
 		}
-		break;
+    return;
 	}
-	
 }
 
 static void usbin_convert(struct usbin *u, unsigned char *buffer, unsigned int samples)
 {
-	union {
-		__s16 s[64];
-		unsigned char b[0];
-	} tmp;
-	unsigned int scnt, maxs, ufmtsh, dfmtsh;
-
-	ufmtsh = AFMT_BYTESSHIFT(u->format);
-	dfmtsh = AFMT_BYTESSHIFT(u->dma.format);
-	maxs = (AFMT_ISSTEREO(u->dma.format | u->format)) ? 32 : 64;
+        unsigned int scnt;
+	unsigned int ufmtb = AFMT_SAMPLEBYTES(u->format);
+	unsigned int dfmtb = AFMT_SAMPLEBYTES(u->dma.format);
+        unsigned char tmp[TMPCOPYWIDTH];
+	unsigned int maxs  = sizeof(tmp)/dfmtb;
+
 	while (samples > 0) {
 		scnt = samples;
 		if (scnt > maxs)
 			scnt = maxs;
-		conversion(buffer, u->format, tmp.b, u->dma.format, tmp.b, scnt);
-		dmabuf_copyin(&u->dma, tmp.b, scnt << dfmtsh);
-		buffer += scnt << ufmtsh;
+
+	        conversion(buffer, u->format, tmp, u->dma.format, scnt);
+                dmabuf_copyin(&u->dma, tmp, scnt * dfmtb);
+                buffer += scnt * ufmtb;
 		samples -= scnt;
 	}
 }		
@@ -837,7 +918,7 @@
 {
 	unsigned int i, maxsize, offs;
 
-	maxsize = (u->freqmax + 0x3fff) >> (14 - AFMT_BYTESSHIFT(u->format));
+	maxsize = ((u->freqmax + 0x3fff) * AFMT_SAMPLEBYTES(u->format)) >> 14;
 	//printk(KERN_DEBUG "usbin_prepare_desc: maxsize %d freq 0x%x format 0x%x\n", maxsize, u->freqn, u->format);
 	for (i = offs = 0; i < DESCFRAMES; i++, offs += maxsize) {
 		urb->iso_frame_desc[i].length = maxsize;
@@ -852,26 +933,26 @@
  */
 static int usbin_retire_desc(struct usbin *u, struct urb *urb)
 {
-	unsigned int i, ufmtsh, dfmtsh, err = 0, cnt, scnt, dmafree;
+	unsigned int i, ufmtb, dfmtb, err = 0, cnt, scnt, dmafree;
 	unsigned char *cp;
 
-	ufmtsh = AFMT_BYTESSHIFT(u->format);
-	dfmtsh = AFMT_BYTESSHIFT(u->dma.format);
+	ufmtb = AFMT_SAMPLEBYTES(u->format);
+	dfmtb = AFMT_SAMPLEBYTES(u->dma.format);
 	for (i = 0; i < DESCFRAMES; i++) {
 		cp = ((unsigned char *)urb->transfer_buffer) + urb->iso_frame_desc[i].offset;
 		if (urb->iso_frame_desc[i].status) {
 			dprintk((KERN_DEBUG "usbin_retire_desc: frame %u status %d\n", i, urb->iso_frame_desc[i].status));
 			continue;
 		}
-		scnt = urb->iso_frame_desc[i].actual_length >> ufmtsh;
+		scnt = urb->iso_frame_desc[i].actual_length / ufmtb;
 		if (!scnt)
 			continue;
-		cnt = scnt << dfmtsh;
+		cnt = scnt * dfmtb;
 		if (!u->dma.mapped) {
 			dmafree = u->dma.dmasize - u->dma.count;
 			if (cnt > dmafree) {
-				scnt = dmafree >> dfmtsh;
-				cnt = scnt << dfmtsh;
+				scnt = dmafree / dfmtb;
+				cnt = scnt * dfmtb;
 				err++;
 			}
 		}
@@ -1015,7 +1096,7 @@
 		u->freqn = ((u->dma.srate << 11) + 62) / 125; /* this will overflow at approx 2MSPS */
 		u->freqmax = u->freqn + (u->freqn >> 2);
 		u->phase = 0;
-		maxsze = (u->freqmax + 0x3fff) >> (14 - AFMT_BYTESSHIFT(u->format));
+		maxsze = ((u->freqmax + 0x3fff) * AFMT_SAMPLEBYTES(u->format)) >> 14;
 		bufsz = DESCFRAMES * maxsze;
 		if (u->durb[0].urb.transfer_buffer)
 			kfree(u->durb[0].urb.transfer_buffer);
@@ -1166,33 +1247,31 @@
 
 static void usbout_convert(struct usbout *u, unsigned char *buffer, unsigned int samples)
 {
-	union {
-		__s16 s[64];
-		unsigned char b[0];
-	} tmp;
-	unsigned int scnt, maxs, ufmtsh, dfmtsh;
-
-	ufmtsh = AFMT_BYTESSHIFT(u->format);
-	dfmtsh = AFMT_BYTESSHIFT(u->dma.format);
-	maxs = (AFMT_ISSTEREO(u->dma.format | u->format)) ? 32 : 64;
+        unsigned char tmp[TMPCOPYWIDTH];
+        unsigned int scnt;
+	unsigned int ufmtb = AFMT_SAMPLEBYTES(u->format);
+	unsigned int dfmtb = AFMT_SAMPLEBYTES(u->dma.format);
+	unsigned int maxs  = sizeof(tmp)/dfmtb;
+	
 	while (samples > 0) {
 		scnt = samples;
 		if (scnt > maxs)
 			scnt = maxs;
-		dmabuf_copyout(&u->dma, tmp.b, scnt << dfmtsh);
-		conversion(tmp.b, u->dma.format, buffer, u->format, tmp.b, scnt);
-		buffer += scnt << ufmtsh;
+
+		dmabuf_copyout(&u->dma, tmp, scnt * dfmtb);
+		conversion(tmp, u->dma.format, buffer, u->format, scnt);
+                buffer += scnt * ufmtb;
 		samples -= scnt;
 	}
 }		
 
 static int usbout_prepare_desc(struct usbout *u, struct urb *urb)
 {
-	unsigned int i, ufmtsh, dfmtsh, err = 0, cnt, scnt, offs;
+	unsigned int i, ufmtb, dfmtb, err = 0, cnt, scnt, offs;
 	unsigned char *cp = urb->transfer_buffer;
 
-	ufmtsh = AFMT_BYTESSHIFT(u->format);
-	dfmtsh = AFMT_BYTESSHIFT(u->dma.format);
+	ufmtb = AFMT_SAMPLEBYTES(u->format);
+	dfmtb = AFMT_SAMPLEBYTES(u->dma.format);
 	for (i = offs = 0; i < DESCFRAMES; i++) {
 		urb->iso_frame_desc[i].offset = offs;
 		u->phase = (u->phase & 0x3fff) + u->freqm;
@@ -1201,11 +1280,11 @@
 			urb->iso_frame_desc[i].length = 0;
 			continue;
 		}
-		cnt = scnt << dfmtsh;
+		cnt = scnt * dfmtb;
 		if (!u->dma.mapped) {
 			if (cnt > u->dma.count) {
-				scnt = u->dma.count >> dfmtsh;
-				cnt = scnt << dfmtsh;
+				scnt = u->dma.count / dfmtb;
+				cnt = scnt * dfmtb;
 				err++;
 			}
 			u->dma.count -= cnt;
@@ -1218,7 +1297,7 @@
 			/* we need sampling format conversion */
 			usbout_convert(u, cp, scnt);
 		}
-		cnt = scnt << ufmtsh;
+		cnt = scnt * ufmtb;
 		urb->iso_frame_desc[i].length = cnt;
 		offs += cnt;
 		cp += cnt;
@@ -1380,7 +1459,8 @@
 		u->freqn = u->freqm = ((u->dma.srate << 11) + 62) / 125; /* this will overflow at approx 2MSPS */
 		u->freqmax = u->freqn + (u->freqn >> 2);
 		u->phase = 0;
-		maxsze = (u->freqmax + 0x3fff) >> (14 - AFMT_BYTESSHIFT(u->format));
+		maxsze = ((u->freqmax + 0x3fff) * AFMT_SAMPLEBYTES(u->format)) >>14;
+
 		bufsz = DESCFRAMES * maxsze;
 		if (u->durb[0].urb.transfer_buffer)
 			kfree(u->durb[0].urb.transfer_buffer);
@@ -1473,27 +1553,86 @@
 }
 
 /* --------------------------------------------------------------------- */
+/* allowed conversions (sign, endian, width, channels), and relative
+   weighting penalties against fuzzy match selection.  For the
+   purposes of not confusing users, 'lossy' format translation is
+   disallowed, eg, don't allow a mono 8 bit device to successfully
+   open as 5.1, 24 bit... Never allow a mode that tries to deliver greater
+   than the hard capabilities of the device.
+
+   device --=> app
+
+   signed   => unsigned : 1
+   unsigned => signed   : 1
+
+   le       => be       : 1
+   be       => le       : 1
+
+   8        => 16       : not allowed
+   8        => 24       : not allowed
+   8        => 32       : not allowed
+   16       => 24       : not allowed
+   16       => 32       : not allowed
+   24       => 32       : not allowed
+
+   16       => 8        : 4
+   24       => 16       : 4
+   24       => 8        : 5
+   32       => 24       : 4
+   32       => 16       : 5
+   32       => 8        : 5
 
-static unsigned int format_goodness(struct audioformat *afp, unsigned int fmt, unsigned int srate)
-{
+   mono     => stereo   : not allowed
+   stereo   => mono     : 32 (downmix to L+R/2)
+
+   N        => >N       : not allowed
+   N        => <N       : 32 */
+
+static unsigned int format_goodness(struct audioformat *afp, unsigned int app,
+				    unsigned int srate){
 	unsigned int g = 0;
+	unsigned int sratelo=afp->sratelo;
+	unsigned int sratehi=afp->sratehi;
+	unsigned int dev=afp->format;
+
+	if(AFMT_SIGN(dev) && !AFMT_SIGN(app))     g += 1;
+	if(!AFMT_SIGN(dev) && AFMT_SIGN(app))     g += 1;
+	if(AFMT_ENDIAN(dev) && !AFMT_ENDIAN(app)) g += 1;
+	if(!AFMT_ENDIAN(dev) && AFMT_ENDIAN(app)) g += 1;
+
+	switch(AFMT_BYTES(app)+AFMT_BYTES(dev)*10){
+	case 12: return ~0;
+	case 13: return ~0;
+	case 14: return ~0;
+	case 21: g += 4; break;
+	case 23: return ~0;
+	case 24: return ~0;
+	case 31: g += 5; break;
+	case 32: g += 4; break;
+	case 34: return ~0;
+	case 41: g += 6; break;
+	case 42: g += 5; break;
+	case 43: g += 4; break;
+	}
+
+	if(AFMT_CHANNELS(dev) > AFMT_CHANNELS(app)){
+	        g+=32;
+	}else if(AFMT_CHANNELS(dev) < AFMT_CHANNELS(app)){
+	        return ~0;
+	}
+	  
+	g<<=20;
+
+	if (srate < sratelo)
+	        g += sratelo - srate;
+        if (srate > sratehi)
+	        g += srate - sratehi;
 
-	if (srate < afp->sratelo)
-		g += afp->sratelo - srate;
-	if (srate > afp->sratehi)
-		g += srate - afp->sratehi;
-	if (AFMT_ISSTEREO(afp->format) && !AFMT_ISSTEREO(fmt))
-		g += 0x100000;
-	if (!AFMT_ISSTEREO(afp->format) && AFMT_ISSTEREO(fmt))
-		g += 0x400000;
-	if (AFMT_IS16BIT(afp->format) && !AFMT_IS16BIT(fmt))
-		g += 0x100000;
-	if (!AFMT_IS16BIT(afp->format) && AFMT_IS16BIT(fmt))
-		g += 0x400000;
-	return g;
+	return(g);
 }
 
-static int find_format(struct audioformat *afp, unsigned int nr, unsigned int fmt, unsigned int srate)
+static int find_format(struct audioformat *afp, unsigned int nr, 
+			  unsigned int fmt, unsigned int srate)
 {
 	unsigned int i, g, gb = ~0;
 	int j = -1; /* default to failure */
@@ -1501,8 +1640,7 @@
 	/* find "best" format (according to format_goodness) */
 	for (i = 0; i < nr; i++) {
 		g = format_goodness(&afp[i], fmt, srate);
-		if (g >= gb) 
-			continue;
+		if (g >= gb) continue;
 		j = i;
 		gb = g;
 	}
@@ -2116,8 +2254,8 @@
 			set_current_state(TASK_RUNNING);
 			return -EBUSY;
 		}
-		tmo = 3 * HZ * count / as->usbout.dma.srate;
-		tmo >>= AFMT_BYTESSHIFT(as->usbout.dma.format);
+		tmo = 3 * HZ * count / (as->usbout.dma.srate * 
+					AFMT_SAMPLEBYTES(as->usbout.dma.format));
 		if (!schedule_timeout(tmo + 1)) {
 			printk(KERN_DEBUG "usbaudio: dma timed out??\n");
 			break;
@@ -2218,7 +2356,7 @@
 		return ret;
 	if (!access_ok(VERIFY_READ, buffer, count))
 		return -EFAULT;
-	start_thr = (as->usbout.dma.srate << AFMT_BYTESSHIFT(as->usbout.dma.format)) / (1000 / (3 * DESCFRAMES));
+	start_thr = (as->usbout.dma.srate * AFMT_SAMPLEBYTES(as->usbout.dma.format)) / (1000 / (3 * DESCFRAMES));
 	add_wait_queue(&as->usbout.dma.wait, &wait);
 	while (count > 0) {
 #if 0
@@ -2410,6 +2548,7 @@
 		if (get_user(val, (int *)arg))
 			return -EFAULT;
 		val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format;
+		val2 &= 0x00ffffff;
 		if (val)
 			val2 |= AFMT_STEREO;
 		else
@@ -2423,19 +2562,22 @@
 			return -EFAULT;
 		if (val != 0) {
 			val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format;
-			if (val == 1)
-				val2 &= ~AFMT_STEREO;
-			else
-				val2 |= AFMT_STEREO;
+			
+			val2 &= 0x00ffffff;
+			val2 |= (val-1)<<24;
+
 			if (set_format(as, file->f_mode, val2, 0))
 				return -EIO;
 		}
 		val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format;
-		return put_user(AFMT_ISSTEREO(val2) ? 2 : 1, (int *)arg);
+		return put_user(AFMT_CHANNELS(val2), (int *)arg);
 
 	case SNDCTL_DSP_GETFMTS: /* Returns a mask */
 		return put_user(AFMT_U8 | AFMT_U16_LE | AFMT_U16_BE |
-				AFMT_S8 | AFMT_S16_LE | AFMT_S16_BE, (int *)arg);
+				AFMT_S8 | AFMT_S16_LE | AFMT_S16_BE |
+				AFMT_U24_LE | AFMT_U24_BE | AFMT_S24_LE | AFMT_S24_BE |
+				AFMT_U32_LE | AFMT_U32_BE | AFMT_S32_LE | AFMT_S32_BE,
+				(int *)arg);
 
 	case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/
 		if (get_user(val, (int *)arg))
@@ -2444,15 +2586,17 @@
 			if (hweight32(val) != 1)
 				return -EINVAL;
 			if (!(val & (AFMT_U8 | AFMT_U16_LE | AFMT_U16_BE |
-				     AFMT_S8 | AFMT_S16_LE | AFMT_S16_BE)))
+				     AFMT_S8 | AFMT_S16_LE | AFMT_S16_BE |
+				     AFMT_U24_LE | AFMT_U24_BE | AFMT_S24_LE | AFMT_S24_BE |
+				     AFMT_U32_LE | AFMT_U32_BE | AFMT_S32_LE | AFMT_S32_BE)))
 				return -EINVAL;
 			val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format;
-			val |= val2 & AFMT_STEREO;
+			val |= val2 & AFMT_CHMASK;
 			if (set_format(as, file->f_mode, val, 0))
 				return -EIO;
 		}
 		val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format;
-		return put_user(val2 & ~AFMT_STEREO, (int *)arg);
+		return put_user(val2 & ~AFMT_CHMASK, (int *)arg);
 
 	case SNDCTL_DSP_POST:
 		return 0;
@@ -2491,7 +2635,24 @@
 	case SNDCTL_DSP_GETOSPACE:
 		if (!(file->f_mode & FMODE_WRITE))
 			return -EINVAL;
-		if (!(as->usbout.flags & FLG_RUNNING) && (val = prog_dmabuf_out(as)) != 0)
+
+		/*if (!(as->usbout.flags & FLG_RUNNING) && (val = prog_dmabuf_out(as)) != 0)
+
+		The above is potentially disasterous; if the
+		userspace app calls the GETOSPACE ioctl() before a
+		data write on the device (as can happen in a
+		sensible client that's tracking the write buffer
+		low watermark), the kernel driver will never
+		recover from momentary starvation (recall that
+		FLG_RUNNING will be cleared by usbout_completed)
+		because the ioctl will keep resetting the DMA
+		buffer before each write, potentially never
+		allowing us to fill the buffer back to the DMA
+		restart threshhold.
+
+		Can you tell this was actually biting me? :-) */
+
+		if ((!as->usbout.dma.ready) && (val = prog_dmabuf_out(as)) != 0)
 			return val;
 		spin_lock_irqsave(&as->lock, flags);
 		abinfo.fragsize = as->usbout.dma.fragsize;
@@ -2504,7 +2665,9 @@
 	case SNDCTL_DSP_GETISPACE:
 		if (!(file->f_mode & FMODE_READ))
 			return -EINVAL;
-		if (!(as->usbin.flags & FLG_RUNNING) && (val = prog_dmabuf_in(as)) != 0)
+		
+		/*if (!(as->usbin.flags & FLG_RUNNING) && (val = prog_dmabuf_in(as)) != 0)*/
+		if ((!as->usbin.dma.ready) && (val = prog_dmabuf_in(as)) != 0)
 			return val;
 		spin_lock_irqsave(&as->lock, flags);
 		abinfo.fragsize = as->usbin.dma.fragsize;
@@ -2552,11 +2715,14 @@
 
        case SNDCTL_DSP_GETBLKSIZE:
 		if (file->f_mode & FMODE_WRITE) {
-			if ((val = prog_dmabuf_out(as)))
+
+		  /* do not clobber devices that are already running! */
+		  if ((!as->usbout.dma.ready) && (val = prog_dmabuf_out(as)) != 0)
 				return val;
 			return put_user(as->usbout.dma.fragsize, (int *)arg);
 		}
-		if ((val = prog_dmabuf_in(as)))
+		/* do not clobber devices that are already running! */
+		if ((!as->usbin.dma.ready) && (val = prog_dmabuf_in(as)) != 0)
 			return val;
 		return put_user(as->usbin.dma.fragsize, (int *)arg);
 
@@ -2604,11 +2770,11 @@
 
 	case SOUND_PCM_READ_CHANNELS:
 		val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format;
-		return put_user(AFMT_ISSTEREO(val2) ? 2 : 1, (int *)arg);
+		return put_user(AFMT_CHANNELS(val2), (int *)arg);
 
 	case SOUND_PCM_READ_BITS:
 		val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format;
-		return put_user(AFMT_IS16BIT(val2) ? 16 : 8, (int *)arg);
+		return put_user(AFMT_BYTES(val2) * 8, (int *)arg);
 
 	case SOUND_PCM_WRITE_FILTER:
 	case SNDCTL_DSP_SETSYNCRO:
@@ -2864,7 +3030,9 @@
 				       dev->devnum, asifin, i);
 				continue;
 			}
-			format = (fmt[5] == 2) ? (AFMT_U16_LE | AFMT_U8) : (AFMT_S16_LE | AFMT_S8);
+			format = (fmt[5] == 2) ? 
+			  (AFMT_U32_LE | AFMT_U24_LE | AFMT_U16_LE | AFMT_U8) : 
+			  (AFMT_S32_LE | AFMT_S24_LE | AFMT_S16_LE | AFMT_S8);
 			fmt = find_csinterface_descriptor(buffer, buflen, NULL, FORMAT_TYPE, asifin, i);
 			if (!fmt) {
 				printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u FORMAT_TYPE descriptor not found\n", 
@@ -2876,7 +3044,7 @@
 				       dev->devnum, asifin, i);
 				continue;
 			}
-			if (fmt[4] < 1 || fmt[4] > 2 || fmt[5] < 1 || fmt[5] > 2) {
+			if (fmt[4] < 1 || fmt[4] > MAXCHANNELS || fmt[5] < 1 || fmt[5] > 4) {
 				printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u unsupported channels %u framesize %u\n", 
 				       dev->devnum, asifin, i, fmt[4], fmt[5]);
 				continue;
@@ -2889,13 +3057,26 @@
 			}
 			if (as->numfmtin >= MAXFORMATS)
 				continue;
+			printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u channels %u framesize %u configured\n", 
+				       dev->devnum, asifin, i, fmt[4], fmt[5]);
 			fp = &as->fmtin[as->numfmtin++];
-			if (fmt[5] == 2)
-				format &= (AFMT_U16_LE | AFMT_S16_LE);
-			else
+			switch (fmt[5]) {
+			case 1:
 				format &= (AFMT_U8 | AFMT_S8);
-			if (fmt[4] == 2)
-				format |= AFMT_STEREO;
+				break;
+			case 2:
+				format &= (AFMT_U16_LE | AFMT_S16_LE);
+				break;
+			case 3:
+				format &= (AFMT_U24_LE | AFMT_S24_LE);
+				break;
+			case 4:
+				format &= (AFMT_U32_LE | AFMT_S32_LE);
+				break;
+			}
+			
+			format |= (fmt[4]-1) << 24;
+
 			fp->format = format;
 			fp->altsetting = i;
 			fp->sratelo = fp->sratehi = fmt[8] | (fmt[9] << 8) | (fmt[10] << 16);
@@ -2944,7 +3125,10 @@
 				       dev->devnum, asifout, i);
 				continue;
 			}
-			format = (fmt[5] == 2) ? (AFMT_U16_LE | AFMT_U8) : (AFMT_S16_LE | AFMT_S8);
+			format = (fmt[5] == 2) ? 
+			  (AFMT_U32_LE | AFMT_U24_LE | AFMT_U16_LE | AFMT_U8) : 
+			  (AFMT_S32_LE | AFMT_S24_LE | AFMT_S16_LE | AFMT_S8);
+
 			/* Dallas DS4201 workaround */
 			if (dev->descriptor.idVendor == 0x04fa && dev->descriptor.idProduct == 0x4201)
 				format = (AFMT_S16_LE | AFMT_S8);
@@ -2959,7 +3143,7 @@
 				       dev->devnum, asifout, i);
 				continue;
 			}
-			if (fmt[4] < 1 || fmt[4] > 2 || fmt[5] < 1 || fmt[5] > 2) {
+			if (fmt[4] < 1 || fmt[4] > MAXCHANNELS || fmt[5] < 1 || fmt[5] > 4) {
 				printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u unsupported channels %u framesize %u\n", 
 				       dev->devnum, asifout, i, fmt[4], fmt[5]);
 				continue;
@@ -2972,13 +3156,27 @@
 			}
 			if (as->numfmtout >= MAXFORMATS)
 				continue;
+			printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u channels %u framesize %u configured\n", 
+			       dev->devnum, asifout, i, fmt[4], fmt[5]);
 			fp = &as->fmtout[as->numfmtout++];
-			if (fmt[5] == 2)
-				format &= (AFMT_U16_LE | AFMT_S16_LE);
-			else
+
+			switch (fmt[5]) {
+			case 1:
 				format &= (AFMT_U8 | AFMT_S8);
-			if (fmt[4] == 2)
-				format |= AFMT_STEREO;
+				break;
+			case 2:
+				format &= (AFMT_U16_LE | AFMT_S16_LE);
+				break;
+			case 3:
+				format &= (AFMT_U24_LE | AFMT_S24_LE);
+				break;
+			case 4:
+				format &= (AFMT_U32_LE | AFMT_S32_LE);
+				break;
+			}
+
+			format |= (fmt[4]-1) << 24;
+
 			fp->format = format;
 			fp->altsetting = i;
 			fp->sratelo = fp->sratehi = fmt[8] | (fmt[9] << 8) | (fmt[10] << 16);
diff -urN linux-2.4.21-pre2/drivers/usb/hcd/ehci-dbg.c linux-2.4.21-pre3/drivers/usb/hcd/ehci-dbg.c
--- linux-2.4.21-pre2/drivers/usb/hcd/ehci-dbg.c	Mon Jan  6 16:32:29 2003
+++ linux-2.4.21-pre3/drivers/usb/hcd/ehci-dbg.c	Mon Jan  6 16:32:32 2003
@@ -18,10 +18,45 @@
 
 /* this file is part of ehci-hcd.c */
 
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,50)
+
+#define ehci_dbg(ehci, fmt, args...) \
+	dev_dbg (*(ehci)->hcd.controller, fmt, ## args )
+#define ehci_err(ehci, fmt, args...) \
+	dev_err (*(ehci)->hcd.controller, fmt, ## args )
+#define ehci_info(ehci, fmt, args...) \
+	dev_info (*(ehci)->hcd.controller, fmt, ## args )
+#define ehci_warn(ehci, fmt, args...) \
+	dev_warn (*(ehci)->hcd.controller, fmt, ## args )
+
+#else
+
+#ifdef DEBUG
+#define ehci_dbg(ehci, fmt, args...) \
+	printk(KERN_DEBUG "%s %s: " fmt, hcd_name, \
+		(ehci)->hcd.pdev->slot_name, ## args )
+#else
+#define ehci_dbg(ehci, fmt, args...) do { } while (0)
+#endif
+
+#define ehci_err(ehci, fmt, args...) \
+	printk(KERN_ERR "%s %s: " fmt, hcd_name, \
+		(ehci)->hcd.pdev->slot_name, ## args )
+#define ehci_info(ehci, fmt, args...) \
+	printk(KERN_INFO "%s %s: " fmt, hcd_name, \
+		(ehci)->hcd.pdev->slot_name, ## args )
+#define ehci_warn(ehci, fmt, args...) \
+	printk(KERN_WARNING "%s %s: " fmt, hcd_name, \
+		(ehci)->hcd.pdev->slot_name, ## args )
+#endif
+
+
 #ifdef EHCI_VERBOSE_DEBUG
 #	define vdbg dbg
+#	define ehci_vdbg ehci_dbg
 #else
-	static inline void vdbg (char *fmt, ...) { }
+#	define vdbg(fmt,args...) do { } while (0)
+#	define ehci_vdbg(ehci, fmt, args...) do { } while (0)
 #endif
 
 #ifdef	DEBUG
@@ -34,7 +69,8 @@
 {
 	u32	params = readl (&ehci->caps->hcs_params);
 
-	dbg ("%s hcs_params 0x%x dbg=%d%s cc=%d pcc=%d%s%s ports=%d",
+	ehci_dbg (ehci,
+		"%s hcs_params 0x%x dbg=%d%s cc=%d pcc=%d%s%s ports=%d\n",
 		label, params,
 		HCS_DEBUG_PORT (params),
 		HCS_INDICATOR (params) ? " ind" : "",
@@ -56,9 +92,8 @@
 				((i & 0x1) ? ((byte)&0xf) : ((byte>>4)&0xf)));
 			strcat(buf, tmp);
 		}
-		dbg ("%s: %s portroute %s", 
-			hcd_to_bus (&ehci->hcd)->bus_name, label,
-			buf);
+		ehci_dbg (ehci, "%s portroute %s\n",
+				label, buf);
 	}
 }
 #else
@@ -77,19 +112,16 @@
 {
 	u32	params = readl (&ehci->caps->hcc_params);
 
-	if (HCC_EXT_CAPS (params)) {
-		// EHCI 0.96 ... could interpret these (legacy?)
-		dbg ("%s extended capabilities at pci %2x",
-			label, HCC_EXT_CAPS (params));
-	}
 	if (HCC_ISOC_CACHE (params)) {
-		dbg ("%s hcc_params %04x caching frame %s%s%s",
+		ehci_dbg (ehci,
+		     "%s hcc_params %04x caching frame %s%s%s\n",
 		     label, params,
 		     HCC_PGM_FRAMELISTLEN (params) ? "256/512/1024" : "1024",
 		     HCC_CANPARK (params) ? " park" : "",
 		     HCC_64BIT_ADDR (params) ? " 64 bit addr" : "");
 	} else {
-		dbg ("%s hcc_params %04x caching %d uframes %s%s%s",
+		ehci_dbg (ehci,
+		     "%s hcc_params %04x thresh %d uframes %s%s%s\n",
 		     label,
 		     params,
 		     HCC_ISOC_THRES (params),
@@ -235,19 +267,19 @@
 #define dbg_status(ehci, label, status) { \
 	char _buf [80]; \
 	dbg_status_buf (_buf, sizeof _buf, label, status); \
-	dbg ("%s", _buf); \
+	ehci_dbg (ehci, "%s\n", _buf); \
 }
 
 #define dbg_cmd(ehci, label, command) { \
 	char _buf [80]; \
 	dbg_command_buf (_buf, sizeof _buf, label, command); \
-	dbg ("%s", _buf); \
+	ehci_dbg (ehci, "%s\n", _buf); \
 }
 
-#define dbg_port(hcd, label, port, status) { \
+#define dbg_port(ehci, label, port, status) { \
 	char _buf [80]; \
 	dbg_port_buf (_buf, sizeof _buf, label, port, status); \
-	dbg ("%s", _buf); \
+	ehci_dbg (ehci, "%s\n", _buf); \
 }
 
 /*-------------------------------------------------------------------------*/
@@ -272,6 +304,7 @@
 static void qh_lines (struct ehci_qh *qh, char **nextp, unsigned *sizep)
 {
 	u32			scratch;
+	u32			hw_curr;
 	struct list_head	*entry;
 	struct ehci_qtd		*td;
 	unsigned		temp;
@@ -279,20 +312,23 @@
 	char			*next = *nextp;
 
 	scratch = cpu_to_le32p (&qh->hw_info1);
-	temp = snprintf (next, size, "qh/%p dev%d %cs ep%d %08x %08x",
+	hw_curr = cpu_to_le32p (&qh->hw_current);
+	temp = snprintf (next, size,
+			"qh/%p dev%d %cs ep%d %08x %08x (%08x %08x)",
 			qh, scratch & 0x007f,
 			speed_char (scratch),
 			(scratch >> 8) & 0x000f,
-			scratch, cpu_to_le32p (&qh->hw_info2));
+			scratch, cpu_to_le32p (&qh->hw_info2),
+			hw_curr, cpu_to_le32p (&qh->hw_token));
 	size -= temp;
 	next += temp;
 
 	list_for_each (entry, &qh->qtd_list) {
-		td = list_entry (entry, struct ehci_qtd,
-				qtd_list);
+		td = list_entry (entry, struct ehci_qtd, qtd_list);
 		scratch = cpu_to_le32p (&td->hw_token);
 		temp = snprintf (next, size,
-				"\n\ttd/%p %s len=%d %08x urb %p",
+				"\n\t%std/%p %s len=%d %08x urb %p",
+				(hw_curr == td->qtd_dma) ? "*" : "",
 				td, ({ char *tmp;
 				 switch ((scratch>>8)&0x03) {
 				 case 0: tmp = "out"; break;
@@ -335,12 +371,8 @@
 	 * one QH per line, and TDs we know about
 	 */
 	spin_lock_irqsave (&ehci->lock, flags);
-	if (ehci->async) {
-		qh = ehci->async;
-		do {
-			qh_lines (qh, &next, &size);
-		} while ((qh = qh->qh_next.qh) != ehci->async);
-	}
+	for (qh = ehci->async->qh_next.qh; qh; qh = qh->qh_next.qh)
+		qh_lines (qh, &next, &size);
 	if (ehci->reclaim) {
 		temp = snprintf (next, size, "\nreclaim =\n");
 		size -= temp;
@@ -552,8 +584,8 @@
 	size -= temp;
 	next += temp;
 
-	temp = snprintf (next, size, "complete %ld unlink %ld qpatch %ld\n",
-		ehci->stats.complete, ehci->stats.unlink, ehci->stats.qpatch);
+	temp = snprintf (next, size, "complete %ld unlink %ld\n",
+		ehci->stats.complete, ehci->stats.unlink);
 	size -= temp;
 	next += temp;
 #endif
diff -urN linux-2.4.21-pre2/drivers/usb/hcd/ehci-hcd.c linux-2.4.21-pre3/drivers/usb/hcd/ehci-hcd.c
--- linux-2.4.21-pre2/drivers/usb/hcd/ehci-hcd.c	Mon Jan  6 16:32:29 2003
+++ linux-2.4.21-pre3/drivers/usb/hcd/ehci-hcd.c	Mon Jan  6 16:32:32 2003
@@ -63,13 +63,14 @@
  * First was PCMCIA, like ISA; then CardBus, which is PCI.
  * Next comes "CardBay", using USB 2.0 signals.
  *
- * Contains additional contributions by: Brad Hards, Rory Bolt, and more.
+ * Contains additional contributions by Brad Hards, Rory Bolt, and others.
  * Special thanks to Intel and VIA for providing host controllers to
  * test this driver on, and Cypress (including In-System Design) for
  * providing early devices for those host controllers to talk to!
  *
  * HISTORY:
  *
+ * 2002-11-29	Correct handling for hw async_next register.
  * 2002-08-06	Handling for bulk and interrupt transfers is mostly shared;
  *	only scheduling is different, no arbitrary limitations.
  * 2002-07-25	Sanity check PCI reads, mostly for better cardbus support,
@@ -92,7 +93,7 @@
  * 2001-June	Works with usb-storage and NEC EHCI on 2.4
  */
 
-#define DRIVER_VERSION "2002-Sep-23"
+#define DRIVER_VERSION "2002-Dec-20"
 #define DRIVER_AUTHOR "David Brownell"
 #define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver"
 
@@ -116,19 +117,13 @@
 #define	EHCI_TUNE_MULT_TT	1
 
 #define EHCI_WATCHDOG_JIFFIES	(HZ/100)	/* arbitrary; ~10 msec */
-#define EHCI_ASYNC_JIFFIES	(HZ/3)		/* async idle timeout */
+#define EHCI_ASYNC_JIFFIES	(HZ/20)		/* async idle timeout */
 
 /* Initial IRQ latency:  lower than default */
 static int log2_irq_thresh = 0;		// 0 to 6
 MODULE_PARM (log2_irq_thresh, "i");
 MODULE_PARM_DESC (log2_irq_thresh, "log2 IRQ latency, 1-64 microframes");
 
-/* allow irqs at least every N URB completions */
-static int max_completions = 16;
-MODULE_PARM (max_completions, "i");
-MODULE_PARM_DESC (max_completions,
-		"limit for urb completions called with irqs disenabled");
-
 #define	INTR_MASK (STS_IAA | STS_FATAL | STS_ERR | STS_INT)
 
 /*-------------------------------------------------------------------------*/
@@ -202,7 +197,7 @@
 	dbg_cmd (ehci, "reset", command);
 	writel (command, &ehci->regs->command);
 	ehci->hcd.state = USB_STATE_HALT;
-	return handshake (&ehci->regs->command, CMD_RESET, 0, 250);
+	return handshake (&ehci->regs->command, CMD_RESET, 0, 250 * 1000);
 }
 
 /* idle the controller (from running) */
@@ -217,7 +212,7 @@
 
 	/* wait for any schedule enables/disables to take effect */
 	temp = 0;
-	if (ehci->async)
+	if (ehci->async->qh_next.qh)
 		temp = STS_ASS;
 	if (ehci->next_uframe != -1)
 		temp |= STS_PSS;
@@ -250,9 +245,7 @@
 
 /*-------------------------------------------------------------------------*/
 
-static void ehci_tasklet (unsigned long param);
-
-static void ehci_irq (struct usb_hcd *hcd);
+static void ehci_work(struct ehci_hcd *ehci, struct pt_regs *regs);
 
 static void ehci_watchdog (unsigned long param)
 {
@@ -260,10 +253,25 @@
 	unsigned long		flags;
 
 	spin_lock_irqsave (&ehci->lock, flags);
-	/* guard against lost IAA, which wedges everything */
-	ehci_irq (&ehci->hcd);
- 	/* unlink the last qh after it's idled a while */
- 	if (ehci->async_idle) {
+
+	/* lost IAA irqs wedge things badly; seen with a vt8235 */
+	if (ehci->reclaim) {
+		u32		status = readl (&ehci->regs->status);
+
+		if (status & STS_IAA) {
+			ehci_vdbg (ehci, "lost IAA\n");
+			writel (STS_IAA, &ehci->regs->status);
+			ehci->reclaim_ready = 1;
+		}
+	}
+
+	ehci_work (ehci, NULL);
+	if (ehci->reclaim && !timer_pending (&ehci->watchdog))
+		mod_timer (&ehci->watchdog,
+				jiffies + EHCI_WATCHDOG_JIFFIES);
+
+ 	/* stop async processing after it's idled a while */
+	else if (ehci->async_idle) {
  		start_unlink_async (ehci, ehci->async);
  		ehci->async_idle = 0;
 	}
@@ -289,12 +297,12 @@
 			pci_read_config_dword (ehci->hcd.pdev, where, &cap);
 		} while ((cap & (1 << 16)) && msec);
 		if (cap & (1 << 16)) {
-			info ("BIOS handoff failed (%d, %04x)", where, cap);
+			ehci_err (ehci, "BIOS handoff failed (%d, %04x)\n",
+				where, cap);
 			return 1;
 		} 
-		dbg ("BIOS handoff succeeded");
-	} else
-		dbg ("BIOS handoff not needed");
+		ehci_dbg (ehci, "BIOS handoff succeeded\n");
+	}
 	return 0;
 }
 
@@ -325,14 +333,14 @@
 		u32		cap;
 
 		pci_read_config_dword (ehci->hcd.pdev, temp, &cap);
-		dbg ("capability %04x at %02x", cap, temp);
+		ehci_dbg (ehci, "capability %04x at %02x\n", cap, temp);
 		switch (cap & 0xff) {
 		case 1:			/* BIOS/SMM/... handoff */
 			if (bios_handoff (ehci, temp, cap) != 0)
 				return -EOPNOTSUPP;
 			break;
 		case 0:			/* illegal reserved capability */
-			warn ("illegal capability!");
+			ehci_warn (ehci, "illegal capability!\n");
 			cap = 0;
 			/* FALLTHROUGH */
 		default:		/* unknown */
@@ -362,7 +370,6 @@
 	else					// N microframes cached
 		ehci->i_thresh = 2 + HCC_ISOC_THRES (hcc_params);
 
-	ehci->async = 0;
 	ehci->reclaim = 0;
 	ehci->next_uframe = -1;
 
@@ -377,6 +384,22 @@
 	writel (ehci->periodic_dma, &ehci->regs->frame_list);
 
 	/*
+	 * dedicate a qh for the async ring head, since we couldn't unlink
+	 * a 'real' qh without stopping the async schedule [4.8].  use it
+	 * as the 'reclamation list head' too.
+	 * its dummy is used in hw_alt_next of many tds, to prevent the qh
+	 * from automatically advancing to the next td after short reads.
+	 */
+	ehci->async->qh_next.qh = 0;
+	ehci->async->hw_next = QH_NEXT (ehci->async->qh_dma);
+	ehci->async->hw_info1 = cpu_to_le32 (QH_HEAD);
+	ehci->async->hw_token = cpu_to_le32 (QTD_STS_HALT);
+	ehci->async->hw_qtd_next = EHCI_LIST_END;
+	ehci->async->qh_state = QH_STATE_LINKED;
+	ehci->async->hw_alt_next = QTD_NEXT (ehci->async->dummy->qtd_dma);
+	writel ((u32)ehci->async->qh_dma, &ehci->regs->async_next);
+
+	/*
 	 * hcc_params controls whether ehci->regs->segment must (!!!)
 	 * be used; it constrains QH/ITD/SITD and QTD locations.
 	 * pci_pool consistent memory always uses segment zero.
@@ -390,7 +413,7 @@
 	if (HCC_64BIT_ADDR (hcc_params)) {
 		writel (0, &ehci->regs->segment);
 		if (!pci_set_dma_mask (ehci->hcd.pdev, 0xffffffffffffffffULL))
-			info ("enabled 64bit PCI DMA (DAC)");
+			ehci_info (ehci, "enabled 64bit PCI DMA\n");
 	}
 
 	/* clear interrupt enables, set irq latency */
@@ -409,9 +432,6 @@
 
 	/* set async sleep time = 10 us ... ? */
 
-	ehci->tasklet.func = ehci_tasklet;
-	ehci->tasklet.data = (unsigned long) ehci;
-
 	init_timer (&ehci->watchdog);
 	ehci->watchdog.function = ehci_watchdog;
 	ehci->watchdog.data = (unsigned long) ehci;
@@ -437,10 +457,10 @@
         /* PCI Serial Bus Release Number is at 0x60 offset */
 	pci_read_config_byte (hcd->pdev, 0x60, &tempbyte);
 	temp = readw (&ehci->caps->hci_version);
-	info ("USB %x.%x support enabled, EHCI rev %x.%02x, %s %s",
-	      ((tempbyte & 0xf0)>>4), (tempbyte & 0x0f),
-	       temp >> 8, temp & 0xff,
-	       hcd_name, DRIVER_VERSION);
+	ehci_info (ehci,
+		"USB %x.%x enabled, EHCI %x.%02x, driver %s\n",
+		((tempbyte & 0xf0)>>4), (tempbyte & 0x0f),
+		temp >> 8, temp & 0xff, DRIVER_VERSION);
 
 	/*
 	 * From here on, khubd concurrently accesses the root
@@ -472,15 +492,16 @@
 {
 	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
 
-	dbg ("%s: stop", hcd_to_bus (hcd)->bus_name);
+	ehci_dbg (ehci, "stop\n");
 
 	/* no more interrupts ... */
 	if (hcd->state == USB_STATE_RUNNING)
 		ehci_ready (ehci);
-	if (in_interrupt ())		/* should not happen!! */
-		err ("stopped %s!", RUN_CONTEXT);
-	else
-		del_timer_sync (&ehci->watchdog);
+	if (in_interrupt ()) {		/* must not happen!! */
+		ehci_err (ehci, "stopped in_interrupt!\n");
+		return;
+	}
+	del_timer_sync (&ehci->watchdog);
 	ehci_reset (ehci);
 
 	/* let companion controllers work when we aren't */
@@ -489,15 +510,16 @@
 	remove_debug_files (ehci);
 
 	/* root hub is shut down separately (first, when possible) */
-	tasklet_disable (&ehci->tasklet);
-	ehci_tasklet ((unsigned long) ehci);
+	spin_lock_irq (&ehci->lock);
+	ehci_work (ehci, NULL);
+	spin_unlock_irq (&ehci->lock);
 	ehci_mem_cleanup (ehci);
 
 #ifdef	EHCI_STATS
-	dbg ("irq normal %ld err %ld reclaim %ld",
+	ehci_dbg (ehci, "irq normal %ld err %ld reclaim %ld\n",
 		ehci->stats.normal, ehci->stats.error, ehci->stats.reclaim);
-	dbg ("complete %ld unlink %ld qpatch %ld",
-		ehci->stats.complete, ehci->stats.unlink, ehci->stats.qpatch);
+	ehci_dbg (ehci, "complete %ld unlink %ld\n",
+		ehci->stats.complete, ehci->stats.unlink);
 #endif
 
 	dbg_status (ehci, "ehci_stop completed", readl (&ehci->regs->status));
@@ -598,27 +620,21 @@
 /*-------------------------------------------------------------------------*/
 
 /*
- * tasklet scheduled by some interrupts and other events
- * calls driver completion functions ... but not in_irq()
+ * ehci_work is called from some interrupts, timers, and so on.
+ * it calls driver completion functions, after dropping ehci->lock.
  */
-static void ehci_tasklet (unsigned long param)
+static void ehci_work (struct ehci_hcd *ehci, struct pt_regs *regs)
 {
-	struct ehci_hcd		*ehci = (struct ehci_hcd *) param;
-
-	spin_lock_irq (&ehci->lock);
-
 	if (ehci->reclaim_ready)
-		end_unlink_async (ehci);
-	scan_async (ehci);
+		end_unlink_async (ehci, regs);
+	scan_async (ehci, regs);
 	if (ehci->next_uframe != -1)
-		scan_periodic (ehci);
-
-	spin_unlock_irq (&ehci->lock);
+		scan_periodic (ehci, regs);
 }
 
 /*-------------------------------------------------------------------------*/
 
-static void ehci_irq (struct usb_hcd *hcd)
+static void ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs)
 {
 	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
 	u32			status = readl (&ehci->regs->status);
@@ -626,7 +642,7 @@
 
 	/* e.g. cardbus physical eject */
 	if (status == ~(u32) 0) {
-		dbg ("%s: device removed!", hcd_to_bus (hcd)->bus_name);
+		ehci_dbg (ehci, "device removed\n");
 		goto dead;
 	}
 
@@ -634,6 +650,8 @@
 	if (!status)			/* irq sharing? */
 		return;
 
+	spin_lock (&ehci->lock);
+
 	/* clear (just) interrupts */
 	writel (status, &ehci->regs->status);
 	readl (&ehci->regs->command);	/* unblock posted write */
@@ -664,8 +682,7 @@
 
 	/* PCI errors [4.15.2.4] */
 	if (unlikely ((status & STS_FATAL) != 0)) {
-		err ("%s: fatal error, state %x",
-			hcd_to_bus (hcd)->bus_name, hcd->state);
+		ehci_err (ehci, "fatal error\n");
 dead:
 		ehci_reset (ehci);
 		/* generic layer kills/unlinks all urbs, then
@@ -674,9 +691,9 @@
 		bh = 1;
 	}
 
-	/* most work doesn't need to be in_irq() */
-	if (likely (bh == 1))
-		tasklet_schedule (&ehci->tasklet);
+	if (bh)
+		ehci_work (ehci, regs);
+	spin_unlock (&ehci->lock);
 }
 
 /*-------------------------------------------------------------------------*/
@@ -737,52 +754,52 @@
 static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
 {
 	struct ehci_hcd		*ehci = hcd_to_ehci (hcd);
-	struct ehci_qh		*qh = (struct ehci_qh *) urb->hcpriv;
+	struct ehci_qh		*qh;
 	unsigned long		flags;
+	int			maybe_irq = 1;
 
-	dbg ("%s urb_dequeue %p qh %p state %d",
-		hcd_to_bus (hcd)->bus_name, urb, qh, qh->qh_state);
-
+	spin_lock_irqsave (&ehci->lock, flags);
 	switch (usb_pipetype (urb->pipe)) {
 	// case PIPE_CONTROL:
 	// case PIPE_BULK:
 	default:
-		spin_lock_irqsave (&ehci->lock, flags);
-		if (ehci->reclaim) {
-			dbg ("dq %p: reclaim = %p, %s",
-				qh, ehci->reclaim, RUN_CONTEXT);
-			if (qh == ehci->reclaim) {
-				/* unlinking qh for another queued urb? */
-				spin_unlock_irqrestore (&ehci->lock, flags);
-				return 0;
-			}
-			if (in_interrupt ()) {
-				spin_unlock_irqrestore (&ehci->lock, flags);
-				return -EAGAIN;
-			}
-			while (qh->qh_state == QH_STATE_LINKED
-					&& ehci->reclaim
-					&& ehci->hcd.state != USB_STATE_HALT
-					) {
-				spin_unlock_irqrestore (&ehci->lock, flags);
-				/* let pending unlinks complete */
-				wait_ms (1);
-				spin_lock_irqsave (&ehci->lock, flags);
+		qh = (struct ehci_qh *) urb->hcpriv;
+		if (!qh)
+			break;
+		while (qh->qh_state == QH_STATE_LINKED
+				&& ehci->reclaim
+				&& HCD_IS_RUNNING (ehci->hcd.state)
+				) {
+			spin_unlock_irqrestore (&ehci->lock, flags);
+
+			if (maybe_irq) {
+				if (in_interrupt ())
+					return -EAGAIN;
+				maybe_irq = 0;
 			}
+			/* let pending unlinks complete, so this can start */
+			wait_ms (1);
+
+			spin_lock_irqsave (&ehci->lock, flags);
 		}
+		if (!HCD_IS_RUNNING (ehci->hcd.state) && ehci->reclaim)
+			end_unlink_async (ehci, NULL);
+
+		/* something else might have unlinked the qh by now */
 		if (qh->qh_state == QH_STATE_LINKED)
 			start_unlink_async (ehci, qh);
-		spin_unlock_irqrestore (&ehci->lock, flags);
 		break;
 
 	case PIPE_INTERRUPT:
-		spin_lock_irqsave (&ehci->lock, flags);
+		qh = (struct ehci_qh *) urb->hcpriv;
+		if (!qh)
+			break;
 		if (qh->qh_state == QH_STATE_LINKED) {
 			/* messy, can spin or block a microframe ... */
 			intr_deschedule (ehci, qh, 1);
 			/* qh_state == IDLE */
 		}
-		qh_completions (ehci, qh);
+		qh_completions (ehci, qh, NULL);
 
 		/* reschedule QH iff another request is queued */
 		if (!list_empty (&qh->qtd_list)
@@ -800,7 +817,6 @@
 			}
 			return status;
 		}
-		spin_unlock_irqrestore (&ehci->lock, flags);
 		break;
 
 	case PIPE_ISOCHRONOUS:
@@ -811,6 +827,7 @@
 		urb->transfer_flags |= EHCI_STATE_UNLINK;
 		break;
 	}
+	spin_unlock_irqrestore (&ehci->lock, flags);
 	return 0;
 }
 
@@ -868,11 +885,11 @@
 			 */
 			while (qh->qh_state == QH_STATE_LINKED
 					&& ehci->reclaim
-					&& ehci->hcd.state != USB_STATE_HALT
+					&& HCD_IS_RUNNING (ehci->hcd.state)
 					) {
 				spin_unlock_irqrestore (&ehci->lock, flags);
 				/* wait_ms() won't spin, we're a thread;
-				 * and we know IRQ+tasklet can progress
+				 * and we know IRQ/timer/... can progress
 				 */
 				wait_ms (1);
 				spin_lock_irqsave (&ehci->lock, flags);
diff -urN linux-2.4.21-pre2/drivers/usb/hcd/ehci-hub.c linux-2.4.21-pre3/drivers/usb/hcd/ehci-hub.c
--- linux-2.4.21-pre2/drivers/usb/hcd/ehci-hub.c	Mon Jan  6 16:32:29 2003
+++ linux-2.4.21-pre3/drivers/usb/hcd/ehci-hub.c	Mon Jan  6 16:32:32 2003
@@ -40,18 +40,15 @@
 
 	/* if reset finished and it's still not enabled -- handoff */
 	if (!(port_status & PORT_PE)) {
-		dbg ("%s port %d full speed, give to companion, 0x%x",
-			hcd_to_bus (&ehci->hcd)->bus_name,
-			index + 1, port_status);
+		ehci_dbg (ehci, "port %d full speed --> companion\n",
+			index + 1);
 
 		// what happens if HCS_N_CC(params) == 0 ?
 		port_status |= PORT_OWNER;
 		writel (port_status, &ehci->regs->port_status [index]);
 
 	} else
-		dbg ("%s port %d high speed",
-			hcd_to_bus (&ehci->hcd)->bus_name,
-			index + 1);
+		ehci_dbg (ehci, "port %d high speed\n", index + 1);
 
 	return port_status;
 }
@@ -277,7 +274,7 @@
 #ifndef	EHCI_VERBOSE_DEBUG
 	if (status & ~0xffff)	/* only if wPortChange is interesting */
 #endif
-		dbg_port (hcd, "GetStatus", wIndex + 1, temp);
+		dbg_port (ehci, "GetStatus", wIndex + 1, temp);
 		// we "know" this alignment is good, caller used kmalloc()...
 		*((u32 *) buf) = cpu_to_le32 (status);
 		break;
@@ -313,14 +310,12 @@
 			/* line status bits may report this as low speed */
 			if ((temp & (PORT_PE|PORT_CONNECT)) == PORT_CONNECT
 					&& PORT_USB11 (temp)) {
-				dbg ("%s port %d low speed, give to companion",
-					hcd_to_bus (&ehci->hcd)->bus_name,
+				ehci_dbg (ehci,
+					"port %d low speed --> companion\n",
 					wIndex + 1);
 				temp |= PORT_OWNER;
 			} else {
-				vdbg ("%s port %d reset",
-					hcd_to_bus (&ehci->hcd)->bus_name,
-					wIndex + 1);
+				ehci_vdbg (ehci, "port %d reset\n", wIndex + 1);
 				temp |= PORT_RESET;
 				temp &= ~PORT_PE;
 
diff -urN linux-2.4.21-pre2/drivers/usb/hcd/ehci-mem.c linux-2.4.21-pre3/drivers/usb/hcd/ehci-mem.c
--- linux-2.4.21-pre2/drivers/usb/hcd/ehci-mem.c	Fri Aug  2 17:39:44 2002
+++ linux-2.4.21-pre3/drivers/usb/hcd/ehci-mem.c	Mon Jan  6 16:32:32 2003
@@ -58,6 +58,15 @@
 
 /* Allocate the key transfer structures from the previously allocated pool */
 
+static inline void ehci_qtd_init (struct ehci_qtd *qtd, dma_addr_t dma)
+{
+	memset (qtd, 0, sizeof *qtd);
+	qtd->qtd_dma = dma;
+	qtd->hw_next = EHCI_LIST_END;
+	qtd->hw_alt_next = EHCI_LIST_END;
+	INIT_LIST_HEAD (&qtd->qtd_list);
+}
+
 static struct ehci_qtd *ehci_qtd_alloc (struct ehci_hcd *ehci, int flags)
 {
 	struct ehci_qtd		*qtd;
@@ -65,11 +74,9 @@
 
 	qtd = pci_pool_alloc (ehci->qtd_pool, flags, &dma);
 	if (qtd != 0) {
-		memset (qtd, 0, sizeof *qtd);
-		qtd->qtd_dma = dma;
-		qtd->hw_next = EHCI_LIST_END;
-		qtd->hw_alt_next = EHCI_LIST_END;
-		INIT_LIST_HEAD (&qtd->qtd_list);
+		ehci_qtd_init (qtd, dma);
+		if (ehci->async)
+			qtd->hw_alt_next = ehci->async->hw_alt_next;
 	}
 	return qtd;
 }
@@ -87,12 +94,21 @@
 
 	qh = (struct ehci_qh *)
 		pci_pool_alloc (ehci->qh_pool, flags, &dma);
-	if (qh) {
-		memset (qh, 0, sizeof *qh);
-		atomic_set (&qh->refcount, 1);
-		qh->qh_dma = dma;
-		// INIT_LIST_HEAD (&qh->qh_list);
-		INIT_LIST_HEAD (&qh->qtd_list);
+	if (!qh)
+		return qh;
+
+	memset (qh, 0, sizeof *qh);
+	atomic_set (&qh->refcount, 1);
+	qh->qh_dma = dma;
+	// INIT_LIST_HEAD (&qh->qh_list);
+	INIT_LIST_HEAD (&qh->qtd_list);
+
+	/* dummy td enables safe urb queuing */
+	qh->dummy = ehci_qtd_alloc (ehci, flags);
+	if (qh->dummy == 0) {
+		ehci_dbg (ehci, "no dummy td\n");
+		pci_pool_free (ehci->qh_pool, qh, qh->qh_dma);
+		qh = 0;
 	}
 	return qh;
 }
@@ -100,21 +116,21 @@
 /* to share a qh (cpu threads, or hc) */
 static inline struct ehci_qh *qh_get (/* ehci, */ struct ehci_qh *qh)
 {
-	// dbg ("get %p (%d++)", qh, qh->refcount.counter);
 	atomic_inc (&qh->refcount);
 	return qh;
 }
 
 static void qh_put (struct ehci_hcd *ehci, struct ehci_qh *qh)
 {
-	// dbg ("put %p (--%d)", qh, qh->refcount.counter);
 	if (!atomic_dec_and_test (&qh->refcount))
 		return;
 	/* clean qtds first, and know this is not linked */
 	if (!list_empty (&qh->qtd_list) || qh->qh_next.ptr) {
-		dbg ("unused qh not empty!");
+		ehci_dbg (ehci, "unused qh not empty!\n");
 		BUG ();
 	}
+	if (qh->dummy)
+		ehci_qtd_free (ehci, qh->dummy);
 	pci_pool_free (ehci->qh_pool, qh, qh->qh_dma);
 }
 
@@ -127,6 +143,10 @@
 
 static void ehci_mem_cleanup (struct ehci_hcd *ehci)
 {
+	if (ehci->async)
+		qh_put (ehci, ehci->async);
+	ehci->async = 0;
+
 	/* PCI consistent memory and pools */
 	if (ehci->qtd_pool)
 		pci_pool_destroy (ehci->qtd_pool);
@@ -169,21 +189,21 @@
 			4096 /* can't cross 4K */,
 			flags);
 	if (!ehci->qtd_pool) {
-		dbg ("no qtd pool");
-		ehci_mem_cleanup (ehci);
-		return -ENOMEM;
+		goto fail;
 	}
 
-	/* QH for control/bulk/intr transfers */
+	/* QHs for control/bulk/intr transfers */
 	ehci->qh_pool = pci_pool_create ("ehci_qh", ehci->hcd.pdev,
 			sizeof (struct ehci_qh),
 			32 /* byte alignment (for hw parts) */,
 			4096 /* can't cross 4K */,
 			flags);
 	if (!ehci->qh_pool) {
-		dbg ("no qh pool");
-		ehci_mem_cleanup (ehci);
-		return -ENOMEM;
+		goto fail;
+	}
+	ehci->async = ehci_qh_alloc (ehci, flags);
+	if (!ehci->async) {
+		goto fail;
 	}
 
 	/* ITD for high speed ISO transfers */
@@ -193,9 +213,7 @@
 			4096 /* can't cross 4K */,
 			flags);
 	if (!ehci->itd_pool) {
-		dbg ("no itd pool");
-		ehci_mem_cleanup (ehci);
-		return -ENOMEM;
+		goto fail;
 	}
 
 	/* SITD for full/low speed split ISO transfers */
@@ -205,9 +223,7 @@
 			4096 /* can't cross 4K */,
 			flags);
 	if (!ehci->sitd_pool) {
-		dbg ("no sitd pool");
-		ehci_mem_cleanup (ehci);
-		return -ENOMEM;
+		goto fail;
 	}
 
 	/* Hardware periodic table */
@@ -216,9 +232,7 @@
 			ehci->periodic_size * sizeof (u32),
 			&ehci->periodic_dma);
 	if (ehci->periodic == 0) {
-		dbg ("no hw periodic table");
-		ehci_mem_cleanup (ehci);
-		return -ENOMEM;
+		goto fail;
 	}
 	for (i = 0; i < ehci->periodic_size; i++)
 		ehci->periodic [i] = EHCI_LIST_END;
@@ -226,11 +240,14 @@
 	/* software shadow of hardware table */
 	ehci->pshadow = kmalloc (ehci->periodic_size * sizeof (void *), flags);
 	if (ehci->pshadow == 0) {
-		dbg ("no shadow periodic table");
-		ehci_mem_cleanup (ehci);
-		return -ENOMEM;
+		goto fail;
 	}
 	memset (ehci->pshadow, 0, ehci->periodic_size * sizeof (void *));
 
 	return 0;
+
+fail:
+	ehci_dbg (ehci, "couldn't init memory\n");
+	ehci_mem_cleanup (ehci);
+	return -ENOMEM;
 }
diff -urN linux-2.4.21-pre2/drivers/usb/hcd/ehci-q.c linux-2.4.21-pre3/drivers/usb/hcd/ehci-q.c
--- linux-2.4.21-pre2/drivers/usb/hcd/ehci-q.c	Mon Jan  6 16:32:29 2003
+++ linux-2.4.21-pre3/drivers/usb/hcd/ehci-q.c	Mon Jan  6 16:32:32 2003
@@ -73,11 +73,6 @@
 	qtd->hw_token = cpu_to_le32 ((count << 16) | token);
 	qtd->length = count;
 
-#if 0
-	vdbg ("  qtd_fill %p, token %8x bytes %d dma %x",
-		qtd, le32_to_cpu (qtd->hw_token), count, qtd->hw_buf [0]);
-#endif
-
 	return count;
 }
 
@@ -85,11 +80,12 @@
 
 /* update halted (but potentially linked) qh */
 
-static inline void qh_update (struct ehci_qh *qh, struct ehci_qtd *qtd)
+static inline void
+qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd)
 {
 	qh->hw_current = 0;
 	qh->hw_qtd_next = QTD_NEXT (qtd->qtd_dma);
-	qh->hw_alt_next = EHCI_LIST_END;
+	qh->hw_alt_next = ehci->async->hw_alt_next;
 
 	/* HC must see latest qtd and qh data before we clear ACTIVE+HALT */
 	wmb ();
@@ -98,14 +94,29 @@
 
 /*-------------------------------------------------------------------------*/
 
-static inline void qtd_copy_status (struct urb *urb, size_t length, u32 token)
+#define IS_SHORT_READ(token) (QTD_LENGTH (token) != 0 && QTD_PID (token) == 1)
+
+static inline void qtd_copy_status (
+	struct ehci_hcd *ehci,
+	struct urb *urb,
+	size_t length,
+	u32 token
+)
 {
 	/* count IN/OUT bytes, not SETUP (even short packets) */
 	if (likely (QTD_PID (token) != 2))
 		urb->actual_length += length - QTD_LENGTH (token);
 
 	/* don't modify error codes */
-	if (unlikely (urb->status == -EINPROGRESS && (token & QTD_STS_HALT))) {
+	if (unlikely (urb->status != -EINPROGRESS))
+		return;
+
+	/* force cleanup after short read; not always an error */
+	if (unlikely (IS_SHORT_READ (token)))
+		urb->status = -EREMOTEIO;
+
+	/* serious "can't proceed" faults reported by the hardware */
+	if (token & QTD_STS_HALT) {
 		if (token & QTD_STS_BABBLE) {
 			/* FIXME "must" disable babbling device's port too */
 			urb->status = -EOVERFLOW;
@@ -130,8 +141,9 @@
 		else	/* unknown */
 			urb->status = -EPROTO;
 
-		dbg ("ep %d-%s qtd token %08x --> status %d",
-			/* devpath */
+		ehci_vdbg (ehci,
+			"dev%d ep%d%s qtd token %08x --> status %d\n",
+			usb_pipedevice (urb->pipe),
 			usb_pipeendpoint (urb->pipe),
 			usb_pipein (urb->pipe) ? "in" : "out",
 			token, urb->status);
@@ -158,7 +170,8 @@
 	}
 }
 
-static void ehci_urb_done (struct ehci_hcd *ehci, struct urb *urb)
+static void
+ehci_urb_done (struct ehci_hcd *ehci, struct urb *urb, struct pt_regs *regs)
 {
 #ifdef	INTR_AUTOMAGIC
 	struct urb		*resubmit = 0;
@@ -185,27 +198,31 @@
 #endif
 		}
 		qh_put (ehci, qh);
-		urb->hcpriv = 0;
 	}
 
-	if (likely (urb->status == -EINPROGRESS)) {
-		if (urb->actual_length != urb->transfer_buffer_length
-				&& (urb->transfer_flags & URB_SHORT_NOT_OK))
-			urb->status = -EREMOTEIO;
-		else
+	spin_lock (&urb->lock);
+	urb->hcpriv = 0;
+	switch (urb->status) {
+	case -EINPROGRESS:		/* success */
+		urb->status = 0;
+	default:			/* fault */
+		COUNT (ehci->stats.complete);
+		break;
+	case -EREMOTEIO:		/* fault or normal */
+		if (!(urb->transfer_flags & URB_SHORT_NOT_OK))
 			urb->status = 0;
-	}
-
-	if (likely (urb->status == 0))
 		COUNT (ehci->stats.complete);
-	else if (urb->status == -ECONNRESET || urb->status == -ENOENT)
+		break;
+	case -ECONNRESET:		/* canceled */
+	case -ENOENT:
 		COUNT (ehci->stats.unlink);
-	else
-		COUNT (ehci->stats.error);
+		break;
+	}
+	spin_unlock (&urb->lock);
 
 	/* complete() can reenter this HCD */
 	spin_unlock (&ehci->lock);
-	usb_hcd_giveback_urb (&ehci->hcd, urb);
+	usb_hcd_giveback_urb (&ehci->hcd, urb, regs);
 
 #ifdef	INTR_AUTOMAGIC
 	if (resubmit && ((urb->status == -ENOENT)
@@ -235,139 +252,115 @@
  * Chases up to qh->hw_current.  Returns number of completions called,
  * indicating how much "real" work we did.
  */
+#define HALT_BIT cpu_to_le32(QTD_STS_HALT)
 static unsigned
-qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
+qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, struct pt_regs *regs)
 {
-	struct ehci_qtd		*qtd, *last;
-	struct list_head	*next, *qtd_list = &qh->qtd_list;
-	int			unlink = 0, halted = 0;
+	struct ehci_qtd		*last = 0;
+	struct list_head	*entry, *tmp;
+	int			stopped = 0;
 	unsigned		count = 0;
 
-	if (unlikely (list_empty (qtd_list)))
+	if (unlikely (list_empty (&qh->qtd_list)))
 		return count;
 
-	/* scan QTDs till end of list, or we reach an active one */
-	for (qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list),
-			    	last = 0, next = 0;
-			next != qtd_list;
-			last = qtd, qtd = list_entry (next,
-						struct ehci_qtd, qtd_list)) {
-		struct urb	*urb = qtd->urb;
+	/* remove de-activated QTDs from front of queue.
+	 * after faults (including short reads), cleanup this urb
+	 * then let the queue advance.
+	 * if queue is stopped, handles unlinks.
+	 */
+	list_for_each_safe (entry, tmp, &qh->qtd_list) {
+		struct ehci_qtd	*qtd;
+		struct urb	*urb;
 		u32		token = 0;
 
+		qtd = list_entry (entry, struct ehci_qtd, qtd_list);
+		urb = qtd->urb;
+
 		/* clean up any state from previous QTD ...*/
 		if (last) {
 			if (likely (last->urb != urb)) {
-				ehci_urb_done (ehci, last->urb);
+				ehci_urb_done (ehci, last->urb, regs);
 				count++;
 			}
-
-			/* qh overlays can have HC's old cached copies of
-			 * next qtd ptrs, if an URB was queued afterwards.
-			 */
-			if (cpu_to_le32 (last->qtd_dma) == qh->hw_current
-					&& last->hw_next != qh->hw_qtd_next) {
-				qh->hw_alt_next = last->hw_alt_next;
-				qh->hw_qtd_next = last->hw_next;
-				COUNT (ehci->stats.qpatch);
-			}
-
 			ehci_qtd_free (ehci, last);
 			last = 0;
 		}
-		next = qtd->qtd_list.next;
 
-		/* QTDs at tail may be active if QH+HC are running,
-		 * or when unlinking some urbs queued to this QH
-		 */
+		/* hardware copies qtd out of qh overlay */
+		rmb ();
 		token = le32_to_cpu (qtd->hw_token);
-		halted = halted
-			|| (__constant_cpu_to_le32 (QTD_STS_HALT)
-				& qh->hw_token) != 0
-			|| (ehci->hcd.state == USB_STATE_HALT)
-			|| (qh->qh_state == QH_STATE_IDLE);
-
-		// FIXME Remove the automagic unlink mode.
-		// Drivers can now clean up safely; it's their job.
-		//
-		// FIXME Removing it should fix the short read scenarios
-		// with "huge" urb data (more than one 16+KByte td) with
-		// the short read someplace other than the last data TD.
-		// Except the control case: 'retrigger' status ACKs.
-
-		/* fault: unlink the rest, since this qtd saw an error? */
-		if (unlikely ((token & QTD_STS_HALT) != 0)) {
-			unlink = 1;
-			/* status copied below */
-
-		/* QH halts only because of fault (above) or unlink (here). */
-		} else if (unlikely (halted != 0)) {
-
-			/* unlinking everything because of HC shutdown? */
-			if (ehci->hcd.state == USB_STATE_HALT) {
-				unlink = 1;
-
-			/* explicit unlink, maybe starting here? */
-			} else if (qh->qh_state == QH_STATE_IDLE
-					&& (urb->status == -ECONNRESET
-						|| urb->status == -ESHUTDOWN
-						|| urb->status == -ENOENT)) {
-				unlink = 1;
-
-			/* QH halted to unlink urbs _after_ this?  */
-			} else if (!unlink && (token & QTD_STS_ACTIVE) != 0) {
-				qtd = 0;
-				continue;
-			}
+		stopped = stopped
+			|| (qh->qh_state == QH_STATE_IDLE)
+			|| (HALT_BIT & qh->hw_token) != 0
+			|| (ehci->hcd.state == USB_STATE_HALT);
+
+		/* always clean up qtds the hc de-activated */
+		if ((token & QTD_STS_ACTIVE) == 0) {
+
+			/* magic dummy for short reads; won't advance */
+			if (IS_SHORT_READ (token)
+					&& !(token & QTD_STS_HALT)
+					&& ehci->async->hw_alt_next
+						== qh->hw_alt_next)
+				goto halt;
 
-			/* unlink the rest?  once we start unlinking, after
-			 * a fault or explicit unlink, we unlink all later
-			 * urbs.  usb spec requires that for faults...
-			 */
-			if (unlink && urb->status == -EINPROGRESS)
-				urb->status = -ECONNRESET;
+		/* stop scanning when we reach qtds the hc is using */
+		} else if (likely (!stopped)) {
+			last = 0;
+			break;
 
-		/* Else QH is active, so we must not modify QTDs
-		 * that HC may be working on.  No more qtds to check.
-		 */
-		} else if (unlikely ((token & QTD_STS_ACTIVE) != 0)) {
-			next = qtd_list;
-			qtd = 0;
-			continue;
+		} else {
+			/* ignore active qtds unless some previous qtd
+			 * for the urb faulted (including short read) or
+			 * its urb was canceled.  we may patch qh or qtds.
+			 */
+			if ((token & QTD_STS_ACTIVE)
+					&& urb->status == -EINPROGRESS) {
+				last = 0;
+				continue;
+			}
+			if ((HALT_BIT & qh->hw_token) == 0) {
+halt:
+				qh->hw_token |= HALT_BIT;
+				wmb ();
+				stopped = 1;
+			}
 		}
-
+ 
+		/* remove it from the queue */
 		spin_lock (&urb->lock);
-		qtd_copy_status (urb, qtd->length, token);
+		qtd_copy_status (ehci, urb, qtd->length, token);
 		spin_unlock (&urb->lock);
 
+		if (stopped && qtd->qtd_list.prev != &qh->qtd_list) {
+			last = list_entry (qtd->qtd_list.prev,
+					struct ehci_qtd, qtd_list);
+			last->hw_next = qtd->hw_next;
+		}
 		list_del (&qtd->qtd_list);
-
-#if 0
-		if (urb->status == -EINPROGRESS)
-			vdbg ("  qtd %p ok, urb %p, token %8x, len %d",
-				qtd, urb, token, urb->actual_length);
-		else
-			vdbg ("urb %p status %d, qtd %p, token %8x, len %d",
-				urb, urb->status, qtd, token,
-				urb->actual_length);
-#endif
+		last = qtd;
 	}
 
 	/* last urb's completion might still need calling */
 	if (likely (last != 0)) {
-		ehci_urb_done (ehci, last->urb);
+		ehci_urb_done (ehci, last->urb, regs);
 		count++;
 		ehci_qtd_free (ehci, last);
 	}
 
-	/* reactivate queue after error and driver's cleanup */
-	if (unlikely (halted && !list_empty (qtd_list))) {
-		qh_update (qh, list_entry (qtd_list->next,
-				struct ehci_qtd, qtd_list));
+	/* update qh after fault cleanup */
+	if (unlikely ((HALT_BIT & qh->hw_token) != 0)) {
+		qh_update (ehci, qh,
+			list_empty (&qh->qtd_list)
+				? qh->dummy
+				: list_entry (qh->qtd_list.next,
+					struct ehci_qtd, qtd_list));
 	}
 
 	return count;
 }
+#undef HALT_BIT
 
 /*-------------------------------------------------------------------------*/
 
@@ -413,7 +406,6 @@
 	qtd = ehci_qtd_alloc (ehci, flags);
 	if (unlikely (!qtd))
 		return 0;
-	qtd_prev = 0;
 	list_add_tail (&qtd->qtd_list, head);
 	qtd->urb = urb;
 
@@ -421,6 +413,8 @@
 	token |= (EHCI_TUNE_CERR << 10);
 	/* for split transactions, SplitXState initialized to zero */
 
+	len = urb->transfer_buffer_length;
+	is_input = usb_pipein (urb->pipe);
 	if (usb_pipecontrol (urb->pipe)) {
 		/* SETUP pid */
 		qtd_fill (qtd, urb->setup_dma, sizeof (struct usb_ctrlrequest),
@@ -436,15 +430,14 @@
 		qtd_prev->hw_next = QTD_NEXT (qtd->qtd_dma);
 		list_add_tail (&qtd->qtd_list, head);
 
-		if (!(urb->transfer_flags & URB_SHORT_NOT_OK))
+		if (len > 0 && is_input
+				&& !(urb->transfer_flags & URB_SHORT_NOT_OK))
 			status_patch = 1;
 	} 
 
 	/*
 	 * data transfer stage:  buffer setup
 	 */
-	len = urb->transfer_buffer_length;
-	is_input = usb_pipein (urb->pipe);
 	if (likely (len > 0))
 		buf = urb->transfer_dma;
 	else
@@ -464,7 +457,6 @@
 	for (;;) {
 		int this_qtd_len;
 
-		qtd->urb = urb;
 		this_qtd_len = qtd_fill (qtd, buf, len, token);
 		len -= this_qtd_len;
 		buf += this_qtd_len;
@@ -497,7 +489,7 @@
 			token ^= 0x0100;	/* "in" <--> "out"  */
 			token |= QTD_TOGGLE;	/* force DATA1 */
 		} else if (usb_pipebulk (urb->pipe)
-				&& (urb->transfer_flags & USB_ZERO_PACKET)
+				&& (urb->transfer_flags & URB_ZERO_PACKET)
 				&& !(urb->transfer_buffer_length % maxpacket)) {
 			one_more = 1;
 		}
@@ -576,10 +568,9 @@
  * there are additional complications: c-mask, maybe FSTNs.
  */
 static struct ehci_qh *
-ehci_qh_make (
+qh_make (
 	struct ehci_hcd		*ehci,
 	struct urb		*urb,
-	struct list_head	*qtd_list,
 	int			flags
 ) {
 	struct ehci_qh		*qh = ehci_qh_alloc (ehci, flags);
@@ -622,7 +613,6 @@
 			if (qh->period < 1) {
 				dbg ("intr period %d uframes, NYET!",
 						urb->interval);
-				qh = 0;
 				goto done;
 			}
 		} else {
@@ -683,27 +673,20 @@
 		break;
 	default:
  		dbg ("bogus dev %p speed %d", urb->dev, urb->dev->speed);
- 		return 0;
+done:
+		qh_put (ehci, qh);
+		return 0;
 	}
 
 	/* NOTE:  if (PIPE_INTERRUPT) { scheduler sets s-mask } */
 
+	/* init as halted, toggle clear, advance to dummy */
 	qh->qh_state = QH_STATE_IDLE;
 	qh->hw_info1 = cpu_to_le32 (info1);
 	qh->hw_info2 = cpu_to_le32 (info2);
-
-	/* initialize sw and hw queues with these qtds */
-	if (!list_empty (qtd_list)) {
-		list_splice (qtd_list, &qh->qtd_list);
-		qh_update (qh, list_entry (qtd_list->next, struct ehci_qtd, qtd_list));
-	} else {
-		qh->hw_qtd_next = qh->hw_alt_next = EHCI_LIST_END;
-	}
-
-	/* initialize data toggle state */
-	clear_toggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, qh);
-
-done:
+	qh_update (ehci, qh, qh->dummy);
+	qh->hw_token = cpu_to_le32 (QTD_STS_HALT);
+	usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, 1);
 	return qh;
 }
 #undef hb_mult
@@ -716,33 +699,35 @@
 static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
 {
 	u32		dma = QH_NEXT (qh->qh_dma);
-	struct ehci_qh	*q;
+	struct ehci_qh	*head;
 
-	if (unlikely (!(q = ehci->async))) {
+	/* (re)start the async schedule? */
+	head = ehci->async;
+	if (ehci->async_idle)
+		del_timer (&ehci->watchdog);
+	if (!head->qh_next.qh) {
 		u32	cmd = readl (&ehci->regs->command);
 
-		/* in case a clear of CMD_ASE didn't take yet */
-		(void) handshake (&ehci->regs->status, STS_ASS, 0, 150);
-
-		qh->hw_info1 |= __constant_cpu_to_le32 (QH_HEAD); /* [4.8] */
-		qh->qh_next.qh = qh;
-		qh->hw_next = dma;
-		wmb ();
-		ehci->async = qh;
-		writel ((u32)qh->qh_dma, &ehci->regs->async_next);
-		cmd |= CMD_ASE | CMD_RUN;
-		writel (cmd, &ehci->regs->command);
-		ehci->hcd.state = USB_STATE_RUNNING;
-		/* posted write need not be known to HC yet ... */
-	} else {
-		/* splice right after "start" of ring */
-		qh->hw_info1 &= ~__constant_cpu_to_le32 (QH_HEAD); /* [4.8] */
-		qh->qh_next = q->qh_next;
-		qh->hw_next = q->hw_next;
-		wmb ();
-		q->qh_next.qh = qh;
-		q->hw_next = dma;
+		if (!(cmd & CMD_ASE)) {
+			/* in case a clear of CMD_ASE didn't take yet */
+			(void) handshake (&ehci->regs->status, STS_ASS, 0, 150);
+			cmd |= CMD_ASE | CMD_RUN;
+			writel (cmd, &ehci->regs->command);
+			ehci->hcd.state = USB_STATE_RUNNING;
+			/* posted write need not be known to HC yet ... */
+		}
 	}
+
+	qh->hw_token &= ~__constant_cpu_to_le32 (QTD_STS_HALT);
+
+	/* splice right after start */
+	qh->qh_next = head->qh_next;
+	qh->hw_next = head->hw_next;
+	wmb ();
+
+	head->qh_next.qh = qh;
+	head->hw_next = dma;
+
 	qh->qh_state = QH_STATE_LINKED;
 	/* qtd completions reported later by interrupt */
 
@@ -768,6 +753,11 @@
 	struct ehci_qh		*qh = 0;
 
 	qh = (struct ehci_qh *) *ptr;
+	if (unlikely (qh == 0)) {
+		/* can't sleep here, we have ehci->lock... */
+		qh = qh_make (ehci, urb, SLAB_ATOMIC);
+		*ptr = qh;
+	}
 	if (likely (qh != 0)) {
 		struct ehci_qtd	*qtd;
 
@@ -777,75 +767,94 @@
 			qtd = list_entry (qtd_list->next, struct ehci_qtd,
 					qtd_list);
 
-		/* maybe patch the qh used for set_address */
-		if (unlikely (epnum == 0
-				&& le32_to_cpu (qh->hw_info1 & 0x7f) == 0))
-			qh->hw_info1 |= cpu_to_le32 (usb_pipedevice(urb->pipe));
-
-		/* append to tds already queued to this qh? */
-		if (unlikely (!list_empty (&qh->qtd_list) && qtd)) {
-			struct ehci_qtd		*last_qtd;
-			int			short_rx = 0;
-			u32			hw_next;
-
-			/* update the last qtd's "next" pointer */
-			// dbg_qh ("non-empty qh", ehci, qh);
-			last_qtd = list_entry (qh->qtd_list.prev,
-					struct ehci_qtd, qtd_list);
-			hw_next = QTD_NEXT (qtd->qtd_dma);
-			last_qtd->hw_next = hw_next;
-
-			/* previous urb allows short rx? maybe optimize. */
-			if (!(last_qtd->urb->transfer_flags & URB_SHORT_NOT_OK)
-					&& (epnum & 0x10)) {
-				// only the last QTD for now
-				last_qtd->hw_alt_next = hw_next;
-				short_rx = 1;
-			}
-
-			/* Adjust any old copies in qh overlay too.
-			 * Interrupt code must cope with case of HC having it
-			 * cached, and clobbering these updates.
-			 * ... complicates getting rid of extra interrupts!
-			 * (Or:  use dummy td, so cache always stays valid.)
-			 */
-			if (qh->hw_current == cpu_to_le32 (last_qtd->qtd_dma)) {
-				wmb ();
-				qh->hw_qtd_next = hw_next;
-				if (short_rx)
-					qh->hw_alt_next = hw_next
-				    		| (qh->hw_alt_next & 0x1e);
-				vdbg ("queue to qh %p, patch", qh);
+		/* control qh may need patching after enumeration */
+		if (unlikely (epnum == 0)) {
+			/* set_address changes the address */
+			if (le32_to_cpu (qh->hw_info1 & 0x7f) == 0)
+				qh->hw_info1 |= cpu_to_le32 (
+						usb_pipedevice (urb->pipe));
+
+			/* for full speed, ep0 maxpacket can grow */
+			else if (!(qh->hw_info1 & cpu_to_le32 (0x3 << 12))) {
+				u32	info, max;
+
+				info = le32_to_cpu (qh->hw_info1);
+				max = urb->dev->descriptor.bMaxPacketSize0;
+				if (max > (0x07ff & (info >> 16))) {
+					info &= ~(0x07ff << 16);
+					info |= max << 16;
+					qh->hw_info1 = cpu_to_le32 (info);
+				}
 			}
+		}
 
-		/* no URB queued */
-		} else {
-			// dbg_qh ("empty qh", ehci, qh);
+		/* FIXME:  changing config or interface setting is not
+		 * supported yet.  preferred fix is for usbcore to tell
+		 * us to clear out each endpoint's state, but...
+		 */
 
-			/* NOTE: we already canceled any queued URBs
-			 * when the endpoint halted.
+		/* usb_clear_halt() means qh data toggle gets reset */
+		if (unlikely (!usb_gettoggle (urb->dev,
+					(epnum & 0x0f), !(epnum & 0x10)))
+				&& !usb_pipecontrol (urb->pipe)) {
+			/* "never happens": drivers do stall cleanup right */
+			if (qh->qh_state != QH_STATE_IDLE
+					&& (cpu_to_le32 (QTD_STS_HALT)
+						& qh->hw_token) == 0)
+				ehci_warn (ehci, "clear toggle dev%d "
+						"ep%d%s: not idle\n",
+						usb_pipedevice (urb->pipe),
+						epnum & 0x0f,
+						usb_pipein (urb->pipe)
+							? "in" : "out");
+			/* else we know this overlay write is safe */
+			clear_toggle (urb->dev,
+				epnum & 0x0f, !(epnum & 0x10), qh);
+		}
+
+		/* just one way to queue requests: swap with the dummy qtd.
+		 * only hc or qh_completions() usually modify the overlay.
+		 */
+		if (likely (qtd != 0)) {
+			struct ehci_qtd		*dummy;
+			dma_addr_t		dma;
+			u32			token;
+
+			/* to avoid racing the HC, use the dummy td instead of
+			 * the first td of our list (becomes new dummy).  both
+			 * tds stay deactivated until we're done, when the
+			 * HC is allowed to fetch the old dummy (4.10.2).
 			 */
+			token = qtd->hw_token;
+			qtd->hw_token = 0;
+			wmb ();
+			dummy = qh->dummy;
+
+			dma = dummy->qtd_dma;
+			*dummy = *qtd;
+			dummy->qtd_dma = dma;
+
+			list_del (&qtd->qtd_list);
+			list_add (&dummy->qtd_list, qtd_list);
+			__list_splice (qtd_list, qh->qtd_list.prev);
+
+			ehci_qtd_init (qtd, qtd->qtd_dma);
+			qtd->hw_alt_next = ehci->async->hw_alt_next;
+			qh->dummy = qtd;
+
+			/* hc must see the new dummy at list end */
+			dma = qtd->qtd_dma;
+			qtd = list_entry (qh->qtd_list.prev,
+					struct ehci_qtd, qtd_list);
+			qtd->hw_next = QTD_NEXT (dma);
 
-			/* usb_clear_halt() means qh data toggle gets reset */
-			if (unlikely (!usb_gettoggle (urb->dev,
-						(epnum & 0x0f),
-						!(epnum & 0x10)))) {
-				clear_toggle (urb->dev,
-					epnum & 0x0f, !(epnum & 0x10), qh);
-			}
-			if (qtd)
-				qh_update (qh, qtd);
-		}
-		list_splice (qtd_list, qh->qtd_list.prev);
+			/* let the hc process these next qtds */
+			wmb ();
+			dummy->hw_token = token;
 
-	} else {
-		/* can't sleep here, we have ehci->lock... */
-		qh = ehci_qh_make (ehci, urb, qtd_list, SLAB_ATOMIC);
-		// if (qh) dbg_qh ("new qh", ehci, qh);
-		*ptr = qh;
+			urb->hcpriv = qh_get (qh);
+		}
 	}
-	if (qh)
-		urb->hcpriv = qh_get (qh);
 	return qh;
 }
 
@@ -897,27 +906,39 @@
 /*-------------------------------------------------------------------------*/
 
 /* the async qh for the qtds being reclaimed are now unlinked from the HC */
-/* caller must not own ehci->lock */
 
-static void end_unlink_async (struct ehci_hcd *ehci)
+static void end_unlink_async (struct ehci_hcd *ehci, struct pt_regs *regs)
 {
 	struct ehci_qh		*qh = ehci->reclaim;
 
 	del_timer (&ehci->watchdog);
 
+	qh->hw_next = cpu_to_le32 (qh->qh_dma);
 	qh->qh_state = QH_STATE_IDLE;
 	qh->qh_next.qh = 0;
 	qh_put (ehci, qh);			// refcount from reclaim 
 	ehci->reclaim = 0;
 	ehci->reclaim_ready = 0;
 
-	qh_completions (ehci, qh);
+	qh_completions (ehci, qh, regs);
 
 	if (!list_empty (&qh->qtd_list)
 			&& HCD_IS_RUNNING (ehci->hcd.state))
 		qh_link_async (ehci, qh);
-	else
+	else {
 		qh_put (ehci, qh);		// refcount from async list
+
+		/* it's not free to turn the async schedule on/off; leave it
+		 * active but idle for a while once it empties.
+		 */
+		if (HCD_IS_RUNNING (ehci->hcd.state)
+				&& ehci->async->qh_next.qh == 0
+				&& !timer_pending (&ehci->watchdog)) {
+			ehci->async_idle = 1;
+			mod_timer (&ehci->watchdog,
+					jiffies + EHCI_ASYNC_JIFFIES);
+		}
+	}
 }
 
 /* makes sure the async qh will become idle */
@@ -930,7 +951,6 @@
 
 #ifdef DEBUG
 	if (ehci->reclaim
-			|| !ehci->async
 			|| qh->qh_state != QH_STATE_LINKED
 #ifdef CONFIG_SMP
 // this macro lies except on SMP compiles
@@ -940,49 +960,33 @@
 		BUG ();
 #endif
 
-	qh->qh_state = QH_STATE_UNLINK;
-	ehci->reclaim = qh = qh_get (qh);
-
-	// dbg_qh ("start unlink", ehci, qh);
-
-	/* Remove the last QH (qhead)?  Stop async schedule first. */
-	if (unlikely (qh == ehci->async && qh->qh_next.qh == qh)) {
+	/* stop async schedule right now? */
+	if (unlikely (qh == ehci->async)) {
 		/* can't get here without STS_ASS set */
 		if (ehci->hcd.state != USB_STATE_HALT) {
 			writel (cmd & ~CMD_ASE, &ehci->regs->command);
-			(void) handshake (&ehci->regs->status, STS_ASS, 0, 150);
-#if 0
-			// one VT8235 system wants to die with STS_FATAL
-			// unless this qh is leaked here. others seem ok...
-			qh = qh_get (qh);
-			dbg_qh ("async/off", ehci, qh);
-#endif
+			wmb ();
+			// handshake later, if we need to
 		}
-		qh->qh_next.qh = ehci->async = 0;
-
-		ehci->reclaim_ready = 1;
-		tasklet_schedule (&ehci->tasklet);
 		return;
 	} 
 
-	if (unlikely (ehci->hcd.state == USB_STATE_HALT)) {
-		ehci->reclaim_ready = 1;
-		tasklet_schedule (&ehci->tasklet);
-		return;
-	}
+	qh->qh_state = QH_STATE_UNLINK;
+	ehci->reclaim = qh = qh_get (qh);
 
 	prev = ehci->async;
-	while (prev->qh_next.qh != qh && prev->qh_next.qh != ehci->async)
+	while (prev->qh_next.qh != qh)
 		prev = prev->qh_next.qh;
 
-	if (qh->hw_info1 & __constant_cpu_to_le32 (QH_HEAD)) {
-		ehci->async = prev;
-		prev->hw_info1 |= __constant_cpu_to_le32 (QH_HEAD);
-	}
 	prev->hw_next = qh->hw_next;
 	prev->qh_next = qh->qh_next;
 	wmb ();
 
+	if (unlikely (ehci->hcd.state == USB_STATE_HALT)) {
+		end_unlink_async (ehci, NULL);
+		return;
+	}
+
 	ehci->reclaim_ready = 0;
 	cmd |= CMD_IAAD;
 	writel (cmd, &ehci->regs->command);
@@ -994,56 +998,43 @@
 /*-------------------------------------------------------------------------*/
 
 static void
-scan_async (struct ehci_hcd *ehci)
+scan_async (struct ehci_hcd *ehci, struct pt_regs *regs)
 {
 	struct ehci_qh		*qh;
 	unsigned		count;
 
 rescan:
-	qh = ehci->async;
+	qh = ehci->async->qh_next.qh;
 	count = 0;
 	if (likely (qh != 0)) {
 		do {
 			/* clean any finished work for this qh */
 			if (!list_empty (&qh->qtd_list)) {
-				// dbg_qh ("scan_async", ehci, qh);
-				qh = qh_get (qh);
+				int temp;
 
-				/* concurrent unlink could happen here */
-				count += qh_completions (ehci, qh);
+				/* unlinks could happen here; completion
+				 * reporting drops the lock.
+				 */
+				qh = qh_get (qh);
+				temp = qh_completions (ehci, qh, regs);
 				qh_put (ehci, qh);
+				if (temp != 0) {
+					count += temp;
+					goto rescan;
+				}
 			}
 
 			/* unlink idle entries, reducing HC PCI usage as
-			 * well as HCD schedule-scanning costs.  removing
-			 * the last qh is deferred, since it's costly.
+			 * well as HCD schedule-scanning costs.
 			 *
 			 * FIXME don't unlink idle entries so quickly; it
 			 * can penalize (common) half duplex protocols.
 			 */
 			if (list_empty (&qh->qtd_list) && !ehci->reclaim) {
-				if (qh->qh_next.qh != qh) {
-					// dbg ("irq/empty");
-					start_unlink_async (ehci, qh);
-				} else if (!timer_pending (&ehci->watchdog)) {
-					/* can't use IAA for last entry */
-					ehci->async_idle = 1;
-					mod_timer (&ehci->watchdog,
-						jiffies + EHCI_ASYNC_JIFFIES);
-				}
-			}
-
-			/* keep latencies down: let any irqs in */
-			if (count > max_completions) {
-				spin_unlock_irq (&ehci->lock);
-				cpu_relax ();
-				spin_lock_irq (&ehci->lock);
-				goto rescan;
+				start_unlink_async (ehci, qh);
 			}
 
 			qh = qh->qh_next.qh;
-			if (!qh)		/* unlinked? */
-				goto rescan;
-		} while (qh != ehci->async);
+		} while (qh);
 	}
 }
diff -urN linux-2.4.21-pre2/drivers/usb/hcd/ehci-sched.c linux-2.4.21-pre3/drivers/usb/hcd/ehci-sched.c
--- linux-2.4.21-pre2/drivers/usb/hcd/ehci-sched.c	Mon Jan  6 16:32:29 2003
+++ linux-2.4.21-pre3/drivers/usb/hcd/ehci-sched.c	Mon Jan  6 16:32:32 2003
@@ -190,7 +190,7 @@
 	/* posted write ... PSS happens later */
 	ehci->hcd.state = USB_STATE_RUNNING;
 
-	/* make sure tasklet scans these */
+	/* make sure ehci_work scans these */
 	ehci->next_uframe = readl (&ehci->regs->frame_index)
 				% (ehci->periodic_size << 3);
 	return 0;
@@ -495,7 +495,8 @@
 intr_complete (
 	struct ehci_hcd	*ehci,
 	unsigned	frame,
-	struct ehci_qh	*qh
+	struct ehci_qh	*qh,
+	struct pt_regs	*regs
 ) {
 	unsigned	count;
 
@@ -509,7 +510,7 @@
 	}
 	
 	/* handle any completions */
-	count = qh_completions (ehci, qh);
+	count = qh_completions (ehci, qh, regs);
 
 	if (unlikely (list_empty (&qh->qtd_list)))
 		intr_deschedule (ehci, qh, 0);
@@ -738,7 +739,7 @@
 	}
 
 	/* explicit start frame? */
-	if (!(urb->transfer_flags & USB_ISO_ASAP)) {
+	if (!(urb->transfer_flags & URB_ISO_ASAP)) {
 		unsigned	temp;
 
 		/* sanity check: must be in range */
@@ -867,7 +868,8 @@
 itd_complete (
 	struct ehci_hcd	*ehci,
 	struct ehci_itd	*itd,
-	unsigned	uframe
+	unsigned	uframe,
+	struct pt_regs	*regs
 ) {
 	struct urb				*urb = itd->urb;
 	struct usb_iso_packet_descriptor	*desc;
@@ -922,7 +924,7 @@
 
 	/* complete() can reenter this HCD */
 	spin_unlock (&ehci->lock);
-	usb_hcd_giveback_urb (&ehci->hcd, urb);
+	usb_hcd_giveback_urb (&ehci->hcd, urb, regs);
 	spin_lock (&ehci->lock);
 
 	/* defer stopping schedule; completion can submit */
@@ -973,7 +975,7 @@
 /*-------------------------------------------------------------------------*/
 
 static void
-scan_periodic (struct ehci_hcd *ehci)
+scan_periodic (struct ehci_hcd *ehci, struct pt_regs *regs)
 {
 	unsigned	frame, clock, now_uframe, mod;
 	unsigned	count = 0;
@@ -999,14 +1001,6 @@
 		u32			type, *hw_p;
 		unsigned		uframes;
 
-		/* keep latencies down: let any irqs in */
-		if (count > max_completions) {
-			spin_unlock_irq (&ehci->lock);
-			cpu_relax ();
-			count = 0;
-			spin_lock_irq (&ehci->lock);
-		}
-
 restart:
 		/* scan schedule to _before_ current frame index */
 		if (frame == clock)
@@ -1031,7 +1025,7 @@
 				temp = q.qh->qh_next;
 				type = Q_NEXT_TYPE (q.qh->hw_next);
 				count += intr_complete (ehci, frame,
-						qh_get (q.qh));
+						qh_get (q.qh), regs);
 				qh_put (ehci, q.qh);
 				q = temp;
 				break;
@@ -1064,7 +1058,7 @@
 
 						/* might free q.itd ... */
 						count += itd_complete (ehci,
-							temp.itd, uf);
+							temp.itd, uf, regs);
 						break;
 					}
 				}
diff -urN linux-2.4.21-pre2/drivers/usb/hcd/ehci.h linux-2.4.21-pre3/drivers/usb/hcd/ehci.h
--- linux-2.4.21-pre2/drivers/usb/hcd/ehci.h	Mon Jan  6 16:32:29 2003
+++ linux-2.4.21-pre3/drivers/usb/hcd/ehci.h	Mon Jan  6 16:32:32 2003
@@ -31,11 +31,6 @@
 	/* termination of urbs from core */
 	unsigned long		complete;
 	unsigned long		unlink;
-
-	/* qhs patched to recover from td queueing race
-	 * (can avoid by using 'dummy td', allowing fewer irqs)
-	 */
-	unsigned long		qpatch;
 };
 
 /* ehci_hcd->lock guards shared data against other CPUs:
@@ -70,9 +65,6 @@
 	int			next_uframe;	/* scan periodic, start here */
 	unsigned		periodic_sched;	/* periodic activity count */
 
-	/* deferred work from IRQ, etc */
-	struct tasklet_struct	tasklet;
-
 	/* per root hub port */
 	unsigned long		reset_done [EHCI_MAX_ROOT_PORTS];
 
@@ -311,6 +303,7 @@
 	dma_addr_t		qh_dma;		/* address of qh */
 	union ehci_shadow	qh_next;	/* ptr to qh; or periodic */
 	struct list_head	qtd_list;	/* sw qtd list */
+	struct ehci_qtd		*dummy;
 
 	atomic_t		refcount;
 
diff -urN linux-2.4.21-pre2/drivers/usb/hcd.c linux-2.4.21-pre3/drivers/usb/hcd.c
--- linux-2.4.21-pre2/drivers/usb/hcd.c	Mon Jan  6 16:32:29 2003
+++ linux-2.4.21-pre3/drivers/usb/hcd.c	Mon Jan  6 16:32:32 2003
@@ -437,7 +437,7 @@
 	}
 
 	/* any errors get returned through the urb completion */
-	usb_hcd_giveback_urb (hcd, urb);
+	usb_hcd_giveback_urb (hcd, urb, 0);
 	return 0;
 }
 
@@ -518,7 +518,7 @@
 		urb->hcpriv = 0;
 		spin_unlock_irqrestore (&urb->lock, flags);
 
-		usb_hcd_giveback_urb (hcd, urb);
+		usb_hcd_giveback_urb (hcd, urb, 0);
 	}
 }
 
@@ -553,7 +553,7 @@
 	spin_unlock_irqrestore (&hcd_data_lock, flags);
 
 	/* we rely on RH callback code not unlinking its URB! */
-	usb_hcd_giveback_urb (hcd, urb);
+	usb_hcd_giveback_urb (hcd, urb, 0);
 }
 
 /*-------------------------------------------------------------------------*/
@@ -663,7 +663,8 @@
 	hcd->driver = driver;
 	hcd->description = driver->description;
 	hcd->pdev = dev;
-	info ("%s @ %s, %s", hcd->description,  dev->slot_name, dev->name);
+	printk (KERN_INFO "%s %s: %s\n",
+			hcd->description,  dev->slot_name, dev->name);
 
 #ifndef __sparc__
 	sprintf (buf, "%d", dev->irq);
@@ -682,7 +683,8 @@
 
 	hcd->regs = base;
 	hcd->region = region;
-	info ("irq %s, %s %p", bufp,
+	printk (KERN_INFO "%s %s: irq %s, %s %p\n",
+		hcd->description,  dev->slot_name, bufp,
 		(driver->flags & HCD_MEMORY) ? "pci mem" : "io base",
 		base);
 
@@ -739,7 +741,8 @@
 	hcd = pci_get_drvdata(dev);
 	if (!hcd)
 		return;
-	info ("remove: %s, state %x", hcd->bus->bus_name, hcd->state);
+	printk (KERN_INFO "%s %s: remove state %x\n",
+		hcd->description,  dev->slot_name, hcd->state);
 
 	if (in_interrupt ()) BUG ();
 
@@ -817,7 +820,8 @@
 	int			retval;
 
 	hcd = pci_get_drvdata(dev);
-	info ("suspend %s to state %d", hcd->bus->bus_name, state);
+	printk (KERN_INFO "%s %s: suspend to state %d\n",
+		hcd->description,  dev->slot_name, state);
 
 	pci_save_state (dev, hcd->pci_state);
 
@@ -846,7 +850,8 @@
 	int			retval;
 
 	hcd = pci_get_drvdata(dev);
-	info ("resume %s", hcd->bus->bus_name);
+	printk (KERN_INFO "%s %s: resume\n",
+		hcd->description,  dev->slot_name);
 
 	/* guard against multiple resumes (APM bug?) */
 	atomic_inc (&hcd->resume_count);
@@ -1309,7 +1314,7 @@
 		if (usb_pipetype (urb->pipe) == PIPE_INTERRUPT)
 			retval = -EAGAIN;
 		else
-			retval = -EINVAL;
+			retval = -EBUSY;
 		goto done;
 	}
 
@@ -1349,8 +1354,6 @@
 	if (!(urb->transfer_flags & (USB_ASYNC_UNLINK|USB_TIMEOUT_KILLED))
 			&& HCD_IS_RUNNING (hcd->state)
 			&& !retval) {
-		dbg ("%s: wait for giveback urb %p",
-			hcd->bus->bus_name, urb);
 		wait_for_completion (&splice.done);
 	} else if ((urb->transfer_flags & USB_ASYNC_UNLINK) && retval == 0) {
 		return -EINPROGRESS;
@@ -1428,7 +1431,7 @@
 	if (unlikely (hcd->state == USB_STATE_HALT))	/* irq sharing? */
 		return;
 
-	hcd->driver->irq (hcd);
+	hcd->driver->irq (hcd, r);
 	if (hcd->state != start && hcd->state == USB_STATE_HALT)
 		hc_died (hcd);
 }
@@ -1439,6 +1442,7 @@
  * usb_hcd_giveback_urb - return URB from HCD to device driver
  * @hcd: host controller returning the URB
  * @urb: urb being returned to the USB device driver.
+ * @regs: saved hardware registers (ignored on 2.4 kernels)
  * Context: in_interrupt()
  *
  * This hands the URB from HCD to its USB device driver, using its
@@ -1456,7 +1460,7 @@
  * ISO streaming functionality can be achieved by having completion handlers
  * re-queue URBs.  Such explicit queuing doesn't discard error reports.
  */
-void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb)
+void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs)
 {
 	urb_unlink (urb);
 
@@ -1466,10 +1470,6 @@
 	// completions for periodic urbs need hooks inside the HCD.
 	// hcd_monitor_hook(MONITOR_URB_UPDATE, urb, dev)
 
-	if (urb->status)
-		dbg ("giveback urb %p status %d len %d",
-			urb, urb->status, urb->actual_length);
-
 	// NOTE:  2.5 does this if !URB_NO_DMA_MAP transfer flag
 	if (usb_pipecontrol (urb->pipe))
 		pci_unmap_single (hcd->pdev, urb->setup_dma,
diff -urN linux-2.4.21-pre2/drivers/usb/hcd.h linux-2.4.21-pre3/drivers/usb/hcd.h
--- linux-2.4.21-pre2/drivers/usb/hcd.h	Mon Jan  6 16:32:29 2003
+++ linux-2.4.21-pre3/drivers/usb/hcd.h	Mon Jan  6 16:32:32 2003
@@ -103,7 +103,7 @@
 	const char	*description;	/* "ehci-hcd" etc */
 
 	/* irq handler */
-	void	(*irq) (struct usb_hcd *hcd);
+	void	(*irq) (struct usb_hcd *hcd, struct pt_regs *regs);
 
 	int	flags;
 #define	HCD_MEMORY	0x0001		/* HC regs use memory (else I/O) */
@@ -148,7 +148,8 @@
 				char *buf, u16 wLength);
 };
 
-extern void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb);
+extern void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb,
+		struct pt_regs *regs);
 
 #ifdef CONFIG_PCI
 
@@ -282,3 +283,5 @@
 usb_hub_tt_clear_buffer (struct usb_device *dev, int pipe)
 	{ }
 
+#define URB_ZERO_PACKET	USB_ZERO_PACKET
+#define URB_ISO_ASAP	USB_ISO_ASAP
diff -urN linux-2.4.21-pre2/drivers/usb/ibmcam.c linux-2.4.21-pre3/drivers/usb/ibmcam.c
--- linux-2.4.21-pre2/drivers/usb/ibmcam.c	Fri Aug  2 17:39:45 2002
+++ linux-2.4.21-pre3/drivers/usb/ibmcam.c	Mon Jan  6 16:32:32 2003
@@ -78,7 +78,7 @@
 };
 
 /*
- * This structure lives in uvd_t->user field.
+ * This structure lives in uvd->user field.
  */
 typedef struct {
 	int initialized;	/* Had we already sent init sequence? */
@@ -87,7 +87,7 @@
 } ibmcam_t;
 #define	IBMCAM_T(uvd)	((ibmcam_t *)((uvd)->user_data))
 
-usbvideo_t *cams = NULL;
+struct usbvideo *cams = NULL;
 
 static int debug = 0;
 
@@ -249,9 +249,9 @@
  * History:
  * 1/21/00  Created.
  */
-static ParseState_t ibmcam_find_header(uvd_t *uvd) /* FIXME: Add frame here */
+static enum ParseState ibmcam_find_header(struct uvd *uvd) /* FIXME: Add frame here */
 {
-	usbvideo_frame_t *frame;
+	struct usbvideo_frame *frame;
 	ibmcam_t *icam;
 
 	if ((uvd->curframe) < 0 || (uvd->curframe >= USBVIDEO_NUMFRAMES)) {
@@ -397,9 +397,9 @@
  * 21-Jan-2000 Created.
  * 12-Oct-2000 Reworked to reflect interlaced nature of the data.
  */
-static ParseState_t ibmcam_parse_lines(
-	uvd_t *uvd,
-	usbvideo_frame_t *frame,
+static enum ParseState ibmcam_parse_lines(
+	struct uvd *uvd,
+	struct usbvideo_frame *frame,
 	long *pcopylen)
 {
 	unsigned char *f;
@@ -662,9 +662,9 @@
  * them both as R component in attempt to at least partially recover the
  * lost resolution.
  */
-static ParseState_t ibmcam_model2_320x240_parse_lines(
-	uvd_t *uvd,
-	usbvideo_frame_t *frame,
+static enum ParseState ibmcam_model2_320x240_parse_lines(
+	struct uvd *uvd,
+	struct usbvideo_frame *frame,
 	long *pcopylen)
 {
 	unsigned char *f, *la, *lb;
@@ -816,9 +816,9 @@
 		return scan_Continue;
 }
 
-static ParseState_t ibmcam_model3_parse_lines(
-	uvd_t *uvd,
-	usbvideo_frame_t *frame,
+static enum ParseState ibmcam_model3_parse_lines(
+	struct uvd *uvd,
+	struct usbvideo_frame *frame,
 	long *pcopylen)
 {
 	unsigned char *data;
@@ -961,9 +961,9 @@
  * History:
  * 10-Feb-2001 Created.
  */
-static ParseState_t ibmcam_model4_128x96_parse_lines(
-	uvd_t *uvd,
-	usbvideo_frame_t *frame,
+static enum ParseState ibmcam_model4_128x96_parse_lines(
+	struct uvd *uvd,
+	struct usbvideo_frame *frame,
 	long *pcopylen)
 {
 	const unsigned char *data_rv, *data_gv, *data_bv;
@@ -1049,9 +1049,9 @@
  * History:
  * 1/21/00  Created.
  */
-void ibmcam_ProcessIsocData(uvd_t *uvd, usbvideo_frame_t *frame)
+void ibmcam_ProcessIsocData(struct uvd *uvd, struct usbvideo_frame *frame)
 {
-	ParseState_t newstate;
+	enum ParseState newstate;
 	long copylen = 0;
 	int mod = IBMCAM_T(uvd)->camera_model;
 
@@ -1128,7 +1128,7 @@
  * 1/27/00  Added check for dev == NULL; this happens if camera is unplugged.
  */
 static int ibmcam_veio(
-	uvd_t *uvd,
+	struct uvd *uvd,
 	unsigned char req,
 	unsigned short value,
 	unsigned short index)
@@ -1195,7 +1195,7 @@
  * History:
  * 1/18/00  Created.
  */
-static int ibmcam_calculate_fps(uvd_t *uvd)
+static int ibmcam_calculate_fps(struct uvd *uvd)
 {
 	return 3 + framerate*4 + framerate/2;
 }
@@ -1209,33 +1209,33 @@
  * History:
  * 1/2/00   Created.
  */
-static void ibmcam_send_FF_04_02(uvd_t *uvd)
+static void ibmcam_send_FF_04_02(struct uvd *uvd)
 {
 	ibmcam_veio(uvd, 0, 0x00FF, 0x0127);
 	ibmcam_veio(uvd, 0, 0x0004, 0x0124);
 	ibmcam_veio(uvd, 0, 0x0002, 0x0124);
 }
 
-static void ibmcam_send_00_04_06(uvd_t *uvd)
+static void ibmcam_send_00_04_06(struct uvd *uvd)
 {
 	ibmcam_veio(uvd, 0, 0x0000, 0x0127);
 	ibmcam_veio(uvd, 0, 0x0004, 0x0124);
 	ibmcam_veio(uvd, 0, 0x0006, 0x0124);
 }
 
-static void ibmcam_send_x_00(uvd_t *uvd, unsigned short x)
+static void ibmcam_send_x_00(struct uvd *uvd, unsigned short x)
 {
 	ibmcam_veio(uvd, 0, x,      0x0127);
 	ibmcam_veio(uvd, 0, 0x0000, 0x0124);
 }
 
-static void ibmcam_send_x_00_05(uvd_t *uvd, unsigned short x)
+static void ibmcam_send_x_00_05(struct uvd *uvd, unsigned short x)
 {
 	ibmcam_send_x_00(uvd, x);
 	ibmcam_veio(uvd, 0, 0x0005, 0x0124);
 }
 
-static void ibmcam_send_x_00_05_02(uvd_t *uvd, unsigned short x)
+static void ibmcam_send_x_00_05_02(struct uvd *uvd, unsigned short x)
 {
 	ibmcam_veio(uvd, 0, x,      0x0127);
 	ibmcam_veio(uvd, 0, 0x0000, 0x0124);
@@ -1243,7 +1243,7 @@
 	ibmcam_veio(uvd, 0, 0x0002, 0x0124);
 }
 
-static void ibmcam_send_x_01_00_05(uvd_t *uvd, unsigned short x)
+static void ibmcam_send_x_01_00_05(struct uvd *uvd, unsigned short x)
 {
 	ibmcam_veio(uvd, 0, x,      0x0127);
 	ibmcam_veio(uvd, 0, 0x0001, 0x0124);
@@ -1251,7 +1251,7 @@
 	ibmcam_veio(uvd, 0, 0x0005, 0x0124);
 }
 
-static void ibmcam_send_x_00_05_02_01(uvd_t *uvd, unsigned short x)
+static void ibmcam_send_x_00_05_02_01(struct uvd *uvd, unsigned short x)
 {
 	ibmcam_veio(uvd, 0, x,      0x0127);
 	ibmcam_veio(uvd, 0, 0x0000, 0x0124);
@@ -1260,7 +1260,7 @@
 	ibmcam_veio(uvd, 0, 0x0001, 0x0124);
 }
 
-static void ibmcam_send_x_00_05_02_08_01(uvd_t *uvd, unsigned short x)
+static void ibmcam_send_x_00_05_02_08_01(struct uvd *uvd, unsigned short x)
 {
 	ibmcam_veio(uvd, 0, x,      0x0127);
 	ibmcam_veio(uvd, 0, 0x0000, 0x0124);
@@ -1270,7 +1270,7 @@
 	ibmcam_veio(uvd, 0, 0x0001, 0x0124);
 }
 
-static void ibmcam_Packet_Format1(uvd_t *uvd, unsigned char fkey, unsigned char val)
+static void ibmcam_Packet_Format1(struct uvd *uvd, unsigned char fkey, unsigned char val)
 {
 	ibmcam_send_x_01_00_05(uvd, unknown_88);
 	ibmcam_send_x_00_05(uvd, fkey);
@@ -1284,20 +1284,20 @@
 	ibmcam_send_FF_04_02(uvd);
 }
 
-static void ibmcam_PacketFormat2(uvd_t *uvd, unsigned char fkey, unsigned char val)
+static void ibmcam_PacketFormat2(struct uvd *uvd, unsigned char fkey, unsigned char val)
 {
 	ibmcam_send_x_01_00_05	(uvd, unknown_88);
 	ibmcam_send_x_00_05	(uvd, fkey);
 	ibmcam_send_x_00_05_02	(uvd, val);
 }
 
-static void ibmcam_model2_Packet2(uvd_t *uvd)
+static void ibmcam_model2_Packet2(struct uvd *uvd)
 {
 	ibmcam_veio(uvd, 0, 0x00ff, 0x012d);
 	ibmcam_veio(uvd, 0, 0xfea3, 0x0124);
 }
 
-static void ibmcam_model2_Packet1(uvd_t *uvd, unsigned short v1, unsigned short v2)
+static void ibmcam_model2_Packet1(struct uvd *uvd, unsigned short v1, unsigned short v2)
 {
 	ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
 	ibmcam_veio(uvd, 0, 0x00ff, 0x012e);
@@ -1318,7 +1318,7 @@
  * 00_0096_0127
  * 00_fea8_0124	
 */
-static void ibmcam_model3_Packet1(uvd_t *uvd, unsigned short v1, unsigned short v2)
+static void ibmcam_model3_Packet1(struct uvd *uvd, unsigned short v1, unsigned short v2)
 {
 	ibmcam_veio(uvd, 0, 0x0078, 0x012d);
 	ibmcam_veio(uvd, 0, v1,     0x012f);
@@ -1327,7 +1327,7 @@
 	ibmcam_veio(uvd, 0, 0xfea8, 0x0124);
 }
 
-static void ibmcam_model4_BrightnessPacket(uvd_t *uvd, int i)
+static void ibmcam_model4_BrightnessPacket(struct uvd *uvd, int i)
 {
 	ibmcam_veio(uvd, 0, 0x00aa, 0x012d);
 	ibmcam_veio(uvd, 0, 0x0026, 0x012f);
@@ -1353,7 +1353,7 @@
  * History:
  * 1/2/00   Created.
  */
-static void ibmcam_adjust_contrast(uvd_t *uvd)
+static void ibmcam_adjust_contrast(struct uvd *uvd)
 {
 	unsigned char a_contrast = uvd->vpic.contrast >> 12;
 	unsigned char new_contrast;
@@ -1428,7 +1428,7 @@
  * 1/5/00   Created.
  * 2/20/00  Added support for Model 2 cameras.
  */
-static void ibmcam_change_lighting_conditions(uvd_t *uvd)
+static void ibmcam_change_lighting_conditions(struct uvd *uvd)
 {
 	static const char proc[] = "ibmcam_change_lighting_conditions";
 
@@ -1474,7 +1474,7 @@
  * range [0..6], where 0 is most smooth and 6 is most sharp (raw image, I guess).
  * Recommended value is 4. Cameras model 2 do not have this feature at all.
  */
-static void ibmcam_set_sharpness(uvd_t *uvd)
+static void ibmcam_set_sharpness(struct uvd *uvd)
 {
 	static const char proc[] = "ibmcam_set_sharpness";
 
@@ -1543,7 +1543,7 @@
  *
  * This procedure changes brightness of the picture.
  */
-static void ibmcam_set_brightness(uvd_t *uvd)
+static void ibmcam_set_brightness(struct uvd *uvd)
 {
 	static const char proc[] = "ibmcam_set_brightness";
 	static const unsigned short n = 1;
@@ -1608,7 +1608,7 @@
 	}
 }
 
-static void ibmcam_set_hue(uvd_t *uvd)
+static void ibmcam_set_hue(struct uvd *uvd)
 {
 	switch (IBMCAM_T(uvd)->camera_model) {
 	case IBMCAM_MODEL_2:
@@ -1704,14 +1704,14 @@
  * This procedure gets called from V4L interface to update picture settings.
  * Here we change brightness and contrast.
  */
-static void ibmcam_adjust_picture(uvd_t *uvd)
+static void ibmcam_adjust_picture(struct uvd *uvd)
 {
 	ibmcam_adjust_contrast(uvd);
 	ibmcam_set_brightness(uvd);
 	ibmcam_set_hue(uvd);
 }
 
-static int ibmcam_model1_setup(uvd_t *uvd)
+static int ibmcam_model1_setup(struct uvd *uvd)
 {
 	const int ntries = 5;
 	int i;
@@ -1908,7 +1908,7 @@
 	return (CAMERA_IS_OPERATIONAL(uvd) ? 0 : -EFAULT);
 }
 
-static int ibmcam_model2_setup(uvd_t *uvd)
+static int ibmcam_model2_setup(struct uvd *uvd)
 {
 	ibmcam_veio(uvd, 0, 0x0000, 0x0100);	/* LED on */
 	ibmcam_veio(uvd, 1, 0x0000, 0x0116);
@@ -1966,7 +1966,7 @@
  * This code adds finishing touches to the video data interface.
  * Here we configure the frame rate and turn on the LED.
  */
-static void ibmcam_model1_setup_after_video_if(uvd_t *uvd)
+static void ibmcam_model1_setup_after_video_if(struct uvd *uvd)
 {
 	unsigned short internal_frame_rate;
 
@@ -1978,7 +1978,7 @@
 	ibmcam_veio(uvd, 0, 0xc0, 0x010c);
 }
 
-static void ibmcam_model2_setup_after_video_if(uvd_t *uvd)
+static void ibmcam_model2_setup_after_video_if(struct uvd *uvd)
 {
 	unsigned short setup_model2_rg2, setup_model2_sat, setup_model2_yb;
 
@@ -2154,7 +2154,7 @@
 	usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp));
 }
 
-static void ibmcam_model4_setup_after_video_if(uvd_t *uvd)
+static void ibmcam_model4_setup_after_video_if(struct uvd *uvd)
 {
 	switch (uvd->videosize) {
 	case VIDEOSIZE_128x96:
@@ -2704,7 +2704,7 @@
 	usb_clear_halt(uvd->dev, usb_rcvisocpipe(uvd->dev, uvd->video_endp));
 }
 
-static void ibmcam_model3_setup_after_video_if(uvd_t *uvd)
+static void ibmcam_model3_setup_after_video_if(struct uvd *uvd)
 {
 	int i;
 	/*
@@ -3485,7 +3485,7 @@
  * This code tells camera to stop streaming. The interface remains
  * configured and bandwidth - claimed.
  */
-static void ibmcam_video_stop(uvd_t *uvd)
+static void ibmcam_video_stop(struct uvd *uvd)
 {
 	switch (IBMCAM_T(uvd)->camera_model) {
 	case IBMCAM_MODEL_1:
@@ -3546,7 +3546,7 @@
  * History:
  * 1/2/00   Created.
  */
-static void ibmcam_reinit_iso(uvd_t *uvd, int do_stop)
+static void ibmcam_reinit_iso(struct uvd *uvd, int do_stop)
 {
 	switch (IBMCAM_T(uvd)->camera_model) {
 	case IBMCAM_MODEL_1:
@@ -3570,7 +3570,7 @@
 	}
 }
 
-static void ibmcam_video_start(uvd_t *uvd)
+static void ibmcam_video_start(struct uvd *uvd)
 {
 	ibmcam_change_lighting_conditions(uvd);
 	ibmcam_set_sharpness(uvd);
@@ -3580,7 +3580,7 @@
 /*
  * Return negative code on failure, 0 on success.
  */
-static int ibmcam_setup_on_open(uvd_t *uvd)
+static int ibmcam_setup_on_open(struct uvd *uvd)
 {
 	int setup_ok = 0; /* Success by default */
 	/* Send init sequence only once, it's large! */
@@ -3602,7 +3602,7 @@
 	return setup_ok;
 }
 
-static void ibmcam_configure_video(uvd_t *uvd)
+static void ibmcam_configure_video(struct uvd *uvd)
 {
 	if (uvd == NULL)
 		return;
@@ -3658,7 +3658,7 @@
  */
 static void *ibmcam_probe(struct usb_device *dev, unsigned int ifnum, const struct usb_device_id *devid)
 {
-	uvd_t *uvd = NULL;
+	struct uvd *uvd = NULL;
 	int i, nas, model=0, canvasX=0, canvasY=0;
 	int actInterface=-1, inactInterface=-1, maxPS=0;
 	unsigned char video_ep = 0;
@@ -3868,7 +3868,7 @@
 	MOD_INC_USE_COUNT;
 	uvd = usbvideo_AllocateDevice(cams);
 	if (uvd != NULL) {
-		/* Here uvd is a fully allocated uvd_t object */
+		/* Here uvd is a fully allocated uvd object */
 		uvd->flags = flags;
 		uvd->debug = debug;
 		uvd->dev = dev;
@@ -3899,6 +3899,17 @@
 	return uvd;
 }
 
+
+static struct usb_device_id id_table[] = {
+	{ USB_DEVICE_VER(IBMCAM_VENDOR_ID, IBMCAM_PRODUCT_ID, 0x0002, 0x0002) },	/* Model 1 */
+	{ USB_DEVICE_VER(IBMCAM_VENDOR_ID, IBMCAM_PRODUCT_ID, 0x030a, 0x030a) },	/* Model 2 */
+	{ USB_DEVICE_VER(IBMCAM_VENDOR_ID, IBMCAM_PRODUCT_ID, 0x0301, 0x0301) },	/* Model 3 */
+	{ USB_DEVICE_VER(IBMCAM_VENDOR_ID, NETCAM_PRODUCT_ID, 0x030a, 0x030a) },	/* Model 4 */
+	{ USB_DEVICE_VER(IBMCAM_VENDOR_ID, VEO_800C_PRODUCT_ID, 0x030a, 0x030a) },	/* Model 2 */
+	{ USB_DEVICE_VER(IBMCAM_VENDOR_ID, VEO_800D_PRODUCT_ID, 0x030a, 0x030a) },	/* Model 4 */
+	{ }  /* Terminating entry */
+};
+
 /*
  * ibmcam_init()
  *
@@ -3910,7 +3921,7 @@
  */
 static int __init ibmcam_init(void)
 {
-	usbvideo_cb_t cbTbl;
+	struct usbvideo_cb cbTbl;
 	memset(&cbTbl, 0, sizeof(cbTbl));
 	cbTbl.probe = ibmcam_probe;
 	cbTbl.setupOnOpen = ibmcam_setup_on_open;
@@ -3926,7 +3937,8 @@
 		sizeof(ibmcam_t),
 		"ibmcam",
 		&cbTbl,
-		THIS_MODULE);
+		THIS_MODULE,
+		id_table);
 }
 
 static void __exit ibmcam_cleanup(void)
@@ -3934,15 +3946,6 @@
 	usbvideo_Deregister(&cams);
 }
 
-static __devinitdata struct usb_device_id id_table[] = {
-	{ USB_DEVICE_VER(IBMCAM_VENDOR_ID, IBMCAM_PRODUCT_ID, 0x0002, 0x0002) },	/* Model 1 */
-	{ USB_DEVICE_VER(IBMCAM_VENDOR_ID, IBMCAM_PRODUCT_ID, 0x030a, 0x030a) },	/* Model 2 */
-	{ USB_DEVICE_VER(IBMCAM_VENDOR_ID, IBMCAM_PRODUCT_ID, 0x0301, 0x0301) },	/* Model 3 */
-	{ USB_DEVICE_VER(IBMCAM_VENDOR_ID, NETCAM_PRODUCT_ID, 0x030a, 0x030a) },	/* Model 4 */
-	{ USB_DEVICE_VER(IBMCAM_VENDOR_ID, VEO_800C_PRODUCT_ID, 0x030a, 0x030a) },	/* Model 2 */
-	{ USB_DEVICE_VER(IBMCAM_VENDOR_ID, VEO_800D_PRODUCT_ID, 0x030a, 0x030a) },	/* Model 4 */
-	{ }  /* Terminating entry */
-};
 MODULE_DEVICE_TABLE(usb, id_table);
 
 module_init(ibmcam_init);
diff -urN linux-2.4.21-pre2/drivers/usb/kaweth.c linux-2.4.21-pre3/drivers/usb/kaweth.c
--- linux-2.4.21-pre2/drivers/usb/kaweth.c	Thu Nov 28 15:53:14 2002
+++ linux-2.4.21-pre3/drivers/usb/kaweth.c	Mon Jan  6 16:32:32 2003
@@ -691,7 +691,7 @@
 		kaweth_dbg("%s: TX status %d.", kaweth->net->name, urb->status);
 
 	netif_wake_queue(kaweth->net);
-	dev_kfree_skb(skb);
+	dev_kfree_skb_irq(skb);
 }
 
 /****************************************************************
@@ -709,7 +709,7 @@
 	if (kaweth->removed) {
 	/* our device is undergoing disconnection - we bail out */
 		spin_unlock(&kaweth->device_lock);
-		dev_kfree_skb(skb);
+		dev_kfree_skb_irq(skb);
 		return 0;
 	}
 
@@ -721,7 +721,7 @@
 		/* no such luck - we make our own */
 		struct sk_buff *copied_skb;
 		copied_skb = skb_copy_expand(skb, 2, 0, GFP_ATOMIC);
-		dev_kfree_skb_any(skb);
+		dev_kfree_skb_irq(skb);
 		skb = copied_skb;
 		if (!copied_skb) {
 			kaweth->stats.tx_errors++;
@@ -751,7 +751,7 @@
 		kaweth->stats.tx_errors++;
 
 		netif_start_queue(net);
-		dev_kfree_skb(skb);
+		dev_kfree_skb_irq(skb);
 	}
 	else
 	{
diff -urN linux-2.4.21-pre2/drivers/usb/ov511.c linux-2.4.21-pre3/drivers/usb/ov511.c
--- linux-2.4.21-pre2/drivers/usb/ov511.c	Thu Nov 28 15:53:14 2002
+++ linux-2.4.21-pre3/drivers/usb/ov511.c	Mon Jan  6 16:32:32 2003
@@ -1,7 +1,7 @@
 /*
  * OmniVision OV511 Camera-to-USB Bridge Driver
  *
- * Copyright (c) 1999-2002 Mark W. McClelland
+ * Copyright (c) 1999-2003 Mark W. McClelland
  * Original decompression code Copyright 1998-2000 OmniVision Technologies
  * Many improvements by Bret Wallach <bwallac1@san.rr.com>
  * Color fixes by by Orion Sky Lawlor <olawlor@acm.org> (2/26/2000)
@@ -11,6 +11,7 @@
  * Original SAA7111A code by Dave Perks <dperks@ibm.net>
  * URB error messages from pwc driver by Nemosoft
  * generic_ioctl() code from videodev.c by Gerd Knorr and Alan Cox
+ * Memory management (rvmalloc) code from bttv driver, by Gerd Knorr and others
  *
  * Based on the Linux CPiA driver written by Peter Pregler,
  * Scott J. Bertin and Johannes Erdfelt.
@@ -48,6 +49,7 @@
 #include <asm/semaphore.h>
 #include <asm/processor.h>
 #include <linux/wrapper.h>
+#include <linux/mm.h>
 
 #if defined (__i386__)
 	#include <asm/cpufeature.h>
@@ -58,9 +60,9 @@
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v1.61 for Linux 2.4"
+#define DRIVER_VERSION "v1.63 for Linux 2.4"
 #define EMAIL "mark@alpha.dyndns.org"
-#define DRIVER_AUTHOR "Mark McClelland <mmcclell@bigfoot.com> & Bret Wallach \
+#define DRIVER_AUTHOR "Mark McClelland <mark@alpha.dyndns.org> & Bret Wallach \
 	& Orion Sky Lawlor <olawlor@acm.org> & Kevin Moore & Charl P. Botha \
 	<cpbotha@ieee.org> & Claudio Matsuoka <claudio@conectiva.com>"
 #define DRIVER_DESC "ov511 USB Camera Driver"
@@ -97,7 +99,6 @@
 static int cams			= 1;
 static int compress;
 static int testpat;
-static int sensor_gbr;
 static int dumppix;
 static int led 			= 1;
 static int dump_bridge;
@@ -122,6 +123,7 @@
 static int unit_video[OV511_MAX_UNIT_VIDEO];
 static int remove_zeros;
 static int mirror;
+static int ov518_color;
 
 MODULE_PARM(autobright, "i");
 MODULE_PARM_DESC(autobright, "Sensor automatically changes brightness");
@@ -199,6 +201,8 @@
   "Remove zero-padding from uncompressed incoming data");
 MODULE_PARM(mirror, "i");
 MODULE_PARM_DESC(mirror, "Reverse image horizontally");
+MODULE_PARM(ov518_color, "i");
+MODULE_PARM_DESC(ov518_color, "Enable OV518 color (experimental)");
 
 MODULE_AUTHOR(DRIVER_AUTHOR);
 MODULE_DESCRIPTION(DRIVER_DESC);
@@ -222,7 +226,7 @@
 /* MMX support is present in kernel and CPU. Checked upon decomp module load. */
 static int ov51x_mmx_available;
 
-static __devinitdata struct usb_device_id device_table [] = {
+static struct usb_device_id device_table [] = {
 	{ USB_DEVICE(VEND_OMNIVISION, PROD_OV511) },
 	{ USB_DEVICE(VEND_OMNIVISION, PROD_OV511PLUS) },
 	{ USB_DEVICE(VEND_OMNIVISION, PROD_OV518) },
@@ -261,7 +265,9 @@
 	{ 100, "Lifeview RoboCam" },
 	{ 102, "AverMedia InterCam Elite" },
 	{ 112, "MediaForte MV300" },	/* or OV7110 evaluation kit */
+	{ 134, "Ezonics EZCam II" },
 	{ 192, "Webeye 2000B" },
+	{ 253, "Alpha Vision Tech. AlphaCam SE" },
 	{  -1, NULL }
 };
 
@@ -294,6 +300,7 @@
 	{ -1, NULL }
 };
 
+#if defined(CONFIG_VIDEO_PROC_FS)
 static struct symbolic_list senlist[] = {
 	{ SEN_OV76BE,	"OV76BE" },
 	{ SEN_OV7610,	"OV7610" },
@@ -309,6 +316,7 @@
 	{ SEN_SAA7111A,	"SAA7111A" },
 	{ -1, NULL }
 };
+#endif
 
 /* URB error codes: */
 static struct symbolic_list urb_errlist[] = {
@@ -403,7 +411,7 @@
 extern struct proc_dir_entry *video_proc_entry;
 
 static struct file_operations ov511_control_fops = {
-	ioctl:		ov51x_control_ioctl,
+	.ioctl =	ov51x_control_ioctl,
 };
 
 #define YES_NO(x) ((x) ? "yes" : "no")
@@ -463,6 +471,8 @@
 		       symbolic(senlist, ov->sensor));
 	out += sprintf(out, "packet_size     : %d\n", ov->packet_size);
 	out += sprintf(out, "framebuffer     : 0x%p\n", ov->fbuf);
+	out += sprintf(out, "packet_numbering: %d\n", ov->packet_numbering);
+	out += sprintf(out, "topology        : %s\n", ov->usb_path);
 
 	len = out - page;
 	len -= off;
@@ -554,8 +564,8 @@
 			ov511_read_proc_button, ov);
 		if (!ov->proc_button)
 			return;
+		ov->proc_button->owner = THIS_MODULE;
 	}
-	ov->proc_button->owner = THIS_MODULE;
 
 	/* Create "control" entry (ioctl() interface) */
 	PDEBUG(4, "creating /proc/video/ov511/%s/control", dirname);
@@ -1133,8 +1143,6 @@
 	return 0;
 }
 
-#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
-
 /* Write to a specific I2C slave ID and register, using the specified mask */
 static int
 i2c_w_slave(struct usb_ov511 *ov,
@@ -1190,8 +1198,6 @@
 	return rc;
 }
 
-#endif /* defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) */
-
 /* Sets I2C read and write slave IDs. Returns <0 for error */
 static int
 ov51x_set_slave_ids(struct usb_ov511 *ov, unsigned char sid)
@@ -1237,8 +1243,7 @@
 static void
 dump_i2c_range(struct usb_ov511 *ov, int reg1, int regn)
 {
-	int i;
-	int rc;
+	int i, rc;
 
 	for (i = reg1; i <= regn; i++) {
 		rc = i2c_r(ov, i);
@@ -1256,8 +1261,7 @@
 static void
 dump_reg_range(struct usb_ov511 *ov, int reg1, int regn)
 {
-	int i;
-	int rc;
+	int i, rc;
 
 	for (i = reg1; i <= regn; i++) {
 		rc = reg_r(ov, i);
@@ -1265,7 +1269,6 @@
 	}
 }
 
-/* FIXME: Should there be an OV518 version of this? */
 static void
 ov511_dump_regs(struct usb_ov511 *ov)
 {
@@ -1291,6 +1294,31 @@
 	dump_reg_range(ov, 0xa0, 0xbf);
 
 }
+
+static void
+ov518_dump_regs(struct usb_ov511 *ov)
+{
+	info("VIDEO MODE REGS");
+	dump_reg_range(ov, 0x20, 0x2f);
+	info("DATA PUMP AND SNAPSHOT REGS");
+	dump_reg_range(ov, 0x30, 0x3f);
+	info("I2C REGS");
+	dump_reg_range(ov, 0x40, 0x4f);
+	info("SYSTEM CONTROL AND VENDOR REGS");
+	dump_reg_range(ov, 0x50, 0x5f);
+	info("60 - 6F");
+	dump_reg_range(ov, 0x60, 0x6f);
+	info("70 - 7F");
+	dump_reg_range(ov, 0x70, 0x7f);
+	info("Y QUANTIZATION TABLE");
+	dump_reg_range(ov, 0x80, 0x8f);
+	info("UV QUANTIZATION TABLE");
+	dump_reg_range(ov, 0x90, 0x9f);
+	info("A0 - BF");
+	dump_reg_range(ov, 0xa0, 0xbf);
+	info("CBR");
+	dump_reg_range(ov, 0xc0, 0xcf);
+}
 #endif
 
 /*****************************************************************************/
@@ -1332,9 +1360,9 @@
 ov51x_clear_snapshot(struct usb_ov511 *ov)
 {
 	if (ov->bclass == BCL_OV511) {
-		reg_w(ov, R51x_SYS_SNAP, 0x01);
-		reg_w(ov, R51x_SYS_SNAP, 0x03);
-		reg_w(ov, R51x_SYS_SNAP, 0x01);
+		reg_w(ov, R51x_SYS_SNAP, 0x00);
+		reg_w(ov, R51x_SYS_SNAP, 0x02);
+		reg_w(ov, R51x_SYS_SNAP, 0x00);
 	} else if (ov->bclass == BCL_OV518) {
 		warn("snapshot reset not supported yet on OV518(+)");
 	} else {
@@ -1379,7 +1407,7 @@
 	if (i2c_w(ov, 0x12, 0x80) < 0) return -EIO;
 
 	/* Wait for it to initialize */
-	schedule_timeout (1 + 150 * HZ / 1000);
+	schedule_timeout(1 + 150 * HZ / 1000);
 
 	for (i = 0, success = 0; i < i2c_detect_tries && !success; i++) {
 		if ((i2c_r(ov, OV7610_REG_ID_HIGH) == 0x7F) &&
@@ -2014,10 +2042,6 @@
 
 	p->whiteness = 105 << 8;
 
-	/* Can we get these from frame[0]? -claudio? */
-	p->depth = ov->frame[0].depth;
-	p->palette = ov->frame[0].format;
-
 	return 0;
 }
 
@@ -2425,9 +2449,10 @@
 #endif
 		break;
 	case SEN_OV6620:
-	case SEN_OV6630:
 		i2c_w(ov, 0x14, qvga?0x24:0x04);
-		/* No special settings yet */
+		break;
+	case SEN_OV6630:
+		i2c_w(ov, 0x14, qvga?0xa0:0x80);
 		break;
 	default:
 		err("Invalid sensor");
@@ -2441,13 +2466,33 @@
 			/* these aren't valid on the OV6620/OV7620/6630? */
 			i2c_w_mask(ov, 0x0e, 0x40, 0x40);
 		}
-		i2c_w_mask(ov, 0x13, 0x20, 0x20);
+
+		if (ov->sensor == SEN_OV6630 && ov->bridge == BRG_OV518
+		    && ov518_color) {
+			i2c_w_mask(ov, 0x12, 0x00, 0x10);
+			i2c_w_mask(ov, 0x13, 0x00, 0x20);
+		} else {
+			i2c_w_mask(ov, 0x13, 0x20, 0x20);
+		}
 	} else {
 		if (ov->sensor == SEN_OV7610 || ov->sensor == SEN_OV76BE) {
 			/* not valid on the OV6620/OV7620/6630? */
 			i2c_w_mask(ov, 0x0e, 0x00, 0x40);
 		}
-		i2c_w_mask(ov, 0x13, 0x00, 0x20);
+
+		/* The OV518 needs special treatment. Although both the OV518
+		 * and the OV6630 support a 16-bit video bus, only the 8 bit Y
+		 * bus is actually used. The UV bus is tied to ground.
+		 * Therefore, the OV6630 needs to be in 8-bit multiplexed
+		 * output mode */
+
+		if (ov->sensor == SEN_OV6630 && ov->bridge == BRG_OV518
+		    && ov518_color) {
+			i2c_w_mask(ov, 0x12, 0x10, 0x10);
+			i2c_w_mask(ov, 0x13, 0x20, 0x20);
+		} else {
+			i2c_w_mask(ov, 0x13, 0x00, 0x20);
+		}
 	}
 
 	/******** Clock programming ********/
@@ -2505,19 +2550,11 @@
 	if (framedrop >= 0)
 		i2c_w(ov, 0x16, framedrop);
 
-	if (sensor_gbr)
-		i2c_w_mask(ov, 0x12, 0x08, 0x08);
-	else
-		i2c_w_mask(ov, 0x12, 0x00, 0x08);
-
 	/* Test Pattern */
 	i2c_w_mask(ov, 0x12, (testpat?0x02:0x00), 0x02);
 
-	/* Auto white balance */
-//	if (awb)
-		i2c_w_mask(ov, 0x12, 0x04, 0x04);
-//	else
-//		i2c_w_mask(ov, 0x12, 0x00, 0x04);
+	/* Enable auto white balance */
+	i2c_w_mask(ov, 0x12, 0x04, 0x04);
 
 	// This will go away as soon as ov51x_mode_init_sensor_regs()
 	// is fully tested.
@@ -2706,7 +2743,7 @@
 	reg_w(ov, R511_CAM_PXDIV, 0x00);
 	reg_w(ov, R511_CAM_LNDIV, 0x00);
 
-	/* YUV420, low pass filer on */
+	/* YUV420, low pass filter on */
 	reg_w(ov, R511_CAM_OPTS, 0x03);
 
 	/* Snapshot additions */
@@ -2782,8 +2819,29 @@
 	reg_w(ov, 0x3d, 0);
 	reg_w(ov, 0x3e, 0);
 
-	reg_w(ov, 0x28, (mode == VIDEO_PALETTE_GREY) ? 0x00:0x80);
-	reg_w(ov, 0x38, (mode == VIDEO_PALETTE_GREY) ? 0x00:0x80);
+	if (ov->bridge == BRG_OV518 && ov518_color) {
+		/* OV518 needs U and V swapped */
+		i2c_w_mask(ov, 0x15, 0x00, 0x01);
+
+	 	if (mode == VIDEO_PALETTE_GREY) {
+			/* Set 16-bit input format (UV data are ignored) */
+			reg_w_mask(ov, 0x20, 0x00, 0x08);
+
+			/* Set 8-bit (4:0:0) output format */
+			reg_w_mask(ov, 0x28, 0x00, 0xf0);
+			reg_w_mask(ov, 0x38, 0x00, 0xf0);
+		} else {
+			/* Set 8-bit (YVYU) input format */
+			reg_w_mask(ov, 0x20, 0x08, 0x08);
+
+			/* Set 12-bit (4:2:0) output format */
+			reg_w_mask(ov, 0x28, 0x80, 0xf0);
+			reg_w_mask(ov, 0x38, 0x80, 0xf0);
+		}
+	} else {
+		reg_w(ov, 0x28, (mode == VIDEO_PALETTE_GREY) ? 0x00:0x80);
+		reg_w(ov, 0x38, (mode == VIDEO_PALETTE_GREY) ? 0x00:0x80);
+	}
 
 	hsegs = width / 16;
 	vsegs = height / 4;
@@ -3176,7 +3234,7 @@
 }
 
 /*
- * For RAW BW (YUV400) images, data shows up in 256 byte segments.
+ * For RAW BW (YUV 4:0:0) images, data show up in 256 byte segments.
  * The segments represent 4 squares of 8x8 pixels as follows:
  *
  *      0  1 ...  7    64  65 ...  71   ...  192 193 ... 199
@@ -3207,7 +3265,7 @@
 }
 
 /*
- * For YUV4:2:0 images, the data shows up in 384 byte segments.
+ * For YUV 4:2:0 images, the data show up in 384 byte segments.
  * The first 64 bytes of each segment are U, the next 64 are V.  The U and
  * V are arranged as follows:
  *
@@ -3226,8 +3284,8 @@
  *           ...              ...                    ...
  *     56 57 ... 63   120 121 ... 127   ...  248 249 ... 255
  *
- * Note that the U and V data in one segment represents a 16 x 16 pixel
- * area, but the Y data represents a 32 x 8 pixel area. If the width is not an
+ * Note that the U and V data in one segment represent a 16 x 16 pixel
+ * area, but the Y data represent a 32 x 8 pixel area. If the width is not an
  * even multiple of 32, the extra 8x8 blocks within a 32x8 block belong to the
  * next horizontal stripe.
  *
@@ -3235,7 +3293,7 @@
  * verbatim, in order, into the frame. When used with vidcat -f ppm -s 640x480
  * this puts the data on the standard output and can be analyzed with the
  * parseppm.c utility I wrote.  That's a much faster way for figuring out how
- * this data is scrambled.
+ * these data are scrambled.
  */
 
 /* Converts from raw, uncompressed segments at pIn0 to a YUV420P frame at pOut0.
@@ -4144,7 +4202,10 @@
 		}
 	}
 
+	/* Resubmit this URB */
 	urb->dev = ov->dev;
+	if ((i = usb_submit_urb(urb)) != 0)
+		err("usb_submit_urb() ret %d", i);
 
 	return;
 }
@@ -4216,6 +4277,7 @@
 
 	for (n = 0; n < OV511_NUMSBUF; n++) {
 		urb = usb_alloc_urb(FRAMES_PER_DESC);
+
 		if (!urb) {
 			err("init isoc: usb_alloc_urb ret. NULL");
 			return -ENOMEM;
@@ -4229,6 +4291,7 @@
 		urb->complete = ov51x_isoc_irq;
 		urb->number_of_packets = FRAMES_PER_DESC;
 		urb->transfer_buffer_length = ov->packet_size * FRAMES_PER_DESC;
+		urb->interval = 1;
 		for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
 			urb->iso_frame_desc[fx].offset = ov->packet_size * fx;
 			urb->iso_frame_desc[fx].length = ov->packet_size;
@@ -4237,10 +4300,6 @@
 
 	ov->streaming = 1;
 
-	ov->sbuf[OV511_NUMSBUF - 1].urb->next = ov->sbuf[0].urb;
-	for (n = 0; n < OV511_NUMSBUF - 1; n++)
-		ov->sbuf[n].urb->next = ov->sbuf[n+1].urb;
-
 	for (n = 0; n < OV511_NUMSBUF; n++) {
 		ov->sbuf[n].urb->dev = ov->dev;
 		err = usb_submit_urb(ov->sbuf[n].urb);
@@ -4398,11 +4457,6 @@
 	PDEBUG(4, "entered");
 	down(&ov->buf_lock);
 
-	if (ov->buf_state == BUF_PEND_DEALLOC) {
-		ov->buf_state = BUF_ALLOCATED;
-		del_timer(&ov->buf_timer);
-	}
-
 	if (ov->buf_state == BUF_ALLOCATED)
 		goto out;
 
@@ -4475,8 +4529,9 @@
  ***************************************************************************/
 
 static int
-ov51x_v4l1_open(struct video_device *vdev, int flags)
+ov51x_v4l1_open(struct inode *inode, struct file *file)
 {
+	struct video_device *vdev = video_devdata(file);
 	struct usb_ov511 *ov = vdev->priv;
 	int err, i;
 
@@ -4520,6 +4575,7 @@
 	}
 
 	ov->user++;
+	file->private_data = vdev;
 
 	if (ov->led_policy == LED_AUTO)
 		ov51x_led_control(ov, 1);
@@ -4529,9 +4585,10 @@
 	return err;
 }
 
-static void
-ov51x_v4l1_close(struct video_device *vdev)
+static int
+ov51x_v4l1_close(struct inode *inode, struct file *file)
 {
+	struct video_device *vdev = file->private_data;
 	struct usb_ov511 *ov = vdev->priv;
 
 	PDEBUG(4, "ov511_close");
@@ -4560,19 +4617,22 @@
 		up(&ov->cbuf_lock);
 
 		ov51x_dealloc(ov, 1);
-		video_unregister_device(&ov->vdev);
 		kfree(ov);
 		ov = NULL;
 	}
 
-	return;
+	file->private_data = NULL;
+	return 0;
 }
 
 /* Do not call this function directly! */
 static int
-ov51x_v4l1_ioctl_internal(struct usb_ov511 *ov, unsigned int cmd,
-			  void *arg)
+ov51x_v4l1_ioctl_internal(struct inode *inode, struct file *file,
+			  unsigned int cmd, void *arg)
 {
+	struct video_device *vdev = file->private_data;
+	struct usb_ov511 *ov = vdev->priv;
+
 	PDEBUG(5, "IOCtl: 0x%X", cmd);
 
 	if (!ov->dev)
@@ -4666,6 +4726,10 @@
 		if (sensor_get_picture(ov, p))
 			return -EIO;
 
+		/* Can we get these from frame[0]? -claudio? */
+		p->depth = ov->frame[0].depth;
+		p->palette = ov->frame[0].format;
+
 		return 0;
 	}
 	case VIDIOCSPICT:
@@ -4830,8 +4894,8 @@
 
 		depth = get_depth(vm->format);
 		if (!depth) {
-			err("VIDIOCMCAPTURE: invalid format (%s)",
-			    symbolic(v4l1_plist, vm->format));
+			PDEBUG(2, "VIDIOCMCAPTURE: invalid format (%s)",
+			       symbolic(v4l1_plist, vm->format));
 			return -EINVAL;
 		}
 
@@ -4852,8 +4916,8 @@
 		}
 
 		if (force_palette && (vm->format != force_palette)) {
-			info("palette rejected (%s)",
-			     symbolic(v4l1_plist, vm->format));
+			PDEBUG(2, "palette rejected (%s)",
+			       symbolic(v4l1_plist, vm->format));
 			return -EINVAL;
 		}
 
@@ -4982,88 +5046,55 @@
 
 		return 0;
 	}
-	default:
-		PDEBUG(3, "Unsupported IOCtl: 0x%X", cmd);
-		return -ENOIOCTLCMD;
-	} /* end switch */
-
-	return 0;
-}
-
-/* This is implemented as video_generic_ioctl() in the new V4L's videodev.c */
-static int
-ov51x_v4l1_generic_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
-{
-	char	sbuf[128];
-	void    *mbuf = NULL;
-	void	*parg = NULL;
-	int	err  = -EINVAL;
-
-	/*  Copy arguments into temp kernel buffer  */
-	switch (_IOC_DIR(cmd)) {
-	case _IOC_NONE:
-		parg = arg;
-		break;
-	case _IOC_READ: /* some v4l ioctls are marked wrong ... */
-	case _IOC_WRITE:
-	case (_IOC_WRITE | _IOC_READ):
-		if (_IOC_SIZE(cmd) <= sizeof(sbuf)) {
-			parg = sbuf;
-		} else {
-			/* too big to allocate from stack */
-			mbuf = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL);
-			if (NULL == mbuf)
-				return -ENOMEM;
-			parg = mbuf;
-		}
+	case OV511IOC_WI2C:
+	{
+		struct ov511_i2c_struct *w = arg;
 
-		err = -EFAULT;
-		if (copy_from_user(parg, arg, _IOC_SIZE(cmd)))
-			goto out;
-		break;
+		return i2c_w_slave(ov, w->slave, w->reg, w->value, w->mask);
 	}
+	case OV511IOC_RI2C:
+	{
+		struct ov511_i2c_struct *r = arg;
+		int rc;
 
-	err = ov51x_v4l1_ioctl_internal(vdev->priv, cmd, parg);
-	if (err == -ENOIOCTLCMD)
-		err = -EINVAL;
-	if (err < 0)
-		goto out;
+		rc = i2c_r_slave(ov, r->slave, r->reg);
+		if (rc < 0)
+			return rc;
 
-	/*  Copy results into user buffer  */
-	switch (_IOC_DIR(cmd))
-	{
-	case _IOC_READ:
-	case (_IOC_WRITE | _IOC_READ):
-		if (copy_to_user(arg, parg, _IOC_SIZE(cmd)))
-			err = -EFAULT;
-		break;
+		r->value = rc;
+		return 0;
 	}
+	default:
+		PDEBUG(3, "Unsupported IOCtl: 0x%X", cmd);
+		return -ENOIOCTLCMD;
+	} /* end switch */
 
-out:
-	if (mbuf)
-		kfree(mbuf);
-	return err;
+	return 0;
 }
 
 static int
-ov51x_v4l1_ioctl(struct video_device *vdev, unsigned int cmd, void *arg)
+ov51x_v4l1_ioctl(struct inode *inode, struct file *file,
+		 unsigned int cmd, unsigned long arg)
 {
+	struct video_device *vdev = file->private_data;
 	struct usb_ov511 *ov = vdev->priv;
 	int rc;
 
 	if (down_interruptible(&ov->lock))
 		return -EINTR;
 
-	rc = ov51x_v4l1_generic_ioctl(vdev, cmd, arg);
+	rc = video_usercopy(inode, file, cmd, arg, ov51x_v4l1_ioctl_internal);
 
 	up(&ov->lock);
 	return rc;
 }
 
-static inline long
-ov51x_v4l1_read(struct video_device *vdev, char *buf, unsigned long count,
-		int noblock)
+static int
+ov51x_v4l1_read(struct file *file, char *buf, size_t cnt, loff_t *ppos)
 {
+	struct video_device *vdev = file->private_data;
+	int noblock = file->f_flags&O_NONBLOCK;
+	unsigned long count = cnt;
 	struct usb_ov511 *ov = vdev->priv;
 	int i, rc = 0, frmx = -1;
 	struct ov511_frame *frame;
@@ -5188,7 +5219,7 @@
 	PDEBUG(4, "{copy} count used=%ld, new bytes_read=%ld",
 		count, frame->bytes_read);
 
-	/* If all data has been read... */
+	/* If all data have been read... */
 	if (frame->bytes_read
 	    >= get_frame_length(frame)) {
 		frame->bytes_read = 0;
@@ -5213,9 +5244,11 @@
 }
 
 static int
-ov51x_v4l1_mmap(struct video_device *vdev, const char *adr, unsigned long size)
+ov51x_v4l1_mmap(struct file *file, struct vm_area_struct *vma)
 {
-	unsigned long start = (unsigned long)adr;
+	struct video_device *vdev = file->private_data;
+	unsigned long start = vma->vm_start;
+	unsigned long size  = vma->vm_end - vma->vm_start;
 	struct usb_ov511 *ov = vdev->priv;
 	unsigned long page, pos;
 
@@ -5251,16 +5284,22 @@
 	return 0;
 }
 
+static struct file_operations ov511_fops = {
+	.owner =	THIS_MODULE,
+	.open =		ov51x_v4l1_open,
+	.release =	ov51x_v4l1_close,
+	.read =		ov51x_v4l1_read,
+	.mmap =		ov51x_v4l1_mmap,
+	.ioctl =	ov51x_v4l1_ioctl,
+	.llseek =	no_llseek,
+};
+
 static struct video_device vdev_template = {
-	owner:		THIS_MODULE,
-	name:		"OV511 USB Camera",
-	type:		VID_TYPE_CAPTURE,
-	hardware:	VID_HARDWARE_OV511,
-	open:		ov51x_v4l1_open,
-	close:		ov51x_v4l1_close,
-	read:		ov51x_v4l1_read,
-	ioctl:		ov51x_v4l1_ioctl,
-	mmap:		ov51x_v4l1_mmap,
+	.owner =	THIS_MODULE,
+	.name =		"OV511 USB Camera",
+	.type =		VID_TYPE_CAPTURE,
+	.hardware =	VID_HARDWARE_OV511,
+	.fops =		&ov511_fops,
 };
 
 #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
@@ -5450,8 +5489,7 @@
 		if (copy_from_user(&w, arg, sizeof(w)))
 			return -EFAULT;
 
-		return i2c_w_slave(ov, w.slave, w.reg, w.value,
-			w.mask);
+		return i2c_w_slave(ov, w.slave, w.reg, w.value,	w.mask);
 	}
 	case OV511IOC_RI2C:
 	{
@@ -5657,19 +5695,16 @@
 		ov->sensor = SEN_OV7610;
 	} else if ((rc & 3) == 1) {
 		/* I don't know what's different about the 76BE yet. */
-		if (i2c_r(ov, 0x15) & 1) {
+		if (i2c_r(ov, 0x15) & 1)
 			info("Sensor is an OV7620AE");
-			info("PLEASE REPORT THE EXISTENCE OF THIS SENSOR TO");
-			info("THE DRIVER AUTHOR");
-		} else {
+		else
 			info("Sensor is an OV76BE");
-		}
 
 		/* OV511+ will return all zero isoc data unless we
 		 * configure the sensor as a 7620. Someone needs to
 		 * find the exact reg. setting that causes this. */
 		if (ov->bridge == BRG_OV511PLUS) {
-			info("Enabling 511+/76BE workaround");
+			info("Enabling 511+/7620AE workaround");
 			ov->sensor = SEN_OV7620;
 		} else {
 			ov->sensor = SEN_OV76BE;
@@ -5754,86 +5789,61 @@
 		{ OV511_I2C_BUS, 0x4f, 0x04 },
 // Do 50-53 have any effect?
 // Toggle 0x12[2] off and on here?
-		{ OV511_DONE_BUS, 0x0, 0x00 },
+		{ OV511_DONE_BUS, 0x0, 0x00 },	/* END MARKER */
 	};
 
-	/* This chip is undocumented so many of these are guesses. OK=verified,
-	 * A=Added since 6620, U=unknown function (not a 6620 reg) */
 	static struct ov511_regvals aRegvalsNorm6x30[] = {
-	/*OK*/	{ OV511_I2C_BUS, 0x12, 0x80 }, /* reset */
-	/*00?*/	{ OV511_I2C_BUS, 0x11, 0x01 },
-	/*OK*/	{ OV511_I2C_BUS, 0x03, 0x60 },
-	/*0A?*/	{ OV511_I2C_BUS, 0x05, 0x7f }, /* For when autoadjust is off */
+		{ OV511_I2C_BUS, 0x12, 0x80 }, /* reset */
+		{ OV511_I2C_BUS, 0x11, 0x00 },
+		{ OV511_I2C_BUS, 0x03, 0x60 },
+		{ OV511_I2C_BUS, 0x05, 0x7f }, /* For when autoadjust is off */
 		{ OV511_I2C_BUS, 0x07, 0xa8 },
 		/* The ratio of 0x0c and 0x0d  controls the white point */
-	/*OK*/	{ OV511_I2C_BUS, 0x0c, 0x24 },
-	/*OK*/	{ OV511_I2C_BUS, 0x0d, 0x24 },
-	/*A*/	{ OV511_I2C_BUS, 0x0e, 0x20 },
-
-//	/*24?*/	{ OV511_I2C_BUS, 0x12, 0x28 }, /* Enable AGC */
-//		{ OV511_I2C_BUS, 0x12, 0x24 }, /* Enable AGC */
-
-//	/*A*/	{ OV511_I2C_BUS, 0x13, 0x21 },
-//	/*A*/	{ OV511_I2C_BUS, 0x13, 0x25 }, /* Tristate Y and UV busses */
-
-//	/*04?*/	{ OV511_I2C_BUS, 0x14, 0x80 },
-		/* 0x16: 0x06 helps frame stability with moving objects */
-	/*03?*/	{ OV511_I2C_BUS, 0x16, 0x06 },
-//	/*OK*/	{ OV511_I2C_BUS, 0x20, 0x30 }, /* Aperture correction enable */
+		{ OV511_I2C_BUS, 0x0c, 0x24 },
+		{ OV511_I2C_BUS, 0x0d, 0x24 },
+		{ OV511_I2C_BUS, 0x0e, 0x20 },
+//		{ OV511_I2C_BUS, 0x14, 0x80 },
+		{ OV511_I2C_BUS, 0x16, 0x03 },
+//		{ OV511_I2C_BUS, 0x20, 0x30 }, /* Aperture correction enable */
 		// 21 & 22? The suggested values look wrong. Go with default
-	/*A*/	{ OV511_I2C_BUS, 0x23, 0xc0 },
-	/*A*/	{ OV511_I2C_BUS, 0x25, 0x9a }, // Check this against default
-//	/*OK*/	{ OV511_I2C_BUS, 0x26, 0xb2 }, /* BLC enable */
+		{ OV511_I2C_BUS, 0x23, 0xc0 },
+		{ OV511_I2C_BUS, 0x25, 0x9a }, // Check this against default
+//		{ OV511_I2C_BUS, 0x26, 0xb2 }, /* BLC enable */
 
 		/* 0x28: 0x05 Selects RGB format if RGB on */
-//	/*04?*/	{ OV511_I2C_BUS, 0x28, 0x05 },
-//	/*04?*/	{ OV511_I2C_BUS, 0x28, 0x45 }, // DEBUG: Tristate UV bus
+//		{ OV511_I2C_BUS, 0x28, 0x05 },
+//		{ OV511_I2C_BUS, 0x28, 0x45 }, // DEBUG: Tristate UV bus
 
-	/*OK*/	{ OV511_I2C_BUS, 0x2a, 0x04 }, /* Disable framerate adjust */
-//	/*OK*/	{ OV511_I2C_BUS, 0x2b, 0xac }, /* Framerate; Set 2a[7] first */
-//	/*U*/	{ OV511_I2C_BUS, 0x2c, 0xa0 },
+		{ OV511_I2C_BUS, 0x2a, 0x04 }, /* Disable framerate adjust */
+//		{ OV511_I2C_BUS, 0x2b, 0xac }, /* Framerate; Set 2a[7] first */
 		{ OV511_I2C_BUS, 0x2d, 0x99 },
-//	/*A*/	{ OV511_I2C_BUS, 0x33, 0x26 }, // Reserved bits on 6620
-//	/*d2?*/	{ OV511_I2C_BUS, 0x34, 0x03 }, /* Max A/D range */
-//	/*U*/	{ OV511_I2C_BUS, 0x36, 0x8f }, // May not be necessary
-//	/*U*/	{ OV511_I2C_BUS, 0x37, 0x80 }, // May not be necessary
-//	/*8b?*/	{ OV511_I2C_BUS, 0x38, 0x83 },
-//	/*40?*/	{ OV511_I2C_BUS, 0x39, 0xc0 }, // 6630 adds bit 7
+//		{ OV511_I2C_BUS, 0x33, 0x26 }, // Reserved bits on 6620
+//		{ OV511_I2C_BUS, 0x34, 0x03 }, /* Max A/D range */
+//		{ OV511_I2C_BUS, 0x38, 0x83 },
+//		{ OV511_I2C_BUS, 0x39, 0xc0 }, // 6630 adds bit 7
 //		{ OV511_I2C_BUS, 0x3c, 0x39 }, /* Enable AEC mode changing */
 //		{ OV511_I2C_BUS, 0x3c, 0x3c }, /* Change AEC mode */
 //		{ OV511_I2C_BUS, 0x3c, 0x24 }, /* Disable AEC mode changing */
-	/*OK*/	{ OV511_I2C_BUS, 0x3d, 0x80 },
-//	/*A*/	{ OV511_I2C_BUS, 0x3f, 0x0e },
-//	/*U*/	{ OV511_I2C_BUS, 0x40, 0x00 },
-//	/*U*/	{ OV511_I2C_BUS, 0x41, 0x00 },
-//	/*U*/	{ OV511_I2C_BUS, 0x42, 0x80 },
-//	/*U*/	{ OV511_I2C_BUS, 0x43, 0x3f },
-//	/*U*/	{ OV511_I2C_BUS, 0x44, 0x80 },
-//	/*U*/	{ OV511_I2C_BUS, 0x45, 0x20 },
-//	/*U*/	{ OV511_I2C_BUS, 0x46, 0x20 },
-//	/*U*/	{ OV511_I2C_BUS, 0x47, 0x80 },
-//	/*U*/	{ OV511_I2C_BUS, 0x48, 0x7f },
-//	/*U*/	{ OV511_I2C_BUS, 0x49, 0x00 },
+		{ OV511_I2C_BUS, 0x3d, 0x80 },
+//		{ OV511_I2C_BUS, 0x3f, 0x0e },
 
 		/* These next two registers (0x4a, 0x4b) are undocumented. They
 		 * control the color balance */
-//	/*OK?*/	{ OV511_I2C_BUS, 0x4a, 0x80 }, // Check these
-//	/*OK?*/	{ OV511_I2C_BUS, 0x4b, 0x80 },
-//	/*U*/	{ OV511_I2C_BUS, 0x4c, 0xd0 },
-	/*d2?*/	{ OV511_I2C_BUS, 0x4d, 0x10 }, /* This reduces noise a bit */
-	/*c1?*/	{ OV511_I2C_BUS, 0x4e, 0x40 },
-	/*04?*/	{ OV511_I2C_BUS, 0x4f, 0x07 },
-//	/*U*/	{ OV511_I2C_BUS, 0x50, 0xff },
-	/*U*/	{ OV511_I2C_BUS, 0x54, 0x23 },
-//	/*U*/	{ OV511_I2C_BUS, 0x55, 0xff },
-//	/*U*/	{ OV511_I2C_BUS, 0x56, 0x12 },
-	/*U*/	{ OV511_I2C_BUS, 0x57, 0x81 },
-//	/*U*/	{ OV511_I2C_BUS, 0x58, 0x75 },
-	/*U*/	{ OV511_I2C_BUS, 0x59, 0x01 },
-	/*U*/	{ OV511_I2C_BUS, 0x5a, 0x2c },
-	/*U*/	{ OV511_I2C_BUS, 0x5b, 0x0f },
-//	/*U*/	{ OV511_I2C_BUS, 0x5c, 0x10 },
-		{ OV511_DONE_BUS, 0x0, 0x00 },
+//		{ OV511_I2C_BUS, 0x4a, 0x80 }, // Check these
+//		{ OV511_I2C_BUS, 0x4b, 0x80 },
+		{ OV511_I2C_BUS, 0x4d, 0x10 }, /* U = 0.563u, V = 0.714v */
+		{ OV511_I2C_BUS, 0x4e, 0x40 },
+
+		/* UV average mode, color killer: strongest */
+		{ OV511_I2C_BUS, 0x4f, 0x07 },
+
+		{ OV511_I2C_BUS, 0x54, 0x23 }, /* Max AGC gain: 18dB */
+		{ OV511_I2C_BUS, 0x57, 0x81 }, /* (default) */
+		{ OV511_I2C_BUS, 0x59, 0x01 }, /* AGC dark current comp: +1 */
+		{ OV511_I2C_BUS, 0x5a, 0x2c }, /* (undocumented) */
+		{ OV511_I2C_BUS, 0x5b, 0x0f }, /* AWB chrominance levels */
+//		{ OV511_I2C_BUS, 0x5c, 0x10 },
+		{ OV511_DONE_BUS, 0x0, 0x00 },	/* END MARKER */
 	};
 
 	PDEBUG(4, "starting sensor configuration");
@@ -5854,16 +5864,19 @@
 		return -1;
 	}
 
-	if ((rc & 3) == 0)
+	if ((rc & 3) == 0) {
 		ov->sensor = SEN_OV6630;
-	else if ((rc & 3) == 1)
+		info("Sensor is an OV6630");
+	} else if ((rc & 3) == 1) {
 		ov->sensor = SEN_OV6620;
-	else if ((rc & 3) == 2)
+		info("Sensor is an OV6620");
+	} else if ((rc & 3) == 2) {
 		ov->sensor = SEN_OV6630;
-	else if ((rc & 3) == 3)
+		info("Sensor is an OV6630AE");
+	} else if ((rc & 3) == 3) {
 		ov->sensor = SEN_OV6630;
-
-	info("Sensor is an %s", symbolic(senlist, ov->sensor));
+		info("Sensor is an OV6630AF");
+	}
 
 	/* Set sensor-specific vars */
 	ov->maxwidth = 352;
@@ -6061,9 +6074,9 @@
 
 	static struct ov511_regvals aRegvalsNorm511[] = {
 		{ OV511_REG_BUS, R511_DRAM_FLOW_CTL, 	0x01 },
-		{ OV511_REG_BUS, R51x_SYS_SNAP,		0x01 },
-		{ OV511_REG_BUS, R51x_SYS_SNAP,		0x03 },
-		{ OV511_REG_BUS, R51x_SYS_SNAP,		0x01 },
+		{ OV511_REG_BUS, R51x_SYS_SNAP,		0x00 },
+		{ OV511_REG_BUS, R51x_SYS_SNAP,		0x02 },
+		{ OV511_REG_BUS, R51x_SYS_SNAP,		0x00 },
 		{ OV511_REG_BUS, R511_FIFO_OPTS,	0x1f },
 		{ OV511_REG_BUS, R511_COMP_EN,		0x00 },
 		{ OV511_REG_BUS, R511_COMP_LUT_EN,	0x03 },
@@ -6072,9 +6085,9 @@
 
 	static struct ov511_regvals aRegvalsNorm511Plus[] = {
 		{ OV511_REG_BUS, R511_DRAM_FLOW_CTL,	0xff },
-		{ OV511_REG_BUS, R51x_SYS_SNAP,		0x01 },
-		{ OV511_REG_BUS, R51x_SYS_SNAP,		0x03 },
-		{ OV511_REG_BUS, R51x_SYS_SNAP,		0x01 },
+		{ OV511_REG_BUS, R51x_SYS_SNAP,		0x00 },
+		{ OV511_REG_BUS, R51x_SYS_SNAP,		0x02 },
+		{ OV511_REG_BUS, R51x_SYS_SNAP,		0x00 },
 		{ OV511_REG_BUS, R511_FIFO_OPTS,	0xff },
 		{ OV511_REG_BUS, R511_COMP_EN,		0x00 },
 		{ OV511_REG_BUS, R511_COMP_LUT_EN,	0x03 },
@@ -6223,7 +6236,7 @@
 		{ OV511_REG_BUS, 0x5d,			0x03 },
 		{ OV511_REG_BUS, 0x24,			0x9f },
 		{ OV511_REG_BUS, 0x25,			0x90 },
-		{ OV511_REG_BUS, 0x20,			0x00 }, /* Was 0x08 */
+		{ OV511_REG_BUS, 0x20,			0x00 },
 		{ OV511_REG_BUS, 0x51,			0x04 },
 		{ OV511_REG_BUS, 0x71,			0x19 },
 		{ OV511_DONE_BUS, 0x0, 0x00 },
@@ -6236,7 +6249,7 @@
 		{ OV511_REG_BUS, 0x5d,			0x03 },
 		{ OV511_REG_BUS, 0x24,			0x9f },
 		{ OV511_REG_BUS, 0x25,			0x90 },
-		{ OV511_REG_BUS, 0x20,			0x60 }, /* Was 0x08 */
+		{ OV511_REG_BUS, 0x20,			0x60 },
 		{ OV511_REG_BUS, 0x51,			0x02 },
 		{ OV511_REG_BUS, 0x71,			0x19 },
 		{ OV511_REG_BUS, 0x40,			0xff },
@@ -6286,11 +6299,20 @@
 
 	if (ov518_init_compression(ov)) goto error;
 
-	/* OV518+ has packet numbering turned on by default */
 	if (ov->bridge == BRG_OV518)
-		ov->packet_numbering = 0;
-	else
+	{
+		struct usb_interface *ifp = &ov->dev->config[0].interface[0];
+		__u16 mxps = ifp->altsetting[7].endpoint[0].wMaxPacketSize;
+
+		/* Some OV518s have packet numbering by default, some don't */
+		if (mxps == 897)
+			ov->packet_numbering = 1;
+		else
+			ov->packet_numbering = 0;
+	} else {
+		/* OV518+ has packet numbering turned on by default */
 		ov->packet_numbering = 1;
+	}
 
 	ov518_set_packet_size(ov, 0);
 
@@ -6450,7 +6472,13 @@
 
 	ov->buf_state = BUF_NOT_ALLOCATED;
 
-	/* Must be kmalloc()'ed, for DMA accessibility */
+	if (usb_make_path(dev, ov->usb_path, OV511_USB_PATH_LEN) < 0) {
+		err("usb_make_path error");
+		goto error_dealloc;
+	}
+
+	/* Allocate control transfer buffer. */
+	/* Must be kmalloc()'ed, for DMA compatibility */
 	ov->cbuf = kmalloc(OV511_CBUF_SIZE, GFP_KERNEL);
 	if (!ov->cbuf)
 		goto error;
@@ -6480,8 +6508,12 @@
 		goto error;
 
 #ifdef OV511_DEBUG
-	if (dump_bridge)
-		ov511_dump_regs(ov);
+	if (dump_bridge) {
+		if (ov->bclass == BCL_OV511)
+			ov511_dump_regs(ov);
+		else
+			ov518_dump_regs(ov);
+	}
 #endif
 
 	memcpy(&ov->vdev, &vdev_template, sizeof(vdev_template));
@@ -6506,7 +6538,8 @@
 		goto error;
 	}
 
-	info("Device registered on minor %d", ov->vdev.minor);
+	info("Device at %s registered to minor %d", ov->usb_path,
+	     ov->vdev.minor);
 
 #if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS)
 	create_proc_ov511_cam(ov);
@@ -6551,10 +6584,8 @@
 
 	PDEBUG(3, "");
 
-	/* We don't want people trying to open up the device */
-	if (!ov->user)
-		video_unregister_device(&ov->vdev);
-	else
+	video_unregister_device(&ov->vdev);
+	if (ov->user)
 		PDEBUG(3, "Device open...deferring video_unregister_device");
 
 	for (n = 0; n < OV511_NUMFRAMES; n++)
@@ -6592,13 +6623,15 @@
 	}
 
 	MOD_DEC_USE_COUNT;
+
+	PDEBUG(3, "Disconnect complete");
 }
 
 static struct usb_driver ov511_driver = {
-	name:		"ov511",
-	id_table:       device_table,
-	probe:		ov51x_probe,
-	disconnect:	ov51x_disconnect
+	.name =		"ov511",
+	.id_table =	device_table,
+	.probe =	ov51x_probe,
+	.disconnect =	ov51x_disconnect
 };
 
 
diff -urN linux-2.4.21-pre2/drivers/usb/ov511.h linux-2.4.21-pre3/drivers/usb/ov511.h
--- linux-2.4.21-pre2/drivers/usb/ov511.h	Thu Nov 28 15:53:14 2002
+++ linux-2.4.21-pre3/drivers/usb/ov511.h	Mon Jan  6 16:32:32 2003
@@ -253,6 +253,9 @@
 /* Control transfers use up to 4 bytes */
 #define OV511_CBUF_SIZE		4
 
+/* Size of usb_make_path() buffer */
+#define OV511_USB_PATH_LEN	64
+
 /* Bridge types */
 enum {
 	BRG_UNKNOWN,
@@ -286,21 +289,6 @@
 	SEN_SAA7111A,
 };
 
-// Not implemented yet
-#if 0
-/* Sensor classes */
-enum {
-	SCL_UNKNOWN,
-	SCL_OV7610,	/* 7610, 76BE, 7620AE (for now) */
-	SCL_OV7620,
-	SCL_OV6620,	
-	SCL_OV6630,	/* 6630, 6630AE, 6630AF */
-	SCL_OV8600,
-	SCL_KS0127,	/* SEN_KS0127, SEN_KS0127B */
-	SCL_SAA7111A,
-};
-#endif
-
 enum {
 	STATE_SCANNING,		/* Scanning for start */
 	STATE_HEADER,		/* Parsing header */
@@ -311,7 +299,6 @@
 enum {
 	BUF_NOT_ALLOCATED,
 	BUF_ALLOCATED,
-	BUF_PEND_DEALLOC,	/* ov511->buf_timer is set */
 };
 
 /* --------- Definition of ioctl interface --------- */
@@ -466,6 +453,7 @@
 	int customid;
 	char *desc;
 	unsigned char iface;
+	char usb_path[OV511_USB_PATH_LEN];
 
 	/* Determined by sensor type */
 	int maxwidth;
@@ -521,7 +509,6 @@
 	int bridge;		/* Type of bridge (BRG_*) */
 	int bclass;		/* Class of bridge (BCL_*) */
 	int sensor;		/* Type of image sensor chip (SEN_*) */
-	int sclass;		/* Type of image sensor chip (SCL_*) */
 
 	int packet_size;	/* Frame size per isoc desc */
 	int packet_numbering;	/* Is ISO frame numbering enabled? */
@@ -537,7 +524,6 @@
 	/* Framebuffer/sbuf management */
 	int buf_state;
 	struct semaphore buf_lock;
-	struct timer_list buf_timer;
 
 	struct ov51x_decomp_ops *decomp_ops;
 
diff -urN linux-2.4.21-pre2/drivers/usb/pegasus.c linux-2.4.21-pre3/drivers/usb/pegasus.c
--- linux-2.4.21-pre2/drivers/usb/pegasus.c	Mon Jan  6 16:32:29 2003
+++ linux-2.4.21-pre3/drivers/usb/pegasus.c	Mon Jan  6 16:32:32 2003
@@ -42,7 +42,7 @@
 /*
  * Version Information
  */
-#define DRIVER_VERSION "v0.4.28 (2002/12/13)"
+#define DRIVER_VERSION "v0.4.29 (2002/12/31)"
 #define DRIVER_AUTHOR "Petko Manolov <petkan@users.sourceforge.net>"
 #define DRIVER_DESC "Pegasus/Pegasus II USB Ethernet driver"
 
@@ -723,6 +723,9 @@
 	int res;
 
 	down(&pegasus->sem);
+
+	set_registers(pegasus, EthID, 6, net->dev_addr);
+	
 	FILL_BULK_URB(pegasus->rx_urb, pegasus->usb,
 		      usb_rcvbulkpipe(pegasus->usb, 1),
 		      pegasus->rx_buff, PEGASUS_MAX_MTU,
diff -urN linux-2.4.21-pre2/drivers/usb/pegasus.h linux-2.4.21-pre3/drivers/usb/pegasus.h
--- linux-2.4.21-pre2/drivers/usb/pegasus.h	Mon Jan  6 16:32:29 2003
+++ linux-2.4.21-pre3/drivers/usb/pegasus.h	Mon Jan  6 16:32:32 2003
@@ -122,6 +122,7 @@
 #define	VENDOR_COMPAQ		0x049f
 #define	VENDOR_COREGA		0x07aa
 #define	VENDOR_DLINK		0x2001
+#define	VENDOR_EASIDOCK		0x1342
 #define VENDOR_ELCON		0x0db7
 #define	VENDOR_ELSA		0x05cc
 #define	VENDOR_HAWKING		0x0e66
@@ -211,6 +212,8 @@
 		DEFAULT_GPIO_RESET )
 PEGASUS_DEV( "GOLDPFEIL USB Adapter", VENDOR_ELCON, 0x0002,
 		DEFAULT_GPIO_RESET | PEGASUS_II | HAS_HOME_PNA )
+PEGASUS_DEV( "EasiDock Ethernet", VENDOR_EASIDOCK, 0x0304,
+		DEFAULT_GPIO_RESET )
 PEGASUS_DEV( "Elsa Micolink USB2Ethernet", VENDOR_ELSA, 0x3000,
 		DEFAULT_GPIO_RESET )
 PEGASUS_DEV( "Hawking UF100 10/100 Ethernet", VENDOR_HAWKING, 0x400c,
@@ -235,8 +238,8 @@
 		LINKSYS_GPIO_RESET )
 PEGASUS_DEV( "Linksys USB100TX", VENDOR_LINKSYS, 0x2204,
 		LINKSYS_GPIO_RESET | HAS_HOME_PNA )
-PEGASUS_DEV( "Linksys USB Ethernet Adapter", VENDOR_LINKSYS, 0x2206,
-		LINKSYS_GPIO_RESET )
+PEGASUS_DEV( "Linksys USB10T Ethernet Adapter", VENDOR_LINKSYS, 0x2206,
+		LINKSYS_GPIO_RESET | PEGASUS_II)
 PEGASUS_DEV( "Linksys USB USB10TX", VENDOR_LINKSYS, 0x400b,
 		LINKSYS_GPIO_RESET | PEGASUS_II )
 PEGASUS_DEV( "Linksys USB10TX", VENDOR_LINKSYS, 0x200c,
@@ -247,7 +250,7 @@
 		DEFAULT_GPIO_RESET )
 PEGASUS_DEV( "MELCO/BUFFALO LUA2-TX", VENDOR_MELCO, 0x0009,
 		DEFAULT_GPIO_RESET | PEGASUS_II )
-PEGASUS_DEV( "NETGEAR FA101", VENDOR_MELCO, 0x1020,
+PEGASUS_DEV( "NETGEAR FA101", VENDOR_NETGEAR, 0x1020,
 		DEFAULT_GPIO_RESET | PEGASUS_II )
 PEGASUS_DEV( "smartNIC 2 PnP Adapter", VENDOR_SMARTBRIDGES, 0x0003,
 		DEFAULT_GPIO_RESET | PEGASUS_II )
diff -urN linux-2.4.21-pre2/drivers/usb/rtl8150.c linux-2.4.21-pre3/drivers/usb/rtl8150.c
--- linux-2.4.21-pre2/drivers/usb/rtl8150.c	Mon Jan  6 16:32:29 2003
+++ linux-2.4.21-pre3/drivers/usb/rtl8150.c	Mon Jan  6 16:32:32 2003
@@ -7,7 +7,6 @@
  */
 
 #include <linux/config.h>
-#include <linux/init.h>
 #include <linux/sched.h>
 #include <linux/signal.h>
 #include <linux/slab.h>
@@ -21,7 +20,7 @@
 #include <asm/uaccess.h>
 
 /* Version Information */
-#define DRIVER_VERSION "v0.4.2 (2002/09/19)"
+#define DRIVER_VERSION "v0.4.3 (2002/12/31)"
 #define DRIVER_AUTHOR "Petko Manolov <petkan@users.sourceforge.net>"
 #define DRIVER_DESC "rtl8150 based usb-ethernet driver"
 
@@ -354,7 +353,7 @@
 	case -ENOENT:
 		return;
 	case -ETIMEDOUT:
-		warn("reset needed may be?..");
+		warn("need a device reset?..");
 		goto goon;
 	default:
 		warn("Rx status %d", urb->status);
@@ -497,7 +496,7 @@
 	count = (count & 0x3f) ? count : count + 1;
 	memcpy(dev->tx_buff, skb->data, skb->len);
 	FILL_BULK_URB(dev->tx_urb, dev->udev, usb_sndbulkpipe(dev->udev, 2),
-		      dev->tx_buff, RTL8150_MAX_MTU, write_bulk_callback, dev);
+		      dev->tx_buff, count, write_bulk_callback, dev);
 	dev->tx_urb->transfer_buffer_length = count;
 
 	if ((res = usb_submit_urb(dev->tx_urb))) {
@@ -525,6 +524,9 @@
 	}
 
 	down(&dev->sem);
+
+	set_registers(dev, IDR, 6, netdev->dev_addr);
+	
 	FILL_BULK_URB(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1),
 		      dev->rx_buff, RTL8150_MAX_MTU, read_bulk_callback, dev);
 	if ((res = usb_submit_urb(dev->rx_urb)))
diff -urN linux-2.4.21-pre2/drivers/usb/scanner.c linux-2.4.21-pre3/drivers/usb/scanner.c
--- linux-2.4.21-pre2/drivers/usb/scanner.c	Thu Nov 28 15:53:14 2002
+++ linux-2.4.21-pre3/drivers/usb/scanner.c	Mon Jan  6 16:32:32 2003
@@ -1,7 +1,7 @@
 /* -*- linux-c -*- */
 
 /* 
- * Driver for USB Scanners (linux-2.4.18)
+ * Driver for USB Scanners (linux-2.4.21)
  *
  * Copyright (C) 1999, 2000, 2001, 2002 David E. Nelson
  *
@@ -311,6 +311,25 @@
  *    - Changed maintainership from David E. Nelson to Brian
  *      Beattie <beattie@beattie-home.net>.
  *
+ * 0.4.9  12/19/2002
+ *    - Added vendor/product ids for Nikon, Mustek, Plustek, Genius, Epson,
+ *      Canon, Umax, Hewlett-Packard, Benq, Agfa, Minolta scanners.
+ *      Thanks to Dieter Faulbaum <faulbaum@mail.bessy.de>, Stian Jordet
+ *      <liste@jordet.nu>, "Yann E. MORIN" <yann.morin.1998@anciens.enib.fr>,
+ *      "Jaeger, Gerhard" <gerhard@gjaeger.de>, Ira Childress 
+ *      <ichildress@mn.rr.com>, Till Kamppeter <till.kamppeter@gmx.net>,
+ *      Ed Hamrick <EdHamrick@aol.com>, Oliver Schwartz
+ *      <Oliver.Schwartz@gmx.de> and everyone else who sent ids.
+ *    - Some Benq, Genius and Plustek ids are identified now.
+ *    - Don't clutter syslog with "Unable to access minor data" messages.
+ *    - Accept scanners with only one bulk (in) endpoint (thanks to Sergey
+ *      Vlasov <vsu@mivlgu.murom.ru>).
+ *    - Accept devices with more than one interface. Only use interfaces that
+ *      look like belonging to scanners.
+ *    - Use altsetting[0], not altsetting[ifnum].
+ *    - Add locking to ioctl_scanner(). Thanks to Oliver Neukum
+ *      <oliver@neukum.name>.
+ *
  * TODO
  *    - Remove the 2/3 endpoint limitation
  *    - Performance
@@ -384,8 +403,6 @@
 
 	int err=0;
 
-	MOD_INC_USE_COUNT;
-
 	down(&scn_mutex);
 
 	scn_minor = USB_SCN_MINOR(inode);
@@ -394,8 +411,7 @@
 
 	if (!p_scn_table[scn_minor]) {
 		up(&scn_mutex);
-		MOD_DEC_USE_COUNT;
-		err("open_scanner(%d): Unable to access minor data", scn_minor);
+		dbg("open_scanner(%d): Unable to access minor data", scn_minor);
 		return -ENODEV;
 	}
 
@@ -403,7 +419,7 @@
 
 	dev = scn->scn_dev;
 
-	down(&(scn->sem));	/* Now protect the scn_usb_data structure */ 
+	down(&(scn->sem));	/* Now protect the scn_usb_data structure */
 
 	up(&scn_mutex); /* Now handled by the above */
 
@@ -436,9 +452,6 @@
 
 	up(&(scn->sem)); /* Wake up any possible contending processes */
 
-	if (err)
-		MOD_DEC_USE_COUNT;
-
 	return err;
 }
 
@@ -469,8 +482,6 @@
 	up(&scn_mutex);
 	up(&(scn->sem));
 
-	MOD_DEC_USE_COUNT;
-
 	return 0;
 }
 
@@ -496,6 +507,12 @@
 
 	down(&(scn->sem));
 
+	if (!scn->bulk_out_ep) {
+		/* This scanner does not have a bulk-out endpoint */
+		up(&(scn->sem));
+		return -EINVAL;
+	}
+
 	scn_minor = scn->scn_minor;
 
 	obuf = scn->obuf;
@@ -636,7 +653,7 @@
 				goto data_recvd;
 			}
 		}
-		
+
 		if (result == -EPIPE) { /* No hope */
 			if(usb_clear_halt(dev, scn->bulk_in_ep)) {
 				err("read_scanner(%d): Failure to clear endpoint halt condition (%Zd).", scn_minor, ret);
@@ -684,30 +701,26 @@
 ioctl_scanner(struct inode *inode, struct file *file,
 	      unsigned int cmd, unsigned long arg)
 {
+	struct scn_usb_data *scn;
 	struct usb_device *dev;
+	int retval = -ENOTTY;
 
-	kdev_t scn_minor;
-
-	scn_minor = USB_SCN_MINOR(inode);
-
-	if (!p_scn_table[scn_minor]) {
-		err("ioctl_scanner(%d): invalid scn_minor", scn_minor);
-		return -ENODEV;
-	}
+	scn = file->private_data;
+	down(&(scn->sem));
 
-	dev = p_scn_table[scn_minor]->scn_dev;
+	dev = scn->scn_dev;
 
 	switch (cmd)
 	{
 	case SCANNER_IOCTL_VENDOR :
-		return (put_user(dev->descriptor.idVendor, (unsigned int *) arg));
+		retval = (put_user(dev->descriptor.idVendor, (unsigned int *) arg));
+		break;
 	case SCANNER_IOCTL_PRODUCT :
-		return (put_user(dev->descriptor.idProduct, (unsigned int *) arg));
+		retval = (put_user(dev->descriptor.idProduct, (unsigned int *) arg));
+		break;
 #ifdef PV8630
 	case PV8630_IOCTL_INREQUEST :
 	{
-		int result;
-
 		struct {
 			__u8  data;
 			__u8  request;
@@ -715,48 +728,42 @@
 			__u16 index;
 		} args;
 
-		if (copy_from_user(&args, (void *)arg, sizeof(args)))
-			return -EFAULT;
+		if (copy_from_user(&args, (void *)arg, sizeof(args))) {
+			retval = -EFAULT;
+			break;
+		}
 
-		result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+		retval = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
 					 args.request, USB_TYPE_VENDOR|
 					 USB_RECIP_DEVICE|USB_DIR_IN,
 					 args.value, args.index, &args.data,
 					 1, HZ*5);
 
-		dbg("ioctl_scanner(%d): inreq: args.data:%x args.value:%x args.index:%x args.request:%x\n", scn_minor, args.data, args.value, args.index, args.request);
-
 		if (copy_to_user((void *)arg, &args, sizeof(args)))
-			return -EFAULT;
-
-		dbg("ioctl_scanner(%d): inreq: result:%d\n", scn_minor, result);
+			retval = -EFAULT;
 
-		return result;
+		break;
 	}
 	case PV8630_IOCTL_OUTREQUEST :
 	{
-		int result;
-
 		struct {
 			__u8  request;
 			__u16 value;
 			__u16 index;
 		} args;
 
-		if (copy_from_user(&args, (void *)arg, sizeof(args)))
-			return -EFAULT;
-
-		dbg("ioctl_scanner(%d): outreq: args.value:%x args.index:%x args.request:%x\n", scn_minor, args.value, args.index, args.request);
+		if (copy_from_user(&args, (void *)arg, sizeof(args))) {
+			retval = -EFAULT;
+			break;
+		}
 
-		result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+		retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
 					 args.request, USB_TYPE_VENDOR|
 					 USB_RECIP_DEVICE|USB_DIR_OUT,
 					 args.value, args.index, NULL,
 					 0, HZ*5);
 
-		dbg("ioctl_scanner(%d): outreq: result:%d\n", scn_minor, result);
-
-		return result;
+		break;
 	}
 #endif /* PV8630 */
  	case SCANNER_IOCTL_CTRLMSG:
@@ -767,19 +774,26 @@
  		} cmsg;
  		int pipe, nb, ret;
  		unsigned char buf[64];
- 
- 		if (copy_from_user(&cmsg, (void *)arg, sizeof(cmsg)))
- 			return -EFAULT;
+		retval = 0;
+
+ 		if (copy_from_user(&cmsg, (void *)arg, sizeof(cmsg))) {
+ 			retval = -EFAULT;
+			break;
+		}
 
  		nb = cmsg.req.wLength;
 
- 		if (nb > sizeof(buf))
- 			return -EINVAL;
+ 		if (nb > sizeof(buf)) {
+ 			retval = -EINVAL;
+			break;
+		}
 
  		if ((cmsg.req.bRequestType & 0x80) == 0) {
  			pipe = usb_sndctrlpipe(dev, 0);
- 			if (nb > 0 && copy_from_user(buf, cmsg.data, nb))
- 				return -EFAULT;
+ 			if (nb > 0 && copy_from_user(buf, cmsg.data, nb)) {
+ 				retval = -EFAULT;
+				break;
+			}
  		} else {
  			pipe = usb_rcvctrlpipe(dev, 0);
 		}
@@ -791,23 +805,26 @@
  				      buf, nb, HZ);
 
  		if (ret < 0) {
- 			err("ioctl_scanner(%d): control_msg returned %d\n", scn_minor, ret);
- 			return -EIO;
+ 			err("ioctl_scanner: control_msg returned %d\n", ret);
+ 			retval = -EIO;
+			break;
  		}
 
  		if (nb > 0 && (cmsg.req.bRequestType & 0x80) && copy_to_user(cmsg.data, buf, nb))
- 			return -EFAULT;
+ 			retval = -EFAULT;
 
- 		return 0;
+ 		break;
  	}
 	default:
-		return -ENOTTY;
+		break;
 	}
-	return 0;
+	up(&(scn->sem));
+	return retval;
 }
 
 static struct
 file_operations usb_scanner_fops = {
+	owner:		THIS_MODULE,
 	read:		read_scanner,
 	write:		write_scanner,
 	ioctl:		ioctl_scanner,
@@ -886,24 +903,27 @@
 		return NULL;
 	}
 
-	if (dev->config[0].bNumInterfaces != 1) {
-		info("probe_scanner: Only one device interface is supported.");
+	interface = dev->config[0].interface[ifnum].altsetting;
+
+	if (interface[0].bInterfaceClass != USB_CLASS_VENDOR_SPEC &&
+	    interface[0].bInterfaceClass != USB_CLASS_PER_INTERFACE &&
+	    interface[0].bInterfaceClass != 16) {
+		dbg("probe_scanner: This interface doesn't look like a scanner (class=0x%x).", interface[0].bInterfaceClass);
 		return NULL;
 	}
 
-	interface = dev->config[0].interface[ifnum].altsetting;
-	endpoint = interface[ifnum].endpoint;
+	endpoint = interface[0].endpoint;
 
 /*
- * Start checking for two bulk endpoints OR two bulk endpoints *and* one
+ * Start checking for one or two bulk endpoints and an optional
  * interrupt endpoint. If we have an interrupt endpoint go ahead and
  * setup the handler. FIXME: This is a future enhancement...
  */
 
 	dbg("probe_scanner: Number of Endpoints:%d", (int) interface->bNumEndpoints);
 
-	if ((interface->bNumEndpoints != 2) && (interface->bNumEndpoints != 3)) {
-		info("probe_scanner: Only two or three endpoints supported.");
+	if ((interface->bNumEndpoints < 1) || (interface->bNumEndpoints > 3)) {
+		info("probe_scanner: Only 1, 2, or 3 endpoints supported.");
 		return NULL;
 	}
 
@@ -942,6 +962,12 @@
  */
 
 	switch(interface->bNumEndpoints) {
+	case 1:
+		if (!have_bulk_in) {
+			info("probe_scanner: One bulk-in endpoint required.");
+			return NULL;
+		}
+		break;
 	case 2:
 		if (!have_bulk_in || !have_bulk_out) {
 			info("probe_scanner: Two bulk endpoints required.");
diff -urN linux-2.4.21-pre2/drivers/usb/scanner.h linux-2.4.21-pre3/drivers/usb/scanner.h
--- linux-2.4.21-pre2/drivers/usb/scanner.h	Mon Jan  6 16:32:29 2003
+++ linux-2.4.21-pre3/drivers/usb/scanner.h	Mon Jan  6 16:32:32 2003
@@ -1,5 +1,5 @@
 /*
- * Driver for USB Scanners (linux-2.4.18)
+ * Driver for USB Scanners (linux-2.4.21)
  *
  * Copyright (C) 1999, 2000, 2001, 2002 David E. Nelson
  *
@@ -41,7 +41,7 @@
  */
 // #define PV8630 
 
-#define DRIVER_VERSION "0.4.6"
+#define DRIVER_VERSION "0.4.9"
 #define DRIVER_DESC "USB Scanner Driver"
 
 #include <linux/usb.h>
@@ -67,19 +67,19 @@
 // #define WR_DATA_DUMP /* DEBUG does not have to be defined. */
 
 static struct usb_device_id scanner_device_ids [] = {
-	/* Acer */
-	{ USB_DEVICE(0x04a5, 0x2060) },	/* Prisa Acerscan 620U & 640U (!)*/
-	{ USB_DEVICE(0x04a5, 0x2040) },	/* Prisa AcerScan 620U (!) */
-	{ USB_DEVICE(0x04a5, 0x20c0) },  /* Prisa AcerScan 1240UT */
-	{ USB_DEVICE(0x04a5, 0x2022) },	/* Vuego Scan Brisa 340U */
+	/* Acer (now Benq) */
+	{ USB_DEVICE(0x04a5, 0x2060) },	/* 620U & 640U (!)*/
+	{ USB_DEVICE(0x04a5, 0x2040) },	/* 620U (!) */
+	{ USB_DEVICE(0x04a5, 0x20c0) }, /* 1240UT, 1240U */
+	{ USB_DEVICE(0x04a5, 0x2022) },	/* 340U */
 	{ USB_DEVICE(0x04a5, 0x1a20) },	/* Unknown - Oliver Schwartz */
-	{ USB_DEVICE(0x04a5, 0x1a2a) },	/* Unknown - Oliver Schwartz */
-	{ USB_DEVICE(0x04a5, 0x207e) },	/* Prisa 640BU */
+	{ USB_DEVICE(0x04a5, 0x1a2a) },	/* Another 620U */
+	{ USB_DEVICE(0x04a5, 0x207e) },	/* 640BU */
 	{ USB_DEVICE(0x04a5, 0x20be) },	/* Unknown - Oliver Schwartz */
-	{ USB_DEVICE(0x04a5, 0x20c0) },	/* Unknown - Oliver Schwartz */
 	{ USB_DEVICE(0x04a5, 0x20de) },	/* S2W 3300U */
-	{ USB_DEVICE(0x04a5, 0x20b0) },	/* Unknown - Oliver Schwartz */
-	{ USB_DEVICE(0x04a5, 0x20fe) },	/* Unknown - Oliver Schwartz */
+	{ USB_DEVICE(0x04a5, 0x20b0) },	/* Benq 4300 */
+	{ USB_DEVICE(0x04a5, 0x20fe) },	/* Benq 5300 */
+	{ USB_DEVICE(0x04a5, 0x20fc) }, /* Benq 5000 */
 	/* Agfa */
 	{ USB_DEVICE(0x06bd, 0x0001) },	/* SnapScan 1212U */
 	{ USB_DEVICE(0x06bd, 0x0002) },	/* SnapScan 1236U */
@@ -89,24 +89,37 @@
 	{ USB_DEVICE(0x06bd, 0x2095) }, /* SnapScan e25 */
 	{ USB_DEVICE(0x06bd, 0x2097) }, /* SnapScan e26 */
 	{ USB_DEVICE(0x06bd, 0x208d) }, /* Snapscan e40 */
+	{ USB_DEVICE(0x06bd, 0x2093) }, /* SnapScan e10*/
+	{ USB_DEVICE(0x06bd, 0x20ff) }, /* SnapScan e42*/
+	{ USB_DEVICE(0x06bd, 0x208f) }, /* SnapScan e50*/
+	{ USB_DEVICE(0x06bd, 0x20fd) }, /* SnapScan e52*/
+	/* Benq: see Acer */
 	/* Canon */
+	{ USB_DEVICE(0x04a9, 0x2201) }, /* FB320U */
+	{ USB_DEVICE(0x04a9, 0x2205) }, /* FB1210U */
 	{ USB_DEVICE(0x04a9, 0x2202) }, /* CanoScan FB620U */
 	{ USB_DEVICE(0x04a9, 0x2204) }, /* CanoScan FB630U/FB636U */
 	{ USB_DEVICE(0x04a9, 0x2206) }, /* CanoScan N650U/N656U */
 	{ USB_DEVICE(0x04a9, 0x2207) }, /* CanoScan N1220U */
 	{ USB_DEVICE(0x04a9, 0x2208) }, /* CanoScan D660U */ 
 	{ USB_DEVICE(0x04a9, 0x220b) }, /* D646U */
+	{ USB_DEVICE(0x04a9, 0x220d) }, /* CanoScan N670U/N676U/LIDE 20 */
+	{ USB_DEVICE(0x04a9, 0x220e) }, /* CanoScan N1240U/LIDE 30 */
+	{ USB_DEVICE(0x04a9, 0x3042) }, /* FS4000US */
 	/* Colorado -- See Primax/Colorado below */
 	/* Epson -- See Seiko/Epson below */
 	/* Genius */
 	{ USB_DEVICE(0x0458, 0x2001) },	/* ColorPage-Vivid Pro */
 	{ USB_DEVICE(0x0458, 0x2007) },	/* ColorPage HR6 V2 */
-	{ USB_DEVICE(0x0458, 0x2008) },	/* Unknown */
-	{ USB_DEVICE(0x0458, 0x2009) },	/* Unknown */
-	{ USB_DEVICE(0x0458, 0x2013) },	/* Unknown */
-	{ USB_DEVICE(0x0458, 0x2015) },	/* Unknown  */
-	{ USB_DEVICE(0x0458, 0x2016) },	/* Unknown  */
+	{ USB_DEVICE(0x0458, 0x2008) }, /* ColorPage-HR6 V2 */
+	{ USB_DEVICE(0x0458, 0x2009) }, /* ColorPage-HR6A */
+	{ USB_DEVICE(0x0458, 0x2011) }, /* ColorPage-Vivid3x */
+	{ USB_DEVICE(0x0458, 0x2013) }, /* ColorPage-HR7 */
+        { USB_DEVICE(0x0458, 0x2015) }, /* ColorPage-HR7LE */
+	{ USB_DEVICE(0x0458, 0x2016) }, /* ColorPage-HR6X */
 	/* Hewlett Packard */
+	{ USB_DEVICE(0x03f0, 0x0505) }, /* ScanJet 2100C */
+	{ USB_DEVICE(0x03f0, 0x0901) }, /* 2300C */
 	{ USB_DEVICE(0x03f0, 0x0205) },	/* 3300C */
 	{ USB_DEVICE(0x03f0, 0x0405) }, /* 3400C */
 	{ USB_DEVICE(0x03f0, 0x0101) },	/* 4100C */
@@ -137,6 +150,8 @@
 	//	{ USB_DEVICE(0x05da, 0x80ac) },	/* ScanMaker V6UL - SpicyU */
 	/* Minolta */
 	//	{ USB_DEVICE(0x0638,0x026a) }, /* Minolta Dimage Scan Dual II */
+	//	{ USB_DEVICE(0x0686, 0x4004) }, /* Scan Elite II (need interrupt ep) */
+	{ USB_DEVICE(0x0686, 0x400d) }, /* Scan Dual III  */
 	/* Mustek */
 	{ USB_DEVICE(0x055f, 0x0001) },	/* ScanExpress 1200 CU */
 	{ USB_DEVICE(0x0400, 0x1000) },	/* BearPaw 1200 */
@@ -157,19 +172,22 @@
 	{ USB_DEVICE(0x055f, 0x021e) }, /* BearPaw 1200 TA/CS */
 	{ USB_DEVICE(0x055f, 0x0400) }, /* BearPaw 2400 TA PRO */
 	{ USB_DEVICE(0x055f, 0x1000) }, /* BearPaw 4800 TA PRO */
+	/* Nikon */
+	{ USB_DEVICE(0x04b0, 0x4000) },  /* Coolscan LS 40 ED */
 	/* Plustek */
-	{ USB_DEVICE(0x07b3, 0x0017) }, /* OpticPro UT12 */
-	{ USB_DEVICE(0x07b3, 0x0011) }, /* OpticPro UT24 */
+	{ USB_DEVICE(0x07b3, 0x0017) }, /* OpticPro UT12/UT16/UT24 */
+	{ USB_DEVICE(0x07b3, 0x0011) }, /* OpticPro U24 */
+	{ USB_DEVICE(0x07b3, 0x0010) }, /* OpticPro U12 */
+	{ USB_DEVICE(0x07b3, 0x0015) }, /* OpticPro U24 */
 	{ USB_DEVICE(0x07b3, 0x0005) }, /* Unknown */
 	{ USB_DEVICE(0x07b3, 0x0007) }, /* Unknown */
 	{ USB_DEVICE(0x07b3, 0x000F) }, /* Unknown */
-	{ USB_DEVICE(0x07b3, 0x0010) }, /* Unknown */
 	{ USB_DEVICE(0x07b3, 0x0012) }, /* Unknown */
 	{ USB_DEVICE(0x07b3, 0x0013) }, /* Unknown */
 	{ USB_DEVICE(0x07b3, 0x0014) }, /* Unknown */
-	{ USB_DEVICE(0x07b3, 0x0015) }, /* Unknown */
 	{ USB_DEVICE(0x07b3, 0x0016) }, /* Unknown */
 	{ USB_DEVICE(0x07b3, 0x0012) }, /* Unknown */
+	{ USB_DEVICE(0x07b3, 0x0401) }, /* OpticPro 1248U */
 	/* Primax/Colorado */
 	{ USB_DEVICE(0x0461, 0x0300) },	/* G2-300 #1 */
 	{ USB_DEVICE(0x0461, 0x0380) },	/* G2-600 #1 */
@@ -187,10 +205,13 @@
 	// { USB_DEVICE(0x0475, 0x0103) },	/* Episode - undetected endpoint */
 	/* Seiko/Epson Corp. */
 	{ USB_DEVICE(0x04b8, 0x0101) },	/* Perfection 636U and 636Photo */
+	{ USB_DEVICE(0x04b8, 0x0102) }, /* GT-2200 */
 	{ USB_DEVICE(0x04b8, 0x0103) },	/* Perfection 610 */
 	{ USB_DEVICE(0x04b8, 0x0104) },	/* Perfection 1200U and 1200Photo*/
+	{ USB_DEVICE(0x04b8, 0x0105) }, /* StylusScan 2000 */
 	{ USB_DEVICE(0x04b8, 0x0106) },	/* Stylus Scan 2500 */
 	{ USB_DEVICE(0x04b8, 0x0107) },	/* Expression 1600 */
+	{ USB_DEVICE(0x04b8, 0x0109) }, /* Expression 1640XL */
 	{ USB_DEVICE(0x04b8, 0x010a) }, /* Perfection 1640SU and 1640SU Photo */
 	{ USB_DEVICE(0x04b8, 0x010b) }, /* Perfection 1240U */
 	{ USB_DEVICE(0x04b8, 0x010c) }, /* Perfection 640U */
@@ -200,11 +221,16 @@
 	{ USB_DEVICE(0x04b8, 0x0112) }, /* Perfection 2450 - GT-9700 for the Japanese mkt */
 	{ USB_DEVICE(0x04b8, 0x0114) }, /* Perfection 660 */
 	{ USB_DEVICE(0x04b8, 0x011b) }, /* Perfection 2400 Photo */
+	{ USB_DEVICE(0x04b8, 0x011c) }, /* Perfection 3200 */
+	{ USB_DEVICE(0x04b8, 0x011d) }, /* Perfection 1260 */
 	{ USB_DEVICE(0x04b8, 0x011e) }, /* Perfection 1660 Photo */
+	{ USB_DEVICE(0x04b8, 0x0802) }, /* Stylus CX3200 */
 	/* Umax */
 	{ USB_DEVICE(0x1606, 0x0010) },	/* Astra 1220U */
 	{ USB_DEVICE(0x1606, 0x0030) },	/* Astra 2000U */
+	{ USB_DEVICE(0x1606, 0x0060) }, /* Astra 3400U/3450U */
 	{ USB_DEVICE(0x1606, 0x0130) }, /* Astra 2100U */
+	{ USB_DEVICE(0x1606, 0x0160) }, /* Astra 5400U */  
 	{ USB_DEVICE(0x1606, 0x0230) },	/* Astra 2200U */
 	/* Visioneer */
 	{ USB_DEVICE(0x04a7, 0x0221) },	/* OneTouch 5300 USB */
diff -urN linux-2.4.21-pre2/drivers/usb/serial/ipaq.c linux-2.4.21-pre3/drivers/usb/serial/ipaq.c
--- linux-2.4.21-pre2/drivers/usb/serial/ipaq.c	Mon Jan  6 16:32:29 2003
+++ linux-2.4.21-pre3/drivers/usb/serial/ipaq.c	Mon Jan  6 16:32:32 2003
@@ -9,6 +9,10 @@
  *	the Free Software Foundation; either version 2 of the License, or
  *	(at your option) any later version.
  *
+ * (12/12/2002) ganesh
+ * 	Added support for practically all devices supported by ActiveSync
+ * 	on Windows. Thanks to Wes Cilldhaire <billybobjoehenrybob@hotmail.com>.
+ *
  * (26/11/2002) ganesh
  * 	Added insmod options to specify product and vendor id.
  * 	Use modprobe ipaq vendor=0xfoo product=0xbar
@@ -68,9 +72,9 @@
  * Version Information
  */
 
-#define DRIVER_VERSION "v0.4"
+#define DRIVER_VERSION "v0.5"
 #define DRIVER_AUTHOR "Ganesh Varadarajan <ganesh@veritas.com>"
-#define DRIVER_DESC "USB Compaq iPAQ, HP Jornada, Casio EM500 driver"
+#define DRIVER_DESC "USB PocketPC PDA driver"
 
 static int	product, vendor;
 
@@ -94,10 +98,36 @@
 static struct usb_device_id ipaq_id_table [] = {
 	/* The first entry is a placeholder for the insmod-specified device */
 	{ USB_DEVICE(COMPAQ_VENDOR_ID, COMPAQ_IPAQ_ID) },
+	{ USB_DEVICE(ASKEY_VENDOR_ID, ASKEY_PRODUCT_ID) },
+	{ USB_DEVICE(BCOM_VENDOR_ID, BCOM_0065_ID) },
+	{ USB_DEVICE(BCOM_VENDOR_ID, BCOM_0066_ID) },
+	{ USB_DEVICE(BCOM_VENDOR_ID, BCOM_0067_ID) },
+	{ USB_DEVICE(CASIO_VENDOR_ID, CASIO_2001_ID) },
+	{ USB_DEVICE(CASIO_VENDOR_ID, CASIO_EM500_ID) },
 	{ USB_DEVICE(COMPAQ_VENDOR_ID, COMPAQ_IPAQ_ID) },
+	{ USB_DEVICE(COMPAQ_VENDOR_ID, COMPAQ_0032_ID) },
 	{ USB_DEVICE(HP_VENDOR_ID, HP_JORNADA_548_ID) },
 	{ USB_DEVICE(HP_VENDOR_ID, HP_JORNADA_568_ID) },
-	{ USB_DEVICE(CASIO_VENDOR_ID, CASIO_EM500_ID) },
+	{ USB_DEVICE(HP_VENDOR_ID, HP_2016_ID) },
+	{ USB_DEVICE(HP_VENDOR_ID, HP_2116_ID) },
+	{ USB_DEVICE(HP_VENDOR_ID, HP_2216_ID) },
+	{ USB_DEVICE(HP_VENDOR_ID, HP_3016_ID) },
+	{ USB_DEVICE(HP_VENDOR_ID, HP_3116_ID) },
+	{ USB_DEVICE(HP_VENDOR_ID, HP_3216_ID) },
+	{ USB_DEVICE(HP_VENDOR_ID, HP_4016_ID) },
+	{ USB_DEVICE(HP_VENDOR_ID, HP_4116_ID) },
+	{ USB_DEVICE(HP_VENDOR_ID, HP_4216_ID) },
+	{ USB_DEVICE(HP_VENDOR_ID, HP_5016_ID) },
+	{ USB_DEVICE(HP_VENDOR_ID, HP_5116_ID) },
+	{ USB_DEVICE(HP_VENDOR_ID, HP_5216_ID) },
+	{ USB_DEVICE(LINKUP_VENDOR_ID, LINKUP_PRODUCT_ID) },
+	{ USB_DEVICE(MICROSOFT_VENDOR_ID, MICROSOFT_00CE_ID) },
+	{ USB_DEVICE(PORTATEC_VENDOR_ID, PORTATEC_PRODUCT_ID) },
+	{ USB_DEVICE(SAGEM_VENDOR_ID, SAGEM_WIRELESS_ID) },
+	{ USB_DEVICE(SOCKET_VENDOR_ID, SOCKET_PRODUCT_ID) },
+	{ USB_DEVICE(TOSHIBA_VENDOR_ID, TOSHIBA_PRODUCT_ID) },
+	{ USB_DEVICE(HTC_VENDOR_ID, HTC_PRODUCT_ID) },
+	{ USB_DEVICE(NEC_VENDOR_ID, NEC_PRODUCT_ID) },
 	{ }					/* Terminating entry */
 };
 
@@ -106,7 +136,7 @@
 /* All of the device info needed for the Compaq iPAQ */
 struct usb_serial_device_type ipaq_device = {
 	.owner =		THIS_MODULE,
-	.name =			"Compaq iPAQ",
+	.name =			"PocketPC PDA",
 	.id_table =		ipaq_id_table,
 	.num_interrupt_in =	NUM_DONT_CARE,
 	.num_bulk_in =		1,
diff -urN linux-2.4.21-pre2/drivers/usb/serial/ipaq.h linux-2.4.21-pre3/drivers/usb/serial/ipaq.h
--- linux-2.4.21-pre2/drivers/usb/serial/ipaq.h	Thu Nov 28 15:53:14 2002
+++ linux-2.4.21-pre3/drivers/usb/serial/ipaq.h	Mon Jan  6 16:32:32 2003
@@ -9,22 +9,66 @@
  *	the Free Software Foundation; either version 2 of the License, or
  *	(at your option) any later version.
  *
- * 
  */
 
 #ifndef __LINUX_USB_SERIAL_IPAQ_H
 #define __LINUX_USB_SERIAL_IPAQ_H
 
+#define ASKEY_VENDOR_ID		0x1690
+#define ASKEY_PRODUCT_ID	0x0601
+
+#define BCOM_VENDOR_ID		0x0960
+#define BCOM_0065_ID		0x0065
+#define BCOM_0066_ID		0x0066
+#define BCOM_0067_ID		0x0067
+
+#define CASIO_VENDOR_ID		0x07cf
+#define CASIO_2001_ID		0x2001
+#define CASIO_EM500_ID		0x2002
 
 #define COMPAQ_VENDOR_ID	0x049f
 #define COMPAQ_IPAQ_ID		0x0003
+#define COMPAQ_0032_ID		0x0032
 
 #define HP_VENDOR_ID		0x03f0
 #define HP_JORNADA_548_ID	0x1016
 #define HP_JORNADA_568_ID	0x1116
+#define HP_2016_ID		0x2016
+#define HP_2116_ID		0x2116
+#define HP_2216_ID		0x2216
+#define HP_3016_ID		0x3016
+#define HP_3116_ID		0x3116
+#define HP_3216_ID		0x3216
+#define HP_4016_ID		0x4016
+#define HP_4116_ID		0x4116
+#define HP_4216_ID		0x4216
+#define HP_5016_ID		0x5016
+#define HP_5116_ID		0x5116
+#define HP_5216_ID		0x5216
+
+#define LINKUP_VENDOR_ID	0x094b
+#define LINKUP_PRODUCT_ID	0x0001
+
+#define MICROSOFT_VENDOR_ID	0x045e
+#define MICROSOFT_00CE_ID	0x00ce
+
+#define PORTATEC_VENDOR_ID	0x0961
+#define PORTATEC_PRODUCT_ID	0x0010
+
+#define SAGEM_VENDOR_ID		0x5e04
+#define SAGEM_WIRELESS_ID	0xce00
+
+#define SOCKET_VENDOR_ID	0x0104
+#define SOCKET_PRODUCT_ID	0x00be
 
-#define CASIO_VENDOR_ID		0x07cf
-#define CASIO_EM500_ID		0x2002
+#define TOSHIBA_VENDOR_ID	0x0930
+#define TOSHIBA_PRODUCT_ID	0x0700
+
+#define HTC_VENDOR_ID		0x0bb4
+#define HTC_PRODUCT_ID		0x00ce
+
+#define NEC_VENDOR_ID		0x0409
+#define NEC_PRODUCT_ID		0x00d5
 
 /*
  * Since we can't queue our bulk write urbs (don't know why - it just
@@ -41,8 +85,7 @@
  *
  * The value of PACKET_SIZE was empirically determined by
  * checking the maximum write sizes sent down by the ppp ldisc.
- * URBDATA_QUEUE_MAX is set to 64K, which is the maximum TCP window size
- * supported by the iPAQ.
+ * URBDATA_QUEUE_MAX is set to 64K, which is the maximum TCP window size.
  */
 
 struct ipaq_packet {
diff -urN linux-2.4.21-pre2/drivers/usb/storage/isd200.c linux-2.4.21-pre3/drivers/usb/storage/isd200.c
--- linux-2.4.21-pre2/drivers/usb/storage/isd200.c	Fri Aug  2 17:39:45 2002
+++ linux-2.4.21-pre3/drivers/usb/storage/isd200.c	Mon Jan  6 16:32:32 2003
@@ -102,13 +102,6 @@
 #define REG_STATUS           0x80
 #define REG_COMMAND          0x80
 
-/* ATA error definitions not in <linux/hdreg.h> */
-#define ATA_ERROR_MEDIA_CHANGE       0x20
-
-/* ATA command definitions not in <linux/hdreg.h> */
-#define ATA_COMMAND_GET_MEDIA_STATUS        0xDA
-#define ATA_COMMAND_MEDIA_EJECT             0xED
-
 /* ATA drive control definitions */
 #define ATA_DC_DISABLE_INTERRUPTS    0x02
 #define ATA_DC_RESET_CONTROLLER      0x04
@@ -353,7 +346,7 @@
         struct sense_data *buf = (struct sense_data *) &srb->sense_buffer[0];
         unsigned char error = info->ATARegs[IDE_ERROR_OFFSET];
 
-	if(error & ATA_ERROR_MEDIA_CHANGE) {
+	if(error & MC_ERR) {
 		buf->ErrorCode = 0x70 | SENSE_ERRCODE_VALID;
 		buf->AdditionalSenseLength = 0xb;
 		buf->Flags = UNIT_ATTENTION;
@@ -1472,7 +1465,7 @@
 			ataCdb->generic.SignatureByte1 = info->ConfigData.ATAMinorCommand;
 			ataCdb->generic.TransferBlockSize = 1;
 			ataCdb->generic.RegisterSelect = REG_COMMAND;
-			ataCdb->write.CommandByte = ATA_COMMAND_GET_MEDIA_STATUS;
+			ataCdb->write.CommandByte = WIN_GETMEDIASTATUS;
 			srb->request_bufflen = 0;
 		} else {
 			US_DEBUGP("   Media Status not supported, just report okay\n");
@@ -1493,7 +1486,7 @@
 			ataCdb->generic.SignatureByte1 = info->ConfigData.ATAMinorCommand;
 			ataCdb->generic.TransferBlockSize = 1;
 			ataCdb->generic.RegisterSelect = REG_COMMAND;
-			ataCdb->write.CommandByte = ATA_COMMAND_GET_MEDIA_STATUS;
+			ataCdb->write.CommandByte = WIN_GETMEDIASTATUS;
 			srb->request_bufflen = 0;
 		} else {
 			US_DEBUGP("   Media Status not supported, just report okay\n");
@@ -1625,14 +1618,14 @@
 			ataCdb->generic.SignatureByte1 = info->ConfigData.ATAMinorCommand;
 			ataCdb->generic.TransferBlockSize = 0;
 			ataCdb->generic.RegisterSelect = REG_COMMAND;
-			ataCdb->write.CommandByte = ATA_COMMAND_MEDIA_EJECT;
+			ataCdb->write.CommandByte = WIN_MEDIAEJECT;
 		} else if ((srb->cmnd[4] & 0x3) == 0x1) {
 			US_DEBUGP("   Get Media Status\n");
 			ataCdb->generic.SignatureByte0 = info->ConfigData.ATAMajorCommand;
 			ataCdb->generic.SignatureByte1 = info->ConfigData.ATAMinorCommand;
 			ataCdb->generic.TransferBlockSize = 1;
 			ataCdb->generic.RegisterSelect = REG_COMMAND;
-			ataCdb->write.CommandByte = ATA_COMMAND_GET_MEDIA_STATUS;
+			ataCdb->write.CommandByte = WIN_GETMEDIASTATUS;
 			srb->request_bufflen = 0;
 		} else {
 			US_DEBUGP("   Nothing to do, just report okay\n");
diff -urN linux-2.4.21-pre2/drivers/usb/ultracam.c linux-2.4.21-pre3/drivers/usb/ultracam.c
--- linux-2.4.21-pre2/drivers/usb/ultracam.c	Wed Oct 17 14:34:06 2001
+++ linux-2.4.21-pre3/drivers/usb/ultracam.c	Mon Jan  6 16:32:32 2003
@@ -16,7 +16,7 @@
 #define MAX_CAMERAS		4	/* How many devices we allow to connect */
 
 /*
- * This structure lives in uvd_t->user field.
+ * This structure lives in uvd->user field.
  */
 typedef struct {
 	int initialized;	/* Had we already sent init sequence? */
@@ -25,7 +25,7 @@
 } ultracam_t;
 #define	ULTRACAM_T(uvd)	((ultracam_t *)((uvd)->user_data))
 
-static usbvideo_t *cams = NULL;
+static struct usbvideo *cams = NULL;
 
 static int debug = 0;
 
@@ -103,7 +103,7 @@
  * 02-Nov-2000 First (mostly dummy) version.
  * 06-Nov-2000 Rewrote to dump all data into frame.
  */
-void ultracam_ProcessIsocData(uvd_t *uvd, usbvideo_frame_t *frame)
+void ultracam_ProcessIsocData(struct uvd *uvd, struct usbvideo_frame *frame)
 {
 	int n;
 
@@ -140,7 +140,7 @@
  * 1/27/00  Added check for dev == NULL; this happens if camera is unplugged.
  */
 static int ultracam_veio(
-	uvd_t *uvd,
+	struct uvd *uvd,
 	unsigned char req,
 	unsigned short value,
 	unsigned short index,
@@ -193,7 +193,7 @@
 /*
  * ultracam_calculate_fps()
  */
-static int ultracam_calculate_fps(uvd_t *uvd)
+static int ultracam_calculate_fps(struct uvd *uvd)
 {
 	return 3 + framerate*4 + framerate/2;
 }
@@ -201,14 +201,14 @@
 /*
  * ultracam_adjust_contrast()
  */
-static void ultracam_adjust_contrast(uvd_t *uvd)
+static void ultracam_adjust_contrast(struct uvd *uvd)
 {
 }
 
 /*
  * ultracam_change_lighting_conditions()
  */
-static void ultracam_change_lighting_conditions(uvd_t *uvd)
+static void ultracam_change_lighting_conditions(struct uvd *uvd)
 {
 }
 
@@ -219,7 +219,7 @@
  * range [0..6], where 0 is most smooth and 6 is most sharp (raw image, I guess).
  * Recommended value is 4. Cameras model 2 do not have this feature at all.
  */
-static void ultracam_set_sharpness(uvd_t *uvd)
+static void ultracam_set_sharpness(struct uvd *uvd)
 {
 }
 
@@ -228,11 +228,11 @@
  *
  * This procedure changes brightness of the picture.
  */
-static void ultracam_set_brightness(uvd_t *uvd)
+static void ultracam_set_brightness(struct uvd *uvd)
 {
 }
 
-static void ultracam_set_hue(uvd_t *uvd)
+static void ultracam_set_hue(struct uvd *uvd)
 {
 }
 
@@ -242,7 +242,7 @@
  * This procedure gets called from V4L interface to update picture settings.
  * Here we change brightness and contrast.
  */
-static void ultracam_adjust_picture(uvd_t *uvd)
+static void ultracam_adjust_picture(struct uvd *uvd)
 {
 	ultracam_adjust_contrast(uvd);
 	ultracam_set_brightness(uvd);
@@ -255,7 +255,7 @@
  * This code tells camera to stop streaming. The interface remains
  * configured and bandwidth - claimed.
  */
-static void ultracam_video_stop(uvd_t *uvd)
+static void ultracam_video_stop(struct uvd *uvd)
 {
 }
 
@@ -266,24 +266,24 @@
  * resets the video pipe. This sequence was observed to reinit the
  * camera or, at least, to initiate ISO data stream.
  */
-static void ultracam_reinit_iso(uvd_t *uvd, int do_stop)
+static void ultracam_reinit_iso(struct uvd *uvd, int do_stop)
 {
 }
 
-static void ultracam_video_start(uvd_t *uvd)
+static void ultracam_video_start(struct uvd *uvd)
 {
 	ultracam_change_lighting_conditions(uvd);
 	ultracam_set_sharpness(uvd);
 	ultracam_reinit_iso(uvd, 0);
 }
 
-static int ultracam_resetPipe(uvd_t *uvd)
+static int ultracam_resetPipe(struct uvd *uvd)
 {
 	usb_clear_halt(uvd->dev, uvd->video_endp);
 	return 0;
 }
 
-static int ultracam_alternateSetting(uvd_t *uvd, int setting)
+static int ultracam_alternateSetting(struct uvd *uvd, int setting)
 {
 	static const char proc[] = "ultracam_alternateSetting";
 	int i;
@@ -299,7 +299,7 @@
 /*
  * Return negative code on failure, 0 on success.
  */
-static int ultracam_setup_on_open(uvd_t *uvd)
+static int ultracam_setup_on_open(struct uvd *uvd)
 {
 	int setup_ok = 0; /* Success by default */
 	/* Send init sequence only once, it's large! */
@@ -487,7 +487,7 @@
 	return setup_ok;
 }
 
-static void ultracam_configure_video(uvd_t *uvd)
+static void ultracam_configure_video(struct uvd *uvd)
 {
 	if (uvd == NULL)
 		return;
@@ -539,7 +539,7 @@
  */
 static void *ultracam_probe(struct usb_device *dev, unsigned int ifnum ,const struct usb_device_id *devid)
 {
-	uvd_t *uvd = NULL;
+	struct uvd *uvd = NULL;
 	int i, nas;
 	int actInterface=-1, inactInterface=-1, maxPS=0;
 	unsigned char video_ep = 0;
@@ -628,7 +628,7 @@
 	MOD_INC_USE_COUNT;
 	uvd = usbvideo_AllocateDevice(cams);
 	if (uvd != NULL) {
-		/* Here uvd is a fully allocated uvd_t object */
+		/* Here uvd is a fully allocated uvd object */
 		uvd->flags = flags;
 		uvd->debug = debug;
 		uvd->dev = dev;
@@ -659,6 +659,12 @@
 	return uvd;
 }
 
+
+static struct usb_device_id id_table[] = {
+	{ USB_DEVICE(ULTRACAM_VENDOR_ID, ULTRACAM_PRODUCT_ID) },
+	{ }  /* Terminating entry */
+};
+
 /*
  * ultracam_init()
  *
@@ -666,7 +672,7 @@
  */
 static int __init ultracam_init(void)
 {
-	usbvideo_cb_t cbTbl;
+	struct usbvideo_cb cbTbl;
 	memset(&cbTbl, 0, sizeof(cbTbl));
 	cbTbl.probe = ultracam_probe;
 	cbTbl.setupOnOpen = ultracam_setup_on_open;
@@ -682,7 +688,8 @@
 		sizeof(ultracam_t),
 		"ultracam",
 		&cbTbl,
-		THIS_MODULE);
+		THIS_MODULE,
+		id_table);
 }
 
 static void __exit ultracam_cleanup(void)
@@ -690,16 +697,7 @@
 	usbvideo_Deregister(&cams);
 }
 
-#if defined(usb_device_id_ver)
-
-static __devinitdata struct usb_device_id id_table[] = {
-	{ USB_DEVICE(ULTRACAM_VENDOR_ID, ULTRACAM_PRODUCT_ID) },
-	{ }  /* Terminating entry */
-};
 MODULE_DEVICE_TABLE(usb, id_table);
-
-
-#endif /* defined(usb_device_id_ver) */
 MODULE_LICENSE("GPL");
 
 module_init(ultracam_init);
diff -urN linux-2.4.21-pre2/drivers/usb/usb-ohci.c linux-2.4.21-pre3/drivers/usb/usb-ohci.c
--- linux-2.4.21-pre2/drivers/usb/usb-ohci.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/drivers/usb/usb-ohci.c	Mon Jan  6 16:32:32 2003
@@ -12,6 +12,7 @@
  * 
  * History:
  *
+ * 2002/10/22 OHCI_USB_OPER for ALi lockup in IBM i1200 (ALEX <thchou@ali>)
  * 2002/03/08 interrupt unlink fix (Matt Hughes), better cleanup on
  *	load failure (Matthew Frederickson)
  * 2002/01/20 async unlink fixes:  return -EINPROGRESS (per spec) and
@@ -2168,7 +2169,11 @@
 
   	/* Reset USB (needed by some controllers) */
 	writel (0, &ohci->regs->control);
-      	
+
+	/* Force a state change from USBRESET to USBOPERATIONAL for ALi */
+	(void) readl (&ohci->regs->control);	/* PCI posting */
+	writel (ohci->hc_control = OHCI_USB_OPER, &ohci->regs->control);
+
 	/* HC Reset requires max 10 ms delay */
 	writel (OHCI_HCR,  &ohci->regs->cmdstatus);
 	while ((readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) {
diff -urN linux-2.4.21-pre2/drivers/usb/usbvideo.c linux-2.4.21-pre3/drivers/usb/usbvideo.c
--- linux-2.4.21-pre2/drivers/usb/usbvideo.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/drivers/usb/usbvideo.c	Mon Jan  6 16:32:32 2003
@@ -42,10 +42,10 @@
  * Local prototypes.
  */
 #if USES_PROC_FS
-static void usbvideo_procfs_level1_create(usbvideo_t *ut);
-static void usbvideo_procfs_level1_destroy(usbvideo_t *ut);
-static void usbvideo_procfs_level2_create(uvd_t *uvd);
-static void usbvideo_procfs_level2_destroy(uvd_t *uvd);
+static void usbvideo_procfs_level1_create(struct usbvideo *ut);
+static void usbvideo_procfs_level1_destroy(struct usbvideo *ut);
+static void usbvideo_procfs_level2_create(struct uvd *uvd);
+static void usbvideo_procfs_level2_destroy(struct uvd *uvd);
 static int usbvideo_default_procfs_read_proc(
 	char *page, char **start, off_t off, int count,
 	int *eof, void *data);
@@ -109,29 +109,41 @@
 	vfree(mem);
 }
 
-void RingQueue_Initialize(RingQueue_t *rq)
+static void RingQueue_Initialize(struct RingQueue *rq)
 {
 	assert(rq != NULL);
 	init_waitqueue_head(&rq->wqh);
 }
 
-void RingQueue_Allocate(RingQueue_t *rq, int rqLen)
+static void RingQueue_Allocate(struct RingQueue *rq, int rqLen)
 {
+	/* Make sure the requested size is a power of 2 and
+	   round up if necessary. This allows index wrapping
+	   using masks rather than modulo */
+
+	int i = 1;
 	assert(rq != NULL);
 	assert(rqLen > 0);
+
+	while(rqLen >> i)
+		i++;
+	if(rqLen != 1 << (i-1))
+		rqLen = 1 << i;
+
 	rq->length = rqLen;
+	rq->ri = rq->wi = 0;
 	rq->queue = usbvideo_rvmalloc(rq->length);
 	assert(rq->queue != NULL);
 }
 
-int RingQueue_IsAllocated(const RingQueue_t *rq)
+static int RingQueue_IsAllocated(const struct RingQueue *rq)
 {
 	if (rq == NULL)
 		return 0;
 	return (rq->queue != NULL) && (rq->length > 0);
 }
 
-void RingQueue_Free(RingQueue_t *rq)
+static void RingQueue_Free(struct RingQueue *rq)
 {
 	assert(rq != NULL);
 	if (RingQueue_IsAllocated(rq)) {
@@ -141,19 +153,39 @@
 	}
 }
 
-int RingQueue_Dequeue(RingQueue_t *rq, unsigned char *dst, int len)
+int RingQueue_Dequeue(struct RingQueue *rq, unsigned char *dst, int len)
 {
-	int i;
+	int rql, toread;
+
 	assert(rq != NULL);
 	assert(dst != NULL);
-	for (i=0; i < len; i++) {
-		dst[i] = rq->queue[rq->ri];
-		RING_QUEUE_DEQUEUE_BYTES(rq,1);
+
+	rql = RingQueue_GetLength(rq);
+	if(!rql)
+		return 0;
+
+	/* Clip requested length to available data */
+	if(len > rql)
+		len = rql;
+
+	toread = len;
+	if(rq->ri > rq->wi) {
+		/* Read data from tail */
+		int read = (toread < (rq->length - rq->ri)) ? toread : rq->length - rq->ri;
+		memcpy(dst, rq->queue + rq->ri, read);
+		toread -= read;
+		dst += read;
+		rq->ri = (rq->ri + read) & (rq->length-1);
+	}
+	if(toread) {
+		/* Read data from head */
+		memcpy(dst, rq->queue + rq->ri, toread);
+		rq->ri = (rq->ri + toread) & (rq->length-1);
 	}
 	return len;
 }
 
-int RingQueue_Enqueue(RingQueue_t *rq, const unsigned char *cdata, int n)
+int RingQueue_Enqueue(struct RingQueue *rq, const unsigned char *cdata, int n)
 {
 	int enqueued = 0;
 
@@ -174,7 +206,7 @@
 		if (m > q_avail)
 			m = q_avail;
 
-		memmove(rq->queue + rq->wi, cdata, m);
+		memcpy(rq->queue + rq->wi, cdata, m);
 		RING_QUEUE_ADVANCE_INDEX(rq, wi, m);
 		cdata += m;
 		enqueued += m;
@@ -183,35 +215,27 @@
 	return enqueued;
 }
 
-int RingQueue_GetLength(const RingQueue_t *rq)
+static void RingQueue_InterruptibleSleepOn(struct RingQueue *rq)
 {
-	int ri, wi;
-
 	assert(rq != NULL);
-
-	ri = rq->ri;
-	wi = rq->wi;
-	if (ri == wi)
-		return 0;
-	else if (ri < wi)
-		return wi - ri;
-	else
-		return wi + (rq->length - ri);
+	interruptible_sleep_on(&rq->wqh);
 }
 
-void RingQueue_InterruptibleSleepOn(RingQueue_t *rq)
+void RingQueue_WakeUpInterruptible(struct RingQueue *rq)
 {
 	assert(rq != NULL);
-	interruptible_sleep_on(&rq->wqh);
+	if (waitqueue_active(&rq->wqh))
+		wake_up_interruptible(&rq->wqh);
 }
 
-void RingQueue_WakeUpInterruptible(RingQueue_t *rq)
+void RingQueue_Flush(struct RingQueue *rq)
 {
 	assert(rq != NULL);
-	if (waitqueue_active(&rq->wqh))
-		wake_up_interruptible(&rq->wqh);
+	rq->ri = 0;
+	rq->wi = 0;
 }
 
+
 /*
  * usbvideo_VideosizeToString()
  *
@@ -240,7 +264,7 @@
  * History:
  * 01-Feb-2000 Created.
  */
-void usbvideo_OverlayChar(uvd_t *uvd, usbvideo_frame_t *frame,
+void usbvideo_OverlayChar(struct uvd *uvd, struct usbvideo_frame *frame,
 			  int x, int y, int ch)
 {
 	static const unsigned short digits[16] = {
@@ -295,7 +319,7 @@
  * History:
  * 01-Feb-2000 Created.
  */
-void usbvideo_OverlayString(uvd_t *uvd, usbvideo_frame_t *frame,
+void usbvideo_OverlayString(struct uvd *uvd, struct usbvideo_frame *frame,
 			    int x, int y, const char *str)
 {
 	while (*str) {
@@ -313,7 +337,7 @@
  * History:
  * 01-Feb-2000 Created.
  */
-void usbvideo_OverlayStats(uvd_t *uvd, usbvideo_frame_t *frame)
+void usbvideo_OverlayStats(struct uvd *uvd, struct usbvideo_frame *frame)
 {
 	const int y_diff = 8;
 	char tmp[16];
@@ -348,7 +372,7 @@
 		q_used = RingQueue_GetLength(&uvd->dp);
 		if ((uvd->dp.ri + q_used) >= uvd->dp.length) {
 			u_hi = uvd->dp.length;
-			u_lo = (q_used + uvd->dp.ri) % uvd->dp.length;
+			u_lo = (q_used + uvd->dp.ri) & (uvd->dp.length-1);
 		} else {
 			u_hi = (q_used + uvd->dp.ri);
 			u_lo = -1;
@@ -436,7 +460,7 @@
  * History:
  * 14-Jan-2000 Corrected default multiplier.
  */
-void usbvideo_ReportStatistics(const uvd_t *uvd)
+void usbvideo_ReportStatistics(const struct uvd *uvd)
 {
 	if ((uvd != NULL) && (uvd->stats.urb_count > 0)) {
 		unsigned long allPackets, badPackets, goodPackets, percent;
@@ -492,7 +516,7 @@
  * purposes.
  */
 void usbvideo_DrawLine(
-	usbvideo_frame_t *frame,
+	struct usbvideo_frame *frame,
 	int x1, int y1,
 	int x2, int y2,
 	unsigned char cr, unsigned char cg, unsigned char cb)
@@ -562,20 +586,19 @@
  * History:
  * 01-Feb-2000 Created.
  */
-void usbvideo_TestPattern(uvd_t *uvd, int fullframe, int pmode)
+void usbvideo_TestPattern(struct uvd *uvd, int fullframe, int pmode)
 {
-	static const char proc[] = "usbvideo_TestPattern";
-	usbvideo_frame_t *frame;
+	struct usbvideo_frame *frame;
 	int num_cell = 0;
 	int scan_length = 0;
 	static int num_pass = 0;
 
 	if (uvd == NULL) {
-		err("%s: uvd == NULL", proc);
+		err("%s: uvd == NULL", __FUNCTION__);
 		return;
 	}
 	if ((uvd->curframe < 0) || (uvd->curframe >= USBVIDEO_NUMFRAMES)) {
-		err("%s: uvd->curframe=%d.", proc, uvd->curframe);
+		err("%s: uvd->curframe=%d.", __FUNCTION__, uvd->curframe);
 		return;
 	}
 
@@ -673,74 +696,72 @@
 
 /* ******************************************************************** */
 
-static void usbvideo_ClientIncModCount(uvd_t *uvd)
+static void usbvideo_ClientIncModCount(struct uvd *uvd)
 {
-	static const char proc[] = "usbvideo_ClientIncModCount";
 	if (uvd == NULL) {
-		err("%s: uvd == NULL", proc);
+		err("%s: uvd == NULL", __FUNCTION__);
 		return;
 	}
 	if (uvd->handle == NULL) {
-		err("%s: uvd->handle == NULL", proc);
+		err("%s: uvd->handle == NULL", __FUNCTION__);
 		return;
 	}
 	if (uvd->handle->md_module == NULL) {
-		err("%s: uvd->handle->md_module == NULL", proc);
+		err("%s: uvd->handle->md_module == NULL", __FUNCTION__);
 		return;
 	}
 	__MOD_INC_USE_COUNT(uvd->handle->md_module);
 }
 
-static void usbvideo_ClientDecModCount(uvd_t *uvd)
+static void usbvideo_ClientDecModCount(struct uvd *uvd)
 {
-	static const char proc[] = "usbvideo_ClientDecModCount";
 	if (uvd == NULL) {
-		err("%s: uvd == NULL", proc);
+		err("%s: uvd == NULL", __FUNCTION__);
 		return;
 	}
 	if (uvd->handle == NULL) {
-		err("%s: uvd->handle == NULL", proc);
+		err("%s: uvd->handle == NULL", __FUNCTION__);
 		return;
 	}
 	if (uvd->handle->md_module == NULL) {
-		err("%s: uvd->handle->md_module == NULL", proc);
+		err("%s: uvd->handle->md_module == NULL", __FUNCTION__);
 		return;
 	}
 	__MOD_DEC_USE_COUNT(uvd->handle->md_module);
 }
 
 int usbvideo_register(
-	usbvideo_t **pCams,
+	struct usbvideo **pCams,
 	const int num_cams,
 	const int num_extra,
 	const char *driverName,
-	const usbvideo_cb_t *cbTbl,
-	struct module *md )
+	const struct usbvideo_cb *cbTbl,
+	struct module *md,
+	const struct usb_device_id *id_table)
 {
-	static const char proc[] = "usbvideo_register";
-	usbvideo_t *cams;
+	struct usbvideo *cams;
 	int i, base_size;
 
 	/* Check parameters for sanity */
 	if ((num_cams <= 0) || (pCams == NULL) || (cbTbl == NULL)) {
-		err("%s: Illegal call", proc);
+		err("%s: Illegal call", __FUNCTION__);
 		return -EINVAL;
 	}
 
 	/* Check registration callback - must be set! */
 	if (cbTbl->probe == NULL) {
-		err("%s: probe() is required!", proc);
+		err("%s: probe() is required!", __FUNCTION__);
 		return -EINVAL;
 	}
 
-	base_size = num_cams * sizeof(uvd_t) + sizeof(usbvideo_t);
-	cams = (usbvideo_t *) kmalloc(base_size, GFP_KERNEL);
+	base_size = num_cams * sizeof(struct uvd) + sizeof(struct usbvideo);
+	cams = (struct usbvideo *) kmalloc(base_size, GFP_KERNEL);
 	if (cams == NULL) {
-		err("Failed to allocate %d. bytes for usbvideo_t", base_size);
+		err("Failed to allocate %d. bytes for usbvideo struct", base_size);
 		return -ENOMEM;
 	}
 	dbg("%s: Allocated $%p (%d. bytes) for %d. cameras",
-	    proc, cams, base_size, num_cams);
+	    __FUNCTION__, cams, base_size, num_cams);
 	memset(cams, 0, base_size);
 
 	/* Copy callbacks, apply defaults for those that are not set */
@@ -749,6 +770,10 @@
 		cams->cb.getFrame = usbvideo_GetFrame;
 	if (cams->cb.disconnect == NULL)
 		cams->cb.disconnect = usbvideo_Disconnect;
+	if (cams->cb.startDataPump == NULL)
+		cams->cb.startDataPump = usbvideo_StartDataPump;
+	if (cams->cb.stopDataPump == NULL)
+		cams->cb.stopDataPump = usbvideo_StopDataPump;
 #if USES_PROC_FS
 	/*
 	 * If both /proc fs callbacks are NULL then we assume that the driver
@@ -764,18 +789,18 @@
 #else /* !USES_PROC_FS */
 	/* Report a warning so that user knows why there is no /proc entries */
 	if ((cams->cb.procfs_read != NULL) || (cams->cb.procfs_write == NULL)) {
-		dbg("%s: /proc fs support requested but not configured!", proc);
+		dbg("%s: /proc fs support requested but not configured!", __FUNCTION__);
 	}
 #endif
 	cams->num_cameras = num_cams;
-	cams->cam = (uvd_t *) &cams[1];
+	cams->cam = (struct uvd *) &cams[1];
 	cams->md_module = md;
 	if (cams->md_module == NULL)
-		warn("%s: module == NULL!", proc);
+		warn("%s: module == NULL!", __FUNCTION__);
 	init_MUTEX(&cams->lock);	/* to 1 == available */
 
 	for (i = 0; i < num_cams; i++) {
-		uvd_t *up = &cams->cam[i];
+		struct uvd *up = &cams->cam[i];
 
 		up->handle = cams;
 
@@ -786,11 +811,11 @@
 			if (up->user_data == NULL) {
 				up->user_size = 0;
 				err("%s: Failed to allocate user_data (%d. bytes)",
-				    proc, up->user_size);
+				    __FUNCTION__, up->user_size);
 				return -ENOMEM;
 			}
 			dbg("%s: Allocated cams[%d].user_data=$%p (%d. bytes)",
-			     proc, i, up->user_data, up->user_size);
+			     __FUNCTION__, i, up->user_data, up->user_size);
 		}
 	}
 
@@ -801,10 +826,11 @@
 	cams->usbdrv.name = cams->drvName;
 	cams->usbdrv.probe = cams->cb.probe;
 	cams->usbdrv.disconnect = cams->cb.disconnect;
+	cams->usbdrv.id_table = id_table;
 
 #if USES_PROC_FS
 	if (cams->uses_procfs) {
-		dbg("%s: Creating /proc filesystem entries.", proc);
+		dbg("%s: Creating /proc filesystem entries.", __FUNCTION__);
 		usbvideo_procfs_level1_create(cams);
 	}
 #endif
@@ -827,35 +853,34 @@
  * if you had some dynamically allocated components in ->user field then
  * you should free them before calling here.
  */
-void usbvideo_Deregister(usbvideo_t **pCams)
+void usbvideo_Deregister(struct usbvideo **pCams)
 {
-	static const char proc[] = "usbvideo_deregister";
-	usbvideo_t *cams;
+	struct usbvideo *cams;
 	int i;
 
 	if (pCams == NULL) {
-		err("%s: pCams == NULL", proc);
+		err("%s: pCams == NULL", __FUNCTION__);
 		return;
 	}
 	cams = *pCams;
 	if (cams == NULL) {
-		err("%s: cams == NULL", proc);
+		err("%s: cams == NULL", __FUNCTION__);
 		return;
 	}
 
 #if USES_PROC_FS
 	if (cams->uses_procfs) {
-		dbg("%s: Deregistering filesystem entries.", proc);
+		dbg("%s: Deregistering filesystem entries.", __FUNCTION__);
 		usbvideo_procfs_level1_destroy(cams);
 	}
 #endif
 
-	dbg("%s: Deregistering %s driver.", proc, cams->drvName);
+	dbg("%s: Deregistering %s driver.", __FUNCTION__, cams->drvName);
 	usb_deregister(&cams->usbdrv);
 
-	dbg("%s: Deallocating cams=$%p (%d. cameras)", proc, cams, cams->num_cameras);
+	dbg("%s: Deallocating cams=$%p (%d. cameras)", __FUNCTION__, cams, cams->num_cameras);
 	for (i=0; i < cams->num_cameras; i++) {
-		uvd_t *up = &cams->cam[i];
+		struct uvd *up = &cams->cam[i];
 		int warning = 0;
 
 		if (up->user_data != NULL) {
@@ -867,16 +892,16 @@
 		}
 		if (warning) {
 			err("%s: Warning: user_data=$%p user_size=%d.",
-			    proc, up->user_data, up->user_size);
+			    __FUNCTION__, up->user_data, up->user_size);
 		} else {
 			dbg("%s: Freeing %d. $%p->user_data=$%p",
-			    proc, i, up, up->user_data);
+			    __FUNCTION__, i, up, up->user_data);
 			kfree(up->user_data);
 		}
 	}
 	/* Whole array was allocated in one chunk */
-	dbg("%s: Freed %d uvd_t structures",
-	    proc, cams->num_cameras);
+	dbg("%s: Freed %d uvd structures",
+	    __FUNCTION__, cams->num_cameras);
 	kfree(cams);
 	*pCams = NULL;
 }
@@ -905,23 +930,22 @@
  */
 void usbvideo_Disconnect(struct usb_device *dev, void *ptr)
 {
-	static const char proc[] = "usbvideo_Disconnect";
-	uvd_t *uvd = (uvd_t *) ptr;
+	struct uvd *uvd = (struct uvd *) ptr;
 	int i;
 
 	if ((dev == NULL) || (uvd == NULL)) {
-		err("%s($%p,$%p): Illegal call.", proc, dev, ptr);
+		err("%s($%p,$%p): Illegal call.", __FUNCTION__, dev, ptr);
 		return;
 	}
 	usbvideo_ClientIncModCount(uvd);
 	if (uvd->debug > 0)
-		info("%s(%p,%p.)", proc, dev, ptr);
+		info("%s(%p,%p.)", __FUNCTION__, dev, ptr);
 
 	down(&uvd->lock);
 	uvd->remove_pending = 1; /* Now all ISO data will be ignored */
 
 	/* At this time we ask to cancel outstanding URBs */
-	usbvideo_StopDataPump(uvd);
+	GET_CALLBACK(uvd, stopDataPump)(uvd);
 
 	for (i=0; i < USBVIDEO_NUMSBUF; i++)
 		usb_free_urb(uvd->sbuf[i].urb);
@@ -930,7 +954,7 @@
 	uvd->dev = NULL;    	    /* USB device is no more */
 
 	if (uvd->user)
-		info("%s: In use, disconnect pending.", proc);
+		info("%s: In use, disconnect pending.", __FUNCTION__);
 	else
 		usbvideo_CameraRelease(uvd);
 	up(&uvd->lock);
@@ -942,28 +966,27 @@
 /*
  * usbvideo_CameraRelease()
  *
- * This code does final release of uvd_t. This happens
+ * This code does final release of struct uvd. This happens
  * after the device is disconnected -and- all clients
  * closed their files.
  *
  * History:
  * 27-Jan-2000 Created.
  */
-void usbvideo_CameraRelease(uvd_t *uvd)
+void usbvideo_CameraRelease(struct uvd *uvd)
 {
-	static const char proc[] = "usbvideo_CameraRelease";
 	if (uvd == NULL) {
-		err("%s: Illegal call", proc);
+		err("%s: Illegal call", __FUNCTION__);
 		return;
 	}
 	video_unregister_device(&uvd->vdev);
 	if (uvd->debug > 0)
-		info("%s: Video unregistered.", proc);
+		info("%s: Video unregistered.", __FUNCTION__);
 
 #if USES_PROC_FS
 	assert(uvd->handle != NULL);
 	if (uvd->handle->uses_procfs) {
-		dbg("%s: Removing /proc/%s/ filesystem entries.", proc, uvd->handle->drvName);
+		dbg("%s: Removing /proc/%s/ filesystem entries.", __FUNCTION__, uvd->handle->drvName);
 		usbvideo_procfs_level2_destroy(uvd);
 	}
 #endif
@@ -984,17 +1007,17 @@
  * History:
  * 27-Jan-2000 Created.
  */
-static int usbvideo_find_struct(usbvideo_t *cams)
+static int usbvideo_find_struct(struct usbvideo *cams)
 {
 	int u, rv = -1;
 
 	if (cams == NULL) {
-		err("No usbvideo_t handle?");
+		err("No usbvideo handle?");
 		return -1;
 	}
 	down(&cams->lock);
 	for (u = 0; u < cams->num_cameras; u++) {
-		uvd_t *uvd = &cams->cam[u];
+		struct uvd *uvd = &cams->cam[u];
 		if (!uvd->uvd_used) /* This one is free */
 		{
 			uvd->uvd_used = 1;	/* In use now */
@@ -1008,13 +1031,13 @@
 	return rv;
 }
 
-uvd_t *usbvideo_AllocateDevice(usbvideo_t *cams)
+struct uvd *usbvideo_AllocateDevice(struct usbvideo *cams)
 {
 	int i, devnum;
-	uvd_t *uvd = NULL;
+	struct uvd *uvd = NULL;
 
 	if (cams == NULL) {
-		err("No usbvideo_t handle?");
+		err("No usbvideo handle?");
 		return NULL;
 	}
 
@@ -1069,24 +1092,23 @@
 	return uvd;
 }
 
-int usbvideo_RegisterVideoDevice(uvd_t *uvd)
+int usbvideo_RegisterVideoDevice(struct uvd *uvd)
 {
-	static const char proc[] = "usbvideo_RegisterVideoDevice";
 	char tmp1[20], tmp2[20];	/* Buffers for printing */
 
 	if (uvd == NULL) {
-		err("%s: Illegal call.", proc);
+		err("%s: Illegal call.", __FUNCTION__);
 		return -EINVAL;
 	}
 	if (uvd->video_endp == 0) {
-		info("%s: No video endpoint specified; data pump disabled.", proc);
+		info("%s: No video endpoint specified; data pump disabled.", __FUNCTION__);
 	}
 	if (uvd->paletteBits == 0) {
-		err("%s: No palettes specified!", proc);
+		err("%s: No palettes specified!", __FUNCTION__);
 		return -EINVAL;
 	}
 	if (uvd->defaultPalette == 0) {
-		info("%s: No default palette!", proc);
+		info("%s: No default palette!", __FUNCTION__);
 	}
 
 	uvd->max_frame_size = VIDEOSIZE_X(uvd->canvas) *
@@ -1096,17 +1118,17 @@
 
 	if (uvd->debug > 0) {
 		info("%s: iface=%d. endpoint=$%02x paletteBits=$%08lx",
-		     proc, uvd->iface, uvd->video_endp, uvd->paletteBits);
+		     __FUNCTION__, uvd->iface, uvd->video_endp, uvd->paletteBits);
 	}
 	if (video_register_device(&uvd->vdev, VFL_TYPE_GRABBER, video_nr) == -1) {
-		err("%s: video_register_device failed", proc);
+		err("%s: video_register_device failed", __FUNCTION__);
 		return -EPIPE;
 	}
 	if (uvd->debug > 1) {
-		info("%s: video_register_device() successful", proc);
+		info("%s: video_register_device() successful", __FUNCTION__);
 	}
 	if (uvd->dev == NULL) {
-		err("%s: uvd->dev == NULL", proc);
+		err("%s: uvd->dev == NULL", __FUNCTION__);
 		return -EINVAL;
 	}
 
@@ -1119,7 +1141,7 @@
 	if (uvd->handle->uses_procfs) {
 		if (uvd->debug > 0) {
 			info("%s: Creating /proc/video/%s/ filesystem entries.",
-			     proc, uvd->handle->drvName);
+			     __FUNCTION__, uvd->handle->drvName);
 		}
 		usbvideo_procfs_level2_create(uvd);
 	}
@@ -1144,14 +1166,14 @@
 
 int usbvideo_v4l_mmap(struct video_device *dev, const char *adr, unsigned long size)
 {
-	uvd_t *uvd = (uvd_t *) dev;
+	struct uvd *uvd = (struct uvd *) dev;
 	unsigned long start = (unsigned long) adr;
 	unsigned long page, pos;
 
 	if (!CAMERA_IS_OPERATIONAL(uvd))
 		return -EFAULT;
 
-	if (size > (((2 * uvd->max_frame_size) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)))
+	if (size > (((USBVIDEO_NUMFRAMES * uvd->max_frame_size) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1)))
 		return -EINVAL;
 
 	pos = (unsigned long) uvd->fbuf;
@@ -1187,19 +1209,18 @@
  */
 int usbvideo_v4l_open(struct video_device *dev, int flags)
 {
-	static const char proc[] = "usbvideo_v4l_open";
-	uvd_t *uvd = (uvd_t *) dev;
+	struct uvd *uvd = (struct uvd *) dev;
 	const int sb_size = FRAMES_PER_DESC * uvd->iso_packet_len;
 	int i, errCode = 0;
 
 	if (uvd->debug > 1)
-		info("%s($%p,$%08x", proc, dev, flags);
+		info("%s($%p,$%08x", __FUNCTION__, dev, flags);
 
 	usbvideo_ClientIncModCount(uvd);
 	down(&uvd->lock);
 
 	if (uvd->user) {
-		err("%s: Someone tried to open an already opened device!", proc);
+		err("%s: Someone tried to open an already opened device!", __FUNCTION__);
 		errCode = -EBUSY;
 	} else {
 		/* Clear statistics */
@@ -1212,10 +1233,10 @@
 		/* Allocate memory for the frame buffers */
 		uvd->fbuf_size = USBVIDEO_NUMFRAMES * uvd->max_frame_size;
 		uvd->fbuf = usbvideo_rvmalloc(uvd->fbuf_size);
-		RingQueue_Allocate(&uvd->dp, 128*1024); /* FIXME #define */
+		RingQueue_Allocate(&uvd->dp, RING_QUEUE_SIZE);
 		if ((uvd->fbuf == NULL) ||
 		    (!RingQueue_IsAllocated(&uvd->dp))) {
-			err("%s: Failed to allocate fbuf or dp", proc);
+			err("%s: Failed to allocate fbuf or dp", __FUNCTION__);
 			errCode = -ENOMEM;
 		} else {
 			/* Allocate all buffers */
@@ -1257,23 +1278,23 @@
 	if (errCode == 0) {
 		/* Start data pump if we have valid endpoint */
 		if (uvd->video_endp != 0)
-			errCode = usbvideo_StartDataPump(uvd);
+			errCode = GET_CALLBACK(uvd, startDataPump)(uvd);
 		if (errCode == 0) {
 			if (VALID_CALLBACK(uvd, setupOnOpen)) {
 				if (uvd->debug > 1)
-					info("%s: setupOnOpen callback", proc);
+					info("%s: setupOnOpen callback", __FUNCTION__);
 				errCode = GET_CALLBACK(uvd, setupOnOpen)(uvd);
 				if (errCode < 0) {
 					err("%s: setupOnOpen callback failed (%d.).",
-					    proc, errCode);
+					    __FUNCTION__, errCode);
 				} else if (uvd->debug > 1) {
-					info("%s: setupOnOpen callback successful", proc);
+					info("%s: setupOnOpen callback successful", __FUNCTION__);
 				}
 			}
 			if (errCode == 0) {
 				uvd->settingsAdjusted = 0;
 				if (uvd->debug > 1)
-					info("%s: Open succeeded.", proc);
+					info("%s: Open succeeded.", __FUNCTION__);
 				uvd->user++;
 			}
 		}
@@ -1282,7 +1303,7 @@
 	if (errCode != 0)
 		usbvideo_ClientDecModCount(uvd);
 	if (uvd->debug > 0)
-		info("%s: Returning %d.", proc, errCode);
+		info("%s: Returning %d.", __FUNCTION__, errCode);
 	return errCode;
 }
 
@@ -1300,15 +1321,14 @@
  */
 void usbvideo_v4l_close(struct video_device *dev)
 {
-	static const char proc[] = "usbvideo_v4l_close";
-	uvd_t *uvd = (uvd_t *)dev;
+	struct uvd *uvd = (struct uvd *)dev;
 	int i;
 
 	if (uvd->debug > 1)
-		info("%s($%p)", proc, dev);
+		info("%s($%p)", __FUNCTION__, dev);
 
-	down(&uvd->lock);	
-	usbvideo_StopDataPump(uvd);
+	down(&uvd->lock);
+	GET_CALLBACK(uvd, stopDataPump)(uvd);
 	usbvideo_rvfree(uvd->fbuf, uvd->fbuf_size);
 	uvd->fbuf = NULL;
 	RingQueue_Free(&uvd->dp);
@@ -1332,7 +1352,7 @@
 	usbvideo_ClientDecModCount(uvd);
 
 	if (uvd->debug > 1)
-		info("%s: Completed.", proc);
+		info("%s: Completed.", __FUNCTION__);
 }
 
 /*
@@ -1345,7 +1365,7 @@
  */
 int usbvideo_v4l_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
 {
-	uvd_t *uvd = (uvd_t *)dev;
+	struct uvd *uvd = (struct uvd *)dev;
 
 	if (!CAMERA_IS_OPERATIONAL(uvd))
 		return -EFAULT;
@@ -1399,6 +1419,9 @@
 
 			if (copy_from_user(&vw, arg, sizeof(vw)))
 				return -EFAULT;
+			if (VALID_CALLBACK(uvd, setVideoMode)) {
+				return GET_CALLBACK(uvd, setVideoMode)(uvd, &vw);
+			}
 			if (vw.flags)
 				return -EINVAL;
 			if (vw.clipcount)
@@ -1416,8 +1439,8 @@
 
 			vw.x = 0;
 			vw.y = 0;
-			vw.width = VIDEOSIZE_X(uvd->canvas);
-			vw.height = VIDEOSIZE_Y(uvd->canvas);
+			vw.width = VIDEOSIZE_X(uvd->videosize);
+			vw.height = VIDEOSIZE_Y(uvd->videosize);
 			vw.chromakey = 0;
 			if (VALID_CALLBACK(uvd, getFPS))
 				vw.flags = GET_CALLBACK(uvd, getFPS)(uvd);
@@ -1432,12 +1455,13 @@
 		case VIDIOCGMBUF:
 		{
 			struct video_mbuf vm;
+			int i;
 
 			memset(&vm, 0, sizeof(vm));
-			vm.size = uvd->max_frame_size * 2;
-			vm.frames = 2;
-			vm.offsets[0] = 0;
-			vm.offsets[1] = uvd->max_frame_size;
+			vm.size = uvd->max_frame_size * USBVIDEO_NUMFRAMES;
+			vm.frames = USBVIDEO_NUMFRAMES;
+			for(i = 0; i < USBVIDEO_NUMFRAMES; i++)
+				vm.offsets[i] = i * uvd->max_frame_size;
 
 			if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm)))
 				return -EFAULT;
@@ -1487,8 +1511,8 @@
 				}
 				return -EINVAL;
 			}
-			if ((vm.frame != 0) && (vm.frame != 1)) {
-				err("VIDIOCMCAPTURE: vm.frame=%d. !E [0,1]", vm.frame);
+			if ((vm.frame < 0) && (vm.frame >= USBVIDEO_NUMFRAMES)) {
+				err("VIDIOCMCAPTURE: vm.frame=%d. !E [0-%d]", vm.frame, USBVIDEO_NUMFRAMES-1);
 				return -EINVAL;
 			}
 			if (uvd->frame[vm.frame].frameState == FrameState_Grabbing) {
@@ -1585,28 +1609,26 @@
  */
 long usbvideo_v4l_read(struct video_device *dev, char *buf, unsigned long count, int noblock)
 {
-	static const char proc[] = "usbvideo_v4l_read";
-	uvd_t *uvd = (uvd_t *) dev;
-	int frmx = -1;
-	usbvideo_frame_t *frame;
+	struct uvd *uvd = (struct uvd *) dev;
+	int frmx = -1, i;
+	struct usbvideo_frame *frame;
 
 	if (!CAMERA_IS_OPERATIONAL(uvd) || (buf == NULL))
 		return -EFAULT;
 
 	if (uvd->debug >= 1)
-		info("%s: %ld. bytes, noblock=%d.", proc, count, noblock);
+		info("%s: %ld. bytes, noblock=%d.", __FUNCTION__, count, noblock);
 
 	down(&uvd->lock);	
 
 	/* See if a frame is completed, then use it. */
-	if ((uvd->frame[0].frameState == FrameState_Done) ||
-	    (uvd->frame[0].frameState == FrameState_Done_Hold) ||
-	    (uvd->frame[0].frameState == FrameState_Error)) {
-		frmx = 0;
-	} else if ((uvd->frame[1].frameState >= FrameState_Done) ||
-		   (uvd->frame[1].frameState == FrameState_Done_Hold) ||
-		   (uvd->frame[1].frameState >= FrameState_Done)) {
-		frmx = 1;
+	for(i = 0; i < USBVIDEO_NUMFRAMES; i++) {
+		if ((uvd->frame[i].frameState == FrameState_Done) ||
+		    (uvd->frame[i].frameState == FrameState_Done_Hold) ||
+		    (uvd->frame[i].frameState == FrameState_Error)) {
+			frmx = i;
+			break;
+		}
 	}
 
 	/* FIXME: If we don't start a frame here then who ever does? */
@@ -1621,10 +1643,12 @@
 	 * We will need to wait until it becomes cooked, of course.
 	 */
 	if (frmx == -1) {
-		if (uvd->frame[0].frameState == FrameState_Grabbing)
-			frmx = 0;
-		else if (uvd->frame[1].frameState == FrameState_Grabbing)
-			frmx = 1;
+		for(i = 0; i < USBVIDEO_NUMFRAMES; i++) {
+			if (uvd->frame[i].frameState == FrameState_Grabbing) {
+				frmx = i;
+				break;
+			}
+		}
 	}
 
 	/*
@@ -1640,7 +1664,7 @@
 	 */
 	if (frmx == -1) {
 		if (uvd->defaultPalette == 0) {
-			err("%s: No default palette; don't know what to do!", proc);
+			err("%s: No default palette; don't know what to do!", __FUNCTION__);
 			count = -EFAULT;
 			goto read_done;
 		}
@@ -1718,7 +1742,7 @@
 	frame->seqRead_Index += count;
 	if (uvd->debug >= 1) {
 		err("%s: {copy} count used=%ld, new seqRead_Index=%ld",
-			proc, count, frame->seqRead_Index);
+		    __FUNCTION__, count, frame->seqRead_Index);
 	}
 
 	/* Finally check if the frame is done with and "release" it */
@@ -1728,8 +1752,8 @@
 
 		/* Mark it as available to be used again. */
 		uvd->frame[frmx].frameState = FrameState_Unused;
-		if (usbvideo_NewFrame(uvd, frmx ? 0 : 1)) {
-			err("%s: usbvideo_NewFrame failed.", proc);
+		if (usbvideo_NewFrame(uvd, (frmx + 1) % USBVIDEO_NUMFRAMES)) {
+			err("%s: usbvideo_NewFrame failed.", __FUNCTION__);
 		}
 	}
 read_done:
@@ -1740,7 +1764,7 @@
 /*
  * Make all of the blocks of data contiguous
  */
-static int usbvideo_CompressIsochronous(uvd_t *uvd, struct urb *urb)
+static int usbvideo_CompressIsochronous(struct uvd *uvd, struct urb *urb)
 {
 	char *cdata;
 	int i, totlen = 0;
@@ -1773,7 +1797,7 @@
 static void usbvideo_IsocIrq(struct urb *urb)
 {
 	int i, len;
-	uvd_t *uvd = urb->context;
+	struct uvd *uvd = urb->context;
 
 	/* We don't want to do anything if we are about to be removed! */
 	if (!CAMERA_IS_OPERATIONAL(uvd))
@@ -1825,17 +1849,16 @@
  *             of hardcoded values. Simplified by using for loop,
  *             allowed any number of URBs.
  */
-int usbvideo_StartDataPump(uvd_t *uvd)
+int usbvideo_StartDataPump(struct uvd *uvd)
 {
-	static const char proc[] = "usbvideo_StartDataPump";
 	struct usb_device *dev = uvd->dev;
 	int i, errFlag;
 
 	if (uvd->debug > 1)
-		info("%s($%p)", proc, uvd);
+		info("%s($%p)", __FUNCTION__, uvd);
 
 	if (!CAMERA_IS_OPERATIONAL(uvd)) {
-		err("%s: Camera is not operational",proc);
+		err("%s: Camera is not operational", __FUNCTION__);
 		return -EFAULT;
 	}
 	uvd->curframe = -1;
@@ -1843,14 +1866,14 @@
 	/* Alternate interface 1 is is the biggest frame size */
 	i = usb_set_interface(dev, uvd->iface, uvd->ifaceAltActive);
 	if (i < 0) {
-		err("%s: usb_set_interface error", proc);
+		err("%s: usb_set_interface error", __FUNCTION__);
 		uvd->last_error = i;
 		return -EBUSY;
 	}
 	if (VALID_CALLBACK(uvd, videoStart))
 		GET_CALLBACK(uvd, videoStart)(uvd);
 	else 
-		err("%s: videoStart not set", proc);
+		err("%s: videoStart not set", __FUNCTION__);
 
 	/* We double buffer the Iso lists */
 	for (i=0; i < USBVIDEO_NUMSBUF; i++) {
@@ -1882,12 +1905,12 @@
 	for (i=0; i < USBVIDEO_NUMSBUF; i++) {
 		errFlag = usb_submit_urb(uvd->sbuf[i].urb);
 		if (errFlag)
-			err("%s: usb_submit_isoc(%d) ret %d", proc, i, errFlag);
+			err("%s: usb_submit_isoc(%d) ret %d", __FUNCTION__, i, errFlag);
 	}
 
 	uvd->streaming = 1;
 	if (uvd->debug > 1)
-		info("%s: streaming=1 video_endp=$%02x", proc, uvd->video_endp);
+		info("%s: streaming=1 video_endp=$%02x", __FUNCTION__, uvd->video_endp);
 	return 0;
 }
 
@@ -1901,13 +1924,12 @@
  * 22-Jan-2000 Corrected order of actions to work after surprise removal.
  * 27-Jan-2000 Used uvd->iface, uvd->ifaceAltInactive instead of hardcoded values.
  */
-void usbvideo_StopDataPump(uvd_t *uvd)
+void usbvideo_StopDataPump(struct uvd *uvd)
 {
-	static const char proc[] = "usbvideo_StopDataPump";
 	int i, j;
 
 	if (uvd->debug > 1)
-		info("%s($%p)", proc, uvd);
+		info("%s($%p)", __FUNCTION__, uvd);
 
 	if ((uvd == NULL) || (!uvd->streaming) || (uvd->dev == NULL))
 		return;
@@ -1916,10 +1938,10 @@
 	for (i=0; i < USBVIDEO_NUMSBUF; i++) {
 		j = usb_unlink_urb(uvd->sbuf[i].urb);
 		if (j < 0)
-			err("%s: usb_unlink_urb() error %d.", proc, j);
+			err("%s: usb_unlink_urb() error %d.", __FUNCTION__, j);
 	}
 	if (uvd->debug > 1)
-		info("%s: streaming=0", proc);
+		info("%s: streaming=0", __FUNCTION__);
 	uvd->streaming = 0;
 
 	if (!uvd->remove_pending) {
@@ -1927,12 +1949,12 @@
 		if (VALID_CALLBACK(uvd, videoStop))
 			GET_CALLBACK(uvd, videoStop)(uvd);
 		else 
-			err("%s: videoStop not set" ,proc);
+			err("%s: videoStop not set", __FUNCTION__);
 
 		/* Set packet size to 0 */
 		j = usb_set_interface(uvd->dev, uvd->iface, uvd->ifaceAltInactive);
 		if (j < 0) {
-			err("%s: usb_set_interface() error %d.", proc, j);
+			err("%s: usb_set_interface() error %d.", __FUNCTION__, j);
 			uvd->last_error = j;
 		}
 	}
@@ -1945,9 +1967,9 @@
  * 29-Mar-00 Added copying of previous frame into the current one.
  * 6-Aug-00  Added model 3 video sizes, removed redundant width, height.
  */
-int usbvideo_NewFrame(uvd_t *uvd, int framenum)
+int usbvideo_NewFrame(struct uvd *uvd, int framenum)
 {
-	usbvideo_frame_t *frame;
+	struct usbvideo_frame *frame;
 	int n;
 
 	if (uvd->debug > 1)
@@ -1965,7 +1987,7 @@
 		uvd->settingsAdjusted = 1;
 	}
 
-	n = (framenum - 1 + USBVIDEO_NUMFRAMES) % USBVIDEO_NUMFRAMES;
+	n = (framenum + 1) % USBVIDEO_NUMFRAMES;
 	if (uvd->frame[n].frameState == FrameState_Ready)
 		framenum = n;
 
@@ -1997,7 +2019,8 @@
 	 */
 	if (!(uvd->flags & FLAGS_SEPARATE_FRAMES)) {
 		/* This copies previous frame into this one to mask losses */
-		memmove(frame->data, uvd->frame[1-framenum].data, uvd->max_frame_size);
+		int prev = (framenum - 1 + USBVIDEO_NUMFRAMES) % USBVIDEO_NUMFRAMES;
+		memmove(frame->data, uvd->frame[prev].data, uvd->max_frame_size);
 	} else {
 		if (uvd->flags & FLAGS_CLEAN_FRAMES) {
 			/* This provides a "clean" frame but slows things down */
@@ -2020,7 +2043,7 @@
  * FLAGS_NO_DECODING set. Therefore, any regular build of any driver
  * based on usbvideo can use this feature at any time.
  */
-void usbvideo_CollectRawData(uvd_t *uvd, usbvideo_frame_t *frame)
+void usbvideo_CollectRawData(struct uvd *uvd, struct usbvideo_frame *frame)
 {
 	int n;
 
@@ -2050,18 +2073,17 @@
 	}
 }
 
-int usbvideo_GetFrame(uvd_t *uvd, int frameNum)
+int usbvideo_GetFrame(struct uvd *uvd, int frameNum)
 {
-	static const char proc[] = "usbvideo_GetFrame";
-	usbvideo_frame_t *frame = &uvd->frame[frameNum];
+	struct usbvideo_frame *frame = &uvd->frame[frameNum];
 
 	if (uvd->debug >= 2)
-		info("%s($%p,%d.)", proc, uvd, frameNum);
+		info("%s($%p,%d.)", __FUNCTION__, uvd, frameNum);
 
 	switch (frame->frameState) {
         case FrameState_Unused:
 		if (uvd->debug >= 2)
-			info("%s: FrameState_Unused", proc);
+			info("%s: FrameState_Unused", __FUNCTION__);
 		return -EINVAL;
         case FrameState_Ready:
         case FrameState_Grabbing:
@@ -2071,7 +2093,7 @@
 	redo:
 		if (!CAMERA_IS_OPERATIONAL(uvd)) {
 			if (uvd->debug >= 2)
-				info("%s: Camera is not operational (1)", proc);
+				info("%s: Camera is not operational (1)", __FUNCTION__);
 			return -EIO;
 		}
 		ntries = 0; 
@@ -2080,24 +2102,24 @@
 			signalPending = signal_pending(current);
 			if (!CAMERA_IS_OPERATIONAL(uvd)) {
 				if (uvd->debug >= 2)
-					info("%s: Camera is not operational (2)", proc);
+					info("%s: Camera is not operational (2)", __FUNCTION__);
 				return -EIO;
 			}
 			assert(uvd->fbuf != NULL);
 			if (signalPending) {
 				if (uvd->debug >= 2)
-					info("%s: Signal=$%08x", proc, signalPending);
+					info("%s: Signal=$%08x", __FUNCTION__, signalPending);
 				if (uvd->flags & FLAGS_RETRY_VIDIOCSYNC) {
 					usbvideo_TestPattern(uvd, 1, 0);
 					uvd->curframe = -1;
 					uvd->stats.frame_num++;
 					if (uvd->debug >= 2)
-						info("%s: Forced test pattern screen", proc);
+						info("%s: Forced test pattern screen", __FUNCTION__);
 					return 0;
 				} else {
 					/* Standard answer: Interrupted! */
 					if (uvd->debug >= 2)
-						info("%s: Interrupted!", proc);
+						info("%s: Interrupted!", __FUNCTION__);
 					return -EINTR;
 				}
 			} else {
@@ -2107,17 +2129,17 @@
 				else if (VALID_CALLBACK(uvd, processData))
 					GET_CALLBACK(uvd, processData)(uvd, frame);
 				else 
-					err("%s: processData not set", proc);
+					err("%s: processData not set", __FUNCTION__);
 			}
 		} while (frame->frameState == FrameState_Grabbing);
 		if (uvd->debug >= 2) {
 			info("%s: Grabbing done; state=%d. (%lu. bytes)",
-			     proc, frame->frameState, frame->seqRead_Length);
+			     __FUNCTION__, frame->frameState, frame->seqRead_Length);
 		}
 		if (frame->frameState == FrameState_Error) {
 			int ret = usbvideo_NewFrame(uvd, frameNum);
 			if (ret < 0) {
-				err("%s: usbvideo_NewFrame() failed (%d.)", proc, ret);
+				err("%s: usbvideo_NewFrame() failed (%d.)", __FUNCTION__, ret);
 				return ret;
 			}
 			goto redo;
@@ -2149,7 +2171,7 @@
 		}
 		frame->frameState = FrameState_Done_Hold;
 		if (uvd->debug >= 2)
-			info("%s: Entered FrameState_Done_Hold state.", proc);
+			info("%s: Entered FrameState_Done_Hold state.", __FUNCTION__);
 		return 0;
 
 	case FrameState_Done_Hold:
@@ -2160,12 +2182,12 @@
 		 * it will be released back into the wild to roam freely.
 		 */
 		if (uvd->debug >= 2)
-			info("%s: FrameState_Done_Hold state.", proc);
+			info("%s: FrameState_Done_Hold state.", __FUNCTION__);
 		return 0;
 	}
 
 	/* Catch-all for other cases. We shall not be here. */
-	err("%s: Invalid state %d.", proc, frame->frameState);
+	err("%s: Invalid state %d.", __FUNCTION__, frame->frameState);
 	frame->frameState = FrameState_Unused;
 	return 0;
 }
@@ -2183,7 +2205,7 @@
  * line above then we just copy next line. Similarly, if we need to
  * create a last line then preceding line is used.
  */
-void usbvideo_DeinterlaceFrame(uvd_t *uvd, usbvideo_frame_t *frame)
+void usbvideo_DeinterlaceFrame(struct uvd *uvd, struct usbvideo_frame *frame)
 {
 	if ((uvd == NULL) || (frame == NULL))
 		return;
@@ -2251,15 +2273,14 @@
  * History:
  * 09-Feb-2001  Created.
  */
-void usbvideo_SoftwareContrastAdjustment(uvd_t *uvd, usbvideo_frame_t *frame)
+void usbvideo_SoftwareContrastAdjustment(struct uvd *uvd, struct usbvideo_frame *frame)
 {
-	static const char proc[] = "usbvideo_SoftwareContrastAdjustment";
 	int i, j, v4l_linesize;
 	signed long adj;
 	const int ccm = 128; /* Color correction median - see below */
 
 	if ((uvd == NULL) || (frame == NULL)) {
-		err("%s: Illegal call.", proc);
+		err("%s: Illegal call.", __FUNCTION__);
 		return;
 	}
 	adj = (uvd->vpic.contrast - 0x8000) >> 8; /* -128..+127 = -ccm..+(ccm-1)*/
@@ -2309,16 +2330,14 @@
 
 extern struct proc_dir_entry *video_proc_entry;
 
-static void usbvideo_procfs_level1_create(usbvideo_t *ut)
+static void usbvideo_procfs_level1_create(struct usbvideo *ut)
 {
-	static const char proc[] = "usbvideo_procfs_level1_create";
-
 	if (ut == NULL) {
-		err("%s: ut == NULL", proc);
+		err("%s: ut == NULL", __FUNCTION__);
 		return;
 	}
 	if (video_proc_entry == NULL) {
-		err("%s: /proc/video/ doesn't exist.", proc);
+		err("%s: /proc/video/ doesn't exist.", __FUNCTION__);
 		return;
 	}
 	ut->procfs_dEntry = create_proc_entry(ut->drvName, S_IFDIR, video_proc_entry);
@@ -2326,16 +2345,14 @@
 		if (ut->md_module != NULL)
 			ut->procfs_dEntry->owner = ut->md_module;
 	} else {
-		err("%s: Unable to initialize /proc/video/%s", proc, ut->drvName);
+		err("%s: Unable to initialize /proc/video/%s", __FUNCTION__, ut->drvName);
 	}
 }
 
-static void usbvideo_procfs_level1_destroy(usbvideo_t *ut)
+static void usbvideo_procfs_level1_destroy(struct usbvideo *ut)
 {
-	static const char proc[] = "usbvideo_procfs_level1_destroy";
-
 	if (ut == NULL) {
-		err("%s: ut == NULL", proc);
+		err("%s: ut == NULL", __FUNCTION__);
 		return;
 	}
 	if (ut->procfs_dEntry != NULL) {
@@ -2344,17 +2361,15 @@
 	}
 }
 
-static void usbvideo_procfs_level2_create(uvd_t *uvd)
+static void usbvideo_procfs_level2_create(struct uvd *uvd)
 {
-	static const char proc[] = "usbvideo_procfs_level2_create";
-
 	if (uvd == NULL) {
-		err("%s: uvd == NULL", proc);
+		err("%s: uvd == NULL", __FUNCTION__);
 		return;
 	}
 	assert(uvd->handle != NULL);
 	if (uvd->handle->procfs_dEntry == NULL) {
-		err("%s: uvd->handle->procfs_dEntry == NULL", proc);
+		err("%s: uvd->handle->procfs_dEntry == NULL", __FUNCTION__);
 		return;
 	}
 
@@ -2368,16 +2383,14 @@
 		uvd->procfs_vEntry->read_proc = uvd->handle->cb.procfs_read;
 		uvd->procfs_vEntry->write_proc = uvd->handle->cb.procfs_write;
 	} else {
-		err("%s: Failed to create entry \"%s\"", proc, uvd->videoName);
+		err("%s: Failed to create entry \"%s\"", __FUNCTION__, uvd->videoName);
 	}
 }
 
-static void usbvideo_procfs_level2_destroy(uvd_t *uvd)
+static void usbvideo_procfs_level2_destroy(struct uvd *uvd)
 {
-	static const char proc[] = "usbvideo_procfs_level2_destroy";
-
 	if (uvd == NULL) {
-		err("%s: uvd == NULL", proc);
+		err("%s: uvd == NULL", __FUNCTION__);
 		return;
 	}
 	if (uvd->procfs_vEntry != NULL) {
diff -urN linux-2.4.21-pre2/drivers/usb/usbvideo.h linux-2.4.21-pre3/drivers/usb/usbvideo.h
--- linux-2.4.21-pre2/drivers/usb/usbvideo.h	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/drivers/usb/usbvideo.h	Mon Jan  6 16:32:32 2003
@@ -113,62 +113,61 @@
     mr = LIMIT_RGB(mm_r); \
 }
 
-#define	RING_QUEUE_ADVANCE_INDEX(rq,ind,n) (rq)->ind = ((rq)->ind + (n)) % (rq)->length
+#define	RING_QUEUE_SIZE		(128*1024)	/* Must be a power of 2 */
+#define	RING_QUEUE_ADVANCE_INDEX(rq,ind,n) (rq)->ind = ((rq)->ind + (n)) & ((rq)->length-1)
 #define	RING_QUEUE_DEQUEUE_BYTES(rq,n) RING_QUEUE_ADVANCE_INDEX(rq,ri,n)
-#define	RING_QUEUE_PEEK(rq,ofs) ((rq)->queue[((ofs) + (rq)->ri) % (rq)->length])
+#define	RING_QUEUE_PEEK(rq,ofs) ((rq)->queue[((ofs) + (rq)->ri) & ((rq)->length-1)])
 
-typedef struct {
+struct RingQueue {
 	unsigned char *queue;	/* Data from the Isoc data pump */
 	int length;		/* How many bytes allocated for the queue */
 	int wi;			/* That's where we write */
 	int ri;			/* Read from here until you hit write index */
 	wait_queue_head_t wqh;	/* Processes waiting */
-} RingQueue_t;
+};
 
-typedef enum {
+enum ScanState {
 	ScanState_Scanning,	/* Scanning for header */
 	ScanState_Lines		/* Parsing lines */
-} ScanState_t;
+};
 
 /* Completion states of the data parser */
-typedef enum {
+enum ParseState {
 	scan_Continue,		/* Just parse next item */
 	scan_NextFrame,		/* Frame done, send it to V4L */
 	scan_Out,		/* Not enough data for frame */
 	scan_EndParse		/* End parsing */
-} ParseState_t;
+};
 
-typedef enum {
+enum FrameState {
 	FrameState_Unused,	/* Unused (no MCAPTURE) */
 	FrameState_Ready,	/* Ready to start grabbing */
 	FrameState_Grabbing,	/* In the process of being grabbed into */
 	FrameState_Done,	/* Finished grabbing, but not been synced yet */
 	FrameState_Done_Hold,	/* Are syncing or reading */
 	FrameState_Error,	/* Something bad happened while processing */
-} FrameState_t;
+};
 
 /*
  * Some frames may contain only even or odd lines. This type
  * specifies what type of deinterlacing is required.
  */
-typedef enum {
+enum Deinterlace {
 	Deinterlace_None=0,
 	Deinterlace_FillOddLines,
 	Deinterlace_FillEvenLines
-} Deinterlace_t;
-
-struct usb_device;
+};
 
 #define USBVIDEO_NUMFRAMES	2	/* How many frames we work with */
 #define USBVIDEO_NUMSBUF	2	/* How many URBs linked in a ring */
 
 /* This structure represents one Isoc request - URB and buffer */
-typedef struct {
+struct usbvideo_sbuf {
 	char *data;
 	struct urb *urb;
-} usbvideo_sbuf_t;
+};
 
-typedef struct {
+struct usbvideo_frame {
 	char *data;		/* Frame buffer */
 	unsigned long header;	/* Significant bits from the header */
 
@@ -176,9 +175,9 @@
 	videosize_t request;	/* That's what the application asked for */
 	unsigned short palette;	/* The desired format */
 
-	FrameState_t frameState;/* State of grabbing */
-	ScanState_t scanstate;	/* State of scanning */
-	Deinterlace_t deinterlace;
+	enum FrameState frameState;/* State of grabbing */
+	enum ScanState scanstate;	/* State of scanning */
+	enum Deinterlace deinterlace;
 	int flags;		/* USBVIDEO_FRAME_FLAG_xxx bit flags */
 
 	int curline;		/* Line of frame we're working on */
@@ -187,10 +186,10 @@
 	long seqRead_Index;	/* Amount of data that has been already read */
 
 	void *user;		/* Additional data that user may need */
-} usbvideo_frame_t;
+};
 
 /* Statistics that can be overlaid on screen */
-typedef struct {
+struct usbvideo_statistics {
         unsigned long frame_num;	/* Sequential number of the frame */
         unsigned long urb_count;        /* How many URBs we received so far */
         unsigned long urb_length;       /* Length of last URB */
@@ -198,14 +197,14 @@
         unsigned long header_count;     /* How many frame headers we found */
 	unsigned long iso_skip_count;	/* How many empty ISO packets received */
 	unsigned long iso_err_count;	/* How many bad ISO packets received */
-} usbvideo_statistics_t;
+};
 
-struct s_usbvideo_t;
+struct usbvideo;
 
-typedef struct {
+struct uvd {
 	struct video_device vdev;	/* Must be the first field! */
 	struct usb_device *dev;
-	struct s_usbvideo_t *handle;	/* Points back to the usbvideo_t */
+	struct usbvideo *handle;	/* Points back to the struct usbvideo */
 	void *user_data;		/* Camera-dependent data */
 	int user_size;			/* Size of that camera-dependent data */
 	int debug;			/* Debug level for usbvideo */
@@ -235,58 +234,61 @@
 	int curframe;
 	int iso_packet_len;	/* Videomode-dependent, saves bus bandwidth */
 
-	RingQueue_t dp;		/* Isoc data pump */
-	usbvideo_frame_t frame[USBVIDEO_NUMFRAMES];
-	usbvideo_sbuf_t sbuf[USBVIDEO_NUMSBUF];
+	struct RingQueue dp;	/* Isoc data pump */
+	struct usbvideo_frame frame[USBVIDEO_NUMFRAMES];
+	struct usbvideo_sbuf sbuf[USBVIDEO_NUMSBUF];
 
 	volatile int remove_pending;	/* If set then about to exit */
 
 	struct video_picture vpic, vpic_old;	/* Picture settings */
 	struct video_capability vcap;		/* Video capabilities */
 	struct video_channel vchan;	/* May be used for tuner support */
-	usbvideo_statistics_t stats;
+	struct usbvideo_statistics stats;
 	struct proc_dir_entry *procfs_vEntry;	/* /proc/video/MYDRIVER/video2 */
 	char videoName[32];		/* Holds name like "video7" */
-} uvd_t;
+};
 
 /*
  * usbvideo callbacks (virtual methods). They are set when usbvideo
  * services are registered. All of these default to NULL, except those
  * that default to usbvideo-provided methods.
  */
-typedef struct {
+struct usbvideo_cb {
 	void *(*probe)(struct usb_device *, unsigned int,const struct usb_device_id *);
-	void (*userFree)(uvd_t *);
+	void (*userFree)(struct uvd *);
 	void (*disconnect)(struct usb_device *, void *);
-	int (*setupOnOpen)(uvd_t *);
-	void (*videoStart)(uvd_t *);
-	void (*videoStop)(uvd_t *);
-	void (*processData)(uvd_t *, usbvideo_frame_t *);
-	void (*postProcess)(uvd_t *, usbvideo_frame_t *);
-	void (*adjustPicture)(uvd_t *);
-	int (*getFPS)(uvd_t *);
-	int (*overlayHook)(uvd_t *, usbvideo_frame_t *);
-	int (*getFrame)(uvd_t *, int);
+	int (*setupOnOpen)(struct uvd *);
+	void (*videoStart)(struct uvd *);
+	void (*videoStop)(struct uvd *);
+	void (*processData)(struct uvd *, struct usbvideo_frame *);
+	void (*postProcess)(struct uvd *, struct usbvideo_frame *);
+	void (*adjustPicture)(struct uvd *);
+	int (*getFPS)(struct uvd *);
+	int (*overlayHook)(struct uvd *, struct usbvideo_frame *);
+	int (*getFrame)(struct uvd *, int);
 	int (*procfs_read)(char *page,char **start,off_t off,int count,int *eof,void *data);
 	int (*procfs_write)(struct file *file,const char *buffer,unsigned long count,void *data);
-} usbvideo_cb_t;
+	int (*startDataPump)(struct uvd *uvd);
+	void (*stopDataPump)(struct uvd *uvd);
+	int (*setVideoMode)(struct uvd *uvd, struct video_window *vw);
+};
 
-struct s_usbvideo_t {
+struct usbvideo {
 	int num_cameras;		/* As allocated */
 	struct usb_driver usbdrv;	/* Interface to the USB stack */
 	char drvName[80];		/* Driver name */
 	struct semaphore lock;		/* Mutex protecting camera structures */
-	usbvideo_cb_t cb;		/* Table of callbacks (virtual methods) */
+	struct usbvideo_cb cb;		/* Table of callbacks (virtual methods) */
 	struct video_device vdt;	/* Video device template */
-	uvd_t *cam;			/* Array of camera structures */
+	struct uvd *cam;			/* Array of camera structures */
 	int uses_procfs;		/* Non-zero if we create /proc entries */
 	struct proc_dir_entry *procfs_dEntry;	/* /proc/video/MYDRIVER */
 	struct module *md_module;	/* Minidriver module */
 };
-typedef struct s_usbvideo_t usbvideo_t;
+
 
 /*
- * This macro retrieves callback address from the uvd_t object.
+ * This macro retrieves callback address from the struct uvd object.
  * No validity checks are done here, so be sure to check the
  * callback beforehand with VALID_CALLBACK.
  */
@@ -302,29 +304,34 @@
 #define	VALID_CALLBACK(uvd,cbName) ((((uvd) != NULL) && \
 		((uvd)->handle != NULL)) ? GET_CALLBACK(uvd,cbName) : NULL)
 
-void RingQueue_Initialize(RingQueue_t *rq);
-void RingQueue_Allocate(RingQueue_t *rq, int rqLen);
-int  RingQueue_IsAllocated(const RingQueue_t *rq);
-void RingQueue_Free(RingQueue_t *rq);
-int  RingQueue_Dequeue(RingQueue_t *rq, unsigned char *dst, int len);
-int  RingQueue_Enqueue(RingQueue_t *rq, const unsigned char *cdata, int n);
-int  RingQueue_GetLength(const RingQueue_t *rq);
-void RingQueue_InterruptibleSleepOn(RingQueue_t *rq);
-void RingQueue_WakeUpInterruptible(RingQueue_t *rq);
+int  RingQueue_Dequeue(struct RingQueue *rq, unsigned char *dst, int len);
+int  RingQueue_Enqueue(struct RingQueue *rq, const unsigned char *cdata, int n);
+void RingQueue_WakeUpInterruptible(struct RingQueue *rq);
+void RingQueue_Flush(struct RingQueue *rq);
+
+static inline int RingQueue_GetLength(const struct RingQueue *rq)
+{
+	return (rq->wi - rq->ri + rq->length) & (rq->length-1);
+}
+
+static inline int RingQueue_GetFreeSpace(const struct RingQueue *rq)
+{
+	return rq->length - RingQueue_GetLength(rq);
+}
 
-void usbvideo_CollectRawData(uvd_t *uvd, usbvideo_frame_t *frame);
+void usbvideo_CollectRawData(struct uvd *uvd, struct usbvideo_frame *frame);
 void usbvideo_DrawLine(
-	usbvideo_frame_t *frame,
+	struct usbvideo_frame *frame,
 	int x1, int y1,
 	int x2, int y2,
 	unsigned char cr, unsigned char cg, unsigned char cb);
 void usbvideo_HexDump(const unsigned char *data, int len);
-void usbvideo_OverlayChar(uvd_t *uvd, usbvideo_frame_t *frame, int x, int y, int ch);
-void usbvideo_OverlayString(uvd_t *uvd, usbvideo_frame_t *frame, int x, int y, const char *str);
-void usbvideo_OverlayStats(uvd_t *uvd, usbvideo_frame_t *frame);
-void usbvideo_ReportStatistics(const uvd_t *uvd);
+void usbvideo_OverlayChar(struct uvd *uvd, struct usbvideo_frame *frame, int x, int y, int ch);
+void usbvideo_OverlayString(struct uvd *uvd, struct usbvideo_frame *frame, int x, int y, const char *str);
+void usbvideo_OverlayStats(struct uvd *uvd, struct usbvideo_frame *frame);
+void usbvideo_ReportStatistics(const struct uvd *uvd);
 void usbvideo_SayAndWait(const char *what);
-void usbvideo_TestPattern(uvd_t *uvd, int fullframe, int pmode);
+void usbvideo_TestPattern(struct uvd *uvd, int fullframe, int pmode);
 void usbvideo_VideosizeToString(char *buf, int bufLen, videosize_t vs);
 
 /* Memory allocation routines */
@@ -334,17 +341,18 @@
 void usbvideo_rvfree(void *mem, unsigned long size);
 
 int usbvideo_register(
-	usbvideo_t **pCams,
+	struct usbvideo **pCams,
 	const int num_cams,
 	const int num_extra,
 	const char *driverName,
-	const usbvideo_cb_t *cbTable,
-	struct module *md);
-uvd_t *usbvideo_AllocateDevice(usbvideo_t *cams);
-int usbvideo_RegisterVideoDevice(uvd_t *uvd);
-void usbvideo_Deregister(usbvideo_t **uvt);
+	const struct usbvideo_cb *cbTable,
+	struct module *md,
+	const struct usb_device_id *id_table);
+struct uvd *usbvideo_AllocateDevice(struct usbvideo *cams);
+int usbvideo_RegisterVideoDevice(struct uvd *uvd);
+void usbvideo_Deregister(struct usbvideo **uvt);
 void usbvideo_Disconnect(struct usb_device *dev, void *ptr);
-void usbvideo_CameraRelease(uvd_t *uvd);
+void usbvideo_CameraRelease(struct uvd *uvd);
 
 void usbvideo_v4l_close(struct video_device *dev);
 int usbvideo_v4l_initialize(struct video_device *dev);
@@ -356,12 +364,12 @@
 long usbvideo_v4l_write(struct video_device *dev, const char *buf,
 			unsigned long count, int noblock);
 
-int usbvideo_GetFrame(uvd_t *uvd, int frameNum);
-int usbvideo_NewFrame(uvd_t *uvd, int framenum);
-int usbvideo_StartDataPump(uvd_t *uvd);
-void usbvideo_StopDataPump(uvd_t *uvd);
-void usbvideo_DeinterlaceFrame(uvd_t *uvd, usbvideo_frame_t *frame);
-void usbvideo_SoftwareContrastAdjustment(uvd_t *uvd, usbvideo_frame_t *frame);
+int usbvideo_GetFrame(struct uvd *uvd, int frameNum);
+int usbvideo_NewFrame(struct uvd *uvd, int framenum);
+int usbvideo_StartDataPump(struct uvd *uvd);
+void usbvideo_StopDataPump(struct uvd *uvd);
+void usbvideo_DeinterlaceFrame(struct uvd *uvd, struct usbvideo_frame *frame);
+void usbvideo_SoftwareContrastAdjustment(struct uvd *uvd, struct usbvideo_frame *frame);
 
 /*
  * This code performs bounds checking - use it when working with
@@ -377,7 +385,7 @@
  * VIDEOSIZE_X(fr->request), total VIDEOSIZE_Y(frame->request) lines.
  */
 static inline void RGB24_PUTPIXEL(
-	usbvideo_frame_t *fr,
+	struct usbvideo_frame *fr,
 	int ix, int iy,
 	unsigned char vr,
 	unsigned char vg,
diff -urN linux-2.4.21-pre2/drivers/video/matrox/Makefile linux-2.4.21-pre3/drivers/video/matrox/Makefile
--- linux-2.4.21-pre2/drivers/video/matrox/Makefile	Fri Aug  2 17:39:45 2002
+++ linux-2.4.21-pre3/drivers/video/matrox/Makefile	Mon Jan  6 16:32:32 2003
@@ -7,7 +7,7 @@
 # All of the (potential) objects that export symbols.
 # This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'.
 
-export-objs    :=  matroxfb_base.o matroxfb_accel.o matroxfb_DAC1064.o matroxfb_Ti3026.o matroxfb_misc.o g450_pll.o
+export-objs    :=  matroxfb_base.o matroxfb_accel.o matroxfb_DAC1064.o matroxfb_Ti3026.o matroxfb_misc.o g450_pll.o matroxfb_g450.o
 
 # Each configuration option enables a list of files.
 
@@ -15,8 +15,13 @@
 
 obj-$(CONFIG_FB_MATROX)           += matroxfb_base.o matroxfb_accel.o matroxfb_DAC1064.o matroxfb_Ti3026.o matroxfb_misc.o $(my-obj-y)
 obj-$(CONFIG_FB_MATROX_I2C)       += i2c-matroxfb.o
-obj-$(CONFIG_FB_MATROX_MAVEN)     += matroxfb_maven.o matroxfb_crtc2.o
-obj-$(CONFIG_FB_MATROX_G450)	  += matroxfb_g450.o matroxfb_crtc2.o
+ifeq ($(CONFIG_FB_MATROX_MAVEN),y)
+  obj-$(CONFIG_FB_MATROX)     += matroxfb_maven.o matroxfb_crtc2.o
+endif
+ifeq ($(CONFIG_FB_MATROX_G450),y)
+  obj-$(CONFIG_FB_MATROX)	  += matroxfb_g450.o matroxfb_crtc2.o
+endif
+obj-$(CONFIG_FB_MATROX_PROC)	  += matroxfb_proc.o
 
 include $(TOPDIR)/Rules.make
 
diff -urN linux-2.4.21-pre2/drivers/video/matrox/g450_pll.c linux-2.4.21-pre3/drivers/video/matrox/g450_pll.c
--- linux-2.4.21-pre2/drivers/video/matrox/g450_pll.c	Fri Aug  2 17:39:45 2002
+++ linux-2.4.21-pre3/drivers/video/matrox/g450_pll.c	Mon Jan  6 16:32:32 2003
@@ -2,11 +2,11 @@
  *
  * Hardware accelerated Matrox PCI cards - G450/G550 PLL control.
  *
- * (c) 2001 Petr Vandrovec <vandrove@vc.cvut.cz>
+ * (c) 2001-2002 Petr Vandrovec <vandrove@vc.cvut.cz>
  *
  * Portions Copyright (c) 2001 Matrox Graphics Inc.
  *
- * Version: 1.62 2001/11/29
+ * Version: 1.64 2002/06/10
  *
  * This file is subject to the terms and conditions of the GNU General Public
  * License. See the file COPYING in the main directory of this archive for
@@ -33,6 +33,10 @@
 	return (ACCESS_FBINFO(features).pll.ref_freq * n + (m >> 1)) / m;
 }
 
+unsigned int g450_mnp2f(CPMINFO unsigned int mnp) {
+	return g450_vco2f(mnp, g450_mnp2vco(PMINFO mnp));
+}
+
 static inline unsigned int pll_freq_delta(unsigned int f1, unsigned int f2) {
 	if (f2 < f1) {
     		f2 = f1 - f2;
@@ -52,40 +56,42 @@
 	m = (mnp >> 16) & 0xFF;
 	p = mnp & 0xFF;
 
-	if (m == 0 || m == 0xFF) {
-		if (m == 0) {
-			if (p & 0x40) {
-				return NO_MORE_MNP;
+	do {
+		if (m == 0 || m == 0xFF) {
+			if (m == 0) {
+				if (p & 0x40) {
+					return NO_MORE_MNP;
+				}
+			        if (p & 3) {
+					p--;
+				} else {
+					p = 0x40;
+				}
+				tvco >>= 1;
+				if (tvco < pi->vcomin) {
+					return NO_MORE_MNP;
+				}
+				*fvco = tvco;
 			}
-		        if (p & 3) {
-				p--;
+
+			p &= 0x43;
+			if (tvco < 550000) {
+/*				p |= 0x00; */
+			} else if (tvco < 700000) {
+				p |= 0x08;
+			} else if (tvco < 1000000) {
+				p |= 0x10;
+			} else if (tvco < 1150000) {
+				p |= 0x18;
 			} else {
-				p = 0x40;
+				p |= 0x20;
 			}
-			tvco >>= 1;
-			if (tvco < pi->vcomin) {
-				return NO_MORE_MNP;
-			}
-			*fvco = tvco;
-		}
-
-		p &= 0x43;
-		if (tvco < 550000) {
-/*			p |= 0x00; */
-		} else if (tvco < 700000) {
-			p |= 0x08;
-		} else if (tvco < 1000000) {
-			p |= 0x10;
-		} else if (tvco < 1150000) {
-			p |= 0x18;
+			m = 9;
 		} else {
-			p |= 0x20;
+			m--;
 		}
-		m = 9;
-	} else {
-		m--;
-	}
-        n = ((tvco * (m+1) + ACCESS_FBINFO(features).pll.ref_freq) / (ACCESS_FBINFO(features).pll.ref_freq * 2)) - 2;
+        	n = ((tvco * (m+1) + ACCESS_FBINFO(features).pll.ref_freq) / (ACCESS_FBINFO(features).pll.ref_freq * 2)) - 2;
+	} while (n < 0x03 || n > 0x7A);
 	return (m << 16) | (n << 8) | p;
 }
 
@@ -219,7 +225,7 @@
 	}
 }
 
-static inline void g450_setpll_cond(WPMINFO unsigned int mnp, unsigned int pll) {
+void matroxfb_g450_setpll_cond(WPMINFO unsigned int mnp, unsigned int pll) {
 	if (g450_cmppll(PMINFO mnp, pll)) {
 		g450_setpll(PMINFO mnp, pll);
 	}
@@ -385,10 +391,8 @@
 			unsigned int vco;
 			unsigned int delta;
 
-			if ((mnp & 0xFF00) < 0x0300 || (mnp & 0xFF00) > 0x7A00) {
-				continue;
-			}
 			vco = g450_mnp2vco(PMINFO mnp);
+#if 0			
 			if (pll == M_VIDEO_PLL) {
 				unsigned int big, small;
 
@@ -406,6 +410,7 @@
 					continue;
 				}
 			}
+#endif			
 			delta = pll_freq_delta(fout, g450_vco2f(mnp, vco));
 			for (idx = mnpcount; idx > 0; idx--) {
 				/* == is important; due to nextpll algorithm we get
@@ -426,7 +431,7 @@
 	}
 	/* VideoPLL and PixelPLL matched: do nothing... In all other cases we should get at least one frequency */
 	if (!mnpcount) {
-		return 1;
+		return -EBUSY;
 	}
 	{
 		unsigned long flags;
@@ -435,15 +440,15 @@
 		matroxfb_DAC_lock_irqsave(flags);
 		mnp = g450_checkcache(PMINFO ci, mnparray[0]);
 		if (mnp != NO_MORE_MNP) {
-			g450_setpll_cond(PMINFO mnp, pll);
+			matroxfb_g450_setpll_cond(PMINFO mnp, pll);
 		} else {
 			mnp = g450_findworkingpll(PMINFO pll, mnparray, mnpcount);
 			g450_addcache(ci, mnparray[0], mnp);
 		}
 		updatehwstate_clk(&ACCESS_FBINFO(hw), mnp, pll);
 		matroxfb_DAC_unlock_irqrestore(flags);
+		return mnp;
 	}
-	return 0;
 }
 
 /* It must be greater than number of possible PLL values.
@@ -465,8 +470,10 @@
 }
 
 EXPORT_SYMBOL(matroxfb_g450_setclk);
+EXPORT_SYMBOL(g450_mnp2f);
+EXPORT_SYMBOL(matroxfb_g450_setpll_cond);
 
-MODULE_AUTHOR("(c) 2001 Petr Vandrovec <vandrove@vc.cvut.cz>");
+MODULE_AUTHOR("(c) 2001-2002 Petr Vandrovec <vandrove@vc.cvut.cz>");
 MODULE_DESCRIPTION("Matrox G450/G550 PLL driver");
 
 MODULE_LICENSE("GPL");
diff -urN linux-2.4.21-pre2/drivers/video/matrox/g450_pll.h linux-2.4.21-pre3/drivers/video/matrox/g450_pll.h
--- linux-2.4.21-pre2/drivers/video/matrox/g450_pll.h	Fri Aug  2 17:39:45 2002
+++ linux-2.4.21-pre3/drivers/video/matrox/g450_pll.h	Mon Jan  6 16:32:32 2003
@@ -4,5 +4,7 @@
 #include "matroxfb_base.h"
 
 int matroxfb_g450_setclk(WPMINFO unsigned int fout, unsigned int pll);
+unsigned int g450_mnp2f(CPMINFO unsigned int mnp);
+void matroxfb_g450_setpll_cond(WPMINFO unsigned int mnp, unsigned int pll);
 
 #endif	/* __G450_PLL_H__ */
diff -urN linux-2.4.21-pre2/drivers/video/matrox/i2c-matroxfb.c linux-2.4.21-pre3/drivers/video/matrox/i2c-matroxfb.c
--- linux-2.4.21-pre2/drivers/video/matrox/i2c-matroxfb.c	Fri Nov  9 14:07:41 2001
+++ linux-2.4.21-pre3/drivers/video/matrox/i2c-matroxfb.c	Mon Jan  6 16:32:32 2003
@@ -2,9 +2,9 @@
  *
  * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200, G400 and G450.
  *
- * (c) 1998-2001 Petr Vandrovec <vandrove@vc.cvut.cz>
+ * (c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz>
  *
- * Version: 1.51 2001/01/19
+ * Version: 1.64 2002/06/10
  *
  * See matroxfb_base.c for contributors.
  *
@@ -30,6 +30,12 @@
 
 /******************************************************/
 
+struct matroxfb_dh_maven_info {
+	struct i2c_bit_adapter	maven;
+	struct i2c_bit_adapter	ddc1;
+	struct i2c_bit_adapter	ddc2;
+};
+
 static int matroxfb_read_gpio(struct matrox_fb_info* minfo) {
 	unsigned long flags;
 	int v;
@@ -40,7 +46,7 @@
 	return v;
 }
 
-static inline void matroxfb_set_gpio(struct matrox_fb_info* minfo, int mask, int val) {
+static void matroxfb_set_gpio(struct matrox_fb_info* minfo, int mask, int val) {
 	unsigned long flags;
 	int v;
 
@@ -53,7 +59,7 @@
 }
 
 /* software I2C functions */
-static void matroxfb_i2c_set(struct matrox_fb_info* minfo, int mask, int state) {
+static inline void matroxfb_i2c_set(struct matrox_fb_info* minfo, int mask, int state) {
 	if (state)
 		state = 0;
 	else
@@ -61,68 +67,24 @@
 	matroxfb_set_gpio(minfo, ~mask, state);
 }
 
-static void matroxfb_maven_setsda(void* data, int state) {
-	matroxfb_i2c_set(data, MAT_DATA, state);
-}
-
-static void matroxfb_maven_setscl(void* data, int state) {
-	matroxfb_i2c_set(data, MAT_CLK, state);
-}
-
-static int matroxfb_maven_getsda(void* data) {
-	return (matroxfb_read_gpio(data) & MAT_DATA) ? 1 : 0;
+static void matroxfb_gpio_setsda(void* data, int state) {
+	struct i2c_bit_adapter* b = data;
+	matroxfb_i2c_set(b->minfo, b->mask.data, state);
 }
 
-static int matroxfb_maven_getscl(void* data) {
-	return (matroxfb_read_gpio(data) & MAT_CLK) ? 1 : 0;
+static void matroxfb_gpio_setscl(void* data, int state) {
+	struct i2c_bit_adapter* b = data;
+	matroxfb_i2c_set(b->minfo, b->mask.clock, state);
 }
 
-static void matroxfb_ddc1_setsda(void* data, int state) {
-	matroxfb_i2c_set(data, DDC1_DATA, state);
+static int matroxfb_gpio_getsda(void* data) {
+	struct i2c_bit_adapter* b = data;
+	return (matroxfb_read_gpio(b->minfo) & b->mask.data) ? 1 : 0;
 }
 
-static void matroxfb_ddc1_setscl(void* data, int state) {
-	matroxfb_i2c_set(data, DDC1_CLK, state);
-}
-
-static int matroxfb_ddc1_getsda(void* data) {
-	return (matroxfb_read_gpio(data) & DDC1_DATA) ? 1 : 0;
-}
-
-static int matroxfb_ddc1_getscl(void* data) {
-	return (matroxfb_read_gpio(data) & DDC1_CLK) ? 1 : 0;
-}
-
-static void matroxfb_ddc1b_setsda(void* data, int state) {
-	matroxfb_i2c_set(data, DDC1B_DATA, state);
-}
-
-static void matroxfb_ddc1b_setscl(void* data, int state) {
-	matroxfb_i2c_set(data, DDC1B_CLK, state);
-}
-
-static int matroxfb_ddc1b_getsda(void* data) {
-	return (matroxfb_read_gpio(data) & DDC1B_DATA) ? 1 : 0;
-}
-
-static int matroxfb_ddc1b_getscl(void* data) {
-	return (matroxfb_read_gpio(data) & DDC1B_CLK) ? 1 : 0;
-}
-
-static void matroxfb_ddc2_setsda(void* data, int state) {
-	matroxfb_i2c_set(data, DDC2_DATA, state);
-}
-
-static void matroxfb_ddc2_setscl(void* data, int state) {
-	matroxfb_i2c_set(data, DDC2_CLK, state);
-}
-
-static int matroxfb_ddc2_getsda(void* data) {
-	return (matroxfb_read_gpio(data) & DDC2_DATA) ? 1 : 0;
-}
-
-static int matroxfb_ddc2_getscl(void* data) {
-	return (matroxfb_read_gpio(data) & DDC2_CLK) ? 1 : 0;
+static int matroxfb_gpio_getscl(void* data) {
+	struct i2c_bit_adapter* b = data;
+	return (matroxfb_read_gpio(b->minfo) & b->mask.clock) ? 1 : 0;
 }
 
 static void matroxfb_dh_inc_use(struct i2c_adapter* dummy) {
@@ -133,97 +95,36 @@
 	MOD_DEC_USE_COUNT;
 }
 
-static struct i2c_adapter matroxmaven_i2c_adapter_template =
+static struct i2c_adapter matrox_i2c_adapter_template =
 {
-	"",
-	I2C_HW_B_G400,
-
-	NULL,
-	NULL,
-
-	matroxfb_dh_inc_use,
-	matroxfb_dh_dec_use,
-	NULL,
-	NULL,
-	NULL,
+	.id =		I2C_HW_B_G400,
+	.inc_use =	matroxfb_dh_inc_use,
+	.dec_use =	matroxfb_dh_dec_use,
 };
 
-static struct i2c_algo_bit_data matroxmaven_i2c_algo_template =
+static struct i2c_algo_bit_data matrox_i2c_algo_template =
 {
 	NULL,
-	matroxfb_maven_setsda,
-	matroxfb_maven_setscl,
-	matroxfb_maven_getsda,
-	matroxfb_maven_getscl,
+	matroxfb_gpio_setsda,
+	matroxfb_gpio_setscl,
+	matroxfb_gpio_getsda,
+	matroxfb_gpio_getscl,
 	10, 10, 100,
 };
 
-static struct i2c_adapter matrox_ddc1_adapter_template =
-{
-	"",
-	I2C_HW_B_G400, /* DDC */
-
-	NULL,
-	NULL,
-
-	matroxfb_dh_inc_use,
-	matroxfb_dh_dec_use,
-	NULL,
-	NULL,
-	NULL,
-};
-
-static struct i2c_algo_bit_data matrox_ddc1_algo_template =
-{
-	NULL,
-	matroxfb_ddc1_setsda,
-	matroxfb_ddc1_setscl,
-	matroxfb_ddc1_getsda,
-	matroxfb_ddc1_getscl,
-	10, 10, 100,
-};
-
-static struct i2c_algo_bit_data matrox_ddc1b_algo_template =
-{
-	NULL,
-	matroxfb_ddc1b_setsda,
-	matroxfb_ddc1b_setscl,
-	matroxfb_ddc1b_getsda,
-	matroxfb_ddc1b_getscl,
-	10, 10, 100,
-};
-
-static struct i2c_adapter matrox_ddc2_adapter_template =
-{
-	"",
-	I2C_HW_B_G400,	/* DDC */
-
-	NULL,
-	NULL,
-
-	matroxfb_dh_inc_use, /* should increment matroxfb_maven usage too, this DDC is coupled with maven_client */
-	matroxfb_dh_dec_use, /* should decrement matroxfb_maven usage too */
-	NULL,
-	NULL,
-	NULL,
-};
-
-static struct i2c_algo_bit_data matrox_ddc2_algo_template =
-{
-	NULL,
-	matroxfb_ddc2_setsda,
-	matroxfb_ddc2_setscl,
-	matroxfb_ddc2_getsda,
-	matroxfb_ddc2_getscl,
-	10, 10, 100,
-};
-
-static int i2c_bus_reg(struct i2c_bit_adapter* b, struct matrox_fb_info* minfo) {
+static int i2c_bus_reg(struct i2c_bit_adapter* b, struct matrox_fb_info* minfo, 
+		unsigned int data, unsigned int clock, const char* name) {
 	int err;
 
-	b->adapter.data = minfo;
+	b->minfo = minfo;
+	b->mask.data = data;
+	b->mask.clock = clock;
+	b->adapter = matrox_i2c_adapter_template;
+	sprintf(b->adapter.name, name, GET_FB_IDX(minfo->fbcon.node));
+	b->adapter.data = b;
 	b->adapter.algo_data = &b->bac;
-	b->bac.data = minfo;
+	b->bac = matrox_i2c_algo_template;
+	b->bac.data = b;
 	err = i2c_bit_add_bus(&b->adapter);
 	b->initialized = !err;
 	return err;
@@ -236,50 +137,14 @@
 	}
 }
 
-static inline int i2c_maven_init(struct matroxfb_dh_maven_info* minfo2) {
-	struct i2c_bit_adapter *b = &minfo2->maven;
-
-	b->adapter = matroxmaven_i2c_adapter_template;
-	b->bac = matroxmaven_i2c_algo_template;
-	sprintf(b->adapter.name, "MAVEN:fb%u on i2c-matroxfb", GET_FB_IDX(minfo2->primary_dev->fbcon.node));
-	return i2c_bus_reg(b, minfo2->primary_dev);
-}
-
 static inline void i2c_maven_done(struct matroxfb_dh_maven_info* minfo2) {
 	i2c_bit_bus_del(&minfo2->maven);
 }
 
-static inline int i2c_ddc1_init(struct matroxfb_dh_maven_info* minfo2) {
-	struct i2c_bit_adapter *b = &minfo2->ddc1;
-
-	b->adapter = matrox_ddc1_adapter_template;
-	b->bac = matrox_ddc1_algo_template;
-	sprintf(b->adapter.name, "DDC:fb%u #0 on i2c-matroxfb", GET_FB_IDX(minfo2->primary_dev->fbcon.node));
-	return i2c_bus_reg(b, minfo2->primary_dev);
-}
-
-static inline int i2c_ddc1b_init(struct matroxfb_dh_maven_info* minfo2) {
-	struct i2c_bit_adapter *b = &minfo2->ddc1;
-
-	b->adapter = matrox_ddc1_adapter_template;
-	b->bac = matrox_ddc1b_algo_template;
-	sprintf(b->adapter.name, "DDC:fb%u #0 on i2c-matroxfb", GET_FB_IDX(minfo2->primary_dev->fbcon.node));
-	return i2c_bus_reg(b, minfo2->primary_dev);
-}
-
 static inline void i2c_ddc1_done(struct matroxfb_dh_maven_info* minfo2) {
 	i2c_bit_bus_del(&minfo2->ddc1);
 }
 
-static inline int i2c_ddc2_init(struct matroxfb_dh_maven_info* minfo2) {
-	struct i2c_bit_adapter *b = &minfo2->ddc2;
-
-	b->adapter = matrox_ddc2_adapter_template;
-	b->bac = matrox_ddc2_algo_template;
-	sprintf(b->adapter.name, "DDC:fb%u #1 on i2c-matroxfb", GET_FB_IDX(minfo2->primary_dev->fbcon.node));
-	return i2c_bus_reg(b, minfo2->primary_dev);
-}
-
 static inline void i2c_ddc2_done(struct matroxfb_dh_maven_info* minfo2) {
 	i2c_bit_bus_del(&minfo2->ddc2);
 }
@@ -299,24 +164,26 @@
 	matroxfb_DAC_unlock_irqrestore(flags);
 
 	memset(m2info, 0, sizeof(*m2info));
-	m2info->maven.minfo = m2info;
-	m2info->ddc1.minfo = m2info;
-	m2info->ddc2.minfo = m2info;
-	m2info->primary_dev = minfo;
-
-	if (ACCESS_FBINFO(devflags.accelerator) == FB_ACCEL_MATROX_MGA2064W ||
-	    ACCESS_FBINFO(devflags.accelerator) == FB_ACCEL_MATROX_MGA2164W ||
-	    ACCESS_FBINFO(devflags.accelerator) == FB_ACCEL_MATROX_MGA2164W_AGP)
-		err = i2c_ddc1b_init(m2info);
-	else
-		err = i2c_ddc1_init(m2info);
+
+	switch (ACCESS_FBINFO(chip)) {
+		case MGA_2064:
+		case MGA_2164:
+			err = i2c_bus_reg(&m2info->ddc1, minfo, DDC1B_DATA, DDC1B_CLK, "DDC:fb%u #0 on i2c-matroxfb");
+			break;
+		default:
+			err = i2c_bus_reg(&m2info->ddc1, minfo, DDC1_DATA, DDC1_CLK, "DDC:fb%u #0 on i2c-matroxfb");
+			break;
+	}
 	if (err)
 		goto fail_ddc1;
-	if (ACCESS_FBINFO(devflags.maven_capable)) {
-		err = i2c_ddc2_init(m2info);
-		if (err)
+	if (ACCESS_FBINFO(devflags.dualhead)) {
+		err = i2c_bus_reg(&m2info->ddc2, minfo, DDC2_DATA, DDC2_CLK, "DDC:fb%u #1 on i2c-matroxfb");
+		if (err == -ENODEV) {
+			printk(KERN_INFO "i2c-matroxfb: VGA->TV plug detected, DDC unavailable.\n");
+		} else if (err)
 			printk(KERN_INFO "i2c-matroxfb: Could not register secondary output i2c bus. Continuing anyway.\n");
-		err = i2c_maven_init(m2info);
+		/* Register maven bus even on G450/G550 */
+		err = i2c_bus_reg(&m2info->maven, minfo, MAT_DATA, MAT_CLK, "MAVEN:fb%u on i2c-matroxfb");
 		if (err)
 			printk(KERN_INFO "i2c-matroxfb: Could not register Maven i2c bus. Continuing anyway.\n");
 	}
@@ -337,10 +204,10 @@
 }
 
 static struct matroxfb_driver i2c_matroxfb = {
-	LIST_HEAD_INIT(i2c_matroxfb.node),
-	"i2c-matroxfb",
-	i2c_matroxfb_probe,
-	i2c_matroxfb_remove,
+	.node =		LIST_HEAD_INIT(i2c_matroxfb.node),
+	.name =		"i2c-matroxfb",
+	.probe = 	i2c_matroxfb_probe,
+	.remove =	i2c_matroxfb_remove,
 };
 
 static int __init i2c_matroxfb_init(void) {
@@ -355,7 +222,7 @@
 	matroxfb_unregister_driver(&i2c_matroxfb);
 }
 
-MODULE_AUTHOR("(c) 1999-2001 Petr Vandrovec <vandrove@vc.cvut.cz>");
+MODULE_AUTHOR("(c) 1999-2002 Petr Vandrovec <vandrove@vc.cvut.cz>");
 MODULE_DESCRIPTION("Support module providing I2C buses present on Matrox videocards");
 
 module_init(i2c_matroxfb_init);
diff -urN linux-2.4.21-pre2/drivers/video/matrox/matroxfb_DAC1064.c linux-2.4.21-pre3/drivers/video/matrox/matroxfb_DAC1064.c
--- linux-2.4.21-pre2/drivers/video/matrox/matroxfb_DAC1064.c	Fri Aug  2 17:39:45 2002
+++ linux-2.4.21-pre3/drivers/video/matrox/matroxfb_DAC1064.c	Mon Jan  6 16:32:32 2003
@@ -2,11 +2,11 @@
  *
  * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200, G400 and G450.
  *
- * (c) 1998-2001 Petr Vandrovec <vandrove@vc.cvut.cz>
+ * (c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz>
  *
  * Portions Copyright (c) 2001 Matrox Graphics Inc.
  *
- * Version: 1.62 2001/11/29
+ * Version: 1.64 2002/06/10
  *
  * See matroxfb_base.c for contributors.
  *
@@ -161,37 +161,20 @@
 	unsigned int p;
 
 	DBG("DAC1064_calcclock")
+	
+	/* only for devices older than G450 */
 
 	fvco = PLL_calcclock(PMINFO freq, fmax, in, feed, &p);
 	
-	if (ACCESS_FBINFO(devflags.g450dac)) {
-		if (fvco <= 300000)		/* 276-324 */
-			;
-		else if (fvco <= 400000)	/* 378-438 */
-			p |= 0x08;
-		else if (fvco <= 550000)	/* 540-567 */
-			p |= 0x10;
-		else if (fvco <= 690000)	/* 675-695 */
-			p |= 0x18;
-		else if (fvco <= 800000)	/* 776-803 */
-			p |= 0x20;
-		else if (fvco <= 891000)	/* 891-891 */
-			p |= 0x28;
-		else if (fvco <= 940000)	/* 931-945 */
-			p |= 0x30;
-		else				/* <959 */
-			p |= 0x38;
-	} else {
-		p = (1 << p) - 1;
-		if (fvco <= 100000)
-			;
-		else if (fvco <= 140000)
-			p |= 0x08;
-		else if (fvco <= 180000)
-			p |= 0x10;
-		else
-			p |= 0x18;
-	}
+	p = (1 << p) - 1;
+	if (fvco <= 100000)
+		;
+	else if (fvco <= 140000)
+		p |= 0x08;
+	else if (fvco <= 180000)
+		p |= 0x10;
+	else
+		p |= 0x18;
 	*post = p;
 }
 
@@ -293,31 +276,164 @@
 	hw->MXoptionReg = mx;
 }
 
+static void g450_set_plls(WPMINFO2) {
+	u_int32_t c2_ctl;
+	unsigned int pxc;
+	struct matrox_hw_state* hw = &ACCESS_FBINFO(hw);
+	int pixelmnp;
+	int videomnp;
+	
+	c2_ctl = hw->crtc2.ctl & ~0x4007;	/* Clear PLL + enable for CRTC2 */
+	c2_ctl |= 0x0001;			/* Enable CRTC2 */
+	hw->DACreg[POS1064_XPWRCTRL] &= ~0x02;	/* Stop VIDEO PLL */
+	pixelmnp = ACCESS_FBINFO(crtc1).mnp;
+	videomnp = ACCESS_FBINFO(crtc2).mnp;
+	if (videomnp < 0) {
+		c2_ctl &= ~0x0001;			/* Disable CRTC2 */
+		hw->DACreg[POS1064_XPWRCTRL] &= ~0x10;	/* Powerdown CRTC2 */
+	} else if (ACCESS_FBINFO(crtc2).pixclock == ACCESS_FBINFO(features).pll.ref_freq) {
+		c2_ctl |=  0x4002;	/* Use reference directly */
+	} else if (videomnp == pixelmnp) {
+		c2_ctl |=  0x0004;	/* Use pixel PLL */
+	} else {
+		if (0 == ((videomnp ^ pixelmnp) & 0xFFFFFF00)) {
+			/* PIXEL and VIDEO PLL must not use same frequency. We modify N
+			   of PIXEL PLL in such case because of VIDEO PLL may be source
+			   of TVO clocks, and chroma subcarrier is derived from its
+			   pixel clocks */
+			pixelmnp += 0x000100;
+		}
+		c2_ctl |=  0x0006;	/* Use video PLL */
+		hw->DACreg[POS1064_XPWRCTRL] |= 0x02;
+		
+		outDAC1064(PMINFO M1064_XPWRCTRL, hw->DACreg[POS1064_XPWRCTRL]);
+		matroxfb_g450_setpll_cond(PMINFO videomnp, M_VIDEO_PLL);
+	}
+
+	hw->DACreg[POS1064_XPIXCLKCTRL] &= ~M1064_XPIXCLKCTRL_PLL_UP;
+	if (pixelmnp >= 0) {
+		hw->DACreg[POS1064_XPIXCLKCTRL] |= M1064_XPIXCLKCTRL_PLL_UP;
+		
+		outDAC1064(PMINFO M1064_XPIXCLKCTRL, hw->DACreg[POS1064_XPIXCLKCTRL]);
+		matroxfb_g450_setpll_cond(PMINFO pixelmnp, M_PIXEL_PLL_C);
+	}
+	if (c2_ctl != hw->crtc2.ctl) {
+		hw->crtc2.ctl = c2_ctl;
+		mga_outl(0x3C10, c2_ctl);
+	}
+
+	pxc = ACCESS_FBINFO(crtc1).pixclock;
+	if (pxc == 0 || ACCESS_FBINFO(outputs[2]).src == MATROXFB_SRC_CRTC2) {
+		pxc = ACCESS_FBINFO(crtc2).pixclock;
+	}
+	if (ACCESS_FBINFO(chip) == MGA_G550) {
+		if (pxc < 45000) {
+			hw->DACreg[POS1064_XPANMODE] = 0x00;	/* 0-50 */
+		} else if (pxc < 55000) {
+			hw->DACreg[POS1064_XPANMODE] = 0x08;	/* 34-62 */
+		} else if (pxc < 70000) {
+			hw->DACreg[POS1064_XPANMODE] = 0x10;	/* 42-78 */
+		} else if (pxc < 85000) {
+			hw->DACreg[POS1064_XPANMODE] = 0x18;	/* 62-92 */
+		} else if (pxc < 100000) {
+			hw->DACreg[POS1064_XPANMODE] = 0x20;	/* 74-108 */
+		} else if (pxc < 115000) {
+			hw->DACreg[POS1064_XPANMODE] = 0x28;	/* 94-122 */
+		} else if (pxc < 125000) {
+			hw->DACreg[POS1064_XPANMODE] = 0x30;	/* 108-132 */
+		} else {
+			hw->DACreg[POS1064_XPANMODE] = 0x38;	/* 120-168 */
+		}
+	} else {
+		/* G450 */
+		if (pxc < 45000) {
+			hw->DACreg[POS1064_XPANMODE] = 0x00;	/* 0-54 */
+		} else if (pxc < 65000) {
+			hw->DACreg[POS1064_XPANMODE] = 0x08;	/* 38-70 */
+		} else if (pxc < 85000) {
+			hw->DACreg[POS1064_XPANMODE] = 0x10;	/* 56-96 */
+		} else if (pxc < 105000) {
+			hw->DACreg[POS1064_XPANMODE] = 0x18;	/* 80-114 */
+		} else if (pxc < 135000) {
+			hw->DACreg[POS1064_XPANMODE] = 0x20;	/* 102-144 */
+		} else if (pxc < 160000) {
+			hw->DACreg[POS1064_XPANMODE] = 0x28;	/* 132-166 */
+		} else if (pxc < 175000) {
+			hw->DACreg[POS1064_XPANMODE] = 0x30;	/* 154-182 */
+		} else {
+			hw->DACreg[POS1064_XPANMODE] = 0x38;	/* 170-204 */
+		}
+	}
+}
+
 void DAC1064_global_init(WPMINFO2) {
 	struct matrox_hw_state* hw = &ACCESS_FBINFO(hw);
 
 	hw->DACreg[POS1064_XMISCCTRL] &= M1064_XMISCCTRL_DAC_WIDTHMASK;
 	hw->DACreg[POS1064_XMISCCTRL] |= M1064_XMISCCTRL_LUT_EN;
 	hw->DACreg[POS1064_XPIXCLKCTRL] = M1064_XPIXCLKCTRL_PLL_UP | M1064_XPIXCLKCTRL_EN | M1064_XPIXCLKCTRL_SRC_PLL;
-	hw->DACreg[POS1064_XOUTPUTCONN] = 0x01;	/* output #1 enabled */
-	if (ACCESS_FBINFO(output.ph) & MATROXFB_OUTPUT_CONN_SECONDARY) {
-		if (ACCESS_FBINFO(devflags.g450dac)) {
-			hw->DACreg[POS1064_XPIXCLKCTRL] = M1064_XPIXCLKCTRL_PLL_UP | M1064_XPIXCLKCTRL_EN | M1064_XPIXCLKCTRL_SRC_PLL2;
-			hw->DACreg[POS1064_XOUTPUTCONN] = 0x05;	/* output #1 enabled; CRTC1 connected to output #2 */
-		} else {
+	if (ACCESS_FBINFO(devflags.g450dac)) {
+		hw->DACreg[POS1064_XPWRCTRL] = 0x1F;	/* powerup everything */
+		hw->DACreg[POS1064_XOUTPUTCONN] = 0x00;	/* disable outputs */
+		hw->DACreg[POS1064_XMISCCTRL] |= M1064_XMISCCTRL_DAC_EN;
+		switch (ACCESS_FBINFO(outputs[0]).src) {
+			case MATROXFB_SRC_CRTC1:
+			case MATROXFB_SRC_CRTC2:
+				hw->DACreg[POS1064_XOUTPUTCONN] |= 0x01;	/* enable output; CRTC1/2 selection is in CRTC2 ctl */
+				break;
+			case MATROXFB_SRC_NONE:
+				hw->DACreg[POS1064_XMISCCTRL] &= ~M1064_XMISCCTRL_DAC_EN;
+				break;
+		}
+		switch (ACCESS_FBINFO(outputs[1]).src) {
+			case MATROXFB_SRC_CRTC1:
+				hw->DACreg[POS1064_XOUTPUTCONN] |= 0x04;
+				break;
+			case MATROXFB_SRC_CRTC2:
+				if (ACCESS_FBINFO(outputs[1]).mode == MATROXFB_OUTPUT_MODE_MONITOR) {
+					hw->DACreg[POS1064_XOUTPUTCONN] |= 0x08;
+				} else {
+					hw->DACreg[POS1064_XOUTPUTCONN] |= 0x0C;
+				}
+				break;
+			case MATROXFB_SRC_NONE:
+				hw->DACreg[POS1064_XPWRCTRL] &= ~0x01;		/* Poweroff DAC2 */
+				break;
+		}
+		switch (ACCESS_FBINFO(outputs[2]).src) {
+			case MATROXFB_SRC_CRTC1:
+				hw->DACreg[POS1064_XOUTPUTCONN] |= 0x20;
+				break;
+			case MATROXFB_SRC_CRTC2:
+				hw->DACreg[POS1064_XOUTPUTCONN] |= 0x40;
+				break;
+			case MATROXFB_SRC_NONE:
+#if 0
+				/* HELP! If we boot without DFP connected to DVI, we can
+				   poweroff TMDS. But if we boot with DFP connected,
+				   TMDS generated clocks are used instead of ALL pixclocks
+				   available... If someone knows which register
+				   handles it, please reveal this secret to me... */			
+				hw->DACreg[POS1064_XPWRCTRL] &= ~0x04;		/* Poweroff TMDS */
+#endif				
+				break;
+		}
+		/* Now set timming related variables... */
+		g450_set_plls(PMINFO2);
+	} else {
+		if (ACCESS_FBINFO(outputs[1]).src == MATROXFB_SRC_CRTC1) {
 			hw->DACreg[POS1064_XPIXCLKCTRL] = M1064_XPIXCLKCTRL_PLL_UP | M1064_XPIXCLKCTRL_EN | M1064_XPIXCLKCTRL_SRC_EXT;
 			hw->DACreg[POS1064_XMISCCTRL] |= GX00_XMISCCTRL_MFC_MAFC | G400_XMISCCTRL_VDO_MAFC12;
-		}
-	} else if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_SECONDARY) {
-		hw->DACreg[POS1064_XMISCCTRL] |= GX00_XMISCCTRL_MFC_MAFC | G400_XMISCCTRL_VDO_C2_MAFC12;
-		hw->DACreg[POS1064_XOUTPUTCONN] = 0x09; /* output #1 enabled; CRTC2 connected to output #2 */
-	} else if (ACCESS_FBINFO(output.ph) & MATROXFB_OUTPUT_CONN_DFP)
-		hw->DACreg[POS1064_XMISCCTRL] |= GX00_XMISCCTRL_MFC_PANELLINK | G400_XMISCCTRL_VDO_MAFC12;
-	else
-		hw->DACreg[POS1064_XMISCCTRL] |= GX00_XMISCCTRL_MFC_DIS;
+		} else if (ACCESS_FBINFO(outputs[1]).src == MATROXFB_SRC_CRTC2) {
+			hw->DACreg[POS1064_XMISCCTRL] |= GX00_XMISCCTRL_MFC_MAFC | G400_XMISCCTRL_VDO_C2_MAFC12;
+		} else if (ACCESS_FBINFO(outputs[2]).src == MATROXFB_SRC_CRTC1)
+			hw->DACreg[POS1064_XMISCCTRL] |= GX00_XMISCCTRL_MFC_PANELLINK | G400_XMISCCTRL_VDO_MAFC12;
+		else
+			hw->DACreg[POS1064_XMISCCTRL] |= GX00_XMISCCTRL_MFC_DIS;
 
-	if ((ACCESS_FBINFO(output.ph) | ACCESS_FBINFO(output.sh)) & MATROXFB_OUTPUT_CONN_PRIMARY)
-		hw->DACreg[POS1064_XMISCCTRL] |= M1064_XMISCCTRL_DAC_EN;
+		if (ACCESS_FBINFO(outputs[0]).src != MATROXFB_SRC_NONE)
+			hw->DACreg[POS1064_XMISCCTRL] |= M1064_XMISCCTRL_DAC_EN;
+	}
 }
 
 void DAC1064_global_restore(WPMINFO2) {
@@ -329,8 +445,9 @@
 		outDAC1064(PMINFO 0x20, 0x04);
 		outDAC1064(PMINFO 0x1F, ACCESS_FBINFO(devflags.dfp_type));
 		if (ACCESS_FBINFO(devflags.g450dac)) {
-			outDAC1064(PMINFO M1064_XSYNCCTRL, 0xCC);	/* only matrox know... */
-			outDAC1064(PMINFO M1064_XPWRCTRL, 0x1F);	/* powerup everything */
+			outDAC1064(PMINFO M1064_XSYNCCTRL, 0xCC);
+			outDAC1064(PMINFO M1064_XPWRCTRL, hw->DACreg[POS1064_XPWRCTRL]);
+			outDAC1064(PMINFO M1064_XPANMODE, hw->DACreg[POS1064_XPANMODE]);
 			outDAC1064(PMINFO M1064_XOUTPUTCONN, hw->DACreg[POS1064_XOUTPUTCONN]);
 		}
 	}
@@ -368,13 +485,13 @@
 			return 1;	/* unsupported depth */
 		}
 	}
-
-	DAC1064_global_init(PMINFO2);
 	hw->DACreg[POS1064_XVREFCTRL] = ACCESS_FBINFO(features.DAC1064.xvrefctrl);
 	hw->DACreg[POS1064_XGENCTRL] &= ~M1064_XGENCTRL_SYNC_ON_GREEN_MASK;
 	hw->DACreg[POS1064_XGENCTRL] |= (m->sync & FB_SYNC_ON_GREEN)?M1064_XGENCTRL_SYNC_ON_GREEN:M1064_XGENCTRL_NO_SYNC_ON_GREEN;
 	hw->DACreg[POS1064_XCURADDL] = ACCESS_FBINFO(features.DAC1064.cursorimage) >> 10;
 	hw->DACreg[POS1064_XCURADDH] = ACCESS_FBINFO(features.DAC1064.cursorimage) >> 18;
+
+	DAC1064_global_init(PMINFO2);
 	return 0;
 }
 
@@ -471,13 +588,8 @@
 #endif
 }
 
-static int m1064_compute(void* outdev, struct my_timming* m) {
-#define minfo ((struct matrox_fb_info*)outdev)
-#ifdef CONFIG_FB_MATROX_G450
-	if (ACCESS_FBINFO(devflags.g450dac)) {
-		matroxfb_g450_setclk(PMINFO m->pixclock, M_PIXEL_PLL_C);
-	} else 
-#endif
+static int m1064_compute(void* out, struct my_timming* m) {
+#define minfo ((struct matrox_fb_info*)out)
 	{
 		int i;
 		int tmout;
@@ -504,37 +616,28 @@
 	return 0;
 }
 
-static int m1064_program(void* outdev) {
-	/* nothing, hardware is set in m1064_compute */
-	return 0;
-}
-
-static int m1064_start(void* outdev) {
-	/* nothing */
-	return 0;
-}
-
-static void m1064_incuse(void* outdev) {
-	/* nothing yet; MODULE_INC_USE in future... */
-}
-
-static void m1064_decuse(void* outdev) {
-	/* nothing yet; MODULE_DEC_USE in future... */
-}
+static struct matrox_altout m1064 = {
+	.owner   = THIS_MODULE,
+	.name	 = "Primary output",
+	.compute = m1064_compute,
+};
 
-static int m1064_setmode(void* outdev, u_int32_t mode) {
-	if (mode != MATROXFB_OUTPUT_MODE_MONITOR)
-		return -EINVAL;
+static int g450_compute(void* out, struct my_timming* m) {
+#define minfo ((struct matrox_fb_info*)out)
+	if (m->mnp < 0) {
+		m->mnp = matroxfb_g450_setclk(PMINFO m->pixclock, (m->crtc == MATROXFB_SRC_CRTC1) ? M_PIXEL_PLL_C : M_VIDEO_PLL);
+		if (m->mnp >= 0) {
+			m->pixclock = g450_mnp2f(PMINFO m->mnp);
+		}
+	}
+#undef minfo
 	return 0;
 }
 
-static struct matrox_altout m1064 = {
-	m1064_compute,
-	m1064_program,
-	m1064_start,
-	m1064_incuse,
-	m1064_decuse,
-	m1064_setmode
+static struct matrox_altout g450out = {
+	.owner   = THIS_MODULE,
+	.name	 = "Primary output",
+	.compute = g450_compute,
 };
 
 #endif /* NEED_DAC1064 */
@@ -680,7 +783,10 @@
 	ACCESS_FBINFO(features.accel.has_cacheflush) = 1;
 	ACCESS_FBINFO(cursor.timer.function) = matroxfb_DAC1064_flashcursor;
 
-	ACCESS_FBINFO(primout) = &m1064;
+	ACCESS_FBINFO(outputs[0]).output = &m1064;
+	ACCESS_FBINFO(outputs[0]).src = MATROXFB_SRC_CRTC1;
+	ACCESS_FBINFO(outputs[0]).data = MINFO;
+	ACCESS_FBINFO(outputs[0]).mode = MATROXFB_OUTPUT_MODE_MONITOR;
 
 	if (ACCESS_FBINFO(devflags.noinit))
 		return 0;	/* do not modify settings */
@@ -726,8 +832,13 @@
 	    ((ACCESS_FBINFO(values).reg.opt3 & 0x300000) == 0x300000)) {
 		matroxfb_g450_setclk(PMINFO ACCESS_FBINFO(values.pll.video), M_VIDEO_PLL);
 	} else {
-		/* slow down video clocks... */
-		matroxfb_g450_setclk(PMINFO 0, M_VIDEO_PLL);
+		unsigned long flags;
+		unsigned int pwr;
+		
+		matroxfb_DAC_lock_irqsave(flags);
+		pwr = inDAC1064(PMINFO M1064_XPWRCTRL) & ~0x02;
+		outDAC1064(PMINFO M1064_XPWRCTRL, pwr);
+		matroxfb_DAC_unlock_irqrestore(flags);
 	}
 	matroxfb_g450_setclk(PMINFO ACCESS_FBINFO(values.pll.system), M_SYSTEM_PLL);
 	
@@ -864,7 +975,14 @@
 	ACCESS_FBINFO(capable.plnwt) = ACCESS_FBINFO(devflags.accelerator) == FB_ACCEL_MATROX_MGAG100
 			? ACCESS_FBINFO(devflags.sgram) : 1;
 
-	ACCESS_FBINFO(primout) = &m1064;
+	if (ACCESS_FBINFO(devflags.g450dac)) {
+		ACCESS_FBINFO(outputs[0]).output = &g450out;
+	} else {
+		ACCESS_FBINFO(outputs[0]).output = &m1064;
+	}
+	ACCESS_FBINFO(outputs[0]).src = MATROXFB_SRC_CRTC1;
+	ACCESS_FBINFO(outputs[0]).data = MINFO;
+	ACCESS_FBINFO(outputs[0]).mode = MATROXFB_OUTPUT_MODE_MONITOR;
 
 	if (ACCESS_FBINFO(devflags.g450dac)) {
 		/* we must do this always, BIOS does not do it for us
@@ -895,8 +1013,7 @@
 
 		hw->MXoptionReg |= 0x1080;
 		pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg);
-		mga_outl(M_CTLWTST, 0x00000300);
-		/* mga_outl(M_CTLWTST, 0x03258A31); */
+		mga_outl(M_CTLWTST, ACCESS_FBINFO(values).reg.mctlwtst);
 		udelay(100);
 		mga_outb(0x1C05, 0x00);
 		mga_outb(0x1C05, 0x80);
@@ -947,17 +1064,18 @@
 		pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION2_REG, reg50);
 
 		if (ACCESS_FBINFO(devflags.memtype) == -1)
-			ACCESS_FBINFO(devflags.memtype) = 0;
-		hw->MXoptionReg |= (ACCESS_FBINFO(devflags.memtype) & 7) << 10;
+			hw->MXoptionReg |= ACCESS_FBINFO(values).reg.opt & 0x1C00;
+		else
+			hw->MXoptionReg |= (ACCESS_FBINFO(devflags.memtype) & 7) << 10;
 		if (ACCESS_FBINFO(devflags.sgram))
 			hw->MXoptionReg |= 0x4000;
-		mga_outl(M_CTLWTST, 0x042450A1);
-		mga_outl(M_MEMRDBK, 0x00000108);
+		mga_outl(M_CTLWTST, ACCESS_FBINFO(values).reg.mctlwtst);
+		mga_outl(M_MEMRDBK, ACCESS_FBINFO(values).reg.memrdbk);
 		udelay(200);
 		mga_outl(M_MACCESS, 0x00000000);
 		mga_outl(M_MACCESS, 0x00008000);
 		udelay(100);
-		mga_outl(M_MEMRDBK, 0x00000108);
+		mga_outl(M_MEMRDBK, ACCESS_FBINFO(values).reg.memrdbk);
 		hw->MXoptionReg |= 0x00040020;
 	}
 	pci_write_config_dword(ACCESS_FBINFO(pcidev), PCI_OPTION_REG, hw->MXoptionReg);
diff -urN linux-2.4.21-pre2/drivers/video/matrox/matroxfb_DAC1064.h linux-2.4.21-pre3/drivers/video/matrox/matroxfb_DAC1064.h
--- linux-2.4.21-pre2/drivers/video/matrox/matroxfb_DAC1064.h	Fri Aug  2 17:39:45 2002
+++ linux-2.4.21-pre3/drivers/video/matrox/matroxfb_DAC1064.h	Mon Jan  6 16:32:32 2003
@@ -146,6 +146,8 @@
 
 #define M1064_XPWRCTRL		0xA0
 
+#define M1064_XPANMODE		0xA2
+
 enum POS1064 {
 	POS1064_XCURADDL=0, POS1064_XCURADDH, POS1064_XCURCTRL,
 	POS1064_XCURCOL0RED, POS1064_XCURCOL0GREEN, POS1064_XCURCOL0BLUE,
@@ -156,7 +158,7 @@
 	POS1064_XGENIOCTRL, POS1064_XGENIODATA, POS1064_XZOOMCTRL, POS1064_XSENSETEST,
 	POS1064_XCRCBITSEL,
 	POS1064_XCOLKEYMASKL, POS1064_XCOLKEYMASKH, POS1064_XCOLKEYL, POS1064_XCOLKEYH,
-	POS1064_XOUTPUTCONN };
+	POS1064_XOUTPUTCONN, POS1064_XPANMODE, POS1064_XPWRCTRL };
 
 
 #endif	/* __MATROXFB_DAC1064_H__ */
diff -urN linux-2.4.21-pre2/drivers/video/matrox/matroxfb_Ti3026.c linux-2.4.21-pre3/drivers/video/matrox/matroxfb_Ti3026.c
--- linux-2.4.21-pre2/drivers/video/matrox/matroxfb_Ti3026.c	Fri Aug  2 17:39:45 2002
+++ linux-2.4.21-pre3/drivers/video/matrox/matroxfb_Ti3026.c	Mon Jan  6 16:32:32 2003
@@ -2,11 +2,11 @@
  *
  * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200 and G400
  *
- * (c) 1998,1999,2000 Petr Vandrovec <vandrove@vc.cvut.cz>
+ * (c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz>
  *
  * Portions Copyright (c) 2001 Matrox Graphics Inc.
  *
- * Version: 1.62 2000/11/29
+ * Version: 1.64 2002/06/10
  *
  * MTRR stuff: 1998 Tom Rini <trini@kernel.crashing.org>
  *
@@ -84,6 +84,7 @@
 #include "matroxfb_Ti3026.h"
 #include "matroxfb_misc.h"
 #include "matroxfb_accel.h"
+#include <linux/matroxfb.h>
 
 #ifdef CONFIG_FB_MATROX_MILLENIUM
 #define outTi3026 matroxfb_DAC_out
@@ -811,6 +812,11 @@
 	ti3026_ramdac_init(PMINFO2);
 }
 
+static struct matrox_altout ti3026_output = {
+	.owner   = THIS_MODULE,
+	.name	 = "Primary output",
+};
+
 static int Ti3026_preinit(WPMINFO2) {
 	static const int vxres_mill2[] = { 512,        640, 768,  800,  832,  960,
 					  1024, 1152, 1280,      1600, 1664, 1920,
@@ -829,6 +835,11 @@
 	ACCESS_FBINFO(capable.vxres) = isMilleniumII(MINFO)?vxres_mill2:vxres_mill1;
 	ACCESS_FBINFO(cursor.timer.function) = matroxfb_ti3026_flashcursor;
 
+	ACCESS_FBINFO(outputs[0]).data = MINFO;
+	ACCESS_FBINFO(outputs[0]).output = &ti3026_output;
+	ACCESS_FBINFO(outputs[0]).src = MATROXFB_SRC_CRTC1;
+	ACCESS_FBINFO(outputs[0]).mode = MATROXFB_OUTPUT_MODE_MONITOR;
+
 	if (ACCESS_FBINFO(devflags.noinit))
 		return 0;
 	/* preserve VGA I/O, BIOS and PPC */
diff -urN linux-2.4.21-pre2/drivers/video/matrox/matroxfb_accel.c linux-2.4.21-pre3/drivers/video/matrox/matroxfb_accel.c
--- linux-2.4.21-pre2/drivers/video/matrox/matroxfb_accel.c	Mon Oct 15 13:47:13 2001
+++ linux-2.4.21-pre3/drivers/video/matrox/matroxfb_accel.c	Mon Jan  6 16:32:32 2003
@@ -143,6 +143,8 @@
 	ACCESS_FBINFO(accel.m_opmode) = mopmode;
 }
 
+EXPORT_SYMBOL(matrox_cfbX_init);
+
 static void matrox_cfbX_bmove(struct display* p, int sy, int sx, int dy, int dx, int height, int width) {
 	int pixx = p->var.xres_virtual, start, end;
 	CRITFLAGS
@@ -756,6 +758,7 @@
 }
 #endif
 
+#if defined(FBCON_HAS_CFB16) || defined(FBCON_HAS_CFB24) || defined(FBCON_HAS_CFB32)
 static void matrox_cfbX_revc(struct display* p, int xx, int yy) {
 	CRITFLAGS
 	MINFO_FROM_DISP(p);
@@ -776,6 +779,7 @@
 
 	CRITEND
 }
+#endif
 
 static void matrox_cfbX_clear_margins(struct vc_data* conp, struct display* p, int bottom_only) {
 	unsigned int bottom_height, right_width;
@@ -943,7 +947,7 @@
 	CRITEND
 }
 
-void matrox_text_createcursor(WPMINFO struct display* p) {
+static void matrox_text_createcursor(WPMINFO struct display* p) {
 	CRITFLAGS
 
 	if (ACCESS_FBINFO(currcon_display) != p)
@@ -1029,6 +1033,8 @@
 	var->xres_virtual = vxres * hf;
 }
 
+EXPORT_SYMBOL(matrox_text_round);
+
 static int matrox_text_setfont(struct display* p, int width, int height) {
 	DBG("matrox_text_setfont");
 
@@ -1223,6 +1229,8 @@
 	}
 }
 
+EXPORT_SYMBOL(initMatrox);
+
 void matrox_init_putc(WPMINFO struct display* p, void (*dac_createcursor)(WPMINFO struct display* p)) {
 	int i;
 
@@ -1245,4 +1253,7 @@
 		ACCESS_FBINFO(curr.putcs) = matrox_cfbX_putcs;
 	}
 }
+
+EXPORT_SYMBOL(matrox_init_putc);
+
 MODULE_LICENSE("GPL");
diff -urN linux-2.4.21-pre2/drivers/video/matrox/matroxfb_accel.h linux-2.4.21-pre3/drivers/video/matrox/matroxfb_accel.h
--- linux-2.4.21-pre2/drivers/video/matrox/matroxfb_accel.h	Mon Dec 11 13:16:53 2000
+++ linux-2.4.21-pre3/drivers/video/matrox/matroxfb_accel.h	Mon Jan  6 16:32:32 2003
@@ -5,7 +5,6 @@
 
 void matrox_init_putc(WPMINFO struct display* p, void (*)(WPMINFO struct display *p));
 void matrox_cfbX_init(WPMINFO struct display* p);
-void matrox_text_createcursor(WPMINFO struct display* p);
 void matrox_text_round(CPMINFO struct fb_var_screeninfo* var, struct display* p);
 void initMatrox(WPMINFO struct display* p);
 
diff -urN linux-2.4.21-pre2/drivers/video/matrox/matroxfb_base.c linux-2.4.21-pre3/drivers/video/matrox/matroxfb_base.c
--- linux-2.4.21-pre2/drivers/video/matrox/matroxfb_base.c	Fri Aug  2 17:39:45 2002
+++ linux-2.4.21-pre3/drivers/video/matrox/matroxfb_base.c	Mon Jan  6 16:32:32 2003
@@ -2,11 +2,11 @@
  *
  * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200 and G400
  *
- * (c) 1998-2001 Petr Vandrovec <vandrove@vc.cvut.cz>
+ * (c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz>
  *
  * Portions Copyright (c) 2001 Matrox Graphics Inc.
  *
- * Version: 1.62 2001/11/29
+ * Version: 1.64 2002/06/10
  *
  * MTRR stuff: 1998 Tom Rini <trini@kernel.crashing.org>
  *
@@ -77,6 +77,15 @@
  *               "Uns Lider" <unslider@miranda.org>
  *                     G100 PLNWT fixes
  *
+ *               "Denis Zaitsev" <zzz@cd-club.ru>
+ *                     Fixes
+ *
+ *               "Mike Pieper" <mike@pieper-family.de>
+ *                     TVOut enhandcements, V4L2 control interface.
+ *
+ *               "Diego Biurrun" <diego@biurrun.de>
+ *                     DFP testing
+ *
  * (following author is not in any relation with this code, but his code
  *  is included in this driver)
  *
@@ -100,6 +109,7 @@
 #include "matroxfb_Ti3026.h"
 #include "matroxfb_maven.h"
 #include "matroxfb_crtc2.h"
+#include "matroxfb_g450.h"
 #include <linux/matroxfb.h>
 #include <asm/uaccess.h>
 
@@ -199,6 +209,7 @@
 	}
 	matroxfb_unregister_device(MINFO);
 	unregister_framebuffer(&ACCESS_FBINFO(fbcon));
+	matroxfb_g450_shutdown(PMINFO2);
 	del_timer_sync(&ACCESS_FBINFO(cursor.timer));
 #ifdef CONFIG_MTRR
 	if (ACCESS_FBINFO(mtrr.vram_valid))
@@ -220,7 +231,7 @@
 
 static int matroxfb_open(struct fb_info *info, int user)
 {
-#define minfo ((struct matrox_fb_info*)info)
+#define minfo (list_entry(info, struct matrox_fb_info, fbcon))
 	DBG_LOOP("matroxfb_open")
 
 	if (ACCESS_FBINFO(dead)) {
@@ -233,7 +244,7 @@
 
 static int matroxfb_release(struct fb_info *info, int user)
 {
-#define minfo ((struct matrox_fb_info*)info)
+#define minfo (list_entry(info, struct matrox_fb_info, fbcon))
 	DBG_LOOP("matroxfb_release")
 
 	if (!(--ACCESS_FBINFO(usecount)) && ACCESS_FBINFO(dead)) {
@@ -245,7 +256,7 @@
 
 static int matroxfb_pan_display(struct fb_var_screeninfo *var, int con,
 		struct fb_info* info) {
-#define minfo ((struct matrox_fb_info*)info)
+#define minfo (list_entry(info, struct matrox_fb_info, fbcon))
 
 	DBG("matroxfb_pan_display")
 
@@ -271,7 +282,7 @@
 
 static int matroxfb_updatevar(int con, struct fb_info *info)
 {
-#define minfo ((struct matrox_fb_info*)info)
+#define minfo (list_entry(info, struct matrox_fb_info, fbcon))
 	DBG("matroxfb_updatevar");
 
 	matrox_pan_var(PMINFO &fb_display[con].var);
@@ -391,12 +402,43 @@
 }
 
 static int matroxfb_decode_var(CPMINFO struct display* p, struct fb_var_screeninfo *var, int *visual, int *video_cmap_len, unsigned int* ydstorg) {
+	struct RGBT {
+		unsigned char bpp;
+		struct {
+			unsigned char offset,
+				      length;
+		} red,
+		  green,
+		  blue,
+		  transp;
+		signed char visual;
+	};
+	static const struct RGBT table[]= {
+#if defined FBCON_HAS_VGATEXT
+		{ 0,{ 0,6},{0,6},{0,6},{ 0,0},MX_VISUAL_PSEUDOCOLOR},
+#endif
+#if defined FBCON_HAS_CFB4 || defined FBCON_HAS_CFB8
+		{ 8,{ 0,8},{0,8},{0,8},{ 0,0},MX_VISUAL_PSEUDOCOLOR},
+#endif
+#if defined FBCON_HAS_CFB16
+		{15,{10,5},{5,5},{0,5},{15,1},MX_VISUAL_DIRECTCOLOR},
+		{16,{11,5},{5,6},{0,5},{ 0,0},MX_VISUAL_DIRECTCOLOR},
+#endif
+#if defined FBCON_HAS_CFB24
+		{24,{16,8},{8,8},{0,8},{ 0,0},MX_VISUAL_DIRECTCOLOR},
+#endif
+#if defined FBCON_HAS_CFB32
+		{32,{16,8},{8,8},{0,8},{24,8},MX_VISUAL_DIRECTCOLOR}
+#endif
+	};
+	struct RGBT const *rgbt;
+	unsigned int bpp = var->bits_per_pixel;
 	unsigned int vramlen;
 	unsigned int memlen;
 
 	DBG("matroxfb_decode_var")
 
-	switch (var->bits_per_pixel) {
+	switch (bpp) {
 #ifdef FBCON_HAS_VGATEXT
 		case 0:	 if (!ACCESS_FBINFO(capable.text)) return -EINVAL;
 			 break;
@@ -425,22 +467,22 @@
 		var->yres_virtual = var->yres;
 	if (var->xres_virtual < var->xres)
 		var->xres_virtual = var->xres;
-	if (var->bits_per_pixel) {
-		var->xres_virtual = matroxfb_pitch_adjust(PMINFO var->xres_virtual, var->bits_per_pixel);
-		memlen = var->xres_virtual * var->bits_per_pixel * var->yres_virtual / 8;
+	if (bpp) {
+		var->xres_virtual = matroxfb_pitch_adjust(PMINFO var->xres_virtual, bpp);
+		memlen = var->xres_virtual * bpp * var->yres_virtual / 8;
 		if (memlen > vramlen) {
-			var->yres_virtual = vramlen * 8 / (var->xres_virtual * var->bits_per_pixel);
-			memlen = var->xres_virtual * var->bits_per_pixel * var->yres_virtual / 8;
+			var->yres_virtual = vramlen * 8 / (var->xres_virtual * bpp);
+			memlen = var->xres_virtual * bpp * var->yres_virtual / 8;
 		}
 		/* There is hardware bug that no line can cross 4MB boundary */
 		/* give up for CFB24, it is impossible to easy workaround it */
 		/* for other try to do something */
 		if (!ACCESS_FBINFO(capable.cross4MB) && (memlen > 0x400000)) {
-			if (var->bits_per_pixel == 24) {
+			if (bpp == 24) {
 				/* sorry */
 			} else {
 				unsigned int linelen;
-				unsigned int m1 = linelen = var->xres_virtual * var->bits_per_pixel / 8;
+				unsigned int m1 = linelen = var->xres_virtual * bpp / 8;
 				unsigned int m2 = PAGE_SIZE;	/* or 128 if you do not need PAGE ALIGNED address */
 				unsigned int max_yres;
 
@@ -484,104 +526,43 @@
 	if (var->yoffset + var->yres > var->yres_virtual)
 		var->yoffset = var->yres_virtual - var->yres;
 
-	if (var->bits_per_pixel == 0) {
-		var->red.offset = 0;
-		var->red.length = 6;
-		var->green.offset = 0;
-		var->green.length = 6;
-		var->blue.offset = 0;
-		var->blue.length = 6;
-		var->transp.offset = 0;
-		var->transp.length = 0;
-		*visual = MX_VISUAL_PSEUDOCOLOR;
-	} else if (var->bits_per_pixel == 4) {
-		var->red.offset = 0;
-		var->red.length = 8;
-		var->green.offset = 0;
-		var->green.length = 8;
-		var->blue.offset = 0;
-		var->blue.length = 8;
-		var->transp.offset = 0;
-		var->transp.length = 0;
-		*visual = MX_VISUAL_PSEUDOCOLOR;
-	} else if (var->bits_per_pixel <= 8) {
-		var->red.offset = 0;
-		var->red.length = 8;
-		var->green.offset = 0;
-		var->green.length = 8;
-		var->blue.offset = 0;
-		var->blue.length = 8;
-		var->transp.offset = 0;
-		var->transp.length = 0;
-		*visual = MX_VISUAL_PSEUDOCOLOR;
-	} else {
-		if (var->bits_per_pixel <= 16) {
-			if (var->green.length == 5) {
-				var->red.offset    = 10;
-				var->red.length    = 5;
-				var->green.offset  = 5;
-				var->green.length  = 5;
-				var->blue.offset   = 0;
-				var->blue.length   = 5;
-				var->transp.offset = 15;
-				var->transp.length = 1;
-			} else {
-				var->red.offset    = 11;
-				var->red.length    = 5;
-				var->green.offset  = 5;
-				var->green.length  = 6;
-				var->blue.offset   = 0;
-				var->blue.length   = 5;
-				var->transp.offset = 0;
-				var->transp.length = 0;
-			}
-		} else if (var->bits_per_pixel <= 24) {
-			var->red.offset    = 16;
-			var->red.length    = 8;
-			var->green.offset  = 8;
-			var->green.length  = 8;
-			var->blue.offset   = 0;
-			var->blue.length   = 8;
-			var->transp.offset = 0;
-			var->transp.length = 0;
-		} else {
-			var->red.offset    = 16;
-			var->red.length    = 8;
-			var->green.offset  = 8;
-			var->green.length  = 8;
-			var->blue.offset   = 0;
-			var->blue.length   = 8;
-			var->transp.offset = 24;
-			var->transp.length = 8;
-		}
-		dprintk("matroxfb: truecolor: "
-		       "size=%d:%d:%d:%d, shift=%d:%d:%d:%d\n",
-		       var->transp.length,
-		       var->red.length,
-		       var->green.length,
-		       var->blue.length,
-		       var->transp.offset,
-		       var->red.offset,
-		       var->green.offset,
-		       var->blue.offset);
-		*visual = MX_VISUAL_DIRECTCOLOR;
+	if (bpp == 16 && var->green.length == 5) {
+		bpp--; /* an artifical value - 15 */
 	}
+
+	for (rgbt = table; rgbt->bpp < bpp; rgbt++);
+#define	SETCLR(clr)\
+	var->clr.offset = rgbt->clr.offset;\
+	var->clr.length = rgbt->clr.length
+	SETCLR(red);
+	SETCLR(green);
+	SETCLR(blue);
+	SETCLR(transp);
+#undef	SETCLR
+	*visual = rgbt->visual;
+
+	if (bpp > 8)
+		dprintk("matroxfb: truecolor: "
+			"size=%d:%d:%d:%d, shift=%d:%d:%d:%d\n",
+			var->transp.length, var->red.length, var->green.length, var->blue.length,
+			var->transp.offset, var->red.offset, var->green.offset, var->blue.offset);
+
 	*video_cmap_len = matroxfb_get_cmap_len(var);
 	dprintk(KERN_INFO "requested %d*%d/%dbpp (%d*%d)\n", var->xres, var->yres, var->bits_per_pixel,
 				var->xres_virtual, var->yres_virtual);
 	return 0;
 }
 
-static int matrox_setcolreg(unsigned regno, unsigned red, unsigned green,
-			    unsigned blue, unsigned transp,
-			    struct fb_info *fb_info)
+static int matroxfb_setcolreg(unsigned regno, unsigned red, unsigned green,
+			      unsigned blue, unsigned transp,
+			      struct fb_info *fb_info)
 {
 	struct display* p;
 #ifdef CONFIG_FB_MATROX_MULTIHEAD
-	struct matrox_fb_info* minfo = (struct matrox_fb_info*)fb_info;
+	struct matrox_fb_info* minfo = list_entry(fb_info, struct matrox_fb_info, fbcon);
 #endif
 
-	DBG("matrox_setcolreg")
+	DBG("matroxfb_setcolreg")
 
 	/*
 	 *  Set a single color register. The values supplied are
@@ -656,15 +637,17 @@
 	return 0;
 }
 
-static void do_install_cmap(WPMINFO struct display* dsp)
+static inline void my_install_cmap(WPMINFO2)
 {
-	DBG("do_install_cmap")
+	/* Do not touch this code if you do not understand what it does! */
+	/* Never try to use do_install_cmap() instead. It is crap. */
+	struct fb_cmap* cmap = &ACCESS_FBINFO(currcon_display)->cmap;
 
-	if (dsp->cmap.len)
-		fb_set_cmap(&dsp->cmap, 1, matrox_setcolreg, &ACCESS_FBINFO(fbcon));
+	if (cmap->len)
+		fb_set_cmap(cmap, 1, matroxfb_setcolreg, &ACCESS_FBINFO(fbcon));
 	else
 		fb_set_cmap(fb_default_cmap(ACCESS_FBINFO(curr.cmap_len)),
-			    1, matrox_setcolreg, &ACCESS_FBINFO(fbcon));
+			    1, matroxfb_setcolreg, &ACCESS_FBINFO(fbcon));
 }
 
 static int matroxfb_get_fix(struct fb_fix_screeninfo *fix, int con,
@@ -673,7 +656,7 @@
 	struct display* p;
 	DBG("matroxfb_get_fix")
 
-#define minfo ((struct matrox_fb_info*)info)
+#define minfo (list_entry(info, struct matrox_fb_info, fbcon))
 
 	if (ACCESS_FBINFO(dead)) {
 		return -ENXIO;
@@ -684,7 +667,7 @@
 	else
 		p = ACCESS_FBINFO(fbcon.disp);
 
-	memset(fix, 0, sizeof(struct fb_fix_screeninfo));
+	memset(fix, 0, sizeof(*fix));
 	strcpy(fix->id,"MATROX");
 
 	fix->smem_start = ACCESS_FBINFO(video.base) + ACCESS_FBINFO(curr.ydstorg.bytes);
@@ -706,7 +689,7 @@
 static int matroxfb_get_var(struct fb_var_screeninfo *var, int con,
 			 struct fb_info *info)
 {
-#define minfo ((struct matrox_fb_info*)info)
+#define minfo (list_entry(info, struct matrox_fb_info, fbcon))
 	DBG("matroxfb_get_var")
 
 	if(con < 0)
@@ -720,7 +703,7 @@
 static int matroxfb_set_var(struct fb_var_screeninfo *var, int con,
 			 struct fb_info *info)
 {
-#define minfo ((struct matrox_fb_info*)info)
+#define minfo (list_entry(info, struct matrox_fb_info, fbcon))
 	int err;
 	int visual;
 	int cmap_len;
@@ -760,7 +743,7 @@
 	}
 	display->var = *var;
 	/* cmap */
-	display->screen_base = vaddr_va(ACCESS_FBINFO(video.vbase)) + ydstorg;
+	display->screen_base = ACCESS_FBINFO(fbcon.screen_base) = vaddr_va(ACCESS_FBINFO(video.vbase)) + ydstorg;
 	display->visual = visual;
 	display->ypanstep = 1;
 	display->ywrapstep = 0;
@@ -816,8 +799,10 @@
 
 		{	struct my_timming mt;
 			struct matrox_hw_state* hw;
+			int out;
 
 			matroxfb_var2my(var, &mt);
+			mt.crtc = MATROXFB_SRC_CRTC1;
 			/* CRTC1 delays */
 			switch (var->bits_per_pixel) {
 				case  0:	mt.delay = 31 + 0; break;
@@ -832,6 +817,16 @@
 			del_timer_sync(&ACCESS_FBINFO(cursor.timer));
 			ACCESS_FBINFO(cursor.state) = CM_ERASE;
 
+			down_read(&ACCESS_FBINFO(altout).lock);
+			for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
+				if (ACCESS_FBINFO(outputs[out]).src == MATROXFB_SRC_CRTC1 &&
+				    ACCESS_FBINFO(outputs[out]).output->compute) {
+					ACCESS_FBINFO(outputs[out]).output->compute(ACCESS_FBINFO(outputs[out]).data, &mt);
+				}
+			}
+			up_read(&ACCESS_FBINFO(altout).lock);
+			ACCESS_FBINFO(crtc1).pixclock = mt.pixclock;
+			ACCESS_FBINFO(crtc1).mnp = mt.mnp;
 			ACCESS_FBINFO(hw_switch->init(PMINFO &mt, display));
 			if (display->type == FB_TYPE_TEXT) {
 				if (fontheight(display))
@@ -847,40 +842,25 @@
 			hw->CRTC[0x0C] = (pos & 0xFF00) >> 8;
 			hw->CRTCEXT[0] = (hw->CRTCEXT[0] & 0xF0) | ((pos >> 16) & 0x0F) | ((pos >> 14) & 0x40);
 			hw->CRTCEXT[8] = pos >> 21;
-			if (ACCESS_FBINFO(output.ph) & (MATROXFB_OUTPUT_CONN_PRIMARY | MATROXFB_OUTPUT_CONN_DFP)) {
-				if (ACCESS_FBINFO(primout))
-					ACCESS_FBINFO(primout)->compute(MINFO, &mt);
-			}
-			if (ACCESS_FBINFO(output.ph) & MATROXFB_OUTPUT_CONN_SECONDARY) {
-				down_read(&ACCESS_FBINFO(altout.lock));
-				if (ACCESS_FBINFO(altout.output))
-					ACCESS_FBINFO(altout.output)->compute(ACCESS_FBINFO(altout.device), &mt);
-				up_read(&ACCESS_FBINFO(altout.lock));
-			}
+		
 			ACCESS_FBINFO(hw_switch->restore(PMINFO display));
-			if (ACCESS_FBINFO(output.ph) & (MATROXFB_OUTPUT_CONN_PRIMARY | MATROXFB_OUTPUT_CONN_DFP)) {
-				if (ACCESS_FBINFO(primout))
-					ACCESS_FBINFO(primout)->program(MINFO);
-			}
-			if (ACCESS_FBINFO(output.ph) & MATROXFB_OUTPUT_CONN_SECONDARY) {
-				down_read(&ACCESS_FBINFO(altout.lock));
-				if (ACCESS_FBINFO(altout.output))
-					ACCESS_FBINFO(altout.output)->program(ACCESS_FBINFO(altout.device));
-				up_read(&ACCESS_FBINFO(altout.lock));
+			down_read(&ACCESS_FBINFO(altout).lock);
+			for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
+				if (ACCESS_FBINFO(outputs[out]).src == MATROXFB_SRC_CRTC1 &&
+				    ACCESS_FBINFO(outputs[out]).output->program) {
+					ACCESS_FBINFO(outputs[out]).output->program(ACCESS_FBINFO(outputs[out]).data);
+				}
 			}
 			ACCESS_FBINFO(cursor.redraw) = 1;
-			if (ACCESS_FBINFO(output.ph) & (MATROXFB_OUTPUT_CONN_PRIMARY | MATROXFB_OUTPUT_CONN_DFP)) {
-				if (ACCESS_FBINFO(primout))
-					ACCESS_FBINFO(primout)->start(MINFO);
-			}
-			if (ACCESS_FBINFO(output.ph) & MATROXFB_OUTPUT_CONN_SECONDARY) {
-				down_read(&ACCESS_FBINFO(altout.lock));
-				if (ACCESS_FBINFO(altout.output))
-					ACCESS_FBINFO(altout.output)->start(ACCESS_FBINFO(altout.device));
-				up_read(&ACCESS_FBINFO(altout.lock));
+			for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
+				if (ACCESS_FBINFO(outputs[out]).src == MATROXFB_SRC_CRTC1 &&
+				    ACCESS_FBINFO(outputs[out]).output->start) {
+					ACCESS_FBINFO(outputs[out]).output->start(ACCESS_FBINFO(outputs[out]).data);
+				}
 			}
+			up_read(&ACCESS_FBINFO(altout).lock);
 			matrox_cfbX_init(PMINFO display);
-			do_install_cmap(PMINFO display);
+			my_install_cmap(PMINFO2);
 #if defined(CONFIG_FB_COMPAT_XPMAC)
 			if (console_fb_info == &ACCESS_FBINFO(fbcon)) {
 				int vmode, cmode;
@@ -913,7 +893,7 @@
 
 	DBG("matrox_getcolreg")
 
-#define minfo ((struct matrox_fb_info*)info)
+#define minfo (list_entry(info, struct matrox_fb_info, fbcon))
 	/*
 	 *  Read a single color register and split it into colors/transparent.
 	 *  Return != 0 for invalid regno.
@@ -933,7 +913,7 @@
 static int matroxfb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
 			     struct fb_info *info)
 {
-#define minfo ((struct matrox_fb_info*)info)
+#define minfo (list_entry(info, struct matrox_fb_info, fbcon))
 	struct display* dsp = (con < 0) ? ACCESS_FBINFO(fbcon.disp)
 					: fb_display + con;
 
@@ -959,7 +939,7 @@
 {
 	unsigned int cmap_len;
 	struct display* dsp = (con < 0) ? info->disp : (fb_display + con);
-#define minfo ((struct matrox_fb_info*)info)
+#define minfo (list_entry(info, struct matrox_fb_info, fbcon))
 
 	DBG("matroxfb_set_cmap")
 
@@ -976,7 +956,7 @@
 			return err;
 	}
 	if (con == ACCESS_FBINFO(currcon)) {			/* current console? */
-		return fb_set_cmap(cmap, kspc, matrox_setcolreg, info);
+		return fb_set_cmap(cmap, kspc, matroxfb_setcolreg, info);
 	} else
 		fb_copy_cmap(cmap, &dsp->cmap, kspc ? 0 : 1);
 	return 0;
@@ -1006,11 +986,16 @@
 	return 0;
 }
 
+static struct matrox_altout panellink_output = {
+	.owner   = THIS_MODULE,
+	.name	 = "Panellink output",
+};
+
 static int matroxfb_ioctl(struct inode *inode, struct file *file,
 			  unsigned int cmd, unsigned long arg, int con,
 			  struct fb_info *info)
 {
-#define minfo ((struct matrox_fb_info*)info)
+#define minfo (list_entry(info, struct matrox_fb_info, fbcon))
 	DBG("matroxfb_ioctl")
 
 	if (ACCESS_FBINFO(dead)) {
@@ -1033,80 +1018,74 @@
 		case MATROXFB_SET_OUTPUT_MODE:
 			{
 				struct matroxioc_output_mode mom;
+				struct matrox_altout *oproc;
 				int val;
 
 				if (copy_from_user(&mom, (struct matroxioc_output_mode*)arg, sizeof(mom)))
 					return -EFAULT;
-				if (mom.output >= sizeof(u_int32_t))
-					return -EINVAL;
-				switch (mom.output) {
-					case MATROXFB_OUTPUT_PRIMARY:
-						if (mom.mode != MATROXFB_OUTPUT_MODE_MONITOR)
-							return -EINVAL;
-						/* mode did not change... */
-						return 0;
-					case MATROXFB_OUTPUT_SECONDARY:
+				if (mom.output >= MATROXFB_MAX_OUTPUTS)
+					return -ENXIO;
+				down_read(&ACCESS_FBINFO(altout.lock));
+				oproc = ACCESS_FBINFO(outputs[mom.output]).output;
+				if (!oproc) {
+					val = -ENXIO;
+				} else if (!oproc->verifymode) {
+					if (mom.mode == MATROXFB_OUTPUT_MODE_MONITOR) {
+						val = 0;
+					} else {
 						val = -EINVAL;
-						down_read(&ACCESS_FBINFO(altout.lock));
-						if (ACCESS_FBINFO(altout.output) && ACCESS_FBINFO(altout.device))
-							val = ACCESS_FBINFO(altout.output)->setmode(ACCESS_FBINFO(altout.device), mom.mode);
-						up_read(&ACCESS_FBINFO(altout.lock));
-						if (val != 1)
-							return val;
-						if (ACCESS_FBINFO(output.ph) & MATROXFB_OUTPUT_CONN_SECONDARY)
-							matroxfb_switch(ACCESS_FBINFO(currcon), info);
-						if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_SECONDARY) {
+					}
+				} else {
+					val = oproc->verifymode(ACCESS_FBINFO(outputs[mom.output]).data, mom.mode);
+				}
+				if (!val) {
+					if (ACCESS_FBINFO(outputs[mom.output]).mode != mom.mode) {
+						ACCESS_FBINFO(outputs[mom.output]).mode = mom.mode;
+						val = 1;
+					}
+				}
+				up_read(&ACCESS_FBINFO(altout.lock));
+				if (val != 1)
+					return val;
+				switch (ACCESS_FBINFO(outputs[mom.output]).src) {
+					case MATROXFB_SRC_CRTC1:
+						matroxfb_switch(ACCESS_FBINFO(currcon), info);
+						break;
+					case MATROXFB_SRC_CRTC2:
+						{
 							struct matroxfb_dh_fb_info* crtc2;
 
 							down_read(&ACCESS_FBINFO(crtc2.lock));
-							crtc2 = (struct matroxfb_dh_fb_info*)(ACCESS_FBINFO(crtc2.info));
+							crtc2 = ACCESS_FBINFO(crtc2.info);
 							if (crtc2)
 								crtc2->fbcon.switch_con(crtc2->currcon, &crtc2->fbcon);
 							up_read(&ACCESS_FBINFO(crtc2.lock));
 						}
-						return 0;
-					case MATROXFB_OUTPUT_DFP:
-						if (!(ACCESS_FBINFO(output.all) & MATROXFB_OUTPUT_CONN_DFP))
-							return -ENXIO;
-						if (mom.mode!= MATROXFB_OUTPUT_MODE_MONITOR)
-							return -EINVAL;
-						/* mode did not change... */
-						return 0;
-					default:
-						return -EINVAL;
+						break;
 				}
 				return 0;
 			}
 		case MATROXFB_GET_OUTPUT_MODE:
 			{
 				struct matroxioc_output_mode mom;
+				struct matrox_altout *oproc;
 				int val;
 
 				if (copy_from_user(&mom, (struct matroxioc_output_mode*)arg, sizeof(mom)))
 					return -EFAULT;
-				if (mom.output >= sizeof(u_int32_t))
-					return -EINVAL;
-				switch (mom.output) {
-					case MATROXFB_OUTPUT_PRIMARY:
-						mom.mode = MATROXFB_OUTPUT_MODE_MONITOR;
-						break;
-					case MATROXFB_OUTPUT_SECONDARY:
-						val = -EINVAL;
-						down_read(&ACCESS_FBINFO(altout.lock));
-						if (ACCESS_FBINFO(altout.output) && ACCESS_FBINFO(altout.device))
-							val = ACCESS_FBINFO(altout.output)->getmode(ACCESS_FBINFO(altout.device), &mom.mode);
-						up_read(&ACCESS_FBINFO(altout.lock));
-						if (val)
-							return val;
-						break;
-					case MATROXFB_OUTPUT_DFP:
-						if (!(ACCESS_FBINFO(output.all) & MATROXFB_OUTPUT_CONN_DFP))
-							return -ENXIO;
-						mom.mode = MATROXFB_OUTPUT_MODE_MONITOR;
-						break;
-					default:
-						return -EINVAL;
+				if (mom.output >= MATROXFB_MAX_OUTPUTS)
+					return -ENXIO;
+				down_read(&ACCESS_FBINFO(altout.lock));
+				oproc = ACCESS_FBINFO(outputs[mom.output]).output;
+				if (!oproc) {
+					val = -ENXIO;
+				} else {
+					mom.mode = ACCESS_FBINFO(outputs[mom.output]).mode;
+					val = 0;
 				}
+				up_read(&ACCESS_FBINFO(altout.lock));
+				if (val)
+					return val;
 				if (copy_to_user((struct matroxioc_output_mode*)arg, &mom, sizeof(mom)))
 					return -EFAULT;
 				return 0;
@@ -1114,55 +1093,248 @@
 		case MATROXFB_SET_OUTPUT_CONNECTION:
 			{
 				u_int32_t tmp;
+				int i;
+				int changes;
 
 				if (copy_from_user(&tmp, (u_int32_t*)arg, sizeof(tmp)))
 					return -EFAULT;
-				if (tmp & ~ACCESS_FBINFO(output.all))
-					return -EINVAL;
-				if (tmp & ACCESS_FBINFO(output.sh))
-					return -EINVAL;
-				if (tmp & MATROXFB_OUTPUT_CONN_DFP) {
-					if (tmp & MATROXFB_OUTPUT_CONN_SECONDARY)
-						return -EINVAL;
-					if (ACCESS_FBINFO(output.sh))
-						return -EINVAL;
+				for (i = 0; i < 32; i++) {
+					if (tmp & (1 << i)) {
+						if (i >= MATROXFB_MAX_OUTPUTS)
+							return -ENXIO;
+						if (!ACCESS_FBINFO(outputs[i]).output)
+							return -ENXIO;
+						switch (ACCESS_FBINFO(outputs[i]).src) {
+							case MATROXFB_SRC_NONE:
+							case MATROXFB_SRC_CRTC1:
+								break;
+							default:
+								return -EBUSY;
+						}
+					}
 				}
-				if (tmp == ACCESS_FBINFO(output.ph))
+				if (ACCESS_FBINFO(devflags.panellink)) {
+					if (tmp & MATROXFB_OUTPUT_CONN_DFP) {
+						if (tmp & MATROXFB_OUTPUT_CONN_SECONDARY)
+							return -EINVAL;
+						for (i = 0; i < MATROXFB_MAX_OUTPUTS; i++) {
+							if (ACCESS_FBINFO(outputs[i]).src == MATROXFB_SRC_CRTC2) {
+								return -EBUSY;
+							}
+						}
+					}
+				}
+				changes = 0;
+				for (i = 0; i < MATROXFB_MAX_OUTPUTS; i++) {
+					if (tmp & (1 << i)) {
+						if (ACCESS_FBINFO(outputs[i]).src != MATROXFB_SRC_CRTC1) {
+							changes = 1;
+							ACCESS_FBINFO(outputs[i]).src = MATROXFB_SRC_CRTC1;
+						}
+					} else if (ACCESS_FBINFO(outputs[i]).src == MATROXFB_SRC_CRTC1) {
+						changes = 1;
+						ACCESS_FBINFO(outputs[i]).src = MATROXFB_SRC_NONE;
+					}
+				}
+				if (!changes)
 					return 0;
-				ACCESS_FBINFO(output.ph) = tmp;
 				matroxfb_switch(ACCESS_FBINFO(currcon), info);
 				return 0;
 			}
 		case MATROXFB_GET_OUTPUT_CONNECTION:
 			{
-				if (put_user(ACCESS_FBINFO(output.ph), (u_int32_t*)arg))
+				u_int32_t conn = 0;
+				int i;
+
+				for (i = 0; i < MATROXFB_MAX_OUTPUTS; i++) {
+					if (ACCESS_FBINFO(outputs[i]).src == MATROXFB_SRC_CRTC1) {
+						conn |= 1 << i;
+					}
+				}
+				if (put_user(conn, (u_int32_t*)arg))
 					return -EFAULT;
 				return 0;
 			}
 		case MATROXFB_GET_AVAILABLE_OUTPUTS:
 			{
-				u_int32_t tmp;
+				u_int32_t conn = 0;
+				int i;
 
-				tmp = ACCESS_FBINFO(output.all) & ~ACCESS_FBINFO(output.sh);
-				if (ACCESS_FBINFO(output.ph) & MATROXFB_OUTPUT_CONN_DFP)
-					tmp &= ~MATROXFB_OUTPUT_CONN_SECONDARY;
-				if (ACCESS_FBINFO(output.ph) & MATROXFB_OUTPUT_CONN_SECONDARY)
-					tmp &= ~MATROXFB_OUTPUT_CONN_DFP;
-				if (put_user(tmp, (u_int32_t*)arg))
+				for (i = 0; i < MATROXFB_MAX_OUTPUTS; i++) {
+					if (ACCESS_FBINFO(outputs[i]).output) {
+						switch (ACCESS_FBINFO(outputs[i]).src) {
+							case MATROXFB_SRC_NONE:
+							case MATROXFB_SRC_CRTC1:
+								conn |= 1 << i;
+								break;
+						}
+					}
+				}
+				if (ACCESS_FBINFO(devflags.panellink)) {
+					if (conn & MATROXFB_OUTPUT_CONN_DFP)
+						conn &= ~MATROXFB_OUTPUT_CONN_SECONDARY;
+					if (conn & MATROXFB_OUTPUT_CONN_SECONDARY)
+						conn &= ~MATROXFB_OUTPUT_CONN_DFP;
+				}
+				if (put_user(conn, (u_int32_t*)arg))
 					return -EFAULT;
 				return 0;
 			}
 		case MATROXFB_GET_ALL_OUTPUTS:
 			{
-				if (put_user(ACCESS_FBINFO(output.all), (u_int32_t*)arg))
+				u_int32_t conn = 0;
+				int i;
+
+				for (i = 0; i < MATROXFB_MAX_OUTPUTS; i++) {
+					if (ACCESS_FBINFO(outputs[i]).output) {
+						conn |= 1 << i;
+					}
+				}
+				if (put_user(conn, (u_int32_t*)arg))
+					return -EFAULT;
+				return 0;
+			}
+		case 0x80585600:
+			{
+				struct {
+					char name[32];
+					int type;
+					int inputs;
+					int outputs;
+					int audios;
+					int maxwidth;
+					int maxheight;
+					int minwidth;
+					int minheight;
+					int maxframerate;
+					__u32 flags;
+					__u32 reserved[4];
+				} r;
+				
+				memset(&r, 0, sizeof(r));
+				strcat(r.name, "Matrox TVO");
+				r.type = 2; // output
+				r.inputs = 0;
+				r.outputs = 1;
+				r.audios = 0;
+				r.maxwidth = 1023;	// max visible width
+				r.maxheight = 625;	// max picture height: full PAL
+				r.minwidth = 512;	// min visible width... ~480
+				r.minheight = 480;	// min picture height: visible portion of NTSC
+				r.maxframerate = 60;	// max: NTSC, 60Hz interlaced
+				r.flags = 0;		// nothing is supported...
+				if (copy_to_user((void*)arg, &r, sizeof(r)))
 					return -EFAULT;
 				return 0;
+				
+			}
+		case MATROXFB_TVOQUERYCTRL:
+			{
+				struct matroxfb_queryctrl qctrl;
+				int err;
+
+				if (copy_from_user(&qctrl, (struct matroxfb_queryctrl*)arg, sizeof(qctrl)))
+					return -EFAULT;
+
+				down_read(&ACCESS_FBINFO(altout).lock);
+				if (!ACCESS_FBINFO(outputs[1]).output) {
+					err = -ENXIO;
+				} else if (ACCESS_FBINFO(outputs[1]).output->getqueryctrl) {
+					err = ACCESS_FBINFO(outputs[1]).output->getqueryctrl(ACCESS_FBINFO(outputs[1]).data, &qctrl);
+				} else {
+					err = -EINVAL;
+				}
+				up_read(&ACCESS_FBINFO(altout).lock);
+				if (err >= 0 &&
+				    copy_to_user((struct matroxfb_queryctrl*)arg, &qctrl, sizeof(qctrl)))
+					return -EFAULT;
+				return err;
+			}
+		case MATROXFB_G_TVOCTRL:
+			{
+				struct matroxfb_control ctrl;
+				int err;
+
+				if (copy_from_user(&ctrl, (struct matroxfb_control*)arg, sizeof(ctrl)))
+					return -EFAULT;
+
+				down_read(&ACCESS_FBINFO(altout).lock);
+				if (!ACCESS_FBINFO(outputs[1]).output) {
+					err = -ENXIO;
+				} else if (ACCESS_FBINFO(outputs[1]).output->getctrl) {
+					err = ACCESS_FBINFO(outputs[1]).output->getctrl(ACCESS_FBINFO(outputs[1]).data, &ctrl);
+				} else {
+					err = -EINVAL;
+				}
+				up_read(&ACCESS_FBINFO(altout).lock);
+				if (err >= 0 &&
+				    copy_to_user((struct matroxfb_control*)arg, &ctrl, sizeof(ctrl)))
+					return -EFAULT;
+				return err;
+			}
+		case MATROXFB_S_TVOCTRL:
+			{
+				struct matroxfb_control ctrl;
+				int err;
+
+				if (copy_from_user(&ctrl, (struct matroxfb_control*)arg, sizeof(ctrl)))
+					return -EFAULT;
+
+				down_read(&ACCESS_FBINFO(altout).lock);
+				if (!ACCESS_FBINFO(outputs[1]).output) {
+					err = -ENXIO;
+				} else if (ACCESS_FBINFO(outputs[1]).output->setctrl) {
+					err = ACCESS_FBINFO(outputs[1]).output->setctrl(ACCESS_FBINFO(outputs[1]).data, &ctrl);
+				} else {
+					err = -EINVAL;
+				}
+				up_read(&ACCESS_FBINFO(altout).lock);
+				return err;
 			}
 	}
-	return -EINVAL;
+	return -ENOTTY;
 #undef minfo
 }
 
+/* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */
+
+static int matroxfb_blank(int blank, struct fb_info *info)
+{
+#define minfo (list_entry(info, struct matrox_fb_info, fbcon))
+	int seq;
+	int crtc;
+	CRITFLAGS
+
+	DBG("matroxfb_blank")
+
+	if (ACCESS_FBINFO(dead))
+		return 1;
+
+	switch (blank) {
+		case 1:  seq = 0x20; crtc = 0x00; break; /* works ??? */
+		case 2:  seq = 0x20; crtc = 0x10; break;
+		case 3:  seq = 0x20; crtc = 0x20; break;
+		case 4:  seq = 0x20; crtc = 0x30; break;
+		default: seq = 0x00; crtc = 0x00; break;
+	}
+
+	CRITBEGIN
+
+	mga_outb(M_SEQ_INDEX, 1);
+	mga_outb(M_SEQ_DATA, (mga_inb(M_SEQ_DATA) & ~0x20) | seq);
+	mga_outb(M_EXTVGA_INDEX, 1);
+	mga_outb(M_EXTVGA_DATA, (mga_inb(M_EXTVGA_DATA) & ~0x30) | crtc);
+
+	CRITEND
+	return 0;
+#undef minfo
+}
+
+static void matroxfb_blank24(int blank, struct fb_info *info)
+{
+	matroxfb_blank(blank, info);
+}
+
 static struct fb_ops matroxfb_ops = {
 	owner:		THIS_MODULE,
 	fb_open:	matroxfb_open,
@@ -1178,7 +1350,7 @@
 
 int matroxfb_switch(int con, struct fb_info *info)
 {
-#define minfo ((struct matrox_fb_info*)info)
+#define minfo (list_entry(info, struct matrox_fb_info, fbcon))
 	struct fb_cmap* cmap;
 	struct display *p;
 
@@ -1226,40 +1398,6 @@
 #undef minfo
 }
 
-/* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */
-
-static void matroxfb_blank(int blank, struct fb_info *info)
-{
-#define minfo ((struct matrox_fb_info*)info)
-	int seq;
-	int crtc;
-	CRITFLAGS
-
-	DBG("matroxfb_blank")
-
-	if (ACCESS_FBINFO(dead))
-		return;
-
-	switch (blank) {
-		case 1:  seq = 0x20; crtc = 0x00; break; /* works ??? */
-		case 2:  seq = 0x20; crtc = 0x10; break;
-		case 3:  seq = 0x20; crtc = 0x20; break;
-		case 4:  seq = 0x20; crtc = 0x30; break;
-		default: seq = 0x00; crtc = 0x00; break;
-	}
-
-	CRITBEGIN
-
-	mga_outb(M_SEQ_INDEX, 1);
-	mga_outb(M_SEQ_DATA, (mga_inb(M_SEQ_DATA) & ~0x20) | seq);
-	mga_outb(M_EXTVGA_INDEX, 1);
-	mga_outb(M_EXTVGA_DATA, (mga_inb(M_EXTVGA_DATA) & ~0x30) | crtc);
-
-	CRITEND
-
-#undef minfo
-}
-
 #define RSDepth(X)	(((X) >> 8) & 0x0F)
 #define RS8bpp		0x1
 #define RS15bpp		0x2
@@ -1407,10 +1545,10 @@
 #define DEVF_VIDEO64BIT		0x0001
 #define	DEVF_SWAPS		0x0002
 #define DEVF_SRCORG		0x0004
-#define DEVF_BOTHDACS		0x0008	/* put CRTC1 on both outputs by default */
+#define DEVF_DUALHEAD		0x0008
 #define DEVF_CROSS4MB		0x0010
 #define DEVF_TEXT4B		0x0020
-#define DEVF_DDC_8_2		0x0040
+/* #define DEVF_recycled	0x0040	*/
 /* #define DEVF_recycled	0x0080	*/
 #define DEVF_SUPPORT32MB	0x0100
 #define DEVF_ANY_VXRES		0x0200
@@ -1420,14 +1558,14 @@
 #define DEVF_PANELLINK_CAPABLE	0x2000
 #define DEVF_G450DAC		0x4000
 
-#define DEVF_GCORE	(DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB | DEVF_DDC_8_2)
-#define DEVF_G2CORE	(DEVF_GCORE | DEVF_ANY_VXRES | DEVF_MAVEN_CAPABLE | DEVF_PANELLINK_CAPABLE | DEVF_SRCORG)
+#define DEVF_GCORE	(DEVF_VIDEO64BIT | DEVF_SWAPS | DEVF_CROSS4MB)
+#define DEVF_G2CORE	(DEVF_GCORE | DEVF_ANY_VXRES | DEVF_MAVEN_CAPABLE | DEVF_PANELLINK_CAPABLE | DEVF_SRCORG | DEVF_DUALHEAD)
 #define DEVF_G100	(DEVF_GCORE) /* no doc, no vxres... */
 #define DEVF_G200	(DEVF_G2CORE)
 #define DEVF_G400	(DEVF_G2CORE | DEVF_SUPPORT32MB | DEVF_TEXT16B | DEVF_CRTC2)
 /* if you'll find how to drive DFP... */
-#define DEVF_G450	(DEVF_GCORE | DEVF_ANY_VXRES | DEVF_SUPPORT32MB | DEVF_TEXT16B | DEVF_CRTC2 | DEVF_G450DAC | DEVF_SRCORG)
-#define DEVF_G550	(DEVF_G450 | DEVF_BOTHDACS)
+#define DEVF_G450	(DEVF_GCORE | DEVF_ANY_VXRES | DEVF_SUPPORT32MB | DEVF_TEXT16B | DEVF_CRTC2 | DEVF_G450DAC | DEVF_SRCORG | DEVF_DUALHEAD)
+#define DEVF_G550	(DEVF_G450)
 
 static struct board {
 	unsigned short vendor, device, rev, svid, sid;
@@ -1621,27 +1759,24 @@
 		ACCESS_FBINFO(devflags.text_type_aux) = FB_AUX_TEXT_MGA_STEP8;
 	}
 #ifdef CONFIG_FB_MATROX_32MB
-	ACCESS_FBINFO(devflags.support32MB) = b->flags & DEVF_SUPPORT32MB;
+	ACCESS_FBINFO(devflags.support32MB) = (b->flags & DEVF_SUPPORT32MB) != 0;
 #endif
 	ACCESS_FBINFO(devflags.precise_width) = !(b->flags & DEVF_ANY_VXRES);
-	ACCESS_FBINFO(devflags.crtc2) = b->flags & DEVF_CRTC2;
-	ACCESS_FBINFO(devflags.maven_capable) = b->flags & DEVF_MAVEN_CAPABLE;
+	ACCESS_FBINFO(devflags.crtc2) = (b->flags & DEVF_CRTC2) != 0;
+	ACCESS_FBINFO(devflags.maven_capable) = (b->flags & DEVF_MAVEN_CAPABLE) != 0;
+	ACCESS_FBINFO(devflags.dualhead) = (b->flags & DEVF_DUALHEAD) != 0;
 	if (b->flags & DEVF_PANELLINK_CAPABLE) {
-		ACCESS_FBINFO(output.all) |= MATROXFB_OUTPUT_CONN_DFP;
+		ACCESS_FBINFO(outputs[2]).data = MINFO;
+		ACCESS_FBINFO(outputs[2]).output = &panellink_output;
 		if (dfp)
-			ACCESS_FBINFO(output.ph) |= MATROXFB_OUTPUT_CONN_DFP;
-	}
-	if (b->flags & DEVF_BOTHDACS) {
-#ifdef CONFIG_FB_MATROX_G450	
-		ACCESS_FBINFO(output.all) |= MATROXFB_OUTPUT_CONN_SECONDARY;
-		ACCESS_FBINFO(output.ph) |= MATROXFB_OUTPUT_CONN_SECONDARY;
-#else
-		printk(KERN_INFO "Only digital output of G550 is now working (in analog mode). Enable G450 support in\n");
-		printk(KERN_INFO "kernel configuration if you have analog monitor connected to G550 analog output.\n");
-#endif
+			ACCESS_FBINFO(outputs[2]).src = MATROXFB_SRC_CRTC1;
+		else
+			ACCESS_FBINFO(outputs[2]).src = MATROXFB_SRC_NONE;
+		ACCESS_FBINFO(outputs[2]).mode = MATROXFB_OUTPUT_MODE_MONITOR;
+		ACCESS_FBINFO(devflags.panellink) = 1;
 	}
 	ACCESS_FBINFO(devflags.dfp_type) = dfp_type;
-	ACCESS_FBINFO(devflags.g450dac) = b->flags & DEVF_G450DAC;
+	ACCESS_FBINFO(devflags.g450dac) = (b->flags & DEVF_G450DAC) != 0;
 	ACCESS_FBINFO(devflags.textstep) = ACCESS_FBINFO(devflags.vgastep) * ACCESS_FBINFO(devflags.textmode);
 	ACCESS_FBINFO(devflags.textvram) = 65536 / ACCESS_FBINFO(devflags.textmode);
 
@@ -1747,6 +1882,7 @@
 
 	if (!ACCESS_FBINFO(devflags.novga))
 		request_region(0x3C0, 32, "matrox");
+	matroxfb_g450_connect(PMINFO2);
 	ACCESS_FBINFO(hw_switch->reset(PMINFO2));
 
 	ACCESS_FBINFO(fbcon.monspecs.hfmin) = 0;
@@ -1775,7 +1911,7 @@
 	ACCESS_FBINFO(fbcon.disp) = d;
 	ACCESS_FBINFO(fbcon.switch_con) = &matroxfb_switch;
 	ACCESS_FBINFO(fbcon.updatevar) = &matroxfb_updatevar;
-	ACCESS_FBINFO(fbcon.blank) = &matroxfb_blank;
+	ACCESS_FBINFO(fbcon.blank) = &matroxfb_blank24;
 	/* after __init time we are like module... no logo */
 	ACCESS_FBINFO(fbcon.flags) = hotplug ? FBINFO_FLAG_MODULE : FBINFO_FLAG_DEFAULT;
 	ACCESS_FBINFO(video.len_usable) &= PAGE_MASK;
@@ -1907,6 +2043,7 @@
 	}
 	return 0;
 failVideoIO:;
+	matroxfb_g450_shutdown(PMINFO2);
 	mga_iounmap(ACCESS_FBINFO(video.vbase));
 failCtrlIO:;
 	mga_iounmap(ACCESS_FBINFO(mmio.vbase));
@@ -2094,10 +2231,6 @@
 	init_rwsem(&ACCESS_FBINFO(crtc2.lock));
 	init_rwsem(&ACCESS_FBINFO(altout.lock));
 
-	ACCESS_FBINFO(output.all) = MATROXFB_OUTPUT_CONN_PRIMARY;
-	ACCESS_FBINFO(output.ph) = MATROXFB_OUTPUT_CONN_PRIMARY;
-	ACCESS_FBINFO(output.sh) = 0;
-
 	err = initMatrox2(PMINFO d, b);
 	if (!err) {
 #ifndef CONFIG_FB_MATROX_MULTIHEAD
@@ -2511,8 +2644,8 @@
 
 /* *************************** init module code **************************** */
 
-MODULE_AUTHOR("(c) 1998-2001 Petr Vandrovec <vandrove@vc.cvut.cz>");
-MODULE_DESCRIPTION("Accelerated FBDev driver for Matrox Millennium/Mystique/G100/G200/G400/G450");
+MODULE_AUTHOR("(c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz>");
+MODULE_DESCRIPTION("Accelerated FBDev driver for Matrox Millennium/Mystique/G100/G200/G400/G450/G550");
 MODULE_LICENSE("GPL");
 
 MODULE_PARM(mem, "i");
diff -urN linux-2.4.21-pre2/drivers/video/matrox/matroxfb_base.h linux-2.4.21-pre3/drivers/video/matrox/matroxfb_base.h
--- linux-2.4.21-pre2/drivers/video/matrox/matroxfb_base.h	Fri Aug  2 17:39:45 2002
+++ linux-2.4.21-pre3/drivers/video/matrox/matroxfb_base.h	Mon Jan  6 16:32:32 2003
@@ -291,6 +291,8 @@
 
 struct my_timming {
 	unsigned int pixclock;
+	int mnp;
+	unsigned int crtc;
 	unsigned int HDisplay;
 	unsigned int HSyncStart;
 	unsigned int HSyncEnd;
@@ -367,6 +369,10 @@
 	u_int16_t hcorr;
 };
 
+struct matrox_crtc2 {
+	u_int32_t ctl, hparam, hsync, vparam, vsync, preload, datactl;
+};
+
 struct matrox_hw_state {
 	u_int32_t	MXoptionReg;
 	unsigned char	DACclk[6];
@@ -384,10 +390,7 @@
 	/* TVOut only */
 	struct mavenregs	maven;
 
-	/* CRTC2 only */
-	/* u_int32_t	TBD */
-
-	unsigned int	vidclk;
+	struct matrox_crtc2	crtc2;
 };
 
 struct matrox_accel_data {
@@ -398,16 +401,28 @@
 	u_int32_t	m_opmode;
 };
 
+struct matroxfb_queryctrl;
+struct matroxfb_control;
+
 struct matrox_altout {
+	struct module	*owner;
+	const char	*name;
 	int		(*compute)(void* altout_dev, struct my_timming* input);
 	int		(*program)(void* altout_dev);
 	int		(*start)(void* altout_dev);
-	void		(*incuse)(void* altout_dev);
-	void		(*decuse)(void* altout_dev);
-	int		(*setmode)(void* altout_dev, u_int32_t mode);
-	int		(*getmode)(void* altout_dev, u_int32_t* mode);
+	int		(*verifymode)(void* altout_dev, u_int32_t mode);
+	int		(*getqueryctrl)(void* altout_dev,
+					struct matroxfb_queryctrl* ctrl);
+	int		(*getctrl)(void* altout_dev, 
+				   struct matroxfb_control* ctrl);
+	int		(*setctrl)(void* altout_dev, 
+				   struct matroxfb_control* ctrl);
 };
 
+#define MATROXFB_SRC_NONE	0
+#define MATROXFB_SRC_CRTC1	1
+#define MATROXFB_SRC_CRTC2	2
+
 enum mga_chip { MGA_2064, MGA_2164, MGA_1064, MGA_1164, MGA_G100, MGA_G200, MGA_G400, MGA_G450, MGA_G550 };
 
 struct matrox_bios {
@@ -424,9 +439,9 @@
 
 struct matrox_switch;
 struct matroxfb_driver;
+struct matroxfb_dh_fb_info;
 
 struct matrox_fb_info {
-	/* fb_info must be first */
 	struct fb_info		fbcon;
 
 	struct list_head	next_fb;
@@ -442,21 +457,19 @@
 	struct pci_dev*		pcidev;
 
 	struct {
-		u_int32_t	all;
-		u_int32_t	ph;
-		u_int32_t	sh;
-			      } output;
-	struct matrox_altout*	primout;
-	struct {
-	struct fb_info*		info;
 	struct rw_semaphore	lock;
-			      } crtc2;
 	struct {
-	struct matrox_altout*	output;
-	void*			device;
-	struct rw_semaphore	lock;
+		int brightness, contrast, saturation, hue, gamma;
+		int testout, deflicker;
+				} tvo_params;
 			      } altout;
-
+#define MATROXFB_MAX_OUTPUTS		3
+	struct {
+	unsigned int		src;
+	struct matrox_altout*	output;
+	void*			data;
+	unsigned int		mode;
+			      } outputs[MATROXFB_MAX_OUTPUTS];
 #define MATROXFB_MAX_FB_DRIVERS		5
 	struct matroxfb_driver* (drivers[MATROXFB_MAX_FB_DRIVERS]);
 	void*			(drivers_data[MATROXFB_MAX_FB_DRIVERS]);
@@ -545,6 +558,8 @@
 		int		memtype;
 		int		g450dac;
 		int		dfp_type;
+		int		panellink;	/* G400 DFP possible (not G450/G550) */
+		int		dualhead;
 		unsigned int	fbResource;
 			      } devflags;
 	struct display_switch	dispsw;
@@ -561,6 +576,16 @@
 			      } cursor;
 	struct matrox_bios	bios;
 	struct {
+		unsigned int	pixclock;
+		int		mnp;
+			      } crtc1;
+	struct {
+		unsigned int 	pixclock;
+		int		mnp;
+	struct matroxfb_dh_fb_info*	info;
+	struct rw_semaphore	lock;
+			      } crtc2;
+	struct {
 		struct matrox_pll_limits	pixel;
 		struct matrox_pll_limits	system;
 		struct matrox_pll_limits	video;
@@ -625,7 +650,7 @@
 #define PMINFO   PMINFO2 ,
 
 static inline struct matrox_fb_info* mxinfo(const struct display* p) {
-	return (struct matrox_fb_info*)p->fb_info;
+	return list_entry(p->fb_info, struct matrox_fb_info, fbcon);
 }
 
 #define PMXINFO(p)	   mxinfo(p),
diff -urN linux-2.4.21-pre2/drivers/video/matrox/matroxfb_crtc2.c linux-2.4.21-pre3/drivers/video/matrox/matroxfb_crtc2.c
--- linux-2.4.21-pre2/drivers/video/matrox/matroxfb_crtc2.c	Fri Aug  2 17:39:45 2002
+++ linux-2.4.21-pre3/drivers/video/matrox/matroxfb_crtc2.c	Mon Jan  6 16:32:32 2003
@@ -2,11 +2,11 @@
  *
  * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200, G400 and G450.
  *
- * (c) 1998-2001 Petr Vandrovec <vandrove@vc.cvut.cz>
+ * (c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz>
  *
  * Portions Copyright (c) 2001 Matrox Graphics Inc.
  *
- * Version: 1.62 2001/11/29
+ * Version: 1.64 2002/06/10
  *
  */
 
@@ -28,8 +28,8 @@
 
 static int matroxfb_dh_getcolreg(unsigned regno, unsigned *red, unsigned *green,
 		unsigned *blue, unsigned *transp, struct fb_info* info) {
-#define m2info ((struct matroxfb_dh_fb_info*)info)
-	if (regno > 16)
+#define m2info (list_entry(info, struct matroxfb_dh_fb_info, fbcon))
+	if (regno >= 16)
 		return 1;
 	*red = m2info->palette[regno].red;
 	*blue = m2info->palette[regno].blue;
@@ -41,10 +41,10 @@
 
 static int matroxfb_dh_setcolreg(unsigned regno, unsigned red, unsigned green,
 		unsigned blue, unsigned transp, struct fb_info* info) {
-#define m2info ((struct matroxfb_dh_fb_info*)info)
+#define m2info (list_entry(info, struct matroxfb_dh_fb_info, fbcon))
 	struct display* p;
 
-	if (regno > 16)
+	if (regno >= 16)
 		return 1;
 	m2info->palette[regno].red = red;
 	m2info->palette[regno].blue = blue;
@@ -84,86 +84,129 @@
 #undef m2info
 }
 
-static void do_install_cmap(struct matroxfb_dh_fb_info* m2info, struct display* p) {
-	if (p->cmap.len)
-		fb_set_cmap(&p->cmap, 1, matroxfb_dh_setcolreg, &m2info->fbcon);
+static inline void my_install_cmap(struct matroxfb_dh_fb_info* m2info)
+{
+	/* Do not touch this code if you do not understand what it does! */
+	/* Never try to use do_install_cmap() instead. It is crap. */
+	struct fb_cmap* cmap = &m2info->currcon_display->cmap;
+	
+	if (cmap->len)
+		fb_set_cmap(cmap, 1, matroxfb_dh_setcolreg, &m2info->fbcon);
 	else
 		fb_set_cmap(fb_default_cmap(16), 1, matroxfb_dh_setcolreg, &m2info->fbcon);
 }
 
+
 static void matroxfb_dh_restore(struct matroxfb_dh_fb_info* m2info,
 		struct my_timming* mt,
 		struct display* p,
 		int mode,
 		unsigned int pos) {
-	u_int32_t tmp;
+	struct matrox_crtc2 c2;
 	MINFO_FROM(m2info->primary_dev);
 
 	switch (mode) {
 		case 15:
-			tmp = 0x00200000;
+			c2.ctl = 0x00200000;
 			break;
 		case 16:
-			tmp = 0x00400000;
+			c2.ctl = 0x00400000;
 			break;
 /*		case 32: */
 		default:
-			tmp = 0x00800000;
+			c2.ctl = 0x00800000;
 			break;
 	}
-
-	if (ACCESS_FBINFO(output.sh)) {
-		tmp |= 0x00000001;	/* enable CRTC2 */
-
-		if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_SECONDARY) {
-			if (ACCESS_FBINFO(devflags.g450dac)) {
-				tmp |= 0x00000006; /* source from secondary pixel PLL */
-				/* no vidrst */
-			} else {
-				tmp |= 0x00000002; /* source from VDOCLK */
-				tmp |= 0xC0000000; /* enable vvidrst & hvidrst */
-				/* MGA TVO is our clock source */
+	c2.ctl |= 0x00000001;	/* enable CRTC2 */
+	c2.datactl = 0;
+	if (ACCESS_FBINFO(outputs[1]).src == MATROXFB_SRC_CRTC2) {
+		if (ACCESS_FBINFO(devflags.g450dac)) {
+			c2.ctl |= 0x00000006; /* source from secondary pixel PLL */
+			/* no vidrst when in monitor mode */
+			if (ACCESS_FBINFO(outputs[1]).mode != MATROXFB_OUTPUT_MODE_MONITOR) {
+				c2.ctl |=  0xC0001000; /* Enable H/V vidrst */
 			}
-		} else if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_PRIMARY) {
-			tmp |= 0x00000004; /* source from pixclock */
-			/* PIXPLL is our clock source */
-		}
-
-		if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_PRIMARY)
-			tmp |= 0x00100000;	/* connect CRTC2 to DAC */
+		} else {
+			c2.ctl |= 0x00000002; /* source from VDOCLK */
+			c2.ctl |= 0xC0000000; /* enable vvidrst & hvidrst */
+			/* MGA TVO is our clock source */
+		}
+	} else if (ACCESS_FBINFO(outputs[0]).src == MATROXFB_SRC_CRTC2) {
+		c2.ctl |= 0x00000004; /* source from pixclock */
+		/* PIXPLL is our clock source */
+	}
+	if (ACCESS_FBINFO(outputs[0]).src == MATROXFB_SRC_CRTC2) {
+		c2.ctl |= 0x00100000;	/* connect CRTC2 to DAC */
 	}
 	if (mt->interlaced) {
-		tmp |= 0x02000000;	/* interlaced, second field is bigger, as G450 apparently ignores it */
+		c2.ctl |= 0x02000000;	/* interlaced, second field is bigger, as G450 apparently ignores it */
 		mt->VDisplay >>= 1;
 		mt->VSyncStart >>= 1;
 		mt->VSyncEnd >>= 1;
 		mt->VTotal >>= 1;
 	}
-	mga_outl(0x3C10, tmp | 0x10000000);	/* depth and so on... 0x10000000 is VIDRST polarity */
-	mga_outl(0x3C14, ((mt->HDisplay - 8) << 16) | (mt->HTotal - 8));
-	mga_outl(0x3C18, ((mt->HSyncEnd - 8) << 16) | (mt->HSyncStart - 8));
-	mga_outl(0x3C1C, ((mt->VDisplay - 1) << 16) | (mt->VTotal - 1));
-	mga_outl(0x3C20, ((mt->VSyncEnd - 1) << 16) | (mt->VSyncStart - 1));
-	mga_outl(0x3C24, ((mt->VSyncStart) << 16) | (mt->HSyncStart));	/* preload */
+	if ((mt->HTotal & 7) == 2) {
+		c2.datactl |= 0x00000010;
+		mt->HTotal &= ~7;
+	}
+	c2.ctl |= 0x10000000;	/* 0x10000000 is VIDRST polarity */
+	c2.hparam = ((mt->HDisplay - 8) << 16) | (mt->HTotal - 8);
+	c2.hsync = ((mt->HSyncEnd - 8) << 16) | (mt->HSyncStart - 8);
+	c2.vparam = ((mt->VDisplay - 1) << 16) | (mt->VTotal - 1);
+	c2.vsync = ((mt->VSyncEnd - 1) << 16) | (mt->VSyncStart - 1);
+	c2.preload = ((mt->VSyncStart) << 16) | (mt->HSyncStart);
+	mga_outl(0x3C14, c2.hparam);
+	mga_outl(0x3C18, c2.hsync);
+	mga_outl(0x3C1C, c2.vparam);
+	mga_outl(0x3C20, c2.vsync);
+	mga_outl(0x3C24, c2.preload);
 	{
 		u_int32_t linelen = p->var.xres_virtual * (p->var.bits_per_pixel >> 3);
-		if (mt->interlaced) {
+		if (c2.ctl & 0x02000000) {
 			/* field #0 is smaller, so... */
 			mga_outl(0x3C2C, pos);			/* field #1 vmemory start */
 			mga_outl(0x3C28, pos + linelen);	/* field #0 vmemory start */
 			linelen <<= 1;
+			m2info->interlaced = 1;
 		} else {
 			mga_outl(0x3C28, pos);		/* vmemory start */
+			m2info->interlaced = 0;
 		}
 		mga_outl(0x3C40, linelen);
 	}
-	tmp = 0x0FFF0000;		/* line compare */
-	if (mt->sync & FB_SYNC_HOR_HIGH_ACT)
-		tmp |= 0x00000100;
-	if (mt->sync & FB_SYNC_VERT_HIGH_ACT)
-		tmp |= 0x00000200;
-	mga_outl(0x3C44, tmp);
-	mga_outl(0x3C4C, 0);		/* data control */
+	mga_outl(0x3C4C, c2.datactl);		/* data control */
+	if (c2.ctl & 0x02000000) {
+		int i;
+
+		mga_outl(0x3C10, c2.ctl & ~0x02000000);
+                for (i = 0; i < 2; i++) {
+                        unsigned int nl;
+                        unsigned int lastl = 0;
+
+                        while ((nl = mga_inl(0x3C48) & 0xFFF) >= lastl) {
+                                lastl = nl;
+                        }
+                }
+	}
+        mga_outl(0x3C10, c2.ctl);
+	ACCESS_FBINFO(hw).crtc2.ctl = c2.ctl;
+	{
+		u_int32_t tmp;
+
+		tmp = 0x0FFF0000;		/* line compare */
+		if (mt->sync & FB_SYNC_HOR_HIGH_ACT)
+			tmp |= 0x00000100;
+		if (mt->sync & FB_SYNC_VERT_HIGH_ACT)
+			tmp |= 0x00000200;
+		mga_outl(0x3C44, tmp);
+	}
+}
+
+static void matroxfb_dh_disable(struct matroxfb_dh_fb_info* m2info) {
+	MINFO_FROM(m2info->primary_dev);
+
+	mga_outl(0x3C10, 0x00000004);	/* disable CRTC2, CRTC1->DAC1, PLL as clock source */
+	ACCESS_FBINFO(hw).crtc2.ctl = 0x00000004;
 }
 
 static void matroxfb_dh_cfbX_init(struct matroxfb_dh_fb_info* m2info,
@@ -176,19 +219,18 @@
 	unsigned int pos;
 	unsigned int linelen;
 	unsigned int pixelsize;
+	MINFO_FROM(m2info->primary_dev);
 
-#define minfo (m2info->primary_dev)
 	pixelsize = var->bits_per_pixel >> 3;
 	linelen = var->xres_virtual * pixelsize;
 	pos = var->yoffset * linelen + var->xoffset * pixelsize;
 	pos += m2info->video.offbase;
-	if (var->vmode & FB_VMODE_INTERLACED) {
+	if (m2info->interlaced) {
 		mga_outl(0x3C2C, pos);
 		mga_outl(0x3C28, pos + linelen);
 	} else {
 		mga_outl(0x3C28, pos);
 	}
-#undef minfo
 }
 
 static int matroxfb_dh_decode_var(struct matroxfb_dh_fb_info* m2info,
@@ -292,7 +334,7 @@
 }
 
 static int matroxfb_dh_open(struct fb_info* info, int user) {
-#define m2info ((struct matroxfb_dh_fb_info*)info)
+#define m2info (list_entry(info, struct matroxfb_dh_fb_info, fbcon))
 	MINFO_FROM(m2info->primary_dev);
 
 	if (MINFO) {
@@ -305,7 +347,7 @@
 }
 
 static int matroxfb_dh_release(struct fb_info* info, int user) {
-#define m2info ((struct matroxfb_dh_fb_info*)info)
+#define m2info (list_entry(info, struct matroxfb_dh_fb_info, fbcon))
 	MINFO_FROM(m2info->primary_dev);
 
 	if (MINFO) {
@@ -316,7 +358,7 @@
 
 static int matroxfb_dh_get_fix(struct fb_fix_screeninfo* fix, int con,
 		struct fb_info* info) {
-#define m2info ((struct matroxfb_dh_fb_info*)info)
+#define m2info (list_entry(info, struct matroxfb_dh_fb_info, fbcon))
 	struct display* p;
 
 	if (con >= 0)
@@ -345,7 +387,7 @@
 
 static int matroxfb_dh_get_var(struct fb_var_screeninfo* var, int con,
 		struct fb_info* info) {
-#define m2info ((struct matroxfb_dh_fb_info*)info)
+#define m2info (list_entry(info, struct matroxfb_dh_fb_info, fbcon))
 	if (con < 0)
 		*var = m2info->fbcon.disp->var;
 	else
@@ -356,7 +398,7 @@
 
 static int matroxfb_dh_set_var(struct fb_var_screeninfo* var, int con,
 		struct fb_info* info) {
-#define m2info ((struct matroxfb_dh_fb_info*)info)
+#define m2info (list_entry(info, struct matroxfb_dh_fb_info, fbcon))
 	struct display* p;
 	int chgvar;
 	int visual;
@@ -390,7 +432,7 @@
 		chgvar = 0;
 	p->var = *var;
 	/* cmap */
-	p->screen_base = vaddr_va(m2info->video.vbase);
+	p->screen_base = m2info->fbcon.screen_base = vaddr_va(m2info->video.vbase);
 	p->visual = visual;
 	p->ypanstep = 1;
 	p->ywrapstep = 0;
@@ -405,48 +447,52 @@
 	if (con == m2info->currcon) {
 		struct my_timming mt;
 		unsigned int pos;
+		int out;
+		int cnt;
 
 		matroxfb_var2my(var, &mt);
+		mt.crtc = MATROXFB_SRC_CRTC2;
 		/* CRTC2 delay */
 		mt.delay = 34;
 
 		pos = (var->yoffset * var->xres_virtual + var->xoffset) * var->bits_per_pixel >> 3;
 		pos += m2info->video.offbase;
-		DAC1064_global_init(PMINFO2);
-		if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_PRIMARY) {
-			if (ACCESS_FBINFO(primout))
-				ACCESS_FBINFO(primout)->compute(MINFO, &mt);
+		cnt = 0;
+		down_read(&ACCESS_FBINFO(altout).lock);
+		for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
+			if (ACCESS_FBINFO(outputs[out]).src == MATROXFB_SRC_CRTC2) {
+				cnt++;
+				if (ACCESS_FBINFO(outputs[out]).output->compute) {
+					ACCESS_FBINFO(outputs[out]).output->compute(ACCESS_FBINFO(outputs[out]).data, &mt);
+				}
+			}
 		}
-		if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_SECONDARY) {
-			down_read(&ACCESS_FBINFO(altout.lock));
-			if (ACCESS_FBINFO(altout.output))
-				ACCESS_FBINFO(altout.output)->compute(ACCESS_FBINFO(altout.device), &mt);
-			up_read(&ACCESS_FBINFO(altout.lock));
+		ACCESS_FBINFO(crtc2).pixclock = mt.pixclock;
+		ACCESS_FBINFO(crtc2).mnp = mt.mnp;
+		up_read(&ACCESS_FBINFO(altout).lock);
+		if (cnt) {
+			matroxfb_dh_restore(m2info, &mt, p, mode, pos);
+		} else {
+			matroxfb_dh_disable(m2info);
 		}
-		matroxfb_dh_restore(m2info, &mt, p, mode, pos);
+		DAC1064_global_init(PMINFO2);
 		DAC1064_global_restore(PMINFO2);
-		if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_PRIMARY) {
-			if (ACCESS_FBINFO(primout))
-				ACCESS_FBINFO(primout)->program(MINFO);
-		}
-		if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_SECONDARY) {
-			down_read(&ACCESS_FBINFO(altout.lock));
-			if (ACCESS_FBINFO(altout.output))
-				ACCESS_FBINFO(altout.output)->program(ACCESS_FBINFO(altout.device));
-			up_read(&ACCESS_FBINFO(altout.lock));
-		}
-		if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_PRIMARY) {
-			if (ACCESS_FBINFO(primout))
-				ACCESS_FBINFO(primout)->start(MINFO);
+		down_read(&ACCESS_FBINFO(altout).lock);
+		for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
+			if (ACCESS_FBINFO(outputs[out]).src == MATROXFB_SRC_CRTC2 &&
+			    ACCESS_FBINFO(outputs[out]).output->program) {
+				ACCESS_FBINFO(outputs[out]).output->program(ACCESS_FBINFO(outputs[out]).data);
+			}
 		}
-		if (ACCESS_FBINFO(output.sh) & MATROXFB_OUTPUT_CONN_SECONDARY) {
-			down_read(&ACCESS_FBINFO(altout.lock));
-			if (ACCESS_FBINFO(altout.output))
-				ACCESS_FBINFO(altout.output)->start(ACCESS_FBINFO(altout.device));
-			up_read(&ACCESS_FBINFO(altout.lock));
+		for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
+			if (ACCESS_FBINFO(outputs[out]).src == MATROXFB_SRC_CRTC2 &&
+			    ACCESS_FBINFO(outputs[out]).output->start) {
+				ACCESS_FBINFO(outputs[out]).output->start(ACCESS_FBINFO(outputs[out]).data);
+			}
 		}
+		up_read(&ACCESS_FBINFO(altout).lock);
 		matroxfb_dh_cfbX_init(m2info, p);
-		do_install_cmap(m2info, p);
+		my_install_cmap(m2info);
 	}
 	return 0;
 #undef m2info
@@ -454,7 +500,7 @@
 
 static int matroxfb_dh_get_cmap(struct fb_cmap* cmap, int kspc, int con,
 		struct fb_info* info) {
-#define m2info ((struct matroxfb_dh_fb_info*)info)
+#define m2info (list_entry(info, struct matroxfb_dh_fb_info, fbcon))
 	struct display* dsp;
 
 	if (con < 0)
@@ -473,7 +519,7 @@
 
 static int matroxfb_dh_set_cmap(struct fb_cmap* cmap, int kspc, int con,
 		struct fb_info* info) {
-#define m2info ((struct matroxfb_dh_fb_info*)info)
+#define m2info (list_entry(info, struct matroxfb_dh_fb_info, fbcon))
 	struct display* dsp;
 
 	if (con < 0)
@@ -497,7 +543,7 @@
 
 static int matroxfb_dh_pan_display(struct fb_var_screeninfo* var, int con,
 		struct fb_info* info) {
-#define m2info ((struct matroxfb_dh_fb_info*)info)
+#define m2info (list_entry(info, struct matroxfb_dh_fb_info, fbcon))
 	if (var->xoffset + fb_display[con].var.xres > fb_display[con].var.xres_virtual ||
 	    var->yoffset + fb_display[con].var.yres > fb_display[con].var.yres_virtual)
 		return -EINVAL;
@@ -530,7 +576,7 @@
 		unsigned long arg,
 		int con,
 		struct fb_info* info) {
-#define m2info ((struct matroxfb_dh_fb_info*)info)
+#define m2info (list_entry(info, struct matroxfb_dh_fb_info, fbcon))
 	MINFO_FROM(m2info->primary_dev);
 
 	DBG("matroxfb_crtc2_ioctl")
@@ -557,38 +603,84 @@
 		case MATROXFB_SET_OUTPUT_CONNECTION:
 			{
 				u_int32_t tmp;
+				int out;
+				int changes;
 
 				if (get_user(tmp, (u_int32_t*)arg))
 					return -EFAULT;
-				if (tmp & ~ACCESS_FBINFO(output.all))
-					return -EINVAL;
-				if (tmp & ACCESS_FBINFO(output.ph))
-					return -EINVAL;
-				if (tmp & MATROXFB_OUTPUT_CONN_DFP)
-					return -EINVAL;
-				if ((ACCESS_FBINFO(output.ph) & MATROXFB_OUTPUT_CONN_DFP) && tmp)
-					return -EINVAL;
-				if (tmp == ACCESS_FBINFO(output.sh))
+				for (out = 0; out < 32; out++) {
+					if (tmp & (1 << out)) {
+						if (out >= MATROXFB_MAX_OUTPUTS)
+							return -ENXIO;
+						if (!ACCESS_FBINFO(outputs[out]).output)
+							return -ENXIO;
+						switch (ACCESS_FBINFO(outputs[out]).src) {
+							case MATROXFB_SRC_NONE:
+							case MATROXFB_SRC_CRTC2:
+								break;
+							default:
+								return -EBUSY;
+						}
+					}
+				}
+				if (ACCESS_FBINFO(devflags.panellink)) {
+					if (tmp & MATROXFB_OUTPUT_CONN_DFP)
+						return -EINVAL;
+					if ((ACCESS_FBINFO(outputs[2]).src == MATROXFB_SRC_CRTC1) && tmp)
+						return -EBUSY;
+				}
+				changes = 0;
+				for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
+					if (tmp & (1 << out)) {
+						if (ACCESS_FBINFO(outputs[out]).src != MATROXFB_SRC_CRTC2) {
+							changes = 1;
+							ACCESS_FBINFO(outputs[out]).src = MATROXFB_SRC_CRTC2;
+						}
+					} else if (ACCESS_FBINFO(outputs[out]).src == MATROXFB_SRC_CRTC2) {
+						changes = 1;
+						ACCESS_FBINFO(outputs[out]).src = MATROXFB_SRC_NONE;
+					}
+				}
+				if (!changes)
 					return 0;
-				ACCESS_FBINFO(output.sh) = tmp;
 				matroxfb_dh_switch(m2info->currcon, info);
 				return 0;
 			}
 		case MATROXFB_GET_OUTPUT_CONNECTION:
 			{
-				if (put_user(ACCESS_FBINFO(output.sh), (u_int32_t*)arg))
+				u_int32_t conn = 0;
+				int out;
+
+				for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
+					if (ACCESS_FBINFO(outputs[out]).src == MATROXFB_SRC_CRTC2) {
+						conn |= 1 << out;
+					}
+				}
+				if (put_user(conn, (u_int32_t*)arg))
 					return -EFAULT;
 				return 0;
 			}
 		case MATROXFB_GET_AVAILABLE_OUTPUTS:
 			{
-				u_int32_t tmp;
+				u_int32_t tmp = 0;
+				int out;
 
-				/* we do not support DFP from CRTC2 */
-				tmp = ACCESS_FBINFO(output.all) & ~ACCESS_FBINFO(output.ph) & ~MATROXFB_OUTPUT_CONN_DFP;
-				/* CRTC1 in DFP mode disables CRTC2 at all (I know, I'm lazy) */
-				if (ACCESS_FBINFO(output.ph) & MATROXFB_OUTPUT_CONN_DFP)
-					tmp = 0;
+				for (out = 0; out < MATROXFB_MAX_OUTPUTS; out++) {
+					if (ACCESS_FBINFO(outputs[out]).output) {
+						switch (ACCESS_FBINFO(outputs[out]).src) {
+							case MATROXFB_SRC_NONE:
+							case MATROXFB_SRC_CRTC2:
+								tmp |= 1 << out;
+								break;
+						}
+					}
+				}
+				if (ACCESS_FBINFO(devflags.panellink)) {
+					tmp &= ~MATROXFB_OUTPUT_CONN_DFP;
+					if (ACCESS_FBINFO(outputs[2]).src == MATROXFB_SRC_CRTC1) {
+						tmp = 0;
+					}
+				}
 				if (put_user(tmp, (u_int32_t*)arg))
 					return -EFAULT;
 				return 0;
@@ -598,6 +690,24 @@
 #undef m2info
 }
 
+static int matroxfb_dh_blank(int blank, struct fb_info* info) {
+#define m2info (list_entry(info, struct matroxfb_dh_fb_info, fbcon))
+	switch (blank) {
+		case 1:
+		case 2:
+		case 3:
+		case 4:
+		default:;
+	}
+	/* do something... */
+	return 0;
+#undef m2info
+}
+
+static void matroxfb_dh_blank24(int blank, struct fb_info* info) {
+	matroxfb_dh_blank(blank, info);
+}
+
 static struct fb_ops matroxfb_dh_ops = {
 	owner:		THIS_MODULE,
 	fb_open:	matroxfb_dh_open,
@@ -612,7 +722,7 @@
 };
 
 static int matroxfb_dh_switch(int con, struct fb_info* info) {
-#define m2info ((struct matroxfb_dh_fb_info*)info)
+#define m2info (list_entry(info, struct matroxfb_dh_fb_info, fbcon))
 	struct fb_cmap* cmap;
 	struct display* p;
 
@@ -635,25 +745,12 @@
 }
 
 static int matroxfb_dh_updatevar(int con, struct fb_info* info) {
-#define m2info ((struct matroxfb_dh_fb_info*)info)
+#define m2info (list_entry(info, struct matroxfb_dh_fb_info, fbcon))
 	matroxfb_dh_pan_var(m2info, &fb_display[con].var);
 	return 0;
 #undef m2info
 }
 
-static void matroxfb_dh_blank(int blank, struct fb_info* info) {
-#define m2info ((struct matroxfb_dh_fb_info*)info)
-	switch (blank) {
-		case 1:
-		case 2:
-		case 3:
-		case 4:
-		default:;
-	}
-	/* do something... */
-#undef m2info
-}
-
 static struct fb_var_screeninfo matroxfb_dh_defined = {
 		640,480,640,480,/* W,H, virtual W,H */
 		0,0,		/* offset */
@@ -692,7 +789,7 @@
 	m2info->fbcon.disp = d;
 	m2info->fbcon.switch_con = &matroxfb_dh_switch;
 	m2info->fbcon.updatevar = &matroxfb_dh_updatevar;
-	m2info->fbcon.blank = &matroxfb_dh_blank;
+	m2info->fbcon.blank = &matroxfb_dh_blank24;
 	m2info->fbcon.flags = FBINFO_FLAG_DEFAULT;
 	m2info->currcon = -1;
 	m2info->currcon_display = d;
@@ -722,11 +819,10 @@
 	/*
 	 *  If we have unused output, connect CRTC2 to it...
 	 */
-	if ((ACCESS_FBINFO(output.all) & MATROXFB_OUTPUT_CONN_SECONDARY) && 
-	   !(ACCESS_FBINFO(output.ph) & MATROXFB_OUTPUT_CONN_SECONDARY) &&
-	   !(ACCESS_FBINFO(output.ph) & MATROXFB_OUTPUT_CONN_DFP)) {
-		ACCESS_FBINFO(output.sh) |= MATROXFB_OUTPUT_CONN_SECONDARY;
-		ACCESS_FBINFO(output.sh) &= ~MATROXFB_OUTPUT_CONN_DFP;
+	if (ACCESS_FBINFO(outputs[1]).output &&
+	    ACCESS_FBINFO(outputs[1]).src == MATROXFB_SRC_NONE &&
+	    ACCESS_FBINFO(outputs[2]).src == MATROXFB_SRC_NONE) {
+		ACCESS_FBINFO(outputs[1]).src = MATROXFB_SRC_CRTC2;
 	}
 
 	matroxfb_dh_set_var(&matroxfb_dh_defined, -2, &m2info->fbcon);
@@ -739,7 +835,7 @@
 	}
 	down_write(&ACCESS_FBINFO(crtc2.lock));
 	oldcrtc2 = ACCESS_FBINFO(crtc2.info);
-	ACCESS_FBINFO(crtc2.info) = &m2info->fbcon;
+	ACCESS_FBINFO(crtc2.info) = m2info;
 	up_write(&ACCESS_FBINFO(crtc2.lock));
 	if (oldcrtc2) {
 		printk(KERN_ERR "matroxfb_crtc2: Internal consistency check failed: crtc2 already present: %p\n",
@@ -768,16 +864,16 @@
 #define minfo (m2info->primary_dev)
 	if (m2info->fbcon_registered) {
 		int id;
-		struct fb_info* crtc2;
+		struct matroxfb_dh_fb_info* crtc2;
 
 		down_write(&ACCESS_FBINFO(crtc2.lock));
 		crtc2 = ACCESS_FBINFO(crtc2.info);
-		if (crtc2 == &m2info->fbcon)
+		if (crtc2 == m2info)
 			ACCESS_FBINFO(crtc2.info) = NULL;
 		up_write(&ACCESS_FBINFO(crtc2.lock));
-		if (crtc2 != &m2info->fbcon) {
+		if (crtc2 != m2info) {
 			printk(KERN_ERR "matroxfb_crtc2: Internal consistency check failed: crtc2 mismatch at unload: %p != %p\n",
-				crtc2, &m2info->fbcon);
+				crtc2, m2info);
 			printk(KERN_ERR "matroxfb_crtc2: Expect kernel crash after module unload.\n");
 			return;
 		}
@@ -815,6 +911,7 @@
 
 static void matroxfb_crtc2_remove(struct matrox_fb_info* minfo, void* crtc2) {
 	matroxfb_dh_deregisterfb(crtc2);
+	kfree(crtc2);
 }
 
 static struct matroxfb_driver crtc2 = {
@@ -831,7 +928,7 @@
 	matroxfb_unregister_driver(&crtc2);
 }
 
-MODULE_AUTHOR("(c) 1999-2001 Petr Vandrovec <vandrove@vc.cvut.cz>");
+MODULE_AUTHOR("(c) 1999-2002 Petr Vandrovec <vandrove@vc.cvut.cz>");
 MODULE_DESCRIPTION("Matrox G400 CRTC2 driver");
 MODULE_LICENSE("GPL");
 module_init(matroxfb_crtc2_init);
diff -urN linux-2.4.21-pre2/drivers/video/matrox/matroxfb_crtc2.h linux-2.4.21-pre3/drivers/video/matrox/matroxfb_crtc2.h
--- linux-2.4.21-pre2/drivers/video/matrox/matroxfb_crtc2.h	Mon Dec 11 13:16:53 2000
+++ linux-2.4.21-pre3/drivers/video/matrox/matroxfb_crtc2.h	Mon Jan  6 16:32:32 2003
@@ -29,6 +29,8 @@
 
 	int			currcon;
 	struct display*		currcon_display;
+	
+	int			interlaced:1;
 
 	union {
 #ifdef FBCON_HAS_CFB16
diff -urN linux-2.4.21-pre2/drivers/video/matrox/matroxfb_g450.c linux-2.4.21-pre3/drivers/video/matrox/matroxfb_g450.c
--- linux-2.4.21-pre2/drivers/video/matrox/matroxfb_g450.c	Fri Aug  2 17:39:45 2002
+++ linux-2.4.21-pre3/drivers/video/matrox/matroxfb_g450.c	Mon Jan  6 16:32:32 2003
@@ -2,145 +2,634 @@
  *
  * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200, G400 and G450.
  *
- * (c) 1998-2001 Petr Vandrovec <vandrove@vc.cvut.cz>
+ * (c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz>
  *
  * Portions Copyright (c) 2001 Matrox Graphics Inc.
  *
- * Version: 1.62 2001/11/29
+ * Version: 1.64 2002/06/02
  *
  * See matroxfb_base.c for contributors.
  *
  */
 
-#include "matroxfb_g450.h"
+#include "matroxfb_base.h"
 #include "matroxfb_misc.h"
 #include "matroxfb_DAC1064.h"
 #include "g450_pll.h"
 #include <linux/matroxfb.h>
 #include <asm/uaccess.h>
+#include <asm/div64.h>
 
-static int matroxfb_g450_compute(void* md, struct my_timming* mt) {
-#define m2info ((struct matroxfb_g450_info*)md)
-#define minfo (m2info->primary_dev)
-	ACCESS_FBINFO(hw).vidclk = mt->pixclock;
-#undef minfo
-#undef m2info
-	return 0;
+/* Definition of the various controls */
+struct mctl {
+	struct matroxfb_queryctrl desc;
+	size_t control;
+};
+
+#define BLMIN	0xF3
+#define WLMAX	0x3FF
+
+static const struct mctl g450_controls[] =
+{	{ { MATROXFB_CID_BRIGHTNESS,
+	  "brightness",
+	  0, WLMAX-BLMIN, 1, 370-BLMIN, 
+	  MATROXFB_CTRL_TYPE_INTEGER, 0, 0,
+	  "picture"
+	}, offsetof(struct matrox_fb_info, altout.tvo_params.brightness) },
+	{ { MATROXFB_CID_CONTRAST,
+	  "contrast",
+	  0, 1023, 1, 127, 
+	  MATROXFB_CTRL_TYPE_INTEGER, 0, 0,
+	  "picture"
+	}, offsetof(struct matrox_fb_info, altout.tvo_params.contrast) },
+	{ { MATROXFB_CID_SATURATION,
+	  "saturation",
+	  0, 255, 1, 165, 
+	  MATROXFB_CTRL_TYPE_INTEGER, 0, 0,
+	  "picture"
+	}, offsetof(struct matrox_fb_info, altout.tvo_params.saturation) },
+	{ { MATROXFB_CID_HUE,
+	  "hue",
+	  0, 255, 1, 0, 
+	  MATROXFB_CTRL_TYPE_INTEGER, 0, 0,
+	  "picture"
+	}, offsetof(struct matrox_fb_info, altout.tvo_params.hue) },
+	{ { MATROXFB_CID_TESTOUT,
+	  "test output",
+	  0, 1, 1, 0, 
+	  MATROXFB_CTRL_TYPE_BOOLEAN, 0, 0,
+	  "picture"
+	}, offsetof(struct matrox_fb_info, altout.tvo_params.testout) },
+};
+
+#define G450CTRLS (sizeof(g450_controls)/sizeof(g450_controls[0]))
+
+/* Return: positive number: id found
+           -EINVAL:         id not found, return failure
+	   -ENOENT:         id not found, create fake disabled control */
+static int get_ctrl_id(__u32 v4l2_id) {
+	int i;
+
+	for (i = 0; i < G450CTRLS; i++) {
+		if (v4l2_id < g450_controls[i].desc.id) {
+			if (g450_controls[i].desc.id == 0x08000000) {
+				return -EINVAL;
+			}
+			return -ENOENT;
+		}
+		if (v4l2_id == g450_controls[i].desc.id) {
+			return i;
+		}
+	}
+	return -EINVAL;
 }
 
-static int matroxfb_g450_program(void* md) {
-#define m2info ((struct matroxfb_g450_info*)md)
-#define minfo (m2info->primary_dev)
-	matroxfb_g450_setclk(PMINFO ACCESS_FBINFO(hw).vidclk, M_VIDEO_PLL);
-#undef minfo
-#undef m2info	
-	return 0;
+static inline int* get_ctrl_ptr(WPMINFO unsigned int idx) {
+	return (int*)((char*)MINFO + g450_controls[idx].control);
 }
 
-static int matroxfb_g450_start(void* md) {
-	return 0;
+static void tvo_fill_defaults(WPMINFO2) {
+	unsigned int i;
+	
+	for (i = 0; i < G450CTRLS; i++) {
+		*get_ctrl_ptr(PMINFO i) = g450_controls[i].desc.default_value;
+	}
 }
 
-static void matroxfb_g450_incuse(void* md) {
-	MOD_INC_USE_COUNT;
+static int cve2_get_reg(WPMINFO int reg) {
+	unsigned long flags;
+	int val;
+	
+	matroxfb_DAC_lock_irqsave(flags);
+	matroxfb_DAC_out(PMINFO 0x87, reg);
+	val = matroxfb_DAC_in(PMINFO 0x88);
+	matroxfb_DAC_unlock_irqrestore(flags);
+	return val;
 }
 
-static void matroxfb_g450_decuse(void* md) {
-	MOD_DEC_USE_COUNT;
+static void cve2_set_reg(WPMINFO int reg, int val) {
+	unsigned long flags;
+
+	matroxfb_DAC_lock_irqsave(flags);
+	matroxfb_DAC_out(PMINFO 0x87, reg);
+	matroxfb_DAC_out(PMINFO 0x88, val);
+	matroxfb_DAC_unlock_irqrestore(flags);
+}
+
+static void cve2_set_reg10(WPMINFO int reg, int val) {
+	unsigned long flags;
+
+	matroxfb_DAC_lock_irqsave(flags);
+	matroxfb_DAC_out(PMINFO 0x87, reg);
+	matroxfb_DAC_out(PMINFO 0x88, val >> 2);
+	matroxfb_DAC_out(PMINFO 0x87, reg + 1);
+	matroxfb_DAC_out(PMINFO 0x88, val & 3);
+	matroxfb_DAC_unlock_irqrestore(flags);
+}
+
+static void g450_compute_bwlevel(CPMINFO int *bl, int *wl) {
+	const int b = ACCESS_FBINFO(altout.tvo_params.brightness) + BLMIN;
+	const int c = ACCESS_FBINFO(altout.tvo_params.contrast);
+
+	*bl = max(b - c, BLMIN);
+	*wl = min(b + c, WLMAX);
 }
 
-static int matroxfb_g450_set_mode(void* md, u_int32_t arg) {
-	if (arg == MATROXFB_OUTPUT_MODE_MONITOR) {
-		return 1;
+static int g450_query_ctrl(void* md, struct matroxfb_queryctrl *p) {
+	int i;
+	
+	i = get_ctrl_id(p->id);
+	if (i >= 0) {
+		*p = g450_controls[i].desc;
+		return 0;
+	}
+	if (i == -ENOENT) {
+		static const struct matroxfb_queryctrl disctrl = 
+			{ 0, "", 0, 0, 0, 0, 0, 1, 1, "Disabled" };
+			
+		i = p->id;
+		*p = disctrl;
+		p->id = i;
+		sprintf(p->name, "Ctrl #%08X", i);
+		return 0;
 	}
 	return -EINVAL;
 }
 
-static int matroxfb_g450_get_mode(void* md, u_int32_t* arg) {
-	*arg = MATROXFB_OUTPUT_MODE_MONITOR;
+static int g450_set_ctrl(void* md, struct matroxfb_control *p) {
+	int i;
+	MINFO_FROM(md);
+	
+	i = get_ctrl_id(p->id);
+	if (i < 0) return -EINVAL;
+
+	/*
+	 * Check if changed.
+	 */
+	if (p->value == *get_ctrl_ptr(PMINFO i)) return 0;
+
+	/*
+	 * Check limits.
+	 */
+	if (p->value > g450_controls[i].desc.maximum) return -EINVAL;
+	if (p->value < g450_controls[i].desc.minimum) return -EINVAL;
+
+	/*
+	 * Store new value.
+	 */
+	*get_ctrl_ptr(PMINFO i) = p->value;
+
+	switch (p->id) {
+		case MATROXFB_CID_BRIGHTNESS:
+		case MATROXFB_CID_CONTRAST:
+			{
+				int blacklevel, whitelevel;
+				g450_compute_bwlevel(PMINFO &blacklevel, &whitelevel);
+				cve2_set_reg10(PMINFO 0x0e, blacklevel);
+				cve2_set_reg10(PMINFO 0x1e, whitelevel);
+			}
+			break;
+		case MATROXFB_CID_SATURATION:
+			cve2_set_reg(PMINFO 0x20, p->value);
+			cve2_set_reg(PMINFO 0x22, p->value);
+			break;
+		case MATROXFB_CID_HUE:
+			cve2_set_reg(PMINFO 0x25, p->value);
+			break;
+		case MATROXFB_CID_TESTOUT:
+			{
+				unsigned char val = cve2_get_reg (PMINFO 0x05);
+				if (p->value) val |=  0x02;
+				else          val &= ~0x02;
+				cve2_set_reg(PMINFO 0x05, val);
+			}
+			break;
+	}
+	
+
+	return 0;
+}
+
+static int g450_get_ctrl(void* md, struct matroxfb_control *p) {
+	int i;
+	MINFO_FROM(md);
+	
+	i = get_ctrl_id(p->id);
+	if (i < 0) return -EINVAL;
+	p->value = *get_ctrl_ptr(PMINFO i);
 	return 0;
 }
 
-static struct matrox_altout matroxfb_g450_altout = {
-	matroxfb_g450_compute,
-	matroxfb_g450_program,
-	matroxfb_g450_start,
-	matroxfb_g450_incuse,
-	matroxfb_g450_decuse,
-	matroxfb_g450_set_mode,
-	matroxfb_g450_get_mode
+struct output_desc {
+	unsigned int	h_vis;
+	unsigned int	h_f_porch;
+	unsigned int	h_sync;
+	unsigned int	h_b_porch;
+	unsigned long long int	chromasc;
+	unsigned int	burst;
+	unsigned int	v_total;
 };
 
-static int matroxfb_g450_connect(struct matroxfb_g450_info* m2info) {
-	MINFO_FROM(m2info->primary_dev);
+static void computeRegs(WPMINFO struct mavenregs* r, struct my_timming* mt, const struct output_desc* outd) {
+	u_int32_t chromasc;
+	u_int32_t hlen;
+	u_int32_t hsl;
+	u_int32_t hbp;
+	u_int32_t hfp;
+	u_int32_t hvis;
+	unsigned int pixclock;
+	unsigned long long piic;
+	int mnp;
+	int over;
+	
+	r->regs[0x80] = 0x03;	/* | 0x40 for SCART */
 
-	down_write(&ACCESS_FBINFO(altout.lock));
-	ACCESS_FBINFO(altout.device) = m2info;
-	ACCESS_FBINFO(altout.output) = &matroxfb_g450_altout;
-	up_write(&ACCESS_FBINFO(altout.lock));
-	ACCESS_FBINFO(output.all) |= MATROXFB_OUTPUT_CONN_SECONDARY;
-	matroxfb_switch(ACCESS_FBINFO(currcon), (struct fb_info*)MINFO);	
-	return 0;
+	hvis = ((mt->HDisplay << 1) + 3) & ~3;
+	
+	if (hvis >= 2048) {
+		hvis = 2044;
+	}
+	
+	piic = 1000000000ULL * hvis;
+	do_div(piic, outd->h_vis);
+
+	dprintk(KERN_DEBUG "Want %u kHz pixclock\n", (unsigned int)piic);
+	
+	mnp = matroxfb_g450_setclk(PMINFO piic, M_VIDEO_PLL);
+	
+	mt->mnp = mnp;
+	mt->pixclock = g450_mnp2f(PMINFO mnp);
+
+	dprintk(KERN_DEBUG "MNP=%08X\n", mnp);
+
+	pixclock = 1000000000U / mt->pixclock;
+
+	dprintk(KERN_DEBUG "Got %u ps pixclock\n", pixclock);
+
+	piic = outd->chromasc;
+	do_div(piic, mt->pixclock);
+	chromasc = piic;
+	
+	dprintk(KERN_DEBUG "Chroma is %08X\n", chromasc);
+
+	r->regs[0] = piic >> 24;
+	r->regs[1] = piic >> 16;
+	r->regs[2] = piic >>  8;
+	r->regs[3] = piic >>  0;
+	hbp = (((outd->h_b_porch + pixclock) / pixclock)) & ~1;
+	hfp = (((outd->h_f_porch + pixclock) / pixclock)) & ~1;
+	hsl = (((outd->h_sync + pixclock) / pixclock)) & ~1;
+	hlen = hvis + hfp + hsl + hbp;
+	over = hlen & 0x0F;
+	
+	dprintk(KERN_DEBUG "WL: vis=%u, hf=%u, hs=%u, hb=%u, total=%u\n", hvis, hfp, hsl, hbp, hlen);
+
+	if (over) {
+		hfp -= over;
+		hlen -= over;
+		if (over <= 2) {
+		} else if (over < 10) {
+			hfp += 4;
+			hlen += 4;
+		} else {
+			hfp += 16;
+			hlen += 16;
+		}
+	}
+
+	/* maybe cve2 has requirement 800 < hlen < 1184 */
+	r->regs[0x08] = hsl;
+	r->regs[0x09] = (outd->burst + pixclock - 1) / pixclock;	/* burst length */
+	r->regs[0x0A] = hbp;
+	r->regs[0x2C] = hfp;
+	r->regs[0x31] = hvis / 8;
+	r->regs[0x32] = hvis & 7;
+	
+	dprintk(KERN_DEBUG "PG: vis=%04X, hf=%02X, hs=%02X, hb=%02X, total=%04X\n", hvis, hfp, hsl, hbp, hlen);
+
+	r->regs[0x84] = 1;	/* x sync point */
+	r->regs[0x85] = 0;
+	hvis = hvis >> 1;
+	hlen = hlen >> 1;
+	
+	dprintk(KERN_DEBUG "hlen=%u hvis=%u\n", hlen, hvis);
+
+	mt->interlaced = 1;
+
+	mt->HDisplay = hvis & ~7;
+	mt->HSyncStart = mt->HDisplay + 8;
+	mt->HSyncEnd = (hlen & ~7) - 8;
+	mt->HTotal = hlen;
+
+	{
+		int upper;
+		unsigned int vtotal;
+		unsigned int vsyncend;
+		unsigned int vdisplay;
+		
+		vtotal = mt->VTotal;
+		vsyncend = mt->VSyncEnd;
+		vdisplay = mt->VDisplay;
+		if (vtotal < outd->v_total) {
+			unsigned int yovr = outd->v_total - vtotal;
+			
+			vsyncend += yovr >> 1;
+		} else if (vtotal > outd->v_total) {
+			vdisplay = outd->v_total - 4;
+			vsyncend = outd->v_total;
+		}
+		upper = (outd->v_total - vsyncend) >> 1;	/* in field lines */
+		r->regs[0x17] = outd->v_total / 4;
+		r->regs[0x18] = outd->v_total & 3;
+		r->regs[0x33] = upper - 1;	/* upper blanking */
+		r->regs[0x82] = upper;		/* y sync point */
+		r->regs[0x83] = upper >> 8;
+		
+		mt->VDisplay = vdisplay;
+		mt->VSyncStart = outd->v_total - 2;
+		mt->VSyncEnd = outd->v_total;
+		mt->VTotal = outd->v_total;
+	}
+}
+
+static void cve2_init_TVdata(int norm, struct mavenregs* data, const struct output_desc** outd) {
+	static const struct output_desc paloutd = {
+		.h_vis	   = 52148148,	// ps
+		.h_f_porch =  1407407,	// ps
+		.h_sync    =  4666667,	// ps
+		.h_b_porch =  5777778,	// ps
+		.chromasc  = 19042247534182ULL,	// 4433618.750 Hz
+		.burst     =  2518518,	// ps
+		.v_total   =      625,
+	};
+	static const struct output_desc ntscoutd = {
+		.h_vis     = 52888889,	// ps
+		.h_f_porch =  1333333,	// ps
+		.h_sync    =  4666667,	// ps
+		.h_b_porch =  4666667,	// ps
+		.chromasc  = 15374030659475ULL,	// 3579545.454 Hz
+		.burst     =  2418418,	// ps
+		.v_total   =      525,	// lines
+	};
+
+	static const struct mavenregs palregs = { {
+		0x2A, 0x09, 0x8A, 0xCB,	/* 00: chroma subcarrier */
+		0x00,
+		0x00,	/* test */
+		0xF9,	/* modified by code (F9 written...) */
+		0x00,	/* ? not written */
+		0x7E,	/* 08 */
+		0x44,	/* 09 */
+		0x9C,	/* 0A */
+		0x2E,	/* 0B */
+		0x21,	/* 0C */
+		0x00,	/* ? not written */
+//		0x3F, 0x03, /* 0E-0F */
+		0x3C, 0x03,
+		0x3C, 0x03, /* 10-11 */
+		0x1A,	/* 12 */
+		0x2A,	/* 13 */
+		0x1C, 0x3D, 0x14, /* 14-16 */
+		0x9C, 0x01, /* 17-18 */
+		0x00,	/* 19 */
+		0xFE,	/* 1A */
+		0x7E,	/* 1B */
+		0x60,	/* 1C */
+		0x05,	/* 1D */
+//		0x89, 0x03, /* 1E-1F */
+		0xAD, 0x03,
+//		0x72,	/* 20 */
+		0xA5,
+		0x07,	/* 21 */
+//		0x72,	/* 22 */
+		0xA5,
+		0x00,	/* 23 */
+		0x00,	/* 24 */
+		0x00,	/* 25 */
+		0x08,	/* 26 */
+		0x04,	/* 27 */
+		0x00,	/* 28 */
+		0x1A,	/* 29 */
+		0x55, 0x01, /* 2A-2B */
+		0x26,	/* 2C */
+		0x07, 0x7E, /* 2D-2E */
+		0x02, 0x54, /* 2F-30 */
+		0xB0, 0x00, /* 31-32 */
+		0x14,	/* 33 */
+		0x49,	/* 34 */
+		0x00,	/* 35 written multiple times */
+		0x00,	/* 36 not written */
+		0xA3,	/* 37 */
+		0xC8,	/* 38 */
+		0x22,	/* 39 */
+		0x02,	/* 3A */
+		0x22,	/* 3B */
+		0x3F, 0x03, /* 3C-3D */
+		0x00,	/* 3E written multiple times */
+		0x00,	/* 3F not written */
+	} };
+	static struct mavenregs ntscregs = { {
+		0x21, 0xF0, 0x7C, 0x1F,	/* 00: chroma subcarrier */
+		0x00,
+		0x00,	/* test */
+		0xF9,	/* modified by code (F9 written...) */
+		0x00,	/* ? not written */
+		0x7E,	/* 08 */
+		0x43,	/* 09 */
+		0x7E,	/* 0A */
+		0x3D,	/* 0B */
+		0x00,	/* 0C */
+		0x00,	/* ? not written */
+		0x41, 0x00, /* 0E-0F */
+		0x3C, 0x00, /* 10-11 */
+		0x17,	/* 12 */
+		0x21,	/* 13 */
+		0x1B, 0x1B, 0x24, /* 14-16 */
+		0x83, 0x01, /* 17-18 */
+		0x00,	/* 19 */
+		0x0F,	/* 1A */
+		0x0F,	/* 1B */
+		0x60,	/* 1C */
+		0x05,	/* 1D */
+		//0x89, 0x02, /* 1E-1F */
+		0xC0, 0x02, /* 1E-1F */
+		//0x5F,	/* 20 */
+		0x9C,	/* 20 */
+		0x04,	/* 21 */
+		//0x5F,	/* 22 */
+		0x9C,	/* 22 */
+		0x01,	/* 23 */
+		0x02,	/* 24 */
+		0x00,	/* 25 */
+		0x0A,	/* 26 */
+		0x05,	/* 27 */
+		0x00,	/* 28 */
+		0x10,	/* 29 */
+		0xFF, 0x03, /* 2A-2B */
+		0x24,	/* 2C */
+		0x0F, 0x78, /* 2D-2E */
+		0x00, 0x00, /* 2F-30 */
+		0xB2, 0x04, /* 31-32 */
+		0x14,	/* 33 */
+		0x02,	/* 34 */
+		0x00,	/* 35 written multiple times */
+		0x00,	/* 36 not written */
+		0xA3,	/* 37 */
+		0xC8,	/* 38 */
+		0x15,	/* 39 */
+		0x05,	/* 3A */
+		0x3B,	/* 3B */
+		0x3C, 0x00, /* 3C-3D */
+		0x00,	/* 3E written multiple times */
+		0x00,	/* never written */
+	} };
+
+	if (norm == MATROXFB_OUTPUT_MODE_PAL) {
+		*data = palregs;
+		*outd = &paloutd;
+	} else {
+  		*data = ntscregs;
+		*outd = &ntscoutd;
+	}
+ 	return;
 }
 
-static void matroxfb_g450_shutdown(struct matroxfb_g450_info* m2info) {
-	MINFO_FROM(m2info->primary_dev);
+#define LR(x) cve2_set_reg(PMINFO (x), m->regs[(x)])
+static void cve2_init_TV(WPMINFO const struct mavenregs* m) {
+	int i;
+
+	LR(0x80);
+	LR(0x82); LR(0x83);
+	LR(0x84); LR(0x85);
 	
-	if (MINFO) {
-		ACCESS_FBINFO(output.all) &= ~MATROXFB_OUTPUT_CONN_SECONDARY;
-		ACCESS_FBINFO(output.ph) &= ~MATROXFB_OUTPUT_CONN_SECONDARY;
-		ACCESS_FBINFO(output.sh) &= ~MATROXFB_OUTPUT_CONN_SECONDARY;
-		down_write(&ACCESS_FBINFO(altout.lock));
-		ACCESS_FBINFO(altout.device) = NULL;
-		ACCESS_FBINFO(altout.output) = NULL;
-		up_write(&ACCESS_FBINFO(altout.lock));
-		m2info->primary_dev = NULL;
+	cve2_set_reg(PMINFO 0x3E, 0x01);
+	
+	for (i = 0; i < 0x3E; i++) {
+		LR(i);
 	}
+	cve2_set_reg(PMINFO 0x3E, 0x00);
 }
 
-/* we do not have __setup() yet */
-static void* matroxfb_g450_probe(struct matrox_fb_info* minfo) {
-	struct matroxfb_g450_info* m2info;
+static int matroxfb_g450_compute(void* md, struct my_timming* mt) {
+	MINFO_FROM(md);
 
-	/* hardware is not G450... */
-	if (!ACCESS_FBINFO(devflags.g450dac))
-		return NULL;
-	m2info = (struct matroxfb_g450_info*)kmalloc(sizeof(*m2info), GFP_KERNEL);
-	if (!m2info) {
-		printk(KERN_ERR "matroxfb_g450: Not enough memory for G450 DAC control structs\n");
-		return NULL;
+	dprintk(KERN_DEBUG "Computing, mode=%u\n", ACCESS_FBINFO(outputs[1]).mode);
+
+	if (mt->crtc == MATROXFB_SRC_CRTC2 &&
+	    ACCESS_FBINFO(outputs[1]).mode != MATROXFB_OUTPUT_MODE_MONITOR) {
+		const struct output_desc* outd;
+
+		cve2_init_TVdata(ACCESS_FBINFO(outputs[1]).mode, &ACCESS_FBINFO(hw).maven, &outd);
+		{
+			int blacklevel, whitelevel;
+			g450_compute_bwlevel(PMINFO &blacklevel, &whitelevel);
+			ACCESS_FBINFO(hw).maven.regs[0x0E] = blacklevel >> 2;
+			ACCESS_FBINFO(hw).maven.regs[0x0F] = blacklevel & 3;
+			ACCESS_FBINFO(hw).maven.regs[0x1E] = whitelevel >> 2;
+			ACCESS_FBINFO(hw).maven.regs[0x1F] = whitelevel & 3;
+
+			ACCESS_FBINFO(hw).maven.regs[0x20] =
+			ACCESS_FBINFO(hw).maven.regs[0x22] = ACCESS_FBINFO(altout.tvo_params.saturation);
+
+			ACCESS_FBINFO(hw).maven.regs[0x25] = ACCESS_FBINFO(altout.tvo_params.hue);
+
+			if (ACCESS_FBINFO(altout.tvo_params.testout)) {
+				ACCESS_FBINFO(hw).maven.regs[0x05] |= 0x02;
+			}
+		}
+		computeRegs(PMINFO &ACCESS_FBINFO(hw).maven, mt, outd);
+	} else if (mt->mnp < 0) {
+		/* We must program clocks before CRTC2, otherwise interlaced mode
+		   startup may fail */
+		mt->mnp = matroxfb_g450_setclk(PMINFO mt->pixclock, (mt->crtc == MATROXFB_SRC_CRTC1) ? M_PIXEL_PLL_C : M_VIDEO_PLL);
+		mt->pixclock = g450_mnp2f(PMINFO mt->mnp);
 	}
-	memset(m2info, 0, sizeof(*m2info));
-	m2info->primary_dev = MINFO;
-	if (matroxfb_g450_connect(m2info)) {
-		kfree(m2info);
-		printk(KERN_ERR "matroxfb_g450: G450 DAC failed to initialize\n");
-		return NULL;
+	dprintk(KERN_DEBUG "Pixclock = %u\n", mt->pixclock);
+	return 0;
+}
+
+static int matroxfb_g450_program(void* md) {
+	MINFO_FROM(md);
+	
+	if (ACCESS_FBINFO(outputs[1]).mode != MATROXFB_OUTPUT_MODE_MONITOR) {
+		cve2_init_TV(PMINFO &ACCESS_FBINFO(hw).maven);
 	}
-	return m2info;
+	return 0;
 }
 
-static void matroxfb_g450_remove(struct matrox_fb_info* minfo, void* g450) {
-	matroxfb_g450_shutdown(g450);
-	kfree(g450);
+static int matroxfb_g450_verify_mode(void* md, u_int32_t arg) {
+	MINFO_FROM(md);
+	
+	switch (arg) {
+		case MATROXFB_OUTPUT_MODE_PAL:
+		case MATROXFB_OUTPUT_MODE_NTSC:
+		case MATROXFB_OUTPUT_MODE_MONITOR:
+			return 0;
+	}
+	return -EINVAL;
 }
 
-static struct matroxfb_driver g450 = {
-		name:	"Matrox G450 output #2",
-		probe:	matroxfb_g450_probe,
-		remove:	matroxfb_g450_remove };
+static int g450_dvi_compute(void* md, struct my_timming* mt) {
+	MINFO_FROM(md);
 
-static int matroxfb_g450_init(void) {
-	matroxfb_register_driver(&g450);
+	if (mt->mnp < 0) {
+		mt->mnp = matroxfb_g450_setclk(PMINFO mt->pixclock, (mt->crtc == MATROXFB_SRC_CRTC1) ? M_PIXEL_PLL_C : M_VIDEO_PLL);
+		mt->pixclock = g450_mnp2f(PMINFO mt->mnp);
+	}
 	return 0;
 }
 
-static void matroxfb_g450_exit(void) {
-	matroxfb_unregister_driver(&g450);
+static struct matrox_altout matroxfb_g450_altout = {
+	.owner   = THIS_MODULE,
+	.name	 = "Secondary output",
+	.compute = matroxfb_g450_compute,
+	.program = matroxfb_g450_program,
+	.verifymode = matroxfb_g450_verify_mode,
+	.getqueryctrl = g450_query_ctrl,
+	.getctrl = g450_get_ctrl,
+	.setctrl = g450_set_ctrl,
+};
+
+static struct matrox_altout matroxfb_g450_dvi = {
+	.owner   = THIS_MODULE,
+	.name	 = "DVI output",
+	.compute = g450_dvi_compute,
+};
+
+void matroxfb_g450_connect(WPMINFO2) {
+	if (ACCESS_FBINFO(devflags.g450dac)) {
+		down_write(&ACCESS_FBINFO(altout.lock));
+		tvo_fill_defaults(PMINFO2);
+		ACCESS_FBINFO(outputs[1]).src = MATROXFB_SRC_CRTC1;
+		ACCESS_FBINFO(outputs[1]).data = MINFO;
+		ACCESS_FBINFO(outputs[1]).output = &matroxfb_g450_altout;
+		ACCESS_FBINFO(outputs[1]).mode = MATROXFB_OUTPUT_MODE_MONITOR;
+		ACCESS_FBINFO(outputs[2]).src = MATROXFB_SRC_CRTC1;
+		ACCESS_FBINFO(outputs[2]).data = MINFO;
+		ACCESS_FBINFO(outputs[2]).output = &matroxfb_g450_dvi;
+		ACCESS_FBINFO(outputs[2]).mode = MATROXFB_OUTPUT_MODE_MONITOR;
+		up_write(&ACCESS_FBINFO(altout.lock));
+	}
 }
 
-MODULE_AUTHOR("(c) 2000-2001 Petr Vandrovec <vandrove@vc.cvut.cz>");
-MODULE_DESCRIPTION("Matrox G450 secondary output driver");
+void matroxfb_g450_shutdown(WPMINFO2) {
+	if (ACCESS_FBINFO(devflags.g450dac)) {
+		down_write(&ACCESS_FBINFO(altout.lock));
+		ACCESS_FBINFO(outputs[1]).src = MATROXFB_SRC_NONE;
+		ACCESS_FBINFO(outputs[1]).output = NULL;
+		ACCESS_FBINFO(outputs[1]).data = NULL;
+		ACCESS_FBINFO(outputs[1]).mode = MATROXFB_OUTPUT_MODE_MONITOR;
+		ACCESS_FBINFO(outputs[2]).src = MATROXFB_SRC_NONE;
+		ACCESS_FBINFO(outputs[2]).output = NULL;
+		ACCESS_FBINFO(outputs[2]).data = NULL;
+		ACCESS_FBINFO(outputs[2]).mode = MATROXFB_OUTPUT_MODE_MONITOR;
+		up_write(&ACCESS_FBINFO(altout.lock));
+	}
+}
+
+EXPORT_SYMBOL(matroxfb_g450_connect);
+EXPORT_SYMBOL(matroxfb_g450_shutdown);
+
+MODULE_AUTHOR("(c) 2000-2002 Petr Vandrovec <vandrove@vc.cvut.cz>");
+MODULE_DESCRIPTION("Matrox G450/G550 output driver");
 MODULE_LICENSE("GPL");
-module_init(matroxfb_g450_init);
-module_exit(matroxfb_g450_exit);
diff -urN linux-2.4.21-pre2/drivers/video/matrox/matroxfb_g450.h linux-2.4.21-pre3/drivers/video/matrox/matroxfb_g450.h
--- linux-2.4.21-pre2/drivers/video/matrox/matroxfb_g450.h	Fri Nov  9 14:07:41 2001
+++ linux-2.4.21-pre3/drivers/video/matrox/matroxfb_g450.h	Mon Jan  6 16:32:32 2003
@@ -1,12 +1,14 @@
 #ifndef __MATROXFB_G450_H__
 #define __MATROXFB_G450_H__
 
-#include <linux/ioctl.h>
 #include "matroxfb_base.h"
 
-struct matroxfb_g450_info {
-	struct matrox_fb_info*	primary_dev;
-	unsigned int		timmings;
-};
+#ifdef CONFIG_FB_MATROX_G450
+void matroxfb_g450_connect(WPMINFO2);
+void matroxfb_g450_shutdown(WPMINFO2);
+#else
+static inline void matroxfb_g450_connect(WPMINFO2) { };
+static inline void matroxfb_g450_shutdown(WPMINFO2) { };
+#endif
 
-#endif /* __MATROXFB_MAVEN_H__ */
+#endif /* __MATROXFB_G450_H__ */
diff -urN linux-2.4.21-pre2/drivers/video/matrox/matroxfb_maven.c linux-2.4.21-pre3/drivers/video/matrox/matroxfb_maven.c
--- linux-2.4.21-pre2/drivers/video/matrox/matroxfb_maven.c	Fri Aug  2 17:39:45 2002
+++ linux-2.4.21-pre3/drivers/video/matrox/matroxfb_maven.c	Mon Jan  6 16:32:32 2003
@@ -2,11 +2,11 @@
  *
  * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200, G400 and G450.
  *
- * (c) 1998-2001 Petr Vandrovec <vandrove@vc.cvut.cz>
+ * (c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz>
  *
  * Portions Copyright (c) 2001 Matrox Graphics Inc.
  *
- * Version: 1.62 2001/11/29
+ * Version: 1.64 2002/06/10
  *
  * See matroxfb_base.c for contributors.
  *
@@ -30,13 +30,125 @@
 #define MGATVO_B	1
 #define MGATVO_C	2
 
+static const struct maven_gamma {
+  unsigned char reg83;
+  unsigned char reg84;
+  unsigned char reg85;
+  unsigned char reg86;
+  unsigned char reg87;
+  unsigned char reg88;
+  unsigned char reg89;
+  unsigned char reg8a;
+  unsigned char reg8b;
+} maven_gamma[] = {
+  { 131, 57, 223, 15, 117, 212, 251, 91, 156},
+  { 133, 61, 128, 63, 180, 147, 195, 100, 180},
+  { 131, 19, 63, 31, 50, 66, 171, 64, 176},
+  { 0, 0, 0, 31, 16, 16, 16, 100, 200},
+  { 8, 23, 47, 73, 147, 244, 220, 80, 195},
+  { 22, 43, 64, 80, 147, 115, 58, 85, 168},
+  { 34, 60, 80, 214, 147, 212, 188, 85, 167},
+  { 45, 77, 96, 216, 147, 99, 91, 85, 159},
+  { 56, 76, 112, 107, 147, 212, 148, 64, 144},
+  { 65, 91, 128, 137, 147, 196, 17, 69, 148},
+  { 72, 104, 136, 138, 147, 180, 245, 73, 147},
+  { 87, 116, 143, 126, 16, 83, 229, 77, 144},
+  { 95, 119, 152, 254, 244, 83, 221, 77, 151},
+  { 100, 129, 159, 156, 244, 148, 197, 77, 160},
+  { 105, 141, 167, 247, 244, 132, 181, 84, 166},
+  { 105, 147, 168, 247, 244, 245, 181, 90, 170},
+  { 120, 153, 175, 248, 212, 229, 165, 90, 180},
+  { 119, 156, 176, 248, 244, 229, 84, 74, 160},
+  { 119, 158, 183, 248, 244, 229, 149, 78, 165}
+};
+
+/* Definition of the various controls */
+struct mctl {
+	struct matroxfb_queryctrl desc;
+	size_t control;
+};
+
+#define BLMIN	0x0FF
+#define WLMAX	0x3FF
+
+static const struct mctl maven_controls[] =
+{	{ { MATROXFB_CID_BRIGHTNESS,
+	  "brightness",
+	  0, WLMAX - BLMIN, 1, 379 - BLMIN, 
+	  MATROXFB_CTRL_TYPE_INTEGER, 0, 0,
+	  "picture"
+	}, offsetof(struct matrox_fb_info, altout.tvo_params.brightness) },
+	{ { MATROXFB_CID_CONTRAST,
+	  "contrast",
+	  0, 1023, 1, 127, 
+	  MATROXFB_CTRL_TYPE_INTEGER, 0, 0,
+	  "picture"
+	}, offsetof(struct matrox_fb_info, altout.tvo_params.contrast) },
+	{ { MATROXFB_CID_SATURATION,
+	  "saturation",
+	  0, 255, 1, 155, 
+	  MATROXFB_CTRL_TYPE_INTEGER, 0, 0,
+	  "picture"
+	}, offsetof(struct matrox_fb_info, altout.tvo_params.saturation) },
+	{ { MATROXFB_CID_HUE,
+	  "hue",
+	  0, 255, 1, 0, 
+	  MATROXFB_CTRL_TYPE_INTEGER, 0, 0,
+	  "picture"
+	}, offsetof(struct matrox_fb_info, altout.tvo_params.hue) },
+	{ { MATROXFB_CID_GAMMA,
+	  "gamma",
+	  0, sizeof(maven_gamma)/sizeof(maven_gamma[0])-1, 1, 3, 
+	  MATROXFB_CTRL_TYPE_INTEGER, 0, 0,
+	  "picture"
+	}, offsetof(struct matrox_fb_info, altout.tvo_params.gamma) },
+	{ { MATROXFB_CID_TESTOUT,
+	  "test output",
+	  0, 1, 1, 0, 
+	  MATROXFB_CTRL_TYPE_BOOLEAN, 0, 0,
+	  "picture"
+	}, offsetof(struct matrox_fb_info, altout.tvo_params.testout) },
+	{ { MATROXFB_CID_DEFLICKER,
+	  "deflicker mode",
+	  0, 2, 1, 0, 
+	  MATROXFB_CTRL_TYPE_INTEGER, 0, 0,
+	  "picture"
+	}, offsetof(struct matrox_fb_info, altout.tvo_params.deflicker) },
+
+};
+
+#define MAVCTRLS (sizeof(maven_controls)/sizeof(maven_controls[0]))
+
+/* Return: positive number: id found
+           -EINVAL:         id not found, return failure
+	   -ENOENT:         id not found, create fake disabled control */
+static int get_ctrl_id(__u32 v4l2_id) {
+	int i;
+
+	for (i = 0; i < MAVCTRLS; i++) {
+		if (v4l2_id < maven_controls[i].desc.id) {
+			if (maven_controls[i].desc.id == 0x08000000) {
+				return -EINVAL;
+			}
+			return -ENOENT;
+		}
+		if (v4l2_id == maven_controls[i].desc.id) {
+			return i;
+		}
+	}
+	return -EINVAL;
+}
+
 struct maven_data {
 	struct matrox_fb_info*		primary_head;
 	struct i2c_client*		client;
-	int				mode;
 	int				version;
 };
 
+static int* get_ctrl_ptr(struct maven_data* md, int idx) {
+	return (int*)((char*)(md->primary_head) + maven_controls[idx].control);
+}
+
 static int maven_get_reg(struct i2c_client* c, char reg) {
 	char dst;
 	struct i2c_msg msgs[] = {{ c->addr, I2C_M_REV_DIR_ADDR, sizeof(reg), &reg },
@@ -127,8 +239,8 @@
 	fwant = htotal * vtotal;
 	fmax = pll->vco_freq_max / ctl->den;
 
-	printk(KERN_DEBUG "want: %u, xtal: %u, h: %u, v: %u, fmax: %u\n",
-		fwant, fxtal, htotal, vtotal, fmax);
+/*	printk(KERN_DEBUG "want: %u, xtal: %u, h: %u, v: %u, fmax: %u\n",
+		fwant, fxtal, htotal, vtotal, fmax);	*/
 	for (p = 1; p <= pll->post_shift_max; p++) {
 		if (fwant * 2 > fmax)
 			break;
@@ -163,9 +275,9 @@
 			ln = ln - scrlen;
 			if (ln > htotal)
 				continue;
-			printk(KERN_DEBUG "Match: %u / %u / %u / %u\n", n, m, p, ln);
+			dprintk(KERN_DEBUG "Match: %u / %u / %u / %u\n", n, m, p, ln);
 			if (ln > besth2) {
-				printk(KERN_DEBUG "Better...\n");
+				dprintk(KERN_DEBUG "Better...\n");
 				*h2 = besth2 = ln;
 				*post = p;
 				*in = m;
@@ -221,6 +333,38 @@
 	return;
 }
 
+static unsigned char maven_compute_deflicker (const struct maven_data* md) {
+	unsigned char df;
+	
+	df = (md->version == MGATVO_B?0x40:0x00);
+	switch (md->primary_head->altout.tvo_params.deflicker) {
+		case 0:
+/*			df |= 0x00; */
+			break;
+		case 1:
+			df |= 0xB1;
+			break;
+		case 2:
+			df |= 0xA2;
+			break;
+	}
+	return df;
+}
+
+static void maven_compute_bwlevel (const struct maven_data* md,
+				   int *bl, int *wl) {
+	const int b = md->primary_head->altout.tvo_params.brightness + BLMIN;
+	const int c = md->primary_head->altout.tvo_params.contrast;
+
+	*bl = max(b - c, BLMIN);
+	*wl = min(b + c, WLMAX);
+}
+
+static const struct maven_gamma* maven_compute_gamma (const struct maven_data* md) {
+ 	return maven_gamma + md->primary_head->altout.tvo_params.gamma;
+}
+
+
 static void maven_init_TVdata(const struct maven_data* md, struct mavenregs* data) {
 	static struct mavenregs palregs = { {
 		0x2A, 0x09, 0x8A, 0xCB,	/* 00: chroma subcarrier */
@@ -326,26 +470,50 @@
 		0x00,	/* 3E written multiple times */
 		0x00,	/* never written */
 	}, MODE_NTSC, 525, 60 };
+	MINFO_FROM(md->primary_head);
 
-	if (md->mode & MODE_PAL)
+	if (ACCESS_FBINFO(outputs[1].mode) == MODE_PAL)
 		*data = palregs;
-	else
-		*data = ntscregs;
-
-	data->regs[0x93] = 0xA2;
-
-	/* gamma correction registers */
-	data->regs[0x83] = 0x00;
-	data->regs[0x84] = 0x00;
-	data->regs[0x85] = 0x00;
-	data->regs[0x86] = 0x1F;
-	data->regs[0x87] = 0x10;
-	data->regs[0x88] = 0x10;
-	data->regs[0x89] = 0x10;
-	data->regs[0x8A] = 0x64;	/* 100 */
-	data->regs[0x8B] = 0xC8;	/* 200 */
-
-	return;
+  	else
+  		*data = ntscregs;
+  
+ 	/* Set deflicker */
+ 	data->regs[0x93] = maven_compute_deflicker(md);
+ 
+ 	/* set gamma */
+ 	{
+		const struct maven_gamma* g;
+		g = maven_compute_gamma(md);
+		data->regs[0x83] = g->reg83;
+		data->regs[0x84] = g->reg84;
+		data->regs[0x85] = g->reg85;
+		data->regs[0x86] = g->reg86;
+		data->regs[0x87] = g->reg87;
+		data->regs[0x88] = g->reg88;
+		data->regs[0x89] = g->reg89;
+		data->regs[0x8A] = g->reg8a;
+		data->regs[0x8B] = g->reg8b;
+ 	}
+ 
+ 	/* Set contrast / brightness */
+ 	{
+		int bl, wl;
+		maven_compute_bwlevel (md, &bl, &wl);
+		data->regs[0x0e] = bl >> 2;
+		data->regs[0x0f] = bl & 3;
+		data->regs[0x1e] = wl >> 2;
+		data->regs[0x1f] = wl & 3;
+ 	}
+
+ 	/* Set saturation */
+ 	{
+		data->regs[0x20] =
+		data->regs[0x22] = ACCESS_FBINFO(altout.tvo_params.saturation);
+ 	}
+ 
+ 	/* Set HUE */
+	data->regs[0x25] = ACCESS_FBINFO(altout.tvo_params.hue);
+ 	return;
 }
 
 #define LR(x) maven_set_reg(c, (x), m->regs[(x)])
@@ -386,7 +554,7 @@
 	LRP(0x17);
 	LR(0x0B);
 	LR(0x0C);
-	if (m->mode & MODE_PAL) {
+	if (m->mode == MODE_PAL) {
 		maven_set_reg(c, 0x35, 0x10); /* ... */
 	} else {
 		maven_set_reg(c, 0x35, 0x0F); /* ... */
@@ -424,7 +592,7 @@
 	LR(0x27);
 	LR(0x21);
 	LRP(0x2A);
-	if (m->mode & MODE_PAL)
+	if (m->mode == MODE_PAL)
 		maven_set_reg(c, 0x35, 0x1D);	/* ... */
 	else
 		maven_set_reg(c, 0x35, 0x1C);
@@ -473,7 +641,7 @@
 	LR(0xC2);
 
 	maven_get_reg(c, 0x8D);
-	maven_set_reg(c, 0x8D, 0x00);
+	maven_set_reg(c, 0x8D, 0x04);
 
 	LR(0x20);	/* saturation #1 */
 	LR(0x22);	/* saturation #2 */
@@ -498,7 +666,7 @@
 	LR(0x8B);
 
 	val = maven_get_reg(c, 0x8D);
-	val &= 0x10;			/* 0x10 or anything ored with it */
+	val &= 0x14;			/* 0x10 or anything ored with it */
 	maven_set_reg(c, 0x8D, val);
 
 	LR(0x33);
@@ -524,7 +692,7 @@
 	LR(0x27);
 	LR(0x21);
 	LRP(0x2A);
-	if (m->mode & MODE_PAL)
+	if (m->mode == MODE_PAL)
 		maven_set_reg(c, 0x35, 0x1D);
 	else
 		maven_set_reg(c, 0x35, 0x1C);
@@ -562,7 +730,7 @@
 		unsigned int a, b, c, h2;
 		unsigned int h = ht + 2 + x;
 
-		if (!matroxfb_mavenclock((m->mode & MODE_PAL) ? &maven_PAL : &maven_NTSC, h, vt, &a, &b, &c, &h2)) {
+		if (!matroxfb_mavenclock((m->mode == MODE_PAL) ? &maven_PAL : &maven_NTSC, h, vt, &a, &b, &c, &h2)) {
 			unsigned int diff = h - h2;
 
 			if (diff < err) {
@@ -584,8 +752,8 @@
 	unsigned int tmpi;
 	unsigned int a, bv, c;
 
-	m->mode = md->mode;
-	if (MODE_TV(md->mode)) {
+	m->mode = md->primary_head->outputs[1].mode;
+	if (MODE_TV(m->mode)) {
 		unsigned int lmargin;
 		unsigned int umargin;
 		unsigned int vslen;
@@ -804,7 +972,7 @@
 	m->regs[0xB0] = 0x03;	/* output: monitor */
 	m->regs[0xB1] = 0xA0;	/* ??? */
 	m->regs[0x8C] = 0x20;	/* must be set... */
-	m->regs[0x8D] = 0x00;	/* defaults to 0x10: test signal */
+	m->regs[0x8D] = 0x04;	/* defaults to 0x10: test signal */
 	m->regs[0xB9] = 0x1A;	/* defaults to 0x2C: too bright */
 	m->regs[0xBF] = 0x22;	/* makes picture stable */
 
@@ -849,25 +1017,122 @@
 	return 0;
 }
 
-static inline int maven_resync(struct maven_data* md) {
+static inline int maven_start(struct maven_data* md) {
 	struct i2c_client* c = md->client;
 	maven_set_reg(c, 0x95, 0x20);	/* start whole thing */
 	return 0;
 }
 
-static int maven_set_output_mode(struct maven_data* md, u_int32_t arg) {
-	switch (arg) {
-		case MATROXFB_OUTPUT_MODE_PAL:
-		case MATROXFB_OUTPUT_MODE_NTSC:
-		case MATROXFB_OUTPUT_MODE_MONITOR:
-			md->mode = arg;
-			return 1;
+static int maven_get_queryctrl (struct maven_data* md, 
+				struct matroxfb_queryctrl *p) {
+	int i;
+	
+	i = get_ctrl_id(p->id);
+	if (i >= 0) {
+		*p = maven_controls[i].desc;
+		return 0;
+	}
+	if (i == -ENOENT) {
+		static const struct matroxfb_queryctrl disctrl = 
+			{ 0, "", 0, 0, 0, 0, 0, 1, 1, "Disabled" };
+			
+		i = p->id;
+		*p = disctrl;
+		p->id = i;
+		sprintf(p->name, "Ctrl #%08X", i);
+		return 0;
 	}
 	return -EINVAL;
 }
 
-static int maven_get_output_mode(struct maven_data* md, u_int32_t *arg) {
-	*arg = md->mode;
+static int maven_set_control (struct maven_data* md, 
+			      struct matroxfb_control *p) {
+	int i;
+	
+	i = get_ctrl_id(p->id);
+	if (i < 0) return -EINVAL;
+
+	/*
+	 * Check if changed.
+	 */
+	if (p->value == *get_ctrl_ptr(md, i)) return 0;
+
+	/*
+	 * Check limits.
+	 */
+	if (p->value > maven_controls[i].desc.maximum) return -EINVAL;
+	if (p->value < maven_controls[i].desc.minimum) return -EINVAL;
+
+	/*
+	 * Store new value.
+	 */
+	*get_ctrl_ptr(md, i) = p->value;
+
+	switch (p->id) {
+		case MATROXFB_CID_BRIGHTNESS:
+		case MATROXFB_CID_CONTRAST:
+		{
+		  int blacklevel, whitelevel;
+		  maven_compute_bwlevel(md, &blacklevel, &whitelevel);
+		  blacklevel = (blacklevel >> 2) | ((blacklevel & 3) << 8);
+		  whitelevel = (whitelevel >> 2) | ((whitelevel & 3) << 8);
+		  maven_set_reg_pair(md->client, 0x0e, blacklevel);
+		  maven_set_reg_pair(md->client, 0x1e, whitelevel);
+		}
+		break;
+		case MATROXFB_CID_SATURATION:
+		{
+		  maven_set_reg(md->client, 0x20, p->value);
+		  maven_set_reg(md->client, 0x22, p->value);
+		}
+		break;
+		case MATROXFB_CID_HUE:
+		{
+		  maven_set_reg(md->client, 0x25, p->value);
+		}
+		break;
+		case MATROXFB_CID_GAMMA:
+		{
+		  const struct maven_gamma* g;
+		  g = maven_compute_gamma(md);
+		  maven_set_reg(md->client, 0x83, g->reg83);
+		  maven_set_reg(md->client, 0x84, g->reg84);
+		  maven_set_reg(md->client, 0x85, g->reg85);
+		  maven_set_reg(md->client, 0x86, g->reg86);
+		  maven_set_reg(md->client, 0x87, g->reg87);
+		  maven_set_reg(md->client, 0x88, g->reg88);
+		  maven_set_reg(md->client, 0x89, g->reg89);
+		  maven_set_reg(md->client, 0x8a, g->reg8a);
+		  maven_set_reg(md->client, 0x8b, g->reg8b);
+		}
+		break;
+		case MATROXFB_CID_TESTOUT:
+		{
+			unsigned char val 
+			  = maven_get_reg (md->client,0x8d);
+			if (p->value) val |= 0x10;
+			else          val &= ~0x10;
+			maven_set_reg (md->client, 0x8d, val);
+		}
+		break;
+		case MATROXFB_CID_DEFLICKER:
+		{
+		  maven_set_reg(md->client, 0x93, maven_compute_deflicker(md));
+		}
+		break;
+	}
+	
+
+	return 0;
+}
+
+static int maven_get_control (struct maven_data* md, 
+			      struct matroxfb_control *p) {
+	int i;
+	
+	i = get_ctrl_id(p->id);
+	if (i < 0) return -EINVAL;
+	p->value = *get_ctrl_ptr(md, i);
 	return 0;
 }
 
@@ -890,56 +1155,73 @@
 }
 
 static int maven_out_start(void* md) {
-	return maven_resync(md);
+	return maven_start(md);
 }
 
-static void maven_out_incuse(void* md) {
-	if (md)
-		i2c_inc_use_client(((struct maven_data*)md)->client);
+static int maven_out_verify_mode(void* md, u_int32_t arg) {
+	switch (arg) {
+		case MATROXFB_OUTPUT_MODE_PAL:
+		case MATROXFB_OUTPUT_MODE_NTSC:
+		case MATROXFB_OUTPUT_MODE_MONITOR:
+			return 0;
+	}
+	return -EINVAL;
 }
 
-static void maven_out_decuse(void* md) {
-	if (md)
-		i2c_dec_use_client(((struct maven_data*)md)->client);
+static int maven_out_get_queryctrl(void* md, struct matroxfb_queryctrl* p) {
+        return maven_get_queryctrl(md, p);
 }
 
-static int maven_out_set_mode(void* md, u_int32_t arg) {
-	return maven_set_output_mode(md, arg);
+static int maven_out_get_ctrl(void* md, struct matroxfb_control* p) {
+	return maven_get_control(md, p);
 }
 
-static int maven_out_get_mode(void* md, u_int32_t* arg) {
-	return maven_get_output_mode(md, arg);
+static int maven_out_set_ctrl(void* md, struct matroxfb_control* p) {
+	return maven_set_control(md, p);
 }
 
 static struct matrox_altout maven_altout = {
-	maven_out_compute,
-	maven_out_program,
-	maven_out_start,
-	maven_out_incuse,
-	maven_out_decuse,
-	maven_out_set_mode,
-	maven_out_get_mode
+	.owner   = THIS_MODULE,
+	.name	 = "Secondary output",
+	.compute = maven_out_compute,
+	.program = maven_out_program,
+	.start   = maven_out_start,
+	.verifymode = maven_out_verify_mode,
+	.getqueryctrl = maven_out_get_queryctrl,
+	.getctrl = maven_out_get_ctrl,
+	.setctrl = maven_out_set_ctrl,
 };
 
 static int maven_init_client(struct i2c_client* clnt) {
 	struct i2c_adapter* a = clnt->adapter;
 	struct maven_data* md = clnt->data;
-	struct matroxfb_dh_maven_info* m2info __attribute__((unused)) = ((struct i2c_bit_adapter*)a)->minfo;
-	MINFO_FROM(m2info->primary_dev);
+	MINFO_FROM(list_entry(a, struct i2c_bit_adapter, adapter)->minfo);
 
-	md->mode = MODE_MONITOR;
 	md->primary_head = MINFO;
 	md->client = clnt;
 	down_write(&ACCESS_FBINFO(altout.lock));
-	ACCESS_FBINFO(altout.device) = md;
-	ACCESS_FBINFO(altout.output) = &maven_altout;
+	ACCESS_FBINFO(outputs[1]).output = &maven_altout;
+	ACCESS_FBINFO(outputs[1]).src = MATROXFB_SRC_NONE;
+	ACCESS_FBINFO(outputs[1]).data = md;
+	ACCESS_FBINFO(outputs[1]).mode = MATROXFB_OUTPUT_MODE_MONITOR;
 	up_write(&ACCESS_FBINFO(altout.lock));
-	ACCESS_FBINFO(output.all) |= MATROXFB_OUTPUT_CONN_SECONDARY;
 	if (maven_get_reg(clnt, 0xB2) < 0x14) {
 		md->version = MGATVO_B;
+		/* Tweak some things for this old chip */
 	} else {
 		md->version = MGATVO_C;
 	}
+	/*
+	 * Set all parameters to its initial values.
+	 */
+	{
+		unsigned int i;
+
+		for (i = 0; i < MAVCTRLS; ++i) {
+			*get_ctrl_ptr(md, i) = maven_controls[i].desc.default_value;
+		}
+	}
+
 	return 0;
 }
 
@@ -947,13 +1229,14 @@
 	struct maven_data* md = clnt->data;
 
 	if (md->primary_head) {
-		md->primary_head->output.all &= ~MATROXFB_OUTPUT_CONN_SECONDARY;
-		md->primary_head->output.ph &= ~MATROXFB_OUTPUT_CONN_SECONDARY;
-		md->primary_head->output.sh &= ~MATROXFB_OUTPUT_CONN_SECONDARY;
-		down_write(&md->primary_head->altout.lock);
-		md->primary_head->altout.device = NULL;
-		md->primary_head->altout.output = NULL;
-		up_write(&md->primary_head->altout.lock);
+		MINFO_FROM(md->primary_head);
+
+		down_write(&ACCESS_FBINFO(altout.lock));
+		ACCESS_FBINFO(outputs[1]).src = MATROXFB_SRC_NONE;
+		ACCESS_FBINFO(outputs[1]).output = NULL;
+		ACCESS_FBINFO(outputs[1]).data = NULL;
+		ACCESS_FBINFO(outputs[1]).mode = MATROXFB_OUTPUT_MODE_MONITOR;
+		up_write(&ACCESS_FBINFO(altout.lock));
 		md->primary_head = NULL;
 	}
 	return 0;
@@ -1066,7 +1349,7 @@
 		i2c_del_driver(&maven_driver);
 }
 
-MODULE_AUTHOR("(c) 1999-2001 Petr Vandrovec <vandrove@vc.cvut.cz>");
+MODULE_AUTHOR("(c) 1999-2002 Petr Vandrovec <vandrove@vc.cvut.cz>");
 MODULE_DESCRIPTION("Matrox G200/G400 Matrox MGA-TVO driver");
 MODULE_LICENSE("GPL");
 module_init(matroxfb_maven_init);
diff -urN linux-2.4.21-pre2/drivers/video/matrox/matroxfb_maven.h linux-2.4.21-pre3/drivers/video/matrox/matroxfb_maven.h
--- linux-2.4.21-pre2/drivers/video/matrox/matroxfb_maven.h	Mon Dec 11 13:16:53 2000
+++ linux-2.4.21-pre3/drivers/video/matrox/matroxfb_maven.h	Mon Jan  6 16:32:32 2003
@@ -6,21 +6,15 @@
 #include <linux/i2c-algo-bit.h>
 #include "matroxfb_base.h"
 
-struct matroxfb_dh_maven_info;
-
 struct i2c_bit_adapter {
 	struct i2c_adapter		adapter;
 	int				initialized;
 	struct i2c_algo_bit_data	bac;
-	struct matroxfb_dh_maven_info  *minfo;
-};
-
-struct matroxfb_dh_maven_info {
-	struct matrox_fb_info*	primary_dev;
-
-	struct i2c_bit_adapter	maven;
-	struct i2c_bit_adapter	ddc1;
-	struct i2c_bit_adapter	ddc2;
+	struct matrox_fb_info*		minfo;
+	struct {
+		unsigned int		data;
+		unsigned int		clock;
+				      } mask;
 };
 
 #endif /* __MATROXFB_MAVEN_H__ */
diff -urN linux-2.4.21-pre2/drivers/video/matrox/matroxfb_misc.c linux-2.4.21-pre3/drivers/video/matrox/matroxfb_misc.c
--- linux-2.4.21-pre2/drivers/video/matrox/matroxfb_misc.c	Fri Aug  2 17:39:45 2002
+++ linux-2.4.21-pre3/drivers/video/matrox/matroxfb_misc.c	Mon Jan  6 16:32:32 2003
@@ -2,11 +2,11 @@
  *
  * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200 and G400
  *
- * (c) 1998,1999,2000,2001 Petr Vandrovec <vandrove@vc.cvut.cz>
+ * (c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz>
  *
  * Portions Copyright (c) 2001 Matrox Graphics Inc.
  *
- * Version: 1.62 2001/11/29
+ * Version: 1.66 2002/10/14
  *
  * MTRR stuff: 1998 Tom Rini <trini@kernel.crashing.org>
  *
@@ -146,6 +146,7 @@
 	if (!pixclock) pixclock = 10000;	/* 10ns = 100MHz */
 	mt->pixclock = 1000000000 / pixclock;
 	if (mt->pixclock < 1) mt->pixclock = 1;
+	mt->mnp = -1;
 	mt->dblscan = var->vmode & FB_VMODE_DOUBLE;
 	mt->interlaced = var->vmode & FB_VMODE_INTERLACED;
 	mt->HDisplay = var->xres;
@@ -360,7 +361,8 @@
 			  ((hd      & 0x100) >> 7) | /* blanking */
 			  ((hs      & 0x100) >> 6) | /* sync start */
 			   (hbe     & 0x040);	 /* end hor. blanking */
-	if (ACCESS_FBINFO(output.ph) & MATROXFB_OUTPUT_CONN_SECONDARY)
+	/* FIXME: Enable vidrst only on G400, and only if TV-out is used */
+	if (ACCESS_FBINFO(outputs[1]).src == MATROXFB_SRC_CRTC1)
 		hw->CRTCEXT[1] |= 0x88;		/* enable horizontal and vertical vidrst */
 	hw->CRTCEXT[2] =  ((vt & 0xC00) >> 10) |
 			  ((vd & 0x400) >>  8) |	/* disp end */
@@ -847,7 +849,7 @@
 					  ( bd->pins[86]        & 0x0000000F);
 	MINFO->values.reg.opt		= ((bd->pins[53] << 15) & 0x00400000) |
 					  ((bd->pins[53] << 22) & 0x10000000) |
-					  ((bd->pins[53] << 10) & 0x00001C00);
+					  ((bd->pins[53] <<  7) & 0x00001C00);
 	MINFO->values.reg.opt3		= get_u32(bd->pins + 67);
 	MINFO->values.pll.system	= (bd->pins[ 65] == 0xFF) ? 200000 			: bd->pins[ 65] * 4000;
 	MINFO->features.pll.ref_freq	= (bd->pins[ 92] & 0x01) ? 14318 : 27000;
@@ -872,7 +874,7 @@
 	mult = bd->pins[4]?8000:6000;
 	
 	MINFO->limits.pixel.vcomax	= (bd->pins[ 38] == 0xFF) ? 600000			: bd->pins[ 38] * mult;
-	MINFO->limits.system.vcomax	= (bd->pins[ 36] == 0xFF) ? MINFO->limits.pixel.vcomax	: bd->pins[ 39] * mult;
+	MINFO->limits.system.vcomax	= (bd->pins[ 36] == 0xFF) ? MINFO->limits.pixel.vcomax	: bd->pins[ 36] * mult;
 	MINFO->limits.video.vcomax	= (bd->pins[ 37] == 0xFF) ? MINFO->limits.system.vcomax	: bd->pins[ 37] * mult;
 	MINFO->limits.pixel.vcomin	= (bd->pins[123] == 0xFF) ? 256000			: bd->pins[123] * mult;
 	MINFO->limits.system.vcomin	= (bd->pins[121] == 0xFF) ? MINFO->limits.pixel.vcomin	: bd->pins[121] * mult;
@@ -1011,6 +1013,6 @@
 EXPORT_SYMBOL(matroxfb_vgaHWrestore);		/* DAC1064, Ti3026 */
 EXPORT_SYMBOL(matroxfb_read_pins);
 
-MODULE_AUTHOR("(c) 1999-2001 Petr Vandrovec <vandrove@vc.cvut.cz>");
+MODULE_AUTHOR("(c) 1999-2002 Petr Vandrovec <vandrove@vc.cvut.cz>");
 MODULE_DESCRIPTION("Miscellaneous support for Matrox video cards");
 MODULE_LICENSE("GPL");
diff -urN linux-2.4.21-pre2/drivers/video/matrox/matroxfb_proc.c linux-2.4.21-pre3/drivers/video/matrox/matroxfb_proc.c
--- linux-2.4.21-pre2/drivers/video/matrox/matroxfb_proc.c	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre3/drivers/video/matrox/matroxfb_proc.c	Mon Jan  6 16:32:32 2003
@@ -0,0 +1,151 @@
+/*
+ *
+ * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200, G400 and G450.
+ *
+ * (c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz>
+ *
+ * Version: 1.64 2002/06/10
+ *
+ */
+
+#include "matroxfb_base.h"
+#include <linux/proc_fs.h>
+
+static struct proc_dir_entry* mga_pde;
+
+struct procinfo {
+	struct matrox_fb_info* info;
+	struct proc_dir_entry* pde;
+};
+
+static inline void remove_pde(struct proc_dir_entry* pde) {
+	if (pde) {
+		remove_proc_entry(pde->name, pde->parent);
+	}
+}
+
+#ifndef CONFIG_PROC_FS
+static int bios_read_proc(char* buffer, char** start, off_t offset,
+			  int size, int *eof, void *data) {
+	return 0;
+}
+
+static int pins_read_proc(char* buffer, char** start, off_t offset,
+			  int size, int *eof, void *data) {
+	return 0;
+}
+#else
+/* This macro frees the machine specific function from bounds checking and
+ * this like that... */
+#define PRINT_PROC(fmt,args...)					\
+	do {							\
+		len += sprintf(buffer+len, fmt, ##args );	\
+		if (begin + len > offset + size)		\
+			break;					\
+		if (begin + len < offset) {			\
+			begin += len;				\
+			len = 0;				\
+		}						\
+	} while(0)
+
+static int bios_read_proc(char* buffer, char** start, off_t offset,
+			  int size, int *eof, void *data) {
+	int len = 0;
+	off_t begin = 0;
+	struct matrox_bios* bd = data;
+
+	do {
+		*eof = 0;
+		if (bd->bios_valid) {
+			PRINT_PROC("BIOS:   %u.%u.%u\n", bd->version.vMaj, bd->version.vMin, bd->version.vRev);
+			PRINT_PROC("Output: 0x%02X\n", bd->output.state);
+			PRINT_PROC("TVOut:  %s\n", bd->output.tvout?"yes":"no");
+			PRINT_PROC("PINS:   %s\n", bd->pins_len ? "found" : "not found");
+			PRINT_PROC("Info:   %p\n", bd);
+		} else {
+			PRINT_PROC("BIOS:   Invalid\n");
+		}
+		*eof = 1;
+	} while (0);
+	if (offset >= begin + len)
+		return 0;
+	*start = buffer + (offset - begin);
+	return size < begin + len - offset ? size : begin + len - offset;
+}
+
+static int pins_read_proc(char* buffer, char** start, off_t offset,
+			  int size, int *eof, void *data) {
+	struct matrox_bios* bd = data;
+	
+	if (offset >= bd->pins_len) {
+		*eof = 1;
+		return 0;
+	}
+	if (offset + size >= bd->pins_len) {
+		size = bd->pins_len - offset;
+		*eof = 1;
+	}
+	memcpy(buffer, bd->pins + offset, size);
+	*start = buffer;
+	return size;
+}
+#endif /* CONFIG_PROC_FS */
+
+static void* matroxfb_proc_probe(struct matrox_fb_info* minfo) {
+	struct procinfo* binfo;
+	char b[10];
+
+	binfo = (struct procinfo*)kmalloc(sizeof(*binfo), GFP_KERNEL);
+	if (!binfo) {
+		printk(KERN_ERR "matroxfb_proc: Not enough memory for /proc control structs\n");
+		return NULL;
+	}
+	binfo->info = minfo;
+	sprintf(b, "fb%u", GET_FB_IDX(minfo->fbcon.node));
+	binfo->pde = proc_mkdir(b, mga_pde);
+	if (binfo->pde) {
+		create_proc_read_entry("bios", 0, binfo->pde, bios_read_proc, &minfo->bios);
+		if (minfo->bios.pins_len) {
+			struct proc_dir_entry* p = create_proc_read_entry("pins", 0, binfo->pde, pins_read_proc, &minfo->bios);
+			if (p) {
+				p->size = minfo->bios.pins_len;
+			}
+		}
+	}
+	return binfo;
+}
+
+static void matroxfb_proc_remove(struct matrox_fb_info* minfo, void* binfoI) {
+	struct procinfo* binfo = binfoI;
+
+	if (binfo->pde) {
+		remove_proc_entry("pins", binfo->pde);
+		remove_proc_entry("bios", binfo->pde);
+		remove_pde(binfo->pde);
+	}
+	kfree(binfo);
+}
+
+static struct matroxfb_driver procfn = {
+	.name =		"Matrox /proc driver",
+	.probe =	matroxfb_proc_probe,
+	.remove =	matroxfb_proc_remove
+};
+
+static int matroxfb_proc_init(void) {
+	mga_pde = proc_mkdir("driver/mga", NULL);
+	matroxfb_register_driver(&procfn);
+	return 0;
+}
+
+static void matroxfb_proc_exit(void) {
+	matroxfb_unregister_driver(&procfn);
+	remove_pde(mga_pde);
+}
+
+MODULE_AUTHOR("(c) 2001-2002 Petr Vandrovec <vandrove@vc.cvut.cz>");
+MODULE_DESCRIPTION("Matrox /proc driver");
+MODULE_LICENSE("GPL");
+module_init(matroxfb_proc_init);
+module_exit(matroxfb_proc_exit);
+/* we do not have __setup() */
diff -urN linux-2.4.21-pre2/fs/exec.c linux-2.4.21-pre3/fs/exec.c
--- linux-2.4.21-pre2/fs/exec.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/fs/exec.c	Mon Jan  6 16:32:33 2003
@@ -35,6 +35,8 @@
 #include <linux/highmem.h>
 #include <linux/spinlock.h>
 #include <linux/personality.h>
+#include <linux/swap.h>
+#include <linux/utsname.h>
 #define __NO_VERSION__
 #include <linux/module.h>
 
@@ -47,6 +49,8 @@
 #endif
 
 int core_uses_pid;
+char core_pattern[65] = "core";
+/* The maximal length of core_pattern is also specified in sysctl.c */ 
 
 static struct linux_binfmt *formats;
 static rwlock_t binfmt_lock = RW_LOCK_UNLOCKED;
@@ -953,10 +957,126 @@
 		__MOD_DEC_USE_COUNT(old->module);
 }
 
+#define CORENAME_MAX_SIZE 64
+
+/* format_corename will inspect the pattern parameter, and output a
+ * name into corename, which must have space for at least
+ * CORENAME_MAX_SIZE bytes plus one byte for the zero terminator.
+ */
+void format_corename(char *corename, const char *pattern, long signr)
+{
+	const char *pat_ptr = pattern;
+	char *out_ptr = corename;
+	char *const out_end = corename + CORENAME_MAX_SIZE;
+	int rc;
+	int pid_in_pattern = 0;
+
+	/* Repeat as long as we have more pattern to process and more output
+	   space */
+	while (*pat_ptr) {
+		if (*pat_ptr != '%') {
+			if (out_ptr == out_end)
+				goto out;
+			*out_ptr++ = *pat_ptr++;
+		} else {
+			switch (*++pat_ptr) {
+			case 0:
+				goto out;
+			/* Double percent, output one percent */
+			case '%':
+				if (out_ptr == out_end)
+					goto out;
+				*out_ptr++ = '%';
+				break;
+			/* pid */
+			case 'p':
+				pid_in_pattern = 1;
+				rc = snprintf(out_ptr, out_end - out_ptr,
+					      "%d", current->pid);
+				if (rc > out_end - out_ptr)
+					goto out;
+				out_ptr += rc;
+				break;
+			/* uid */
+			case 'u':
+				rc = snprintf(out_ptr, out_end - out_ptr,
+					      "%d", current->uid);
+				if (rc > out_end - out_ptr)
+					goto out;
+				out_ptr += rc;
+				break;
+			/* gid */
+			case 'g':
+				rc = snprintf(out_ptr, out_end - out_ptr,
+					      "%d", current->gid);
+				if (rc > out_end - out_ptr)
+					goto out;
+				out_ptr += rc;
+				break;
+			/* signal that caused the coredump */
+			case 's':
+				rc = snprintf(out_ptr, out_end - out_ptr,
+					      "%ld", signr);
+				if (rc > out_end - out_ptr)
+					goto out;
+				out_ptr += rc;
+				break;
+			/* UNIX time of coredump */
+			case 't': {
+				struct timeval tv;
+				do_gettimeofday(&tv);
+				rc = snprintf(out_ptr, out_end - out_ptr,
+					      "%ld", tv.tv_sec);
+				if (rc > out_end - out_ptr)
+					goto out;
+				out_ptr += rc;
+				break;
+			}
+			/* hostname */
+			case 'h':
+				down_read(&uts_sem);
+				rc = snprintf(out_ptr, out_end - out_ptr,
+					      "%s", system_utsname.nodename);
+				up_read(&uts_sem);
+				if (rc > out_end - out_ptr)
+					goto out;
+				out_ptr += rc;
+				break;
+			/* executable */
+			case 'e':
+				rc = snprintf(out_ptr, out_end - out_ptr,
+					      "%s", current->comm);
+				if (rc > out_end - out_ptr)
+					goto out;
+				out_ptr += rc;
+				break;
+			default:
+				break;
+			}
+			++pat_ptr;
+		}
+	}
+	/* Backward compatibility with core_uses_pid:
+	 *
+	 * If core_pattern does not include a %p (as is the default)
+	 * and core_uses_pid is set, then .%pid will be appended to
+	 * the filename */
+	if (!pid_in_pattern
+            && (core_uses_pid || atomic_read(&current->mm->mm_users) != 1)) {
+		rc = snprintf(out_ptr, out_end - out_ptr,
+			      ".%d", current->pid);
+		if (rc > out_end - out_ptr)
+			goto out;
+		out_ptr += rc;
+	}
+      out:
+	*out_ptr = 0;
+}
+
 int do_coredump(long signr, struct pt_regs * regs)
 {
 	struct linux_binfmt * binfmt;
-	char corename[6+sizeof(current->comm)+10];
+	char corename[CORENAME_MAX_SIZE + 1];
 	struct file * file;
 	struct inode * inode;
 	int retval = 0;
@@ -971,9 +1091,7 @@
 	if (current->rlim[RLIMIT_CORE].rlim_cur < binfmt->min_coredump)
 		goto fail;
 
-	memcpy(corename,"core", 5); /* include trailing \0 */
- 	if (core_uses_pid || atomic_read(&current->mm->mm_users) != 1)
- 		sprintf(&corename[4], ".%d", current->pid);
+ 	format_corename(corename, core_pattern, signr);
 	file = filp_open(corename, O_CREAT | 2 | O_NOFOLLOW, 0600);
 	if (IS_ERR(file))
 		goto fail;
diff -urN linux-2.4.21-pre2/fs/ext3/ialloc.c linux-2.4.21-pre3/fs/ext3/ialloc.c
--- linux-2.4.21-pre2/fs/ext3/ialloc.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/fs/ext3/ialloc.c	Mon Jan  6 16:32:33 2003
@@ -64,8 +64,8 @@
 	if (!bh) {
 		ext3_error (sb, "read_inode_bitmap",
 			    "Cannot read inode bitmap - "
-			    "block_group = %lu, inode_bitmap = %lu",
-			    block_group, (unsigned long) gdp->bg_inode_bitmap);
+			    "block_group = %lu, inode_bitmap = %u",
+			    block_group, gdp->bg_inode_bitmap);
 		retval = -EIO;
 	}
 	/*
@@ -531,19 +531,19 @@
 }
 
 /* Verify that we are loading a valid orphan from disk */
-struct inode *ext3_orphan_get (struct super_block * sb, ino_t ino)
+struct inode *ext3_orphan_get(struct super_block *sb, unsigned long ino)
 {
-	ino_t max_ino = le32_to_cpu(EXT3_SB(sb)->s_es->s_inodes_count);
+	unsigned long max_ino = le32_to_cpu(EXT3_SB(sb)->s_es->s_inodes_count);
 	unsigned long block_group;
 	int bit;
 	int bitmap_nr;
 	struct buffer_head *bh;
 	struct inode *inode = NULL;
-	
+
 	/* Error cases - e2fsck has already cleaned up for us */
 	if (ino > max_ino) {
 		ext3_warning(sb, __FUNCTION__,
-			     "bad orphan ino %ld!  e2fsck was run?\n", ino);
+			     "bad orphan ino %lu!  e2fsck was run?\n", ino);
 		return NULL;
 	}
 
@@ -552,7 +552,7 @@
 	if ((bitmap_nr = load_inode_bitmap(sb, block_group)) < 0 ||
 	    !(bh = EXT3_SB(sb)->s_inode_bitmap[bitmap_nr])) {
 		ext3_warning(sb, __FUNCTION__,
-			     "inode bitmap error for orphan %ld\n", ino);
+			     "inode bitmap error for orphan %lu\n", ino);
 		return NULL;
 	}
 
@@ -563,16 +563,16 @@
 	if (!ext3_test_bit(bit, bh->b_data) || !(inode = iget(sb, ino)) ||
 	    is_bad_inode(inode) || NEXT_ORPHAN(inode) > max_ino) {
 		ext3_warning(sb, __FUNCTION__,
-			     "bad orphan inode %ld!  e2fsck was run?\n", ino);
+			     "bad orphan inode %lu!  e2fsck was run?\n", ino);
 		printk(KERN_NOTICE "ext3_test_bit(bit=%d, block=%ld) = %d\n",
 		       bit, bh->b_blocknr, ext3_test_bit(bit, bh->b_data));
 		printk(KERN_NOTICE "inode=%p\n", inode);
 		if (inode) {
 			printk(KERN_NOTICE "is_bad_inode(inode)=%d\n",
 			       is_bad_inode(inode));
-			printk(KERN_NOTICE "NEXT_ORPHAN(inode)=%d\n",
+			printk(KERN_NOTICE "NEXT_ORPHAN(inode)=%u\n",
 			       NEXT_ORPHAN(inode));
-			printk(KERN_NOTICE "max_ino=%ld\n", max_ino);
+			printk(KERN_NOTICE "max_ino=%lu\n", max_ino);
 		}
 		/* Avoid freeing blocks if we got a bad deleted inode */
 		if (inode && inode->i_nlink == 0)
diff -urN linux-2.4.21-pre2/fs/ext3/namei.c linux-2.4.21-pre3/fs/ext3/namei.c
--- linux-2.4.21-pre2/fs/ext3/namei.c	Mon Jan  6 16:32:29 2003
+++ linux-2.4.21-pre3/fs/ext3/namei.c	Mon Jan  6 16:32:33 2003
@@ -716,10 +716,10 @@
 {
 	struct list_head *prev;
 	struct ext3_sb_info *sbi;
-	ino_t ino_next; 
+	unsigned long ino_next;
 	struct ext3_iloc iloc;
 	int err = 0;
-	
+
 	lock_super(inode->i_sb);
 	if (list_empty(&inode->u.ext3_i.i_orphan)) {
 		unlock_super(inode->i_sb);
@@ -730,7 +730,7 @@
 	prev = inode->u.ext3_i.i_orphan.prev;
 	sbi = EXT3_SB(inode->i_sb);
 
-	jbd_debug(4, "remove inode %ld from orphan list\n", inode->i_ino);
+	jbd_debug(4, "remove inode %lu from orphan list\n", inode->i_ino);
 
 	list_del(&inode->u.ext3_i.i_orphan);
 	INIT_LIST_HEAD(&inode->u.ext3_i.i_orphan);
@@ -741,13 +741,13 @@
 	 * list in memory. */
 	if (!handle)
 		goto out;
-	
+
 	err = ext3_reserve_inode_write(handle, inode, &iloc);
 	if (err)
 		goto out_err;
 
 	if (prev == &sbi->s_orphan) {
-		jbd_debug(4, "superblock will point to %ld\n", ino_next);
+		jbd_debug(4, "superblock will point to %lu\n", ino_next);
 		BUFFER_TRACE(sbi->s_sbh, "get_write_access");
 		err = ext3_journal_get_write_access(handle, sbi->s_sbh);
 		if (err)
@@ -758,8 +758,8 @@
 		struct ext3_iloc iloc2;
 		struct inode *i_prev =
 			list_entry(prev, struct inode, u.ext3_i.i_orphan);
-		
-		jbd_debug(4, "orphan inode %ld will point to %ld\n",
+
+		jbd_debug(4, "orphan inode %lu will point to %lu\n",
 			  i_prev->i_ino, ino_next);
 		err = ext3_reserve_inode_write(handle, i_prev, &iloc2);
 		if (err)
@@ -774,7 +774,7 @@
 	if (err)
 		goto out_brelse;
 
-out_err: 	
+out_err:
 	ext3_std_error(inode->i_sb, err);
 out:
 	unlock_super(inode->i_sb);
diff -urN linux-2.4.21-pre2/fs/jbd/journal.c linux-2.4.21-pre3/fs/jbd/journal.c
--- linux-2.4.21-pre2/fs/jbd/journal.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/fs/jbd/journal.c	Mon Jan  6 16:32:33 2003
@@ -730,14 +730,21 @@
  * need to set up all of the mapping information to tell the journaling
  * system where the journal blocks are.
  *
- * journal_init_dev creates a journal which maps a fixed contiguous
- * range of blocks on an arbitrary block device.
- *
- * journal_init_inode creates a journal which maps an on-disk inode as
- * the journal.  The inode must exist already, must support bmap() and
- * must have all data blocks preallocated.
  */
 
+ /**
+  *  journal_t * journal_init_dev() - creates an initialises a journal structure
+  *  @kdev: Block device on which to create the journal
+  *  @fs_dev: Device which hold journalled filesystem for this journal.
+  *  @start: Block nr Start of journal.
+  *  @len:  Lenght of the journal in blocks.
+  *  @blocksize: blocksize of journalling device
+  *  @returns: a newly created journal_t *
+  *  
+  *  journal_init_dev creates a journal which maps a fixed contiguous
+  *  range of blocks on an arbitrary block device.
+  * 
+  */
 journal_t * journal_init_dev(kdev_t dev, kdev_t fs_dev,
 			int start, int len, int blocksize)
 {
@@ -760,7 +767,15 @@
 
 	return journal;
 }
-
+ 
+/** 
+ *  journal_t * journal_init_inode () - creates a journal which maps to a inode.
+ *  @inode: An inode to create the journal in
+ *  
+ * journal_init_inode creates a journal which maps an on-disk inode as
+ * the journal.  The inode must exist already, must support bmap() and
+ * must have all data blocks preallocated.
+ */
 journal_t * journal_init_inode (struct inode *inode)
 {
 	struct buffer_head *bh;
@@ -850,12 +865,15 @@
 	return 0;
 }
 
-/*
+/** 
+ * int journal_create() - Initialise the new journal file
+ * @journal: Journal to create. This structure must have been initialised
+ * 
  * Given a journal_t structure which tells us which disk blocks we can
  * use, create a new journal superblock and initialise all of the
- * journal fields from scratch.  */
-
-int journal_create (journal_t *journal)
+ * journal fields from scratch.  
+ **/
+int journal_create(journal_t *journal)
 {
 	unsigned long blocknr;
 	struct buffer_head *bh;
@@ -916,11 +934,14 @@
 	return journal_reset(journal);
 }
 
-/*
+/** 
+ * void journal_update_superblock() - Update journal sb on disk.
+ * @journal: The journal to update.
+ * @wait: Set to '0' if you don't want to wait for IO completion.
+ *
  * Update a journal's dynamic superblock fields and write it to disk,
  * optionally waiting for the IO to complete.
-*/
-
+ */
 void journal_update_superblock(journal_t *journal, int wait)
 {
 	journal_superblock_t *sb = journal->j_superblock;
@@ -1036,12 +1057,14 @@
 }
 
 
-/*
+/**
+ * int journal_load() - Read journal from disk.
+ * @journal: Journal to act on.
+ * 
  * Given a journal_t structure which tells us which disk blocks contain
  * a journal, read the journal from disk to initialise the in-memory
  * structures.
  */
-
 int journal_load(journal_t *journal)
 {
 	int err;
@@ -1086,11 +1109,13 @@
 	return -EIO;
 }
 
-/*
+/**
+ * void journal_destroy() - Release a journal_t structure.
+ * @journal: Journal to act on.
+* 
  * Release a journal_t structure once it is no longer in use by the
  * journaled object.
  */
-
 void journal_destroy (journal_t *journal)
 {
 	/* Wait for the commit thread to wake up and die. */
@@ -1128,8 +1153,12 @@
 }
 
 
-/* Published API: Check whether the journal uses all of a given set of
- * features.  Return true (non-zero) if it does. */
+/**
+ *int journal_check_used_features () - Check if features specified are used.
+ * 
+ * Check whether the journal uses all of a given set of
+ * features.  Return true (non-zero) if it does. 
+ **/
 
 int journal_check_used_features (journal_t *journal, unsigned long compat,
 				 unsigned long ro, unsigned long incompat)
@@ -1151,7 +1180,10 @@
 	return 0;
 }
 
-/* Published API: Check whether the journaling code supports the use of
+/**
+ * int journal_check_available_features() - Check feature set in journalling layer
+ * 
+ * Check whether the journaling code supports the use of
  * all of a given set of features on this journal.  Return true
  * (non-zero) if it can. */
 
@@ -1180,8 +1212,13 @@
 	return 0;
 }
 
-/* Published API: Mark a given journal feature as present on the
- * superblock.  Returns true if the requested features could be set. */
+/**
+ * int journal_set_features () - Mark a given journal feature in the superblock
+ *
+ * Mark a given journal feature as present on the
+ * superblock.  Returns true if the requested features could be set. 
+ *
+ */
 
 int journal_set_features (journal_t *journal, unsigned long compat,
 			  unsigned long ro, unsigned long incompat)
@@ -1207,12 +1244,12 @@
 }
 
 
-/*
- * Published API:
+/**
+ * int journal_update_format () - Update on-disk journal structure.
+ *
  * Given an initialised but unloaded journal struct, poke about in the
  * on-disk structure to update it to the most recent supported version.
  */
-
 int journal_update_format (journal_t *journal)
 {
 	journal_superblock_t *sb;
@@ -1262,7 +1299,10 @@
 }
 
 
-/*
+/**
+ * int journal_flush () - Flush journal
+ * @journal: Journal to act on.
+ * 
  * Flush all data for a given journal to disk and empty the journal.
  * Filesystems can use this when remounting readonly to ensure that
  * recovery does not need to happen on remount.
@@ -1316,12 +1356,16 @@
 	return err;
 }
 
-/*
+/**
+ * int journal_wipe() - Wipe journal contents
+ * @journal: Journal to act on.
+ * @write: flag (see below)
+ * 
  * Wipe out all of the contents of a journal, safely.  This will produce
  * a warning if the journal contains any valid recovery information.
  * Must be called between journal_init_*() and journal_load().
  *
- * If (write) is non-zero, then we wipe out the journal on disk; otherwise
+ * If 'write' is non-zero, then we wipe out the journal on disk; otherwise
  * we merely suppress recovery.
  */
 
@@ -1370,43 +1414,11 @@
 }
 
 /*
- * journal_abort: perform a complete, immediate shutdown of the ENTIRE
- * journal (not of a single transaction).  This operation cannot be
- * undone without closing and reopening the journal.
- *
- * The journal_abort function is intended to support higher level error
- * recovery mechanisms such as the ext2/ext3 remount-readonly error
- * mode.
- *
- * Journal abort has very specific semantics.  Any existing dirty,
- * unjournaled buffers in the main filesystem will still be written to
- * disk by bdflush, but the journaling mechanism will be suspended
- * immediately and no further transaction commits will be honoured.
- *
- * Any dirty, journaled buffers will be written back to disk without
- * hitting the journal.  Atomicity cannot be guaranteed on an aborted
- * filesystem, but we _do_ attempt to leave as much data as possible
- * behind for fsck to use for cleanup.
- *
- * Any attempt to get a new transaction handle on a journal which is in
- * ABORT state will just result in an -EROFS error return.  A
- * journal_stop on an existing handle will return -EIO if we have
- * entered abort state during the update.
- *
- * Recursive transactions are not disturbed by journal abort until the
- * final journal_stop, which will receive the -EIO error.
- *
- * Finally, the journal_abort call allows the caller to supply an errno
- * which will be recored (if possible) in the journal superblock.  This
- * allows a client to record failure conditions in the middle of a
- * transaction without having to complete the transaction to record the
- * failure to disk.  ext3_error, for example, now uses this
- * functionality.
+ * Journal abort has very specific semantics, which we describe
+ * for journal abort. 
  *
- * Errors which originate from within the journaling layer will NOT
- * supply an errno; a null errno implies that absolutely no further
- * writes are done to the journal (unless there are any already in
- * progress).
+ * Two internal function, which provide abort to te jbd layer
+ * itself are here.
  */
 
 /* Quick version for internal journal use (doesn't lock the journal).
@@ -1444,7 +1456,52 @@
 		journal_update_superblock(journal, 1);
 }
 
-/* Full version for external use */
+/**
+ * void journal_abort () - Shutdown the journal immediately.
+ * @journal: the journal to shutdown.
+ * @errno:   an error number to record in the journal indicating
+ *           the reason for the shutdown.
+ *
+ * Perform a complete, immediate shutdown of the ENTIRE
+ * journal (not of a single transaction).  This operation cannot be
+ * undone without closing and reopening the journal.
+ *           
+ * The journal_abort function is intended to support higher level error
+ * recovery mechanisms such as the ext2/ext3 remount-readonly error
+ * mode.
+ *
+ * Journal abort has very specific semantics.  Any existing dirty,
+ * unjournaled buffers in the main filesystem will still be written to
+ * disk by bdflush, but the journaling mechanism will be suspended
+ * immediately and no further transaction commits will be honoured.
+ *
+ * Any dirty, journaled buffers will be written back to disk without
+ * hitting the journal.  Atomicity cannot be guaranteed on an aborted
+ * filesystem, but we _do_ attempt to leave as much data as possible
+ * behind for fsck to use for cleanup.
+ *
+ * Any attempt to get a new transaction handle on a journal which is in
+ * ABORT state will just result in an -EROFS error return.  A
+ * journal_stop on an existing handle will return -EIO if we have
+ * entered abort state during the update.
+ *
+ * Recursive transactions are not disturbed by journal abort until the
+ * final journal_stop, which will receive the -EIO error.
+ *
+ * Finally, the journal_abort call allows the caller to supply an errno
+ * which will be recorded (if possible) in the journal superblock.  This
+ * allows a client to record failure conditions in the middle of a
+ * transaction without having to complete the transaction to record the
+ * failure to disk.  ext3_error, for example, now uses this
+ * functionality.
+ *
+ * Errors which originate from within the journaling layer will NOT
+ * supply an errno; a null errno implies that absolutely no further
+ * writes are done to the journal (unless there are any already in
+ * progress).
+ * 
+ */
+
 void journal_abort (journal_t *journal, int errno)
 {
 	lock_journal(journal);
@@ -1452,6 +1509,17 @@
 	unlock_journal(journal);
 }
 
+/** 
+ * int journal_errno () - returns the journal's error state.
+ * @journal: journal to examine.
+ *
+ * This is the errno numbet set with journal_abort(), the last
+ * time the journal was mounted - if the journal was stopped
+ * without calling abort this will be 0.
+ *
+ * If the journal has been aborted on this mount time -EROFS will
+ * be returned.
+ */
 int journal_errno (journal_t *journal)
 {
 	int err;
@@ -1465,6 +1533,14 @@
 	return err;
 }
 
+
+
+/** 
+ * int journal_clear_err () - clears the journal's error state
+ *
+ * An error must be cleared or Acked to take a FS out of readonly
+ * mode.
+ */
 int journal_clear_err (journal_t *journal)
 {
 	int err = 0;
@@ -1478,6 +1554,13 @@
 	return err;
 }
 
+
+/** 
+ * void journal_ack_err() - Ack journal err.
+ *
+ * An error must be cleared or Acked to take a FS out of readonly
+ * mode.
+ */
 void journal_ack_err (journal_t *journal)
 {
 	lock_journal(journal);
@@ -1664,8 +1747,8 @@
  *
  * Whenever a buffer has an attached journal_head, its ->b_state:BH_JBD bit
  * is set.  This bit is tested in core kernel code where we need to take
- * JBD-specific actions.  Testing the zeroness of ->b_private is not reliable
- * there.
+ * JBD-specific actions.  Testing the zeroness of ->b_journal_head is not
+ * reliable there.
  *
  * When a buffer has its BH_JBD bit set, its ->b_count is elevated by one.
  *
diff -urN linux-2.4.21-pre2/fs/jbd/recovery.c linux-2.4.21-pre3/fs/jbd/recovery.c
--- linux-2.4.21-pre2/fs/jbd/recovery.c	Mon Feb 25 11:38:08 2002
+++ linux-2.4.21-pre3/fs/jbd/recovery.c	Mon Jan  6 16:32:33 2003
@@ -207,20 +207,22 @@
 		var -= ((journal)->j_last - (journal)->j_first);	\
 } while (0)
 
-/*
- * journal_recover
- *
+/**
+ * int journal_recover(journal_t *journal) - recovers a on-disk journal
+ * @journal: the journal to recover
+ * 
  * The primary function for recovering the log contents when mounting a
  * journaled device.  
- * 
+ */
+int journal_recover(journal_t *journal)
+{
+/*
  * Recovery is done in three passes.  In the first pass, we look for the
  * end of the log.  In the second, we assemble the list of revoke
  * blocks.  In the third and final pass, we replay any un-revoked blocks
  * in the log.  
  */
 
-int journal_recover(journal_t *journal)
-{
 	int			err;
 	journal_superblock_t *	sb;
 
@@ -264,20 +266,23 @@
 	return err;
 }
 
-/*
- * journal_skip_recovery
- *
+/**
+ * int journal_skip_recovery() - Start journal and wipe exiting records 
+ * @journal: journal to startup
+ * 
  * Locate any valid recovery information from the journal and set up the
  * journal structures in memory to ignore it (presumably because the
  * caller has evidence that it is out of date).  
- *
+ * This function does'nt appear to be exorted..
+ */
+int journal_skip_recovery(journal_t *journal)
+{
+/*
  * We perform one pass over the journal to allow us to tell the user how
  * much recovery information is being erased, and to let us initialise
  * the journal transaction sequence numbers to the next unused ID. 
  */
 
-int journal_skip_recovery(journal_t *journal)
-{
 	int			err;
 	journal_superblock_t *	sb;
 
diff -urN linux-2.4.21-pre2/fs/jbd/transaction.c linux-2.4.21-pre3/fs/jbd/transaction.c
--- linux-2.4.21-pre2/fs/jbd/transaction.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/fs/jbd/transaction.c	Mon Jan  6 16:32:33 2003
@@ -223,19 +223,20 @@
 	return handle;
 }
 
-/*
- * Obtain a new handle.  
+/**
+ * handle_t *journal_start() - Obtain a new handle.  
+ * @journal: Journal to start transaction on.
+ * @nblocks: number of block buffer we might modify
  *
  * We make sure that the transaction can guarantee at least nblocks of
  * modified buffers in the log.  We block until the log can guarantee
  * that much space.  
  *
- * This function is visible to journal users (like ext2fs), so is not
+ * This function is visible to journal users (like ext3fs), so is not
  * called with the journal already locked.
  *
  * Return a pointer to a newly allocated handle, or NULL on failure
  */
-
 handle_t *journal_start(journal_t *journal, int nblocks)
 {
 	handle_t *handle = journal_current_handle();
@@ -325,7 +326,11 @@
 	return ret;
 }
 
-/*
+/**
+ * handle_t *journal_try_start() - Don't block, but try and get a handle
+ * @journal: Journal to start transaction on.
+ * @nblocks: number of block buffer we might modify
+ * 
  * Try to start a handle, but non-blockingly.  If we weren't able
  * to, return an ERR_PTR value.
  */
@@ -369,16 +374,18 @@
 	return handle;
 }
 
-/*
- * journal_extend: extend buffer credits.
- *
+/**
+ * int journal_extend() - extend buffer credits.
+ * @handle:  handle to 'extend'
+ * @nblocks: nr blocks to try to extend by.
+ * 
  * Some transactions, such as large extends and truncates, can be done
  * atomically all at once or in several stages.  The operation requests
  * a credit for a number of buffer modications in advance, but can
  * extend its credit if it needs more.  
  *
  * journal_extend tries to give the running handle more buffer credits.
- * It does not guarantee that allocation: this is a best-effort only.
+ * It does not guarantee that allocation - this is a best-effort only.
  * The calling process MUST be able to deal cleanly with a failure to
  * extend here.
  *
@@ -387,7 +394,6 @@
  * return code < 0 implies an error
  * return code > 0 implies normal transaction-full status.
  */
-
 int journal_extend (handle_t *handle, int nblocks)
 {
 	transaction_t *transaction = handle->h_transaction;
@@ -436,8 +442,12 @@
 }
 
 
-/*
- * journal_restart: restart a handle for a multi-transaction filesystem
+/**
+ * int journal_restart() - restart a handle .
+ * @handle:  handle to restart
+ * @nblocks: nr credits requested
+ * 
+ * Restart a handle for a multi-transaction filesystem
  * operation.
  *
  * If the journal_extend() call above fails to grant new buffer credits
@@ -479,8 +489,9 @@
 }
 
 
-/* 
- * Barrier operation: establish a transaction barrier. 
+/**
+ * void journal_lock_updates () - establish a transaction barrier.
+ * @journal:  Journal to establish a barrier on.
  *
  * This locks out any further updates from being started, and blocks
  * until all existing updates have completed, returning only once the
@@ -488,7 +499,6 @@
  *
  * The journal lock should not be held on entry.
  */
-
 void journal_lock_updates (journal_t *journal)
 {
 	lock_journal(journal);
@@ -516,12 +526,14 @@
 	down(&journal->j_barrier);
 }
 
-/*
+/**
+ * void journal_unlock_updates (journal_t* journal) - release barrier
+ * @journal:  Journal to release the barrier on.
+ * 
  * Release a transaction barrier obtained with journal_lock_updates().
  *
  * Should be called without the journal lock held.
  */
-
 void journal_unlock_updates (journal_t *journal)
 {
 	lock_journal(journal);
@@ -535,23 +547,14 @@
 }
 
 /*
- * journal_get_write_access: notify intent to modify a buffer for metadata
- * (not data) update.
- *
- * If the buffer is already part of the current transaction, then there
- * is nothing we need to do.  If it is already part of a prior
+ * if the buffer is already part of the current transaction, then there
+ * is nothing we need to do.  if it is already part of a prior
  * transaction which we are still committing to disk, then we need to
  * make sure that we do not overwrite the old copy: we do copy-out to
- * preserve the copy going to disk.  We also account the buffer against
+ * preserve the copy going to disk.  we also account the buffer against
  * the handle's metadata buffer credits (unless the buffer is already
  * part of the transaction, that is).
- *
- * Returns an error code or 0 on success.
- *
- * In full data journalling mode the buffer may be of type BJ_AsyncData,
- * because we're write()ing a buffer which is also part of a shared mapping.
  */
-
 static int
 do_get_write_access(handle_t *handle, struct journal_head *jh, int force_copy) 
 {
@@ -756,6 +759,17 @@
 	return error;
 }
 
+/**
+ * int journal_get_write_access() - notify intent to modify a buffer for metadata (not data) update.
+ * @handle: transaction to add buffer modifications to
+ * @bh:     bh to be used for metadata writes
+ *
+ * Returns an error code or 0 on success.
+ *
+ * In full data journalling mode the buffer may be of type BJ_AsyncData,
+ * because we're write()ing a buffer which is also part of a shared mapping.
+ */
+
 int journal_get_write_access (handle_t *handle, struct buffer_head *bh) 
 {
 	transaction_t *transaction = handle->h_transaction;
@@ -786,6 +800,13 @@
  * There is no lock ranking violation: it was a newly created,
  * unlocked buffer beforehand. */
 
+/**
+ * int journal_get_create_access () - notify intent to use newly created bh
+ * @handle: ransaction to new buffer to
+ * @bh: new buffer.
+ *
+ * Call this if you create a new bh.
+ */
 int journal_get_create_access (handle_t *handle, struct buffer_head *bh) 
 {
 	transaction_t *transaction = handle->h_transaction;
@@ -847,13 +868,14 @@
 
 
 
-/*
- * journal_get_undo_access: Notify intent to modify metadata with non-
- * rewindable consequences
- *
+/**
+ * int journal_get_undo_access() -  Notify intent to modify metadata with non-rewindable consequences
+ * @handle: transaction
+ * @bh: buffer to undo
+ * 
  * Sometimes there is a need to distinguish between metadata which has
  * been committed to disk and that which has not.  The ext3fs code uses
- * this for freeing and allocating space: we have to make sure that we
+ * this for freeing and allocating space, we have to make sure that we
  * do not reuse freed space until the deallocation has been committed,
  * since if we overwrote that space we would make the delete
  * un-rewindable in case of a crash.
@@ -865,13 +887,12 @@
  * as we know that the buffer has definitely been committed to disk.
  * 
  * We never need to know which transaction the committed data is part
- * of: buffers touched here are guaranteed to be dirtied later and so
+ * of, buffers touched here are guaranteed to be dirtied later and so
  * will be committed to a new transaction in due course, at which point
  * we can discard the old committed data pointer.
  *
  * Returns error number or 0 on success.  
  */
-
 int journal_get_undo_access (handle_t *handle, struct buffer_head *bh)
 {
 	journal_t *journal = handle->h_transaction->t_journal;
@@ -913,10 +934,12 @@
 	return err;
 }
 
-/* 
- * journal_dirty_data: mark a buffer as containing dirty data which
- * needs to be flushed before we can commit the current transaction.  
- *
+/** 
+ * int journal_dirty_data() -  mark a buffer as containing dirty data which needs to be flushed before we can commit the current transaction.  
+ * @handle: transaction
+ * @bh: bufferhead to mark
+ * @async: flag
+ * 
  * The buffer is placed on the transaction's data list and is marked as
  * belonging to the transaction.
  *
@@ -925,7 +948,10 @@
  * t_async_datalist.
  * 
  * Returns error number or 0 on success.  
- *
+ */
+int journal_dirty_data (handle_t *handle, struct buffer_head *bh, int async)
+{
+/*
  * journal_dirty_data() can be called via page_launder->ext3_writepage
  * by kswapd.  So it cannot block.  Happily, there's nothing here
  * which needs lock_journal if `async' is set.
@@ -934,9 +960,6 @@
  * between BJ_AsyncData and BJ_SyncData according to who tried to
  * change its state last.
  */
-
-int journal_dirty_data (handle_t *handle, struct buffer_head *bh, int async)
-{
 	journal_t *journal = handle->h_transaction->t_journal;
 	int need_brelse = 0;
 	int wanted_jlist = async ? BJ_AsyncData : BJ_SyncData;
@@ -1079,24 +1102,28 @@
 	return 0;
 }
 
-/* 
- * journal_dirty_metadata: mark a buffer as containing dirty metadata
- * which needs to be journaled as part of the current transaction.
+/** 
+ * int journal_dirty_metadata() -  mark a buffer as containing dirty metadata
+ * @handle: transaction to add buffer to.
+ * @bh: buffer to mark 
+ * 
+ * mark dirty metadata which needs to be journaled as part of the current transaction.
  *
  * The buffer is placed on the transaction's metadata list and is marked
  * as belonging to the transaction.  
  *
+ * Returns error number or 0 on success.  
+ */
+int journal_dirty_metadata (handle_t *handle, struct buffer_head *bh)
+{
+/*
  * Special care needs to be taken if the buffer already belongs to the
  * current committing transaction (in which case we should have frozen
  * data present for that commit).  In that case, we don't relink the
  * buffer: that only gets done when the old transaction finally
  * completes its commit.
  * 
- * Returns error number or 0 on success.  
  */
-
-int journal_dirty_metadata (handle_t *handle, struct buffer_head *bh)
-{
 	transaction_t *transaction = handle->h_transaction;
 	journal_t *journal = transaction->t_journal;
 	struct journal_head *jh = bh2jh(bh);
@@ -1182,9 +1209,12 @@
 }
 #endif
 
-/* 
- * journal_forget: bforget() for potentially-journaled buffers.  We can
- * only do the bforget if there are no commits pending against the
+/** 
+ * void journal_forget() - bforget() for potentially-journaled buffers.
+ * @handle: transaction handle
+ * @bh:     bh to 'forget'
+ *
+ * We can only do the bforget if there are no commits pending against the
  * buffer.  If the buffer is dirty in the current running transaction we
  * can safely unlink it. 
  *
@@ -1196,7 +1226,6 @@
  * Allow this call even if the handle has aborted --- it may be part of
  * the caller's cleanup after an abort.
  */
-
 void journal_forget (handle_t *handle, struct buffer_head *bh)
 {
 	transaction_t *transaction = handle->h_transaction;
@@ -1357,7 +1386,10 @@
 	jcb->jcb_func = func;
 }
 
-/*
+/**
+ * int journal_stop() - complete a transaction
+ * @handle: tranaction to complete.
+ * 
  * All done for a particular handle.
  *
  * There is not much action needed here.  We just return any remaining
@@ -1370,7 +1402,6 @@
  * return -EIO if a journal_abort has been executed since the
  * transaction began.
  */
-
 int journal_stop(handle_t *handle)
 {
 	transaction_t *transaction = handle->h_transaction;
@@ -1455,8 +1486,10 @@
 	return err;
 }
 
-/*
- * For synchronous operations: force any uncommitted trasnactions
+/**int journal_force_commit() - force any uncommitted transactions
+ * @journal: journal to force
+ *
+ * For synchronous operations: force any uncommitted transactions
  * to disk.  May seem kludgy, but it reuses all the handle batching
  * code in a very simple manner.
  */
@@ -1660,6 +1693,26 @@
 	return 0;
 }
 
+
+/** 
+ * int journal_try_to_free_buffers() - try to free page buffers.
+ * @journal: journal for operation
+ * @page: to try and free
+ * @gfp_mask: 'IO' mode for try_to_free_buffers()
+ *
+ * 
+ * For all the buffers on this page,
+ * if they are fully written out ordered data, move them onto BUF_CLEAN
+ * so try_to_free_buffers() can reap them.
+ * 
+ * This function returns non-zero if we wish try_to_free_buffers()
+ * to be called. We do this if the page is releasable by try_to_free_buffers().
+ * We also do it if the page has locked or dirty buffers and the caller wants
+ * us to perform sync or async writeout.
+ */
+int journal_try_to_free_buffers(journal_t *journal, 
+				struct page *page, int gfp_mask)
+{
 /*
  * journal_try_to_free_buffers().  For all the buffers on this page,
  * if they are fully written out ordered data, move them onto BUF_CLEAN
@@ -1684,14 +1737,7 @@
  * cannot happen because we never reallocate freed data as metadata
  * while the data is part of a transaction.  Yes?
  *
- * This function returns non-zero if we wish try_to_free_buffers()
- * to be called. We do this is the page is releasable by try_to_free_buffers().
- * We also do it if the page has locked or dirty buffers and the caller wants
- * us to perform sync or async writeout.
  */
-int journal_try_to_free_buffers(journal_t *journal, 
-				struct page *page, int gfp_mask)
-{
 	struct buffer_head *bh;
 	struct buffer_head *tmp;
 	int locked_or_dirty = 0;
@@ -1903,8 +1949,15 @@
 	return may_free;
 }
 
-/*
- * Return non-zero if the page's buffers were successfully reaped
+/** 
+ * int journal_flushpage() 
+ * @journal: journal to use for flush... 
+ * @page:    page to flush
+ * @offset:  length of page to flush.
+ *
+ * Reap page buffers containing data after offset in page.
+ *
+ * Return non-zero if the page's buffers were successfully reaped.
  */
 int journal_flushpage(journal_t *journal, 
 		      struct page *page, 
diff -urN linux-2.4.21-pre2/fs/namei.c linux-2.4.21-pre3/fs/namei.c
--- linux-2.4.21-pre2/fs/namei.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/fs/namei.c	Mon Jan  6 16:32:33 2003
@@ -1974,8 +1974,10 @@
 	 * bloody create() on broken symlinks. Furrfu...
 	 */
 	name = __getname();
-	if (!name)
+	if (!name) {
+		path_release(nd);
 		return -ENOMEM;
+	}
 	strcpy(name, nd->last.name);
 	nd->last.name = name;
 	return 0;
diff -urN linux-2.4.21-pre2/fs/nfsd/nfs3xdr.c linux-2.4.21-pre3/fs/nfsd/nfs3xdr.c
--- linux-2.4.21-pre2/fs/nfsd/nfs3xdr.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/fs/nfsd/nfs3xdr.c	Mon Jan  6 16:32:33 2003
@@ -52,7 +52,7 @@
 static inline u32 *
 decode_fh(u32 *p, struct svc_fh *fhp)
 {
-	int size;
+	unsigned int size;
 	fh_init(fhp, NFS3_FHSIZE);
 	size = ntohl(*p++);
 	if (size > NFS3_FHSIZE)
diff -urN linux-2.4.21-pre2/include/asm-arm/arch-cl7500/system.h linux-2.4.21-pre3/include/asm-arm/arch-cl7500/system.h
--- linux-2.4.21-pre2/include/asm-arm/arch-cl7500/system.h	Thu Apr 12 12:20:31 2001
+++ linux-2.4.21-pre3/include/asm-arm/arch-cl7500/system.h	Mon Jan  6 16:32:33 2003
@@ -18,6 +18,6 @@
 	do {					\
 		iomd_writeb(0, IOMD_ROMCR0);	\
 		cpu_reset(0);			\
-	} while (0);
+	} while (0)
 
 #endif
diff -urN linux-2.4.21-pre2/include/asm-arm/arch-sa1100/keyboard.h linux-2.4.21-pre3/include/asm-arm/arch-sa1100/keyboard.h
--- linux-2.4.21-pre2/include/asm-arm/arch-sa1100/keyboard.h	Fri Aug  2 17:39:45 2002
+++ linux-2.4.21-pre3/include/asm-arm/arch-sa1100/keyboard.h	Mon Jan  6 16:32:33 2003
@@ -10,8 +10,8 @@
 #include <asm/mach-types.h>
 #include <asm/arch/assabet.h>
 
-#define kbd_disable_irq()	do { } while(0);
-#define kbd_enable_irq()	do { } while(0);
+#define kbd_disable_irq()	do { } while(0)
+#define kbd_enable_irq()	do { } while(0)
 
 extern int sa1111_kbd_init_hw(void);
 extern void gc_kbd_init_hw(void);
diff -urN linux-2.4.21-pre2/include/asm-i386/cpufeature.h linux-2.4.21-pre3/include/asm-i386/cpufeature.h
--- linux-2.4.21-pre2/include/asm-i386/cpufeature.h	Fri Dec 21 09:42:03 2001
+++ linux-2.4.21-pre3/include/asm-i386/cpufeature.h	Mon Jan  6 16:32:33 2003
@@ -64,6 +64,8 @@
 #define X86_FEATURE_CYRIX_ARR	(3*32+ 2) /* Cyrix ARRs (= MTRRs) */
 #define X86_FEATURE_CENTAUR_MCR	(3*32+ 3) /* Centaur MCRs (= MTRRs) */
 
+#define cpu_has(c, bit)            test_bit(bit, (c)->x86_capability)
+
 #endif /* __ASM_I386_CPUFEATURE_H */
 
 /* 
diff -urN linux-2.4.21-pre2/include/asm-i386/msr.h linux-2.4.21-pre3/include/asm-i386/msr.h
--- linux-2.4.21-pre2/include/asm-i386/msr.h	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/include/asm-i386/msr.h	Mon Jan  6 16:32:33 2003
@@ -87,6 +87,7 @@
 #define MSR_K7_EVNTSEL0			0xC0010000
 #define MSR_K7_PERFCTR0			0xC0010004
 #define MSR_K7_HWCR			0xC0010015
+#define MSR_K7_CLK_CTL			0xC001001b
 #define MSR_K7_FID_VID_CTL		0xC0010041
 #define MSR_K7_VID_STATUS		0xC0010042
 
@@ -114,5 +115,7 @@
 /* Transmeta defined MSRs */
 #define MSR_TMTA_LONGRUN_CTRL		0x80868010
 #define MSR_TMTA_LONGRUN_FLAGS		0x80868011
+#define MSR_TMTA_LRTI_READOUT		0x80868018
+#define MSR_TMTA_LRTI_VOLT_MHZ		0x8086801a
 
 #endif /* __ASM_MSR_H */
diff -urN linux-2.4.21-pre2/include/asm-i386/smplock.h linux-2.4.21-pre3/include/asm-i386/smplock.h
--- linux-2.4.21-pre2/include/asm-i386/smplock.h	Fri Aug  2 17:39:45 2002
+++ linux-2.4.21-pre3/include/asm-i386/smplock.h	Mon Jan  6 16:32:33 2003
@@ -1,3 +1,6 @@
+#ifndef __ASM_SMPLOCK_H
+#define __ASM_SMPLOCK_H
+
 /*
  * <asm/smplock.h>
  *
@@ -74,3 +77,5 @@
 		 "=m" (current->lock_depth));
 #endif
 }
+
+#endif /* __ASM_SMPLOCK_H */
diff -urN linux-2.4.21-pre2/include/asm-ppc/bitops.h linux-2.4.21-pre3/include/asm-ppc/bitops.h
--- linux-2.4.21-pre2/include/asm-ppc/bitops.h	Mon Jun 11 19:15:27 2001
+++ linux-2.4.21-pre3/include/asm-ppc/bitops.h	Mon Jan  6 16:32:33 2003
@@ -237,8 +237,6 @@
 	return __ilog2(x & -x);
 }
 
-#ifdef __KERNEL__
-
 /*
  * ffs: find first bit set. This is defined the same way as
  * the libc and compiler builtin ffs routines, therefore
@@ -258,8 +256,6 @@
 #define hweight16(x) generic_hweight16(x)
 #define hweight8(x) generic_hweight8(x)
 
-#endif /* __KERNEL__ */
-
 /*
  * This implementation of find_{first,next}_zero_bit was stolen from
  * Linus' asm-alpha/bitops.h.
@@ -306,8 +302,6 @@
 }
 
 
-#ifdef __KERNEL__
-
 #define ext2_set_bit(nr, addr)		__test_and_set_bit((nr) ^ 0x18, addr)
 #define ext2_clear_bit(nr, addr)	__test_and_clear_bit((nr) ^ 0x18, addr)
 
@@ -371,7 +365,5 @@
 #define minix_test_bit(nr,addr) ext2_test_bit(nr,addr)
 #define minix_find_first_zero_bit(addr,size) ext2_find_first_zero_bit(addr,size)
 
-#endif	/* __KERNEL__ */
-
 #endif /* _PPC_BITOPS_H */
 #endif /* __KERNEL__ */
diff -urN linux-2.4.21-pre2/include/asm-ppc/cache.h linux-2.4.21-pre3/include/asm-ppc/cache.h
--- linux-2.4.21-pre2/include/asm-ppc/cache.h	Fri Nov  2 17:43:54 2001
+++ linux-2.4.21-pre3/include/asm-ppc/cache.h	Mon Jan  6 16:32:33 2003
@@ -40,9 +40,8 @@
 		 __section__(".data.cacheline_aligned")))
 #endif
 
-#if defined(__KERNEL__) && !defined(__ASSEMBLY__)
+#ifndef __ASSEMBLY__
 extern void flush_dcache_range(unsigned long start, unsigned long stop);
-
 #endif /* __ASSEMBLY__ */
 
 /* prep registers for L2 */
diff -urN linux-2.4.21-pre2/include/asm-ppc/i8259.h linux-2.4.21-pre3/include/asm-ppc/i8259.h
--- linux-2.4.21-pre2/include/asm-ppc/i8259.h	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/include/asm-ppc/i8259.h	Mon Jan  6 16:32:33 2003
@@ -11,6 +11,5 @@
 
 void i8259_init(unsigned long int_ack);
 int i8259_irq(struct pt_regs *regs);
-int i8259_poll(struct pt_regs *regs);
 
 #endif /* _PPC_KERNEL_i8259_H */
diff -urN linux-2.4.21-pre2/include/asm-ppc/ide.h linux-2.4.21-pre3/include/asm-ppc/ide.h
--- linux-2.4.21-pre2/include/asm-ppc/ide.h	Mon Jan  6 16:32:29 2003
+++ linux-2.4.21-pre3/include/asm-ppc/ide.h	Mon Jan  6 16:32:33 2003
@@ -1,5 +1,5 @@
 /*
- * BK Id: SCCS/s.ide.h 1.16 09/28/01 07:54:24 trini
+ * BK Id: %F% %I% %G% %U% %#%
  */
 /*
  *  linux/include/asm-ppc/ide.h
@@ -86,6 +86,11 @@
 #endif /* CONFIG_BLK_DEV_IDEPCI */
 }
 
+#define __ide_mm_insw(p, a, c)	_insw_ns((volatile u16 *)(p), (a), (c))
+#define __ide_mm_insl(p, a, c)	_insl_ns((volatile u32 *)(p), (a), (c))
+#define __ide_mm_outsw(p, a, c)	_outsw_ns((volatile u16 *)(p), (a), (c))
+#define __ide_mm_outsl(p, a, c)	_outsl_ns((volatile u32 *)(p), (a), (c))
+
 /*
  * The following are not needed for the non-m68k ports
  * unless direct IDE on 8xx
diff -urN linux-2.4.21-pre2/include/asm-ppc/io.h linux-2.4.21-pre3/include/asm-ppc/io.h
--- linux-2.4.21-pre2/include/asm-ppc/io.h	Fri Nov  2 17:43:54 2001
+++ linux-2.4.21-pre3/include/asm-ppc/io.h	Mon Jan  6 16:32:33 2003
@@ -181,7 +181,6 @@
 #define memcpy_fromio(a,b,c)   memcpy((a),(void *)(b),(c))
 #define memcpy_toio(a,b,c)	memcpy((void *)(a),(b),(c))
 
-#ifdef __KERNEL__
 /*
  * Map in an area of physical address space, for accessing
  * I/O devices etc.
@@ -253,8 +252,6 @@
 #define page_to_phys(page)	((page - mem_map) << PAGE_SHIFT)
 #define page_to_bus(page)	(page_to_phys(page) + PCI_DRAM_OFFSET)
 
-#endif /* __KERNEL__ */
-
 /*
  * Enforce In-order Execution of I/O:
  * Acts as a barrier to ensure all previous I/O accesses have
diff -urN linux-2.4.21-pre2/include/asm-ppc/irq.h linux-2.4.21-pre3/include/asm-ppc/irq.h
--- linux-2.4.21-pre2/include/asm-ppc/irq.h	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/include/asm-ppc/irq.h	Mon Jan  6 16:32:33 2003
@@ -13,6 +13,19 @@
 extern void disable_irq_nosync(unsigned int);
 extern void enable_irq(unsigned int);
 
+/*
+ * These constants are used for passing information about interrupt
+ * signal polarity and level/edge sensing to the low-level PIC chip
+ * drivers.
+ */
+#define IRQ_SENSE_MASK		0x1
+#define IRQ_SENSE_LEVEL		0x1	/* interrupt on active level */
+#define IRQ_SENSE_EDGE		0x0	/* interrupt triggered by edge */
+
+#define IRQ_POLARITY_MASK	0x2
+#define IRQ_POLARITY_POSITIVE	0x2	/* high level or low->high edge */
+#define IRQ_POLARITY_NEGATIVE	0x0	/* low level or high->low edge */
+
 #if defined(CONFIG_4xx)
 
 /*
diff -urN linux-2.4.21-pre2/include/asm-ppc/nvram.h linux-2.4.21-pre3/include/asm-ppc/nvram.h
--- linux-2.4.21-pre2/include/asm-ppc/nvram.h	Mon May 21 15:02:06 2001
+++ linux-2.4.21-pre3/include/asm-ppc/nvram.h	Mon Jan  6 16:32:33 2003
@@ -42,8 +42,6 @@
 	pmac_nvram_NR		/* MacOS Name Registry partition */
 };
 
-#ifdef __KERNEL__
-
 /* Return partition offset in nvram */
 extern int	pmac_get_partition(int partition);
 
@@ -51,8 +49,6 @@
 extern u8	pmac_xpram_read(int xpaddr);
 extern void	pmac_xpram_write(int xpaddr, u8 data);
 
-#endif /* __KERNEL__ */
-
 /* Some offsets in XPRAM */
 #define PMAC_XPRAM_MACHINE_LOC	0xe4
 #define PMAC_XPRAM_SOUND_VOLUME	0x08
diff -urN linux-2.4.21-pre2/include/asm-ppc/open_pic.h linux-2.4.21-pre3/include/asm-ppc/open_pic.h
--- linux-2.4.21-pre2/include/asm-ppc/open_pic.h	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/include/asm-ppc/open_pic.h	Mon Jan  6 16:32:33 2003
@@ -42,7 +42,10 @@
 
 /* Exported functions */
 extern void openpic_set_sources(int first_irq, int num_irqs, void *isr);
-extern void openpic_init(int, int, unsigned char *, int);
+extern void openpic_init(int linux_irq_offset);
+extern void openpic_init_nmi_irq(u_int irq);
+extern void openpic_hookup_cascade(u_int irq, char *name,
+				   int (*cascade_fn)(struct pt_regs *));
 extern u_int openpic_irq(void);
 extern void openpic_eoi(void);
 extern void openpic_request_IPIs(void);
diff -urN linux-2.4.21-pre2/include/asm-ppc/page.h linux-2.4.21-pre3/include/asm-ppc/page.h
--- linux-2.4.21-pre2/include/asm-ppc/page.h	Fri Aug  2 17:39:45 2002
+++ linux-2.4.21-pre3/include/asm-ppc/page.h	Mon Jan  6 16:32:33 2003
@@ -76,7 +76,7 @@
 
 
 /* align addr on a size boundry - adjust address up if needed -- Cort */
-#define _ALIGN(addr,size)	(((addr)+size-1)&(~(size-1)))
+#define _ALIGN(addr,size)	(((addr)+(size)-1)&(~((size)-1)))
 
 /* to align the pointer to the (next) page boundary */
 #define PAGE_ALIGN(addr)	(((addr)+PAGE_SIZE-1)&PAGE_MASK)
@@ -117,8 +117,8 @@
 #define __va(x) ___va ((unsigned long)(x))
 
 #define MAP_PAGE_RESERVED	(1<<15)
-#define virt_to_page(kaddr)	(mem_map + (((unsigned long)kaddr-PAGE_OFFSET) >> PAGE_SHIFT))
-#define VALID_PAGE(page)	((page - mem_map) < max_mapnr)
+#define virt_to_page(kaddr)	(mem_map + (((unsigned long)(kaddr)-PAGE_OFFSET) >> PAGE_SHIFT))
+#define VALID_PAGE(page)	(((page) - mem_map) < max_mapnr)
 
 extern unsigned long get_zero_page_fast(void);
 
diff -urN linux-2.4.21-pre2/include/asm-ppc/param.h linux-2.4.21-pre3/include/asm-ppc/param.h
--- linux-2.4.21-pre2/include/asm-ppc/param.h	Tue Aug 28 06:58:33 2001
+++ linux-2.4.21-pre3/include/asm-ppc/param.h	Mon Jan  6 16:32:33 2003
@@ -22,6 +22,6 @@
 
 #ifdef __KERNEL__
 # define CLOCKS_PER_SEC	HZ	/* frequency at which times() counts */
-#endif
+#endif /* __KERNEL__ */
 
 #endif
diff -urN linux-2.4.21-pre2/include/asm-ppc/signal.h linux-2.4.21-pre3/include/asm-ppc/signal.h
--- linux-2.4.21-pre2/include/asm-ppc/signal.h	Mon May 21 15:02:06 2001
+++ linux-2.4.21-pre3/include/asm-ppc/signal.h	Mon Jan  6 16:32:33 2003
@@ -114,7 +114,7 @@
 #define SA_PROBE		SA_ONESHOT
 #define SA_SAMPLE_RANDOM	SA_RESTART
 #define SA_SHIRQ		0x04000000
-#endif
+#endif /* __KERNEL__ */
 
 #define SIG_BLOCK          0	/* for blocking signals */
 #define SIG_UNBLOCK        1	/* for unblocking signals */
@@ -153,7 +153,6 @@
 
 #ifdef __KERNEL__
 #include <asm/sigcontext.h>
-
-#endif
+#endif /* __KERNEL__ */
 
 #endif
diff -urN linux-2.4.21-pre2/include/asm-ppc/socket.h linux-2.4.21-pre3/include/asm-ppc/socket.h
--- linux-2.4.21-pre2/include/asm-ppc/socket.h	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/include/asm-ppc/socket.h	Mon Jan  6 16:32:33 2003
@@ -68,6 +68,6 @@
 					/* other similar things on the	*/
 					/* user level.			*/
 #define	SOCK_MAX	(SOCK_PACKET+1)
-#endif
+#endif /* __KERNEL__ */
 
 #endif /* _ASM_SOCKET_H */
diff -urN linux-2.4.21-pre2/include/asm-ppc/termios.h linux-2.4.21-pre3/include/asm-ppc/termios.h
--- linux-2.4.21-pre2/include/asm-ppc/termios.h	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/include/asm-ppc/termios.h	Mon Jan  6 16:32:33 2003
@@ -156,7 +156,7 @@
 #ifdef __KERNEL__
 /*                   ^C  ^\ del  ^U  ^D   1   0   0   0   0  ^W  ^R  ^Z  ^Q  ^S  ^V  ^U  */
 #define INIT_C_CC "\003\034\177\025\004\001\000\000\000\000\027\022\032\021\023\026\025" 
-#endif
+#endif	/* __KERNEL__ */
 
 /* modem lines */
 #define TIOCM_LE	0x001
diff -urN linux-2.4.21-pre2/include/asm-ppc/unistd.h linux-2.4.21-pre3/include/asm-ppc/unistd.h
--- linux-2.4.21-pre2/include/asm-ppc/unistd.h	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/include/asm-ppc/unistd.h	Mon Jan  6 16:32:33 2003
@@ -117,10 +117,10 @@
 #define __NR_lstat		107
 #define __NR_fstat		108
 #define __NR_olduname		109
-#define __NR_iopl		110
+/* 110 was __NR_iopl */
 #define __NR_vhangup		111
 #define __NR_idle		112
-#define __NR_vm86		113
+/* 113 was __NR_vm86 */
 #define __NR_wait4		114
 #define __NR_swapoff		115
 #define __NR_sysinfo		116
@@ -130,7 +130,7 @@
 #define __NR_clone		120
 #define __NR_setdomainname	121
 #define __NR_uname		122
-#define __NR_modify_ldt		123
+/* 123 was __NR_modify_ldt */
 #define __NR_adjtimex		124
 #define __NR_mprotect		125
 #define __NR_sigprocmask	126
diff -urN linux-2.4.21-pre2/include/asm-ppc/vc_ioctl.h linux-2.4.21-pre3/include/asm-ppc/vc_ioctl.h
--- linux-2.4.21-pre2/include/asm-ppc/vc_ioctl.h	Mon May 21 15:02:06 2001
+++ linux-2.4.21-pre3/include/asm-ppc/vc_ioctl.h	Mon Jan  6 16:32:33 2003
@@ -41,6 +41,6 @@
 extern int console_powermode(int);
 extern struct vc_mode display_info;
 extern struct fb_info *console_fb_info;
-#endif
+#endif /* __KERNEL__ */
 
 #endif /* _LINUX_VC_IOCTL_H */
diff -urN linux-2.4.21-pre2/include/linux/agp_backend.h linux-2.4.21-pre3/include/linux/agp_backend.h
--- linux-2.4.21-pre2/include/linux/agp_backend.h	Mon Jan  6 16:32:29 2003
+++ linux-2.4.21-pre3/include/linux/agp_backend.h	Mon Jan  6 16:32:33 2003
@@ -60,6 +60,7 @@
 	VIA_APOLLO_PRO,
 	VIA_APOLLO_KX133,
 	VIA_APOLLO_KT133,
+	VIA_APOLLO_P4X400,
 	VIA_APOLLO_KT400,
 	SIS_GENERIC,
 	AMD_GENERIC,
diff -urN linux-2.4.21-pre2/include/linux/console.h linux-2.4.21-pre3/include/linux/console.h
--- linux-2.4.21-pre2/include/linux/console.h	Fri Aug  2 17:39:45 2002
+++ linux-2.4.21-pre3/include/linux/console.h	Mon Jan  6 16:32:33 2003
@@ -90,6 +90,7 @@
 #define CON_PRINTBUFFER	(1)
 #define CON_CONSDEV	(2) /* Last on the command line */
 #define CON_ENABLED	(4)
+#define CON_BOOT	(8) /* Only used for initial boot */
 
 struct console
 {
diff -urN linux-2.4.21-pre2/include/linux/ext3_fs.h linux-2.4.21-pre3/include/linux/ext3_fs.h
--- linux-2.4.21-pre2/include/linux/ext3_fs.h	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/include/linux/ext3_fs.h	Mon Jan  6 16:32:33 2003
@@ -622,7 +622,7 @@
 /* ialloc.c */
 extern struct inode * ext3_new_inode (handle_t *, const struct inode *, int);
 extern void ext3_free_inode (handle_t *, struct inode *);
-extern struct inode * ext3_orphan_get (struct super_block *, ino_t);
+extern struct inode * ext3_orphan_get (struct super_block *, unsigned long);
 extern unsigned long ext3_count_free_inodes (struct super_block *);
 extern void ext3_check_inodes_bitmap (struct super_block *);
 extern unsigned long ext3_count_free (struct buffer_head *, unsigned);
diff -urN linux-2.4.21-pre2/include/linux/fs.h linux-2.4.21-pre3/include/linux/fs.h
--- linux-2.4.21-pre2/include/linux/fs.h	Mon Jan  6 16:32:29 2003
+++ linux-2.4.21-pre3/include/linux/fs.h	Mon Jan  6 16:32:33 2003
@@ -396,6 +396,7 @@
 	int (*releasepage) (struct page *, int);
 #define KERNEL_HAS_O_DIRECT /* this is for modules out of the kernel */
 	int (*direct_IO)(int, struct inode *, struct kiobuf *, unsigned long, int);
+	void (*removepage)(struct page *); /* called when page gets removed from the inode */
 };
 
 struct address_space {
@@ -1202,7 +1203,7 @@
 
 static inline void set_buffer_attached(struct buffer_head *bh)
 {
-	__set_bit(BH_Attached, &bh->b_state);
+	set_bit(BH_Attached, &bh->b_state);
 }
 
 static inline void clear_buffer_attached(struct buffer_head *bh)
diff -urN linux-2.4.21-pre2/include/linux/hdlc/ioctl.h linux-2.4.21-pre3/include/linux/hdlc/ioctl.h
--- linux-2.4.21-pre2/include/linux/hdlc/ioctl.h	Wed Dec 31 16:00:00 1969
+++ linux-2.4.21-pre3/include/linux/hdlc/ioctl.h	Mon Jan  6 16:32:33 2003
@@ -0,0 +1,55 @@
+#ifndef __HDLC_IOCTL_H__
+#define __HDLC_IOCTL_H__
+
+typedef struct { 
+	unsigned int clock_rate; /* bits per second */
+	unsigned int clock_type; /* internal, external, TX-internal etc. */
+	unsigned short loopback;
+} sync_serial_settings;          /* V.35, V.24, X.21 */
+
+typedef struct { 
+	unsigned int clock_rate; /* bits per second */
+	unsigned int clock_type; /* internal, external, TX-internal etc. */
+	unsigned short loopback;
+	unsigned int slot_map;
+} te1_settings;                  /* T1, E1 */
+
+typedef struct {
+	unsigned short encoding;
+	unsigned short parity;
+} raw_hdlc_proto;
+
+typedef struct {
+	unsigned int t391;
+	unsigned int t392;
+	unsigned int n391;
+	unsigned int n392;
+	unsigned int n393;
+	unsigned short lmi;
+	unsigned short dce; /* 1 for DCE (network side) operation */
+} fr_proto;
+
+typedef struct {
+	unsigned int dlci;
+} fr_proto_pvc;          /* for creating/deleting FR PVCs */
+
+typedef struct {
+    unsigned int interval;
+    unsigned int timeout;
+} cisco_proto;
+
+/* PPP doesn't need any info now - supply length = 0 to ioctl */
+
+union hdlc_settings {
+	raw_hdlc_proto		raw_hdlc;
+	cisco_proto		cisco;
+	fr_proto		fr;
+	fr_proto_pvc		fr_pvc;
+};
+
+union line_settings {
+	sync_serial_settings	sync;
+	te1_settings		te1;
+};
+
+#endif /* __HDLC_IOCTL_H__ */
diff -urN linux-2.4.21-pre2/include/linux/hdlc.h linux-2.4.21-pre3/include/linux/hdlc.h
--- linux-2.4.21-pre2/include/linux/hdlc.h	Thu Jul 26 13:51:45 2001
+++ linux-2.4.21-pre3/include/linux/hdlc.h	Mon Jan  6 16:32:33 2003
@@ -1,7 +1,7 @@
 /*
  * Generic HDLC support routines for Linux
  *
- * Copyright (C) 1999, 2000 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 1999-2002 Krzysztof Halasa <khc@pm.waw.pl>
  *
  * 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
@@ -12,64 +12,49 @@
 #ifndef __HDLC_H
 #define __HDLC_H
 
-/* Ioctls - to be changed */
-#define HDLCGSLOTMAP	(0x89F4) /* E1/T1 slot bitmap */
-#define HDLCGCLOCK	(0x89F5) /* clock sources */
-#define HDLCGCLOCKRATE	(0x89F6) /* clock rate */
-#define HDLCGMODE	(0x89F7) /* internal to hdlc.c - protocol used */
-#define HDLCGLINE	(0x89F8) /* physical interface */
-#define HDLCSSLOTMAP	(0x89F9)
-#define HDLCSCLOCK	(0x89FA)
-#define HDLCSCLOCKRATE	(0x89FB)
-#define HDLCSMODE	(0x89FC) /* internal to hdlc.c - select protocol */
-#define HDLCPVC		(0x89FD) /* internal to hdlc.c - create/delete PVC */
-#define HDLCSLINE	(0x89FE)
-#define HDLCRUN		(0x89FF) /* Download firmware and run board */
-
-/* Modes */
-#define MODE_NONE	0x00000000 /* Not initialized */
-#define MODE_DCE	0x00000080 /* DCE */
-#define MODE_HDLC	0x00000100 /* Raw HDLC frames */
-#define MODE_CISCO	0x00000200
-#define MODE_PPP	0x00000400
-#define MODE_FR		0x00000800 /* Any LMI */
-#define MODE_FR_ANSI	0x00000801
-#define MODE_FR_CCITT	0x00000802
-#define MODE_X25	0x00001000
-#define MODE_MASK	0x0000FF00
-#define MODE_SOFT	0x80000000 /* Driver modes, using hardware HDLC */
-
-/* Lines */
-#define LINE_DEFAULT	0x00000000
-#define LINE_V35	0x00000001
-#define LINE_RS232	0x00000002
-#define LINE_X21	0x00000003
-#define LINE_T1		0x00000004
-#define LINE_E1		0x00000005
-#define LINE_MASK	0x000000FF
-#define LINE_LOOPBACK	0x80000000 /* On-card loopback */
-
-#define CLOCK_EXT	0	/* External TX and RX clock - DTE */
-#define CLOCK_INT	1	/* Internal TX and RX clock - DCE */
-#define CLOCK_TXINT	2	/* Internal TX and external RX clock */
-#define CLOCK_TXFROMRX	3	/* TX clock derived from external RX clock */
+#define GENERIC_HDLC_VERSION 4	/* For synchronization with sethdlc utility */
 
+#define CLOCK_DEFAULT   0	/* Default setting */
+#define CLOCK_EXT	1	/* External TX and RX clock - DTE */
+#define CLOCK_INT	2	/* Internal TX and RX clock - DCE */
+#define CLOCK_TXINT	3	/* Internal TX and external RX clock */
+#define CLOCK_TXFROMRX	4	/* TX clock derived from external RX clock */
+
+
+#define ENCODING_DEFAULT	0 /* Default setting */
+#define ENCODING_NRZ		1
+#define ENCODING_NRZI		2
+#define ENCODING_FM_MARK	3
+#define ENCODING_FM_SPACE	4
+#define ENCODING_MANCHESTER	5
+
+
+#define PARITY_DEFAULT		0 /* Default setting */
+#define PARITY_NONE		1 /* No parity */
+#define PARITY_CRC16_PR0	2 /* CRC16, initial value 0x0000 */
+#define PARITY_CRC16_PR1	3 /* CRC16, initial value 0xFFFF */
+#define PARITY_CRC16_PR0_CCITT	4 /* CRC16, initial 0x0000, ITU-T version */
+#define PARITY_CRC16_PR1_CCITT	5 /* CRC16, initial 0xFFFF, ITU-T version */
+#define PARITY_CRC32_PR0_CCITT	6 /* CRC32, initial value 0x00000000 */
+#define PARITY_CRC32_PR1_CCITT	7 /* CRC32, initial value 0xFFFFFFFF */
+
+#define LMI_DEFAULT		0 /* Default setting */
+#define LMI_NONE		1 /* No LMI, all PVCs are static */
+#define LMI_ANSI		2 /* ANSI Annex D */
+#define LMI_CCITT		3 /* ITU-T Annex A */
 
-#define HDLC_MAX_MTU 1500	/* Ethernet 1500 bytes */
-#define HDLC_MAX_MRU (HDLC_MAX_MTU + 10) /* max 10 bytes for FR */
 
 #ifdef __KERNEL__
 
 #include <linux/skbuff.h>
 #include <linux/netdevice.h>
 #include <net/syncppp.h>
+#include <linux/hdlc/ioctl.h>
 
-#define MAXLEN_LMISTAT  20	/* max size of status enquiry frame */
+#define HDLC_MAX_MTU 1500	/* Ethernet 1500 bytes */
+#define HDLC_MAX_MRU (HDLC_MAX_MTU + 10) /* max 10 bytes for FR */
 
-#define LINK_STATE_RELIABLE 0x01
-#define LINK_STATE_REQUEST  0x02 /* full stat sent (DCE) / req pending (DTE) */
-#define LINK_STATE_CHANGED  0x04 /* change in PVCs state, send full report */
-#define LINK_STATE_FULLREP_SENT 0x08 /* full report sent */
+#define MAXLEN_LMISTAT  20	/* max size of status enquiry frame */
 
 #define PVC_STATE_NEW       0x01
 #define PVC_STATE_ACTIVE    0x02
@@ -112,6 +97,7 @@
 
 
 typedef struct {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
 	unsigned ea1  : 1;
 	unsigned cr   : 1;
 	unsigned dlcih: 6;
@@ -121,6 +107,19 @@
 	unsigned becn : 1;
 	unsigned fecn : 1;
 	unsigned dlcil: 4;
+#elif defined (__BIG_ENDIAN_BITFIELD)
+	unsigned dlcih: 6;
+	unsigned cr   : 1;
+	unsigned ea1  : 1;
+  
+	unsigned dlcil: 4;
+	unsigned fecn : 1;
+	unsigned becn : 1;
+	unsigned de   : 1;
+	unsigned ea2  : 1;
+#else
+#error  "Please fix <asm/byteorder.h>"
+#endif
 }__attribute__ ((packed)) fr_hdr;
 
 
@@ -151,63 +150,96 @@
 	struct hdlc_device_struct *master;
 	struct pvc_device_struct *next;
 
-	u8 state;
-	u8 newstate;
+	struct {
+		int active;
+		int new;
+		int deleted;
+		int fecn;
+		int becn;
+	}state;
 }pvc_device;
 
 
 
-typedef struct {
-	u32 last_errors;	/* last errors bit list */
-	int last_poll;		/* ! */
-	u8 T391;		/* ! link integrity verification polling timer */
-	u8 T392;		/* ! polling verification timer */
-	u8 N391;		/* full status polling counter */
-	u8 N392;		/* error threshold */
-	u8 N393;		/* monitored events count */
-	u8 N391cnt;
-
-	u8 state;		/* ! */
-	u32 txseq;		/* ! TX sequence number - Cisco uses 4 bytes */
-	u32 rxseq;		/* ! RX sequence number */
-}fr_lmi;			/* ! means used in Cisco HDLC as well */
-
-
 typedef struct hdlc_device_struct {
-	/* to be initialized by hardware driver: */
+	/* To be initialized by hardware driver */
 	struct net_device netdev; /* master net device - must be first */
 	struct net_device_stats stats;
 
-	struct ppp_device pppdev;
-	struct ppp_device *syncppp_ptr;
+	/* used by HDLC layer to take control over HDLC device from hw driver*/
+	int (*attach)(struct hdlc_device_struct *hdlc,
+		      unsigned short encoding, unsigned short parity);
 
-	/* set_mode may be NULL if HDLC-only board */
-	int (*set_mode)(struct hdlc_device_struct *hdlc, int mode);
-	int (*open)(struct hdlc_device_struct *hdlc);
-	void (*close)(struct hdlc_device_struct *hdlc);
-	int (*xmit)(struct hdlc_device_struct *hdlc, struct sk_buff *skb);
-	int (*ioctl)(struct hdlc_device_struct *hdlc, struct ifreq *ifr,
-		     int cmd);
-  
-	/* Only in "hardware" FR modes etc. - may be NULL */
-	int (*create_pvc)(pvc_device *pvc);
-	void (*destroy_pvc)(pvc_device *pvc);
-	int (*open_pvc)(pvc_device *pvc);
-	void (*close_pvc)(pvc_device *pvc);
-
-	/* for hdlc.c internal use only */
-	pvc_device *first_pvc;
-	u16 pvc_count;
-	int mode;
+	/* hardware driver must handle this instead of dev->hard_start_xmit */
+	int (*xmit)(struct sk_buff *skb, struct net_device *dev);
 
-	struct timer_list timer;
-	fr_lmi lmi;
+
+	/* Things below are for HDLC layer internal use only */
+	int (*ioctl)(struct net_device *dev, struct ifreq *ifr, int cmd);
+	int (*open)(struct hdlc_device_struct *hdlc);
+	void (*stop)(struct hdlc_device_struct *hdlc);
+	void (*proto_detach)(struct hdlc_device_struct *hdlc);
+	void (*netif_rx)(struct sk_buff *skb);
+	int proto;		/* IF_PROTO_HDLC/CISCO/FR/etc. */
+
+	union {
+		struct {
+			fr_proto settings;
+			pvc_device *first_pvc;
+			int pvc_count;
+
+			struct timer_list timer;
+			int last_poll;
+			int reliable;
+			int changed;
+			int request;
+			int fullrep_sent;
+			u32 last_errors; /* last errors bit list */
+			u8 n391cnt;
+			u8 txseq; /* TX sequence number */
+			u8 rxseq; /* RX sequence number */
+		}fr;
+
+		struct {
+			cisco_proto settings;
+
+			struct timer_list timer;
+			int last_poll;
+			int up;
+			u32 txseq; /* TX sequence number */
+			u32 rxseq; /* RX sequence number */
+		}cisco;
+
+		struct {
+			raw_hdlc_proto settings;
+		}raw_hdlc;
+
+		struct {
+			struct ppp_device pppdev;
+			struct ppp_device *syncppp_ptr;
+			int (*old_change_mtu)(struct net_device *dev,
+					      int new_mtu);
+		}ppp;
+	}state;
 }hdlc_device;
 
 
+
+int hdlc_raw_ioctl(hdlc_device *hdlc, struct ifreq *ifr);
+int hdlc_cisco_ioctl(hdlc_device *hdlc, struct ifreq *ifr);
+int hdlc_ppp_ioctl(hdlc_device *hdlc, struct ifreq *ifr);
+int hdlc_fr_ioctl(hdlc_device *hdlc, struct ifreq *ifr);
+int hdlc_x25_ioctl(hdlc_device *hdlc, struct ifreq *ifr);
+
+
+/* Exported from hdlc.o */
+
+/* Called by hardware driver when a user requests HDLC service */
+int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd);
+
+/* Must be used by hardware driver on module startup/exit */
 int register_hdlc_device(hdlc_device *hdlc);
 void unregister_hdlc_device(hdlc_device *hdlc);
-void hdlc_netif_rx(hdlc_device *hdlc, struct sk_buff *skb);
 
 
 static __inline__ struct net_device* hdlc_to_dev(hdlc_device *hdlc)
@@ -246,33 +278,6 @@
 }
 
 
-static __inline__ u16 status_to_dlci(hdlc_device *hdlc, u8 *status, u8 *state)
-{
-	*state &= ~(PVC_STATE_ACTIVE | PVC_STATE_NEW);
-	if (status[2] & 0x08)
-		*state |= PVC_STATE_NEW;
-	else if (status[2] & 0x02)
-		*state |= PVC_STATE_ACTIVE;
-
-	return ((status[0] & 0x3F)<<4) | ((status[1] & 0x78)>>3);
-}
-
-
-static __inline__ void dlci_to_status(hdlc_device *hdlc, u16 dlci, u8 *status,
-				      u8 state)
-{
-	status[0] = (dlci>>4) & 0x3F;
-	status[1] = ((dlci<<3) & 0x78) | 0x80;
-	status[2] = 0x80;
-
-	if (state & PVC_STATE_NEW)
-		status[2] |= 0x08;
-	else if (state & PVC_STATE_ACTIVE)
-		status[2] |= 0x02;
-}
-
-
-
 static __inline__ u16 netdev_dlci(struct net_device *dev)
 {
 	return ntohs(*(u16*)dev->dev_addr);
@@ -282,37 +287,15 @@
 
 static __inline__ u16 q922_to_dlci(u8 *hdr)
 {
-	return ((hdr[0] & 0xFC)<<2) | ((hdr[1] & 0xF0)>>4);
+	return ((hdr[0] & 0xFC) << 2) | ((hdr[1] & 0xF0) >> 4);
 }
 
 
 
 static __inline__ void dlci_to_q922(u8 *hdr, u16 dlci)
 {
-	hdr[0] = (dlci>>2) & 0xFC;
-	hdr[1] = ((dlci<<4) & 0xF0) | 0x01;
-}
-
-
-
-static __inline__ int mode_is(hdlc_device *hdlc, int mask)
-{
-	return (hdlc->mode & mask) == mask;
-}
-
-
-
-static __inline__ pvc_device* find_pvc(hdlc_device *hdlc, u16 dlci)
-{
-	pvc_device *pvc=hdlc->first_pvc;
-	
-	while (pvc) {
-		if (netdev_dlci(&pvc->netdev) == dlci)
-			return pvc;
-		pvc=pvc->next;
-	}
-
-	return NULL;
+	hdr[0] = (dlci >> 2) & 0xFC;
+	hdr[1] = ((dlci << 4) & 0xF0) | 0x01;
 }
 
 
@@ -332,5 +315,35 @@
 }
 
 
+
+/* Must be called by hardware driver when HDLC device is being opened */
+static __inline__ int hdlc_open(hdlc_device *hdlc)
+{
+	if (hdlc->proto == -1)
+		return -ENOSYS;		/* no protocol attached */
+
+	if (hdlc->open)
+		return hdlc->open(hdlc);
+	return 0;
+}
+
+
+/* Must be called by hardware driver when HDLC device is being closed */
+static __inline__ void hdlc_close(hdlc_device *hdlc)
+{
+	if (hdlc->stop)
+		hdlc->stop(hdlc);
+}
+
+
+/* May be used by hardware driver to gain control over HDLC device */
+static __inline__ void hdlc_proto_detach(hdlc_device *hdlc)
+{
+	if (hdlc->proto_detach)
+		hdlc->proto_detach(hdlc);
+	hdlc->proto_detach = NULL;
+}
+
+
 #endif /* __KERNEL */
 #endif /* __HDLC_H */
diff -urN linux-2.4.21-pre2/include/linux/if.h linux-2.4.21-pre3/include/linux/if.h
--- linux-2.4.21-pre2/include/linux/if.h	Thu Nov 22 11:47:07 2001
+++ linux-2.4.21-pre3/include/linux/if.h	Mon Jan  6 16:32:33 2003
@@ -21,6 +21,7 @@
 
 #include <linux/types.h>		/* for "__kernel_caddr_t" et al	*/
 #include <linux/socket.h>		/* for "struct sockaddr" et al	*/
+#include <linux/hdlc/ioctl.h>
 
 /* Standard interface flags (netdevice->flags). */
 #define	IFF_UP		0x1		/* interface is up		*/
@@ -48,6 +49,29 @@
 /* Private (from user) interface flags (netdevice->priv_flags). */
 #define IFF_802_1Q_VLAN 0x1             /* 802.1Q VLAN device.          */
 
+
+#define IF_GET_IFACE	0x0001		/* for querying only */
+#define IF_GET_PROTO	0x0002
+
+/* For definitions see hdlc.h */
+#define IF_IFACE_V35	0x1000		/* V.35 serial interface	*/
+#define IF_IFACE_V24	0x1001		/* V.24 serial interface	*/
+#define IF_IFACE_X21	0x1002		/* X.21 serial interface	*/
+#define IF_IFACE_T1	0x1003		/* T1 telco serial interface	*/
+#define IF_IFACE_E1	0x1004		/* E1 telco serial interface	*/
+#define IF_IFACE_SYNC_SERIAL 0x1005	/* cant'b be set by software	*/
+
+/* For definitions see hdlc.h */
+#define IF_PROTO_HDLC	0x2000		/* raw HDLC protocol		*/
+#define IF_PROTO_PPP	0x2001		/* PPP protocol			*/
+#define IF_PROTO_CISCO	0x2002		/* Cisco HDLC protocol		*/
+#define IF_PROTO_FR	0x2003		/* Frame Relay protocol		*/
+#define IF_PROTO_FR_ADD_PVC 0x2004	/*    Create FR PVC		*/
+#define IF_PROTO_FR_DEL_PVC 0x2005	/*    Delete FR PVC		*/
+#define IF_PROTO_X25	0x2006		/* X.25				*/
+
+
+
 /*
  *	Device mapping structure. I'd just gone off and designed a 
  *	beautiful scheme using only loadable modules with arguments
@@ -69,6 +93,23 @@
 	/* 3 bytes spare */
 };
 
+struct if_settings
+{
+	unsigned int type;	/* Type of physical device or protocol */
+	unsigned int size;	/* Size of the data allocated by the caller */
+	union {
+		/* {atm/eth/dsl}_settings anyone ? */
+		raw_hdlc_proto		*raw_hdlc;
+		cisco_proto		*cisco;
+		fr_proto		*fr;
+		fr_proto_pvc		*fr_pvc;
+
+		/* interface settings */
+		sync_serial_settings	*sync;
+		te1_settings		*te1;
+	} ifs_ifsu;
+};
+
 /*
  * Interface request structure used for socket
  * ioctl's.  All interface ioctl's must have parameter
@@ -98,6 +139,7 @@
 		char	ifru_slave[IFNAMSIZ];	/* Just fits the size */
 		char	ifru_newname[IFNAMSIZ];
 		char *	ifru_data;
+		struct	if_settings ifru_settings;
 	} ifr_ifru;
 };
 
@@ -117,6 +159,7 @@
 #define ifr_bandwidth	ifr_ifru.ifru_ivalue    /* link bandwidth	*/
 #define ifr_qlen	ifr_ifru.ifru_ivalue	/* Queue length 	*/
 #define ifr_newname	ifr_ifru.ifru_newname	/* New name		*/
+#define ifr_settings	ifr_ifru.ifru_settings	/* Device/proto settings*/
 
 /*
  * Structure used in SIOCGIFCONF request.
diff -urN linux-2.4.21-pre2/include/linux/if_bonding.h linux-2.4.21-pre3/include/linux/if_bonding.h
--- linux-2.4.21-pre2/include/linux/if_bonding.h	Fri Aug  2 17:39:45 2002
+++ linux-2.4.21-pre3/include/linux/if_bonding.h	Mon Jan  6 16:32:33 2003
@@ -37,9 +37,10 @@
 
 #define BOND_CHECK_MII_STATUS	(SIOCGMIIPHY)
 
-#define BOND_MODE_ROUNDROBIN    0
-#define BOND_MODE_ACTIVEBACKUP  1
-#define BOND_MODE_XOR           2 
+#define BOND_MODE_ROUNDROBIN	0
+#define BOND_MODE_ACTIVEBACKUP	1
+#define BOND_MODE_XOR		2
+#define BOND_MODE_BROADCAST	3
 
 /* each slave's link has 4 states */
 #define BOND_LINK_UP    0           /* link is up and running */
@@ -74,6 +75,7 @@
 	struct slave *prev;
 	struct net_device *dev;
 	short  delay;
+	unsigned long jiffies;	
 	char   link;    /* one of BOND_LINK_XXXX */
 	char   state;   /* one of BOND_STATE_XXXX */
 	unsigned short original_flags;
@@ -93,6 +95,8 @@
 	slave_t *next;
 	slave_t *prev;
 	slave_t *current_slave;
+	slave_t *primary_slave;
+	slave_t *current_arp_slave;
 	__s32 slave_cnt;
 	rwlock_t lock;
 	rwlock_t ptrlock;
diff -urN linux-2.4.21-pre2/include/linux/if_ether.h linux-2.4.21-pre3/include/linux/if_ether.h
--- linux-2.4.21-pre2/include/linux/if_ether.h	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/include/linux/if_ether.h	Mon Jan  6 16:32:33 2003
@@ -85,6 +85,7 @@
 #define ETH_P_CONTROL	0x0016		/* Card specific control frames */
 #define ETH_P_IRDA	0x0017		/* Linux-IrDA			*/
 #define ETH_P_ECONET	0x0018		/* Acorn Econet			*/
+#define ETH_P_HDLC	0x0019		/* HDLC frames			*/
 
 /*
  *	This is an Ethernet frame header.
diff -urN linux-2.4.21-pre2/include/linux/in6.h linux-2.4.21-pre3/include/linux/in6.h
--- linux-2.4.21-pre2/include/linux/in6.h	Thu Nov 22 11:46:18 2001
+++ linux-2.4.21-pre3/include/linux/in6.h	Mon Jan  6 16:32:33 2003
@@ -156,6 +156,7 @@
 #define IPV6_MTU_DISCOVER	23
 #define IPV6_MTU		24
 #define IPV6_RECVERR		25
+#define IPV6_V6ONLY		26
 
 /* IPV6_MTU_DISCOVER values */
 #define IPV6_PMTUDISC_DONT		0
diff -urN linux-2.4.21-pre2/include/linux/jbd.h linux-2.4.21-pre3/include/linux/jbd.h
--- linux-2.4.21-pre2/include/linux/jbd.h	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/include/linux/jbd.h	Mon Jan  6 16:32:33 2003
@@ -70,7 +70,38 @@
 #define JFS_MIN_JOURNAL_BLOCKS 1024
 
 #ifdef __KERNEL__
+
+/**
+ * typedef handle_t - The handle_t type represents a single atomic update being performed by some process.
+ *
+ * All filesystem modifications made by the process go
+ * through this handle.  Recursive operations (such as quota operations)
+ * are gathered into a single update.
+ *
+ * The buffer credits field is used to account for journaled buffers
+ * being modified by the running process.  To ensure that there is
+ * enough log space for all outstanding operations, we need to limit the
+ * number of outstanding buffers possible at any time.  When the
+ * operation completes, any buffer credits not used are credited back to
+ * the transaction, so that at all times we know how many buffers the
+ * outstanding updates on a transaction might possibly touch. 
+ * 
+ * This is an opaque datatype.
+ **/
 typedef struct handle_s		handle_t;	/* Atomic operation type */
+
+
+/**
+ * typedef journal_t - The journal_t maintains all of the journaling state information for a single filesystem.
+ *
+ * journal_t is linked to from the fs superblock structure.
+ * 
+ * We use the journal_t to keep track of all outstanding transaction
+ * activity on the filesystem, and to manage the state of the log
+ * writing process.
+ *
+ * This is an opaque datatype.
+ **/
 typedef struct journal_s	journal_t;	/* Journal control structure */
 #endif
 
@@ -266,7 +297,8 @@
 
 struct jbd_revoke_table_s;
 
-/* The handle_t type represents a single atomic update being performed
+/**
+ * The handle_t type represents a single atomic update being performed
  * by some process.  All filesystem modifications made by the process go
  * through this handle.  Recursive operations (such as quota operations)
  * are gathered into a single update.
@@ -277,7 +309,21 @@
  * number of outstanding buffers possible at any time.  When the
  * operation completes, any buffer credits not used are credited back to
  * the transaction, so that at all times we know how many buffers the
- * outstanding updates on a transaction might possibly touch. */
+ * outstanding updates on a transaction might possibly touch. 
+ *
+ * struct handle_s - The handle_s type is the concrete type associated with handle_t.
+ * @h_transaction: Which compound transaction is this update a part of?
+ * @h_buffer_credits: Number of remaining buffers we are allowed to dirty.
+ * @h_ref: Reference count on this handle
+ * @h_err: Field for caller's use to track errors through large fs operations
+ * @h_sync: flag for sync-on-close
+ * @h_jdata: flag to force data journaling
+ * @h_aborted: flag indicating fatal error on handle
+ **/
+
+/* Docbook can't yet cope with the bit fields, but will leave the documentation
+ * in so it can be fixed later. 
+ */
 
 struct handle_s 
 {
@@ -290,8 +336,8 @@
 	/* Reference count on this handle */
 	int			h_ref;
 
-	/* Field for caller's use to track errors through large fs
-	   operations */
+	/* Field for caller's use to track errors through large fs */
+	/* operations */
 	int			h_err;
 
 	/* List of application registered callbacks for this handle.
@@ -373,7 +419,7 @@
 	 */
 	struct journal_head *	t_async_datalist;
 	
-	/* Doubly-linked circular list of all forget buffers (superseded
+	/* Doubly-linked circular list of all forget buffers (superceded
            buffers which we can un-checkpoint once this transaction
            commits) */
 	struct journal_head *	t_forget;
@@ -425,21 +471,58 @@
 	struct list_head	t_jcb;
 };
 
-
-/* The journal_t maintains all of the journaling state information for a
- * single filesystem.  It is linked to from the fs superblock structure.
- * 
- * We use the journal_t to keep track of all outstanding transaction
- * activity on the filesystem, and to manage the state of the log
- * writing process. */
+/**
+ * struct journal_s - The journal_s type is the concrete type associated with journal_t.
+ * @j_flags:  General journaling state flags
+ * @j_errno:  Is there an outstanding uncleared error on the journal (from a prior abort)? 
+ * @j_sb_buffer: First part of superblock buffer
+ * @j_superblock: Second part of superblock buffer
+ * @j_format_version: Version of the superblock format
+ * @j_barrier_count:  Number of processes waiting to create a barrier lock
+ * @j_barrier: The barrier lock itself
+ * @j_running_transaction: The current running transaction..
+ * @j_committing_transaction: the transaction we are pushing to disk
+ * @j_checkpoint_transactions: a linked circular list of all transactions waiting for checkpointing
+ * @j_wait_transaction_locked: Wait queue for waiting for a locked transaction to start committing, or for a barrier lock to be released
+ * @j_wait_logspace: Wait queue for waiting for checkpointing to complete
+ * @j_wait_done_commit: Wait queue for waiting for commit to complete 
+ * @j_wait_checkpoint:  Wait queue to trigger checkpointing
+ * @j_wait_commit: Wait queue to trigger commit
+ * @j_wait_updates: Wait queue to wait for updates to complete
+ * @j_checkpoint_sem: Semaphore for locking against concurrent checkpoints
+ * @j_sem: The main journal lock, used by lock_journal() 
+ * @j_head: Journal head - identifies the first unused block in the journal
+ * @j_tail: Journal tail - identifies the oldest still-used block in the journal.
+ * @j_free: Journal free - how many free blocks are there in the journal?
+ * @j_first: The block number of the first usable block 
+ * @j_last: The block number one beyond the last usable block
+ * @j_dev: Device where we store the journal
+ * @j_blocksize: blocksize for the location where we store the journal.
+ * @j_blk_offset: starting block offset for into the device where we store the journal
+ * @j_fs_dev: Device which holds the client fs.  For internal journal this will be equal to j_dev
+ * @j_maxlen: Total maximum capacity of the journal region on disk.
+ * @j_inode: Optional inode where we store the journal.  If present, all  journal block numbers are mapped into this inode via bmap().
+ * @j_tail_sequence:  Sequence number of the oldest transaction in the log 
+ * @j_transaction_sequence: Sequence number of the next transaction to grant
+ * @j_commit_sequence: Sequence number of the most recently committed transaction
+ * @j_commit_request: Sequence number of the most recent transaction wanting commit 
+ * @j_uuid: Uuid of client object.
+ * @j_task: Pointer to the current commit thread for this journal
+ * @j_max_transaction_buffers:  Maximum number of metadata buffers to allow in a single compound commit transaction
+ * @j_commit_interval: What is the maximum transaction lifetime before we begin a commit?
+ * @j_commit_timer:  The timer used to wakeup the commit thread
+ * @j_commit_timer_active: Timer flag
+ * @j_all_journals:  Link all journals together - system-wide 
+ * @j_revoke: The revoke table - maintains the list of revoked blocks in the current transaction.
+ **/
 
 struct journal_s
 {
 	/* General journaling state flags */
 	unsigned long		j_flags;
 
-	/* Is there an outstanding uncleared error on the journal (from
-	 * a prior abort)? */
+	/* Is there an outstanding uncleared error on the journal (from */
+	/* a prior abort)? */
 	int			j_errno;
 	
 	/* The superblock buffer */
@@ -461,13 +544,13 @@
 	/* ... the transaction we are pushing to disk ... */
 	transaction_t *		j_committing_transaction;
 	
-	/* ... and a linked circular list of all transactions waiting
-	 * for checkpointing. */
+	/* ... and a linked circular list of all transactions waiting */
+	/* for checkpointing. */
 	/* Protected by journal_datalist_lock */
 	transaction_t *		j_checkpoint_transactions;
 
-	/* Wait queue for waiting for a locked transaction to start
-           committing, or for a barrier lock to be released */
+	/* Wait queue for waiting for a locked transaction to start */
+        /*  committing, or for a barrier lock to be released */
 	wait_queue_head_t	j_wait_transaction_locked;
 	
 	/* Wait queue for waiting for checkpointing to complete */
@@ -494,33 +577,33 @@
 	/* Journal head: identifies the first unused block in the journal. */
 	unsigned long		j_head;
 	
-	/* Journal tail: identifies the oldest still-used block in the
-	 * journal. */
+	/* Journal tail: identifies the oldest still-used block in the */
+	/* journal. */
 	unsigned long		j_tail;
 
 	/* Journal free: how many free blocks are there in the journal? */
 	unsigned long		j_free;
 
-	/* Journal start and end: the block numbers of the first usable
-	 * block and one beyond the last usable block in the journal. */
+	/* Journal start and end: the block numbers of the first usable */
+	/* block and one beyond the last usable block in the journal. */
 	unsigned long		j_first, j_last;
 
-	/* Device, blocksize and starting block offset for the location
-	 * where we store the journal. */
+	/* Device, blocksize and starting block offset for the location */
+	/* where we store the journal. */
 	kdev_t			j_dev;
 	int			j_blocksize;
 	unsigned int		j_blk_offset;
 
-	/* Device which holds the client fs.  For internal journal this
-	 * will be equal to j_dev. */
+	/* Device which holds the client fs.  For internal journal this */
+	/* will be equal to j_dev. */
 	kdev_t			j_fs_dev;
 
 	/* Total maximum capacity of the journal region on disk. */
 	unsigned int		j_maxlen;
 
-	/* Optional inode where we store the journal.  If present, all
-	 * journal block numbers are mapped into this inode via
-	 * bmap(). */
+	/* Optional inode where we store the journal.  If present, all */
+	/* journal block numbers are mapped into this inode via */
+	/* bmap(). */
 	struct inode *		j_inode;
 
 	/* Sequence number of the oldest transaction in the log */
@@ -532,23 +615,23 @@
 	/* Sequence number of the most recent transaction wanting commit */
 	tid_t			j_commit_request;
 
-	/* Journal uuid: identifies the object (filesystem, LVM volume
-	 * etc) backed by this journal.  This will eventually be
-	 * replaced by an array of uuids, allowing us to index multiple
-	 * devices within a single journal and to perform atomic updates
-	 * across them.  */
+	/* Journal uuid: identifies the object (filesystem, LVM volume   */
+	/* etc) backed by this journal.  This will eventually be         */
+	/* replaced by an array of uuids, allowing us to index multiple  */
+	/* devices within a single journal and to perform atomic updates */
+	/* across them.  */
 
 	__u8			j_uuid[16];
 
 	/* Pointer to the current commit thread for this journal */
 	struct task_struct *	j_task;
 
-	/* Maximum number of metadata buffers to allow in a single
-	 * compound commit transaction */
+	/* Maximum number of metadata buffers to allow in a single */
+	/* compound commit transaction */
 	int			j_max_transaction_buffers;
 
-	/* What is the maximum transaction lifetime before we begin a
-	 * commit? */
+	/* What is the maximum transaction lifetime before we begin a */
+	/* commit? */
 	unsigned long		j_commit_interval;
 
 	/* The timer used to wakeup the commit thread: */
@@ -558,8 +641,8 @@
 	/* Link all journals together - system-wide */
 	struct list_head	j_all_journals;
 
-	/* The revoke table: maintains the list of revoked blocks in the
-           current transaction. */
+	/* The revoke table: maintains the list of revoked blocks in the */
+        /*  current transaction. */
 	struct jbd_revoke_table_s *j_revoke;
 };
 
@@ -825,7 +908,7 @@
 #define BJ_SyncData	1	/* Normal data: flush before commit */
 #define BJ_AsyncData	2	/* writepage data: wait on it before commit */
 #define BJ_Metadata	3	/* Normal journaled metadata */
-#define BJ_Forget	4	/* Buffer superseded by this transaction */
+#define BJ_Forget	4	/* Buffer superceded by this transaction */
 #define BJ_IO		5	/* Buffer is for temporary IO use */
 #define BJ_Shadow	6	/* Buffer contents being shadowed to the log */
 #define BJ_LogCtl	7	/* Buffer contains log descriptors */
diff -urN linux-2.4.21-pre2/include/linux/kernel_stat.h linux-2.4.21-pre3/include/linux/kernel_stat.h
--- linux-2.4.21-pre2/include/linux/kernel_stat.h	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/include/linux/kernel_stat.h	Mon Jan  6 16:32:33 2003
@@ -27,7 +27,7 @@
 	unsigned int pgpgin, pgpgout;
 	unsigned int pswpin, pswpout;
 #if defined (__hppa__) 
-	unsigned int irqs[NR_IRQ_REGS][IRQ_PER_REGION];
+	unsigned int irqs[NR_CPUS][NR_IRQ_REGS][IRQ_PER_REGION];
 #elif !defined(CONFIG_ARCH_S390)
 	unsigned int irqs[NR_CPUS][NR_IRQS];
 #endif
@@ -44,7 +44,12 @@
  */
 static inline int kstat_irqs (int irq)
 {
-	return kstat.irqs[IRQ_REGION(irq)][IRQ_OFFSET(irq)];
+	int i, sum=0; 
+
+	for (i = 0 ; i < smp_num_cpus ; i++)
+		sum += kstat.irqs[i][IRQ_REGION(irq)][IRQ_OFFSET(irq)];
+ 
+	return sum;
 }
 #elif !defined(CONFIG_ARCH_S390)
 /*
diff -urN linux-2.4.21-pre2/include/linux/major.h linux-2.4.21-pre3/include/linux/major.h
--- linux-2.4.21-pre2/include/linux/major.h	Fri Aug  2 17:39:45 2002
+++ linux-2.4.21-pre3/include/linux/major.h	Mon Jan  6 16:32:33 2003
@@ -33,6 +33,7 @@
 #define MD_MAJOR        9
 #define MISC_MAJOR	10
 #define SCSI_CDROM_MAJOR 11
+#define	MUX_MAJOR	11	/* PA-RISC only */
 #define QIC02_TAPE_MAJOR 12
 #define XT_DISK_MAJOR	13
 #define SOUND_MAJOR	14
diff -urN linux-2.4.21-pre2/include/linux/matroxfb.h linux-2.4.21-pre3/include/linux/matroxfb.h
--- linux-2.4.21-pre2/include/linux/matroxfb.h	Wed Jul  5 11:55:45 2000
+++ linux-2.4.21-pre3/include/linux/matroxfb.h	Mon Jan  6 16:32:33 2003
@@ -30,5 +30,51 @@
 /* which outputs exist on this framebuffer */
 #define MATROXFB_GET_ALL_OUTPUTS	_IOR('n',0xFB,sizeof(__u32))
 
+struct matroxfb_queryctrl {
+  __u32 id;			/* ID for control */
+  char name[32];		/* A suggested label for this control */
+  int minimum;			/* Minimum value */
+  int maximum;			/* Maximum value */
+  unsigned int step;            /* The increment between values of an integer
+				   control that are distinct on the hardware */
+  int default_value;		/* Driver default value */
+  __u32 type;			/* Control type. */
+  __u32 flags;			/* Control flags */
+  __u32 category;               /* Control category code, useful for 
+				   separating controls by function */
+  char group[32];               /* A suggested label string for the 
+				   control group */
+  __u32 reserved[2];
+};
+
+enum matroxfb_ctrl_type {
+  MATROXFB_CTRL_TYPE_INTEGER=0,	/* An integer-valued control */
+  MATROXFB_CTRL_TYPE_BOOLEAN,	/* A boolean-valued control */
+  MATROXFB_CTRL_TYPE_MENU,	/* The control has a menu of choices */
+  MATROXFB_CTRL_TYPE_BUTTON /* A button which performs an action when clicked */
+};
+
+enum matroxfb_ctrl_id {
+  MATROXFB_CID_BRIGHTNESS=0x00980900,
+  MATROXFB_CID_CONTRAST,
+  MATROXFB_CID_SATURATION,
+  MATROXFB_CID_HUE,
+  MATROXFB_CID_GAMMA	 =0x00980910,
+  MATROXFB_CID_TESTOUT	 =0x08000000,
+  MATROXFB_CID_DEFLICKER,
+  MATROXFB_CID_LAST
+};
+  
+
+#define MATROXFB_TVOQUERYCTRL	_IOWR('V',36,struct matroxfb_queryctrl)
+   
+struct matroxfb_control {
+  __u32 id;			/* A driver-defined ID */
+  int value;			/* The current value, or new value */
+};
+
+#define MATROXFB_G_TVOCTRL	_IOWR('V',27,struct matroxfb_control)
+#define MATROXFB_S_TVOCTRL	_IOW ('V',28,struct matroxfb_control)
+
 #endif
 
diff -urN linux-2.4.21-pre2/include/linux/pci_ids.h linux-2.4.21-pre3/include/linux/pci_ids.h
--- linux-2.4.21-pre2/include/linux/pci_ids.h	Mon Jan  6 16:32:29 2003
+++ linux-2.4.21-pre3/include/linux/pci_ids.h	Mon Jan  6 16:32:33 2003
@@ -136,6 +136,7 @@
 #define PCI_DEVICE_ID_COMPAQ_TACHYON	0xa0fc
 #define PCI_DEVICE_ID_COMPAQ_SMART2P	0xae10
 #define PCI_DEVICE_ID_COMPAQ_NETEL100	0xae32
+#define PCI_DEVICE_ID_COMPAQ_TRIFLEX_IDE 0xae33
 #define PCI_DEVICE_ID_COMPAQ_NETEL10	0xae34
 #define PCI_DEVICE_ID_COMPAQ_NETFLEX3I	0xae35
 #define PCI_DEVICE_ID_COMPAQ_NETEL100D	0xae40
@@ -1014,6 +1015,7 @@
 #define PCI_DEVICE_ID_VIA_8233C_0	0x3109
 #define PCI_DEVICE_ID_VIA_8361		0x3112
 #define PCI_DEVICE_ID_VIA_8233A		0x3147
+#define PCI_DEVICE_ID_VIA_P4X333   0x3168
 #define PCI_DEVICE_ID_VIA_8235        0x3177
 #define PCI_DEVICE_ID_VIA_8377_0  0x3189
 #define PCI_DEVICE_ID_VIA_8377_0	0x3189
diff -urN linux-2.4.21-pre2/include/linux/sched.h linux-2.4.21-pre3/include/linux/sched.h
--- linux-2.4.21-pre2/include/linux/sched.h	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/include/linux/sched.h	Mon Jan  6 16:32:33 2003
@@ -482,8 +482,8 @@
     policy:		SCHED_OTHER,					\
     mm:			NULL,						\
     active_mm:		&init_mm,					\
-    cpus_runnable:	-1,						\
-    cpus_allowed:	-1,						\
+    cpus_runnable:	~0UL,						\
+    cpus_allowed:	~0UL,						\
     run_list:		LIST_HEAD_INIT(tsk.run_list),			\
     next_task:		&tsk,						\
     prev_task:		&tsk,						\
diff -urN linux-2.4.21-pre2/include/linux/sockios.h linux-2.4.21-pre3/include/linux/sockios.h
--- linux-2.4.21-pre2/include/linux/sockios.h	Wed Nov  7 14:39:36 2001
+++ linux-2.4.21-pre3/include/linux/sockios.h	Mon Jan  6 16:32:33 2003
@@ -81,6 +81,8 @@
 #define SIOCGMIIREG	0x8948		/* Read MII PHY register.	*/
 #define SIOCSMIIREG	0x8949		/* Write MII PHY register.	*/
 
+#define SIOCWANDEV	0x894A		/* get/set netdev parameters	*/
+
 /* ARP cache control calls. */
 		    /*  0x8950 - 0x8952  * obsolete calls, don't re-use */
 #define SIOCDARP	0x8953		/* delete ARP table entry	*/
diff -urN linux-2.4.21-pre2/include/linux/soundcard.h linux-2.4.21-pre3/include/linux/soundcard.h
--- linux-2.4.21-pre2/include/linux/soundcard.h	Fri Nov  9 14:11:15 2001
+++ linux-2.4.21-pre3/include/linux/soundcard.h	Mon Jan  6 16:32:33 2003
@@ -554,6 +554,16 @@
 #	define AFMT_MPEG		0x00000200	/* MPEG (2) audio */
 #	define AFMT_AC3		0x00000400	/* Dolby Digital AC3 */
 
+/* because they *do* exist and we want to use them --Monty */
+#	define AFMT_S24_LE	        0x00000800	
+#	define AFMT_S24_BE	        0x00001000	
+#	define AFMT_U24_LE	        0x00002000	
+#	define AFMT_U24_BE	        0x00004000	
+#	define AFMT_S32_LE	        0x00008000	
+#	define AFMT_S32_BE	        0x00010000	
+#	define AFMT_U32_LE	        0x00020000	
+#	define AFMT_U32_BE	        0x00040000	
+
 /*
  * Buffer status queries.
  */
diff -urN linux-2.4.21-pre2/include/linux/swap.h linux-2.4.21-pre3/include/linux/swap.h
--- linux-2.4.21-pre2/include/linux/swap.h	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/include/linux/swap.h	Mon Jan  6 16:32:33 2003
@@ -147,7 +147,6 @@
 extern void get_swaphandle_info(swp_entry_t, unsigned long *, kdev_t *, 
 					struct inode **);
 extern int swap_duplicate(swp_entry_t);
-extern int swap_count(struct page *);
 extern int valid_swaphandles(swp_entry_t, unsigned long *);
 extern void swap_free(swp_entry_t);
 extern void free_swap_and_cache(swp_entry_t);
diff -urN linux-2.4.21-pre2/include/linux/sysctl.h linux-2.4.21-pre3/include/linux/sysctl.h
--- linux-2.4.21-pre2/include/linux/sysctl.h	Mon Jan  6 16:32:29 2003
+++ linux-2.4.21-pre3/include/linux/sysctl.h	Mon Jan  6 16:32:33 2003
@@ -293,7 +293,8 @@
 	NET_IPV4_NONLOCAL_BIND=88,
 	NET_IPV4_ICMP_RATELIMIT=89,
 	NET_IPV4_ICMP_RATEMASK=90,
-	NET_TCP_TW_REUSE=91
+	NET_TCP_TW_REUSE=91,
+	NET_TCP_FRTO=92
 };
 
 enum {
@@ -346,7 +347,9 @@
 enum {
 	NET_IPV6_CONF=16,
 	NET_IPV6_NEIGH=17,
-	NET_IPV6_ROUTE=18
+	NET_IPV6_ROUTE=18,
+	NET_IPV6_ICMP=19,
+	NET_IPV6_BINDV6ONLY=20
 };
 
 enum {
@@ -374,6 +377,11 @@
 	NET_IPV6_RTR_SOLICIT_DELAY=10
 };
 
+/* /proc/sys/net/ipv6/icmp */
+enum {
+	NET_IPV6_ICMP_RATELIMIT=1
+};
+
 /* /proc/sys/net/<protocol>/neigh/<dev> */
 enum {
 	NET_NEIGH_MCAST_SOLICIT=1,
diff -urN linux-2.4.21-pre2/include/net/addrconf.h linux-2.4.21-pre3/include/net/addrconf.h
--- linux-2.4.21-pre2/include/net/addrconf.h	Thu Nov 22 11:50:06 2001
+++ linux-2.4.21-pre3/include/net/addrconf.h	Mon Jan  6 16:32:33 2003
@@ -191,5 +191,21 @@
 	return (addr->s6_addr32[0] & __constant_htonl(0xFF000000)) == __constant_htonl(0xFF000000);
 }
 
+static inline int ipv6_addr_is_ll_all_nodes(const struct in6_addr *addr)
+{
+	return (addr->s6_addr32[0] == htonl(0xff020000) &&
+		addr->s6_addr32[1] == 0 &&
+		addr->s6_addr32[2] == 0 &&
+		addr->s6_addr32[3] == htonl(0x00000001));
+}
+
+static inline int ipv6_addr_is_ll_all_routers(const struct in6_addr *addr)
+{
+	return (addr->s6_addr32[0] == htonl(0xff020000) &&
+		addr->s6_addr32[1] == 0 &&
+		addr->s6_addr32[2] == 0 &&
+		addr->s6_addr32[3] == htonl(0x00000002));
+}
+
 #endif
 #endif
diff -urN linux-2.4.21-pre2/include/net/ipv6.h linux-2.4.21-pre3/include/net/ipv6.h
--- linux-2.4.21-pre2/include/net/ipv6.h	Thu Nov 22 11:47:43 2001
+++ linux-2.4.21-pre3/include/net/ipv6.h	Mon Jan  6 16:32:33 2003
@@ -74,6 +74,20 @@
 #define IPV6_ADDR_RESERVED	0x2000U	/* reserved address space */
 
 /*
+ *	Addr scopes
+ */
+#ifdef __KERNEL__
+#define IPV6_ADDR_MC_SCOPE(a)	\
+	((a)->s6_addr[1] & 0x0f)	/* nonstandard */
+#define __IPV6_ADDR_SCOPE_INVALID	-1
+#endif
+#define IPV6_ADDR_SCOPE_NODELOCAL	0x01
+#define IPV6_ADDR_SCOPE_LINKLOCAL	0x02
+#define IPV6_ADDR_SCOPE_SITELOCAL	0x05
+#define IPV6_ADDR_SCOPE_ORGLOCAL	0x08
+#define IPV6_ADDR_SCOPE_GLOBAL		0x0e
+
+/*
  *	fragmentation header
  */
 
@@ -88,6 +102,9 @@
 
 #include <net/sock.h>
 
+/* sysctls */
+extern int sysctl_ipv6_bindv6only;
+
 extern struct ipv6_mib		ipv6_statistics[NR_CPUS*2];
 #define IP6_INC_STATS(field)		SNMP_INC_STATS(ipv6_statistics, field)
 #define IP6_INC_STATS_BH(field)		SNMP_INC_STATS_BH(ipv6_statistics, field)
diff -urN linux-2.4.21-pre2/include/net/irda/irlap.h linux-2.4.21-pre3/include/net/irda/irlap.h
--- linux-2.4.21-pre2/include/net/irda/irlap.h	Mon Jan  6 16:32:29 2003
+++ linux-2.4.21-pre3/include/net/irda/irlap.h	Mon Jan  6 16:32:33 2003
@@ -50,8 +50,32 @@
 #define CBROADCAST 0xfe       /* Connection broadcast address */
 #define XID_FORMAT 0x01       /* Discovery XID format */
 
+/* Nobody seems to use this constant. */
 #define LAP_WINDOW_SIZE 8
-#define LAP_MAX_QUEUE  10
+/* We keep the LAP queue very small to minimise the amount of buffering.
+ * this improve latency and reduce resource consumption.
+ * This work only because we have synchronous refilling of IrLAP through
+ * the flow control mechanism (via scheduler and IrTTP).
+ * 2 buffers is the minimum we can work with, one that we send while polling
+ * IrTTP, and another to know that we should not send the pf bit.
+ * Jean II */
+#define LAP_HIGH_THRESHOLD     2
+/* Some rare non TTP clients don't implement flow control, and
+ * so don't comply with the above limit (and neither with this one).
+ * For IAP and management, it doesn't matter, because they never transmit much.
+ *.For IrLPT, this should be fixed.
+ * - Jean II */
+#define LAP_MAX_QUEUE 10
+/* Please note that all IrDA management frames (LMP/TTP conn req/disc and
+ * IAS queries) fall in the second category and are sent to LAP even if TTP
+ * is stopped. This means that those frames will wait only a maximum of
+ * two (2) data frames before beeing sent on the "wire", which speed up
+ * new socket setup when the link is saturated.
+ * Same story for two sockets competing for the medium : if one saturates
+ * the LAP, when the other want to transmit it only has to wait for
+ * maximum three (3) packets (2 + one scheduling), which improve performance
+ * of delay sensitive applications.
+ * Jean II */
 
 #define NR_EXPECTED     1
 #define NR_UNEXPECTED   0
diff -urN linux-2.4.21-pre2/include/net/irda/irlmp.h linux-2.4.21-pre3/include/net/irda/irlmp.h
--- linux-2.4.21-pre2/include/net/irda/irlmp.h	Mon Jan  6 16:32:29 2003
+++ linux-2.4.21-pre3/include/net/irda/irlmp.h	Mon Jan  6 16:32:33 2003
@@ -132,6 +132,7 @@
 
 	struct irlap_cb *irlap;   /* Instance of IrLAP layer */
 	hashbin_t *lsaps;         /* LSAP associated with this link */
+	struct lsap_cb *flow_next;	/* Next lsap to be polled for Tx */
 
 	__u8  caddr;  /* Connection address */
  	__u32 saddr;  /* Source device address */
@@ -235,6 +236,7 @@
 
 void irlmp_status_request(void);
 void irlmp_status_indication(struct lap_cb *, LINK_STATUS link, LOCK_STATUS lock);
+void irlmp_flow_indication(struct lap_cb *self, LOCAL_FLOW flow);
 
 int  irlmp_slsap_inuse(__u8 slsap);
 __u8 irlmp_find_free_slsap(void);
@@ -252,7 +254,9 @@
 
 static inline hashbin_t *irlmp_get_cachelog(void) { return irlmp->cachelog; }
 
-static inline int irlmp_get_lap_tx_queue_len(struct lsap_cb *self)
+/* Check if LAP queue is full.
+ * Used by IrTTP for low control, see comments in irlap.h - Jean II */
+static inline int irlmp_lap_tx_queue_full(struct lsap_cb *self)
 {
 	if (self == NULL)
 		return 0;
@@ -261,7 +265,7 @@
 	if (self->lap->irlap == NULL)
 		return 0;
 
-	return IRLAP_GET_TX_QUEUE_LEN(self->lap->irlap);
+	return(IRLAP_GET_TX_QUEUE_LEN(self->lap->irlap) >= LAP_HIGH_THRESHOLD);
 }
 
 /* After doing a irlmp_dup(), this get one of the two socket back into
diff -urN linux-2.4.21-pre2/include/net/sock.h linux-2.4.21-pre3/include/net/sock.h
--- linux-2.4.21-pre2/include/net/sock.h	Fri Aug  2 17:39:46 2002
+++ linux-2.4.21-pre3/include/net/sock.h	Mon Jan  6 16:32:33 2003
@@ -171,7 +171,8 @@
 	__u8			mc_loop:1,
 	                        recverr:1,
 	                        sndflow:1,
-	                        pmtudisc:2;
+	                        pmtudisc:2,
+				ipv6only:1;
 
 	struct ipv6_mc_socklist	*ipv6_mc_list;
 	struct ipv6_fl_socklist *ipv6_fl_list;
@@ -188,6 +189,12 @@
 	struct icmp6_filter	filter;
 };
 
+#define __ipv6_only_sock(sk)	((sk)->net_pinfo.af_inet6.ipv6only)
+#define ipv6_only_sock(sk)	((sk)->family == PF_INET6 && \
+				 (sk)->net_pinfo.af_inet6.ipv6only)
+#else
+#define __ipv6_only_sock(sk)	0
+#define ipv6_only_sock(sk)	0
 #endif /* IPV6 */
 
 #if defined(CONFIG_INET) || defined(CONFIG_INET_MODULE)
@@ -417,6 +424,9 @@
 	unsigned int		keepalive_intvl;  /* time interval between keep alive probes */
 	int			linger2;
 
+	int                     frto_counter; /* Number of new acks after RTO */
+	__u32                   frto_highmark; /* snd_nxt when RTO occurred */
+
 	unsigned long last_synq_overflow; 
 };
 
diff -urN linux-2.4.21-pre2/include/net/tcp.h linux-2.4.21-pre3/include/net/tcp.h
--- linux-2.4.21-pre2/include/net/tcp.h	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/include/net/tcp.h	Mon Jan  6 16:32:33 2003
@@ -460,6 +460,7 @@
 extern int sysctl_tcp_app_win;
 extern int sysctl_tcp_adv_win_scale;
 extern int sysctl_tcp_tw_reuse;
+extern int sysctl_tcp_frto;
 
 extern atomic_t tcp_memory_allocated;
 extern atomic_t tcp_sockets_allocated;
@@ -710,6 +711,7 @@
 extern int			tcp_child_process(struct sock *parent,
 						  struct sock *child,
 						  struct sk_buff *skb);
+extern void			tcp_enter_frto(struct sock *sk);
 extern void			tcp_enter_loss(struct sock *sk, int how);
 extern void			tcp_clear_retrans(struct tcp_opt *tp);
 extern void			tcp_update_metrics(struct sock *sk);
@@ -1830,4 +1832,17 @@
 
 #define TCP_CHECK_TIMER(sk) do { } while (0)
 
+static inline int tcp_use_frto(const struct sock *sk)
+{
+	const struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+	
+	/* F-RTO must be activated in sysctl and there must be some
+	 * unsent new data, and the advertised window should allow
+	 * sending it.
+	 */
+	return (sysctl_tcp_frto && tp->send_head &&
+		!after(TCP_SKB_CB(tp->send_head)->end_seq,
+		       tp->snd_una + tp->snd_wnd));
+}
+
 #endif	/* _TCP_H */
diff -urN linux-2.4.21-pre2/kernel/sched.c linux-2.4.21-pre3/kernel/sched.c
--- linux-2.4.21-pre2/kernel/sched.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/kernel/sched.c	Mon Jan  6 16:32:33 2003
@@ -116,7 +116,7 @@
 
 #define idle_task(cpu) (init_tasks[cpu_number_map(cpu)])
 #define can_schedule(p,cpu) \
-	((p)->cpus_runnable & (p)->cpus_allowed & (1 << cpu))
+	((p)->cpus_runnable & (p)->cpus_allowed & (1UL << cpu))
 
 #else
 
@@ -359,7 +359,7 @@
 	if (task_on_runqueue(p))
 		goto out;
 	add_to_runqueue(p);
-	if (!synchronous || !(p->cpus_allowed & (1 << smp_processor_id())))
+	if (!synchronous || !(p->cpus_allowed & (1UL << smp_processor_id())))
 		reschedule_idle(p);
 	success = 1;
 out:
diff -urN linux-2.4.21-pre2/kernel/sysctl.c linux-2.4.21-pre3/kernel/sysctl.c
--- linux-2.4.21-pre2/kernel/sysctl.c	Fri Aug  2 17:39:46 2002
+++ linux-2.4.21-pre3/kernel/sysctl.c	Mon Jan  6 16:32:33 2003
@@ -49,6 +49,7 @@
 extern int max_queued_signals;
 extern int sysrq_enabled;
 extern int core_uses_pid;
+extern char core_pattern[];
 extern int cad_pid;
 
 /* this is needed for the proc_dointvec_minmax for [fs_]overflow UID and GID */
@@ -171,6 +172,8 @@
 	 0644, NULL, &proc_dointvec},
 	{KERN_CORE_USES_PID, "core_uses_pid", &core_uses_pid, sizeof(int),
 	 0644, NULL, &proc_dointvec},
+	{KERN_CORE_PATTERN, "core_pattern", core_pattern, 64,
+	 0644, NULL, &proc_dostring, &sysctl_string},
 	{KERN_TAINTED, "tainted", &tainted, sizeof(int),
 	 0644, NULL, &proc_dointvec},
 	{KERN_CAP_BSET, "cap-bound", &cap_bset, sizeof(kernel_cap_t),
diff -urN linux-2.4.21-pre2/mm/filemap.c linux-2.4.21-pre3/mm/filemap.c
--- linux-2.4.21-pre2/mm/filemap.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/mm/filemap.c	Mon Jan  6 16:32:33 2003
@@ -95,6 +95,9 @@
 {
 	struct address_space * mapping = page->mapping;
 
+	if (mapping->a_ops->removepage)
+		mapping->a_ops->removepage(page);
+	
 	mapping->nrpages--;
 	list_del(&page->list);
 	page->mapping = NULL;
diff -urN linux-2.4.21-pre2/mm/shmem.c linux-2.4.21-pre3/mm/shmem.c
--- linux-2.4.21-pre2/mm/shmem.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/mm/shmem.c	Mon Jan  6 16:32:33 2003
@@ -919,14 +919,13 @@
 	struct inode *inode = filp->f_dentry->d_inode;
 	struct address_space *mapping = inode->i_mapping;
 	unsigned long index, offset;
-	int nr = 1;
 
 	index = *ppos >> PAGE_CACHE_SHIFT;
 	offset = *ppos & ~PAGE_CACHE_MASK;
 
-	while (nr && desc->count) {
+	for (;;) {
 		struct page *page;
-		unsigned long end_index, nr;
+		unsigned long end_index, nr, ret;
 
 		end_index = inode->i_size >> PAGE_CACHE_SHIFT;
 		if (index > end_index)
@@ -956,12 +955,14 @@
 		 * "pos" here (the actor routine has to update the user buffer
 		 * pointers and the remaining count).
 		 */
-		nr = file_read_actor(desc, page, offset, nr);
-		offset += nr;
+		ret = file_read_actor(desc, page, offset, nr);
+		offset += ret;
 		index += offset >> PAGE_CACHE_SHIFT;
 		offset &= ~PAGE_CACHE_MASK;
 	
 		page_cache_release(page);
+		if (ret != nr || !desc->count)
+			break;
 	}
 
 	*ppos = ((loff_t) index << PAGE_CACHE_SHIFT) + offset;
diff -urN linux-2.4.21-pre2/mm/swapfile.c linux-2.4.21-pre3/mm/swapfile.c
--- linux-2.4.21-pre2/mm/swapfile.c	Fri Aug  2 17:39:46 2002
+++ linux-2.4.21-pre3/mm/swapfile.c	Mon Jan  6 16:32:33 2003
@@ -1174,47 +1174,6 @@
 }
 
 /*
- * Page lock needs to be held in all cases to prevent races with
- * swap file deletion.
- */
-int swap_count(struct page *page)
-{
-	struct swap_info_struct * p;
-	unsigned long offset, type;
-	swp_entry_t entry;
-	int retval = 0;
-
-	entry.val = page->index;
-	if (!entry.val)
-		goto bad_entry;
-	type = SWP_TYPE(entry);
-	if (type >= nr_swapfiles)
-		goto bad_file;
-	p = type + swap_info;
-	offset = SWP_OFFSET(entry);
-	if (offset >= p->max)
-		goto bad_offset;
-	if (!p->swap_map[offset])
-		goto bad_unused;
-	retval = p->swap_map[offset];
-out:
-	return retval;
-
-bad_entry:
-	printk(KERN_ERR "swap_count: null entry!\n");
-	goto out;
-bad_file:
-	printk(KERN_ERR "swap_count: %s%08lx\n", Bad_file, entry.val);
-	goto out;
-bad_offset:
-	printk(KERN_ERR "swap_count: %s%08lx\n", Bad_offset, entry.val);
-	goto out;
-bad_unused:
-	printk(KERN_ERR "swap_count: %s%08lx\n", Unused_offset, entry.val);
-	goto out;
-}
-
-/*
  * Prior swap_duplicate protects against swap device deletion.
  */
 void get_swaphandle_info(swp_entry_t entry, unsigned long *offset, 
diff -urN linux-2.4.21-pre2/net/802/tr.c linux-2.4.21-pre3/net/802/tr.c
--- linux-2.4.21-pre2/net/802/tr.c	Mon Jan  6 16:32:29 2003
+++ linux-2.4.21-pre3/net/802/tr.c	Mon Jan  6 16:32:33 2003
@@ -66,8 +66,7 @@
  *	up a lot.
  */
  
-rif_cache rif_table[RIF_TABLE_SIZE];
-
+static rif_cache rif_table[RIF_TABLE_SIZE];
 static spinlock_t rif_lock = SPIN_LOCK_UNLOCKED;
 
 #define RIF_TIMEOUT 60*10*HZ
@@ -328,9 +327,9 @@
 	int i;
 	unsigned int hash, rii_p = 0;
 	rif_cache entry;
+	unsigned long flags;
 
-
-	spin_lock_bh(&rif_lock);
+	spin_lock_irqsave(&rif_lock, flags);
 	
 	/*
 	 *	Firstly see if the entry exists
@@ -369,7 +368,7 @@
 		if(!entry) 
 		{
 			printk(KERN_DEBUG "tr.c: Couldn't malloc rif cache entry !\n");
-			spin_unlock_bh(&rif_lock);
+			spin_unlock_irqrestore(&rif_lock,flags);
 			return;
 		}
 
@@ -411,7 +410,7 @@
 		    }                                         
            	entry->last_used=jiffies;               
 	}
-	spin_unlock_bh(&rif_lock);
+	spin_unlock_irqrestore(&rif_lock,flags);
 }
 
 /*
@@ -469,6 +468,7 @@
 	off_t pos=0;
 	int size,i,j,rcf_len,segment,brdgnmb;
 	unsigned long now=jiffies;
+	unsigned long flags;
 
 	rif_cache entry;
 
@@ -477,7 +477,7 @@
 	pos+=size;
 	len+=size;
 
-	spin_lock_bh(&rif_lock);
+	spin_lock_irqsave(&rif_lock, flags);
 	for(i=0;i < RIF_TABLE_SIZE;i++) 
 	{
 		for(entry=rif_table[i];entry;entry=entry->next) {
@@ -526,7 +526,7 @@
 		if(pos>offset+length)
 			break;
 	}
-	spin_unlock_bh(&rif_lock);
+	spin_unlock_irqrestore(&rif_lock,flags);
 
 	*start=buffer+(offset-begin); /* Start of wanted data */
 	len-=(offset-begin);    /* Start slop */
diff -urN linux-2.4.21-pre2/net/8021q/vlan.c linux-2.4.21-pre3/net/8021q/vlan.c
--- linux-2.4.21-pre2/net/8021q/vlan.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/net/8021q/vlan.c	Mon Jan  6 16:32:33 2003
@@ -45,7 +45,7 @@
 
 static char vlan_fullname[] = "802.1Q VLAN Support";
 static unsigned int vlan_version = 1;
-static unsigned int vlan_release = 7;
+static unsigned int vlan_release = 8;
 static char vlan_copyright[] = "Ben Greear <greearb@candelatech.com>";
 static char vlan_buggyright[] = "David S. Miller <davem@redhat.com>";
 
@@ -229,12 +229,13 @@
 			if (real_dev->features &
 			    (NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_FILTER)) {
 				real_dev->vlan_rx_kill_vid(real_dev, vlan_id);
-			} else {
-				br_write_lock(BR_NETPROTO_LOCK);
-				grp->vlan_devices[vlan_id] = NULL;
-				br_write_unlock(BR_NETPROTO_LOCK);
 			}
 
+			br_write_lock(BR_NETPROTO_LOCK);
+			grp->vlan_devices[vlan_id] = NULL;
+			br_write_unlock(BR_NETPROTO_LOCK);
+
+
 			/* Caller unregisters (and if necessary, puts)
 			 * VLAN device, but we get rid of the reference to
 			 * real_dev here.
@@ -256,6 +257,12 @@
 				__grp_unhash(grp);
 				spin_unlock_bh(&vlan_group_lock);
 
+				/* Free the group, after we have removed it
+				 * from the hash.
+				 */
+				kfree(grp);
+				grp = NULL;
+
 				ret = 1;
 			}
 
@@ -626,6 +633,7 @@
 			ret = unregister_vlan_dev(dev,
 						  VLAN_DEV_INFO(vlandev)->vlan_id);
 
+			dev_put(vlandev);
 			unregister_netdevice(vlandev);
 
 			/* Group was destroyed? */
diff -urN linux-2.4.21-pre2/net/8021q/vlan_dev.c linux-2.4.21-pre3/net/8021q/vlan_dev.c
--- linux-2.4.21-pre2/net/8021q/vlan_dev.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/net/8021q/vlan_dev.c	Mon Jan  6 16:32:33 2003
@@ -738,7 +738,7 @@
 
 	while (dmi) {
 		dev_mc_delete(dev, dmi->dmi_addr, dmi->dmi_addrlen, 0);
-		printk(KERN_INFO "%s: del %.2x:%.2x:%.2x:%.2x:%.2x:%.2x mcast address from vlan interface\n",
+		printk(KERN_DEBUG "%s: del %.2x:%.2x:%.2x:%.2x:%.2x:%.2x mcast address from vlan interface\n",
 		       dev->name,
 		       dmi->dmi_addr[0],
 		       dmi->dmi_addr[1],
@@ -820,7 +820,7 @@
 		for (dmi = vlan_dev->mc_list; dmi != NULL; dmi = dmi->next) {
 			if (vlan_should_add_mc(dmi, VLAN_DEV_INFO(vlan_dev)->old_mc_list)) {
 				dev_mc_add(real_dev, dmi->dmi_addr, dmi->dmi_addrlen, 0);
-				printk(KERN_INFO "%s: add %.2x:%.2x:%.2x:%.2x:%.2x:%.2x mcast address to master interface\n",
+				printk(KERN_DEBUG "%s: add %.2x:%.2x:%.2x:%.2x:%.2x:%.2x mcast address to master interface\n",
 				       vlan_dev->name,
 				       dmi->dmi_addr[0],
 				       dmi->dmi_addr[1],
@@ -838,7 +838,7 @@
 				 * delete it from the real list on the underlying device.
 				 */
 				dev_mc_delete(real_dev, dmi->dmi_addr, dmi->dmi_addrlen, 0);
-				printk(KERN_INFO "%s: del %.2x:%.2x:%.2x:%.2x:%.2x:%.2x mcast address from master interface\n",
+				printk(KERN_DEBUG "%s: del %.2x:%.2x:%.2x:%.2x:%.2x:%.2x mcast address from master interface\n",
 				       vlan_dev->name,
 				       dmi->dmi_addr[0],
 				       dmi->dmi_addr[1],
diff -urN linux-2.4.21-pre2/net/bluetooth/bnep/bnep.h linux-2.4.21-pre3/net/bluetooth/bnep/bnep.h
--- linux-2.4.21-pre2/net/bluetooth/bnep/bnep.h	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/net/bluetooth/bnep/bnep.h	Mon Jan  6 16:32:33 2003
@@ -112,25 +112,25 @@
 	__u8  data[0];
 } __attribute__((packed));
 
-// Ioctl interface
-#define BNEPCONADD      1
-#define BNEPCONDEL      2
-#define BNEPGETCONLIST  3
-#define BNEPGETCONINFO  4
+/* BNEP ioctl defines */
+#define BNEPCONNADD	_IOW('B', 200, int)
+#define BNEPCONNDEL	_IOW('B', 201, int)
+#define BNEPGETCONNLIST	_IOR('B', 210, int)
+#define BNEPGETCONNINFO	_IOR('B', 211, int)
 
-struct bnep_conadd_req {
+struct bnep_connadd_req {
 	int   sock;       // Connected socket
 	__u32 flags;
 	__u16 role;
 	char  device[16]; // Name of the Ethernet device
 };
 
-struct bnep_condel_req {
+struct bnep_conndel_req {
 	__u32 flags;
 	__u8  dst[ETH_ALEN];
 };
 
-struct bnep_coninfo {
+struct bnep_conninfo {
 	__u32 flags;
 	__u16 role;
 	__u16 state;	
@@ -138,9 +138,9 @@
 	char  device[16];
 };
 
-struct bnep_conlist_req {
+struct bnep_connlist_req {
 	__u32  cnum;
-	struct bnep_coninfo *ci;
+	struct bnep_conninfo *ci;
 };
 
 struct bnep_proto_filter {
@@ -148,10 +148,10 @@
 	__u16 end;
 };
 
-int bnep_add_connection(struct bnep_conadd_req *req, struct socket *sock);
-int bnep_del_connection(struct bnep_condel_req *req);
-int bnep_get_conlist(struct bnep_conlist_req *req);
-int bnep_get_coninfo(struct bnep_coninfo *ci);
+int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock);
+int bnep_del_connection(struct bnep_conndel_req *req);
+int bnep_get_connlist(struct bnep_connlist_req *req);
+int bnep_get_conninfo(struct bnep_conninfo *ci);
 
 // BNEP sessions
 struct bnep_session {
diff -urN linux-2.4.21-pre2/net/bluetooth/bnep/core.c linux-2.4.21-pre3/net/bluetooth/bnep/core.c
--- linux-2.4.21-pre2/net/bluetooth/bnep/core.c	Mon Jan  6 16:32:29 2003
+++ linux-2.4.21-pre3/net/bluetooth/bnep/core.c	Mon Jan  6 16:32:33 2003
@@ -505,7 +505,7 @@
 	return 0;
 }
 
-int bnep_add_connection(struct bnep_conadd_req *req, struct socket *sock)
+int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
 {
 	struct net_device *dev;
 	struct bnep_session *s, *ss;
@@ -596,7 +596,7 @@
 	return err;
 }
 
-int bnep_del_connection(struct bnep_condel_req *req)
+int bnep_del_connection(struct bnep_conndel_req *req)
 {
 	struct bnep_session *s;
 	int  err = 0;
@@ -621,7 +621,7 @@
 	return err;
 }
 
-static void __bnep_copy_ci(struct bnep_coninfo *ci, struct bnep_session *s)
+static void __bnep_copy_ci(struct bnep_conninfo *ci, struct bnep_session *s)
 {
 	memcpy(ci->dst, s->eh.h_source, ETH_ALEN);
 	strcpy(ci->device, s->dev.name);
@@ -630,7 +630,7 @@
 	ci->role  = s->role;
 }
 
-int bnep_get_conlist(struct bnep_conlist_req *req)
+int bnep_get_connlist(struct bnep_connlist_req *req)
 {
 	struct list_head *p;
 	int err = 0, n = 0;
@@ -639,7 +639,7 @@
 
 	list_for_each(p, &bnep_session_list) {
 		struct bnep_session *s;
-		struct bnep_coninfo ci;
+		struct bnep_conninfo ci;
 
 		s = list_entry(p, struct bnep_session, list);
 
@@ -661,7 +661,7 @@
 	return err;
 }
 
-int bnep_get_coninfo(struct bnep_coninfo *ci)
+int bnep_get_conninfo(struct bnep_conninfo *ci)
 {
 	struct bnep_session *s;
 	int err = 0;
diff -urN linux-2.4.21-pre2/net/bluetooth/bnep/sock.c linux-2.4.21-pre3/net/bluetooth/bnep/sock.c
--- linux-2.4.21-pre2/net/bluetooth/bnep/sock.c	Mon Jan  6 16:32:29 2003
+++ linux-2.4.21-pre3/net/bluetooth/bnep/sock.c	Mon Jan  6 16:32:33 2003
@@ -103,17 +103,17 @@
 
 static int bnep_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
 {
-	struct bnep_conlist_req cl;
-	struct bnep_conadd_req  ca;
-	struct bnep_condel_req  cd;
-	struct bnep_coninfo ci;
+	struct bnep_connlist_req cl;
+	struct bnep_connadd_req  ca;
+	struct bnep_conndel_req  cd;
+	struct bnep_conninfo ci;
 	struct socket *nsock;
 	int err;
 
 	BT_DBG("cmd %x arg %lx", cmd, arg);
 
 	switch (cmd) {
-	case BNEPCONADD:
+	case BNEPCONNADD:
 		if (!capable(CAP_NET_ADMIN))
 			return -EACCES;
 
@@ -136,7 +136,7 @@
 
 		return err;
 	
-	case BNEPCONDEL:
+	case BNEPCONNDEL:
 		if (!capable(CAP_NET_ADMIN))
 			return -EACCES;
 
@@ -145,24 +145,24 @@
 	
 		return bnep_del_connection(&cd);
 
-	case BNEPGETCONLIST:
+	case BNEPGETCONNLIST:
 		if (copy_from_user(&cl, (void *) arg, sizeof(cl)))
 			return -EFAULT;
 
 		if (cl.cnum <= 0)
 			return -EINVAL;
 	
-		err = bnep_get_conlist(&cl);
+		err = bnep_get_connlist(&cl);
 		if (!err && copy_to_user((void *) arg, &cl, sizeof(cl)))
 			return -EFAULT;
 
 		return err;
 
-	case BNEPGETCONINFO:
+	case BNEPGETCONNINFO:
 		if (copy_from_user(&ci, (void *) arg, sizeof(ci)))
 			return -EFAULT;
 
-		err = bnep_get_coninfo(&ci);
+		err = bnep_get_conninfo(&ci);
 		if (!err && copy_to_user((void *) arg, &ci, sizeof(ci)))
 			return -EFAULT;
 
diff -urN linux-2.4.21-pre2/net/bluetooth/rfcomm/core.c linux-2.4.21-pre3/net/bluetooth/rfcomm/core.c
--- linux-2.4.21-pre2/net/bluetooth/rfcomm/core.c	Mon Jan  6 16:32:29 2003
+++ linux-2.4.21-pre3/net/bluetooth/rfcomm/core.c	Mon Jan  6 16:32:33 2003
@@ -258,7 +258,7 @@
 	rfcomm_session_put(s);
 }
 
-static struct rfcomm_dlc *rfcomm_dlc_get(struct rfcomm_session *s, int dlci)
+static struct rfcomm_dlc *rfcomm_dlc_get(struct rfcomm_session *s, u8 dlci)
 {
 	struct rfcomm_dlc *d;
 	struct list_head *p;
@@ -274,7 +274,8 @@
 static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst, u8 channel)
 {
 	struct rfcomm_session *s;
-	int err = 0, dlci = __dlci(0, channel);
+	u8 dlci = __dlci(0, channel);
+	int err = 0;
 
 	BT_DBG("dlc %p state %ld %s %s channel %d dlci %d", 
 			d, d->state, batostr(src), batostr(dst), channel, dlci);
@@ -894,7 +895,7 @@
 }
 
 /* ---- RFCOMM frame reception ---- */
-static int rfcomm_recv_ua(struct rfcomm_session *s, int dlci)
+static int rfcomm_recv_ua(struct rfcomm_session *s, u8 dlci)
 {
 	BT_DBG("session %p state %ld dlci %d", s, s->state, dlci);
 
@@ -935,7 +936,7 @@
 	return 0;
 }
 
-static int rfcomm_recv_dm(struct rfcomm_session *s, int dlci)
+static int rfcomm_recv_dm(struct rfcomm_session *s, u8 dlci)
 {
 	int err = 0;
 
@@ -965,7 +966,7 @@
 	return 0;
 }
 
-static int rfcomm_recv_disc(struct rfcomm_session *s, int dlci)
+static int rfcomm_recv_disc(struct rfcomm_session *s, u8 dlci)
 {
 	int err = 0;
 
@@ -1001,10 +1002,10 @@
 	return 0;
 }
 
-static int rfcomm_recv_sabm(struct rfcomm_session *s, int dlci)
+static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci)
 {
 	struct rfcomm_dlc *d;
-	int channel;
+	u8 channel;
 
 	BT_DBG("session %p state %ld dlci %d", s, s->state, dlci);
 
@@ -1087,7 +1088,7 @@
 {
 	struct rfcomm_pn *pn = (void *) skb->data;
 	struct rfcomm_dlc *d;
-	int dlci = pn->dlci;
+	u8 dlci = pn->dlci;
 
 	BT_DBG("session %p state %ld dlci %d", s, s->state, dlci);
 
@@ -1112,7 +1113,7 @@
 			}
 		}
 	} else {
-		int channel = __srv_channel(dlci);
+		u8 channel = __srv_channel(dlci);
 
 		if (!cr)
 			return 0;
@@ -1138,7 +1139,7 @@
 static int rfcomm_recv_rpn(struct rfcomm_session *s, int cr, int len, struct sk_buff *skb)
 {
 	struct rfcomm_rpn *rpn = (void *) skb->data;
-	int dlci = __get_dlci(rpn->dlci);
+	u8 dlci = __get_dlci(rpn->dlci);
 
 	u8 bit_rate  = 0;
 	u8 data_bits = 0;
@@ -1228,7 +1229,7 @@
 {
 	struct rfcomm_msc *msc = (void *) skb->data;
 	struct rfcomm_dlc *d;
-	int dlci = __get_dlci(msc->dlci);
+	u8 dlci = __get_dlci(msc->dlci);
 
 	BT_DBG("dlci %d cr %d v24 0x%x", dlci, cr, msc->v24_sig);
 
@@ -1294,7 +1295,7 @@
 	return 0;
 }
 
-static int rfcomm_recv_data(struct rfcomm_session *s, int dlci, int pf, struct sk_buff *skb)
+static int rfcomm_recv_data(struct rfcomm_session *s, u8 dlci, int pf, struct sk_buff *skb)
 {
 	struct rfcomm_dlc *d;
 
diff -urN linux-2.4.21-pre2/net/bluetooth/rfcomm/sock.c linux-2.4.21-pre3/net/bluetooth/rfcomm/sock.c
--- linux-2.4.21-pre2/net/bluetooth/rfcomm/sock.c	Mon Jan  6 16:32:29 2003
+++ linux-2.4.21-pre3/net/bluetooth/rfcomm/sock.c	Mon Jan  6 16:32:33 2003
@@ -110,7 +110,7 @@
 }
 
 /* ---- Socket functions ---- */
-static struct sock *__rfcomm_get_sock_by_addr(int channel, bdaddr_t *src)
+static struct sock *__rfcomm_get_sock_by_addr(u8 channel, bdaddr_t *src)
 {
 	struct sock *sk;
 
@@ -126,7 +126,7 @@
 /* Find socket with channel and source bdaddr.
  * Returns closest match.
  */
-static struct sock *__rfcomm_get_sock_by_channel(int state, __u16 channel, bdaddr_t *src)
+static struct sock *__rfcomm_get_sock_by_channel(int state, u8 channel, bdaddr_t *src)
 {
 	struct sock *sk, *sk1 = NULL;
 
@@ -149,7 +149,7 @@
 
 /* Find socket with given address (channel, src).
  * Returns locked socket */
-static inline struct sock *rfcomm_get_sock_by_channel(int state, __u16 channel, bdaddr_t *src)
+static inline struct sock *rfcomm_get_sock_by_channel(int state, u8 channel, bdaddr_t *src)
 {
 	struct sock *s;
 	read_lock(&rfcomm_sk_list.lock);
diff -urN linux-2.4.21-pre2/net/bridge/br_if.c linux-2.4.21-pre3/net/bridge/br_if.c
--- linux-2.4.21-pre2/net/bridge/br_if.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/net/bridge/br_if.c	Mon Jan  6 16:32:33 2003
@@ -143,7 +143,7 @@
 	int i;
 	struct net_bridge_port *p;
 
-	p = kmalloc(sizeof(*p), GFP_KERNEL);
+	p = kmalloc(sizeof(*p), GFP_ATOMIC);
 	if (p == NULL)
 		return p;
 
diff -urN linux-2.4.21-pre2/net/core/dev.c linux-2.4.21-pre3/net/core/dev.c
--- linux-2.4.21-pre2/net/core/dev.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/net/core/dev.c	Mon Jan  6 16:32:33 2003
@@ -2155,7 +2155,8 @@
 			    cmd == SIOCETHTOOL ||
 			    cmd == SIOCGMIIPHY ||
 			    cmd == SIOCGMIIREG ||
-			    cmd == SIOCSMIIREG) {
+			    cmd == SIOCSMIIREG ||
+			    cmd == SIOCWANDEV) {
 				if (dev->do_ioctl) {
 					if (!netif_device_present(dev))
 						return -ENODEV;
@@ -2321,8 +2322,9 @@
 		 */	
 		 
 		default:
-			if (cmd >= SIOCDEVPRIVATE &&
-			    cmd <= SIOCDEVPRIVATE + 15) {
+			if (cmd == SIOCWANDEV ||
+			    (cmd >= SIOCDEVPRIVATE &&
+			     cmd <= SIOCDEVPRIVATE + 15)) {
 				dev_load(ifr.ifr_name);
 				dev_probe_lock();
 				rtnl_lock();
diff -urN linux-2.4.21-pre2/net/ipv4/fib_semantics.c linux-2.4.21-pre3/net/ipv4/fib_semantics.c
--- linux-2.4.21-pre2/net/ipv4/fib_semantics.c	Fri Aug  2 17:39:46 2002
+++ linux-2.4.21-pre3/net/ipv4/fib_semantics.c	Mon Jan  6 16:32:33 2003
@@ -1025,7 +1025,8 @@
 		len = sprintf(buffer, "%s\t%08X\t%08X\t%04X\t%d\t%u\t%d\t%08X\t%d\t%u\t%u",
 			      fi->fib_dev ? fi->fib_dev->name : "*", prefix,
 			      fi->fib_nh->nh_gw, flags, 0, 0, fi->fib_priority,
-			      mask, fi->fib_advmss+40, fi->fib_window, fi->fib_rtt>>3);
+			      mask, (fi->fib_advmss ? fi->fib_advmss+40 : 0),
+			      fi->fib_window, fi->fib_rtt>>3);
 	} else {
 		len = sprintf(buffer, "*\t%08X\t%08X\t%04X\t%d\t%u\t%d\t%08X\t%d\t%u\t%u",
 			      prefix, 0,
diff -urN linux-2.4.21-pre2/net/ipv4/netfilter/ipt_unclean.c linux-2.4.21-pre3/net/ipv4/netfilter/ipt_unclean.c
--- linux-2.4.21-pre2/net/ipv4/netfilter/ipt_unclean.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/net/ipv4/netfilter/ipt_unclean.c	Mon Jan  6 16:32:33 2003
@@ -521,6 +521,16 @@
 		return 0;
 	}
 
+	/* CHECK: Do not use what is unused.
+	 * First bit of fragmentation flags should be unused.
+	 * May be used by OS fingerprinting tools.
+	 * 04 Jun 2002, Maciej Soltysiak, solt@dns.toxicfilms.tv
+	 */
+	if (ntohs(iph->frag_off)>>15) {
+		limpk("IP unused bit set\n");
+		return 0;
+	}
+
 	/* Per-protocol checks. */
 	switch (iph->protocol) {
 	case IPPROTO_ICMP:
diff -urN linux-2.4.21-pre2/net/ipv4/sysctl_net_ipv4.c linux-2.4.21-pre3/net/ipv4/sysctl_net_ipv4.c
--- linux-2.4.21-pre2/net/ipv4/sysctl_net_ipv4.c	Fri Aug  2 17:39:46 2002
+++ linux-2.4.21-pre3/net/ipv4/sysctl_net_ipv4.c	Mon Jan  6 16:32:33 2003
@@ -221,6 +221,8 @@
 	 &sysctl_icmp_ratemask, sizeof(int), 0644, NULL, &proc_dointvec},
 	{NET_TCP_TW_REUSE, "tcp_tw_reuse",
 	 &sysctl_tcp_tw_reuse, sizeof(int), 0644, NULL, &proc_dointvec},
+	{NET_TCP_FRTO, "tcp_frto",
+	 &sysctl_tcp_frto, sizeof(int), 0644, NULL, &proc_dointvec},
 	{0}
 };
 
diff -urN linux-2.4.21-pre2/net/ipv4/tcp_input.c linux-2.4.21-pre3/net/ipv4/tcp_input.c
--- linux-2.4.21-pre2/net/ipv4/tcp_input.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/net/ipv4/tcp_input.c	Mon Jan  6 16:32:33 2003
@@ -60,6 +60,7 @@
  *		Pasi Sarolahti,
  *		Panu Kuhlberg:		Experimental audit of TCP (re)transmission
  *					engine. Lots of bugs are found.
+ *		Pasi Sarolahti:		F-RTO for dealing with spurious RTOs
  */
 
 #include <linux/config.h>
@@ -86,6 +87,7 @@
 int sysctl_tcp_stdurg = 0;
 int sysctl_tcp_rfc1337 = 0;
 int sysctl_tcp_max_orphans = NR_FILE;
+int sysctl_tcp_frto = 0;
 
 #define FLAG_DATA		0x01 /* Incoming frame contained data.		*/
 #define FLAG_WIN_UPDATE		0x02 /* Incoming ACK was a window update.	*/
@@ -955,6 +957,89 @@
 	return flag;
 }
 
+/* RTO occurred, but do not yet enter loss state. Instead, transmit two new
+ * segments to see from the next ACKs whether any data was really missing.
+ * If the RTO was spurious, new ACKs should arrive.
+ */
+void tcp_enter_frto(struct sock *sk)
+{
+	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+	struct sk_buff *skb;
+
+	tp->frto_counter = 1;
+
+	if (tp->ca_state <= TCP_CA_Disorder ||
+            tp->snd_una == tp->high_seq ||
+            (tp->ca_state == TCP_CA_Loss && !tp->retransmits)) {
+		tp->prior_ssthresh = tcp_current_ssthresh(tp);
+		tp->snd_ssthresh = tcp_recalc_ssthresh(tp);
+	}
+
+	/* Have to clear retransmission markers here to keep the bookkeeping
+	 * in shape, even though we are not yet in Loss state.
+	 * If something was really lost, it is eventually caught up
+	 * in tcp_enter_frto_loss.
+	 */
+	tp->retrans_out = 0;
+	tp->undo_marker = tp->snd_una;
+	tp->undo_retrans = 0;
+
+	for_retrans_queue(skb, sk, tp) {
+		TCP_SKB_CB(skb)->sacked &= ~TCPCB_RETRANS;
+	}
+	tcp_sync_left_out(tp);
+
+	tp->ca_state = TCP_CA_Open;
+	tp->frto_highmark = tp->snd_nxt;
+}
+
+/* Enter Loss state after F-RTO was applied. Dupack arrived after RTO,
+ * which indicates that we should follow the traditional RTO recovery,
+ * i.e. mark everything lost and do go-back-N retransmission.
+ */
+void tcp_enter_frto_loss(struct sock *sk)
+{
+	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+	struct sk_buff *skb;
+	int cnt = 0;
+
+	tp->sacked_out = 0;
+	tp->lost_out = 0;
+	tp->fackets_out = 0;
+
+	for_retrans_queue(skb, sk, tp) {
+		cnt++;
+		TCP_SKB_CB(skb)->sacked &= ~TCPCB_LOST;
+		if (!(TCP_SKB_CB(skb)->sacked&TCPCB_SACKED_ACKED)) {
+
+			/* Do not mark those segments lost that were
+			 * forward transmitted after RTO
+			 */
+			if(!after(TCP_SKB_CB(skb)->end_seq,
+				   tp->frto_highmark)) {
+				TCP_SKB_CB(skb)->sacked |= TCPCB_LOST;
+				tp->lost_out++;
+			}
+		} else {
+			tp->sacked_out++;
+			tp->fackets_out = cnt;
+		}
+	}
+	tcp_sync_left_out(tp);
+
+	tp->snd_cwnd = tp->frto_counter + tcp_packets_in_flight(tp)+1;
+	tp->snd_cwnd_cnt = 0;
+	tp->snd_cwnd_stamp = tcp_time_stamp;
+	tp->undo_marker = 0;
+	tp->frto_counter = 0;
+
+	tp->reordering = min_t(unsigned int, tp->reordering,
+					     sysctl_tcp_reordering);
+	tp->ca_state = TCP_CA_Loss;
+	tp->high_seq = tp->frto_highmark;
+	TCP_ECN_queue_cwr(tp);
+}
+
 void tcp_clear_retrans(struct tcp_opt *tp)
 {
 	tp->left_out = 0;
@@ -1525,7 +1610,8 @@
 	/* E. Check state exit conditions. State can be terminated
 	 *    when high_seq is ACKed. */
 	if (tp->ca_state == TCP_CA_Open) {
-		BUG_TRAP(tp->retrans_out == 0);
+		if (!sysctl_tcp_frto)
+			BUG_TRAP(tp->retrans_out == 0);
 		tp->retrans_stamp = 0;
 	} else if (!before(tp->snd_una, tp->high_seq)) {
 		switch (tp->ca_state) {
@@ -1892,6 +1978,41 @@
 	return flag;
 }
 
+static void tcp_process_frto(struct sock *sk, u32 prior_snd_una)
+{
+	struct tcp_opt *tp = &sk->tp_pinfo.af_tcp;
+	
+	tcp_sync_left_out(tp);
+	
+	if (tp->snd_una == prior_snd_una ||
+	    !before(tp->snd_una, tp->frto_highmark)) {
+		/* RTO was caused by loss, start retransmitting in
+		 * go-back-N slow start
+		 */
+		tcp_enter_frto_loss(sk);
+		return;
+	}
+
+	if (tp->frto_counter == 1) {
+		/* First ACK after RTO advances the window: allow two new
+		 * segments out.
+		 */
+		tp->snd_cwnd = tcp_packets_in_flight(tp) + 2;
+	} else {
+		/* Also the second ACK after RTO advances the window.
+		 * The RTO was likely spurious. Reduce cwnd and continue
+		 * in congestion avoidance
+		 */
+		tp->snd_cwnd = min(tp->snd_cwnd, tp->snd_ssthresh);
+		tcp_moderate_cwnd(tp);
+	}
+
+	/* F-RTO affects on two new ACKs following RTO.
+	 * At latest on third ACK the TCP behavor is back to normal.
+	 */
+	tp->frto_counter = (tp->frto_counter + 1) % 3;
+}
+
 /* This routine deals with incoming acks, but not outgoing ones. */
 static int tcp_ack(struct sock *sk, struct sk_buff *skb, int flag)
 {
@@ -1949,6 +2070,9 @@
 	/* See if we can take anything off of the retransmit queue. */
 	flag |= tcp_clean_rtx_queue(sk);
 
+	if (tp->frto_counter)
+		tcp_process_frto(sk, prior_snd_una);
+
 	if (tcp_ack_is_dubious(tp, flag)) {
 		/* Advanve CWND, if state allows this. */
 		if ((flag&FLAG_DATA_ACKED) && prior_in_flight >= tp->snd_cwnd &&
diff -urN linux-2.4.21-pre2/net/ipv4/tcp_ipv4.c linux-2.4.21-pre3/net/ipv4/tcp_ipv4.c
--- linux-2.4.21-pre2/net/ipv4/tcp_ipv4.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/net/ipv4/tcp_ipv4.c	Mon Jan  6 16:32:33 2003
@@ -45,9 +45,13 @@
  *	Vitaly E. Lavrov	:	Transparent proxy revived after year coma.
  *	Andi Kleen		:	Fix new listen.
  *	Andi Kleen		:	Fix accept error reporting.
+ *	YOSHIFUJI Hideaki @USAGI and:	Support IPV6_V6ONLY socket option, which
+ *	Alexey Kuznetsov		allow both IPv4 and IPv6 sockets to bind
+ *					a single port at the same time.
  */
 
 #include <linux/config.h>
+
 #include <linux/types.h>
 #include <linux/fcntl.h>
 #include <linux/random.h>
@@ -182,6 +186,7 @@
 	for( ; sk2 != NULL; sk2 = sk2->bind_next) {
 		if (sk != sk2 &&
 		    sk2->reuse <= 1 &&
+		    !ipv6_only_sock(sk2) &&
 		    sk->bound_dev_if == sk2->bound_dev_if) {
 			if (!sk_reuse	||
 			    !sk2->reuse	||
@@ -418,23 +423,27 @@
 	struct sock *result = NULL;
 	int score, hiscore;
 
-	hiscore=0;
+	hiscore=-1;
 	for(; sk; sk = sk->next) {
-		if(sk->num == hnum) {
+		if(sk->num == hnum && !ipv6_only_sock(sk)) {
 			__u32 rcv_saddr = sk->rcv_saddr;
 
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+			score = sk->family == PF_INET ? 1 : 0;
+#else
 			score = 1;
+#endif
 			if(rcv_saddr) {
 				if (rcv_saddr != daddr)
 					continue;
-				score++;
+				score+=2;
 			}
 			if (sk->bound_dev_if) {
 				if (sk->bound_dev_if != dif)
 					continue;
-				score++;
+				score+=2;
 			}
-			if (score == 3)
+			if (score == 5)
 				return sk;
 			if (score > hiscore) {
 				hiscore = score;
@@ -456,6 +465,7 @@
 		if (sk->num == hnum &&
 		    sk->next == NULL &&
 		    (!sk->rcv_saddr || sk->rcv_saddr == daddr) &&
+		    (sk->family == PF_INET || !ipv6_only_sock(sk)) &&
 		    !sk->bound_dev_if)
 			goto sherry_cache;
 		sk = __tcp_v4_lookup_listener(sk, daddr, hnum, dif);
diff -urN linux-2.4.21-pre2/net/ipv4/tcp_minisocks.c linux-2.4.21-pre3/net/ipv4/tcp_minisocks.c
--- linux-2.4.21-pre2/net/ipv4/tcp_minisocks.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/net/ipv4/tcp_minisocks.c	Mon Jan  6 16:32:33 2003
@@ -715,6 +715,9 @@
 		newtp->snd_cwnd = 2;
 		newtp->snd_cwnd_cnt = 0;
 
+		newtp->frto_counter = 0;
+		newtp->frto_highmark = 0;
+
 		newtp->ca_state = TCP_CA_Open;
 		tcp_init_xmit_timers(newsk);
 		skb_queue_head_init(&newtp->out_of_order_queue);
diff -urN linux-2.4.21-pre2/net/ipv4/tcp_timer.c linux-2.4.21-pre3/net/ipv4/tcp_timer.c
--- linux-2.4.21-pre2/net/ipv4/tcp_timer.c	Mon Oct  1 09:19:57 2001
+++ linux-2.4.21-pre3/net/ipv4/tcp_timer.c	Mon Jan  6 16:32:33 2003
@@ -372,7 +372,11 @@
 		}
 	}
 
-	tcp_enter_loss(sk, 0);
+	if (tcp_use_frto(sk)) {
+		tcp_enter_frto(sk);
+	} else {
+		tcp_enter_loss(sk, 0);
+	}
 
 	if (tcp_retransmit_skb(sk, skb_peek(&sk->write_queue)) > 0) {
 		/* Retransmission failed because of local congestion,
diff -urN linux-2.4.21-pre2/net/ipv4/udp.c linux-2.4.21-pre3/net/ipv4/udp.c
--- linux-2.4.21-pre2/net/ipv4/udp.c	Fri Aug  2 17:39:46 2002
+++ linux-2.4.21-pre3/net/ipv4/udp.c	Mon Jan  6 16:32:33 2003
@@ -61,6 +61,9 @@
  *					return ENOTCONN for unconnected sockets (POSIX)
  *		Janos Farkas	:	don't deliver multi/broadcasts to a different
  *					bound-to-device socket
+ *	YOSHIFUJI Hideaki @USAGI and:	Support IPV6_V6ONLY socket option, which
+ *	Alexey Kuznetsov:		allow both IPv4 and IPv6 sockets to bind
+ *					a single port at the same time.
  *
  *
  *		This program is free software; you can redistribute it and/or
@@ -85,6 +88,7 @@
 #include <linux/netdevice.h>
 #include <net/snmp.h>
 #include <net/ip.h>
+#include <net/ipv6.h>
 #include <net/protocol.h>
 #include <linux/skbuff.h>
 #include <net/sock.h>
@@ -159,6 +163,7 @@
 		     sk2 = sk2->next) {
 			if (sk2->num == snum &&
 			    sk2 != sk &&
+			    !ipv6_only_sock(sk2) &&
 			    sk2->bound_dev_if == sk->bound_dev_if &&
 			    (!sk2->rcv_saddr ||
 			     !sk->rcv_saddr ||
@@ -215,29 +220,34 @@
 	int badness = -1;
 
 	for(sk = udp_hash[hnum & (UDP_HTABLE_SIZE - 1)]; sk != NULL; sk = sk->next) {
-		if(sk->num == hnum) {
-			int score = 0;
+		if(sk->num == hnum && !ipv6_only_sock(sk)) {
+			int score;
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+			score = sk->family == PF_INET ? 1 : 0;
+#else
+			score = 1;
+#endif
 			if(sk->rcv_saddr) {
 				if(sk->rcv_saddr != daddr)
 					continue;
-				score++;
+				score+=2;
 			}
 			if(sk->daddr) {
 				if(sk->daddr != saddr)
 					continue;
-				score++;
+				score+=2;
 			}
 			if(sk->dport) {
 				if(sk->dport != sport)
 					continue;
-				score++;
+				score+=2;
 			}
 			if(sk->bound_dev_if) {
 				if(sk->bound_dev_if != dif)
 					continue;
-				score++;
+				score+=2;
 			}
-			if(score == 4) {
+			if(score == 9) {
 				result = sk;
 				break;
 			} else if(score > badness) {
@@ -273,6 +283,7 @@
 		    (s->daddr && s->daddr!=rmt_addr)			||
 		    (s->dport != rmt_port && s->dport != 0)			||
 		    (s->rcv_saddr  && s->rcv_saddr != loc_addr)		||
+		    ipv6_only_sock(s)					||
 		    (s->bound_dev_if && s->bound_dev_if != dif))
 			continue;
 		break;
diff -urN linux-2.4.21-pre2/net/ipv6/addrconf.c linux-2.4.21-pre3/net/ipv6/addrconf.c
--- linux-2.4.21-pre2/net/ipv6/addrconf.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/net/ipv6/addrconf.c	Mon Jan  6 16:32:33 2003
@@ -786,7 +786,7 @@
 	struct in6_addr addr;
 
 	ipv6_addr_set(&addr,  htonl(0xFE800000), 0, 0, 0);
-	addrconf_prefix_route(&addr, 10, dev, 0, RTF_ADDRCONF);
+	addrconf_prefix_route(&addr, 64, dev, 0, RTF_ADDRCONF);
 }
 
 static struct inet6_dev *addrconf_add_dev(struct net_device *dev)
@@ -947,6 +947,7 @@
 				ipv6_ifa_notify((flags&IFA_F_DEPRECATED) ?
 						0 : RTM_NEWADDR, ifp);
 			in6_ifa_put(ifp);
+			addrconf_verify(0);
 		}
 	}
 	in6_dev_put(in6_dev);
@@ -1161,7 +1162,7 @@
 					flag |= IFA_HOST;
 				}
 				if (idev->dev->flags&IFF_POINTOPOINT)
-					plen = 10;
+					plen = 64;
 				else
 					plen = 96;
 
@@ -1211,7 +1212,7 @@
 {
 	struct inet6_ifaddr * ifp;
 
-	ifp = ipv6_add_addr(idev, addr, 10, IFA_LINK, IFA_F_PERMANENT);
+	ifp = ipv6_add_addr(idev, addr, 64, IFA_LINK, IFA_F_PERMANENT);
 	if (ifp) {
 		addrconf_dad_start(ifp);
 		in6_ifa_put(ifp);
diff -urN linux-2.4.21-pre2/net/ipv6/af_inet6.c linux-2.4.21-pre3/net/ipv6/af_inet6.c
--- linux-2.4.21-pre2/net/ipv6/af_inet6.c	Wed Oct 17 14:16:39 2001
+++ linux-2.4.21-pre3/net/ipv6/af_inet6.c	Mon Jan  6 16:32:33 2003
@@ -88,6 +88,8 @@
 extern void ipv6_sysctl_unregister(void);
 #endif
 
+int sysctl_ipv6_bindv6only;
+
 #ifdef INET_REFCNT_DEBUG
 atomic_t inet6_sock_nr;
 #endif
@@ -173,6 +175,8 @@
 	sk->net_pinfo.af_inet6.mc_loop	  = 1;
 	sk->net_pinfo.af_inet6.pmtudisc	  = IPV6_PMTUDISC_WANT;
 
+	sk->net_pinfo.af_inet6.ipv6only	= sysctl_ipv6_bindv6only;
+	
 	/* Init the ipv4 part of the socket since we can have sockets
 	 * using v6 API for ipv4.
 	 */
diff -urN linux-2.4.21-pre2/net/ipv6/datagram.c linux-2.4.21-pre3/net/ipv6/datagram.c
--- linux-2.4.21-pre2/net/ipv6/datagram.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/net/ipv6/datagram.c	Mon Jan  6 16:32:33 2003
@@ -158,6 +158,7 @@
 	if (serr->ee.ee_origin != SO_EE_ORIGIN_LOCAL) {
 		sin->sin6_family = AF_INET6;
 		sin->sin6_flowinfo = 0;
+		sin->sin6_scope_id = 0;
 		if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP6) {
 			memcpy(&sin->sin6_addr, &skb->nh.ipv6h->saddr, 16);
 			if (sk->net_pinfo.af_inet6.rxopt.all)
diff -urN linux-2.4.21-pre2/net/ipv6/exthdrs.c linux-2.4.21-pre3/net/ipv6/exthdrs.c
--- linux-2.4.21-pre2/net/ipv6/exthdrs.c	Wed Jun 20 21:00:55 2001
+++ linux-2.4.21-pre3/net/ipv6/exthdrs.c	Mon Jan  6 16:32:33 2003
@@ -402,7 +402,13 @@
 	if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8))
 		goto fail;
 
-	len = (skb->h.raw[1]+1)<<2;
+	/*
+	 * RFC2402 2.2 Payload Length
+	 * The 8-bit field specifies the length of AH in 32-bit words 
+	 * (4-byte units), minus "2".
+	 * -- Noriaki Takamiya @USAGI Project
+	 */
+	len = (skb->h.raw[1]+2)<<2;
 
 	if (len&7)
 		goto fail;
diff -urN linux-2.4.21-pre2/net/ipv6/icmp.c linux-2.4.21-pre3/net/ipv6/icmp.c
--- linux-2.4.21-pre2/net/ipv6/icmp.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/net/ipv6/icmp.c	Mon Jan  6 16:32:33 2003
@@ -25,6 +25,7 @@
  *					add more length checks and other fixes.
  *	yoshfuji		:	ensure to sent parameter problem for
  *					fragments.
+ *	YOSHIFUJI Hideaki @USAGI:	added sysctl for icmp rate limit.
  */
 
 #define __NO_VERSION__
@@ -40,6 +41,10 @@
 #include <linux/skbuff.h>
 #include <linux/init.h>
 
+#ifdef CONFIG_SYSCTL
+#include <linux/sysctl.h>
+#endif
+
 #include <linux/inet.h>
 #include <linux/netdevice.h>
 #include <linux/icmpv6.h>
@@ -715,3 +720,12 @@
 
 	return fatal;
 }
+
+#ifdef CONFIG_SYSCTL
+ctl_table ipv6_icmp_table[] = {
+	{NET_IPV6_ICMP_RATELIMIT, "ratelimit",
+	&sysctl_icmpv6_time, sizeof(int), 0644, NULL, &proc_dointvec},
+	{0},
+};
+#endif
+
diff -urN linux-2.4.21-pre2/net/ipv6/ipv6_sockglue.c linux-2.4.21-pre3/net/ipv6/ipv6_sockglue.c
--- linux-2.4.21-pre2/net/ipv6/ipv6_sockglue.c	Thu Sep 20 14:12:56 2001
+++ linux-2.4.21-pre3/net/ipv6/ipv6_sockglue.c	Mon Jan  6 16:32:33 2003
@@ -157,7 +157,8 @@
 				break;
 			}
 
-			if (!(ipv6_addr_type(&np->daddr) & IPV6_ADDR_MAPPED)) {
+			if (ipv6_only_sock(sk) ||
+			    !(ipv6_addr_type(&np->daddr) & IPV6_ADDR_MAPPED)) {
 				retv = -EADDRNOTAVAIL;
 				break;
 			}
@@ -203,6 +204,13 @@
 		}
 		goto e_inval;
 
+	case IPV6_V6ONLY:
+		if (sk->num)
+			goto e_inval;
+		np->ipv6only = valbool;
+		retv = 0;
+		break;
+
 	case IPV6_PKTINFO:
 		np->rxopt.bits.rxinfo = valbool;
 		retv = 0;
@@ -466,6 +474,10 @@
 		break;
 	}
 
+	case IPV6_V6ONLY:
+		val = np->ipv6only;
+		break;
+
 	case IPV6_PKTINFO:
 		val = np->rxopt.bits.rxinfo;
 		break;
diff -urN linux-2.4.21-pre2/net/ipv6/mcast.c linux-2.4.21-pre3/net/ipv6/mcast.c
--- linux-2.4.21-pre2/net/ipv6/mcast.c	Wed Aug 15 01:54:24 2001
+++ linux-2.4.21-pre3/net/ipv6/mcast.c	Mon Jan  6 16:32:33 2003
@@ -18,6 +18,12 @@
 /* Changes:
  *
  *	yoshfuji	: fix format of router-alert option
+ *	YOSHIFUJI Hideaki @USAGI:
+ *		Fixed source address for MLD message based on
+ *		<draft-ietf-magma-mld-source-02.txt>.
+ *	YOSHIFUJI Hideaki @USAGI:
+ *		- Ignore Queries for invalid addresses.
+ *		- MLD for link-local addresses.
  */
 
 #define __NO_VERSION__
@@ -405,6 +411,7 @@
 	unsigned long resptime;
 	struct inet6_dev *idev;
 	struct icmp6hdr *hdr;
+	int addr_type;
 
 	if (!pskb_may_pull(skb, sizeof(struct in6_addr)))
 		return -EINVAL;
@@ -420,6 +427,11 @@
 	resptime = (resptime<<10)/(1024000/HZ);
 
 	addrp = (struct in6_addr *) (hdr + 1);
+	addr_type = ipv6_addr_type(addrp);
+
+	if (addr_type != IPV6_ADDR_ANY &&
+	    !(addr_type&IPV6_ADDR_MULTICAST))
+		return -EINVAL;
 
 	idev = in6_dev_get(skb->dev);
 
@@ -427,7 +439,7 @@
 		return 0;
 
 	read_lock(&idev->lock);
-	if (ipv6_addr_any(addrp)) {
+	if (addr_type == IPV6_ADDR_ANY) {
 		for (ma = idev->mc_list; ma; ma=ma->next)
 			igmp6_group_queried(ma, resptime);
 	} else {
@@ -451,6 +463,7 @@
 	struct in6_addr *addrp;
 	struct inet6_dev *idev;
 	struct icmp6hdr *hdr;
+	int addr_type;
 
 	/* Our own report looped back. Ignore it. */
 	if (skb->pkt_type == PACKET_LOOPBACK)
@@ -462,7 +475,9 @@
 	hdr = (struct icmp6hdr*) skb->h.raw;
 
 	/* Drop reports with not link local source */
-	if (!(ipv6_addr_type(&skb->nh.ipv6h->saddr)&IPV6_ADDR_LINKLOCAL))
+	addr_type = ipv6_addr_type(&skb->nh.ipv6h->saddr);
+	if (addr_type != IPV6_ADDR_ANY && 
+	    !(addr_type&IPV6_ADDR_LINKLOCAL))
 		return -EINVAL;
 
 	addrp = (struct in6_addr *) (hdr + 1);
@@ -529,11 +544,11 @@
 	}
 
 	if (ipv6_get_lladdr(dev, &addr_buf)) {
-#if MCAST_DEBUG >= 1
-		printk(KERN_DEBUG "igmp6: %s no linklocal address\n",
-		       dev->name);
-#endif
-		goto out;
+		/* <draft-ietf-magma-mld-source-02.txt>:
+		 * use unspecified address as the source address 
+		 * when a valid link-local address is not available.
+		 */
+		memset(&addr_buf, 0, sizeof(addr_buf));
 	}
 
 	ip6_nd_hdr(sk, skb, dev, &addr_buf, snd_addr, NEXTHDR_HOP, payload_len);
@@ -566,11 +581,9 @@
 static void igmp6_join_group(struct ifmcaddr6 *ma)
 {
 	unsigned long delay;
-	int addr_type;
-
-	addr_type = ipv6_addr_type(&ma->mca_addr);
 
-	if ((addr_type & (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_LOOPBACK)))
+	if (IPV6_ADDR_MC_SCOPE(&ma->mca_addr) < IPV6_ADDR_SCOPE_LINKLOCAL ||
+	    ipv6_addr_is_ll_all_nodes(&ma->mca_addr))
 		return;
 
 	igmp6_send(&ma->mca_addr, ma->idev->dev, ICMPV6_MGM_REPORT);
@@ -591,11 +604,8 @@
 
 static void igmp6_leave_group(struct ifmcaddr6 *ma)
 {
-	int addr_type;
-
-	addr_type = ipv6_addr_type(&ma->mca_addr);
-
-	if ((addr_type & IPV6_ADDR_LINKLOCAL))
+	if (IPV6_ADDR_MC_SCOPE(&ma->mca_addr) < IPV6_ADDR_SCOPE_LINKLOCAL ||
+	    ipv6_addr_is_ll_all_nodes(&ma->mca_addr))
 		return;
 
 	if (ma->mca_flags & MAF_LAST_REPORTER)
diff -urN linux-2.4.21-pre2/net/ipv6/ndisc.c linux-2.4.21-pre3/net/ipv6/ndisc.c
--- linux-2.4.21-pre2/net/ipv6/ndisc.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/net/ipv6/ndisc.c	Mon Jan  6 16:32:33 2003
@@ -583,6 +583,253 @@
 	}
 }
 
+void ndisc_recv_ns(struct sk_buff *skb)
+{
+	struct nd_msg *msg = (struct nd_msg *)skb->h.raw;
+	struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
+	struct in6_addr *daddr = &skb->nh.ipv6h->daddr;
+	u8 *lladdr = NULL;
+	int lladdrlen = 0;
+	u32 ndoptlen = skb->tail - msg->opt;
+	struct ndisc_options ndopts;
+	struct net_device *dev = skb->dev;
+	struct inet6_ifaddr *ifp;
+	struct neighbour *neigh;
+
+	if (skb->len < sizeof(struct nd_msg)) {
+		if (net_ratelimit())
+			printk(KERN_WARNING "ICMP NS: packet too short\n");
+		return;
+	}
+
+	if (ipv6_addr_type(&msg->target)&IPV6_ADDR_MULTICAST) {
+		if (net_ratelimit())
+			printk(KERN_WARNING "ICMP NS: target address is multicast\n");
+		return;
+	}
+
+	if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
+		if (net_ratelimit())
+			printk(KERN_WARNING "ICMP NS: invalid ND option, ignored.\n");
+		return;
+	}
+
+	if (ndopts.nd_opts_src_lladdr) {
+		lladdr = (u8*)(ndopts.nd_opts_src_lladdr + 1);
+		lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3;
+		if (lladdrlen != NDISC_OPT_SPACE(dev->addr_len)) {
+			if (net_ratelimit())
+				printk(KERN_WARNING "ICMP NS: bad lladdr length.\n");
+			return;
+		}
+	}
+
+	/* XXX: RFC2461 7.1.1:
+	 * 	If the IP source address is the unspecified address, there
+	 *	MUST NOT be source link-layer address option in the message.
+	 *
+	 *	NOTE! Linux kernel < 2.4.4 broke this rule.
+	 */
+		 	
+	/* XXX: RFC2461 7.1.1:
+	 *	If the IP source address is the unspecified address, the IP
+      	 *	destination address MUST be a solicited-node multicast address.
+	 */
+
+	if ((ifp = ipv6_get_ifaddr(&msg->target, dev)) != NULL) {
+		int addr_type = ipv6_addr_type(saddr);
+
+		if (ifp->flags & IFA_F_TENTATIVE) {
+			/* Address is tentative. If the source
+			   is unspecified address, it is someone
+			   does DAD, otherwise we ignore solicitations
+			   until DAD timer expires.
+			 */
+			if (addr_type == IPV6_ADDR_ANY) {
+				if (dev->type == ARPHRD_IEEE802_TR) { 
+					unsigned char *sadr = skb->mac.raw ;
+					if (((sadr[8] &0x7f) != (dev->dev_addr[0] & 0x7f)) ||
+					(sadr[9] != dev->dev_addr[1]) ||
+					(sadr[10] != dev->dev_addr[2]) ||
+					(sadr[11] != dev->dev_addr[3]) ||
+					(sadr[12] != dev->dev_addr[4]) ||
+					(sadr[13] != dev->dev_addr[5])) 
+					{
+						addrconf_dad_failure(ifp) ; 
+					}
+				} else {
+					addrconf_dad_failure(ifp);
+				}
+			} else
+				in6_ifa_put(ifp);
+			return;
+		}
+	
+		if (addr_type == IPV6_ADDR_ANY) {
+			struct in6_addr maddr;
+
+			ipv6_addr_all_nodes(&maddr);
+			ndisc_send_na(dev, NULL, &maddr, &ifp->addr, 
+				      ifp->idev->cnf.forwarding, 0, 
+				      ipv6_addr_type(&ifp->addr)&IPV6_ADDR_ANYCAST ? 0 : 1, 
+				      1);
+			in6_ifa_put(ifp);
+			return;
+		}
+
+		if (addr_type & IPV6_ADDR_UNICAST) {
+			int inc = ipv6_addr_type(daddr)&IPV6_ADDR_MULTICAST;
+
+			if (inc)
+				nd_tbl.stats.rcv_probes_mcast++;
+			else
+				nd_tbl.stats.rcv_probes_ucast++;
+
+			/* 
+			 *	update / create cache entry
+			 *	for the source adddress
+			 */
+
+			neigh = neigh_event_ns(&nd_tbl, lladdr, saddr, dev);
+
+			if (neigh || !dev->hard_header) {
+				ndisc_send_na(dev, neigh, saddr, &ifp->addr, 
+					      ifp->idev->cnf.forwarding, 1, 
+					      ipv6_addr_type(&ifp->addr)&IPV6_ADDR_ANYCAST ? 0 : 1, 
+					      1);
+				if (neigh)
+					neigh_release(neigh);
+			}
+		}
+		in6_ifa_put(ifp);
+	} else {
+		struct inet6_dev *in6_dev = in6_dev_get(dev);
+		int addr_type = ipv6_addr_type(saddr);
+
+		if (in6_dev && in6_dev->cnf.forwarding &&
+		    (addr_type & IPV6_ADDR_UNICAST) &&
+		    pneigh_lookup(&nd_tbl, &msg->target, dev, 0)) {
+			int inc = ipv6_addr_type(daddr)&IPV6_ADDR_MULTICAST;
+
+			if (skb->stamp.tv_sec == 0 ||
+			    skb->pkt_type == PACKET_HOST ||
+			    inc == 0 ||
+			    in6_dev->nd_parms->proxy_delay == 0) {
+				if (inc)
+					nd_tbl.stats.rcv_probes_mcast++;
+				else
+					nd_tbl.stats.rcv_probes_ucast++;
+					
+				neigh = neigh_event_ns(&nd_tbl, lladdr, saddr, dev);
+
+				if (neigh) {
+					ndisc_send_na(dev, neigh, saddr, &msg->target,
+						      0, 1, 0, 1);
+					neigh_release(neigh);
+				}
+			} else {
+				struct sk_buff *n = skb_clone(skb, GFP_ATOMIC);
+				if (n)
+					pneigh_enqueue(&nd_tbl, in6_dev->nd_parms, n);
+				in6_dev_put(in6_dev);
+				return;
+			}
+		}
+		if (in6_dev)
+			in6_dev_put(in6_dev);
+	}
+	return;
+}
+
+void ndisc_recv_na(struct sk_buff *skb)
+{
+	struct nd_msg *msg = (struct nd_msg *)skb->h.raw;
+	struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
+	struct in6_addr *daddr = &skb->nh.ipv6h->daddr;
+	u8 *lladdr = NULL;
+	int lladdrlen = 0;
+	u32 ndoptlen = skb->tail - msg->opt;
+	struct ndisc_options ndopts;
+	struct net_device *dev = skb->dev;
+	struct inet6_ifaddr *ifp;
+	struct neighbour *neigh;
+
+	if (skb->len < sizeof(struct nd_msg)) {
+		if (net_ratelimit())
+			printk(KERN_WARNING "ICMP NA: packet too short\n");
+		return;
+	}
+
+	if (ipv6_addr_type(&msg->target)&IPV6_ADDR_MULTICAST) {
+		if (net_ratelimit())
+			printk(KERN_WARNING "NDISC NA: target address is multicast\n");
+		return;
+	}
+
+	if ((ipv6_addr_type(daddr)&IPV6_ADDR_MULTICAST) &&
+	    msg->icmph.icmp6_solicited) {
+		ND_PRINTK0("NDISC: solicited NA is multicasted\n");
+		return;
+	}
+		
+	if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
+		if (net_ratelimit())
+			printk(KERN_WARNING "ICMP NS: invalid ND option, ignored.\n");
+		return;
+	}
+	if (ndopts.nd_opts_tgt_lladdr) {
+		lladdr = (u8*)(ndopts.nd_opts_tgt_lladdr + 1);
+		lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3;
+		if (lladdrlen != NDISC_OPT_SPACE(dev->addr_len)) {
+			if (net_ratelimit())
+				printk(KERN_WARNING "NDISC NA: invalid lladdr length.\n");
+			return;
+		}
+	}
+	if ((ifp = ipv6_get_ifaddr(&msg->target, dev))) {
+		if (ifp->flags & IFA_F_TENTATIVE) {
+			addrconf_dad_failure(ifp);
+			return;
+		}
+		/* What should we make now? The advertisement
+		   is invalid, but ndisc specs say nothing
+		   about it. It could be misconfiguration, or
+		   an smart proxy agent tries to help us :-)
+		 */
+		ND_PRINTK0("%s: someone advertises our address!\n",
+			   ifp->idev->dev->name);
+		in6_ifa_put(ifp);
+		return;
+	}
+	neigh = neigh_lookup(&nd_tbl, &msg->target, dev);
+
+	if (neigh) {
+		if (neigh->flags & NTF_ROUTER) {
+			if (msg->icmph.icmp6_router == 0) {
+				/*
+				 *	Change: router to host
+				 */
+				struct rt6_info *rt;
+				rt = rt6_get_dflt_router(saddr, dev);
+				if (rt) {
+					/* It is safe only because
+					   we aer in BH */
+					dst_release(&rt->u.dst);
+					ip6_del_rt(rt);
+				}
+			}
+		} else {
+			if (msg->icmph.icmp6_router)
+				neigh->flags |= NTF_ROUTER;
+		}
+
+		neigh_update(neigh, lladdr,
+			     msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE,
+			     msg->icmph.icmp6_override, 1);
+		neigh_release(neigh);
+	}
+}
+
 static void ndisc_router_discovery(struct sk_buff *skb)
 {
         struct ra_msg *ra_msg = (struct ra_msg *) skb->h.raw;
@@ -623,6 +870,7 @@
 	}
 
 	if (!ndisc_parse_options(opt, optlen, &ndopts)) {
+		in6_dev_put(in6_dev);
 		if (net_ratelimit())
 			ND_PRINTK2(KERN_WARNING
 				   "ICMP6 RA: invalid ND option, ignored.\n");
@@ -990,12 +1238,7 @@
 
 int ndisc_rcv(struct sk_buff *skb)
 {
-	struct net_device *dev = skb->dev;
-	struct in6_addr *saddr = &skb->nh.ipv6h->saddr;
-	struct in6_addr *daddr = &skb->nh.ipv6h->daddr;
 	struct nd_msg *msg = (struct nd_msg *) skb->h.raw;
-	struct neighbour *neigh;
-	struct inet6_ifaddr *ifp;
 
 	__skb_push(skb, skb->data-skb->h.raw);
 
@@ -1015,241 +1258,12 @@
 
 	switch (msg->icmph.icmp6_type) {
 	case NDISC_NEIGHBOUR_SOLICITATION:
-	    {
-		struct nd_msg *msg = (struct nd_msg *)skb->h.raw;
-		u8 *lladdr = NULL;
-		int lladdrlen = 0;
-		u32 ndoptlen = skb->tail - msg->opt;
-		struct ndisc_options ndopts;
-
-		if (skb->len < sizeof(struct nd_msg)) {
-			if (net_ratelimit())
-				printk(KERN_WARNING "ICMP NS: packet too short\n");
-			return 0;
-		}
-
-		if (ipv6_addr_type(&msg->target)&IPV6_ADDR_MULTICAST) {
-			if (net_ratelimit())
-				printk(KERN_WARNING "ICMP NS: target address is multicast\n");
-			return 0;
-		}
-
-		if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
-			if (net_ratelimit())
-				printk(KERN_WARNING "ICMP NS: invalid ND option, ignored.\n");
-			return 0;
-		}
-
-		if (ndopts.nd_opts_src_lladdr) {
-			lladdr = (u8*)(ndopts.nd_opts_src_lladdr + 1);
-			lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3;
-			if (lladdrlen != NDISC_OPT_SPACE(skb->dev->addr_len)) {
-				if (net_ratelimit())
-					printk(KERN_WARNING "ICMP NS: bad lladdr length.\n");
-				return 0;
-			}
-		}
-
-		/* XXX: RFC2461 7.1.1:
-		 * 	If the IP source address is the unspecified address, there
-		 *	MUST NOT be source link-layer address option in the message.
-		 *
-		 *	NOTE! Linux kernel < 2.4.4 broke this rule.
-		 */
-		 	
-		/* XXX: RFC2461 7.1.1:
-		 *	If the IP source address is the unspecified address, the IP
-      		 *	destination address MUST be a solicited-node multicast address.
-		 */
-
-		if ((ifp = ipv6_get_ifaddr(&msg->target, dev)) != NULL) {
-			int addr_type = ipv6_addr_type(saddr);
-
-			if (ifp->flags & IFA_F_TENTATIVE) {
-				/* Address is tentative. If the source
-				   is unspecified address, it is someone
-				   does DAD, otherwise we ignore solicitations
-				   until DAD timer expires.
-				 */
-				if (addr_type == IPV6_ADDR_ANY) {
-					if (dev->type == ARPHRD_IEEE802_TR) { 
-						unsigned char *sadr = skb->mac.raw ;
-						if (((sadr[8] &0x7f) != (dev->dev_addr[0] & 0x7f)) ||
-						(sadr[9] != dev->dev_addr[1]) ||
-						(sadr[10] != dev->dev_addr[2]) ||
-						(sadr[11] != dev->dev_addr[3]) ||
-						(sadr[12] != dev->dev_addr[4]) ||
-						(sadr[13] != dev->dev_addr[5])) 
-						{
-							addrconf_dad_failure(ifp) ; 
-						}
-					} else {
-						addrconf_dad_failure(ifp);
-					}
-				} else
-					in6_ifa_put(ifp);
-				return 0;
-			}
-
-			if (addr_type == IPV6_ADDR_ANY) {
-				struct in6_addr maddr;
-
-				ipv6_addr_all_nodes(&maddr);
-				ndisc_send_na(dev, NULL, &maddr, &ifp->addr, 
-					      ifp->idev->cnf.forwarding, 0, 
-					      ipv6_addr_type(&ifp->addr)&IPV6_ADDR_ANYCAST ? 0 : 1, 
-					      1);
-				in6_ifa_put(ifp);
-				return 0;
-			}
-
-			if (addr_type & IPV6_ADDR_UNICAST) {
-				int inc = ipv6_addr_type(daddr)&IPV6_ADDR_MULTICAST;
-
-				if (inc)
-					nd_tbl.stats.rcv_probes_mcast++;
-				else
-					nd_tbl.stats.rcv_probes_ucast++;
-
-				/* 
-				 *	update / create cache entry
-				 *	for the source adddress
-				 */
-
-				neigh = neigh_event_ns(&nd_tbl, lladdr, saddr, skb->dev);
-
-				if (neigh || !dev->hard_header) {
-					ndisc_send_na(dev, neigh, saddr, &ifp->addr, 
-						      ifp->idev->cnf.forwarding, 1, 
-						      ipv6_addr_type(&ifp->addr)&IPV6_ADDR_ANYCAST ? 0 : 1, 
-						      1);
-					if (neigh)
-						neigh_release(neigh);
-				}
-			}
-			in6_ifa_put(ifp);
-		} else {
-			struct inet6_dev *in6_dev = in6_dev_get(dev);
-			int addr_type = ipv6_addr_type(saddr);
-
-			if (in6_dev && in6_dev->cnf.forwarding &&
-			    (addr_type & IPV6_ADDR_UNICAST) &&
-			    pneigh_lookup(&nd_tbl, &msg->target, dev, 0)) {
-				int inc = ipv6_addr_type(daddr)&IPV6_ADDR_MULTICAST;
-
-				if (skb->stamp.tv_sec == 0 ||
-				    skb->pkt_type == PACKET_HOST ||
-				    inc == 0 ||
-				    in6_dev->nd_parms->proxy_delay == 0) {
-					if (inc)
-						nd_tbl.stats.rcv_probes_mcast++;
-					else
-						nd_tbl.stats.rcv_probes_ucast++;
-
-					
-					neigh = neigh_event_ns(&nd_tbl, lladdr, saddr, skb->dev);
-
-					if (neigh) {
-						ndisc_send_na(dev, neigh, saddr, &msg->target,
-							      0, 1, 0, 1);
-						neigh_release(neigh);
-					}
-				} else {
-					struct sk_buff *n = skb_clone(skb, GFP_ATOMIC);
-					if (n)
-						pneigh_enqueue(&nd_tbl, in6_dev->nd_parms, n);
-					in6_dev_put(in6_dev);
-					return 0;
-				}
-			}
-			if (in6_dev)
-				in6_dev_put(in6_dev);
-			
-		}
-		return 0;
-	    }
+		ndisc_recv_ns(skb);
+		break;
 
 	case NDISC_NEIGHBOUR_ADVERTISEMENT:
-	    {
-		struct nd_msg *msg = (struct nd_msg *)skb->h.raw;
-		u8 *lladdr = NULL;
-		int lladdrlen = 0;
-		u32 ndoptlen = skb->tail - msg->opt;
-		struct ndisc_options ndopts;
-
-		if (skb->len < sizeof(struct nd_msg)) {
-			if (net_ratelimit())
-				printk(KERN_WARNING "ICMP NA: packet too short\n");
-			return 0;
-		}
-
-		if (ipv6_addr_type(&msg->target)&IPV6_ADDR_MULTICAST) {
-			if (net_ratelimit())
-				printk(KERN_WARNING "NDISC NA: target address is multicast\n");
-			return 0;
-		}
-
-		if ((ipv6_addr_type(daddr)&IPV6_ADDR_MULTICAST) &&
-		    msg->icmph.icmp6_solicited) {
-			ND_PRINTK0("NDISC: solicited NA is multicasted\n");
-			return 0;
-		}
-		
-		if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {
-			if (net_ratelimit())
-				printk(KERN_WARNING "ICMP NS: invalid ND option, ignored.\n");
-			return 0;
-		}
-		if (ndopts.nd_opts_tgt_lladdr) {
-			lladdr = (u8*)(ndopts.nd_opts_tgt_lladdr + 1);
-			lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3;
-			if (lladdrlen != NDISC_OPT_SPACE(skb->dev->addr_len)) {
-				if (net_ratelimit())
-					printk(KERN_WARNING "NDISC NA: invalid lladdr length.\n");
-				return 0;
-			}
-		}
-		if ((ifp = ipv6_get_ifaddr(&msg->target, dev))) {
-			if (ifp->flags & IFA_F_TENTATIVE) {
-				addrconf_dad_failure(ifp);
-				return 0;
-			}
-			/* What should we make now? The advertisement
-			   is invalid, but ndisc specs say nothing
-			   about it. It could be misconfiguration, or
-			   an smart proxy agent tries to help us :-)
-			 */
-			ND_PRINTK0("%s: someone advertises our address!\n",
-				   ifp->idev->dev->name);
-			in6_ifa_put(ifp);
-			return 0;
-		}
-		neigh = neigh_lookup(&nd_tbl, &msg->target, skb->dev);
-
-		if (neigh) {
-			if (neigh->flags & NTF_ROUTER) {
-				if (msg->icmph.icmp6_router == 0) {
-					/*
-					 *	Change: router to host
-					 */
-					struct rt6_info *rt;
-					rt = rt6_get_dflt_router(saddr, skb->dev);
-					if (rt) {
-						ip6_del_rt(rt);
-					}
-				}
-			} else {
-				if (msg->icmph.icmp6_router)
-					neigh->flags |= NTF_ROUTER;
-			}
-
-			neigh_update(neigh, lladdr,
-				     msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE,
-				     msg->icmph.icmp6_override, 1);
-			neigh_release(neigh);
-		}
+		ndisc_recv_na(skb);
 		break;
-	    }
 
 	case NDISC_ROUTER_ADVERTISEMENT:
 		ndisc_router_discovery(skb);
diff -urN linux-2.4.21-pre2/net/ipv6/netfilter/ip6table_mangle.c linux-2.4.21-pre3/net/ipv6/netfilter/ip6table_mangle.c
--- linux-2.4.21-pre2/net/ipv6/netfilter/ip6table_mangle.c	Mon Feb 25 11:38:14 2002
+++ linux-2.4.21-pre3/net/ipv6/netfilter/ip6table_mangle.c	Mon Jan  6 16:32:33 2003
@@ -157,7 +157,7 @@
 	hop_limit = (*pskb)->nh.ipv6h->hop_limit;
 
 	/* flowlabel and prio (includes version, which shouldn't change either */
-	flowlabel = (u_int32_t) (*pskb)->nh.ipv6h;
+	flowlabel = *((u_int32_t *) (*pskb)->nh.ipv6h);
 
 	ret = ip6t_do_table(pskb, hook, in, out, &packet_mangler, NULL);
 
diff -urN linux-2.4.21-pre2/net/ipv6/route.c linux-2.4.21-pre3/net/ipv6/route.c
--- linux-2.4.21-pre2/net/ipv6/route.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/net/ipv6/route.c	Mon Jan  6 16:32:33 2003
@@ -1397,6 +1397,9 @@
 	   caused by addrconf/ndisc.
 	*/
 	idev = __in6_dev_get(arg->dev);
+	if (idev == NULL)
+		return 0;
+
 	/* For administrative MTU increase, there is no way to discover 
 	   IPv6 PMTU increase, so PMTU increase should be updated here.
 	   Since RFC 1981 doesn't include administrative MTU increase
diff -urN linux-2.4.21-pre2/net/ipv6/sysctl_net_ipv6.c linux-2.4.21-pre3/net/ipv6/sysctl_net_ipv6.c
--- linux-2.4.21-pre2/net/ipv6/sysctl_net_ipv6.c	Sun Mar  1 14:40:42 1998
+++ linux-2.4.21-pre3/net/ipv6/sysctl_net_ipv6.c	Mon Jan  6 16:32:33 2003
@@ -1,5 +1,8 @@
 /*
  * sysctl_net_ipv6.c: sysctl interface to net IPV6 subsystem.
+ *
+ * Changes:
+ * YOSHIFUJI Hideaki @USAGI:	added icmp sysctl table.
  */
 
 #include <linux/mm.h>
@@ -12,11 +15,15 @@
 #include <net/addrconf.h>
 
 extern ctl_table ipv6_route_table[];
+extern ctl_table ipv6_icmp_table[];
 
 #ifdef CONFIG_SYSCTL
 
 ctl_table ipv6_table[] = {
 	{NET_IPV6_ROUTE, "route", NULL, 0, 0555, ipv6_route_table},
+	{NET_IPV6_ICMP, "icmp", NULL, 0, 0500, ipv6_icmp_table},
+	{NET_IPV6_BINDV6ONLY, "bindv6only",
+	 &sysctl_ipv6_bindv6only, sizeof(int), 0644, NULL, &proc_dointvec},
 	{0}
 };
 
diff -urN linux-2.4.21-pre2/net/ipv6/tcp_ipv6.c linux-2.4.21-pre3/net/ipv6/tcp_ipv6.c
--- linux-2.4.21-pre2/net/ipv6/tcp_ipv6.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/net/ipv6/tcp_ipv6.c	Mon Jan  6 16:32:33 2003
@@ -14,6 +14,9 @@
  *
  *	Fixes:
  *	Hideaki YOSHIFUJI	:	sin6_scope_id support
+ *	YOSHIFUJI Hideaki @USAGI and:	Support IPV6_V6ONLY socket option, which
+ *	Alexey Kuznetsov		allow both IPv4 and IPv6 sockets to bind
+ *					a single port at the same time.
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -148,14 +151,23 @@
 					    !sk2->reuse	||
 					    sk2->state == TCP_LISTEN) {
 						/* NOTE: IPv6 tw bucket have different format */
-						if (!sk2->rcv_saddr	||
-						    addr_type == IPV6_ADDR_ANY ||
-						    !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr,
-								   sk2->state != TCP_TIME_WAIT ?
-								   &sk2->net_pinfo.af_inet6.rcv_saddr :
-								   &((struct tcp_tw_bucket*)sk)->v6_rcv_saddr) ||
-						    (addr_type==IPV6_ADDR_MAPPED && sk2->family==AF_INET &&
-						     sk->rcv_saddr==sk2->rcv_saddr))
+						if ((!sk2->rcv_saddr && !ipv6_only_sock(sk)) ||
+						    (sk2->family == AF_INET6 &&
+						     ipv6_addr_any(&sk2->net_pinfo.af_inet6.rcv_saddr) &&
+						     !(ipv6_only_sock(sk2) && addr_type == IPV6_ADDR_MAPPED)) ||
+						    (addr_type == IPV6_ADDR_ANY &&
+						     (!ipv6_only_sock(sk) ||
+						      !(sk2->family == AF_INET6 ? ipv6_addr_type(&sk2->net_pinfo.af_inet6.rcv_saddr) == IPV6_ADDR_MAPPED : 1))) ||
+						    (sk2->family == AF_INET6 &&
+						     !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr,
+								    sk2->state != TCP_TIME_WAIT ?
+								    &sk2->net_pinfo.af_inet6.rcv_saddr :
+								    &((struct tcp_tw_bucket*)sk)->v6_rcv_saddr)) ||
+						    (addr_type == IPV6_ADDR_MAPPED && 
+						     !ipv6_only_sock(sk2) &&
+						     (!sk2->rcv_saddr ||
+						      !sk->rcv_saddr ||
+						      sk->rcv_saddr == sk2->rcv_saddr)))
 							break;
 					}
 				}
@@ -602,6 +614,9 @@
 
 		SOCK_DEBUG(sk, "connect: ipv4 mapped\n");
 
+		if (__ipv6_only_sock(sk))
+			return -ENETUNREACH;
+
 		sin.sin_family = AF_INET;
 		sin.sin_port = usin->sin6_port;
 		sin.sin_addr.s_addr = usin->sin6_addr.s6_addr32[3];
diff -urN linux-2.4.21-pre2/net/ipv6/udp.c linux-2.4.21-pre3/net/ipv6/udp.c
--- linux-2.4.21-pre2/net/ipv6/udp.c	Thu Nov 28 15:53:15 2002
+++ linux-2.4.21-pre3/net/ipv6/udp.c	Mon Jan  6 16:32:33 2003
@@ -11,6 +11,9 @@
  *
  *	Fixes:
  *	Hideaki YOSHIFUJI	:	sin6_scope_id support
+ *	YOSHIFUJI Hideaki @USAGI and:	Support IPV6_V6ONLY socket option, which
+ *	Alexey Kuznetsov		allow both IPv4 and IPv6 sockets to bind
+ *					a single port at the same time.
  *
  *	This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -106,13 +109,21 @@
 			if (sk2->num == snum &&
 			    sk2 != sk &&
 			    sk2->bound_dev_if == sk->bound_dev_if &&
-			    (!sk2->rcv_saddr ||
-			     addr_type == IPV6_ADDR_ANY ||
-			     !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr,
-					    &sk2->net_pinfo.af_inet6.rcv_saddr) ||
+			    ((!sk2->rcv_saddr && !ipv6_only_sock(sk)) ||
+			     (sk2->family == AF_INET6 && 
+			      ipv6_addr_any(&sk2->net_pinfo.af_inet6.rcv_saddr) &&
+			      !(ipv6_only_sock(sk2) && addr_type == IPV6_ADDR_MAPPED)) ||
+			     (addr_type == IPV6_ADDR_ANY && 
+			      (!ipv6_only_sock(sk) || 
+			       !(sk2->family == AF_INET6 ? (ipv6_addr_type(&sk2->net_pinfo.af_inet6.rcv_saddr) == IPV6_ADDR_MAPPED) : 1))) ||
+			     (sk2->family == AF_INET6 && 
+			      !ipv6_addr_cmp(&sk->net_pinfo.af_inet6.rcv_saddr,
+					     &sk2->net_pinfo.af_inet6.rcv_saddr)) ||
 			     (addr_type == IPV6_ADDR_MAPPED &&
-			      sk2->family == AF_INET &&
-			      sk->rcv_saddr == sk2->rcv_saddr)) &&
+			      !ipv6_only_sock(sk2) &&
+			      (!sk2->rcv_saddr || 
+			       !sk->rcv_saddr ||
+			       sk->rcv_saddr == sk2->rcv_saddr))) &&
 			    (!sk2->reuse || !sk->reuse))
 				goto fail;
 		}
@@ -221,6 +232,8 @@
 	int			err;
 
 	if (usin->sin6_family == AF_INET) {
+		if (__ipv6_only_sock(sk))
+			return -EAFNOSUPPORT;
 		err = udp_connect(sk, uaddr, addr_len);
 		goto ipv4_connected;
 	}
@@ -256,6 +269,9 @@
 	if (addr_type == IPV6_ADDR_MAPPED) {
 		struct sockaddr_in sin;
 
+		if (__ipv6_only_sock(sk))
+			return -ENETUNREACH;
+
 		sin.sin_family = AF_INET;
 		sin.sin_addr.s_addr = daddr->s6_addr32[3];
 		sin.sin_port = usin->sin6_port;
@@ -783,8 +799,11 @@
 	fl.oif = 0;
 
 	if (sin6) {
-		if (sin6->sin6_family == AF_INET)
+		if (sin6->sin6_family == AF_INET) {
+			if (__ipv6_only_sock(sk))
+				return -ENETUNREACH;
 			return udp_sendmsg(sk, msg, ulen);
+		}
 
 		if (addr_len < SIN6_LEN_RFC2133)
 			return -EINVAL;
@@ -831,6 +850,9 @@
 	if (addr_type == IPV6_ADDR_MAPPED) {
 		struct sockaddr_in sin;
 
+		if (__ipv6_only_sock(sk))
+			return -ENETUNREACH;
+
 		sin.sin_family = AF_INET;
 		sin.sin_addr.s_addr = daddr->s6_addr32[3];
 		sin.sin_port = udh.uh.dest;
diff -urN linux-2.4.21-pre2/net/irda/irlap_event.c linux-2.4.21-pre3/net/irda/irlap_event.c
--- linux-2.4.21-pre2/net/irda/irlap_event.c	Thu Nov 28 15:53:16 2002
+++ linux-2.4.21-pre3/net/irda/irlap_event.c	Mon Jan  6 16:32:33 2003
@@ -253,19 +253,45 @@
 	case LAP_XMIT_P: /* FALLTHROUGH */
 	case LAP_XMIT_S:
 		/* 
+		 * We just received the pf bit and are at the beginning
+		 * of a new LAP transmit window.
 		 * Check if there are any queued data frames, and do not
 		 * try to disconnect link if we send any data frames, since
 		 * that will change the state away form XMIT
 		 */
+		IRDA_DEBUG(2, __FUNCTION__ "() : queue len = %d\n",
+			   skb_queue_len(&self->txq));
+
 		if (skb_queue_len(&self->txq)) {
 			/* Prevent race conditions with irlap_data_request() */
 			self->local_busy = TRUE;
 
+			/* Theory of operation.
+			 * We send frames up to when we fill the window or
+			 * reach line capacity. Those frames will queue up
+			 * in the device queue, and the driver will slowly
+			 * send them.
+			 * After each frame that we send, we poll the higher
+			 * layer for more data. It's the right time to do
+			 * that because the link layer need to perform the mtt
+			 * and then send the first frame, so we can afford
+			 * to send a bit of time in kernel space.
+			 * The explicit flow indication allow to minimise
+			 * buffers (== lower latency), to avoid higher layer
+			 * polling via timers (== less context switches) and
+			 * to implement a crude scheduler - Jean II */
+
 			/* Try to send away all queued data frames */
 			while ((skb = skb_dequeue(&self->txq)) != NULL) {
+				/* Send one frame */
 				ret = (*state[self->state])(self, SEND_I_CMD,
 							    skb, NULL);
 				kfree_skb(skb);
+
+				/* Poll the higher layers for one more frame */
+				irlmp_flow_indication(self->notify.instance,
+						      FLOW_START);
+
 				if (ret == -EPROTO)
 					break; /* Try again later! */
 			}
diff -urN linux-2.4.21-pre2/net/irda/irlmp.c linux-2.4.21-pre3/net/irda/irlmp.c
--- linux-2.4.21-pre2/net/irda/irlmp.c	Mon Jan  6 16:32:29 2003
+++ linux-2.4.21-pre3/net/irda/irlmp.c	Mon Jan  6 16:32:33 2003
@@ -1220,6 +1220,72 @@
 }
 
 /*
+ * Receive flow control indication from LAP.
+ * LAP want us to send it one more frame. We implement a simple round
+ * robin scheduler between the active sockets so that we get a bit of
+ * fairness. Note that the round robin is far from perfect, but it's
+ * better than nothing.
+ * We then poll the selected socket so that we can do synchronous
+ * refilling of IrLAP (which allow to minimise the number of buffers).
+ * Jean II
+ */
+void irlmp_flow_indication(struct lap_cb *self, LOCAL_FLOW flow)
+{
+	struct lsap_cb *next;
+	struct lsap_cb *curr;
+	int	lsap_todo;
+
+	ASSERT(self->magic == LMP_LAP_MAGIC, return;);
+	ASSERT(flow == FLOW_START, return;);
+
+	/* Get the number of lsap. That's the only safe way to know
+	 * that we have looped around... - Jean II */
+	lsap_todo = HASHBIN_GET_SIZE(self->lsaps);
+	IRDA_DEBUG(4, __FUNCTION__ "() : %d lsaps to scan\n", lsap_todo);
+
+	/* Poll lsap in order until the queue is full or until we
+	 * tried them all.
+	 * Most often, the current LSAP will have something to send,
+	 * so we will go through this loop only once. - Jean II */
+	while((lsap_todo--) &&
+	      (IRLAP_GET_TX_QUEUE_LEN(self->irlap) < LAP_HIGH_THRESHOLD)) {
+		/* Try to find the next lsap we should poll. */
+		next = self->flow_next;
+		if(next != NULL) {
+			/* Note that if there is only one LSAP on the LAP
+			 * (most common case), self->flow_next is always NULL,
+			 * so we always avoid this loop. - Jean II */
+			IRDA_DEBUG(4, __FUNCTION__ "() : searching my LSAP\n");
+
+			/* We look again in hashbins, because the lsap
+			 * might have gone away... - Jean II */
+			curr = (struct lsap_cb *) hashbin_get_first(self->lsaps);
+			while((curr != NULL ) && (curr != next))
+				curr = (struct lsap_cb *) hashbin_get_next(self->lsaps);
+		} else
+			curr = NULL;
+
+		/* If we have no lsap, restart from first one */
+		if(curr == NULL)
+			curr = (struct lsap_cb *) hashbin_get_first(self->lsaps);
+		/* Uh-oh... Paranoia */
+		if(curr == NULL)
+			break;
+
+		/* Next time, we will get the next one (or the first one) */
+		self->flow_next = (struct lsap_cb *) hashbin_get_next(self->lsaps);
+		IRDA_DEBUG(4, __FUNCTION__ "() : curr is %p, next was %p and is now %p, still %d to go - queue len = %d\n", curr, next, self->flow_next, lsap_todo, IRLAP_GET_TX_QUEUE_LEN(self->irlap));
+
+		/* Inform lsap user that it can send one more packet. */
+		if (curr->notify.flow_indication != NULL)
+			curr->notify.flow_indication(curr->notify.instance, 
+						     curr, flow);
+		else
+			IRDA_DEBUG(1, __FUNCTION__ "(), no handler\n");
+	}
+}
+
+/*
  * Function irlmp_hint_to_service (hint)
  *
  *    Returns a list of all servics contained in the given hint bits. This
diff -urN linux-2.4.21-pre2/net/sched/sch_gred.c linux-2.4.21-pre3/net/sched/sch_gred.c
--- linux-2.4.21-pre2/net/sched/sch_gred.c	Fri Aug  2 17:39:46 2002
+++ linux-2.4.21-pre3/net/sched/sch_gred.c	Mon Jan  6 16:32:33 2003
@@ -328,18 +328,20 @@
 	struct tc_gred_qopt *ctl;
 	struct tc_gred_sopt *sopt;
 	struct rtattr *tb[TCA_GRED_STAB];
-	struct rtattr *tb2[TCA_GRED_STAB];
+	struct rtattr *tb2[TCA_GRED_DPS];
 	int i;
 
 	if (opt == NULL ||
 		rtattr_parse(tb, TCA_GRED_STAB, RTA_DATA(opt), RTA_PAYLOAD(opt)) )
 			return -EINVAL;
 
-	if (tb[TCA_GRED_PARMS-1] == 0 && tb[TCA_GRED_STAB-1] == 0 &&
-	    tb[TCA_GRED_DPS-1] != 0) {
+	if (tb[TCA_GRED_PARMS-1] == 0 && tb[TCA_GRED_STAB-1] == 0) {
 		rtattr_parse(tb2, TCA_GRED_DPS, RTA_DATA(opt),
 		    RTA_PAYLOAD(opt));
 
+		if (tb2[TCA_GRED_DPS-1] == 0)
+			return -EINVAL;
+
 		sopt = RTA_DATA(tb2[TCA_GRED_DPS-1]);
 		table->DPs=sopt->DPs;   
 		table->def=sopt->def_DP; 
@@ -471,16 +473,18 @@
 	struct gred_sched *table = (struct gred_sched *)sch->data;
 	struct tc_gred_sopt *sopt;
 	struct rtattr *tb[TCA_GRED_STAB];
-	struct rtattr *tb2[TCA_GRED_STAB];
+	struct rtattr *tb2[TCA_GRED_DPS];
 
 	if (opt == NULL ||
 		rtattr_parse(tb, TCA_GRED_STAB, RTA_DATA(opt), RTA_PAYLOAD(opt)) )
 			return -EINVAL;
 
-	if (tb[TCA_GRED_PARMS-1] == 0 && tb[TCA_GRED_STAB-1] == 0 &&
-	    tb[TCA_GRED_DPS-1] != 0) {
+	if (tb[TCA_GRED_PARMS-1] == 0 && tb[TCA_GRED_STAB-1] == 0 ) {
 		rtattr_parse(tb2, TCA_GRED_DPS, RTA_DATA(opt),RTA_PAYLOAD(opt));
 
+		if (tb2[TCA_GRED_DPS-1] == 0)
+			return -EINVAL;
+
 		sopt = RTA_DATA(tb2[TCA_GRED_DPS-1]);
 		table->DPs=sopt->DPs;   
 		table->def=sopt->def_DP; 
diff -urN linux-2.4.21-pre2/net/socket.c linux-2.4.21-pre3/net/socket.c
--- linux-2.4.21-pre2/net/socket.c	Mon Jan  6 16:32:29 2003
+++ linux-2.4.21-pre3/net/socket.c	Mon Jan  6 16:32:33 2003
@@ -1253,7 +1253,7 @@
 		flags |= MSG_DONTWAIT;
 	err=sock_recvmsg(sock, &msg, size, flags);
 
-	if(err >= 0 && addr != NULL && msg.msg_namelen)
+	if(err >= 0 && addr != NULL)
 	{
 		err2=move_addr_to_user(address, msg.msg_namelen, addr, addr_len);
 		if(err2<0)
@@ -1477,7 +1477,7 @@
 		goto out_freeiov;
 	len = err;
 
-	if (uaddr != NULL && msg_sys.msg_namelen) {
+	if (uaddr != NULL) {
 		err = move_addr_to_user(addr, msg_sys.msg_namelen, uaddr, uaddr_len);
 		if (err < 0)
 			goto out_freeiov;
diff -urN linux-2.4.21-pre2/net/sunrpc/svcsock.c linux-2.4.21-pre3/net/sunrpc/svcsock.c
--- linux-2.4.21-pre2/net/sunrpc/svcsock.c	Thu Nov 28 15:53:16 2002
+++ linux-2.4.21-pre3/net/sunrpc/svcsock.c	Mon Jan  6 16:32:33 2003
@@ -461,11 +461,8 @@
 		svc_sock_enqueue(svsk);
 	}
 
-	if (sk->sleep && waitqueue_active(sk->sleep)) {
-		printk(KERN_WARNING "RPC svc_write_space: some sleeping on %p\n",
-		       svsk);
+	if (sk->sleep && waitqueue_active(sk->sleep))
 		wake_up_interruptible(sk->sleep);
-	}
 }
 
 /*
diff -urN linux-2.4.21-pre2/scripts/split-include.c linux-2.4.21-pre3/scripts/split-include.c
--- linux-2.4.21-pre2/scripts/split-include.c	Fri Aug  2 17:39:46 2002
+++ linux-2.4.21-pre3/scripts/split-include.c	Mon Jan  6 16:32:33 2003
@@ -19,8 +19,8 @@
  *    KEY-5    (empty)     -           leave old empty file alone
  */
 
-#include <sys/stat.h>
 #include <sys/types.h>
+#include <sys/stat.h>
 
 #include <ctype.h>
 #include <errno.h>
